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) { |
