[NETNS][IPV6]: inet6_addr - check ipv6 address per namespace

When a new address is added, we must check if the new address does not
already exists.  This patch makes this check to be aware of a network
namespace, so the check will look if the address already exists for
the specified network namespace. While the addresses are browsed, the
addresses which do not belong to the namespace are discarded.

Signed-off-by: Daniel Lezcano <dlezcano@fr.ibm.com>
Signed-off-by: Benjamin Thery <benjamin.thery@bull.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
diff --git a/include/net/addrconf.h b/include/net/addrconf.h
index 1c3a560..d1697b5 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -59,9 +59,11 @@
 extern int			addrconf_del_ifaddr(void __user *arg);
 extern int			addrconf_set_dstaddr(void __user *arg);
 
-extern int			ipv6_chk_addr(struct in6_addr *addr,
+extern int			ipv6_chk_addr(struct net *net,
+					      struct in6_addr *addr,
 					      struct net_device *dev,
 					      int strict);
+
 #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
 extern int			ipv6_chk_home_addr(struct in6_addr *addr);
 #endif
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index d7b4403..f35c3df 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -1206,13 +1206,16 @@
 	return cnt;
 }
 
-int ipv6_chk_addr(struct in6_addr *addr, struct net_device *dev, int strict)
+int ipv6_chk_addr(struct net *net, struct in6_addr *addr,
+		  struct net_device *dev, int strict)
 {
 	struct inet6_ifaddr * ifp;
 	u8 hash = ipv6_addr_hash(addr);
 
 	read_lock_bh(&addrconf_hash_lock);
 	for(ifp = inet6_addr_lst[hash]; ifp; ifp=ifp->lst_next) {
+		if (ifp->idev->dev->nd_net != net)
+			continue;
 		if (ipv6_addr_equal(&ifp->addr, addr) &&
 		    !(ifp->flags&IFA_F_TENTATIVE)) {
 			if (dev == NULL || ifp->idev->dev == dev ||
@@ -1223,7 +1226,6 @@
 	read_unlock_bh(&addrconf_hash_lock);
 	return ifp != NULL;
 }
-
 EXPORT_SYMBOL(ipv6_chk_addr);
 
 static
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index ac8772d..3150c4b 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -314,7 +314,8 @@
 			 */
 			v4addr = LOOPBACK4_IPV6;
 			if (!(addr_type & IPV6_ADDR_MULTICAST))	{
-				if (!ipv6_chk_addr(&addr->sin6_addr, dev, 0)) {
+				if (!ipv6_chk_addr(&init_net, &addr->sin6_addr,
+						   dev, 0)) {
 					if (dev)
 						dev_put(dev);
 					err = -EADDRNOTAVAIL;
diff --git a/net/ipv6/anycast.c b/net/ipv6/anycast.c
index 5c419006..9c7f83f 100644
--- a/net/ipv6/anycast.c
+++ b/net/ipv6/anycast.c
@@ -89,7 +89,7 @@
 		return -EPERM;
 	if (ipv6_addr_is_multicast(addr))
 		return -EINVAL;
-	if (ipv6_chk_addr(addr, NULL, 0))
+	if (ipv6_chk_addr(&init_net, addr, NULL, 0))
 		return -EINVAL;
 
 	pac = sock_kmalloc(sk, sizeof(struct ipv6_ac_socklist), GFP_KERNEL);
diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
index f49a06a..94fa6ae 100644
--- a/net/ipv6/datagram.c
+++ b/net/ipv6/datagram.c
@@ -549,7 +549,8 @@
 						return -ENODEV;
 				}
 			}
-			if (!ipv6_chk_addr(&src_info->ipi6_addr, dev, 0)) {
+			if (!ipv6_chk_addr(&init_net, &src_info->ipi6_addr,
+					   dev, 0)) {
 				if (dev)
 					dev_put(dev);
 				err = -EINVAL;
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c
index 5395afe..cbb5b9c 100644
--- a/net/ipv6/icmp.c
+++ b/net/ipv6/icmp.c
@@ -332,7 +332,7 @@
 	 */
 	addr_type = ipv6_addr_type(&hdr->daddr);
 
-	if (ipv6_chk_addr(&hdr->daddr, skb->dev, 0))
+	if (ipv6_chk_addr(&init_net, &hdr->daddr, skb->dev, 0))
 		saddr = &hdr->daddr;
 
 	/*
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index 29b5321..425c9ae 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -653,8 +653,8 @@
 			ldev = dev_get_by_index(&init_net, p->link);
 
 		if ((ipv6_addr_is_multicast(&p->laddr) ||
-		     likely(ipv6_chk_addr(&p->laddr, ldev, 0))) &&
-		    likely(!ipv6_chk_addr(&p->raddr, NULL, 0)))
+		     likely(ipv6_chk_addr(&init_net, &p->laddr, ldev, 0))) &&
+		    likely(!ipv6_chk_addr(&init_net, &p->raddr, NULL, 0)))
 			ret = 1;
 
 		if (ldev)
@@ -788,12 +788,12 @@
 		if (p->link)
 			ldev = dev_get_by_index(&init_net, p->link);
 
-		if (unlikely(!ipv6_chk_addr(&p->laddr, ldev, 0)))
+		if (unlikely(!ipv6_chk_addr(&init_net, &p->laddr, ldev, 0)))
 			printk(KERN_WARNING
 			       "%s xmit: Local address not yet configured!\n",
 			       p->name);
 		else if (!ipv6_addr_is_multicast(&p->raddr) &&
-			 unlikely(ipv6_chk_addr(&p->raddr, NULL, 0)))
+			 unlikely(ipv6_chk_addr(&init_net, &p->raddr, NULL, 0)))
 			printk(KERN_WARNING
 			       "%s xmit: Routing loop! "
 			       "Remote address found on this node!\n",
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index b66a1f8..e217d3f 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -653,7 +653,7 @@
 	struct in6_addr *target = (struct in6_addr *)&neigh->primary_key;
 	int probes = atomic_read(&neigh->probes);
 
-	if (skb && ipv6_chk_addr(&ipv6_hdr(skb)->saddr, dev, 1))
+	if (skb && ipv6_chk_addr(&init_net, &ipv6_hdr(skb)->saddr, dev, 1))
 		saddr = &ipv6_hdr(skb)->saddr;
 
 	if ((probes -= neigh->parms->ucast_probes) < 0) {
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index 45a580e..cb0b110 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -298,7 +298,8 @@
 		v4addr = LOOPBACK4_IPV6;
 		if (!(addr_type & IPV6_ADDR_MULTICAST))	{
 			err = -EADDRNOTAVAIL;
-			if (!ipv6_chk_addr(&addr->sin6_addr, dev, 0)) {
+			if (!ipv6_chk_addr(&init_net, &addr->sin6_addr,
+					   dev, 0)) {
 				if (dev)
 					dev_put(dev);
 				goto out;
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index bd04aed..74f106a 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -556,7 +556,7 @@
 	if (!(type & IPV6_ADDR_UNICAST))
 		return 0;
 
-	return ipv6_chk_addr(in6, NULL, 0);
+	return ipv6_chk_addr(&init_net, in6, NULL, 0);
 }
 
 /* This function checks if the address is a valid address to be used for
@@ -858,7 +858,8 @@
 			dev = dev_get_by_index(&init_net, addr->v6.sin6_scope_id);
 			if (!dev)
 				return 0;
-			if (!ipv6_chk_addr(&addr->v6.sin6_addr, dev, 0)) {
+			if (!ipv6_chk_addr(&init_net, &addr->v6.sin6_addr,
+					   dev, 0)) {
 				dev_put(dev);
 				return 0;
 			}