patch-2.4.21 linux-2.4.21/net/ipv6/addrconf.c

Next file: linux-2.4.21/net/ipv6/af_inet6.c
Previous file: linux-2.4.21/net/ipv6/Makefile
Back to the patch index
Back to the overall index

diff -urN linux-2.4.20/net/ipv6/addrconf.c linux-2.4.21/net/ipv6/addrconf.c
@@ -28,6 +28,8 @@
  *						packets.
  *	YOSHIFUJI Hideaki @USAGI	:	improved accuracy of
  *						address validation timer.
+ *	Yuji SEKIYA @USAGI		:	Don't assign a same IPv6
+ *						address on a same interface.
  */
 
 #include <linux/config.h>
@@ -105,6 +107,8 @@
 static void addrconf_rs_timer(unsigned long data);
 static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifa);
 
+static int ipv6_chk_same_addr(const struct in6_addr *addr, struct net_device *dev);
+
 static struct notifier_block *inet6addr_chain;
 
 struct ipv6_devconf ipv6_devconf =
@@ -135,21 +139,19 @@
 	MAX_RTR_SOLICITATION_DELAY,	/* rtr solicit delay	*/
 };
 
-int ipv6_addr_type(struct in6_addr *addr)
+/* IPv6 Wildcard Address and Loopback Address defined by RFC2553 */
+const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
+const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
+
+int ipv6_addr_type(const struct in6_addr *addr)
 {
+	int type;
 	u32 st;
 
 	st = addr->s6_addr32[0];
 
-	/* Consider all addresses with the first three bits different of
-	   000 and 111 as unicasts.
-	 */
-	if ((st & htonl(0xE0000000)) != htonl(0x00000000) &&
-	    (st & htonl(0xE0000000)) != htonl(0xE0000000))
-		return IPV6_ADDR_UNICAST;
-
 	if ((st & htonl(0xFF000000)) == htonl(0xFF000000)) {
-		int type = IPV6_ADDR_MULTICAST;
+		type = IPV6_ADDR_MULTICAST;
 
 		switch((st & htonl(0x00FF0000))) {
 			case __constant_htonl(0x00010000):
@@ -166,29 +168,53 @@
 		};
 		return type;
 	}
+	/* check for reserved anycast addresses */
+	
+	if ((st & htonl(0xE0000000)) &&
+	    ((addr->s6_addr32[2] == htonl(0xFDFFFFFF) &&
+	    (addr->s6_addr32[3] | htonl(0x7F)) == (u32)~0) ||
+	    (addr->s6_addr32[2] == 0 && addr->s6_addr32[3] == 0)))
+		type = IPV6_ADDR_ANYCAST;
+	else
+		type = IPV6_ADDR_UNICAST;
+
+	/* Consider all addresses with the first three bits different of
+	   000 and 111 as finished.
+	 */
+	if ((st & htonl(0xE0000000)) != htonl(0x00000000) &&
+	    (st & htonl(0xE0000000)) != htonl(0xE0000000))
+		return type;
 	
 	if ((st & htonl(0xFFC00000)) == htonl(0xFE800000))
-		return (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_UNICAST);
+		return (IPV6_ADDR_LINKLOCAL | type);
 
 	if ((st & htonl(0xFFC00000)) == htonl(0xFEC00000))
-		return (IPV6_ADDR_SITELOCAL | IPV6_ADDR_UNICAST);
+		return (IPV6_ADDR_SITELOCAL | type);
 
 	if ((addr->s6_addr32[0] | addr->s6_addr32[1]) == 0) {
 		if (addr->s6_addr32[2] == 0) {
-			if (addr->s6_addr32[3] == 0)
+			if (addr->in6_u.u6_addr32[3] == 0)
 				return IPV6_ADDR_ANY;
 
 			if (addr->s6_addr32[3] == htonl(0x00000001))
-				return (IPV6_ADDR_LOOPBACK | IPV6_ADDR_UNICAST);
+				return (IPV6_ADDR_LOOPBACK | type);
 
-			return (IPV6_ADDR_COMPATv4 | IPV6_ADDR_UNICAST);
+			return (IPV6_ADDR_COMPATv4 | type);
 		}
 
 		if (addr->s6_addr32[2] == htonl(0x0000ffff))
 			return IPV6_ADDR_MAPPED;
 	}
 
-	return IPV6_ADDR_RESERVED;
+	st &= htonl(0xFF000000);
+	if (st == 0)
+		return IPV6_ADDR_RESERVED;
+	st &= htonl(0xFE000000);
+	if (st == htonl(0x02000000))
+		return IPV6_ADDR_RESERVED;	/* for NSAP */
+	if (st == htonl(0x04000000))
+		return IPV6_ADDR_RESERVED;	/* for IPX */
+	return type;
 }
 
 static void addrconf_del_timer(struct inet6_ifaddr *ifp)
@@ -224,7 +250,6 @@
 	add_timer(&ifp->timer);
 }
 
-
 /* Nobody refers to this device, we may destroy it. */
 
 void in6_dev_finish_destroy(struct inet6_dev *idev)
@@ -303,24 +328,90 @@
 	return idev;
 }
 
+void ipv6_addr_prefix(struct in6_addr *prefix,
+	struct in6_addr *addr, int prefix_len)
+{
+	unsigned long mask;
+	int ncopy, nbits;
+
+	memset(prefix, 0, sizeof(*prefix));
+
+	if (prefix_len <= 0)
+		return;
+	if (prefix_len > 128)
+		prefix_len = 128;
+
+	ncopy = prefix_len / 32;
+	switch (ncopy) {
+	case 4:	prefix->s6_addr32[3] = addr->s6_addr32[3];
+	case 3:	prefix->s6_addr32[2] = addr->s6_addr32[2];
+	case 2:	prefix->s6_addr32[1] = addr->s6_addr32[1];
+	case 1:	prefix->s6_addr32[0] = addr->s6_addr32[0];
+	case 0:	break;
+	}
+	nbits = prefix_len % 32;
+	if (nbits == 0)
+		return;
+
+	mask = ~((1 << (32 - nbits)) - 1);
+	mask = htonl(mask);
+
+	prefix->s6_addr32[ncopy] = addr->s6_addr32[ncopy] & mask;
+}
+
+
+static void dev_forward_change(struct inet6_dev *idev)
+{
+	struct net_device *dev;
+	struct inet6_ifaddr *ifa;
+	struct in6_addr addr;
+
+	if (!idev)
+		return;
+	dev = idev->dev;
+	if (dev && (dev->flags & IFF_MULTICAST)) {
+		ipv6_addr_all_routers(&addr);
+	
+		if (idev->cnf.forwarding)
+			ipv6_dev_mc_inc(dev, &addr);
+		else
+			ipv6_dev_mc_dec(dev, &addr);
+	}
+	for (ifa=idev->addr_list; ifa; ifa=ifa->if_next) {
+		ipv6_addr_prefix(&addr, &ifa->addr, ifa->prefix_len);
+		if (ipv6_addr_any(&addr))
+			continue;
+		if (idev->cnf.forwarding)
+			ipv6_dev_ac_inc(idev->dev, &addr);
+		else
+			ipv6_dev_ac_dec(idev->dev, &addr);
+	}
+}
+
+
 static void addrconf_forward_change(struct inet6_dev *idev)
 {
 	struct net_device *dev;
 
-	if (idev)
+	if (idev) {
+		dev_forward_change(idev);
 		return;
+	}
 
 	read_lock(&dev_base_lock);
 	for (dev=dev_base; dev; dev=dev->next) {
 		read_lock(&addrconf_lock);
 		idev = __in6_dev_get(dev);
-		if (idev)
+		if (idev) {
 			idev->cnf.forwarding = ipv6_devconf.forwarding;
+			dev_forward_change(idev);
+		}
 		read_unlock(&addrconf_lock);
 	}
 	read_unlock(&dev_base_lock);
 }
 
+
 /* Nobody refers to this ifaddr, destroy it */
 
 void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp)
@@ -347,17 +438,28 @@
 /* On success it returns ifp with increased reference count */
 
 static struct inet6_ifaddr *
-ipv6_add_addr(struct inet6_dev *idev, struct in6_addr *addr, int pfxlen,
+ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
 	      int scope, unsigned flags)
 {
 	struct inet6_ifaddr *ifa;
 	int hash;
+	static spinlock_t lock = SPIN_LOCK_UNLOCKED;
+
+	spin_lock_bh(&lock);
+
+	/* Ignore adding duplicate addresses on an interface */
+	if (ipv6_chk_same_addr(addr, idev->dev)) {
+		spin_unlock_bh(&lock);
+		ADBG(("ipv6_add_addr: already assigned\n"));
+		return ERR_PTR(-EEXIST);
+	}
 
 	ifa = kmalloc(sizeof(struct inet6_ifaddr), GFP_ATOMIC);
 
 	if (ifa == NULL) {
+		spin_unlock_bh(&lock);
 		ADBG(("ipv6_add_addr: malloc failed\n"));
-		return NULL;
+		return ERR_PTR(-ENOBUFS);
 	}
 
 	memset(ifa, 0, sizeof(struct inet6_ifaddr));
@@ -373,8 +475,9 @@
 	read_lock(&addrconf_lock);
 	if (idev->dead) {
 		read_unlock(&addrconf_lock);
+		spin_unlock_bh(&lock);
 		kfree(ifa);
-		return NULL;
+		return ERR_PTR(-ENODEV);	/*XXX*/
 	}
 
 	inet6_ifa_count++;
@@ -399,6 +502,7 @@
 	in6_ifa_hold(ifa);
 	write_unlock_bh(&idev->lock);
 	read_unlock(&addrconf_lock);
+	spin_unlock_bh(&lock);
 
 	notifier_call_chain(&inet6addr_chain,NETDEV_UP,ifa);
 
@@ -458,29 +562,20 @@
  *		an address of the attached interface 
  *	iii)	don't use deprecated addresses
  */
-int ipv6_get_saddr(struct dst_entry *dst,
-		   struct in6_addr *daddr, struct in6_addr *saddr)
+int ipv6_dev_get_saddr(struct net_device *dev,
+		   struct in6_addr *daddr, struct in6_addr *saddr, int onlink)
 {
-	int scope;
 	struct inet6_ifaddr *ifp = NULL;
 	struct inet6_ifaddr *match = NULL;
-	struct net_device *dev = NULL;
 	struct inet6_dev *idev;
-	struct rt6_info *rt;
+	int scope;
 	int err;
 
-	rt = (struct rt6_info *) dst;
-	if (rt)
-		dev = rt->rt6i_dev;
 
-	scope = ipv6_addr_scope(daddr);
-	if (rt && (rt->rt6i_flags & RTF_ALLONLINK)) {
-		/*
-		 *	route for the "all destinations on link" rule
-		 *	when no routers are present
-		 */
+	if (!onlink)
+		scope = ipv6_addr_scope(daddr);
+	else
 		scope = IFA_LINK;
-	}
 
 	/*
 	 *	known dev
@@ -568,6 +663,24 @@
 	return err;
 }
 
+
+int ipv6_get_saddr(struct dst_entry *dst,
+		   struct in6_addr *daddr, struct in6_addr *saddr)
+{
+	struct rt6_info *rt;
+	struct net_device *dev = NULL;
+	int onlink;
+
+	rt = (struct rt6_info *) dst;
+	if (rt)
+		dev = rt->rt6i_dev;
+
+	onlink = (rt && (rt->rt6i_flags & RTF_ALLONLINK));
+
+	return ipv6_dev_get_saddr(dev, daddr, saddr, onlink);
+}
+
+
 int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
 {
 	struct inet6_dev *idev;
@@ -621,6 +734,23 @@
 	return ifp != NULL;
 }
 
+static
+int ipv6_chk_same_addr(const struct in6_addr *addr, struct net_device *dev)
+{
+	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 (ipv6_addr_cmp(&ifp->addr, addr) == 0) {
+			if (dev == NULL || ifp->idev->dev == dev)
+				break;
+		}
+	}
+	read_unlock_bh(&addrconf_hash_lock);
+	return ifp != NULL;
+}
+
 struct inet6_ifaddr * ipv6_get_ifaddr(struct in6_addr *addr, struct net_device *dev)
 {
 	struct inet6_ifaddr * ifp;
@@ -660,7 +790,7 @@
 
 /* Join to solicited addr multicast group. */
 
-static void addrconf_join_solict(struct net_device *dev, struct in6_addr *addr)
+void addrconf_join_solict(struct net_device *dev, struct in6_addr *addr)
 {
 	struct in6_addr maddr;
 
@@ -671,7 +801,7 @@
 	ipv6_dev_mc_inc(dev, &maddr);
 }
 
-static void addrconf_leave_solict(struct net_device *dev, struct in6_addr *addr)
+void addrconf_leave_solict(struct net_device *dev, struct in6_addr *addr)
 {
 	struct in6_addr maddr;
 
@@ -744,7 +874,7 @@
 	if (dev->type == ARPHRD_SIT && (dev->flags&IFF_POINTOPOINT))
 		rtmsg.rtmsg_flags |= RTF_NONEXTHOP;
 
-	ip6_route_add(&rtmsg);
+	ip6_route_add(&rtmsg, NULL);
 }
 
 /* Create "default" multicast route to the interface */
@@ -761,7 +891,7 @@
 	rtmsg.rtmsg_ifindex = dev->ifindex;
 	rtmsg.rtmsg_flags = RTF_UP|RTF_ADDRCONF;
 	rtmsg.rtmsg_type = RTMSG_NEWROUTE;
-	ip6_route_add(&rtmsg);
+	ip6_route_add(&rtmsg, NULL);
 }
 
 static void sit_route_add(struct net_device *dev)
@@ -778,7 +908,7 @@
 	rtmsg.rtmsg_flags	= RTF_UP|RTF_NONEXTHOP;
 	rtmsg.rtmsg_ifindex	= dev->ifindex;
 
-	ip6_route_add(&rtmsg);
+	ip6_route_add(&rtmsg, NULL);
 }
 
 static void addrconf_add_lroute(struct net_device *dev)
@@ -786,7 +916,7 @@
 	struct in6_addr addr;
 
 	ipv6_addr_set(&addr,  htonl(0xFE800000), 0, 0, 0);
-	addrconf_prefix_route(&addr, 10, dev, 0, RTF_ADDRCONF);
+	addrconf_prefix_route(&addr, 64, dev, 0, RTF_ADDRCONF);
 }
 
 static struct inet6_dev *addrconf_add_dev(struct net_device *dev)
@@ -870,7 +1000,7 @@
 	if (rt && ((rt->rt6i_flags & (RTF_GATEWAY | RTF_DEFAULT)) == 0)) {
 		if (rt->rt6i_flags&RTF_EXPIRES) {
 			if (pinfo->onlink == 0 || valid_lft == 0) {
-				ip6_del_rt(rt);
+				ip6_del_rt(rt, NULL);
 				rt = NULL;
 			} else {
 				rt->rt6i_expires = rt_expires;
@@ -919,7 +1049,7 @@
 				ifp = ipv6_add_addr(in6_dev, &addr, pinfo->prefix_len,
 						    addr_type&IPV6_ADDR_SCOPE_MASK, 0);
 
-			if (ifp == NULL) {
+			if (IS_ERR(ifp)) {
 				in6_dev_put(in6_dev);
 				return;
 			}
@@ -947,6 +1077,7 @@
 				ipv6_ifa_notify((flags&IFA_F_DEPRECATED) ?
 						0 : RTM_NEWADDR, ifp);
 			in6_ifa_put(ifp);
+			addrconf_verify(0);
 		}
 	}
 	in6_dev_put(in6_dev);
@@ -1033,13 +1164,14 @@
 
 	scope = ipv6_addr_scope(pfx);
 
-	if ((ifp = ipv6_add_addr(idev, pfx, plen, scope, IFA_F_PERMANENT)) != NULL) {
+	ifp = ipv6_add_addr(idev, pfx, plen, scope, IFA_F_PERMANENT);
+	if (!IS_ERR(ifp)) {
 		addrconf_dad_start(ifp);
 		in6_ifa_put(ifp);
 		return 0;
 	}
 
-	return -ENOBUFS;
+	return PTR_ERR(ifp);
 }
 
 static int inet6_addr_del(int ifindex, struct in6_addr *pfx, int plen)
@@ -1131,7 +1263,7 @@
 
 	if (addr.s6_addr32[3]) {
 		ifp = ipv6_add_addr(idev, &addr, 128, scope, IFA_F_PERMANENT);
-		if (ifp) {
+		if (!IS_ERR(ifp)) {
 			spin_lock_bh(&ifp->lock);
 			ifp->flags &= ~IFA_F_TENTATIVE;
 			spin_unlock_bh(&ifp->lock);
@@ -1161,13 +1293,13 @@
 					flag |= IFA_HOST;
 				}
 				if (idev->dev->flags&IFF_POINTOPOINT)
-					plen = 10;
+					plen = 64;
 				else
 					plen = 96;
 
 				ifp = ipv6_add_addr(idev, &addr, plen, flag,
 						    IFA_F_PERMANENT);
-				if (ifp) {
+				if (!IS_ERR(ifp)) {
 					spin_lock_bh(&ifp->lock);
 					ifp->flags &= ~IFA_F_TENTATIVE;
 					spin_unlock_bh(&ifp->lock);
@@ -1181,7 +1313,6 @@
 
 static void init_loopback(struct net_device *dev)
 {
-	struct in6_addr addr;
 	struct inet6_dev  *idev;
 	struct inet6_ifaddr * ifp;
 
@@ -1189,16 +1320,13 @@
 
 	ASSERT_RTNL();
 
-	memset(&addr, 0, sizeof(struct in6_addr));
-	addr.s6_addr32[3] = htonl(0x00000001);
-
 	if ((idev = ipv6_find_idev(dev)) == NULL) {
 		printk(KERN_DEBUG "init loopback: add_dev failed\n");
 		return;
 	}
 
-	ifp = ipv6_add_addr(idev, &addr, 128, IFA_HOST, IFA_F_PERMANENT);
-	if (ifp) {
+	ifp = ipv6_add_addr(idev, &in6addr_loopback, 128, IFA_HOST, IFA_F_PERMANENT);
+	if (!IS_ERR(ifp)) {
 		spin_lock_bh(&ifp->lock);
 		ifp->flags &= ~IFA_F_TENTATIVE;
 		spin_unlock_bh(&ifp->lock);
@@ -1211,8 +1339,8 @@
 {
 	struct inet6_ifaddr * ifp;
 
-	ifp = ipv6_add_addr(idev, addr, 10, IFA_LINK, IFA_F_PERMANENT);
-	if (ifp) {
+	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, IFA_F_PERMANENT);
+	if (!IS_ERR(ifp)) {
 		addrconf_dad_start(ifp);
 		in6_ifa_put(ifp);
 	}
@@ -1455,7 +1583,7 @@
 
 		rtmsg.rtmsg_ifindex = ifp->idev->dev->ifindex;
 
-		ip6_route_add(&rtmsg);
+		ip6_route_add(&rtmsg, NULL);
 	}
 
 out:
@@ -1564,6 +1692,14 @@
 		addrconf_mod_timer(ifp, AC_RS, ifp->idev->cnf.rtr_solicit_interval);
 		spin_unlock_bh(&ifp->lock);
 	}
+
+	if (ifp->idev->cnf.forwarding) {
+		struct in6_addr addr;
+
+		ipv6_addr_prefix(&addr, &ifp->addr, ifp->prefix_len);
+		if (!ipv6_addr_any(&addr))
+			ipv6_dev_ac_inc(ifp->idev->dev, &addr);
+	}
 }
 
 #ifdef CONFIG_PROC_FS
@@ -1863,6 +1999,13 @@
 		break;
 	case RTM_DELADDR:
 		addrconf_leave_solict(ifp->idev->dev, &ifp->addr);
+		if (ifp->idev->cnf.forwarding) {
+			struct in6_addr addr;
+
+			ipv6_addr_prefix(&addr, &ifp->addr, ifp->prefix_len);
+			if (!ipv6_addr_any(&addr))
+				ipv6_dev_ac_dec(ifp->idev->dev, &addr);
+		}
 		if (!ipv6_chk_addr(&ifp->addr, NULL))
 			ip6_rt_addr_del(&ifp->addr, ifp->idev->dev);
 		break;
@@ -1885,11 +2028,7 @@
 		struct inet6_dev *idev = NULL;
 
 		if (valp != &ipv6_devconf.forwarding) {
-			struct net_device *dev = dev_get_by_index(ctl->ctl_name);
-			if (dev) {
-				idev = in6_dev_get(dev);
-				dev_put(dev);
-			}
+			idev = (struct inet6_dev *)ctl->extra1;
 			if (idev == NULL)
 				return ret;
 		} else
@@ -1899,8 +2038,6 @@
 
 		if (*valp)
 			rt6_purge_dflt_routers(0);
-		if (idev)
-			in6_dev_put(idev);
 	}
 
         return ret;
@@ -1977,6 +2114,7 @@
 	for (i=0; i<sizeof(t->addrconf_vars)/sizeof(t->addrconf_vars[0])-1; i++) {
 		t->addrconf_vars[i].data += (char*)p - (char*)&ipv6_devconf;
 		t->addrconf_vars[i].de = NULL;
+		t->addrconf_vars[i].extra1 = idev; /* embedded; no ref */
 	}
 	if (dev) {
 		t->addrconf_dev[0].procname = dev->name;

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)