|
@@ -921,7 +921,8 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev,
|
|
|
+ nla_total_size(4) /* IFLA_EVENT */
|
|
|
+ nla_total_size(4) /* IFLA_NEW_NETNSID */
|
|
|
+ nla_total_size(1); /* IFLA_PROTO_DOWN */
|
|
|
-
|
|
|
+ + nla_total_size(4) /* IFLA_IF_NETNSID */
|
|
|
+ + 0;
|
|
|
}
|
|
|
|
|
|
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,
|
|
|
}
|
|
|
|
|
|
static int rtnl_fill_link_netnsid(struct sk_buff *skb,
|
|
|
- const struct net_device *dev)
|
|
|
+ const struct net_device *dev,
|
|
|
+ struct net *src_net)
|
|
|
{
|
|
|
if (dev->rtnl_link_ops && dev->rtnl_link_ops->get_link_net) {
|
|
|
struct net *link_net = dev->rtnl_link_ops->get_link_net(dev);
|
|
|
|
|
|
if (!net_eq(dev_net(dev), link_net)) {
|
|
|
- int id = peernet2id_alloc(dev_net(dev), link_net);
|
|
|
+ int id = peernet2id_alloc(src_net, link_net);
|
|
|
|
|
|
if (nla_put_s32(skb, IFLA_LINK_NETNSID, id))
|
|
|
return -EMSGSIZE;
|
|
@@ -1427,10 +1429,11 @@ static int rtnl_fill_link_af(struct sk_buff *skb,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
|
|
|
+static int rtnl_fill_ifinfo(struct sk_buff *skb,
|
|
|
+ struct net_device *dev, struct net *src_net,
|
|
|
int type, u32 pid, u32 seq, u32 change,
|
|
|
unsigned int flags, u32 ext_filter_mask,
|
|
|
- u32 event, int *new_nsid)
|
|
|
+ u32 event, int *new_nsid, int tgt_netnsid)
|
|
|
{
|
|
|
struct ifinfomsg *ifm;
|
|
|
struct nlmsghdr *nlh;
|
|
@@ -1448,6 +1451,9 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
|
|
|
ifm->ifi_flags = dev_get_flags(dev);
|
|
|
ifm->ifi_change = change;
|
|
|
|
|
|
+ if (tgt_netnsid >= 0 && nla_put_s32(skb, IFLA_IF_NETNSID, tgt_netnsid))
|
|
|
+ goto nla_put_failure;
|
|
|
+
|
|
|
if (nla_put_string(skb, IFLA_IFNAME, dev->name) ||
|
|
|
nla_put_u32(skb, IFLA_TXQLEN, dev->tx_queue_len) ||
|
|
|
nla_put_u8(skb, IFLA_OPERSTATE,
|
|
@@ -1513,7 +1519,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
|
|
|
goto nla_put_failure;
|
|
|
}
|
|
|
|
|
|
- if (rtnl_fill_link_netnsid(skb, dev))
|
|
|
+ if (rtnl_fill_link_netnsid(skb, dev, src_net))
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
if (new_nsid &&
|
|
@@ -1571,6 +1577,7 @@ static const struct nla_policy ifla_policy[IFLA_MAX+1] = {
|
|
|
[IFLA_XDP] = { .type = NLA_NESTED },
|
|
|
[IFLA_EVENT] = { .type = NLA_U32 },
|
|
|
[IFLA_GROUP] = { .type = NLA_U32 },
|
|
|
+ [IFLA_IF_NETNSID] = { .type = NLA_S32 },
|
|
|
};
|
|
|
|
|
|
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,
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
+static struct net *get_target_net(struct sk_buff *skb, int netnsid)
|
|
|
+{
|
|
|
+ struct net *net;
|
|
|
+
|
|
|
+ net = get_net_ns_by_id(sock_net(skb->sk), netnsid);
|
|
|
+ if (!net)
|
|
|
+ return ERR_PTR(-EINVAL);
|
|
|
+
|
|
|
+ /* For now, the caller is required to have CAP_NET_ADMIN in
|
|
|
+ * the user namespace owning the target net ns.
|
|
|
+ */
|
|
|
+ if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) {
|
|
|
+ put_net(net);
|
|
|
+ return ERR_PTR(-EACCES);
|
|
|
+ }
|
|
|
+ return net;
|
|
|
+}
|
|
|
+
|
|
|
static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
|
|
|
{
|
|
|
struct net *net = sock_net(skb->sk);
|
|
|
+ struct net *tgt_net = net;
|
|
|
int h, s_h;
|
|
|
int idx = 0, s_idx;
|
|
|
struct net_device *dev;
|
|
@@ -1686,6 +1712,7 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
|
|
|
const struct rtnl_link_ops *kind_ops = NULL;
|
|
|
unsigned int flags = NLM_F_MULTI;
|
|
|
int master_idx = 0;
|
|
|
+ int netnsid = -1;
|
|
|
int err;
|
|
|
int hdrlen;
|
|
|
|
|
@@ -1704,6 +1731,15 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
|
|
|
|
|
|
if (nlmsg_parse(cb->nlh, hdrlen, tb, IFLA_MAX,
|
|
|
ifla_policy, NULL) >= 0) {
|
|
|
+ if (tb[IFLA_IF_NETNSID]) {
|
|
|
+ netnsid = nla_get_s32(tb[IFLA_IF_NETNSID]);
|
|
|
+ tgt_net = get_target_net(skb, netnsid);
|
|
|
+ if (IS_ERR(tgt_net)) {
|
|
|
+ tgt_net = net;
|
|
|
+ netnsid = -1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
if (tb[IFLA_EXT_MASK])
|
|
|
ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]);
|
|
|
|
|
@@ -1719,17 +1755,19 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
|
|
|
|
|
|
for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
|
|
|
idx = 0;
|
|
|
- head = &net->dev_index_head[h];
|
|
|
+ head = &tgt_net->dev_index_head[h];
|
|
|
hlist_for_each_entry(dev, head, index_hlist) {
|
|
|
if (link_dump_filtered(dev, master_idx, kind_ops))
|
|
|
goto cont;
|
|
|
if (idx < s_idx)
|
|
|
goto cont;
|
|
|
- err = rtnl_fill_ifinfo(skb, dev, RTM_NEWLINK,
|
|
|
+ err = rtnl_fill_ifinfo(skb, dev, net,
|
|
|
+ RTM_NEWLINK,
|
|
|
NETLINK_CB(cb->skb).portid,
|
|
|
cb->nlh->nlmsg_seq, 0,
|
|
|
flags,
|
|
|
- ext_filter_mask, 0, NULL);
|
|
|
+ ext_filter_mask, 0, NULL,
|
|
|
+ netnsid);
|
|
|
|
|
|
if (err < 0) {
|
|
|
if (likely(skb->len))
|
|
@@ -1748,6 +1786,8 @@ out_err:
|
|
|
cb->args[0] = h;
|
|
|
cb->seq = net->dev_base_seq;
|
|
|
nl_dump_check_consistent(cb, nlmsg_hdr(skb));
|
|
|
+ if (netnsid >= 0)
|
|
|
+ put_net(tgt_net);
|
|
|
|
|
|
return err;
|
|
|
}
|
|
@@ -2360,6 +2400,9 @@ static int rtnl_setlink(struct sk_buff *skb, struct nlmsghdr *nlh,
|
|
|
if (err < 0)
|
|
|
goto errout;
|
|
|
|
|
|
+ if (tb[IFLA_IF_NETNSID])
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+
|
|
|
if (tb[IFLA_IFNAME])
|
|
|
nla_strlcpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ);
|
|
|
else
|
|
@@ -2454,6 +2497,9 @@ static int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh,
|
|
|
if (err < 0)
|
|
|
return err;
|
|
|
|
|
|
+ if (tb[IFLA_IF_NETNSID])
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+
|
|
|
if (tb[IFLA_IFNAME])
|
|
|
nla_strlcpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ);
|
|
|
|
|
@@ -2585,6 +2631,9 @@ replay:
|
|
|
if (err < 0)
|
|
|
return err;
|
|
|
|
|
|
+ if (tb[IFLA_IF_NETNSID])
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+
|
|
|
if (tb[IFLA_IFNAME])
|
|
|
nla_strlcpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ);
|
|
|
else
|
|
@@ -2818,11 +2867,13 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr *nlh,
|
|
|
struct netlink_ext_ack *extack)
|
|
|
{
|
|
|
struct net *net = sock_net(skb->sk);
|
|
|
+ struct net *tgt_net = net;
|
|
|
struct ifinfomsg *ifm;
|
|
|
char ifname[IFNAMSIZ];
|
|
|
struct nlattr *tb[IFLA_MAX+1];
|
|
|
struct net_device *dev = NULL;
|
|
|
struct sk_buff *nskb;
|
|
|
+ int netnsid = -1;
|
|
|
int err;
|
|
|
u32 ext_filter_mask = 0;
|
|
|
|
|
@@ -2830,35 +2881,50 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr *nlh,
|
|
|
if (err < 0)
|
|
|
return err;
|
|
|
|
|
|
+ if (tb[IFLA_IF_NETNSID]) {
|
|
|
+ netnsid = nla_get_s32(tb[IFLA_IF_NETNSID]);
|
|
|
+ tgt_net = get_target_net(skb, netnsid);
|
|
|
+ if (IS_ERR(tgt_net))
|
|
|
+ return PTR_ERR(tgt_net);
|
|
|
+ }
|
|
|
+
|
|
|
if (tb[IFLA_IFNAME])
|
|
|
nla_strlcpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ);
|
|
|
|
|
|
if (tb[IFLA_EXT_MASK])
|
|
|
ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]);
|
|
|
|
|
|
+ err = -EINVAL;
|
|
|
ifm = nlmsg_data(nlh);
|
|
|
if (ifm->ifi_index > 0)
|
|
|
- dev = __dev_get_by_index(net, ifm->ifi_index);
|
|
|
+ dev = __dev_get_by_index(tgt_net, ifm->ifi_index);
|
|
|
else if (tb[IFLA_IFNAME])
|
|
|
- dev = __dev_get_by_name(net, ifname);
|
|
|
+ dev = __dev_get_by_name(tgt_net, ifname);
|
|
|
else
|
|
|
- return -EINVAL;
|
|
|
+ goto out;
|
|
|
|
|
|
+ err = -ENODEV;
|
|
|
if (dev == NULL)
|
|
|
- return -ENODEV;
|
|
|
+ goto out;
|
|
|
|
|
|
+ err = -ENOBUFS;
|
|
|
nskb = nlmsg_new(if_nlmsg_size(dev, ext_filter_mask), GFP_KERNEL);
|
|
|
if (nskb == NULL)
|
|
|
- return -ENOBUFS;
|
|
|
+ goto out;
|
|
|
|
|
|
- err = rtnl_fill_ifinfo(nskb, dev, RTM_NEWLINK, NETLINK_CB(skb).portid,
|
|
|
- nlh->nlmsg_seq, 0, 0, ext_filter_mask, 0, NULL);
|
|
|
+ err = rtnl_fill_ifinfo(nskb, dev, net,
|
|
|
+ RTM_NEWLINK, NETLINK_CB(skb).portid,
|
|
|
+ nlh->nlmsg_seq, 0, 0, ext_filter_mask,
|
|
|
+ 0, NULL, netnsid);
|
|
|
if (err < 0) {
|
|
|
/* -EMSGSIZE implies BUG in if_nlmsg_size */
|
|
|
WARN_ON(err == -EMSGSIZE);
|
|
|
kfree_skb(nskb);
|
|
|
} else
|
|
|
err = rtnl_unicast(nskb, net, NETLINK_CB(skb).portid);
|
|
|
+out:
|
|
|
+ if (netnsid >= 0)
|
|
|
+ put_net(tgt_net);
|
|
|
|
|
|
return err;
|
|
|
}
|
|
@@ -2948,8 +3014,9 @@ struct sk_buff *rtmsg_ifinfo_build_skb(int type, struct net_device *dev,
|
|
|
if (skb == NULL)
|
|
|
goto errout;
|
|
|
|
|
|
- err = rtnl_fill_ifinfo(skb, dev, type, 0, 0, change, 0, 0, event,
|
|
|
- new_nsid);
|
|
|
+ err = rtnl_fill_ifinfo(skb, dev, dev_net(dev),
|
|
|
+ type, 0, 0, change, 0, 0, event,
|
|
|
+ new_nsid, -1);
|
|
|
if (err < 0) {
|
|
|
/* -EMSGSIZE implies BUG in if_nlmsg_size() */
|
|
|
WARN_ON(err == -EMSGSIZE);
|