From 0c7aecd4bde4b7302cd41986d3a29e4f0b0ed218 Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Thu, 15 Jan 2015 15:11:15 +0100 Subject: [PATCH 1/4] netns: add rtnl cmd to add and get peer netns ids With this patch, a user can define an id for a peer netns by providing a FD or a PID. These ids are local to the netns where it is added (ie valid only into this netns). The main function (ie the one exported to other module), peernet2id(), allows to get the id of a peer netns. If no id has been assigned by the user, this function allocates one. These ids will be used in netlink messages to point to a peer netns, for example in case of a x-netns interface. Signed-off-by: Nicolas Dichtel Signed-off-by: David S. Miller --- MAINTAINERS | 1 + include/net/net_namespace.h | 4 + include/uapi/linux/Kbuild | 1 + include/uapi/linux/net_namespace.h | 23 ++++ include/uapi/linux/rtnetlink.h | 5 + net/core/net_namespace.c | 211 +++++++++++++++++++++++++++++ 6 files changed, 245 insertions(+) create mode 100644 include/uapi/linux/net_namespace.h diff --git a/MAINTAINERS b/MAINTAINERS index 9de900572633..9b91d9f0257e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6578,6 +6578,7 @@ F: include/linux/netdevice.h F: include/uapi/linux/in.h F: include/uapi/linux/net.h F: include/uapi/linux/netdevice.h +F: include/uapi/linux/net_namespace.h F: tools/net/ F: tools/testing/selftests/net/ F: lib/random32.c diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index 2e8756b8c775..36faf4990c4b 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h @@ -60,6 +60,7 @@ struct net { struct list_head exit_list; /* Use only net_mutex */ struct user_namespace *user_ns; /* Owning user namespace */ + struct idr netns_ids; struct ns_common ns; @@ -290,6 +291,9 @@ static inline struct net *read_pnet(struct net * const *pnet) #define __net_initconst __initconst #endif +int peernet2id(struct net *net, struct net *peer); +struct net *get_net_ns_by_id(struct net *net, int id); + struct pernet_operations { struct list_head list; int (*init)(struct net *net); diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild index 00b100023c47..14b7b6e44c77 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild @@ -283,6 +283,7 @@ header-y += net.h header-y += netlink_diag.h header-y += netlink.h header-y += netrom.h +header-y += net_namespace.h header-y += net_tstamp.h header-y += nfc.h header-y += nfs2.h diff --git a/include/uapi/linux/net_namespace.h b/include/uapi/linux/net_namespace.h new file mode 100644 index 000000000000..778cd2c3ebf4 --- /dev/null +++ b/include/uapi/linux/net_namespace.h @@ -0,0 +1,23 @@ +/* Copyright (c) 2015 6WIND S.A. + * Author: Nicolas Dichtel + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ +#ifndef _UAPI_LINUX_NET_NAMESPACE_H_ +#define _UAPI_LINUX_NET_NAMESPACE_H_ + +/* Attributes of RTM_NEWNSID/RTM_GETNSID messages */ +enum { + NETNSA_NONE, +#define NETNSA_NSID_NOT_ASSIGNED -1 + NETNSA_NSID, + NETNSA_PID, + NETNSA_FD, + __NETNSA_MAX, +}; + +#define NETNSA_MAX (__NETNSA_MAX - 1) + +#endif /* _UAPI_LINUX_NET_NAMESPACE_H_ */ diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h index a1d18593f41e..5cc5d66bf519 100644 --- a/include/uapi/linux/rtnetlink.h +++ b/include/uapi/linux/rtnetlink.h @@ -132,6 +132,11 @@ enum { RTM_GETMDB = 86, #define RTM_GETMDB RTM_GETMDB + RTM_NEWNSID = 88, +#define RTM_NEWNSID RTM_NEWNSID + RTM_GETNSID = 90, +#define RTM_GETNSID RTM_GETNSID + __RTM_MAX, #define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1) }; diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index ce780c722e48..9d1a4cac83b6 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -15,6 +15,10 @@ #include #include #include +#include +#include +#include +#include #include #include @@ -144,6 +148,77 @@ static void ops_free_list(const struct pernet_operations *ops, } } +static int alloc_netid(struct net *net, struct net *peer, int reqid) +{ + int min = 0, max = 0; + + ASSERT_RTNL(); + + if (reqid >= 0) { + min = reqid; + max = reqid + 1; + } + + return idr_alloc(&net->netns_ids, peer, min, max, GFP_KERNEL); +} + +/* This function is used by idr_for_each(). If net is equal to peer, the + * function returns the id so that idr_for_each() stops. Because we cannot + * returns the id 0 (idr_for_each() will not stop), we return the magic value + * NET_ID_ZERO (-1) for it. + */ +#define NET_ID_ZERO -1 +static int net_eq_idr(int id, void *net, void *peer) +{ + if (net_eq(net, peer)) + return id ? : NET_ID_ZERO; + return 0; +} + +static int __peernet2id(struct net *net, struct net *peer, bool alloc) +{ + int id = idr_for_each(&net->netns_ids, net_eq_idr, peer); + + ASSERT_RTNL(); + + /* Magic value for id 0. */ + if (id == NET_ID_ZERO) + return 0; + if (id > 0) + return id; + + if (alloc) + return alloc_netid(net, peer, -1); + + return -ENOENT; +} + +/* This function returns the id of a peer netns. If no id is assigned, one will + * be allocated and returned. + */ +int peernet2id(struct net *net, struct net *peer) +{ + int id = __peernet2id(net, peer, true); + + return id >= 0 ? id : NETNSA_NSID_NOT_ASSIGNED; +} + +struct net *get_net_ns_by_id(struct net *net, int id) +{ + struct net *peer; + + if (id < 0) + return NULL; + + rcu_read_lock(); + peer = idr_find(&net->netns_ids, id); + if (peer) + get_net(peer); + rcu_read_unlock(); + + return peer; +} + /* * setup_net runs the initializers for the network namespace object. */ @@ -158,6 +233,7 @@ static __net_init int setup_net(struct net *net, struct user_namespace *user_ns) atomic_set(&net->passive, 1); net->dev_base_seq = 1; net->user_ns = user_ns; + idr_init(&net->netns_ids); #ifdef NETNS_REFCNT_DEBUG atomic_set(&net->use_count, 0); @@ -288,6 +364,14 @@ static void cleanup_net(struct work_struct *work) list_for_each_entry(net, &net_kill_list, cleanup_list) { list_del_rcu(&net->list); list_add_tail(&net->exit_list, &net_exit_list); + for_each_net(tmp) { + int id = __peernet2id(tmp, net, false); + + if (id >= 0) + idr_remove(&tmp->netns_ids, id); + } + idr_destroy(&net->netns_ids); + } rtnl_unlock(); @@ -402,6 +486,130 @@ static struct pernet_operations __net_initdata net_ns_ops = { .exit = net_ns_net_exit, }; +static struct nla_policy rtnl_net_policy[NETNSA_MAX + 1] = { + [NETNSA_NONE] = { .type = NLA_UNSPEC }, + [NETNSA_NSID] = { .type = NLA_S32 }, + [NETNSA_PID] = { .type = NLA_U32 }, + [NETNSA_FD] = { .type = NLA_U32 }, +}; + +static int rtnl_net_newid(struct sk_buff *skb, struct nlmsghdr *nlh) +{ + struct net *net = sock_net(skb->sk); + struct nlattr *tb[NETNSA_MAX + 1]; + struct net *peer; + int nsid, err; + + err = nlmsg_parse(nlh, sizeof(struct rtgenmsg), tb, NETNSA_MAX, + rtnl_net_policy); + if (err < 0) + return err; + if (!tb[NETNSA_NSID]) + return -EINVAL; + nsid = nla_get_s32(tb[NETNSA_NSID]); + + if (tb[NETNSA_PID]) + peer = get_net_ns_by_pid(nla_get_u32(tb[NETNSA_PID])); + else if (tb[NETNSA_FD]) + peer = get_net_ns_by_fd(nla_get_u32(tb[NETNSA_FD])); + else + return -EINVAL; + if (IS_ERR(peer)) + return PTR_ERR(peer); + + if (__peernet2id(net, peer, false) >= 0) { + err = -EEXIST; + goto out; + } + + err = alloc_netid(net, peer, nsid); + if (err > 0) + err = 0; +out: + put_net(peer); + return err; +} + +static int rtnl_net_get_size(void) +{ + return NLMSG_ALIGN(sizeof(struct rtgenmsg)) + + nla_total_size(sizeof(s32)) /* NETNSA_NSID */ + ; +} + +static int rtnl_net_fill(struct sk_buff *skb, u32 portid, u32 seq, int flags, + int cmd, struct net *net, struct net *peer) +{ + struct nlmsghdr *nlh; + struct rtgenmsg *rth; + int id; + + ASSERT_RTNL(); + + nlh = nlmsg_put(skb, portid, seq, cmd, sizeof(*rth), flags); + if (!nlh) + return -EMSGSIZE; + + rth = nlmsg_data(nlh); + rth->rtgen_family = AF_UNSPEC; + + id = __peernet2id(net, peer, false); + if (id < 0) + id = NETNSA_NSID_NOT_ASSIGNED; + if (nla_put_s32(skb, NETNSA_NSID, id)) + goto nla_put_failure; + + nlmsg_end(skb, nlh); + return 0; + +nla_put_failure: + nlmsg_cancel(skb, nlh); + return -EMSGSIZE; +} + +static int rtnl_net_getid(struct sk_buff *skb, struct nlmsghdr *nlh) +{ + struct net *net = sock_net(skb->sk); + struct nlattr *tb[NETNSA_MAX + 1]; + struct sk_buff *msg; + int err = -ENOBUFS; + struct net *peer; + + err = nlmsg_parse(nlh, sizeof(struct rtgenmsg), tb, NETNSA_MAX, + rtnl_net_policy); + if (err < 0) + return err; + if (tb[NETNSA_PID]) + peer = get_net_ns_by_pid(nla_get_u32(tb[NETNSA_PID])); + else if (tb[NETNSA_FD]) + peer = get_net_ns_by_fd(nla_get_u32(tb[NETNSA_FD])); + else + return -EINVAL; + + if (IS_ERR(peer)) + return PTR_ERR(peer); + + msg = nlmsg_new(rtnl_net_get_size(), GFP_KERNEL); + if (!msg) { + err = -ENOMEM; + goto out; + } + + err = rtnl_net_fill(msg, NETLINK_CB(skb).portid, nlh->nlmsg_seq, 0, + RTM_GETNSID, net, peer); + if (err < 0) + goto err_out; + + err = rtnl_unicast(msg, net, NETLINK_CB(skb).portid); + goto out; + +err_out: + nlmsg_free(msg); +out: + put_net(peer); + return err; +} + static int __init net_ns_init(void) { struct net_generic *ng; @@ -435,6 +643,9 @@ static int __init net_ns_init(void) register_pernet_subsys(&net_ns_ops); + rtnl_register(PF_UNSPEC, RTM_NEWNSID, rtnl_net_newid, NULL, NULL); + rtnl_register(PF_UNSPEC, RTM_GETNSID, rtnl_net_getid, NULL, NULL); + return 0; } From d37512a277dfb2cef8a578e25a3246f61399a55a Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Thu, 15 Jan 2015 15:11:16 +0100 Subject: [PATCH 2/4] rtnl: add link netns id to interface messages This patch adds a new attribute (IFLA_LINK_NETNSID) which contains the 'link' netns id when this netns is different from the netns where the interface stands (for example for x-net interfaces like ip tunnels). With this attribute, it's possible to interpret correctly all advertised information (like IFLA_LINK, etc.). Signed-off-by: Nicolas Dichtel Signed-off-by: David S. Miller --- include/net/rtnetlink.h | 2 ++ include/uapi/linux/if_link.h | 1 + net/core/rtnetlink.c | 13 +++++++++++++ 3 files changed, 16 insertions(+) diff --git a/include/net/rtnetlink.h b/include/net/rtnetlink.h index e21b9f9653c0..6c6d5393fc34 100644 --- a/include/net/rtnetlink.h +++ b/include/net/rtnetlink.h @@ -46,6 +46,7 @@ static inline int rtnl_msg_family(const struct nlmsghdr *nlh) * to create when creating a new device. * @get_num_rx_queues: Function to determine number of receive queues * to create when creating a new device. + * @get_link_net: Function to get the i/o netns of the device */ struct rtnl_link_ops { struct list_head list; @@ -93,6 +94,7 @@ struct rtnl_link_ops { int (*fill_slave_info)(struct sk_buff *skb, const struct net_device *dev, const struct net_device *slave_dev); + struct net *(*get_link_net)(const struct net_device *dev); }; int __rtnl_link_register(struct rtnl_link_ops *ops); diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 2a8380edbb7e..0deee3eeddbf 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -146,6 +146,7 @@ enum { IFLA_PHYS_PORT_ID, IFLA_CARRIER_CHANGES, IFLA_PHYS_SWITCH_ID, + IFLA_LINK_NETNSID, __IFLA_MAX }; diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 47b39f3e867c..bd6370f0cb31 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -875,6 +875,7 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev, + nla_total_size(1) /* IFLA_OPERSTATE */ + nla_total_size(1) /* IFLA_LINKMODE */ + nla_total_size(4) /* IFLA_CARRIER_CHANGES */ + + nla_total_size(4) /* IFLA_LINK_NETNSID */ + nla_total_size(ext_filter_mask & RTEXT_FILTER_VF ? 4 : 0) /* IFLA_NUM_VF */ + rtnl_vfinfo_size(dev, ext_filter_mask) /* IFLA_VFINFO_LIST */ @@ -1169,6 +1170,18 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, goto nla_put_failure; } + 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(dev_net(dev), link_net); + + if (nla_put_s32(skb, IFLA_LINK_NETNSID, id)) + goto nla_put_failure; + } + } + if (!(af_spec = nla_nest_start(skb, IFLA_AF_SPEC))) goto nla_put_failure; From 1728d4fabd1bc9965728de25dda0b694b8da6450 Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Thu, 15 Jan 2015 15:11:17 +0100 Subject: [PATCH 3/4] tunnels: advertise link netns via netlink Implement rtnl_link_ops->get_link_net() callback so that IFLA_LINK_NETNSID is added to rtnetlink messages. Signed-off-by: Nicolas Dichtel Signed-off-by: David S. Miller --- drivers/net/vxlan.c | 8 ++++++++ include/net/ip6_tunnel.h | 1 + include/net/ip_tunnels.h | 1 + net/ipv4/ip_gre.c | 2 ++ net/ipv4/ip_tunnel.c | 8 ++++++++ net/ipv4/ip_vti.c | 1 + net/ipv4/ipip.c | 1 + net/ipv6/ip6_gre.c | 1 + net/ipv6/ip6_tunnel.c | 9 +++++++++ net/ipv6/ip6_vti.c | 1 + net/ipv6/sit.c | 1 + 11 files changed, 34 insertions(+) diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index c5f79e7513a6..0346eaa6d236 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -2923,6 +2923,13 @@ static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev) return -EMSGSIZE; } +static struct net *vxlan_get_link_net(const struct net_device *dev) +{ + struct vxlan_dev *vxlan = netdev_priv(dev); + + return vxlan->net; +} + static struct rtnl_link_ops vxlan_link_ops __read_mostly = { .kind = "vxlan", .maxtype = IFLA_VXLAN_MAX, @@ -2934,6 +2941,7 @@ static struct rtnl_link_ops vxlan_link_ops __read_mostly = { .dellink = vxlan_dellink, .get_size = vxlan_get_size, .fill_info = vxlan_fill_info, + .get_link_net = vxlan_get_link_net, }; static void vxlan_handle_lowerdev_unregister(struct vxlan_net *vn, diff --git a/include/net/ip6_tunnel.h b/include/net/ip6_tunnel.h index 9326c41c2d7f..76c091b53dae 100644 --- a/include/net/ip6_tunnel.h +++ b/include/net/ip6_tunnel.h @@ -70,6 +70,7 @@ int ip6_tnl_xmit_ctl(struct ip6_tnl *t, const struct in6_addr *laddr, __u16 ip6_tnl_parse_tlv_enc_lim(struct sk_buff *skb, __u8 *raw); __u32 ip6_tnl_get_cap(struct ip6_tnl *t, const struct in6_addr *laddr, const struct in6_addr *raddr); +struct net *ip6_tnl_get_link_net(const struct net_device *dev); static inline void ip6tunnel_xmit(struct sk_buff *skb, struct net_device *dev) { diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h index ce4db3cc5647..2c47061a6954 100644 --- a/include/net/ip_tunnels.h +++ b/include/net/ip_tunnels.h @@ -141,6 +141,7 @@ int ip_tunnel_encap_del_ops(const struct ip_tunnel_encap_ops *op, int ip_tunnel_init(struct net_device *dev); void ip_tunnel_uninit(struct net_device *dev); void ip_tunnel_dellink(struct net_device *dev, struct list_head *head); +struct net *ip_tunnel_get_link_net(const struct net_device *dev); int ip_tunnel_init_net(struct net *net, int ip_tnl_net_id, struct rtnl_link_ops *ops, char *devname); diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 942576e27df1..6e7727f27393 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -829,6 +829,7 @@ static struct rtnl_link_ops ipgre_link_ops __read_mostly = { .dellink = ip_tunnel_dellink, .get_size = ipgre_get_size, .fill_info = ipgre_fill_info, + .get_link_net = ip_tunnel_get_link_net, }; static struct rtnl_link_ops ipgre_tap_ops __read_mostly = { @@ -843,6 +844,7 @@ static struct rtnl_link_ops ipgre_tap_ops __read_mostly = { .dellink = ip_tunnel_dellink, .get_size = ipgre_get_size, .fill_info = ipgre_fill_info, + .get_link_net = ip_tunnel_get_link_net, }; static int __net_init ipgre_tap_init_net(struct net *net) diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index d3e447936720..2cd08280c77b 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c @@ -972,6 +972,14 @@ void ip_tunnel_dellink(struct net_device *dev, struct list_head *head) } EXPORT_SYMBOL_GPL(ip_tunnel_dellink); +struct net *ip_tunnel_get_link_net(const struct net_device *dev) +{ + struct ip_tunnel *tunnel = netdev_priv(dev); + + return tunnel->net; +} +EXPORT_SYMBOL(ip_tunnel_get_link_net); + int ip_tunnel_init_net(struct net *net, int ip_tnl_net_id, struct rtnl_link_ops *ops, char *devname) { diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c index 1a7e979e80ba..94efe148181c 100644 --- a/net/ipv4/ip_vti.c +++ b/net/ipv4/ip_vti.c @@ -531,6 +531,7 @@ static struct rtnl_link_ops vti_link_ops __read_mostly = { .dellink = ip_tunnel_dellink, .get_size = vti_get_size, .fill_info = vti_fill_info, + .get_link_net = ip_tunnel_get_link_net, }; static int __init vti_init(void) diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index 40403114f00a..b58d6689874c 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -498,6 +498,7 @@ static struct rtnl_link_ops ipip_link_ops __read_mostly = { .dellink = ip_tunnel_dellink, .get_size = ipip_get_size, .fill_info = ipip_fill_info, + .get_link_net = ip_tunnel_get_link_net, }; static struct xfrm_tunnel ipip_handler __read_mostly = { diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index 13cda4c6313b..9306a5ff9149 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -1662,6 +1662,7 @@ static struct rtnl_link_ops ip6gre_link_ops __read_mostly = { .dellink = ip6gre_dellink, .get_size = ip6gre_get_size, .fill_info = ip6gre_fill_info, + .get_link_net = ip6_tnl_get_link_net, }; static struct rtnl_link_ops ip6gre_tap_ops __read_mostly = { diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 92b3da571980..266a264ec212 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -1760,6 +1760,14 @@ static int ip6_tnl_fill_info(struct sk_buff *skb, const struct net_device *dev) return -EMSGSIZE; } +struct net *ip6_tnl_get_link_net(const struct net_device *dev) +{ + struct ip6_tnl *tunnel = netdev_priv(dev); + + return tunnel->net; +} +EXPORT_SYMBOL(ip6_tnl_get_link_net); + static const struct nla_policy ip6_tnl_policy[IFLA_IPTUN_MAX + 1] = { [IFLA_IPTUN_LINK] = { .type = NLA_U32 }, [IFLA_IPTUN_LOCAL] = { .len = sizeof(struct in6_addr) }, @@ -1783,6 +1791,7 @@ static struct rtnl_link_ops ip6_link_ops __read_mostly = { .dellink = ip6_tnl_dellink, .get_size = ip6_tnl_get_size, .fill_info = ip6_tnl_fill_info, + .get_link_net = ip6_tnl_get_link_net, }; static struct xfrm6_tunnel ip4ip6_handler __read_mostly = { diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c index ace10d0b3aac..5fb9e212eca8 100644 --- a/net/ipv6/ip6_vti.c +++ b/net/ipv6/ip6_vti.c @@ -1016,6 +1016,7 @@ static struct rtnl_link_ops vti6_link_ops __read_mostly = { .changelink = vti6_changelink, .get_size = vti6_get_size, .fill_info = vti6_fill_info, + .get_link_net = ip6_tnl_get_link_net, }; static void __net_exit vti6_destroy_tunnels(struct vti6_net *ip6n) diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 213546bd6d5d..3cc197c72b59 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -1763,6 +1763,7 @@ static struct rtnl_link_ops sit_link_ops __read_mostly = { .get_size = ipip6_get_size, .fill_info = ipip6_fill_info, .dellink = ipip6_dellink, + .get_link_net = ip_tunnel_get_link_net, }; static struct xfrm_tunnel sit_handler __read_mostly = { From 317f4810e45eebe65d4f8897670df8b779de1467 Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Thu, 15 Jan 2015 15:11:18 +0100 Subject: [PATCH 4/4] rtnl: allow to create device with IFLA_LINK_NETNSID set This patch adds the ability to create a netdevice in a specified netns and then move it into the final netns. In fact, it allows to have a symetry between get and set rtnl messages. Signed-off-by: Nicolas Dichtel Signed-off-by: David S. Miller --- net/core/rtnetlink.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index bd6370f0cb31..a12eecc0f976 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -1248,6 +1248,7 @@ static const struct nla_policy ifla_policy[IFLA_MAX+1] = { [IFLA_PHYS_PORT_ID] = { .type = NLA_BINARY, .len = MAX_PHYS_ITEM_ID_LEN }, [IFLA_CARRIER_CHANGES] = { .type = NLA_U32 }, /* ignored */ [IFLA_PHYS_SWITCH_ID] = { .type = NLA_BINARY, .len = MAX_PHYS_ITEM_ID_LEN }, + [IFLA_LINK_NETNSID] = { .type = NLA_S32 }, }; static const struct nla_policy ifla_info_policy[IFLA_INFO_MAX+1] = { @@ -2021,7 +2022,7 @@ static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh) struct nlattr *slave_attr[m_ops ? m_ops->slave_maxtype + 1 : 0]; struct nlattr **data = NULL; struct nlattr **slave_data = NULL; - struct net *dest_net; + struct net *dest_net, *link_net = NULL; if (ops) { if (ops->maxtype && linkinfo[IFLA_INFO_DATA]) { @@ -2127,7 +2128,18 @@ static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh) if (IS_ERR(dest_net)) return PTR_ERR(dest_net); - dev = rtnl_create_link(dest_net, ifname, name_assign_type, ops, tb); + if (tb[IFLA_LINK_NETNSID]) { + int id = nla_get_s32(tb[IFLA_LINK_NETNSID]); + + link_net = get_net_ns_by_id(dest_net, id); + if (!link_net) { + err = -EINVAL; + goto out; + } + } + + dev = rtnl_create_link(link_net ? : dest_net, ifname, + name_assign_type, ops, tb); if (IS_ERR(dev)) { err = PTR_ERR(dev); goto out; @@ -2155,9 +2167,16 @@ static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh) } } err = rtnl_configure_link(dev, ifm); - if (err < 0) + if (err < 0) { unregister_netdevice(dev); + goto out; + } + + if (link_net) + err = dev_change_net_namespace(dev, dest_net, ifname); out: + if (link_net) + put_net(link_net); put_net(dest_net); return err; }