diff options
Diffstat (limited to 'net/core/rtnetlink.c')
-rw-r--r-- | net/core/rtnetlink.c | 103 |
1 files changed, 85 insertions, 18 deletions
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index de24d394c69e..8a8c51937edf 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c | |||
@@ -921,7 +921,8 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev, | |||
921 | + nla_total_size(4) /* IFLA_EVENT */ | 921 | + nla_total_size(4) /* IFLA_EVENT */ |
922 | + nla_total_size(4) /* IFLA_NEW_NETNSID */ | 922 | + nla_total_size(4) /* IFLA_NEW_NETNSID */ |
923 | + nla_total_size(1); /* IFLA_PROTO_DOWN */ | 923 | + nla_total_size(1); /* IFLA_PROTO_DOWN */ |
924 | 924 | + nla_total_size(4) /* IFLA_IF_NETNSID */ | |
925 | + 0; | ||
925 | } | 926 | } |
926 | 927 | ||
927 | static int rtnl_vf_ports_fill(struct sk_buff *skb, struct net_device *dev) | 928 | static int rtnl_vf_ports_fill(struct sk_buff *skb, struct net_device *dev) |
@@ -1370,13 +1371,14 @@ static noinline_for_stack int nla_put_ifalias(struct sk_buff *skb, | |||
1370 | } | 1371 | } |
1371 | 1372 | ||
1372 | static int rtnl_fill_link_netnsid(struct sk_buff *skb, | 1373 | static int rtnl_fill_link_netnsid(struct sk_buff *skb, |
1373 | const struct net_device *dev) | 1374 | const struct net_device *dev, |
1375 | struct net *src_net) | ||
1374 | { | 1376 | { |
1375 | if (dev->rtnl_link_ops && dev->rtnl_link_ops->get_link_net) { | 1377 | if (dev->rtnl_link_ops && dev->rtnl_link_ops->get_link_net) { |
1376 | struct net *link_net = dev->rtnl_link_ops->get_link_net(dev); | 1378 | struct net *link_net = dev->rtnl_link_ops->get_link_net(dev); |
1377 | 1379 | ||
1378 | if (!net_eq(dev_net(dev), link_net)) { | 1380 | if (!net_eq(dev_net(dev), link_net)) { |
1379 | int id = peernet2id_alloc(dev_net(dev), link_net); | 1381 | int id = peernet2id_alloc(src_net, link_net); |
1380 | 1382 | ||
1381 | if (nla_put_s32(skb, IFLA_LINK_NETNSID, id)) | 1383 | if (nla_put_s32(skb, IFLA_LINK_NETNSID, id)) |
1382 | return -EMSGSIZE; | 1384 | return -EMSGSIZE; |
@@ -1427,10 +1429,11 @@ static int rtnl_fill_link_af(struct sk_buff *skb, | |||
1427 | return 0; | 1429 | return 0; |
1428 | } | 1430 | } |
1429 | 1431 | ||
1430 | static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, | 1432 | static int rtnl_fill_ifinfo(struct sk_buff *skb, |
1433 | struct net_device *dev, struct net *src_net, | ||
1431 | int type, u32 pid, u32 seq, u32 change, | 1434 | int type, u32 pid, u32 seq, u32 change, |
1432 | unsigned int flags, u32 ext_filter_mask, | 1435 | unsigned int flags, u32 ext_filter_mask, |
1433 | u32 event, int *new_nsid) | 1436 | u32 event, int *new_nsid, int tgt_netnsid) |
1434 | { | 1437 | { |
1435 | struct ifinfomsg *ifm; | 1438 | struct ifinfomsg *ifm; |
1436 | struct nlmsghdr *nlh; | 1439 | struct nlmsghdr *nlh; |
@@ -1448,6 +1451,9 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, | |||
1448 | ifm->ifi_flags = dev_get_flags(dev); | 1451 | ifm->ifi_flags = dev_get_flags(dev); |
1449 | ifm->ifi_change = change; | 1452 | ifm->ifi_change = change; |
1450 | 1453 | ||
1454 | if (tgt_netnsid >= 0 && nla_put_s32(skb, IFLA_IF_NETNSID, tgt_netnsid)) | ||
1455 | goto nla_put_failure; | ||
1456 | |||
1451 | if (nla_put_string(skb, IFLA_IFNAME, dev->name) || | 1457 | if (nla_put_string(skb, IFLA_IFNAME, dev->name) || |
1452 | nla_put_u32(skb, IFLA_TXQLEN, dev->tx_queue_len) || | 1458 | nla_put_u32(skb, IFLA_TXQLEN, dev->tx_queue_len) || |
1453 | nla_put_u8(skb, IFLA_OPERSTATE, | 1459 | nla_put_u8(skb, IFLA_OPERSTATE, |
@@ -1513,7 +1519,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, | |||
1513 | goto nla_put_failure; | 1519 | goto nla_put_failure; |
1514 | } | 1520 | } |
1515 | 1521 | ||
1516 | if (rtnl_fill_link_netnsid(skb, dev)) | 1522 | if (rtnl_fill_link_netnsid(skb, dev, src_net)) |
1517 | goto nla_put_failure; | 1523 | goto nla_put_failure; |
1518 | 1524 | ||
1519 | if (new_nsid && | 1525 | if (new_nsid && |
@@ -1571,6 +1577,7 @@ static const struct nla_policy ifla_policy[IFLA_MAX+1] = { | |||
1571 | [IFLA_XDP] = { .type = NLA_NESTED }, | 1577 | [IFLA_XDP] = { .type = NLA_NESTED }, |
1572 | [IFLA_EVENT] = { .type = NLA_U32 }, | 1578 | [IFLA_EVENT] = { .type = NLA_U32 }, |
1573 | [IFLA_GROUP] = { .type = NLA_U32 }, | 1579 | [IFLA_GROUP] = { .type = NLA_U32 }, |
1580 | [IFLA_IF_NETNSID] = { .type = NLA_S32 }, | ||
1574 | }; | 1581 | }; |
1575 | 1582 | ||
1576 | static const struct nla_policy ifla_info_policy[IFLA_INFO_MAX+1] = { | 1583 | static const struct nla_policy ifla_info_policy[IFLA_INFO_MAX+1] = { |
@@ -1674,9 +1681,28 @@ static bool link_dump_filtered(struct net_device *dev, | |||
1674 | return false; | 1681 | return false; |
1675 | } | 1682 | } |
1676 | 1683 | ||
1684 | static struct net *get_target_net(struct sk_buff *skb, int netnsid) | ||
1685 | { | ||
1686 | struct net *net; | ||
1687 | |||
1688 | net = get_net_ns_by_id(sock_net(skb->sk), netnsid); | ||
1689 | if (!net) | ||
1690 | return ERR_PTR(-EINVAL); | ||
1691 | |||
1692 | /* For now, the caller is required to have CAP_NET_ADMIN in | ||
1693 | * the user namespace owning the target net ns. | ||
1694 | */ | ||
1695 | if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) { | ||
1696 | put_net(net); | ||
1697 | return ERR_PTR(-EACCES); | ||
1698 | } | ||
1699 | return net; | ||
1700 | } | ||
1701 | |||
1677 | static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) | 1702 | static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) |
1678 | { | 1703 | { |
1679 | struct net *net = sock_net(skb->sk); | 1704 | struct net *net = sock_net(skb->sk); |
1705 | struct net *tgt_net = net; | ||
1680 | int h, s_h; | 1706 | int h, s_h; |
1681 | int idx = 0, s_idx; | 1707 | int idx = 0, s_idx; |
1682 | struct net_device *dev; | 1708 | struct net_device *dev; |
@@ -1686,6 +1712,7 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) | |||
1686 | const struct rtnl_link_ops *kind_ops = NULL; | 1712 | const struct rtnl_link_ops *kind_ops = NULL; |
1687 | unsigned int flags = NLM_F_MULTI; | 1713 | unsigned int flags = NLM_F_MULTI; |
1688 | int master_idx = 0; | 1714 | int master_idx = 0; |
1715 | int netnsid = -1; | ||
1689 | int err; | 1716 | int err; |
1690 | int hdrlen; | 1717 | int hdrlen; |
1691 | 1718 | ||
@@ -1704,6 +1731,15 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) | |||
1704 | 1731 | ||
1705 | if (nlmsg_parse(cb->nlh, hdrlen, tb, IFLA_MAX, | 1732 | if (nlmsg_parse(cb->nlh, hdrlen, tb, IFLA_MAX, |
1706 | ifla_policy, NULL) >= 0) { | 1733 | ifla_policy, NULL) >= 0) { |
1734 | if (tb[IFLA_IF_NETNSID]) { | ||
1735 | netnsid = nla_get_s32(tb[IFLA_IF_NETNSID]); | ||
1736 | tgt_net = get_target_net(skb, netnsid); | ||
1737 | if (IS_ERR(tgt_net)) { | ||
1738 | tgt_net = net; | ||
1739 | netnsid = -1; | ||
1740 | } | ||
1741 | } | ||
1742 | |||
1707 | if (tb[IFLA_EXT_MASK]) | 1743 | if (tb[IFLA_EXT_MASK]) |
1708 | ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]); | 1744 | ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]); |
1709 | 1745 | ||
@@ -1719,17 +1755,19 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) | |||
1719 | 1755 | ||
1720 | for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { | 1756 | for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { |
1721 | idx = 0; | 1757 | idx = 0; |
1722 | head = &net->dev_index_head[h]; | 1758 | head = &tgt_net->dev_index_head[h]; |
1723 | hlist_for_each_entry(dev, head, index_hlist) { | 1759 | hlist_for_each_entry(dev, head, index_hlist) { |
1724 | if (link_dump_filtered(dev, master_idx, kind_ops)) | 1760 | if (link_dump_filtered(dev, master_idx, kind_ops)) |
1725 | goto cont; | 1761 | goto cont; |
1726 | if (idx < s_idx) | 1762 | if (idx < s_idx) |
1727 | goto cont; | 1763 | goto cont; |
1728 | err = rtnl_fill_ifinfo(skb, dev, RTM_NEWLINK, | 1764 | err = rtnl_fill_ifinfo(skb, dev, net, |
1765 | RTM_NEWLINK, | ||
1729 | NETLINK_CB(cb->skb).portid, | 1766 | NETLINK_CB(cb->skb).portid, |
1730 | cb->nlh->nlmsg_seq, 0, | 1767 | cb->nlh->nlmsg_seq, 0, |
1731 | flags, | 1768 | flags, |
1732 | ext_filter_mask, 0, NULL); | 1769 | ext_filter_mask, 0, NULL, |
1770 | netnsid); | ||
1733 | 1771 | ||
1734 | if (err < 0) { | 1772 | if (err < 0) { |
1735 | if (likely(skb->len)) | 1773 | if (likely(skb->len)) |
@@ -1748,6 +1786,8 @@ out_err: | |||
1748 | cb->args[0] = h; | 1786 | cb->args[0] = h; |
1749 | cb->seq = net->dev_base_seq; | 1787 | cb->seq = net->dev_base_seq; |
1750 | nl_dump_check_consistent(cb, nlmsg_hdr(skb)); | 1788 | nl_dump_check_consistent(cb, nlmsg_hdr(skb)); |
1789 | if (netnsid >= 0) | ||
1790 | put_net(tgt_net); | ||
1751 | 1791 | ||
1752 | return err; | 1792 | return err; |
1753 | } | 1793 | } |
@@ -2360,6 +2400,9 @@ static int rtnl_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, | |||
2360 | if (err < 0) | 2400 | if (err < 0) |
2361 | goto errout; | 2401 | goto errout; |
2362 | 2402 | ||
2403 | if (tb[IFLA_IF_NETNSID]) | ||
2404 | return -EOPNOTSUPP; | ||
2405 | |||
2363 | if (tb[IFLA_IFNAME]) | 2406 | if (tb[IFLA_IFNAME]) |
2364 | nla_strlcpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ); | 2407 | nla_strlcpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ); |
2365 | else | 2408 | else |
@@ -2454,6 +2497,9 @@ static int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh, | |||
2454 | if (err < 0) | 2497 | if (err < 0) |
2455 | return err; | 2498 | return err; |
2456 | 2499 | ||
2500 | if (tb[IFLA_IF_NETNSID]) | ||
2501 | return -EOPNOTSUPP; | ||
2502 | |||
2457 | if (tb[IFLA_IFNAME]) | 2503 | if (tb[IFLA_IFNAME]) |
2458 | nla_strlcpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ); | 2504 | nla_strlcpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ); |
2459 | 2505 | ||
@@ -2585,6 +2631,9 @@ replay: | |||
2585 | if (err < 0) | 2631 | if (err < 0) |
2586 | return err; | 2632 | return err; |
2587 | 2633 | ||
2634 | if (tb[IFLA_IF_NETNSID]) | ||
2635 | return -EOPNOTSUPP; | ||
2636 | |||
2588 | if (tb[IFLA_IFNAME]) | 2637 | if (tb[IFLA_IFNAME]) |
2589 | nla_strlcpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ); | 2638 | nla_strlcpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ); |
2590 | else | 2639 | else |
@@ -2818,11 +2867,13 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr *nlh, | |||
2818 | struct netlink_ext_ack *extack) | 2867 | struct netlink_ext_ack *extack) |
2819 | { | 2868 | { |
2820 | struct net *net = sock_net(skb->sk); | 2869 | struct net *net = sock_net(skb->sk); |
2870 | struct net *tgt_net = net; | ||
2821 | struct ifinfomsg *ifm; | 2871 | struct ifinfomsg *ifm; |
2822 | char ifname[IFNAMSIZ]; | 2872 | char ifname[IFNAMSIZ]; |
2823 | struct nlattr *tb[IFLA_MAX+1]; | 2873 | struct nlattr *tb[IFLA_MAX+1]; |
2824 | struct net_device *dev = NULL; | 2874 | struct net_device *dev = NULL; |
2825 | struct sk_buff *nskb; | 2875 | struct sk_buff *nskb; |
2876 | int netnsid = -1; | ||
2826 | int err; | 2877 | int err; |
2827 | u32 ext_filter_mask = 0; | 2878 | u32 ext_filter_mask = 0; |
2828 | 2879 | ||
@@ -2830,35 +2881,50 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr *nlh, | |||
2830 | if (err < 0) | 2881 | if (err < 0) |
2831 | return err; | 2882 | return err; |
2832 | 2883 | ||
2884 | if (tb[IFLA_IF_NETNSID]) { | ||
2885 | netnsid = nla_get_s32(tb[IFLA_IF_NETNSID]); | ||
2886 | tgt_net = get_target_net(skb, netnsid); | ||
2887 | if (IS_ERR(tgt_net)) | ||
2888 | return PTR_ERR(tgt_net); | ||
2889 | } | ||
2890 | |||
2833 | if (tb[IFLA_IFNAME]) | 2891 | if (tb[IFLA_IFNAME]) |
2834 | nla_strlcpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ); | 2892 | nla_strlcpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ); |
2835 | 2893 | ||
2836 | if (tb[IFLA_EXT_MASK]) | 2894 | if (tb[IFLA_EXT_MASK]) |
2837 | ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]); | 2895 | ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]); |
2838 | 2896 | ||
2897 | err = -EINVAL; | ||
2839 | ifm = nlmsg_data(nlh); | 2898 | ifm = nlmsg_data(nlh); |
2840 | if (ifm->ifi_index > 0) | 2899 | if (ifm->ifi_index > 0) |
2841 | dev = __dev_get_by_index(net, ifm->ifi_index); | 2900 | dev = __dev_get_by_index(tgt_net, ifm->ifi_index); |
2842 | else if (tb[IFLA_IFNAME]) | 2901 | else if (tb[IFLA_IFNAME]) |
2843 | dev = __dev_get_by_name(net, ifname); | 2902 | dev = __dev_get_by_name(tgt_net, ifname); |
2844 | else | 2903 | else |
2845 | return -EINVAL; | 2904 | goto out; |
2846 | 2905 | ||
2906 | err = -ENODEV; | ||
2847 | if (dev == NULL) | 2907 | if (dev == NULL) |
2848 | return -ENODEV; | 2908 | goto out; |
2849 | 2909 | ||
2910 | err = -ENOBUFS; | ||
2850 | nskb = nlmsg_new(if_nlmsg_size(dev, ext_filter_mask), GFP_KERNEL); | 2911 | nskb = nlmsg_new(if_nlmsg_size(dev, ext_filter_mask), GFP_KERNEL); |
2851 | if (nskb == NULL) | 2912 | if (nskb == NULL) |
2852 | return -ENOBUFS; | 2913 | goto out; |
2853 | 2914 | ||
2854 | err = rtnl_fill_ifinfo(nskb, dev, RTM_NEWLINK, NETLINK_CB(skb).portid, | 2915 | err = rtnl_fill_ifinfo(nskb, dev, net, |
2855 | nlh->nlmsg_seq, 0, 0, ext_filter_mask, 0, NULL); | 2916 | RTM_NEWLINK, NETLINK_CB(skb).portid, |
2917 | nlh->nlmsg_seq, 0, 0, ext_filter_mask, | ||
2918 | 0, NULL, netnsid); | ||
2856 | if (err < 0) { | 2919 | if (err < 0) { |
2857 | /* -EMSGSIZE implies BUG in if_nlmsg_size */ | 2920 | /* -EMSGSIZE implies BUG in if_nlmsg_size */ |
2858 | WARN_ON(err == -EMSGSIZE); | 2921 | WARN_ON(err == -EMSGSIZE); |
2859 | kfree_skb(nskb); | 2922 | kfree_skb(nskb); |
2860 | } else | 2923 | } else |
2861 | err = rtnl_unicast(nskb, net, NETLINK_CB(skb).portid); | 2924 | err = rtnl_unicast(nskb, net, NETLINK_CB(skb).portid); |
2925 | out: | ||
2926 | if (netnsid >= 0) | ||
2927 | put_net(tgt_net); | ||
2862 | 2928 | ||
2863 | return err; | 2929 | return err; |
2864 | } | 2930 | } |
@@ -2948,8 +3014,9 @@ struct sk_buff *rtmsg_ifinfo_build_skb(int type, struct net_device *dev, | |||
2948 | if (skb == NULL) | 3014 | if (skb == NULL) |
2949 | goto errout; | 3015 | goto errout; |
2950 | 3016 | ||
2951 | err = rtnl_fill_ifinfo(skb, dev, type, 0, 0, change, 0, 0, event, | 3017 | err = rtnl_fill_ifinfo(skb, dev, dev_net(dev), |
2952 | new_nsid); | 3018 | type, 0, 0, change, 0, 0, event, |
3019 | new_nsid, -1); | ||
2953 | if (err < 0) { | 3020 | if (err < 0) { |
2954 | /* -EMSGSIZE implies BUG in if_nlmsg_size() */ | 3021 | /* -EMSGSIZE implies BUG in if_nlmsg_size() */ |
2955 | WARN_ON(err == -EMSGSIZE); | 3022 | WARN_ON(err == -EMSGSIZE); |