]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - net/ipv4/fib_frontend.c
SUNRPC: call_connect_status() must handle tasks that got transmitted
[linux.git] / net / ipv4 / fib_frontend.c
index 0113993e9b2cc0ceeebe72b74a2043adefd6d1eb..6df95be963116ced81f70e5fb51bfe6114e8965a 100644 (file)
@@ -315,6 +315,32 @@ __be32 fib_compute_spec_dst(struct sk_buff *skb)
        return inet_select_addr(dev, ip_hdr(skb)->saddr, scope);
 }
 
+bool fib_info_nh_uses_dev(struct fib_info *fi, const struct net_device *dev)
+{
+       bool dev_match = false;
+#ifdef CONFIG_IP_ROUTE_MULTIPATH
+       int ret;
+
+       for (ret = 0; ret < fi->fib_nhs; ret++) {
+               struct fib_nh *nh = &fi->fib_nh[ret];
+
+               if (nh->nh_dev == dev) {
+                       dev_match = true;
+                       break;
+               } else if (l3mdev_master_ifindex_rcu(nh->nh_dev) == dev->ifindex) {
+                       dev_match = true;
+                       break;
+               }
+       }
+#else
+       if (fi->fib_nh[0].nh_dev == dev)
+               dev_match = true;
+#endif
+
+       return dev_match;
+}
+EXPORT_SYMBOL_GPL(fib_info_nh_uses_dev);
+
 /* Given (packet source, input interface) and optional (dst, oif, tos):
  * - (main) check, that source is valid i.e. not broadcast or our local
  *   address.
@@ -361,24 +387,8 @@ static int __fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst,
            (res.type != RTN_LOCAL || !IN_DEV_ACCEPT_LOCAL(idev)))
                goto e_inval;
        fib_combine_itag(itag, &res);
-       dev_match = false;
 
-#ifdef CONFIG_IP_ROUTE_MULTIPATH
-       for (ret = 0; ret < res.fi->fib_nhs; ret++) {
-               struct fib_nh *nh = &res.fi->fib_nh[ret];
-
-               if (nh->nh_dev == dev) {
-                       dev_match = true;
-                       break;
-               } else if (l3mdev_master_ifindex_rcu(nh->nh_dev) == dev->ifindex) {
-                       dev_match = true;
-                       break;
-               }
-       }
-#else
-       if (FIB_RES_DEV(res) == dev)
-               dev_match = true;
-#endif
+       dev_match = fib_info_nh_uses_dev(res.fi, dev);
        if (dev_match) {
                ret = FIB_RES_NH(res).nh_scope >= RT_SCOPE_HOST;
                return ret;
@@ -792,19 +802,115 @@ static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh,
        return err;
 }
 
+int ip_valid_fib_dump_req(struct net *net, const struct nlmsghdr *nlh,
+                         struct fib_dump_filter *filter,
+                         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");
+               return -EINVAL;
+       }
+
+       rtm = nlmsg_data(nlh);
+       if (rtm->rtm_dst_len || rtm->rtm_src_len  || rtm->rtm_tos   ||
+           rtm->rtm_scope) {
+               NL_SET_ERR_MSG(extack, "Invalid values in header for FIB dump request");
+               return -EINVAL;
+       }
+       if (rtm->rtm_flags & ~(RTM_F_CLONED | RTM_F_PREFIX)) {
+               NL_SET_ERR_MSG(extack, "Invalid flags for FIB dump request");
+               return -EINVAL;
+       }
+
+       filter->dump_all_families = (rtm->rtm_family == AF_UNSPEC);
+       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;
+}
+EXPORT_SYMBOL_GPL(ip_valid_fib_dump_req);
+
 static int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
 {
+       const struct nlmsghdr *nlh = cb->nlh;
        struct net *net = sock_net(skb->sk);
+       struct fib_dump_filter filter = {};
        unsigned int h, s_h;
        unsigned int e = 0, s_e;
        struct fib_table *tb;
        struct hlist_head *head;
        int dumped = 0, err;
 
-       if (nlmsg_len(cb->nlh) >= sizeof(struct rtmsg) &&
-           ((struct rtmsg *) nlmsg_data(cb->nlh))->rtm_flags & RTM_F_CLONED)
+       if (cb->strict_check) {
+               err = ip_valid_fib_dump_req(net, nlh, &filter, cb);
+               if (err < 0)
+                       return err;
+       } else if (nlmsg_len(nlh) >= sizeof(struct rtmsg)) {
+               struct rtmsg *rtm = nlmsg_data(nlh);
+
+               filter.flags = rtm->rtm_flags & (RTM_F_PREFIX | RTM_F_CLONED);
+       }
+
+       /* fib entries are never clones and ipv4 does not use prefix flag */
+       if (filter.flags & (RTM_F_PREFIX | RTM_F_CLONED))
                return skb->len;
 
+       if (filter.table_id) {
+               tb = fib_get_table(net, filter.table_id);
+               if (!tb) {
+                       if (filter.dump_all_families)
+                               return skb->len;
+
+                       NL_SET_ERR_MSG(cb->extack, "ipv4: FIB table does not exist");
+                       return -ENOENT;
+               }
+
+               err = fib_table_dump(tb, skb, cb, &filter);
+               return skb->len ? : err;
+       }
+
        s_h = cb->args[0];
        s_e = cb->args[1];
 
@@ -819,7 +925,7 @@ static int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
                        if (dumped)
                                memset(&cb->args[2], 0, sizeof(cb->args) -
                                                 2 * sizeof(cb->args[0]));
-                       err = fib_table_dump(tb, skb, cb);
+                       err = fib_table_dump(tb, skb, cb, &filter);
                        if (err < 0) {
                                if (likely(skb->len))
                                        goto out;