diff options
Diffstat (limited to 'net/core/rtnetlink.c')
-rw-r--r-- | net/core/rtnetlink.c | 86 |
1 files changed, 65 insertions, 21 deletions
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index d4ff41739b0f..2d8d8fcfa060 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c | |||
@@ -353,15 +353,46 @@ void __rtnl_link_unregister(struct rtnl_link_ops *ops) | |||
353 | } | 353 | } |
354 | EXPORT_SYMBOL_GPL(__rtnl_link_unregister); | 354 | EXPORT_SYMBOL_GPL(__rtnl_link_unregister); |
355 | 355 | ||
356 | /* Return with the rtnl_lock held when there are no network | ||
357 | * devices unregistering in any network namespace. | ||
358 | */ | ||
359 | static void rtnl_lock_unregistering_all(void) | ||
360 | { | ||
361 | struct net *net; | ||
362 | bool unregistering; | ||
363 | DEFINE_WAIT(wait); | ||
364 | |||
365 | for (;;) { | ||
366 | prepare_to_wait(&netdev_unregistering_wq, &wait, | ||
367 | TASK_UNINTERRUPTIBLE); | ||
368 | unregistering = false; | ||
369 | rtnl_lock(); | ||
370 | for_each_net(net) { | ||
371 | if (net->dev_unreg_count > 0) { | ||
372 | unregistering = true; | ||
373 | break; | ||
374 | } | ||
375 | } | ||
376 | if (!unregistering) | ||
377 | break; | ||
378 | __rtnl_unlock(); | ||
379 | schedule(); | ||
380 | } | ||
381 | finish_wait(&netdev_unregistering_wq, &wait); | ||
382 | } | ||
383 | |||
356 | /** | 384 | /** |
357 | * rtnl_link_unregister - Unregister rtnl_link_ops from rtnetlink. | 385 | * rtnl_link_unregister - Unregister rtnl_link_ops from rtnetlink. |
358 | * @ops: struct rtnl_link_ops * to unregister | 386 | * @ops: struct rtnl_link_ops * to unregister |
359 | */ | 387 | */ |
360 | void rtnl_link_unregister(struct rtnl_link_ops *ops) | 388 | void rtnl_link_unregister(struct rtnl_link_ops *ops) |
361 | { | 389 | { |
362 | rtnl_lock(); | 390 | /* Close the race with cleanup_net() */ |
391 | mutex_lock(&net_mutex); | ||
392 | rtnl_lock_unregistering_all(); | ||
363 | __rtnl_link_unregister(ops); | 393 | __rtnl_link_unregister(ops); |
364 | rtnl_unlock(); | 394 | rtnl_unlock(); |
395 | mutex_unlock(&net_mutex); | ||
365 | } | 396 | } |
366 | EXPORT_SYMBOL_GPL(rtnl_link_unregister); | 397 | EXPORT_SYMBOL_GPL(rtnl_link_unregister); |
367 | 398 | ||
@@ -774,7 +805,8 @@ static inline int rtnl_vfinfo_size(const struct net_device *dev, | |||
774 | return 0; | 805 | return 0; |
775 | } | 806 | } |
776 | 807 | ||
777 | static size_t rtnl_port_size(const struct net_device *dev) | 808 | static size_t rtnl_port_size(const struct net_device *dev, |
809 | u32 ext_filter_mask) | ||
778 | { | 810 | { |
779 | size_t port_size = nla_total_size(4) /* PORT_VF */ | 811 | size_t port_size = nla_total_size(4) /* PORT_VF */ |
780 | + nla_total_size(PORT_PROFILE_MAX) /* PORT_PROFILE */ | 812 | + nla_total_size(PORT_PROFILE_MAX) /* PORT_PROFILE */ |
@@ -790,7 +822,8 @@ static size_t rtnl_port_size(const struct net_device *dev) | |||
790 | size_t port_self_size = nla_total_size(sizeof(struct nlattr)) | 822 | size_t port_self_size = nla_total_size(sizeof(struct nlattr)) |
791 | + port_size; | 823 | + port_size; |
792 | 824 | ||
793 | if (!dev->netdev_ops->ndo_get_vf_port || !dev->dev.parent) | 825 | if (!dev->netdev_ops->ndo_get_vf_port || !dev->dev.parent || |
826 | !(ext_filter_mask & RTEXT_FILTER_VF)) | ||
794 | return 0; | 827 | return 0; |
795 | if (dev_num_vf(dev->dev.parent)) | 828 | if (dev_num_vf(dev->dev.parent)) |
796 | return port_self_size + vf_ports_size + | 829 | return port_self_size + vf_ports_size + |
@@ -826,7 +859,7 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev, | |||
826 | + nla_total_size(ext_filter_mask | 859 | + nla_total_size(ext_filter_mask |
827 | & RTEXT_FILTER_VF ? 4 : 0) /* IFLA_NUM_VF */ | 860 | & RTEXT_FILTER_VF ? 4 : 0) /* IFLA_NUM_VF */ |
828 | + rtnl_vfinfo_size(dev, ext_filter_mask) /* IFLA_VFINFO_LIST */ | 861 | + rtnl_vfinfo_size(dev, ext_filter_mask) /* IFLA_VFINFO_LIST */ |
829 | + rtnl_port_size(dev) /* IFLA_VF_PORTS + IFLA_PORT_SELF */ | 862 | + rtnl_port_size(dev, ext_filter_mask) /* IFLA_VF_PORTS + IFLA_PORT_SELF */ |
830 | + rtnl_link_get_size(dev) /* IFLA_LINKINFO */ | 863 | + rtnl_link_get_size(dev) /* IFLA_LINKINFO */ |
831 | + rtnl_link_get_af_size(dev) /* IFLA_AF_SPEC */ | 864 | + rtnl_link_get_af_size(dev) /* IFLA_AF_SPEC */ |
832 | + nla_total_size(MAX_PHYS_PORT_ID_LEN); /* IFLA_PHYS_PORT_ID */ | 865 | + nla_total_size(MAX_PHYS_PORT_ID_LEN); /* IFLA_PHYS_PORT_ID */ |
@@ -888,11 +921,13 @@ static int rtnl_port_self_fill(struct sk_buff *skb, struct net_device *dev) | |||
888 | return 0; | 921 | return 0; |
889 | } | 922 | } |
890 | 923 | ||
891 | static int rtnl_port_fill(struct sk_buff *skb, struct net_device *dev) | 924 | static int rtnl_port_fill(struct sk_buff *skb, struct net_device *dev, |
925 | u32 ext_filter_mask) | ||
892 | { | 926 | { |
893 | int err; | 927 | int err; |
894 | 928 | ||
895 | if (!dev->netdev_ops->ndo_get_vf_port || !dev->dev.parent) | 929 | if (!dev->netdev_ops->ndo_get_vf_port || !dev->dev.parent || |
930 | !(ext_filter_mask & RTEXT_FILTER_VF)) | ||
896 | return 0; | 931 | return 0; |
897 | 932 | ||
898 | err = rtnl_port_self_fill(skb, dev); | 933 | err = rtnl_port_self_fill(skb, dev); |
@@ -1079,7 +1114,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, | |||
1079 | nla_nest_end(skb, vfinfo); | 1114 | nla_nest_end(skb, vfinfo); |
1080 | } | 1115 | } |
1081 | 1116 | ||
1082 | if (rtnl_port_fill(skb, dev)) | 1117 | if (rtnl_port_fill(skb, dev, ext_filter_mask)) |
1083 | goto nla_put_failure; | 1118 | goto nla_put_failure; |
1084 | 1119 | ||
1085 | if (dev->rtnl_link_ops || rtnl_have_link_slave_info(dev)) { | 1120 | if (dev->rtnl_link_ops || rtnl_have_link_slave_info(dev)) { |
@@ -1198,6 +1233,7 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) | |||
1198 | struct hlist_head *head; | 1233 | struct hlist_head *head; |
1199 | struct nlattr *tb[IFLA_MAX+1]; | 1234 | struct nlattr *tb[IFLA_MAX+1]; |
1200 | u32 ext_filter_mask = 0; | 1235 | u32 ext_filter_mask = 0; |
1236 | int err; | ||
1201 | 1237 | ||
1202 | s_h = cb->args[0]; | 1238 | s_h = cb->args[0]; |
1203 | s_idx = cb->args[1]; | 1239 | s_idx = cb->args[1]; |
@@ -1218,11 +1254,17 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) | |||
1218 | hlist_for_each_entry_rcu(dev, head, index_hlist) { | 1254 | hlist_for_each_entry_rcu(dev, head, index_hlist) { |
1219 | if (idx < s_idx) | 1255 | if (idx < s_idx) |
1220 | goto cont; | 1256 | goto cont; |
1221 | if (rtnl_fill_ifinfo(skb, dev, RTM_NEWLINK, | 1257 | err = rtnl_fill_ifinfo(skb, dev, RTM_NEWLINK, |
1222 | NETLINK_CB(cb->skb).portid, | 1258 | NETLINK_CB(cb->skb).portid, |
1223 | cb->nlh->nlmsg_seq, 0, | 1259 | cb->nlh->nlmsg_seq, 0, |
1224 | NLM_F_MULTI, | 1260 | NLM_F_MULTI, |
1225 | ext_filter_mask) <= 0) | 1261 | ext_filter_mask); |
1262 | /* If we ran out of room on the first message, | ||
1263 | * we're in trouble | ||
1264 | */ | ||
1265 | WARN_ON((err == -EMSGSIZE) && (skb->len == 0)); | ||
1266 | |||
1267 | if (err <= 0) | ||
1226 | goto out; | 1268 | goto out; |
1227 | 1269 | ||
1228 | nl_dump_check_consistent(cb, nlmsg_hdr(skb)); | 1270 | nl_dump_check_consistent(cb, nlmsg_hdr(skb)); |
@@ -1395,7 +1437,8 @@ static int do_set_master(struct net_device *dev, int ifindex) | |||
1395 | return 0; | 1437 | return 0; |
1396 | } | 1438 | } |
1397 | 1439 | ||
1398 | static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm, | 1440 | static int do_setlink(const struct sk_buff *skb, |
1441 | struct net_device *dev, struct ifinfomsg *ifm, | ||
1399 | struct nlattr **tb, char *ifname, int modified) | 1442 | struct nlattr **tb, char *ifname, int modified) |
1400 | { | 1443 | { |
1401 | const struct net_device_ops *ops = dev->netdev_ops; | 1444 | const struct net_device_ops *ops = dev->netdev_ops; |
@@ -1407,7 +1450,7 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm, | |||
1407 | err = PTR_ERR(net); | 1450 | err = PTR_ERR(net); |
1408 | goto errout; | 1451 | goto errout; |
1409 | } | 1452 | } |
1410 | if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) { | 1453 | if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) { |
1411 | err = -EPERM; | 1454 | err = -EPERM; |
1412 | goto errout; | 1455 | goto errout; |
1413 | } | 1456 | } |
@@ -1661,7 +1704,7 @@ static int rtnl_setlink(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
1661 | if (err < 0) | 1704 | if (err < 0) |
1662 | goto errout; | 1705 | goto errout; |
1663 | 1706 | ||
1664 | err = do_setlink(dev, ifm, tb, ifname, 0); | 1707 | err = do_setlink(skb, dev, ifm, tb, ifname, 0); |
1665 | errout: | 1708 | errout: |
1666 | return err; | 1709 | return err; |
1667 | } | 1710 | } |
@@ -1778,7 +1821,8 @@ err: | |||
1778 | } | 1821 | } |
1779 | EXPORT_SYMBOL(rtnl_create_link); | 1822 | EXPORT_SYMBOL(rtnl_create_link); |
1780 | 1823 | ||
1781 | static int rtnl_group_changelink(struct net *net, int group, | 1824 | static int rtnl_group_changelink(const struct sk_buff *skb, |
1825 | struct net *net, int group, | ||
1782 | struct ifinfomsg *ifm, | 1826 | struct ifinfomsg *ifm, |
1783 | struct nlattr **tb) | 1827 | struct nlattr **tb) |
1784 | { | 1828 | { |
@@ -1787,7 +1831,7 @@ static int rtnl_group_changelink(struct net *net, int group, | |||
1787 | 1831 | ||
1788 | for_each_netdev(net, dev) { | 1832 | for_each_netdev(net, dev) { |
1789 | if (dev->group == group) { | 1833 | if (dev->group == group) { |
1790 | err = do_setlink(dev, ifm, tb, NULL, 0); | 1834 | err = do_setlink(skb, dev, ifm, tb, NULL, 0); |
1791 | if (err < 0) | 1835 | if (err < 0) |
1792 | return err; | 1836 | return err; |
1793 | } | 1837 | } |
@@ -1929,12 +1973,12 @@ replay: | |||
1929 | modified = 1; | 1973 | modified = 1; |
1930 | } | 1974 | } |
1931 | 1975 | ||
1932 | return do_setlink(dev, ifm, tb, ifname, modified); | 1976 | return do_setlink(skb, dev, ifm, tb, ifname, modified); |
1933 | } | 1977 | } |
1934 | 1978 | ||
1935 | if (!(nlh->nlmsg_flags & NLM_F_CREATE)) { | 1979 | if (!(nlh->nlmsg_flags & NLM_F_CREATE)) { |
1936 | if (ifm->ifi_index == 0 && tb[IFLA_GROUP]) | 1980 | if (ifm->ifi_index == 0 && tb[IFLA_GROUP]) |
1937 | return rtnl_group_changelink(net, | 1981 | return rtnl_group_changelink(skb, net, |
1938 | nla_get_u32(tb[IFLA_GROUP]), | 1982 | nla_get_u32(tb[IFLA_GROUP]), |
1939 | ifm, tb); | 1983 | ifm, tb); |
1940 | return -ENODEV; | 1984 | return -ENODEV; |
@@ -2321,7 +2365,7 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
2321 | int err = -EINVAL; | 2365 | int err = -EINVAL; |
2322 | __u8 *addr; | 2366 | __u8 *addr; |
2323 | 2367 | ||
2324 | if (!capable(CAP_NET_ADMIN)) | 2368 | if (!netlink_capable(skb, CAP_NET_ADMIN)) |
2325 | return -EPERM; | 2369 | return -EPERM; |
2326 | 2370 | ||
2327 | err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL); | 2371 | err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL); |
@@ -2773,7 +2817,7 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
2773 | sz_idx = type>>2; | 2817 | sz_idx = type>>2; |
2774 | kind = type&3; | 2818 | kind = type&3; |
2775 | 2819 | ||
2776 | if (kind != 2 && !ns_capable(net->user_ns, CAP_NET_ADMIN)) | 2820 | if (kind != 2 && !netlink_net_capable(skb, CAP_NET_ADMIN)) |
2777 | return -EPERM; | 2821 | return -EPERM; |
2778 | 2822 | ||
2779 | if (kind == 2 && nlh->nlmsg_flags&NLM_F_DUMP) { | 2823 | if (kind == 2 && nlh->nlmsg_flags&NLM_F_DUMP) { |