From: Roopa Prabhu <ro...@cumulusnetworks.com>

This is a followup to fib rules sport, dport match support.
Having them supported in getroute makes it easier to test
fib rule lookups. Used by fib rule self tests. Before this patch
getroute used same skb to pass through the route lookup and
for the netlink getroute reply msg. This patch allocates separate
skb's to keep flow dissector happy.

Signed-off-by: Roopa Prabhu <ro...@cumulusnetworks.com>
---
 include/uapi/linux/rtnetlink.h |   2 +
 net/ipv4/route.c               | 151 ++++++++++++++++++++++++++++++-----------
 2 files changed, 115 insertions(+), 38 deletions(-)

diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h
index 9b15005..630ecf4 100644
--- a/include/uapi/linux/rtnetlink.h
+++ b/include/uapi/linux/rtnetlink.h
@@ -327,6 +327,8 @@ enum rtattr_type_t {
        RTA_PAD,
        RTA_UID,
        RTA_TTL_PROPAGATE,
+       RTA_SPORT,
+       RTA_DPORT,
        __RTA_MAX
 };
 
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 1412a7b..e91ed62 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -2568,11 +2568,10 @@ struct rtable *ip_route_output_flow(struct net *net, 
struct flowi4 *flp4,
 EXPORT_SYMBOL_GPL(ip_route_output_flow);
 
 /* called with rcu_read_lock held */
-static int rt_fill_info(struct net *net,  __be32 dst, __be32 src, u32 table_id,
-                       struct flowi4 *fl4, struct sk_buff *skb, u32 portid,
-                       u32 seq)
+static int rt_fill_info(struct net *net, __be32 dst, __be32 src,
+                       struct rtable *rt, u32 table_id, struct flowi4 *fl4,
+                       struct sk_buff *skb, u32 portid, u32 seq)
 {
-       struct rtable *rt = skb_rtable(skb);
        struct rtmsg *r;
        struct nlmsghdr *nlh;
        unsigned long expires = 0;
@@ -2651,6 +2650,14 @@ static int rt_fill_info(struct net *net,  __be32 dst, 
__be32 src, u32 table_id,
                        from_kuid_munged(current_user_ns(), fl4->flowi4_uid)))
                goto nla_put_failure;
 
+       if (fl4->fl4_sport &&
+           nla_put_be16(skb, RTA_SPORT, fl4->fl4_sport))
+               goto nla_put_failure;
+
+       if (fl4->fl4_dport &&
+           nla_put_be16(skb, RTA_DPORT, fl4->fl4_dport))
+               goto nla_put_failure;
+
        error = rt->dst.error;
 
        if (rt_is_input_route(rt)) {
@@ -2668,7 +2675,7 @@ static int rt_fill_info(struct net *net,  __be32 dst, 
__be32 src, u32 table_id,
                        }
                } else
 #endif
-                       if (nla_put_u32(skb, RTA_IIF, skb->dev->ifindex))
+                       if (nla_put_u32(skb, RTA_IIF, fl4->flowi4_iif))
                                goto nla_put_failure;
        }
 
@@ -2683,35 +2690,86 @@ static int rt_fill_info(struct net *net,  __be32 dst, 
__be32 src, u32 table_id,
        return -EMSGSIZE;
 }
 
+static int nla_get_port(struct nlattr *attr, __be16 *port)
+{
+       int p = nla_get_be16(attr);
+
+       if (p <= 0 || p >= 0xffff)
+               return -EINVAL;
+
+       *port = p;
+       return 0;
+}
+
+static int inet_rtm_getroute_reply(struct sk_buff *in_skb, struct nlmsghdr 
*nlh,
+                                  __be32 dst, __be32 src, struct flowi4 *fl4,
+                                  struct rtable *rt, struct fib_result *res)
+{
+       struct net *net = sock_net(in_skb->sk);
+       struct rtmsg *rtm = nlmsg_data(nlh);
+       u32 table_id = RT_TABLE_MAIN;
+       struct sk_buff *skb;
+       int err = 0;
+
+       skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
+       if (!skb) {
+               err = -ENOMEM;
+               return err;
+       }
+
+       if (rtm->rtm_flags & RTM_F_LOOKUP_TABLE)
+               table_id = res->table ? res->table->tb_id : 0;
+
+       if (rtm->rtm_flags & RTM_F_FIB_MATCH)
+               err = fib_dump_info(skb, NETLINK_CB(in_skb).portid,
+                                   nlh->nlmsg_seq, RTM_NEWROUTE, table_id,
+                                   rt->rt_type, res->prefix, res->prefixlen,
+                                   fl4->flowi4_tos, res->fi, 0);
+       else
+               err = rt_fill_info(net, dst, src, rt, table_id,
+                                  fl4, skb, NETLINK_CB(in_skb).portid,
+                                  nlh->nlmsg_seq);
+       if (err < 0)
+               goto errout;
+
+       return rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
+
+errout:
+       kfree_skb(skb);
+       return err;
+}
+
 static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
                             struct netlink_ext_ack *extack)
 {
        struct net *net = sock_net(in_skb->sk);
-       struct rtmsg *rtm;
        struct nlattr *tb[RTA_MAX+1];
+       __be16 sport = 0, dport = 0;
        struct fib_result res = {};
        struct rtable *rt = NULL;
+       struct sk_buff *skb;
+       struct rtmsg *rtm;
        struct flowi4 fl4;
+       struct iphdr *iph;
+       struct udphdr *udph;
        __be32 dst = 0;
        __be32 src = 0;
+       kuid_t uid;
        u32 iif;
        int err;
        int mark;
-       struct sk_buff *skb;
-       u32 table_id = RT_TABLE_MAIN;
-       kuid_t uid;
 
        err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv4_policy,
                          extack);
        if (err < 0)
-               goto errout;
+               return err;
 
        rtm = nlmsg_data(nlh);
 
        skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
        if (!skb) {
                err = -ENOBUFS;
-               goto errout;
+               return err;
        }
 
        /* Reserve room for dummy headers, this skb can pass
@@ -2728,15 +2786,36 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, 
struct nlmsghdr *nlh,
                uid = make_kuid(current_user_ns(), nla_get_u32(tb[RTA_UID]));
        else
                uid = (iif ? INVALID_UID : current_uid());
+       if (tb[RTA_SPORT]) {
+               err = nla_get_port(tb[RTA_SPORT], &sport);
+               if (err)
+                       goto errout_free;
+       }
 
-       /* Bugfix: need to give ip_route_input enough of an IP header to
-        * not gag.
-        */
-       ip_hdr(skb)->protocol = IPPROTO_UDP;
-       ip_hdr(skb)->saddr = src;
-       ip_hdr(skb)->daddr = dst;
+       if (tb[RTA_DPORT]) {
+               err = nla_get_port(tb[RTA_DPORT], &dport);
+               if (err)
+                       goto errout_free;
+       }
 
-       skb_reserve(skb, MAX_HEADER + sizeof(struct iphdr));
+       skb->protocol = htons(ETH_P_IP);
+       iph = skb_put(skb, sizeof(struct iphdr));
+       iph->protocol = IPPROTO_UDP;
+       iph->saddr = src;
+       iph->daddr = dst;
+       iph->version = 0x4;
+       iph->frag_off = 0;
+
+       if (sport || dport) {
+               iph->ihl = 0x5;
+               skb_set_transport_header(skb, skb->len);
+
+               udph = skb_put(skb, sizeof(struct udphdr));
+               udph->dest = dport;
+               udph->source = sport;
+               udph->len = sizeof(struct udphdr);
+               udph->check = 0;
+       }
 
        memset(&fl4, 0, sizeof(fl4));
        fl4.daddr = dst;
@@ -2745,6 +2824,11 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, 
struct nlmsghdr *nlh,
        fl4.flowi4_oif = tb[RTA_OIF] ? nla_get_u32(tb[RTA_OIF]) : 0;
        fl4.flowi4_mark = mark;
        fl4.flowi4_uid = uid;
+       if (sport)
+               fl4.fl4_sport = sport;
+       if (dport)
+               fl4.fl4_dport = dport;
+       fl4.flowi4_proto = IPPROTO_UDP;
 
        rcu_read_lock();
 
@@ -2754,9 +2838,10 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, 
struct nlmsghdr *nlh,
                dev = dev_get_by_index_rcu(net, iif);
                if (!dev) {
                        err = -ENODEV;
-                       goto errout_free;
+                       goto errout_rcu;
                }
 
+               fl4.flowi4_iif = iif; /* for rt_fill_info */
                skb->protocol   = htons(ETH_P_IP);
                skb->dev        = dev;
                skb->mark       = mark;
@@ -2777,42 +2862,32 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, 
struct nlmsghdr *nlh,
        }
 
        if (err)
-               goto errout_free;
+               goto errout_rcu;
 
        if (rtm->rtm_flags & RTM_F_NOTIFY)
                rt->rt_flags |= RTCF_NOTIFY;
 
-       if (rtm->rtm_flags & RTM_F_LOOKUP_TABLE)
-               table_id = res.table ? res.table->tb_id : 0;
-
        if (rtm->rtm_flags & RTM_F_FIB_MATCH) {
                if (!res.fi) {
                        err = fib_props[res.type].error;
                        if (!err)
                                err = -EHOSTUNREACH;
-                       goto errout_free;
+                       goto errout_rcu;
                }
-               err = fib_dump_info(skb, NETLINK_CB(in_skb).portid,
-                                   nlh->nlmsg_seq, RTM_NEWROUTE, table_id,
-                                   rt->rt_type, res.prefix, res.prefixlen,
-                                   fl4.flowi4_tos, res.fi, 0);
-       } else {
-               err = rt_fill_info(net, dst, src, table_id, &fl4, skb,
-                                  NETLINK_CB(in_skb).portid, nlh->nlmsg_seq);
        }
+
+       err = inet_rtm_getroute_reply(in_skb, nlh, dst, src, &fl4, rt, &res);
        if (err < 0)
-               goto errout_free;
+               goto errout_rcu;
 
        rcu_read_unlock();
 
-       err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
-errout:
-       return err;
-
 errout_free:
-       rcu_read_unlock();
        kfree_skb(skb);
-       goto errout;
+       return err;
+errout_rcu:
+       rcu_read_unlock();
+       goto errout_free;
 }
 
 void ip_rt_multicast_event(struct in_device *in_dev)
-- 
2.1.4

Reply via email to