Like the previous IPv4 counterpart, this patch associates every IPv6
address with a corresponding afnet namespace. The namespace can be set
via file descriptor and the inode gets reported during dumping.

Signed-off-by: Hannes Frederic Sowa <han...@stressinduktion.org>
---
 include/net/if_inet6.h |  3 +++
 net/core/afnetns.c     |  3 +++
 net/ipv6/addrconf.c    | 70 +++++++++++++++++++++++++++++++++++++++++---------
 3 files changed, 64 insertions(+), 12 deletions(-)

diff --git a/include/net/if_inet6.h b/include/net/if_inet6.h
index f656f9051acafa..cad645851501f4 100644
--- a/include/net/if_inet6.h
+++ b/include/net/if_inet6.h
@@ -41,6 +41,9 @@ enum {
 struct inet6_ifaddr {
        struct in6_addr         addr;
        __u32                   prefix_len;
+#if IS_ENABLED(CONFIG_AFNETNS)
+       struct afnetns          *afnetns;
+#endif
 
        /* In seconds, relative to tstamp. Expiry is at tstamp + HZ * lft. */
        __u32                   valid_lft;
diff --git a/net/core/afnetns.c b/net/core/afnetns.c
index 12b823ae780796..b96c25b5ebe30d 100644
--- a/net/core/afnetns.c
+++ b/net/core/afnetns.c
@@ -56,6 +56,7 @@ void afnetns_free(struct afnetns *afnetns)
        put_net(afnetns->net);
        kfree(afnetns);
 }
+EXPORT_SYMBOL(afnetns_free);
 
 struct afnetns *afnetns_get_by_fd(int fd)
 {
@@ -76,11 +77,13 @@ struct afnetns *afnetns_get_by_fd(int fd)
        fput(file);
        return afnetns;
 }
+EXPORT_SYMBOL(afnetns_get_by_fd);
 
 unsigned int afnetns_to_inode(struct afnetns *afnetns)
 {
        return afnetns->ns.inum;
 }
+EXPORT_SYMBOL(afnetns_to_inode);
 
 struct afnetns *copy_afnet_ns(unsigned long flags, struct nsproxy *old)
 {
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 8c69768a5c4606..c67f6d3c5b9a7a 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -910,7 +910,9 @@ void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp)
                return;
        }
        ip6_rt_put(ifp->rt);
-
+#if IS_ENABLED(CONFIG_AFNETNS)
+       afnetns_put(ifp->afnetns);
+#endif
        kfree_rcu(ifp, rcu);
 }
 
@@ -942,9 +944,10 @@ static u32 inet6_addr_hash(const struct in6_addr *addr)
 /* On success it returns ifp with increased reference count */
 
 static struct inet6_ifaddr *
-ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr,
-             const struct in6_addr *peer_addr, int pfxlen,
-             int scope, u32 flags, u32 valid_lft, u32 prefered_lft)
+__ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr,
+               const struct in6_addr *peer_addr, int pfxlen,
+               int scope, u32 flags, u32 valid_lft, u32 prefered_lft,
+               struct afnetns *afnetns)
 {
        struct net *net = dev_net(idev->dev);
        struct inet6_ifaddr *ifa = NULL;
@@ -1002,7 +1005,9 @@ ipv6_add_addr(struct inet6_dev *idev, const struct 
in6_addr *addr,
        ifa->addr = *addr;
        if (peer_addr)
                ifa->peer_addr = *peer_addr;
-
+#if IS_ENABLED(CONFIG_AFNETNS)
+       ifa->afnetns = afnetns_get(afnetns);
+#endif
        spin_lock_init(&ifa->lock);
        INIT_DELAYED_WORK(&ifa->dad_work, addrconf_dad_work);
        INIT_HLIST_NODE(&ifa->addr_lst);
@@ -1054,6 +1059,17 @@ ipv6_add_addr(struct inet6_dev *idev, const struct 
in6_addr *addr,
        goto out2;
 }
 
+static struct inet6_ifaddr *ipv6_add_addr(struct inet6_dev *idev,
+                                         const struct in6_addr *addr,
+                                         const struct in6_addr *peer_addr,
+                                         int pfxlen, int scope, u32 flags,
+                                         u32 valid_lft, u32 prefered_lft)
+{
+       return __ipv6_add_addr(idev, addr, peer_addr, pfxlen, scope, flags,
+                              valid_lft, prefered_lft,
+                              net_afnetns(dev_net(idev->dev)));
+}
+
 enum cleanup_prefix_rt_t {
        CLEANUP_PREFIX_RT_NOP,    /* no cleanup action for prefix route */
        CLEANUP_PREFIX_RT_DEL,    /* delete the prefix route */
@@ -2741,7 +2757,8 @@ static int inet6_addr_add(struct net *net, int ifindex,
                          const struct in6_addr *pfx,
                          const struct in6_addr *peer_pfx,
                          unsigned int plen, __u32 ifa_flags,
-                         __u32 prefered_lft, __u32 valid_lft)
+                         __u32 prefered_lft, __u32 valid_lft,
+                         struct afnetns *afnetns)
 {
        struct inet6_ifaddr *ifp;
        struct inet6_dev *idev;
@@ -2799,8 +2816,8 @@ static int inet6_addr_add(struct net *net, int ifindex,
                prefered_lft = timeout;
        }
 
-       ifp = ipv6_add_addr(idev, pfx, peer_pfx, plen, scope, ifa_flags,
-                           valid_lft, prefered_lft);
+       ifp = __ipv6_add_addr(idev, pfx, peer_pfx, plen, scope, ifa_flags,
+                             valid_lft, prefered_lft, afnetns);
 
        if (!IS_ERR(ifp)) {
                if (!(ifa_flags & IFA_F_NOPREFIXROUTE)) {
@@ -2885,7 +2902,8 @@ int addrconf_add_ifaddr(struct net *net, void __user *arg)
        rtnl_lock();
        err = inet6_addr_add(net, ireq.ifr6_ifindex, &ireq.ifr6_addr, NULL,
                             ireq.ifr6_prefixlen, IFA_F_PERMANENT,
-                            INFINITY_LIFE_TIME, INFINITY_LIFE_TIME);
+                            INFINITY_LIFE_TIME, INFINITY_LIFE_TIME,
+                            net_afnetns(net));
        rtnl_unlock();
        return err;
 }
@@ -4502,6 +4520,7 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr 
*nlh)
        struct nlattr *tb[IFA_MAX+1];
        struct in6_addr *pfx, *peer_pfx;
        struct inet6_ifaddr *ifa;
+       struct afnetns *afnetns = NULL;
        struct net_device *dev;
        u32 valid_lft = INFINITY_LIFE_TIME, preferred_lft = INFINITY_LIFE_TIME;
        u32 ifa_flags;
@@ -4537,15 +4556,31 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr 
*nlh)
        ifa_flags &= IFA_F_NODAD | IFA_F_HOMEADDRESS | IFA_F_MANAGETEMPADDR |
                     IFA_F_NOPREFIXROUTE | IFA_F_MCAUTOJOIN;
 
+#if IS_ENABLED(CONFIG_AFNETNS)
+       if (tb[IFA_AFNETNS_FD]) {
+               int fd = nla_get_s32(tb[IFA_AFNETNS_FD]);
+
+               afnetns = afnetns_get_by_fd(fd);
+               if (IS_ERR(afnetns))
+                       return PTR_ERR(afnetns);
+       } else {
+               afnetns = afnetns_get(net_afnetns(net));
+       }
+#else
+       if (tb[IFA_AFNETNS_FD])
+               return -EOPNOTSUPP;
+#endif
+
        ifa = ipv6_get_ifaddr(net, pfx, dev, 1);
        if (!ifa) {
                /*
                 * It would be best to check for !NLM_F_CREATE here but
                 * userspace already relies on not having to provide this.
                 */
-               return inet6_addr_add(net, ifm->ifa_index, pfx, peer_pfx,
+               err =  inet6_addr_add(net, ifm->ifa_index, pfx, peer_pfx,
                                      ifm->ifa_prefixlen, ifa_flags,
-                                     preferred_lft, valid_lft);
+                                     preferred_lft, valid_lft, afnetns);
+               goto out;
        }
 
        if (nlh->nlmsg_flags & NLM_F_EXCL ||
@@ -4555,6 +4590,10 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr 
*nlh)
                err = inet6_addr_modify(ifa, ifa_flags, preferred_lft, 
valid_lft);
 
        in6_ifa_put(ifa);
+out:
+#if IS_ENABLED(CONFIG_AFNETNS)
+       afnetns_put(afnetns);
+#endif
 
        return err;
 }
@@ -4603,7 +4642,8 @@ static inline int inet6_ifaddr_msgsize(void)
               + nla_total_size(16) /* IFA_LOCAL */
               + nla_total_size(16) /* IFA_ADDRESS */
               + nla_total_size(sizeof(struct ifa_cacheinfo))
-              + nla_total_size(4)  /* IFA_FLAGS */;
+              + nla_total_size(4)  /* IFA_FLAGS */
+              + nla_total_size(4); /* IFA_AFNETNS_INODE */
 }
 
 static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa,
@@ -4655,6 +4695,12 @@ static int inet6_fill_ifaddr(struct sk_buff *skb, struct 
inet6_ifaddr *ifa,
        if (nla_put_u32(skb, IFA_FLAGS, ifa->flags) < 0)
                goto error;
 
+#if IS_ENABLED(CONFIG_AFNETNS)
+       if (nla_put_u32(skb, IFA_AFNETNS_INODE,
+                       afnetns_to_inode(ifa->afnetns)))
+               goto error;
+#endif
+
        nlmsg_end(skb, nlh);
        return 0;
 
-- 
2.9.3

Reply via email to