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

Next file: linux/net/ipv6/af_inet6.c
Previous file: linux/net/ipv6/Config.in
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.67/linux/net/ipv6/addrconf.c linux/net/ipv6/addrconf.c
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: addrconf.c,v 1.21 1997/08/09 03:44:24 davem Exp $
+ *	$Id: addrconf.c,v 1.28 1997/11/05 20:20:43 kuznet Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -31,6 +31,7 @@
 #include <linux/netdevice.h>
 #include <linux/if_arp.h>
 #include <linux/route.h>
+#include <linux/inetdevice.h>
 #include <linux/init.h>
 
 #include <linux/proc_fs.h>
@@ -42,7 +43,8 @@
 #include <net/ndisc.h>
 #include <net/ip6_route.h>
 #include <net/addrconf.h>
-#include <net/sit.h>
+#include <net/ip.h>
+#include <linux/if_tunnel.h>
 
 #include <asm/uaccess.h>
 
@@ -92,12 +94,11 @@
 
 	st = addr->s6_addr32[0];
 
-	/* 
-	 * UCast Provider Based Address
-	 * 0x4/3
+	/* Consider all addresses with the first three bits different of
+	   000 and 111 as unicasts.
 	 */
-
-	if ((st & __constant_htonl(0xE0000000)) == __constant_htonl(0x40000000))
+	if ((st & __constant_htonl(0xE0000000)) != __constant_htonl(0x00000000) &&
+	    (st & __constant_htonl(0xE0000000)) != __constant_htonl(0xE0000000))
 		return IPV6_ADDR_UNICAST;
 
 	if ((st & __constant_htonl(0xFF000000)) == __constant_htonl(0xFF000000)) {
@@ -184,6 +185,8 @@
 				printk(KERN_DEBUG "joining all-routers\n");
 #endif
 				idev->router = 1;
+
+				/* Wrong. It is user level function. */
 				ipv6_addr_all_routers(&maddr);
 				ipv6_dev_mc_inc(idev->dev, &maddr);
 			}
@@ -222,6 +225,7 @@
 	memcpy(&ifa->addr, addr, sizeof(struct in6_addr));
 
 	init_timer(&ifa->timer);
+	ifa->timer.data = (unsigned long) ifa;
 	ifa->scope = scope;
 	ifa->idev = idev;
 
@@ -361,7 +365,7 @@
 	}
 
 out:
-	if (ifp == NULL && match)
+	if (ifp == NULL)
 		ifp = match;
 	atomic_dec(&addr_list_lock);
 	return ifp;
@@ -410,6 +414,157 @@
 	return ifp;	
 }
 
+/* Join to solicited addr multicast group. */
+
+static void addrconf_join_solict(struct device *dev, struct in6_addr *addr)
+{
+	struct in6_addr maddr;
+
+	addrconf_addr_solict_mult(addr, &maddr);
+	ipv6_dev_mc_inc(dev, &maddr);
+}
+
+#ifdef CONFIG_IPV6_EUI64
+static int ipv6_generate_eui64(u8 *eui, struct device *dev)
+{
+	switch (dev->type) {
+	case ARPHRD_ETHER:
+		if (dev->addr_len != ETH_ALEN)
+			return -1;
+		memcpy(eui, dev->dev_addr, 3);
+		memcpy(eui + 5, dev->dev_addr+3, 3);
+		eui[3] = 0xFF;
+		eui[4] = 0xFE;
+		eui[0] ^= 2;
+		return 0;
+	}
+	return -1;
+}
+#endif
+
+/*
+ *	Add prefix route.
+ */
+
+static void
+addrconf_prefix_route(struct in6_addr *pfx, int plen, struct device *dev,
+		      unsigned long info)
+{
+	struct in6_rtmsg rtmsg;
+	int err;
+
+	memset(&rtmsg, 0, sizeof(rtmsg));
+	memcpy(&rtmsg.rtmsg_dst, pfx, sizeof(struct in6_addr));
+	rtmsg.rtmsg_dst_len = plen;
+	rtmsg.rtmsg_metric = IP6_RT_PRIO_ADDRCONF;
+	rtmsg.rtmsg_ifindex = dev->ifindex;
+	rtmsg.rtmsg_info = info;
+	rtmsg.rtmsg_flags = RTF_UP|RTF_ADDRCONF;
+
+	/* Prevent useless cloning on PtP SIT.
+	   This thing is done here expecting that the whole
+	   class of non-broadcast devices need not cloning.
+	 */
+	if (dev->type == ARPHRD_SIT && (dev->flags&IFF_POINTOPOINT))
+		rtmsg.rtmsg_flags |= RTF_NONEXTHOP;
+	rtmsg.rtmsg_type = RTMSG_NEWROUTE;
+
+	ip6_route_add(&rtmsg, &err);
+
+	if (err)
+		printk(KERN_DEBUG "IPv6: error %d adding prefix route\n", err);
+}
+
+/* Create "default" multicast route to the interface */
+
+static void addrconf_add_mroute(struct device *dev)
+{
+	struct in6_rtmsg rtmsg;
+	struct rt6_info *rt;
+	int err;
+
+	memset(&rtmsg, 0, sizeof(rtmsg));
+	ipv6_addr_set(&rtmsg.rtmsg_dst,
+		      __constant_htonl(0xFF000000), 0, 0, 0);
+	rtmsg.rtmsg_dst_len = 8;
+	rtmsg.rtmsg_metric = IP6_RT_PRIO_ADDRCONF;
+	rtmsg.rtmsg_ifindex = dev->ifindex;
+	rtmsg.rtmsg_flags = RTF_UP|RTF_ADDRCONF;
+	rtmsg.rtmsg_type = RTMSG_NEWROUTE;
+
+	rt = ip6_route_add(&rtmsg, &err);
+
+	/*
+	 * Pedro makes interesting thing here, he attached
+	 * fake nexthop to multicast route.
+	 * It is trick to avoid cloning, ugly, but efficient. --ANK
+	 */
+
+	if (err)
+		printk(KERN_DEBUG "IPv6: error %d adding mroute\n", err);
+	else
+		rt->rt6i_nexthop = ndisc_get_neigh(dev, &rtmsg.rtmsg_dst);
+}
+
+static void sit_route_add(struct device *dev)
+{
+	struct in6_rtmsg rtmsg;
+	struct rt6_info *rt;
+	int err;
+
+	memset(&rtmsg, 0, sizeof(rtmsg));
+
+	rtmsg.rtmsg_type	= RTMSG_NEWROUTE;
+	rtmsg.rtmsg_metric	= IP6_RT_PRIO_ADDRCONF;
+
+	/* prefix length - 96 bytes "::d.d.d.d" */
+	rtmsg.rtmsg_dst_len	= 96;
+	rtmsg.rtmsg_flags	= RTF_UP;
+	rtmsg.rtmsg_ifindex	= dev->ifindex;
+
+	rt = ip6_route_add(&rtmsg, &err);
+	
+	/* See comment in addrconf_add_mroute.
+	 * It is the same trick, but to avoid cloning for direct
+	 * sit routes i.e. IPv4 comaptible destinations.
+	 */
+	if (err)
+		printk(KERN_DEBUG "sit_route_add: error %d in route_add\n", err);
+	else
+		rt->rt6i_nexthop = ndisc_get_neigh(dev, &rtmsg.rtmsg_dst);
+}
+
+static void addrconf_add_lroute(struct device *dev)
+{
+	struct in6_addr addr;
+
+	ipv6_addr_set(&addr,  __constant_htonl(0xFE800000), 0, 0, 0);
+	addrconf_prefix_route(&addr, 10, dev, 0);
+}
+
+static struct inet6_dev *addrconf_add_dev(struct device *dev)
+{
+	struct in6_addr maddr;
+	struct inet6_dev *idev;
+
+	if ((idev = ipv6_get_idev(dev)) == NULL) {
+		idev = ipv6_add_dev(dev);
+		if (idev == NULL)
+			return NULL;
+	}
+
+	/* Add default multicast route */
+	addrconf_add_mroute(dev);
+
+	/* Add link local route */
+	addrconf_add_lroute(dev);
+	
+	/* Join to all nodes multicast group. */
+	ipv6_addr_all_nodes(&maddr);
+	ipv6_dev_mc_inc(dev, &maddr);
+	return idev;
+}
+
 void addrconf_prefix_rcv(struct device *dev, u8 *opt, int len)
 {
 	struct prefix_info *pinfo;
@@ -432,7 +587,7 @@
 
 	addr_type = ipv6_addr_type(&pinfo->prefix);
 
-	if (addr_type & IPV6_ADDR_LINKLOCAL)
+	if (addr_type & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL))
 		return;
 
 	valid_lft = ntohl(pinfo->valid);
@@ -470,23 +625,12 @@
 			rt->rt6i_expires = rt_expires;
 		}
 	} else if (pinfo->onlink && valid_lft) {
-		struct in6_rtmsg rtmsg;
-		int err;
-
-		memset(&rtmsg, 0, sizeof(rtmsg));
-		
-		printk(KERN_DEBUG "adding on link route\n");
-
-		ipv6_addr_copy(&rtmsg.rtmsg_dst, &pinfo->prefix);
-		rtmsg.rtmsg_dst_len	= pinfo->prefix_len;
-		rtmsg.rtmsg_metric	= IP6_RT_PRIO_ADDRCONF;
-		rtmsg.rtmsg_ifindex	= dev->ifindex;
-		rtmsg.rtmsg_flags	= RTF_UP | RTF_ADDRCONF;
-		rtmsg.rtmsg_info	= rt_expires;
-
-		ip6_route_add(&rtmsg, &err);
+		addrconf_prefix_route(&pinfo->prefix, pinfo->prefix_len,
+				      dev, rt_expires);
 	}
 
+	/* Try to figure out our local address for this prefix */
+
 	if (pinfo->autoconf && ipv6_config.autoconf) {
 		struct inet6_ifaddr * ifp;
 		struct in6_addr addr;
@@ -494,33 +638,41 @@
 
 		plen = pinfo->prefix_len >> 3;
 
-		if (plen + dev->addr_len == sizeof(struct in6_addr)) {
+#ifdef CONFIG_IPV6_EUI64
+		if (pinfo->prefix_len == 64) {
+			memcpy(&addr, &pinfo->prefix, 8);
+			if (ipv6_generate_eui64(addr.s6_addr + 8, dev))
+				return;
+			goto ok;
+		}
+#endif
+#ifndef CONFIG_IPV6_NO_PB
+		if (pinfo->prefix_len == ((sizeof(struct in6_addr) - dev->addr_len)<<3)) {
 			memcpy(&addr, &pinfo->prefix, plen);
 			memcpy(addr.s6_addr + plen, dev->dev_addr,
 			       dev->addr_len);
-		} else {
-			ADBG(("addrconf: prefix_len invalid\n"));
-			return;
+			goto ok;
 		}
+#endif
+		printk(KERN_DEBUG "IPv6 addrconf: prefix with wrong length %d\n", pinfo->prefix_len);
+		return;
 
+ok:
 		ifp = ipv6_chk_addr(&addr);
 
 		if (ifp == NULL && valid_lft) {
 			struct inet6_dev *in6_dev = ipv6_get_idev(dev);
 
-			if (in6_dev == NULL)
-				ADBG(("addrconf: device not configured\n"));
-			
+			if (in6_dev == NULL) {
+				printk(KERN_DEBUG "addrconf: device %s not configured\n", dev->name);
+				return;
+			}
+
 			ifp = ipv6_add_addr(in6_dev, &addr,
 					    addr_type & IPV6_ADDR_SCOPE_MASK);
 
-			if (dev->flags & IFF_MULTICAST) {
-				struct in6_addr maddr;
-
-				/* Join to solicited addr multicast group. */
-				addrconf_addr_solict_mult(&addr, &maddr);
-				ipv6_dev_mc_inc(dev, &maddr);
-			}
+			if (ifp == NULL)
+				return;
 
 			ifp->prefix_len = pinfo->prefix_len;
 
@@ -564,17 +716,32 @@
 	}
 
 	if (dev->type == ARPHRD_SIT) {
-		struct device *dev;
-		
+		struct ifreq ifr;
+		mm_segment_t	oldfs;
+		struct ip_tunnel_parm p;
+
 		if (!(ipv6_addr_type(&ireq.ifr6_addr) & IPV6_ADDR_COMPATv4))
 			return -EADDRNOTAVAIL;
-		
-		dev = sit_add_tunnel(ireq.ifr6_addr.s6_addr32[3]);
-		
-		if (dev == NULL)
-			err = -ENODEV;
-		else
-			err = 0;
+
+		memset(&p, 0, sizeof(p));
+		p.iph.daddr = ireq.ifr6_addr.s6_addr32[3];
+		p.iph.saddr = 0;
+		p.iph.version = 4;
+		p.iph.ihl = 5;
+		p.iph.protocol = IPPROTO_IPV6;
+		p.iph.ttl = 64;
+		ifr.ifr_ifru.ifru_data = (void*)&p;
+
+		oldfs = get_fs(); set_fs(KERNEL_DS);
+		err = dev->do_ioctl(dev, &ifr, SIOCADDTUNNEL);
+		set_fs(oldfs);
+
+		if (err == 0) {
+			err = -ENOBUFS;
+			if ((dev = dev_get(p.name)) == NULL)
+				goto err_exit;
+			err = dev_open(dev);
+		}
 	}
 
 err_exit:
@@ -595,38 +762,27 @@
 	if (!suser())
 		return -EPERM;
 	
-	if(copy_from_user(&ireq, arg, sizeof(struct in6_ifreq)))
+	if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq)))
 		return -EFAULT;
 
-	if((dev = dev_get_by_index(ireq.ifr6_ifindex)) == NULL)
-		return -EINVAL;
+	if ((dev = dev_get_by_index(ireq.ifr6_ifindex)) == NULL)
+		return -ENODEV;
+	
+	if (!(dev->flags&IFF_UP))
+		return -ENETDOWN;
 
-	if ((idev = ipv6_get_idev(dev)) == NULL)
-		return -EINVAL;
+	if ((idev = addrconf_add_dev(dev)) == NULL)
+		return -ENOBUFS;
 
 	scope = ipv6_addr_scope(&ireq.ifr6_addr);
 
 	if((ifp = ipv6_add_addr(idev, &ireq.ifr6_addr, scope)) == NULL)
 		return -ENOMEM;
 
-	ifp->prefix_len = 128;
-
-	if (dev->flags & IFF_MULTICAST) {
-		struct in6_addr maddr;
-
-		/* Join to solicited addr multicast group. */
-		addrconf_addr_solict_mult(&ireq.ifr6_addr, &maddr);
-		ipv6_dev_mc_inc(dev, &maddr);
-	}
-
 	ifp->prefix_len = ireq.ifr6_prefixlen;
 	ifp->flags |= ADDR_PERMANENT;
 
-	if (!(dev->flags & (IFF_NOARP|IFF_LOOPBACK)))
-		addrconf_dad_start(ifp);
-	else
-		ip6_rt_addr_add(&ifp->addr, dev);
-
+	addrconf_dad_start(ifp);
 	return 0;
 }
 
@@ -645,90 +801,22 @@
 		return -EFAULT;
 
 	if ((dev = dev_get_by_index(ireq.ifr6_ifindex)) == NULL)
-		return -EINVAL;
+		return -ENODEV;
 
 	if ((idev = ipv6_get_idev(dev)) == NULL)
-		return -EINVAL;
+		return -ENXIO;
 
 	scope = ipv6_addr_scope(&ireq.ifr6_addr);
 
 	for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) {
-	  if (ifp->scope == scope && 
-	    (!memcmp(&ireq.ifr6_addr, &ifp->addr, sizeof(struct in6_addr)))) {
-	    ipv6_del_addr(ifp);
-	    break;
-	  }
-	}
-	
-	return 0;
-}
-
-static void sit_route_add(struct device *dev)
-{
-	struct in6_rtmsg rtmsg;
-	struct rt6_info *rt;
-	int err;
-
-	ADBG(("sit_route_add(%s): ", dev->name));
-	memset(&rtmsg, 0, sizeof(rtmsg));
-
-	rtmsg.rtmsg_type			= RTMSG_NEWROUTE;
-	rtmsg.rtmsg_metric			= IP6_RT_PRIO_ADDRCONF;
-
-	if (dev->pa_dstaddr == 0) {
-		ADBG(("pa_dstaddr=0, "));
-		/* prefix length - 96 bytes "::d.d.d.d" */
-		rtmsg.rtmsg_dst_len		= 96;
-		rtmsg.rtmsg_flags		= RTF_NONEXTHOP|RTF_UP;
-	} else {
-		ADBG(("pa_dstaddr=%08x, ", dev->pa_dstaddr));
-		rtmsg.rtmsg_dst_len 		= 10;
-		rtmsg.rtmsg_dst.s6_addr32[0]	= __constant_htonl(0xfe800000);
-		rtmsg.rtmsg_dst.s6_addr32[3]	= dev->pa_dstaddr;
-		rtmsg.rtmsg_gateway.s6_addr32[3]= dev->pa_dstaddr;
-		rtmsg.rtmsg_flags		= RTF_UP;
-	}
-
-	rtmsg.rtmsg_ifindex 			= dev->ifindex; 
-	ADBG(("doing ip6_route_add()\n"));
-	rt = ip6_route_add(&rtmsg, &err);
-	
-	if (err) {
-#if ACONF_DEBUG >= 1
-		printk(KERN_DEBUG "sit_route_add: error %d in route_add\n", err);
-#endif
-	}
-
-	ADBG(("sit_route_add(cont): "));
-	if (dev->pa_dstaddr) {
-		struct rt6_info *mrt;
-
-		ADBG(("pa_dstaddr != 0, "));
-		rt->rt6i_nexthop = ndisc_get_neigh(dev, &rtmsg.rtmsg_gateway);
-		if (rt->rt6i_nexthop == NULL) {
-			ADBG(("can't get neighbour\n"));
-			printk(KERN_DEBUG "sit_route: get_neigh failed\n");
+		if (ifp->scope == scope && 
+		    (!memcmp(&ireq.ifr6_addr, &ifp->addr, sizeof(struct in6_addr)))) {
+			ipv6_del_addr(ifp);
+			break;
 		}
-
-		/*
-		 *	Add multicast route.
-		 */
-		ADBG(("add MULT, "));
-		ipv6_addr_set(&rtmsg.rtmsg_dst, __constant_htonl(0xFF000000), 0, 0, 0);
-
-		rtmsg.rtmsg_dst_len = 8;
-		rtmsg.rtmsg_flags = RTF_UP;
-		rtmsg.rtmsg_metric = IP6_RT_PRIO_ADDRCONF;
-
-		memset(&rtmsg.rtmsg_gateway, 0, sizeof(struct in6_addr));
-		ADBG(("doing ip6_route_add()\n"));
-		mrt = ip6_route_add(&rtmsg, &err);
-
-		if (mrt)
-			mrt->rt6i_nexthop = ndisc_get_neigh(dev, &rtmsg.rtmsg_dst);
-	} else {
-		ADBG(("pa_dstaddr==0\n"));
 	}
+
+	return 0;
 }
 
 static void sit_add_v4_addrs(struct inet6_dev *idev)
@@ -739,34 +827,55 @@
 	int scope;
 
 	memset(&addr, 0, sizeof(struct in6_addr));
+	memcpy(&addr.s6_addr32[3], idev->dev->dev_addr, 4);
 
-	if (idev->dev->pa_dstaddr) {
+	if (idev->dev->flags&IFF_POINTOPOINT) {
 		addr.s6_addr32[0] = __constant_htonl(0xfe800000);
 		scope = IFA_LINK;
 	} else {
 		scope = IPV6_ADDR_COMPATv4;
 	}
 
+	if (addr.s6_addr32[3]) {
+		ifp = ipv6_add_addr(idev, &addr, scope);
+		if (ifp) {
+			ifp->flags |= ADDR_PERMANENT;
+			ifp->prefix_len = 128;
+			ip6_rt_addr_add(&ifp->addr, idev->dev);
+		}
+		return;
+	}
+
         for (dev = dev_base; dev != NULL; dev = dev->next) {
-		if (dev->family == AF_INET && (dev->flags & IFF_UP)) {
+		if (dev->ip_ptr && (dev->flags & IFF_UP)) {
+			struct in_device * in_dev = dev->ip_ptr;
+			struct in_ifaddr * ifa;
+
 			int flag = scope;
-			
-			addr.s6_addr32[3] = dev->pa_addr;
 
-			if (dev->flags & IFF_LOOPBACK) {
-				if (idev->dev->pa_dstaddr)
-					continue;
+			for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
+				addr.s6_addr32[3] = ifa->ifa_local;
 				
-				flag |= IFA_HOST;
-			}
-
-			ifp = ipv6_add_addr(idev, &addr, flag);
+				if (ifa->ifa_scope == RT_SCOPE_LINK)
+					continue;
+				if (ifa->ifa_scope >= RT_SCOPE_HOST) {
+					if (idev->dev->flags&IFF_POINTOPOINT)
+						continue;
+					flag |= IFA_HOST;
+				}
 
-			if (ifp == NULL)
-				continue;
+				ifp = ipv6_add_addr(idev, &addr, flag);
+			
+				if (ifp == NULL)
+					continue;
 
-			ifp->flags |= ADDR_PERMANENT;
-			ip6_rt_addr_add(&ifp->addr, dev);
+				if (idev->dev->flags&IFF_POINTOPOINT)
+					ifp->prefix_len = 10;
+				else
+					ifp->prefix_len = 96;
+				ifp->flags |= ADDR_PERMANENT;
+				ip6_rt_addr_add(&ifp->addr, dev);
+			}
 		}
         }
 }
@@ -804,56 +913,98 @@
 		printk(KERN_DEBUG "init_loopback: error in route_add\n");
 }
 
-static void addrconf_eth_config(struct device *dev)
+static void addrconf_add_linklocal(struct inet6_dev *idev, struct in6_addr *addr)
+{
+	struct inet6_ifaddr * ifp;
+
+	ifp = ipv6_add_addr(idev, addr, IFA_LINK);
+	if (ifp == NULL)
+		return;
+
+	ifp->flags = ADDR_PERMANENT;
+	ifp->prefix_len = 10;
+
+	addrconf_dad_start(ifp);
+}
+
+static void addrconf_dev_config(struct device *dev)
 {
 	struct in6_addr addr;
 	struct in6_addr maddr;
-	struct inet6_ifaddr * ifp;
 	struct inet6_dev    * idev;
 
+	if (dev->type != ARPHRD_ETHER) {
+		/* Alas, we support only ethernet autoconfiguration. */
+		return;
+	}
+
+	idev = addrconf_add_dev(dev);
+	if (idev == NULL)
+		return;
+
+#ifdef CONFIG_IPV6_EUI64
 	memset(&addr, 0, sizeof(struct in6_addr));
 
-	/* Generate link local address. */
 	addr.s6_addr[0] = 0xFE;
 	addr.s6_addr[1] = 0x80;
 
-	memcpy(addr.s6_addr + (sizeof(struct in6_addr) - dev->addr_len), 
-	       dev->dev_addr, dev->addr_len);
+	if (ipv6_generate_eui64(addr.s6_addr + 8, dev) == 0)
+		addrconf_add_linklocal(idev, &addr);
+#endif
 
-	idev = ipv6_add_dev(dev);
-	if (idev == NULL)
-		return;
-	
-	ifp = ipv6_add_addr(idev, &addr, IFA_LINK);
-	if (ifp == NULL)
-		return;
+#ifndef CONFIG_IPV6_NO_PB
+	memset(&addr, 0, sizeof(struct in6_addr));
 
-	ifp->flags = ADDR_PERMANENT;
-	ifp->prefix_len = 10;
+	addr.s6_addr[0] = 0xFE;
+	addr.s6_addr[1] = 0x80;
 
-	/* Join to all nodes multicast group. */
-	ipv6_addr_all_nodes(&maddr);
-	ipv6_dev_mc_inc(dev, &maddr);
+	memcpy(addr.s6_addr + (sizeof(struct in6_addr) - dev->addr_len), 
+	       dev->dev_addr, dev->addr_len);
+	addrconf_add_linklocal(idev, &addr);
+#endif
 
 	if (ipv6_config.forwarding) {
 		idev->router = 1;
+
+		/* It is wrong.
+		   It is routing daemon or radvd that must make it,
+		   rather than kernel.
+		 */
 		ipv6_addr_all_routers(&maddr);
 		ipv6_dev_mc_inc(dev, &maddr);
 	}
+}
 
-	/* Join to solicited addr multicast group. */
-	addrconf_addr_solict_mult(&addr, &maddr);
-	ipv6_dev_mc_inc(dev, &maddr);
+static void addrconf_sit_config(struct device *dev)
+{
+	struct inet6_dev *idev;
 
-	/* Start duplicate address detection. */
-	addrconf_dad_start(ifp);
+	/* 
+	 * Configure the tunnel with one of our IPv4 
+	 * addresses... we should configure all of 
+	 * our v4 addrs in the tunnel
+	 */
+
+	idev = ipv6_add_dev(dev);
+	if (idev == NULL) {
+		printk(KERN_DEBUG "init sit: add_dev failed\n");
+		return;
+	}
+
+	sit_add_v4_addrs(idev);
+
+	if (dev->flags&IFF_POINTOPOINT) {
+		addrconf_add_mroute(dev);
+		addrconf_add_lroute(dev);
+	} else
+		sit_route_add(dev);
 }
 
+
 int addrconf_notify(struct notifier_block *this, unsigned long event, 
 		    void * data)
 {
 	struct device *dev;
-	struct inet6_dev    * idev;
 
 	dev = (struct device *) data;
 
@@ -861,34 +1012,15 @@
 	case NETDEV_UP:
 		switch(dev->type) {
 		case ARPHRD_SIT:
-
-			printk(KERN_DEBUG "sit device up: %s\n", dev->name);
-
-			/* 
-			 * Configure the tunnel with one of our IPv4 
-			 * addresses... we should configure all of 
-			 * our v4 addrs in the tunnel
-			 */
-
-			idev = ipv6_add_dev(dev);
-			
-			sit_add_v4_addrs(idev);
-
-			/*
-			 *  we do an hack for now to configure the tunnel
-			 *  route.
-			 */
-
-			sit_route_add(dev);
+			addrconf_sit_config(dev);
 			break;
 
 		case ARPHRD_LOOPBACK:
 			init_loopback(dev);
 			break;
 
-		case ARPHRD_ETHER:
-			printk(KERN_DEBUG "Configuring eth interface\n");
-			addrconf_eth_config(dev);
+		default:
+			addrconf_dev_config(dev);
 			break;
 		};
 
@@ -934,7 +1066,6 @@
 	}
 
 	if (idev == NULL) {
-		printk(KERN_DEBUG "addrconf_ifdown: device not found\n");
 		end_bh_atomic();
 		return -ENODEV;
 	}
@@ -958,8 +1089,8 @@
 				ifa = *bifa;
 				continue;
 			}
-			ifa = ifa->lst_next;
 			bifa = &ifa->lst_next;
+			ifa = *bifa;
 		}
 	}
 
@@ -968,6 +1099,7 @@
 	return 0;
 }
 
+
 static void addrconf_rs_timer(unsigned long data)
 {
 	struct inet6_ifaddr *ifp;
@@ -1003,10 +1135,8 @@
 		struct in6_rtmsg rtmsg;
 		int err;
 
-#if ACONF_DEBUG >= 2
 		printk(KERN_DEBUG "%s: no IPv6 routers present\n",
 		       ifp->idev->dev->name);
-#endif
 
 		memset(&rtmsg, 0, sizeof(struct in6_rtmsg));
 		rtmsg.rtmsg_type = RTMSG_NEWROUTE;
@@ -1031,27 +1161,17 @@
 
 	dev = ifp->idev->dev;
 
-	if (dev->flags & IFF_MULTICAST) {
-		struct in6_rtmsg rtmsg;
-		struct rt6_info *mrt;
-		int err;
-
-		memset(&rtmsg, 0, sizeof(rtmsg));
-		ipv6_addr_set(&rtmsg.rtmsg_dst,
-			      __constant_htonl(0xFF000000), 0, 0, 0);
-
-		rtmsg.rtmsg_dst_len = 8;
-		rtmsg.rtmsg_metric = IP6_RT_PRIO_ADDRCONF;
-		rtmsg.rtmsg_ifindex = dev->ifindex;
-
-		rtmsg.rtmsg_flags = RTF_UP;
+	addrconf_join_solict(dev, &ifp->addr);
 
-		mrt = ip6_route_add(&rtmsg, &err);
+	if (ifp->prefix_len != 128)
+		addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev, 0);
 
-		if (err)
-			printk(KERN_DEBUG "dad_start: mcast route add failed\n");
-		else
-			mrt->rt6i_nexthop = ndisc_get_neigh(dev, &rtmsg.rtmsg_dst);
+	if (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) {
+		start_bh_atomic();
+		ifp->flags &= ~DAD_INCOMPLETE;
+		addrconf_dad_completed(ifp);
+		end_bh_atomic();
+		return;
 	}
 
 	if (rand_seed) {
@@ -1059,15 +1179,12 @@
 		nd_rand_seed = ifp->addr.s6_addr32[3];
 	}
 
-	init_timer(&ifp->timer);
-
 	ifp->probes = ipv6_config.dad_transmits;
 	ifp->flags |= DAD_INCOMPLETE;
 
 	rand_num = ipv6_random() % ipv6_config.rtr_solicit_delay;
 
 	ifp->timer.function = addrconf_dad_timer;
-	ifp->timer.data = (unsigned long) ifp;
 	ifp->timer.expires = jiffies + rand_num;
 
 	add_timer(&ifp->timer);
@@ -1105,62 +1222,41 @@
 
 static void addrconf_dad_completed(struct inet6_ifaddr *ifp)
 {
-	struct device *dev;
-	int err;
-
-	dev = ifp->idev->dev;
+	struct device *	dev = ifp->idev->dev;
 
-	if (ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL) {
-		struct in6_rtmsg rtmsg;
-		struct in6_addr all_routers;
+	/*
+	 *	Configure the address for reception. Now it is valid.
+	 */
 
-		/*
-		 *	1) configure a link route for this interface
-		 *	2) send a (delayed) router solicitation
-		 */
+	ip6_rt_addr_add(&ifp->addr, dev);
 
-		memset(&rtmsg, 0, sizeof(rtmsg));
-		
-		memcpy(&rtmsg.rtmsg_dst, &ifp->addr, sizeof(struct in6_addr));
+	/* If added prefix is link local and forwarding is off,
+	   start sending router solicitations.
+	 */
 
-		rtmsg.rtmsg_dst_len = ifp->prefix_len;
-		rtmsg.rtmsg_metric = IP6_RT_PRIO_ADDRCONF;
-		rtmsg.rtmsg_ifindex = dev->ifindex;
+	if (ipv6_config.forwarding == 0 &&
+	    (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) == 0 &&
+	    (ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL)) {
+		struct in6_addr all_routers;
 
-		rtmsg.rtmsg_flags = RTF_UP;
+		ipv6_addr_set(&all_routers,
+			      __constant_htonl(0xff020000U), 0, 0,
+			      __constant_htonl(0x2U));
 
-		ip6_route_add(&rtmsg, &err);
-		
-		if (err)
-			printk(KERN_DEBUG "dad_complete: error in route_add\n");
+		/*
+		 *	If a host as already performed a random delay
+		 *	[...] as part of DAD [...] there is no need
+		 *	to delay again before sending the first RS
+		 */
+		ndisc_send_rs(ifp->idev->dev, &ifp->addr, &all_routers);
 
-		if (ipv6_config.forwarding == 0) {
-			ipv6_addr_set(&all_routers,
-				      __constant_htonl(0xff020000U), 0, 0,
-				      __constant_htonl(0x2U));
-
-			/*
-			 *	If a host as already performed a random delay
-			 *	[...] as part of DAD [...] there is no need
-			 *	to delay again before sending the first RS
-			 */
-			ndisc_send_rs(ifp->idev->dev, &ifp->addr,
-				      &all_routers);
-
-			ifp->probes = 1;
-			ifp->timer.function = addrconf_rs_timer;
-			ifp->timer.expires = (jiffies +
-					      ipv6_config.rtr_solicit_interval);
-			ifp->idev->if_flags |= IF_RS_SENT;
-			add_timer(&ifp->timer);
-		}
+		ifp->probes = 1;
+		ifp->timer.function = addrconf_rs_timer;
+		ifp->timer.expires = (jiffies +
+				      ipv6_config.rtr_solicit_interval);
+		ifp->idev->if_flags |= IF_RS_SENT;
+		add_timer(&ifp->timer);
 	}
-	
-	/*
-	 *	configure the address for reception
-	 */
-
-	ip6_rt_addr_add(&ifp->addr, dev);
 }
 
 #ifdef CONFIG_PROC_FS
@@ -1251,7 +1347,9 @@
 
 __initfunc(void addrconf_init(void))
 {
+#ifdef MODULE
 	struct device *dev;
+#endif
 
 	/*
 	 *	init address and device hash lists
@@ -1263,24 +1361,25 @@
 
 	memset(inet6_dev_lst, 0, IN6_ADDR_HSIZE * sizeof(struct inet6_dev *));
 
-	/*
-	 *	Init loopback device
-	 */
-
-	dev = dev_get("lo");
-
-	if (dev && (dev->flags & IFF_UP))
-		init_loopback(dev);
+#ifdef MODULE
+	/* This takes sense only during module load. */
 
-	/*
-	 *	and maybe:
-	 *	search availiable AF_INET devs and try to configure them
-	 */
-
-	dev = dev_get("eth0");
+	for (dev = dev_base; dev; dev = dev->next) {
+		if (!(dev->flags&IFF_UP))
+			continue;
 
-	if (dev && (dev->flags & IFF_UP))
-		addrconf_eth_config(dev);
+		switch (dev->type) {
+		case ARPHRD_LOOPBACK:	
+			init_loopback(dev);
+			break;
+		case ARPHRD_ETHER:	
+			addrconf_dev_config(dev);
+			break;
+		default:
+			/* Ignore all other */
+		}
+	}
+#endif
 	
 #ifdef CONFIG_PROC_FS
 	proc_net_register(&iface_proc_entry);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov