net: Enable kernel side filtering of route dumps
authorDavid Ahern <dsahern@gmail.com>
Tue, 16 Oct 2018 01:56:48 +0000 (18:56 -0700)
committerDavid S. Miller <davem@davemloft.net>
Tue, 16 Oct 2018 07:14:07 +0000 (00:14 -0700)
Update parsing of route dump request to enable kernel side filtering.
Allow filtering results by protocol (e.g., which routing daemon installed
the route), route type (e.g., unicast), table id and nexthop device. These
amount to the low hanging fruit, yet a huge improvement, for dumping
routes.

ip_valid_fib_dump_req is called with RTNL held, so __dev_get_by_index can
be used to look up the device index without taking a reference. From
there filter->dev is only used during dump loops with the lock still held.

Set NLM_F_DUMP_FILTERED in the answer_flags so the user knows the results
have been filtered should no entries be returned.

Signed-off-by: David Ahern <dsahern@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/ip_fib.h
net/ipv4/fib_frontend.c
net/ipv4/ipmr.c
net/ipv6/ip6_fib.c
net/ipv6/ip6mr.c
net/mpls/af_mpls.c

index 1eabc9edd2b9c2e146b2458b46b03010fc98befa..e8d9456bf36e1e0d2c7dd6ac5d3c1dbd15f5ddec 100644 (file)
@@ -465,5 +465,5 @@ u32 ip_mtu_from_fib_result(struct fib_result *res, __be32 daddr);
 
 int ip_valid_fib_dump_req(struct net *net, const struct nlmsghdr *nlh,
                          struct fib_dump_filter *filter,
-                         struct netlink_ext_ack *extack);
+                         struct netlink_callback *cb);
 #endif  /* _NET_FIB_H */
index 37dc8ac366fd038eb22474ec227232148fa05e7a..e86ca22551811420c0598e85d93141a16415e4eb 100644 (file)
@@ -804,9 +804,14 @@ errout:
 
 int ip_valid_fib_dump_req(struct net *net, const struct nlmsghdr *nlh,
                          struct fib_dump_filter *filter,
-                         struct netlink_ext_ack *extack)
+                         struct netlink_callback *cb)
 {
+       struct netlink_ext_ack *extack = cb->extack;
+       struct nlattr *tb[RTA_MAX + 1];
        struct rtmsg *rtm;
+       int err, i;
+
+       ASSERT_RTNL();
 
        if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*rtm))) {
                NL_SET_ERR_MSG(extack, "Invalid header for FIB dump request");
@@ -815,8 +820,7 @@ int ip_valid_fib_dump_req(struct net *net, const struct nlmsghdr *nlh,
 
        rtm = nlmsg_data(nlh);
        if (rtm->rtm_dst_len || rtm->rtm_src_len  || rtm->rtm_tos   ||
-           rtm->rtm_table   || rtm->rtm_protocol || rtm->rtm_scope ||
-           rtm->rtm_type) {
+           rtm->rtm_scope) {
                NL_SET_ERR_MSG(extack, "Invalid values in header for FIB dump request");
                return -EINVAL;
        }
@@ -825,9 +829,42 @@ int ip_valid_fib_dump_req(struct net *net, const struct nlmsghdr *nlh,
                return -EINVAL;
        }
 
-       if (nlmsg_attrlen(nlh, sizeof(*rtm))) {
-               NL_SET_ERR_MSG(extack, "Invalid data after header in FIB dump request");
-               return -EINVAL;
+       filter->flags    = rtm->rtm_flags;
+       filter->protocol = rtm->rtm_protocol;
+       filter->rt_type  = rtm->rtm_type;
+       filter->table_id = rtm->rtm_table;
+
+       err = nlmsg_parse_strict(nlh, sizeof(*rtm), tb, RTA_MAX,
+                                rtm_ipv4_policy, extack);
+       if (err < 0)
+               return err;
+
+       for (i = 0; i <= RTA_MAX; ++i) {
+               int ifindex;
+
+               if (!tb[i])
+                       continue;
+
+               switch (i) {
+               case RTA_TABLE:
+                       filter->table_id = nla_get_u32(tb[i]);
+                       break;
+               case RTA_OIF:
+                       ifindex = nla_get_u32(tb[i]);
+                       filter->dev = __dev_get_by_index(net, ifindex);
+                       if (!filter->dev)
+                               return -ENODEV;
+                       break;
+               default:
+                       NL_SET_ERR_MSG(extack, "Unsupported attribute in dump request");
+                       return -EINVAL;
+               }
+       }
+
+       if (filter->flags || filter->protocol || filter->rt_type ||
+           filter->table_id || filter->dev) {
+               filter->filter_set = 1;
+               cb->answer_flags = NLM_F_DUMP_FILTERED;
        }
 
        return 0;
@@ -846,7 +883,7 @@ static int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
        int dumped = 0, err;
 
        if (cb->strict_check) {
-               err = ip_valid_fib_dump_req(net, nlh, &filter, cb->extack);
+               err = ip_valid_fib_dump_req(net, nlh, &filter, cb);
                if (err < 0)
                        return err;
        }
index 3fa988e6a3df7a35744a7cb3d81cab437d02e5e2..7a3e2acda94ce67b92c8680e20c7446c881c4728 100644 (file)
@@ -2532,7 +2532,7 @@ static int ipmr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb)
 
        if (cb->strict_check) {
                err = ip_valid_fib_dump_req(sock_net(skb->sk), cb->nlh,
-                                           &filter, cb->extack);
+                                           &filter, cb);
                if (err < 0)
                        return err;
        }
index a51fc357a05c7f41097c50c7171827a6d79211fd..5562c77022c6798e10b7f507bef3d5a8eb60b839 100644 (file)
@@ -580,7 +580,7 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
        if (cb->strict_check) {
                int err;
 
-               err = ip_valid_fib_dump_req(net, nlh, &arg.filter, cb->extack);
+               err = ip_valid_fib_dump_req(net, nlh, &arg.filter, cb);
                if (err < 0)
                        return err;
        } else if (nlmsg_len(nlh) >= sizeof(struct rtmsg)) {
index 9759b0aecdd6655c25d1922e79114331c94be2dc..c3317ffb09eb6892f7a0e9eda92d57a4c99f7db1 100644 (file)
@@ -2463,7 +2463,7 @@ static int ip6mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb)
 
        if (cb->strict_check) {
                err = ip_valid_fib_dump_req(sock_net(skb->sk), nlh,
-                                           &filter, cb->extack);
+                                           &filter, cb);
                if (err < 0)
                        return err;
        }
index 48f4cbd9fb38318a2092cef054d07d41d26a86f7..24381696932a78f42b9b931e8b8f03f194777ac5 100644 (file)
@@ -2034,15 +2034,16 @@ nla_put_failure:
 #if IS_ENABLED(CONFIG_INET)
 static int mpls_valid_fib_dump_req(struct net *net, const struct nlmsghdr *nlh,
                                   struct fib_dump_filter *filter,
-                                  struct netlink_ext_ack *extack)
+                                  struct netlink_callback *cb)
 {
-       return ip_valid_fib_dump_req(net, nlh, filter, extack);
+       return ip_valid_fib_dump_req(net, nlh, filter, cb);
 }
 #else
 static int mpls_valid_fib_dump_req(struct net *net, const struct nlmsghdr *nlh,
                                   struct fib_dump_filter *filter,
-                                  struct netlink_ext_ack *extack)
+                                  struct netlink_callback *cb)
 {
+       struct netlink_ext_ack *extack = cb->extack;
        struct rtmsg *rtm;
 
        if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*rtm))) {
@@ -2104,7 +2105,7 @@ static int mpls_dump_routes(struct sk_buff *skb, struct netlink_callback *cb)
        if (cb->strict_check) {
                int err;
 
-               err = mpls_valid_fib_dump_req(net, nlh, &filter, cb->extack);
+               err = mpls_valid_fib_dump_req(net, nlh, &filter, cb);
                if (err < 0)
                        return err;