Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branch 'route-dump-filter-fixes'

David Ahern says:

====================
net: Fixups for recent dump filtering changes

Li RongQing noted that tgt_net is leaked in ipv4 due to the recent change
to handle address dumps for a specific device. The report also applies to
ipv6 and other error paths. Patches 1 and 2 fix those leaks.

Patch 3 stops route dumps from erroring out when dumping across address
families and a table id is given. This is needed in preparation for
patch 4.

Patch 4 updates the rtnl_dump_all to handle a failure in one of the dumpit
functions. At the moment, if an address dump returns an error the dump all
loop breaks but the error is dropped. The result can be no data is returned
and no error either leaving the user wondering about the addresses.

Patches were tested with a modified iproute2 to add invalid data to the
dump request causing each specific failure path to be hit in addition
to positive testing that it works as it should when given valid data.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>

+34 -13
+1
include/net/ip_fib.h
··· 226 226 u32 table_id; 227 227 /* filter_set is an optimization that an entry is set */ 228 228 bool filter_set; 229 + bool dump_all_families; 229 230 unsigned char protocol; 230 231 unsigned char rt_type; 231 232 unsigned int flags;
+4 -2
net/core/rtnetlink.c
··· 3333 3333 int idx; 3334 3334 int s_idx = cb->family; 3335 3335 int type = cb->nlh->nlmsg_type - RTM_BASE; 3336 + int ret = 0; 3336 3337 3337 3338 if (s_idx == 0) 3338 3339 s_idx = 1; ··· 3366 3365 cb->prev_seq = 0; 3367 3366 cb->seq = 0; 3368 3367 } 3369 - if (dumpit(skb, cb)) 3368 + ret = dumpit(skb, cb); 3369 + if (ret < 0) 3370 3370 break; 3371 3371 } 3372 3372 cb->family = idx; 3373 3373 3374 - return skb->len; 3374 + return skb->len ? : ret; 3375 3375 } 3376 3376 3377 3377 struct sk_buff *rtmsg_ifinfo_build_skb(int type, struct net_device *dev,
+8 -5
net/ipv4/devinet.c
··· 1761 1761 struct net_device *dev; 1762 1762 struct in_device *in_dev; 1763 1763 struct hlist_head *head; 1764 - int err; 1764 + int err = 0; 1765 1765 1766 1766 s_h = cb->args[0]; 1767 1767 s_idx = idx = cb->args[1]; ··· 1771 1771 err = inet_valid_dump_ifaddr_req(nlh, &fillargs, &tgt_net, 1772 1772 skb->sk, cb); 1773 1773 if (err < 0) 1774 - return err; 1774 + goto put_tgt_net; 1775 1775 1776 + err = 0; 1776 1777 if (fillargs.ifindex) { 1777 1778 dev = __dev_get_by_index(tgt_net, fillargs.ifindex); 1778 - if (!dev) 1779 - return -ENODEV; 1779 + if (!dev) { 1780 + err = -ENODEV; 1781 + goto put_tgt_net; 1782 + } 1780 1783 1781 1784 in_dev = __in_dev_get_rtnl(dev); 1782 1785 if (in_dev) { ··· 1824 1821 if (fillargs.netnsid >= 0) 1825 1822 put_net(tgt_net); 1826 1823 1827 - return skb->len; 1824 + return err < 0 ? err : skb->len; 1828 1825 } 1829 1826 1830 1827 static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh,
+4
net/ipv4/fib_frontend.c
··· 829 829 return -EINVAL; 830 830 } 831 831 832 + filter->dump_all_families = (rtm->rtm_family == AF_UNSPEC); 832 833 filter->flags = rtm->rtm_flags; 833 834 filter->protocol = rtm->rtm_protocol; 834 835 filter->rt_type = rtm->rtm_type; ··· 900 899 if (filter.table_id) { 901 900 tb = fib_get_table(net, filter.table_id); 902 901 if (!tb) { 902 + if (filter.dump_all_families) 903 + return skb->len; 904 + 903 905 NL_SET_ERR_MSG(cb->extack, "ipv4: FIB table does not exist"); 904 906 return -ENOENT; 905 907 }
+3
net/ipv4/ipmr.c
··· 2542 2542 2543 2543 mrt = ipmr_get_table(sock_net(skb->sk), filter.table_id); 2544 2544 if (!mrt) { 2545 + if (filter.dump_all_families) 2546 + return skb->len; 2547 + 2545 2548 NL_SET_ERR_MSG(cb->extack, "ipv4: MR table does not exist"); 2546 2549 return -ENOENT; 2547 2550 }
+8 -6
net/ipv6/addrconf.c
··· 5089 5089 struct net_device *dev; 5090 5090 struct inet6_dev *idev; 5091 5091 struct hlist_head *head; 5092 + int err = 0; 5092 5093 5093 5094 s_h = cb->args[0]; 5094 5095 s_idx = idx = cb->args[1]; 5095 5096 s_ip_idx = cb->args[2]; 5096 5097 5097 5098 if (cb->strict_check) { 5098 - int err; 5099 - 5100 5099 err = inet6_valid_dump_ifaddr_req(nlh, &fillargs, &tgt_net, 5101 5100 skb->sk, cb); 5102 5101 if (err < 0) 5103 - return err; 5102 + goto put_tgt_net; 5104 5103 5104 + err = 0; 5105 5105 if (fillargs.ifindex) { 5106 5106 dev = __dev_get_by_index(tgt_net, fillargs.ifindex); 5107 - if (!dev) 5108 - return -ENODEV; 5107 + if (!dev) { 5108 + err = -ENODEV; 5109 + goto put_tgt_net; 5110 + } 5109 5111 idev = __in6_dev_get(dev); 5110 5112 if (idev) { 5111 5113 err = in6_dump_addrs(idev, skb, cb, s_ip_idx, ··· 5146 5144 if (fillargs.netnsid >= 0) 5147 5145 put_net(tgt_net); 5148 5146 5149 - return skb->len; 5147 + return err < 0 ? err : skb->len; 5150 5148 } 5151 5149 5152 5150 static int inet6_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
+3
net/ipv6/ip6_fib.c
··· 620 620 if (arg.filter.table_id) { 621 621 tb = fib6_get_table(net, arg.filter.table_id); 622 622 if (!tb) { 623 + if (arg.filter.dump_all_families) 624 + return skb->len; 625 + 623 626 NL_SET_ERR_MSG_MOD(cb->extack, "FIB table does not exist"); 624 627 return -ENOENT; 625 628 }
+3
net/ipv6/ip6mr.c
··· 2473 2473 2474 2474 mrt = ip6mr_get_table(sock_net(skb->sk), filter.table_id); 2475 2475 if (!mrt) { 2476 + if (filter.dump_all_families) 2477 + return skb->len; 2478 + 2476 2479 NL_SET_ERR_MSG_MOD(cb->extack, "MR table does not exist"); 2477 2480 return -ENOENT; 2478 2481 }