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

Reply via email to