Each IPv4 address has an associated afnet namespace, so it is only going to be used by applications in the same afnet namespace.
One can open a file descriptor and pass it to the newaddr rtnetlink functions to put an IP address into a specific afnet namespace. Dumping the addresses also returns the appropriate afnetns inode number, so a match with the appropriate afnet namespace can be done in user space. Signed-off-by: Hannes Frederic Sowa <han...@stressinduktion.org> --- include/linux/inetdevice.h | 3 +++ include/net/afnetns.h | 2 ++ include/uapi/linux/if_addr.h | 2 ++ net/core/afnetns.c | 26 ++++++++++++++++++++++++++ net/ipv4/devinet.c | 39 ++++++++++++++++++++++++++++++++++++++- 5 files changed, 71 insertions(+), 1 deletion(-) diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h index ee971f335a8b65..d5ac959e90baa1 100644 --- a/include/linux/inetdevice.h +++ b/include/linux/inetdevice.h @@ -141,6 +141,9 @@ struct in_ifaddr { unsigned char ifa_scope; unsigned char ifa_prefixlen; __u32 ifa_flags; +#if IS_ENABLED(CONFIG_AFNETNS) + struct afnetns *afnetns; +#endif char ifa_label[IFNAMSIZ]; /* In seconds, relative to tstamp. Expiry is at tstamp + HZ * lft. */ diff --git a/include/net/afnetns.h b/include/net/afnetns.h index d5fbb83023acd6..9039086717c356 100644 --- a/include/net/afnetns.h +++ b/include/net/afnetns.h @@ -19,6 +19,8 @@ int afnet_ns_init(void); struct afnetns *afnetns_new(struct net *net); struct afnetns *copy_afnet_ns(unsigned long flags, struct nsproxy *old); +struct afnetns *afnetns_get_by_fd(int fd); +unsigned int afnetns_to_inode(struct afnetns *afnetns); void afnetns_free(struct afnetns *afnetns); static inline struct afnetns *afnetns_get(struct afnetns *afnetns) diff --git a/include/uapi/linux/if_addr.h b/include/uapi/linux/if_addr.h index 4318ab1635cedf..c67703808584eb 100644 --- a/include/uapi/linux/if_addr.h +++ b/include/uapi/linux/if_addr.h @@ -32,6 +32,8 @@ enum { IFA_CACHEINFO, IFA_MULTICAST, IFA_FLAGS, + IFA_AFNETNS_FD, + IFA_AFNETNS_INODE, __IFA_MAX, }; diff --git a/net/core/afnetns.c b/net/core/afnetns.c index 997623e4dc5078..12b823ae780796 100644 --- a/net/core/afnetns.c +++ b/net/core/afnetns.c @@ -2,6 +2,7 @@ #include <net/net_namespace.h> #include <linux/sched.h> #include <linux/sched/task.h> +#include <linux/file.h> #include <linux/nsproxy.h> #include <linux/proc_ns.h> @@ -56,6 +57,31 @@ void afnetns_free(struct afnetns *afnetns) kfree(afnetns); } +struct afnetns *afnetns_get_by_fd(int fd) +{ + struct file *file; + struct ns_common *ns; + struct afnetns *afnetns; + + file = proc_ns_fget(fd); + if (IS_ERR(file)) + return ERR_CAST(file); + + ns = get_proc_ns(file_inode(file)); + if (ns->ops == &afnetns_operations) + afnetns = afnetns_get(ns_to_afnet(ns)); + else + afnetns = ERR_PTR(-EINVAL); + + fput(file); + return afnetns; +} + +unsigned int afnetns_to_inode(struct afnetns *afnetns) +{ + return afnetns->ns.inum; +} + struct afnetns *copy_afnet_ns(unsigned long flags, struct nsproxy *old) { if (flags & CLONE_NEWNET) diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index cebedd545e5e28..d4a38b6e9adb79 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -99,6 +99,7 @@ static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = { [IFA_LABEL] = { .type = NLA_STRING, .len = IFNAMSIZ - 1 }, [IFA_CACHEINFO] = { .len = sizeof(struct ifa_cacheinfo) }, [IFA_FLAGS] = { .type = NLA_U32 }, + [IFA_AFNETNS_FD] = { .type = NLA_S32 }, }; #define IN4_ADDR_HSIZE_SHIFT 8 @@ -203,6 +204,9 @@ static void inet_rcu_free_ifa(struct rcu_head *head) struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head); if (ifa->ifa_dev) in_dev_put(ifa->ifa_dev); +#if IS_ENABLED(CONFIG_AFNETNS) + afnetns_put(ifa->afnetns); +#endif kfree(ifa); } @@ -805,6 +809,26 @@ static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh, else memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); +#if IS_ENABLED(CONFIG_AFNETNS) + if (tb[IFA_AFNETNS_FD]) { + int fd = nla_get_s32(tb[IFA_AFNETNS_FD]); + + ifa->afnetns = afnetns_get_by_fd(fd); + if (IS_ERR(ifa->afnetns)) { + err = PTR_ERR(ifa->afnetns); + ifa->afnetns = afnetns_get(net->afnet_ns); + goto errout_free; + } + } else { + ifa->afnetns = afnetns_get(net->afnet_ns); + } +#else + if (tb[IFA_AFNETNS_FD]) { + err = -EOPNOTSUPP; + goto errout_free; + } +#endif + if (tb[IFA_CACHEINFO]) { struct ifa_cacheinfo *ci; @@ -1089,6 +1113,9 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg) ifa->ifa_mask = inet_make_mask(32); } set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME); +#if IS_ENABLED(CONFIG_AFNETNS) + ifa->afnetns = afnetns_get(net->afnet_ns); +#endif ret = inet_set_ifa(dev, ifa); break; @@ -1444,6 +1471,9 @@ static int inetdev_event(struct notifier_block *this, unsigned long event, in_dev_hold(in_dev); ifa->ifa_dev = in_dev; ifa->ifa_scope = RT_SCOPE_HOST; +#if IS_ENABLED(CONFIG_AFNETNS) + ifa->afnetns = afnetns_get(dev_net(dev)->afnet_ns); +#endif memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME); @@ -1504,7 +1534,8 @@ static size_t inet_nlmsg_size(void) + nla_total_size(4) /* IFA_BROADCAST */ + nla_total_size(IFNAMSIZ) /* IFA_LABEL */ + nla_total_size(4) /* IFA_FLAGS */ - + nla_total_size(sizeof(struct ifa_cacheinfo)); /* IFA_CACHEINFO */ + + nla_total_size(sizeof(struct ifa_cacheinfo)) /* IFA_CACHEINFO */ + + nla_total_size(4); /* IFA_AFNETNS_INODE */ } static inline u32 cstamp_delta(unsigned long cstamp) @@ -1577,6 +1608,12 @@ static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa, preferred, valid)) goto nla_put_failure; +#if IS_ENABLED(CONFIG_AFNETNS) + if (nla_put_u32(skb, IFA_AFNETNS_INODE, + afnetns_to_inode(ifa->afnetns))) + goto nla_put_failure; +#endif + nlmsg_end(skb, nlh); return 0; -- 2.9.3