aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2018-10-24 17:06:26 -0400
committerDavid S. Miller <davem@davemloft.net>2018-10-24 17:06:26 -0400
commit559bf69e3c8f8892c3205417c01cdf8d7823d03c (patch)
tree6664d41461603de1ddaf5024da60e3d4cfa2a8b5
parent01aa9d518eae8a4d75cd3049defc6ed0b6d0a658 (diff)
parentc63586dc9b3ed5d45ba82e16bf9e2170a55521e6 (diff)
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>
-rw-r--r--include/net/ip_fib.h1
-rw-r--r--net/core/rtnetlink.c6
-rw-r--r--net/ipv4/devinet.c13
-rw-r--r--net/ipv4/fib_frontend.c4
-rw-r--r--net/ipv4/ipmr.c3
-rw-r--r--net/ipv6/addrconf.c14
-rw-r--r--net/ipv6/ip6_fib.c3
-rw-r--r--net/ipv6/ip6mr.c3
8 files changed, 34 insertions, 13 deletions
diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h
index e8d9456bf36e..c5969762a8f4 100644
--- a/include/net/ip_fib.h
+++ b/include/net/ip_fib.h
@@ -226,6 +226,7 @@ struct fib_dump_filter {
226 u32 table_id; 226 u32 table_id;
227 /* filter_set is an optimization that an entry is set */ 227 /* filter_set is an optimization that an entry is set */
228 bool filter_set; 228 bool filter_set;
229 bool dump_all_families;
229 unsigned char protocol; 230 unsigned char protocol;
230 unsigned char rt_type; 231 unsigned char rt_type;
231 unsigned int flags; 232 unsigned int flags;
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 0958c7be2c22..f679c7a7d761 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -3333,6 +3333,7 @@ static int rtnl_dump_all(struct sk_buff *skb, struct netlink_callback *cb)
3333 int idx; 3333 int idx;
3334 int s_idx = cb->family; 3334 int s_idx = cb->family;
3335 int type = cb->nlh->nlmsg_type - RTM_BASE; 3335 int type = cb->nlh->nlmsg_type - RTM_BASE;
3336 int ret = 0;
3336 3337
3337 if (s_idx == 0) 3338 if (s_idx == 0)
3338 s_idx = 1; 3339 s_idx = 1;
@@ -3365,12 +3366,13 @@ static int rtnl_dump_all(struct sk_buff *skb, struct netlink_callback *cb)
3365 cb->prev_seq = 0; 3366 cb->prev_seq = 0;
3366 cb->seq = 0; 3367 cb->seq = 0;
3367 } 3368 }
3368 if (dumpit(skb, cb)) 3369 ret = dumpit(skb, cb);
3370 if (ret < 0)
3369 break; 3371 break;
3370 } 3372 }
3371 cb->family = idx; 3373 cb->family = idx;
3372 3374
3373 return skb->len; 3375 return skb->len ? : ret;
3374} 3376}
3375 3377
3376struct sk_buff *rtmsg_ifinfo_build_skb(int type, struct net_device *dev, 3378struct sk_buff *rtmsg_ifinfo_build_skb(int type, struct net_device *dev,
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index 63d5b58fbfdb..9250b309c742 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -1761,7 +1761,7 @@ static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
1761 struct net_device *dev; 1761 struct net_device *dev;
1762 struct in_device *in_dev; 1762 struct in_device *in_dev;
1763 struct hlist_head *head; 1763 struct hlist_head *head;
1764 int err; 1764 int err = 0;
1765 1765
1766 s_h = cb->args[0]; 1766 s_h = cb->args[0];
1767 s_idx = idx = cb->args[1]; 1767 s_idx = idx = cb->args[1];
@@ -1771,12 +1771,15 @@ static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
1771 err = inet_valid_dump_ifaddr_req(nlh, &fillargs, &tgt_net, 1771 err = inet_valid_dump_ifaddr_req(nlh, &fillargs, &tgt_net,
1772 skb->sk, cb); 1772 skb->sk, cb);
1773 if (err < 0) 1773 if (err < 0)
1774 return err; 1774 goto put_tgt_net;
1775 1775
1776 err = 0;
1776 if (fillargs.ifindex) { 1777 if (fillargs.ifindex) {
1777 dev = __dev_get_by_index(tgt_net, fillargs.ifindex); 1778 dev = __dev_get_by_index(tgt_net, fillargs.ifindex);
1778 if (!dev) 1779 if (!dev) {
1779 return -ENODEV; 1780 err = -ENODEV;
1781 goto put_tgt_net;
1782 }
1780 1783
1781 in_dev = __in_dev_get_rtnl(dev); 1784 in_dev = __in_dev_get_rtnl(dev);
1782 if (in_dev) { 1785 if (in_dev) {
@@ -1821,7 +1824,7 @@ put_tgt_net:
1821 if (fillargs.netnsid >= 0) 1824 if (fillargs.netnsid >= 0)
1822 put_net(tgt_net); 1825 put_net(tgt_net);
1823 1826
1824 return skb->len; 1827 return err < 0 ? err : skb->len;
1825} 1828}
1826 1829
1827static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh, 1830static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh,
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c
index 5bf653f36911..6df95be96311 100644
--- a/net/ipv4/fib_frontend.c
+++ b/net/ipv4/fib_frontend.c
@@ -829,6 +829,7 @@ int ip_valid_fib_dump_req(struct net *net, const struct nlmsghdr *nlh,
829 return -EINVAL; 829 return -EINVAL;
830 } 830 }
831 831
832 filter->dump_all_families = (rtm->rtm_family == AF_UNSPEC);
832 filter->flags = rtm->rtm_flags; 833 filter->flags = rtm->rtm_flags;
833 filter->protocol = rtm->rtm_protocol; 834 filter->protocol = rtm->rtm_protocol;
834 filter->rt_type = rtm->rtm_type; 835 filter->rt_type = rtm->rtm_type;
@@ -899,6 +900,9 @@ static int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
899 if (filter.table_id) { 900 if (filter.table_id) {
900 tb = fib_get_table(net, filter.table_id); 901 tb = fib_get_table(net, filter.table_id);
901 if (!tb) { 902 if (!tb) {
903 if (filter.dump_all_families)
904 return skb->len;
905
902 NL_SET_ERR_MSG(cb->extack, "ipv4: FIB table does not exist"); 906 NL_SET_ERR_MSG(cb->extack, "ipv4: FIB table does not exist");
903 return -ENOENT; 907 return -ENOENT;
904 } 908 }
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index 7a3e2acda94c..a6defbec4f1b 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -2542,6 +2542,9 @@ static int ipmr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb)
2542 2542
2543 mrt = ipmr_get_table(sock_net(skb->sk), filter.table_id); 2543 mrt = ipmr_get_table(sock_net(skb->sk), filter.table_id);
2544 if (!mrt) { 2544 if (!mrt) {
2545 if (filter.dump_all_families)
2546 return skb->len;
2547
2545 NL_SET_ERR_MSG(cb->extack, "ipv4: MR table does not exist"); 2548 NL_SET_ERR_MSG(cb->extack, "ipv4: MR table does not exist");
2546 return -ENOENT; 2549 return -ENOENT;
2547 } 2550 }
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 45b84dd5c4eb..7eb09c86fa13 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -5089,23 +5089,25 @@ static int inet6_dump_addr(struct sk_buff *skb, struct netlink_callback *cb,
5089 struct net_device *dev; 5089 struct net_device *dev;
5090 struct inet6_dev *idev; 5090 struct inet6_dev *idev;
5091 struct hlist_head *head; 5091 struct hlist_head *head;
5092 int err = 0;
5092 5093
5093 s_h = cb->args[0]; 5094 s_h = cb->args[0];
5094 s_idx = idx = cb->args[1]; 5095 s_idx = idx = cb->args[1];
5095 s_ip_idx = cb->args[2]; 5096 s_ip_idx = cb->args[2];
5096 5097
5097 if (cb->strict_check) { 5098 if (cb->strict_check) {
5098 int err;
5099
5100 err = inet6_valid_dump_ifaddr_req(nlh, &fillargs, &tgt_net, 5099 err = inet6_valid_dump_ifaddr_req(nlh, &fillargs, &tgt_net,
5101 skb->sk, cb); 5100 skb->sk, cb);
5102 if (err < 0) 5101 if (err < 0)
5103 return err; 5102 goto put_tgt_net;
5104 5103
5104 err = 0;
5105 if (fillargs.ifindex) { 5105 if (fillargs.ifindex) {
5106 dev = __dev_get_by_index(tgt_net, fillargs.ifindex); 5106 dev = __dev_get_by_index(tgt_net, fillargs.ifindex);
5107 if (!dev) 5107 if (!dev) {
5108 return -ENODEV; 5108 err = -ENODEV;
5109 goto put_tgt_net;
5110 }
5109 idev = __in6_dev_get(dev); 5111 idev = __in6_dev_get(dev);
5110 if (idev) { 5112 if (idev) {
5111 err = in6_dump_addrs(idev, skb, cb, s_ip_idx, 5113 err = in6_dump_addrs(idev, skb, cb, s_ip_idx,
@@ -5144,7 +5146,7 @@ put_tgt_net:
5144 if (fillargs.netnsid >= 0) 5146 if (fillargs.netnsid >= 0)
5145 put_net(tgt_net); 5147 put_net(tgt_net);
5146 5148
5147 return skb->len; 5149 return err < 0 ? err : skb->len;
5148} 5150}
5149 5151
5150static int inet6_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb) 5152static int inet6_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index 2a058b408a6a..1b8bc008b53b 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -620,6 +620,9 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
620 if (arg.filter.table_id) { 620 if (arg.filter.table_id) {
621 tb = fib6_get_table(net, arg.filter.table_id); 621 tb = fib6_get_table(net, arg.filter.table_id);
622 if (!tb) { 622 if (!tb) {
623 if (arg.filter.dump_all_families)
624 return skb->len;
625
623 NL_SET_ERR_MSG_MOD(cb->extack, "FIB table does not exist"); 626 NL_SET_ERR_MSG_MOD(cb->extack, "FIB table does not exist");
624 return -ENOENT; 627 return -ENOENT;
625 } 628 }
diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
index c3317ffb09eb..e2ea691e42c6 100644
--- a/net/ipv6/ip6mr.c
+++ b/net/ipv6/ip6mr.c
@@ -2473,6 +2473,9 @@ static int ip6mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb)
2473 2473
2474 mrt = ip6mr_get_table(sock_net(skb->sk), filter.table_id); 2474 mrt = ip6mr_get_table(sock_net(skb->sk), filter.table_id);
2475 if (!mrt) { 2475 if (!mrt) {
2476 if (filter.dump_all_families)
2477 return skb->len;
2478
2476 NL_SET_ERR_MSG_MOD(cb->extack, "MR table does not exist"); 2479 NL_SET_ERR_MSG_MOD(cb->extack, "MR table does not exist");
2477 return -ENOENT; 2480 return -ENOENT;
2478 } 2481 }