patch-2.1.30 linux/net/ipv6/ndisc.c

Next file: linux/net/ipv6/proc.c
Previous file: linux/net/ipv6/mcast.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.29/linux/net/ipv6/ndisc.c linux/net/ipv6/ndisc.c
@@ -6,7 +6,7 @@
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *	Mike Shaver		<shaver@ingenia.com>
  *
- *	$Id: ndisc.c,v 1.28 1996/10/11 16:03:06 roque Exp $
+ *	$Id: ndisc.c,v 1.13 1997/03/18 18:24:41 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -23,8 +23,15 @@
  *	Janos Farkas			:	kmalloc failure checks
  */
 
+/* Set to 3 to get tracing... */
 #define ND_DEBUG 2
 
+#if ND_DEBUG >= 3
+#define NDBG(x) printk x
+#else
+#define NDBG(x)
+#endif
+
 #define __NO_VERSION__
 #include <linux/module.h>
 #include <linux/config.h>
@@ -37,7 +44,6 @@
 #include <linux/in6.h>
 #include <linux/route.h>
 
-#include <net/neighbour.h>
 #include <linux/if_arp.h>
 #include <linux/ipv6.h>
 #include <linux/icmpv6.h>
@@ -48,7 +54,7 @@
 #include <net/ipv6.h>
 #include <net/protocol.h>
 #include <net/ndisc.h>
-#include <net/ipv6_route.h>
+#include <net/ip6_route.h>
 #include <net/addrconf.h>
 
 
@@ -68,9 +74,7 @@
 static struct neigh_table nd_tbl;
 
 unsigned int	ndisc_hash(void *primary_key);
-int			ndisc_eth_resolv(unsigned char *h_dest,
-					 struct device *dev,
-					 struct sk_buff *skb);
+int		ndisc_eth_resolv(unsigned char *h_dest, struct sk_buff *skb);
 
 static struct neigh_ops nd_neigh_ops = {
 	ETH_P_IPV6,
@@ -86,13 +90,8 @@
  *	Protocol variables
  */
 
-int	nd_max_multicast_solicit	= 3;
-int	nd_max_unicast_solicit		= 3;
-int	nd_retrans_timer		= RETRANS_TIMER;
-int	nd_reachable_time		= RECHABLE_TIME;
-int	nd_base_reachable_time		= RECHABLE_TIME;
-int	nd_delay_first_probe		= 5 * HZ;
-int	nd_gc_interval			= 5 * HZ;
+unsigned long	nd_reachable_time		= RECHABLE_TIME;
+int		nd_gc_interval			= 5 * HZ;
 
 /* 
  *	garbage collection timeout must be greater than reachable time
@@ -106,7 +105,7 @@
 
 static int  ndisc_event_timer(struct nd_neigh *ndn);
 
-int ipv6_random(void)
+unsigned long ipv6_random(void)
 {
 	nd_rand_seed=nd_rand_seed*69069L+1;
         return nd_rand_seed^jiffies;
@@ -116,17 +115,15 @@
 {
 	unsigned long val;
 
-	val = ipv6_random() % (MAX_RANDOM_FACTOR * nd_base_reachable_time);
-	if (val < (MIN_RANDOM_FACTOR * nd_base_reachable_time))
-	{
-		val += (MIN_RANDOM_FACTOR * nd_base_reachable_time);
-	}
+	val = ipv6_random() % (MAX_RANDOM_FACTOR *
+			       ipv6_config.nd_base_reachable_time);
+
+	if (val < (MIN_RANDOM_FACTOR * ipv6_config.nd_base_reachable_time))
+		val+= (MIN_RANDOM_FACTOR * ipv6_config.nd_base_reachable_time);
 
 	return val;
 }
 
-void ndisc_verify_reachability(struct neighbour * neigh);
-
 unsigned int ndisc_hash(void *primary_key)
 {
         struct in6_addr *addr = (struct in6_addr *) primary_key;
@@ -152,8 +149,7 @@
 	 *	periodicly compute ReachableTime from random function
 	 */
 	
-	if ((now - last_rand) > REACH_RANDOM_INTERVAL)
-	{
+	if ((now - last_rand) > REACH_RANDOM_INTERVAL) {
 		last_rand = now;
 		nd_reachable_time = rand_reach_time();
 	}
@@ -161,13 +157,10 @@
 	neigh_table_lock(&nd_tbl);
 
 	start_bh_atomic();
-	if (nd_tbl.tbl_lock == 1)
-	{
+	if (nd_tbl.tbl_lock == 1) {
 		ntbl_walk_table(&nd_tbl, ndisc_gc_func, 0, 0, NULL);
 		ndisc_gc_timer.expires = now + nd_gc_interval;
-	}
-	else
-	{
+	} else {
 #if ND_DEBUG >= 2
 		printk(KERN_DEBUG "ndisc_gc delayed: table locked\n");
 #endif
@@ -185,8 +178,7 @@
 	struct nd_neigh *ndn = (struct nd_neigh *) neigh;
         unsigned long now = jiffies;
 
-	if (ndn->ndn_refcnt == 0)
-	{
+	if (ndn->ndn_refcnt == 0) {
 		switch (ndn->ndn_nud_state) {
 		
 		case NUD_REACHABLE:
@@ -196,7 +188,7 @@
 		case NUD_FAILED:
 			return 1;
 		default:
-		}
+		};
 	}
 	return 0;
 }
@@ -209,9 +201,7 @@
 	ndn->ndn_expires = now + timer;
 	
 	if (del_timer(&ndisc_timer))
-	{
 		tval = ndisc_timer.expires;
-	}
 
 	tval = min(tval, ndn->ndn_expires);
 
@@ -222,17 +212,15 @@
 static void ndisc_del_timer(struct nd_neigh *ndn)
 {
 	unsigned long tval = ~0UL;
-
-	if (!(ndn->ndn_nud_state & NUD_IN_TIMER))
-		return;
+	unsigned long neigh_val;
 
 	if (del_timer(&ndisc_timer))
-	{
 		tval = ndisc_timer.expires;
-	}
-	
-	if (tval == ndn->ndn_expires)
-	{
+
+	neigh_val = ndn->ndn_expires;
+	ndn->ndn_expires = 0;
+
+	if (tval == neigh_val) {
 		int i;
 		
 		tval = ~0UL;
@@ -240,27 +228,24 @@
 		neigh_table_lock(&nd_tbl);
 		
 		/* need to search the entire neighbour cache */
-		for (i=0; i < nd_tbl.tbl_size; i++)
-		{
+		for (i=0; i < nd_tbl.tbl_size; i++) {
 			struct neighbour *neigh, *head;
 			head = nd_tbl.hash_buckets[i];
 				
 			if ((neigh = head) == NULL)
 				continue;
-			
-			do
-			{
+
+			do {
 				struct nd_neigh *n;
 
 				n = (struct nd_neigh *) neigh;
 
-				if (n->ndn_nud_state & NUD_IN_TIMER)
-				{
+				if ((n->ndn_nud_state & NUD_IN_TIMER) &&
+				     n->ndn_expires)
 					tval = min(tval, n->ndn_expires);
-				}
-				
+
 				neigh = neigh->next;
-				
+
 			} while (neigh != head);
 		}
 		neigh_table_unlock(&nd_tbl);
@@ -277,12 +262,9 @@
 {
 	struct nd_neigh *ndn = (struct nd_neigh *) neigh;
 
-	if (ndn->ndn_refcnt == 0)
-	{
+	if (ndn->ndn_refcnt == 0) {
 		if (ndn->ndn_nud_state & NUD_IN_TIMER)
-		{
 			ndisc_del_timer(ndn);
-		}
 		
 		return 1;
 	}
@@ -294,20 +276,26 @@
 {
 	struct nd_neigh *ndn;
 
+	NDBG(("ndisc_new_neigh("));
+	if(dev)
+		NDBG(("%s,", dev->name));
+	else
+		NDBG(("[NULL],"));
+	NDBG(("[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]): ",
+	      addr->s6_addr16[0], addr->s6_addr16[1], addr->s6_addr16[2],
+	      addr->s6_addr16[3], addr->s6_addr16[4], addr->s6_addr16[5],
+	      addr->s6_addr16[6], addr->s6_addr16[7]));
+
 	ndn = (struct nd_neigh *) neigh_alloc(sizeof(struct nd_neigh),
-					      GFP_ATOMIC);
-	
-	if (ndn == NULL)
-	{
+					      &nd_neigh_ops);
+	if (ndn == NULL) {
 
 #if ND_DEBUG >= 2
 		printk(KERN_DEBUG "neigh_alloc: out of memory\n");
 #endif
 
 		start_bh_atomic();
-		if (nd_tbl.tbl_lock == 1)
-		{
-
+		if (nd_tbl.tbl_lock == 1) {
 #if ND_DEBUG >= 2
 			printk(KERN_DEBUG "ndisc_alloc: forcing gc\n");
 #endif
@@ -329,12 +317,19 @@
 	ndn->ndn_dev = dev;
 	ndn->ndn_tstamp = jiffies;
 
-	if (dev->type == ARPHRD_LOOPBACK || dev->type == ARPHRD_SIT)
-	{
+	if ((ndn->ndn_type & IPV6_ADDR_MULTICAST)) {
+		NDBG(("MULTICAST(NCF_NOARP) "));
+		ndn->ndn_flags |= NCF_NOARP;
+	}
+
+	if (dev->type == ARPHRD_LOOPBACK || dev->type == ARPHRD_SIT) {
+		NDBG(("%s(NCF_NOARP) ",
+		      (dev->type==ARPHRD_LOOPBACK) ? "LOOPBACK" : "SIT"));
 		ndn->ndn_flags |= NCF_NOARP;
 	}
 
 	neigh_insert(&nd_tbl, (struct neighbour *) ndn);
+	NDBG(("returning ndn(%p)\n", ndn));
 	return ndn;
 }
 
@@ -353,10 +348,9 @@
 	 *	cached information about nexthop and addr resolution
 	 */
 
-	if (dev == NULL)
-	{
+	if (dev == NULL) {
 #if ND_DEBUG >= 1
-		printk(KERN_DEBUG "ncache_get_neigh: NULL device\n");
+		printk(KERN_DEBUG "ndisc_get_neigh: NULL device\n");
 #endif
 		return NULL;
 	}
@@ -365,10 +359,7 @@
 
         neigh = (struct nd_neigh *) neigh_lookup(&nd_tbl, (void *) addr,
 						 sizeof(struct in6_addr), dev);
-
-	
-	if (neigh == NULL)
-	{
+	if (neigh == NULL) {
 		neigh = ndisc_new_neigh(dev, addr);
 
 		if (neigh == NULL)
@@ -377,9 +368,7 @@
 
 	neigh_table_unlock(&nd_tbl);
 
-	atomic_inc(&neigh->ndn_refcnt);
-	
-	return (struct neighbour *) neigh;	
+	return neighbour_clone((struct neighbour *) neigh);
 }
 
 /*
@@ -388,45 +377,35 @@
  *	1 - Address Resolution unfinished / packet queued
  */
 
-int ndisc_eth_resolv(unsigned char *h_dest, struct device *dev,
-		     struct sk_buff *skb)
+int ndisc_eth_resolv(unsigned char *h_dest, struct sk_buff *skb)
 {
-	struct nd_neigh *ndn;
+	struct nd_neigh *ndn = NULL;
 
-	ndn = (struct nd_neigh *) skb->nexthop;
-	
-	if (ndn == NULL)
-	{
-		struct in6_addr *daddr;
-		int addr_type;
-
-		daddr = &skb->nh.ipv6h->daddr;
-		
-		addr_type = ipv6_addr_type(daddr);
-		
-		if (addr_type & IPV6_ADDR_MULTICAST)
-		{
-			ipv6_mc_map(daddr, h_dest);
-			return 0;
-		}
+	if (skb->dst)
+		ndn = (struct nd_neigh *) skb->dst->neighbour;
 
+	if (ndn == NULL) {
 #if ND_DEBUG >= 2
 		printk(KERN_DEBUG "ndisc_eth_resolv: nexthop is NULL\n");
 #endif
 		goto discard;
 	}
 
-	if (skb->pkt_type == PACKET_NDISC)
-		goto ndisc_pkt;
-	
+	if ((ndn->ndn_type & IPV6_ADDR_MULTICAST)) {
+		struct in6_addr *daddr;
+
+		daddr = &skb->nh.ipv6h->daddr;
+		ipv6_mc_map(daddr, h_dest);
+		return 0;
+	}
+
 	switch (ndn->ndn_nud_state) {	
 	case NUD_FAILED:
 	case NUD_NONE:
 		ndisc_event_send((struct neighbour *)ndn, skb);
 
 	case NUD_INCOMPLETE:			
-		if (skb_queue_len(&ndn->neigh.arp_queue) >= NDISC_QUEUE_LEN)
-		{
+		if (skb_queue_len(&ndn->neigh.arp_queue) >= NDISC_QUEUE_LEN) {
 			struct sk_buff *buff;
 			
 			buff = ndn->neigh.arp_queue.prev;
@@ -437,18 +416,15 @@
 		return 1;
 	default:
 		ndisc_event_send((struct neighbour *)ndn, skb);
-	}
+	};
 
-  ndisc_pkt:
-	
-	if ((ndn->ndn_flags & NTF_COMPLETE) == 0)
-	{
+	if ((ndn->ndn_flags & NTF_COMPLETE) == 0) {
 #if ND_DEBUG >=1
 		/* This shouldn't happen */
 		printk(KERN_DEBUG "ND: using incomplete entry\n");
 #endif
 	}
-	memcpy(h_dest, ndn->ndn_ha, dev->addr_len);
+	memcpy(h_dest, ndn->ndn_ha, skb->dev->addr_len);
 	return 0;
 
   discard:
@@ -457,14 +433,12 @@
 	return 1;
 }
 
-
 /*
  *	Send a Neighbour Advertisement
  */
 
 void ndisc_send_na(struct device *dev, struct nd_neigh *ndn,
-		   struct in6_addr *daddr,
-		   struct in6_addr *solicited_addr,
+		   struct in6_addr *daddr, struct in6_addr *solicited_addr,
 		   int router, int solicited, int override, int inc_opt) 
 {
         struct sock *sk = ndisc_socket->sk;
@@ -473,38 +447,64 @@
         struct sk_buff *skb;
 	int err;
 
+	NDBG(("ndisc_send_na("));
+	if(dev)
+		NDBG(("%s,", dev->name));
+	else
+		NDBG(("[NULL]"));
+	NDBG(("%p): ", ndn));
+	if(daddr)
+		NDBG(("daddr[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x] ",
+		      daddr->s6_addr16[0], daddr->s6_addr16[1], daddr->s6_addr16[2],
+		      daddr->s6_addr16[3], daddr->s6_addr16[4], daddr->s6_addr16[5],
+		      daddr->s6_addr16[6], daddr->s6_addr16[7]));
+	if(solicited_addr)
+		NDBG(("solicit_addr[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x] ",
+		      solicited_addr->s6_addr16[0], solicited_addr->s6_addr16[1],
+		      solicited_addr->s6_addr16[2], solicited_addr->s6_addr16[3],
+		      solicited_addr->s6_addr16[4], solicited_addr->s6_addr16[5],
+		      solicited_addr->s6_addr16[6], solicited_addr->s6_addr16[7]));
+	NDBG(("rtr(%d)sol(%d)ovr(%d)iopt(%d)\n", router, solicited, override, inc_opt));
+
 	opt_len = ((dev->addr_len + 1) >> 3) + 1;
-	len = sizeof(struct icmpv6hdr) + sizeof(struct in6_addr);
+	len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
 
+#if ND_DEBUG >=1
+	if (dev == NULL) {
+		printk(KERN_DEBUG "send_na: null device\n");
+		return;
+	}
+#endif
 	if (inc_opt)
-	{
 		len += opt_len << 3;
-	}
 
-	skb = sock_alloc_send_skb(sk, MAX_HEADER + len, 0, 0, &err);
+	skb = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,
+				  0, 0, &err);
 
-	if (skb == NULL)
-	{
+	if (skb == NULL) {
 		printk(KERN_DEBUG "send_na: alloc skb failed\n");
 		return;
 	}
-
-	if (ipv6_bld_hdr_2(sk, skb, dev, (struct neighbour *) ndn, 
-			   solicited_addr, daddr, IPPROTO_ICMPV6, len) < 0)
-        {
-		kfree_skb(skb, FREE_WRITE);
-		printk(KERN_DEBUG 
-		       "ndisc_send_na: ipv6_build_header returned < 0\n");
-		return;
+	/*
+	 *	build the MAC header
+	 */
+	
+	if (dev->hard_header_len) {
+		skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
+		if (dev->hard_header) {
+			dev->hard_header(skb, dev, ETH_P_IPV6, ndn->ndn_ha,
+					 NULL, len);
+			skb->arp = 1;
+		}
 	}
 
-	skb->pkt_type = PACKET_NDISC;
-	
+	ip6_nd_hdr(sk, skb, dev, solicited_addr, daddr, IPPROTO_ICMPV6, len);
+
 	msg = (struct nd_msg *) skb_put(skb, len);
 
-        msg->icmph.type = NDISC_NEIGHBOUR_ADVERTISEMENT;
-        msg->icmph.code = 0;
-        msg->icmph.checksum = 0;
+        msg->icmph.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT;
+        msg->icmph.icmp6_code = 0;
+        msg->icmph.icmp6_cksum = 0;
 
         msg->icmph.icmp6_unused = 0;
         msg->icmph.icmp6_router    = router;
@@ -514,47 +514,66 @@
         /* Set the target address. */
 	ipv6_addr_copy(&msg->target, solicited_addr);
 
-	if (inc_opt)
-	{
+	if (inc_opt) {
 		/* Set the source link-layer address option. */
 		msg->opt.opt_type = ND_OPT_TARGET_LL_ADDR;
 		msg->opt.opt_len = opt_len;
 		memcpy(msg->opt.link_addr, dev->dev_addr, dev->addr_len);
 
-		if ((opt_len << 3) - (2 + dev->addr_len))
-		{
+		if ((opt_len << 3) - (2 + dev->addr_len)) {
 			memset(msg->opt.link_addr + dev->addr_len, 0,
 			       (opt_len << 3) - (2 + dev->addr_len));
 		}
 	}
 
 	/* checksum */
-	msg->icmph.checksum = csum_ipv6_magic(solicited_addr, daddr, len, 
-					      IPPROTO_ICMPV6,
-					      csum_partial((__u8 *) msg, 
-							   len, 0));
+	msg->icmph.icmp6_cksum = csum_ipv6_magic(solicited_addr, daddr, len, 
+						 IPPROTO_ICMPV6,
+						 csum_partial((__u8 *) msg, 
+							      len, 0));
 
-	ipv6_queue_xmit(sk, skb->dev, skb, 1);
+	dev_queue_xmit(skb);
 }        
 
 void ndisc_send_ns(struct device *dev, struct neighbour *neigh,
 		   struct in6_addr *solicit,
 		   struct in6_addr *daddr, struct in6_addr *saddr) 
 {
+	unsigned char ha[MAX_ADDR_LEN];
         struct sock *sk = ndisc_socket->sk;
         struct sk_buff *skb;
         struct nd_msg *msg;
-        int len, opt_len;
+        int len, opt_len;	
+	void *h_dest;
 	int err;
 
+	NDBG(("ndisc_send_ns(%s,%p): ", (dev ? dev->name : "[NULL]"), neigh));
+	if(daddr)
+		NDBG(("daddr[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x] ",
+		      daddr->s6_addr16[0], daddr->s6_addr16[1], daddr->s6_addr16[2],
+		      daddr->s6_addr16[3], daddr->s6_addr16[4], daddr->s6_addr16[5],
+		      daddr->s6_addr16[6], daddr->s6_addr16[7]));
+	if(saddr)
+		NDBG(("saddr[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x] ",
+		      saddr->s6_addr16[0], saddr->s6_addr16[1], saddr->s6_addr16[2],
+		      saddr->s6_addr16[3], saddr->s6_addr16[4], saddr->s6_addr16[5],
+		      saddr->s6_addr16[6], saddr->s6_addr16[7]));
+	if(solicit)
+		NDBG(("solicit[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x] ",
+		      solicit->s6_addr16[0], solicit->s6_addr16[1],
+		      solicit->s6_addr16[2], solicit->s6_addr16[3],
+		      solicit->s6_addr16[4], solicit->s6_addr16[5],
+		      solicit->s6_addr16[6], solicit->s6_addr16[7]));
+	NDBG(("\n"));
+
 	/* length of addr in 8 octet groups.*/
 	opt_len = ((dev->addr_len + 1) >> 3) + 1;
-	len = sizeof(struct icmpv6hdr) + sizeof(struct in6_addr) +
+	len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr) +
                 (opt_len << 3);
 
-	skb = sock_alloc_send_skb(sk, MAX_HEADER + len, 0, 0, &err);
-	if (skb == NULL)
-	{
+	skb = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,
+				  0, 0, &err);
+	if (skb == NULL) {
 #if ND_DEBUG >= 1
 		printk(KERN_DEBUG "send_ns: alloc skb failed\n");
 #endif
@@ -563,41 +582,47 @@
 
 	skb->pkt_type = PACKET_NDISC;
 
-	if (saddr == NULL)
-	{
+	if (saddr == NULL) {
 		struct inet6_ifaddr *ifa;
 
 		/* use link local address */
 		ifa = ipv6_get_lladdr(dev);
 
 		if (ifa)
-		{
 			saddr = &ifa->addr;
-		}
 	}
 
-	if(ipv6_addr_type(daddr) == IPV6_ADDR_MULTICAST)
-	{
+	if ((ipv6_addr_type(daddr) & IPV6_ADDR_MULTICAST)) {
 		nd_stats.snt_probes_mcast++;
-	}
-        else
-	{
+		ipv6_mc_map(daddr, ha);
+		h_dest = ha;
+	} else {
+		if (neigh == NULL) {
+#if ND_DEBUG >= 1
+			printk(KERN_DEBUG "send_ns: ucast destination "
+			       "with null neighbour\n");
+#endif
+			return;
+		}
+		h_dest = neigh->ha;
 		nd_stats.snt_probes_ucast++;
 	}
 
-	if (ipv6_bld_hdr_2(sk, skb, dev, neigh, saddr, daddr, IPPROTO_ICMPV6,
-			   len) < 0 )
-	{
-		kfree_skb(skb, FREE_WRITE);
-		printk(KERN_DEBUG
-		       "ndisc_send_ns: ipv6_build_header returned < 0\n");
-		return;
+	if (dev->hard_header_len) {
+		skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
+		if (dev->hard_header) {
+			dev->hard_header(skb, dev, ETH_P_IPV6, h_dest, NULL,
+					 len);
+			skb->arp = 1;
+		}
 	}
+
+	ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
 	
 	msg = (struct nd_msg *)skb_put(skb, len);
-	msg->icmph.type = NDISC_NEIGHBOUR_SOLICITATION;
-	msg->icmph.code = 0;
-	msg->icmph.checksum = 0;
+	msg->icmph.icmp6_type = NDISC_NEIGHBOUR_SOLICITATION;
+	msg->icmph.icmp6_code = 0;
+	msg->icmph.icmp6_cksum = 0;
 	msg->icmph.icmp6_unused = 0;
 
 	/* Set the target address. */
@@ -609,20 +634,19 @@
 
 	memcpy(msg->opt.link_addr, dev->dev_addr, dev->addr_len);
 
-	if ((opt_len << 3) - (2 + dev->addr_len))
-	{
+	if ((opt_len << 3) - (2 + dev->addr_len)) {
 		memset(msg->opt.link_addr + dev->addr_len, 0,
 		       (opt_len << 3) - (2 + dev->addr_len));
 	}
 
 	/* checksum */
-	msg->icmph.checksum = csum_ipv6_magic(&skb->nh.ipv6h->saddr,
-					      daddr, len, 
-					      IPPROTO_ICMPV6,
-					      csum_partial((__u8 *) msg, 
-							   len, 0));
+	msg->icmph.icmp6_cksum = csum_ipv6_magic(&skb->nh.ipv6h->saddr,
+						 daddr, len, 
+						 IPPROTO_ICMPV6,
+						 csum_partial((__u8 *) msg, 
+							      len, 0));
 	/* send it! */
-	ipv6_queue_xmit(sk, skb->dev, skb, 1);
+	dev_queue_xmit(skb);
 }
 
 void ndisc_send_rs(struct device *dev, struct in6_addr *saddr,
@@ -630,35 +654,52 @@
 {
 	struct sock *sk = ndisc_socket->sk;
         struct sk_buff *skb;
-        struct icmpv6hdr *hdr;
+        struct icmp6hdr *hdr;
 	__u8 * opt;
         int len, opt_len;
 	int err;
 
+	NDBG(("ndisc_send_rs(%s): ", (dev ? dev->name : "[NULL]")));
+	if(daddr)
+		NDBG(("daddr[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x] ",
+		      daddr->s6_addr16[0], daddr->s6_addr16[1], daddr->s6_addr16[2],
+		      daddr->s6_addr16[3], daddr->s6_addr16[4], daddr->s6_addr16[5],
+		      daddr->s6_addr16[6], daddr->s6_addr16[7]));
+	if(saddr)
+		NDBG(("saddr[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x] ",
+		      saddr->s6_addr16[0], saddr->s6_addr16[1], saddr->s6_addr16[2],
+		      saddr->s6_addr16[3], saddr->s6_addr16[4], saddr->s6_addr16[5],
+		      saddr->s6_addr16[6], saddr->s6_addr16[7]));
+	NDBG(("\n"));
+
 	/* length of addr in 8 octet groups.*/
 	opt_len = ((dev->addr_len + 1) >> 3) + 1;
-	len = sizeof(struct icmpv6hdr) + (opt_len << 3);
+	len = sizeof(struct icmp6hdr) + (opt_len << 3);
 
-        skb = sock_alloc_send_skb(sk, MAX_HEADER + len, 0, 0, &err);
-	if (skb == NULL)
-	{
+        skb = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,
+				  0, 0, &err);
+	if (skb == NULL) {
 		printk(KERN_DEBUG "send_ns: alloc skb failed\n");
 		return;
 	}
 
-        if (ipv6_bld_hdr_2(sk, skb, dev, NULL, saddr, daddr, IPPROTO_ICMPV6,
-			   len) < 0 )
-	{
-                kfree_skb(skb, FREE_WRITE);
-                printk(KERN_DEBUG
-                       "ndisc_send_ns: ipv6_build_header returned < 0\n");
-                return;
-        }
+	if (dev->hard_header_len) {
+		skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
+		if (dev->hard_header) {
+			unsigned char ha[MAX_ADDR_LEN];
+
+			ipv6_mc_map(daddr, ha);
+			dev->hard_header(skb, dev, ETH_P_IPV6, ha, NULL, len);
+			skb->arp = 1;
+		}
+	}
+
+	ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
 	
-        hdr = (struct icmpv6hdr *) skb_put(skb, len);
-        hdr->type = NDISC_ROUTER_SOLICITATION;
-        hdr->code = 0;
-        hdr->checksum = 0;
+        hdr = (struct icmp6hdr *) skb_put(skb, len);
+        hdr->icmp6_type = NDISC_ROUTER_SOLICITATION;
+        hdr->icmp6_code = 0;
+        hdr->icmp6_cksum = 0;
         hdr->icmp6_unused = 0;
 
 	opt = (u8*) (hdr + 1);
@@ -669,27 +710,25 @@
 
         memcpy(opt + 2, dev->dev_addr, dev->addr_len);
 
-	if ((opt_len << 3) - (2 + dev->addr_len))
-	{
+	if ((opt_len << 3) - (2 + dev->addr_len)) {
 		memset(opt + 2 + dev->addr_len, 0,
 		       (opt_len << 3) - (2 + dev->addr_len));
 	}
 
 	/* checksum */
-	hdr->checksum = csum_ipv6_magic(&skb->nh.ipv6h->saddr, daddr, len,
-					IPPROTO_ICMPV6,
-					csum_partial((__u8 *) hdr, len, 0));
+	hdr->icmp6_cksum = csum_ipv6_magic(&skb->nh.ipv6h->saddr, daddr, len,
+					   IPPROTO_ICMPV6,
+					   csum_partial((__u8 *) hdr, len, 0));
 
 	/* send it! */
-	ipv6_queue_xmit(sk, skb->dev, skb, 1);
+	dev_queue_xmit(skb);
 }
 		   
 
 static int ndisc_store_hwaddr(struct nd_neigh *ndn, __u8 *opt, int opt_len,
 			      int option)
 {
-	while (*opt != option && opt_len)
-	{
+	while (*opt != option && opt_len) {
 		int len;
 
 		len = opt[1] << 3;
@@ -704,8 +743,7 @@
 		opt_len -= len;
 	}
 
-	if (*opt == option)
-	{
+	if (*opt == option) {
 		memcpy(ndn->neigh.ha, opt + 2, ndn->ndn_dev->addr_len); 
 		return 0;
 	}
@@ -723,40 +761,32 @@
 
 	neigh_table_lock(&nd_tbl);
 	
-	for (i=0; i < nd_tbl.tbl_size; i++)
-	{
+	for (i=0; i < nd_tbl.tbl_size; i++) {
 		struct nd_neigh *ndn, *head;
 
 		head = (struct nd_neigh *) nd_tbl.hash_buckets[i];
 
 		if ((ndn = head) == NULL)
 			continue;
-		
-		do
-		{
-                        if (ndn->ndn_nud_state & NUD_IN_TIMER)
-			{
+
+		do {
+                        if (ndn->ndn_nud_state & NUD_IN_TIMER) {
 				unsigned long time;
 
 				time = ndn->ndn_expires - now;
 
 				if ((long) time <= 0)
-				{
 					time = ndisc_event_timer(ndn);
-				}
 				
 				if (time)
-				{
 					ntimer = min(ntimer, time);
-				}
 			}
 			ndn = (struct nd_neigh *) ndn->neigh.next;
 
 		} while (ndn != head);
 	}
 
-	if (ntimer != (~0UL))
-	{
+	if (ntimer != (~0UL)) {
 		ndisc_timer.expires = now + ntimer;
 		add_timer(&ndisc_timer);
 	}
@@ -774,23 +804,20 @@
 	int max_probes;
 
 	if (ndn->ndn_nud_state == NUD_DELAY)
-	{
 		ndn->ndn_nud_state = NUD_PROBE;
-	}
 
-	max_probes = (ndn->ndn_nud_state == NUD_PROBE ? nd_max_unicast_solicit:
-		      nd_max_multicast_solicit);
+	max_probes = (ndn->ndn_nud_state == NUD_PROBE ?
+		      ipv6_config.nd_max_ucast_solicit:
+		      ipv6_config.nd_max_mcast_solicit);
 
-	if (ndn->ndn_probes == max_probes)
-	{
+	if (ndn->ndn_probes == max_probes) {
 		struct sk_buff *skb;
 
 		ndn->ndn_nud_state = NUD_FAILED;
 		ndn->ndn_flags &= ~NTF_COMPLETE;
 		nd_stats.res_failed++;
 
-		while((skb=skb_dequeue(&ndn->neigh.arp_queue)))
-		{
+		while((skb=skb_dequeue(&ndn->neigh.arp_queue))) {
 			/*
 			 *	"The sender MUST return an ICMP
 			 *	 destination unreachable"
@@ -808,20 +835,17 @@
 	dev = ndn->ndn_dev;
 	target = &ndn->ndn_addr;
 
-	if (ndn->ndn_nud_state == NUD_INCOMPLETE)
-	{
+	if (ndn->ndn_nud_state == NUD_INCOMPLETE) {
 		addrconf_addr_solict_mult(&ndn->ndn_addr, &mcaddr);
 		daddr = &mcaddr;
 		ndn = NULL;
-	}
-	else
-	{
+	} else {
 		daddr = &ndn->ndn_addr;
 	}
 
 	ndisc_send_ns(dev, (struct neighbour *) ndn, target, daddr, NULL);
 
-	return nd_retrans_timer;
+	return ipv6_config.nd_retrans_time;
 }
 
 void ndisc_event_send(struct neighbour *neigh, struct sk_buff *skb)
@@ -830,14 +854,15 @@
 	struct in6_addr daddr;
 	unsigned long now = jiffies;
 	struct in6_addr *saddr = NULL;
-	
+
+	if ((ndn->ndn_flags & NCF_NOARP))
+		return;
+
 	switch (ndn->ndn_nud_state) {
 	case NUD_FAILED:
 		ndn->ndn_probes = 0;
 	case NUD_NONE:
-
-		if (skb && !skb->stamp.tv_sec)
-		{
+		if (skb && !skb->stamp.tv_sec) {
 			/*
 			 *	skb->stamp allows us to know if we are
 			 *	originating the skb or forwarding it.
@@ -850,7 +875,7 @@
 		addrconf_addr_solict_mult(&ndn->ndn_addr, &daddr);
 		ndisc_send_ns(ndn->ndn_dev, NULL, &ndn->ndn_addr, &daddr,
 			      saddr);
-		ndisc_add_timer(ndn, nd_retrans_timer);
+		ndisc_add_timer(ndn, ipv6_config.nd_retrans_time);
 
 		break;
 
@@ -860,7 +885,7 @@
 
 	case NUD_STALE:
 		ndn->ndn_nud_state = NUD_DELAY;
-		ndisc_add_timer(ndn, nd_delay_first_probe);
+		ndisc_add_timer(ndn, ipv6_config.nd_delay_probe_time);
 	}
 }
 
@@ -872,26 +897,21 @@
 {
 	struct sk_buff *skb;
 
+	NDBG(("ndisc_event_na(%p,%p,%d,%d,%d)\n", ndn, opt, opt_len,
+	      solicited, override));
+
 	if (ndn->ndn_nud_state == NUD_NONE)
-	{
 		ndn->ndn_nud_state = NUD_INCOMPLETE;
-	}
 
-	if (ndn->ndn_nud_state == NUD_INCOMPLETE || override)
-	{
-		if (opt_len == 0)
-		{
+	if (ndn->ndn_nud_state == NUD_INCOMPLETE || override) {
+		if (opt_len == 0) {
 			printk(KERN_DEBUG "no opt on NA\n");
-		}
-		else
-		{
-			/* record hardware address */
-
+		} else {
+			/* Record hardware address. */
 			ndn->ndn_flags |= NTF_COMPLETE;
 
 			if (ndisc_store_hwaddr(ndn, opt, opt_len,
-					       ND_OPT_TARGET_LL_ADDR))
-			{
+					       ND_OPT_TARGET_LL_ADDR)) {
 #if ND_DEBUG >= 2
 				printk(KERN_DEBUG
 				       "event_na: invalid TARGET_LL_ADDR\n");
@@ -903,42 +923,40 @@
 		}
 	}
 
-
-	if (solicited || override || ndn->ndn_nud_state == NUD_INCOMPLETE)
-	{
-
+	if (solicited || override || ndn->ndn_nud_state == NUD_INCOMPLETE) {
 		ndn->ndn_probes = 0;
 		ndn->ndn_tstamp = jiffies;
 
 		if (ndn->ndn_nud_state & NUD_IN_TIMER)
-		{
 			ndisc_del_timer(ndn);
-		}
 
 		if (solicited)
-		{
 			ndn->ndn_nud_state = NUD_REACHABLE;
-		}
 		else
-		{
 			ndn->ndn_nud_state = NUD_STALE;
-		}
 	}
 			
 	while ((skb=skb_dequeue(&ndn->neigh.arp_queue)))
-	{
 		dev_queue_xmit(skb);
-	}
 }
 
-static void ndisc_event_ns(struct in6_addr *saddr, struct sk_buff *skb)
+static struct nd_neigh * ndisc_event_ns(struct in6_addr *saddr,
+					struct sk_buff *skb)
 {
 	struct nd_neigh *ndn;
 	u8 *opt;
 	int len;
 
+	NDBG(("ndisc_event_ns: "));
+	if(saddr)
+		NDBG(("saddr[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x] ",
+		      saddr->s6_addr16[0], saddr->s6_addr16[1], saddr->s6_addr16[2],
+		      saddr->s6_addr16[3], saddr->s6_addr16[4], saddr->s6_addr16[5],
+		      saddr->s6_addr16[6], saddr->s6_addr16[7]));
+	NDBG(("\n"));
+
 	opt = skb->h.raw;
-	opt += sizeof(struct icmpv6hdr) + sizeof(struct in6_addr);
+	opt += sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
 
 	len = skb->tail - opt;
 
@@ -949,124 +967,48 @@
 					       skb->dev);
 
 	if (ndn == NULL)
-	{
 		ndn = ndisc_new_neigh(skb->dev, saddr);
-	}
 
        	neigh_table_unlock(&nd_tbl);
 
+	if (ndn == NULL)
+		return NULL;
+
 	switch(ndn->ndn_nud_state) {
-		case NUD_REACHABLE:
-		case NUD_STALE:
-		case NUD_DELAY:
-			if (*opt != ND_OPT_SOURCE_LL_ADDR ||
-			    len != ndn->ndn_dev->addr_len ||
-			    memcmp(ndn->neigh.ha, opt + 2, len))
-			{
-				break;
-			}
+	case NUD_REACHABLE:
+	case NUD_STALE:
+	case NUD_DELAY:
+		if (*opt != ND_OPT_SOURCE_LL_ADDR ||
+		    len != ndn->ndn_dev->addr_len ||
+		    memcmp(ndn->neigh.ha, opt + 2, len))
+			break;
 
-			if (ndn->ndn_nud_state & NUD_IN_TIMER)
-			{
-				ndisc_del_timer(ndn);
-			}
-		default:
-			ndn->ndn_flags |= NTF_COMPLETE;
+		if (ndn->ndn_nud_state & NUD_IN_TIMER)
+			ndisc_del_timer(ndn);
+
+		/* FALLTHROUGH */
+	default:
+		ndn->ndn_flags |= NTF_COMPLETE;
 			
-			if (ndisc_store_hwaddr(ndn, opt, len,
-					       ND_OPT_SOURCE_LL_ADDR))
-			{
+		if (ndisc_store_hwaddr(ndn, opt, len, ND_OPT_SOURCE_LL_ADDR)) {
 #if ND_DEBUG >= 1
-				printk(KERN_DEBUG
-				       "event_ns: invalid SOURCE_LL_ADDR\n");
+			printk(KERN_DEBUG
+			       "event_ns: invalid SOURCE_LL_ADDR\n");
 #endif
 
-				ndn->ndn_flags &= ~NTF_COMPLETE;
-				ndn->ndn_nud_state = NUD_NONE;
-				return;
-			}
-
-			ndn->ndn_nud_state = NUD_STALE;
-			ndn->ndn_tstamp = jiffies;
-			ndn->ndn_probes = 0;
-	}
-
-}
-
-static struct rt6_info *ndisc_get_dflt_router(struct device *dev,
-					      struct in6_addr *addr)
-{	
-	struct rt6_info *iter;
-
-	for (iter = default_rt_list; iter; iter=iter->next)
-	{
-		if (dev == iter->rt_dev &&
-		    ipv6_addr_cmp(&iter->rt_dst, addr) == 0)
-		{
-			return iter;
+			ndn->ndn_flags &= ~NTF_COMPLETE;
+			ndn->ndn_nud_state = NUD_NONE;
+			return ndn;
 		}
-	}
-	return NULL;
-}
-
-static void ndisc_add_dflt_router(struct rt6_info *rt)
-{
-	struct rt6_info *iter;
 
-	rt->rt_ref++;
-	rt->fib_node = &routing_table;
-	rt6_stats.fib_rt_alloc++;
-
-	if (default_rt_list == NULL)
-	{
-		default_rt_list = rt;
-		return;
-	}
-
-	for (iter = default_rt_list; iter->next; iter=iter->next)
-		;
-
-	iter->next = rt;
-}
-
-static void ndisc_del_dflt_router(struct rt6_info *rt)
-{
-	struct rt6_info *iter, *back;
-
-	if (rt == default_rt_list)
-	{
-		default_rt_list = rt->next;
-	}
-	else
-	{
-		back = NULL;
-		for (iter = default_rt_list; iter; iter=iter->next)
-		{
-			if (iter == rt)
-			{
-				back->next = rt->next;
-				break;
-			}
-			back = iter;
-		}
-	}
+		ndn->ndn_nud_state = NUD_STALE;
+		ndn->ndn_tstamp = jiffies;
+		ndn->ndn_probes = 0;
+	};
 
-	rt->fib_node = NULL;
-	rt_release(rt);
+	return ndn;
 }
 
-static void ndisc_purge_dflt_routers(void)
-{
-	struct rt6_info *iter, *rt;
-
-	for (iter = default_rt_list; iter; )
-	{
-		rt = iter;
-		iter=iter->next;
-		rt_release(rt);
-	}
-	default_rt_list = NULL;
-}
 
 static void ndisc_ll_addr_update(struct nd_neigh *ndn, u8* opt, int len,
 				 int type)
@@ -1077,19 +1019,14 @@
 	case NUD_DELAY:
 		if (len == ndn->ndn_dev->addr_len &&
 		    memcmp(ndn->neigh.ha, opt + 2, len) == 0)
-		{
 			break;
-		}
 
 		if (ndn->ndn_nud_state & NUD_IN_TIMER)
-		{
 			ndisc_del_timer(ndn);
-		}
 	default:
 		ndn->ndn_flags |= NTF_COMPLETE;
 		
-		if (ndisc_store_hwaddr(ndn, opt, len, type))
-		{
+		if (ndisc_store_hwaddr(ndn, opt, len, type)) {
 #if ND_DEBUG >=1
 			printk(KERN_DEBUG "NDISC: invalid LL_ADDR\n");
 #endif
@@ -1101,45 +1038,7 @@
 		ndn->ndn_nud_state = NUD_STALE;
 		ndn->ndn_tstamp = jiffies;
 		ndn->ndn_probes = 0;
-	}
-	
-}
-
-struct rt6_info * dflt_rt_lookup(void)
-{
-	struct rt6_info *match = NULL;
-	struct rt6_info *rt;
-	int score = -1;
-	unsigned long now = jiffies;
-
-	for (rt = default_rt_list; rt; rt=rt->next)
-	{
-		struct neighbour *neigh = rt->rt_nexthop;
-		struct nd_neigh *ndn = (struct nd_neigh *) neigh;
-		
-		if (score < 0)
-		{
-			score = 0;
-			match = rt;
-		}
-
-		if (ndn->ndn_nud_state == NUD_REACHABLE)
-		{
-			if (score < 1)
-			{
-				score = 1;
-				match = rt;
-			}
-
-			if (now  - ndn->ndn_tstamp < nd_reachable_time)
-			{
-				return rt;
-			}
-		}
-
-	}
-
-	return match;
+	};
 }
 
 static void ndisc_router_discovery(struct sk_buff *skb)
@@ -1153,10 +1052,11 @@
 
 	__u8 * opt = (__u8 *)(ra_msg + 1);
 
+	NDBG(("ndisc_router_discovery(%p)\n", skb));
+
 	optlen = (skb->tail - skb->h.raw) - sizeof(struct ra_msg);
 
-	if (skb->nh.ipv6h->hop_limit != 255)
-	{
+	if (skb->nh.ipv6h->hop_limit != 255) {
 		printk(KERN_INFO
 		       "NDISC: fake router advertisment received\n");
 		return;
@@ -1167,14 +1067,12 @@
 	 */
 
 	in6_dev = ipv6_get_idev(skb->dev);
-	if (in6_dev == NULL)
-	{
+	if (in6_dev == NULL) {
 		printk(KERN_DEBUG "RA: can't find in6 device\n");
 		return;
 	}
 	
-	if (in6_dev->if_flags & IF_RS_SENT)
-	{
+	if (in6_dev->if_flags & IF_RS_SENT) {
 		/*
 		 *	flag that an RA was received after an RS was sent
 		 *	out on this interface.
@@ -1184,94 +1082,57 @@
 
 	lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime);
 
-	rt = ndisc_get_dflt_router(skb->dev, &skb->nh.ipv6h->saddr);
+	rt = rt6_get_dflt_router(&skb->nh.ipv6h->saddr, skb->dev);
 
-	if (rt && lifetime == 0)
-	{
-		ndisc_del_dflt_router(rt);
+	if (rt && lifetime == 0) {
+		ip6_del_rt(rt);
 		rt = NULL;
 	}
 
-	if (rt == NULL && lifetime)
-	{
-		struct in6_addr *saddr;
-		
+	if (rt == NULL && lifetime) {
 #if ND_DEBUG >= 2
-		printk(KERN_DEBUG "ndisc_rdisc: new default router\n");
+		printk(KERN_DEBUG "ndisc_rdisc: adding default router\n");
 #endif
 
-		rt = (struct rt6_info *) kmalloc(sizeof(struct rt6_info),
-						 GFP_ATOMIC);
+		rt = rt6_add_dflt_router(&skb->nh.ipv6h->saddr, skb->dev);
 
-		if (rt == NULL)
-		{
-			/* We are out-of-memory. Ignore it */
+		if (rt == NULL) {
+#if ND_DEBUG >= 1
+			printk(KERN_DEBUG "route_add failed\n");
+#endif
 			return;
 		}
 
-		saddr = &skb->nh.ipv6h->saddr;
-		neigh_table_lock(&nd_tbl);
-		
-		ndn = (struct nd_neigh *) neigh_lookup(&nd_tbl, saddr,
-						       sizeof(struct in6_addr),
-						       skb->dev);
-
-		if (ndn == NULL)
-		{
-			ndn = ndisc_new_neigh(skb->dev, saddr);
-			
-			if (ndn == NULL)
-			{
-				kfree(rt);
-				neigh_table_unlock(&nd_tbl);
-				return;
-			}
+		ndn = (struct nd_neigh *) rt->rt6i_nexthop;
+		if (ndn == NULL) {
+#if ND_DEBUG >= 1
+			printk(KERN_DEBUG "nd: add default router: null "
+			       "neighbour\n");
+#endif
+			return;
 		}
-		
-		neigh_table_unlock(&nd_tbl);
-
-		atomic_inc(&ndn->ndn_refcnt);
-
 		ndn->ndn_flags |= NCF_ROUTER;
-
-		memset(rt, 0, sizeof(struct rt6_info));
-
-		ipv6_addr_copy(&rt->rt_dst, &skb->nh.ipv6h->saddr);
-		rt->rt_metric = 1;
-		rt->rt_flags = RTF_GATEWAY | RTF_DYNAMIC;
-		rt->rt_dev = skb->dev;
-		rt->rt_nexthop = (struct neighbour *) ndn;
-
-		ndisc_add_dflt_router(rt);
 	}
 
 	if (rt)
-	{
-		rt->rt_expires = jiffies + (HZ * lifetime);
-        }
+		rt->rt6i_expires = jiffies + (HZ * lifetime);
 
 	if (ra_msg->icmph.icmp6_hop_limit)
-	{
-		ipv6_hop_limit = ra_msg->icmph.icmp6_hop_limit;
-	}
+		ipv6_config.hop_limit = ra_msg->icmph.icmp6_hop_limit;
 
 	/*
 	 *	Update Reachable Time and Retrans Timer
 	 */
 
 	if (ra_msg->retrans_timer)
-	{
-		nd_retrans_timer = ntohl(ra_msg->retrans_timer);
-	}
+		ipv6_config.nd_retrans_time = ntohl(ra_msg->retrans_timer);
 
-	if (ra_msg->reachable_time)
-	{
+	if (ra_msg->reachable_time) {
 		__u32 rtime = ntohl(ra_msg->reachable_time);
 
-		if (rtime != nd_base_reachable_time)
-		{
-			nd_base_reachable_time = rtime;
-			nd_gc_staletime	= 3 * nd_base_reachable_time;
+		if (rtime != ipv6_config.nd_base_reachable_time) {
+			ipv6_config.nd_base_reachable_time = rtime;
+			nd_gc_staletime	= 3 * rtime;
 			nd_reachable_time = rand_reach_time();
 		}
 		
@@ -1286,22 +1147,22 @@
 
                 len = (opt[1] << 3);
 
-		if (len == 0)
-		{
+		if (len == 0) {
 			printk(KERN_DEBUG "RA: opt has 0 len\n");
 			break;
 		}
 
                 switch(*opt) {
                 case ND_OPT_SOURCE_LL_ADDR:
-			
+
 			if (rt == NULL)
 				break;
 			
-			ndn = (struct nd_neigh *) rt->rt_nexthop;
+			ndn = (struct nd_neigh *) rt->rt6i_nexthop;
 
-			ndisc_ll_addr_update(ndn, opt, len,
-					     ND_OPT_SOURCE_LL_ADDR);
+			if (ndn)
+				ndisc_ll_addr_update(ndn, opt, len,
+						     ND_OPT_SOURCE_LL_ADDR);
 			break;
 
                 case ND_OPT_PREFIX_INFO:
@@ -1309,17 +1170,17 @@
                         break;
 
                 case ND_OPT_MTU:
-
-			if (rt)
-			{
+			if (rt) {
 				int mtu;
 				struct device *dev;
 				
 				mtu = htonl(*(__u32 *)(opt+4));
-				dev = rt->rt_nexthop->dev;
+				dev = rt->rt6i_dev;
 
-				if (mtu < 576)
-				{
+				if (dev == NULL)
+					break;
+
+				if (mtu < 576) {
 					printk(KERN_DEBUG "NDISC: router "
 					       "announcement with mtu = %d\n",
 					       mtu);
@@ -1327,13 +1188,9 @@
 				}
 
 				if (dev->change_mtu)
-				{
 					dev->change_mtu(dev, mtu);
-				}
 				else
-				{
 					dev->mtu = mtu;
-				}
 			}
                         break;
 
@@ -1343,32 +1200,32 @@
 			break;
 		default:
 			printk(KERN_DEBUG "unkown option in RA\n");
-                }
+                };
                 optlen -= len;
                 opt += len;
         }
-        
 }
 
 void ndisc_forwarding_on(void)
 {
+
 	/*
-	 *	forwarding was turned on
+	 *	Forwarding was turned on.
 	 */
 
-	ndisc_purge_dflt_routers();
+	rt6_purge_dflt_routers(0);
 }
 
 void ndisc_forwarding_off(void)
 {
 	/*
-	 *	forwarding was turned off
+	 *	Forwarding was turned off.
 	 */
 }
 
 static void ndisc_redirect_rcv(struct sk_buff *skb)
 {
-	struct icmpv6hdr *icmph;
+	struct icmp6hdr *icmph;
 	struct in6_addr *dest;
 	struct in6_addr *target;	/* new first hop to destination */
 	struct nd_neigh *ndn;
@@ -1377,75 +1234,66 @@
 	int optlen;
 	u8 * opt;
 
-	if (skb->nh.ipv6h->hop_limit != 255)
-	{
+	NDBG(("ndisc_redirect_rcv(%p)\n", skb));
+
+	if (skb->nh.ipv6h->hop_limit != 255) {
 		printk(KERN_WARNING
 		       "NDISC: fake ICMP redirect received\n");
 		return;
 	}
 
-	if (!(ipv6_addr_type(&skb->nh.ipv6h->saddr) & IPV6_ADDR_LINKLOCAL))
-	{
+	if (!(ipv6_addr_type(&skb->nh.ipv6h->saddr) & IPV6_ADDR_LINKLOCAL)) {
 		printk(KERN_WARNING
 		       "ICMP redirect: source address is not linklocal\n");
 		return;
 	}
 
 	optlen = skb->tail - skb->h.raw;
-	optlen -= sizeof(struct icmpv6hdr) + 2 * sizeof(struct in6_addr);
+	optlen -= sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
 
-	if (optlen < 0)
-	{
+	if (optlen < 0) {
 		printk(KERN_WARNING "ICMP redirect: packet too small\n");
 		return;
 	}
 
-	icmph = (struct icmpv6hdr *) skb->h.raw;
+	icmph = (struct icmp6hdr *) skb->h.raw;
 	target = (struct in6_addr *) (icmph + 1);
 	dest = target + 1;
 
-	if (ipv6_addr_type(dest) & IPV6_ADDR_MULTICAST)
-	{
+	if (ipv6_addr_type(dest) & IPV6_ADDR_MULTICAST) {
 		printk(KERN_WARNING "ICMP redirect for multicast addr\n");
 		return;
 	}
 
-	if (ipv6_addr_cmp(dest, target) == 0)
-	{
+	if (ipv6_addr_cmp(dest, target) == 0) {
 		on_link = 1;
-	}
-	else if (!(ipv6_addr_type(target) & IPV6_ADDR_LINKLOCAL))
-	{
+	} else if (!(ipv6_addr_type(target) & IPV6_ADDR_LINKLOCAL)) {
 		printk(KERN_WARNING
 		       "ICMP redirect: target address is not linklocal\n");
 		return;
 	}
 
 	/* passed validation tests */
+	rt = rt6_redirect(dest, &skb->nh.ipv6h->saddr, target, skb->dev,
+			  on_link);
 
-	rt = ipv6_rt_redirect(skb->dev, dest, target, on_link);
-
-	if (rt == NULL)
-	{
+	if (rt == NULL) {
 		printk(KERN_WARNING "ICMP redirect: no route to host\n");
 		return;
 	}
 
-	ndn = (struct nd_neigh *) rt->rt_nexthop;
+	ndn = (struct nd_neigh *) rt->rt6i_nexthop;
 
 	opt = (u8 *) (dest + 1);
 
-	while (optlen > 0)
-	{
+	while (optlen > 0) {
 		int len;
 
 		len = (opt[1] << 3);
 
 		if (*opt == ND_OPT_TARGET_LL_ADDR)
-		{
 			ndisc_ll_addr_update(ndn, opt, len,
 					     ND_OPT_TARGET_LL_ADDR);
-		}
 
 		opt += len;
 		optlen -= len;
@@ -1456,12 +1304,13 @@
 			 struct in6_addr *target)
 {
 	struct sock *sk = ndisc_socket->sk;
-	int len = sizeof(struct icmpv6hdr) + 2 * sizeof(struct in6_addr);
+	int len = sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
 	struct sk_buff *buff;
 	struct nd_neigh *ndn = (struct nd_neigh *) neigh;
 	struct inet6_ifaddr *ifp;
-	struct icmpv6hdr *icmph;
+	struct icmp6hdr *icmph;
 	struct in6_addr *addrp;
+	struct device *dev;
 	struct rt6_info *rt;
 	int ta_len = 0;
 	u8 *opt;
@@ -1469,19 +1318,25 @@
 	int err;
 	int hlen;
 
-	rt = fibv6_lookup(&skb->nh.ipv6h->saddr, skb->dev, 0);
-	
-	if (rt->rt_flags & RTF_GATEWAY)
-	{
+	dev = skb->dev;
+	rt = rt6_lookup(&skb->nh.ipv6h->saddr, NULL, dev, 0);
+
+	if (rt == NULL || rt->u.dst.error) {
+#if ND_DEBUG >= 1
+		printk(KERN_DEBUG "ndisc_send_redirect: hostunreach\n");
+#endif
+		return;
+	}
+
+	if (rt->rt6i_flags & RTF_GATEWAY) {
 #if ND_DEBUG >= 1
 		printk(KERN_DEBUG "ndisc_send_redirect: not a neighbour\n");
 #endif
 		return;
 	}
 
-	if (ndn->ndn_nud_state == NUD_REACHABLE)
-	{
-		ta_len  = ((neigh->dev->addr_len + 1) >> 3) + 1;
+	if (ndn->ndn_nud_state == NUD_REACHABLE) {
+		ta_len  = ((dev->addr_len + 1) >> 3) + 1;
 		len += (ta_len << 3);
 	}
 
@@ -1489,20 +1344,18 @@
 	rd_len &= ~0x7;
 	len += rd_len;
 
-	ifp = ipv6_get_lladdr(skb->dev);
+	ifp = ipv6_get_lladdr(dev);
 
-	if (ifp == NULL)
-	{
+	if (ifp == NULL) {
 #if ND_DEBUG >= 1
 		printk(KERN_DEBUG "redirect: no link_local addr for dev\n");
 #endif
 		return;
 	}
 
-	buff = sock_alloc_send_skb(sk, MAX_HEADER + len, 0, 0, &err);
-
-	if (buff == NULL)
-	{
+	buff = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,
+				   0, 0, &err);
+	if (buff == NULL) {
 #if ND_DEBUG >= 2
 		printk(KERN_DEBUG "ndisc_send_redirect: alloc_skb failed\n");
 #endif
@@ -1510,17 +1363,23 @@
 	}
 	
 	hlen = 0;
-	if (skb->dev->hard_header_len)
-	{
-		hlen = (skb->dev->hard_header_len + 15) & ~15;
-	}
 
-	skb_reserve(buff, hlen + sizeof(struct ipv6hdr));
+	if (dev->hard_header_len) {
+		skb_reserve(buff, (dev->hard_header_len + 15) & ~15);
+		if (dev->hard_header) {
+			dev->hard_header(buff, dev, ETH_P_IPV6, ndn->ndn_ha,
+					 NULL, len);
+			buff->arp = 1;
+		}
+	}
 	
-	icmph = (struct icmpv6hdr *) skb_put(buff, len);
+	ip6_nd_hdr(sk, buff, dev, &ifp->addr, &skb->nh.ipv6h->saddr,
+		   IPPROTO_ICMPV6, len);
+
+	icmph = (struct icmp6hdr *) skb_put(buff, len);
 
-	memset(icmph, 0, sizeof(struct icmpv6hdr));
-	icmph->type = NDISC_REDIRECT;
+	memset(icmph, 0, sizeof(struct icmp6hdr));
+	icmph->icmp6_type = NDISC_REDIRECT;
 
 	/*
 	 *	copy target and destination addresses
@@ -1537,8 +1396,7 @@
 	 *	include target_address option
 	 */
 
-	if (ta_len)
-	{
+	if (ta_len) {
 		int zb;
 		
 		*(opt++) = ND_OPT_TARGET_LL_ADDR;
@@ -1553,8 +1411,7 @@
 		 */
 
 		zb = (neigh->dev->addr_len + 2) & 0x7; 
-		if (zb)
-		{
+		if (zb) {
 			int comp;
 
 			comp = 8 - zb;
@@ -1574,12 +1431,11 @@
 
 	memcpy(opt, &skb->nh.ipv6h, rd_len - 8);
 	
-	icmph->checksum = csum_ipv6_magic(&ifp->addr, &skb->nh.ipv6h->saddr,
-					  len, IPPROTO_ICMPV6,
-					  csum_partial((u8 *) icmph, len, 0));
+	icmph->icmp6_cksum = csum_ipv6_magic(&ifp->addr, &skb->nh.ipv6h->saddr,
+					     len, IPPROTO_ICMPV6,
+					     csum_partial((u8 *) icmph, len, 0));
 
-	ipv6_xmit(sk, buff, &ifp->addr, &skb->nh.ipv6h->saddr, NULL,
-		  IPPROTO_ICMPV6);
+	dev_queue_xmit(buff);
 }
 
 /* Called by upper layers to validate neighbour cache entries. */
@@ -1587,14 +1443,15 @@
 void ndisc_validate(struct neighbour *neigh)
 {
 	struct nd_neigh *ndn = (struct nd_neigh *) neigh;
-	
+
+	if (neigh == NULL)
+		return;
+
         if (ndn->ndn_nud_state == NUD_INCOMPLETE)
                 return;
 
         if (ndn->ndn_nud_state == NUD_DELAY) 
-	{
                 ndisc_del_timer(ndn);
-        }
 
         nd_stats.rcv_upper_conf++;
         ndn->ndn_nud_state = NUD_REACHABLE;
@@ -1609,26 +1466,30 @@
 	struct nd_neigh *ndn;
 	struct inet6_ifaddr *ifp;
 
-	switch (msg->icmph.type) {
+	NDBG(("ndisc_rcv(type=%d) ", msg->icmph.icmp6_type));
+	switch (msg->icmph.icmp6_type) {
 	case NDISC_NEIGHBOUR_SOLICITATION:
-		if ((ifp = ipv6_chk_addr(&msg->target)))
-		{
+		NDBG(("NS "));
+		if ((ifp = ipv6_chk_addr(&msg->target))) {
 			int addr_type;
 
-			if (ifp->flags & DAD_INCOMPLETE)
-			{
+			if (ifp->flags & DAD_INCOMPLETE) {
 				/*
-				 *	DAD failed 
+				 *	DAD failed
 				 */
 
-				printk(KERN_DEBUG "duplicate address\n");
+				/* XXX Check if this came in over same interface
+				 * XXX we just sent an NS from!  That is valid! -DaveM
+				 */
+
+				printk(KERN_DEBUG "%s: duplicate address\n",
+				       ifp->idev->dev->name);
 				del_timer(&ifp->timer);
 				return 0;
 			}
 
 			addr_type = ipv6_addr_type(saddr);
-			if (addr_type & IPV6_ADDR_UNICAST)
-			{
+			if (addr_type & IPV6_ADDR_UNICAST) {
 				int inc;
 
 				/* 
@@ -1637,25 +1498,18 @@
 				 */
 
 				nd_stats.rcv_probes_ucast++;
-				ndisc_event_ns(saddr, skb);
-
-				neigh_table_lock(&nd_tbl);
 
-				ndn = (struct nd_neigh *)
-					neigh_lookup(&nd_tbl, saddr,
-						     sizeof(struct in6_addr),
-						     dev);
+				ndn = ndisc_event_ns(saddr, skb);
 
-				neigh_table_unlock(&nd_tbl);
+				if (ndn == NULL)
+					return 0;
 
 				inc = ipv6_addr_type(daddr);
 				inc &= IPV6_ADDR_MULTICAST;
 
 				ndisc_send_na(dev, ndn, saddr, &ifp->addr, 
 					      ifp->idev->router, 1, inc, inc);
-			}
-			else
-			{
+			} else {
 #if ND_DEBUG >= 1
 				/* FIXME */
 				printk(KERN_DEBUG "ns: non unicast saddr\n");
@@ -1665,38 +1519,30 @@
 		break;
 
 	case NDISC_NEIGHBOUR_ADVERTISEMENT:
-		
+		NDBG(("NA "));
 		neigh_table_lock(&nd_tbl);	       
 		ndn = (struct nd_neigh *) 
 			neigh_lookup(&nd_tbl, (void *) &msg->target,
 				     sizeof(struct in6_addr), skb->dev);
 		neigh_table_unlock(&nd_tbl);
 
-		if (ndn)
-		{
-			if (ndn->ndn_flags & NCF_ROUTER)
-			{
-				if (msg->icmph.icmp6_router == 0)
-				{
+		if (ndn) {
+			if (ndn->ndn_flags & NCF_ROUTER) {
+				if (msg->icmph.icmp6_router == 0) {
 					/*
 					 *	Change: router to host
 					 */
-					
+#if 0					
 					struct rt6_info *rt;
 					rt = ndisc_get_dflt_router(skb->dev,
 								   saddr);
 					if (rt)
-					{
 						ndisc_del_dflt_router(rt);
-					}
+#endif
 				}
-			}
-			else
-			{
+			} else {
 				if (msg->icmph.icmp6_router)
-				{
 					ndn->ndn_flags |= NCF_ROUTER;
-				}
 			}
 			ndisc_event_na(ndn, (unsigned char *) &msg->opt,
 				       skb->tail - (u8 *)&msg->opt /*opt_len*/,
@@ -1705,24 +1551,28 @@
 		}
 		break;
 
-	}
+	};
 
-	if (ipv6_forwarding == 0)
-	{
-		switch (msg->icmph.type) {
+	if (ipv6_config.forwarding == 0) {
+		switch (msg->icmph.icmp6_type) {
 		case NDISC_ROUTER_ADVERTISEMENT:
-			ndisc_router_discovery(skb);
+			NDBG(("RA "));
+			if (ipv6_config.accept_ra)
+				ndisc_router_discovery(skb);
 			break;
 
 		case NDISC_REDIRECT:
-			ndisc_redirect_rcv(skb);
+			NDBG(("REDIR "));
+			if (ipv6_config.accept_redirects)
+				ndisc_redirect_rcv(skb);
 			break;
-		}
+		};
 	}
 
 	return 0;
 }
 
+#ifdef CONFIG_PROC_FS
 int ndisc_get_info(char *buffer, char **start, off_t offset, int length,
 		   int dummy)
 {
@@ -1732,8 +1582,7 @@
 
 	neigh_table_lock(&nd_tbl);
 
-	for (i = 0; i < nd_tbl.tbl_size; i++)
-	{
+	for (i = 0; i < nd_tbl.tbl_size; i++) {
 		struct neighbour *neigh, *head;
 		head = nd_tbl.hash_buckets[i];
 		
@@ -1744,36 +1593,37 @@
 			struct nd_neigh *ndn = (struct nd_neigh *) neigh;
 			int j;
 
-			for (j=0; j<16; j++)
-			{
+			for (j=0; j<16; j++) {
 				sprintf(buffer + len, "%02x",
 					ndn->ndn_addr.s6_addr[j]);
 				len += 2;
 			}
 
 			len += sprintf(buffer + len,
-				       " %02x %02x %08lx %08lx %04x %04lx ", i,
+				       " %02x %02x %02x %02x %08lx %08lx %08lx %04x %04x %04lx %8s ", i,
+				       ndn->ndn_plen,
+				       ndn->ndn_type,
 				       ndn->ndn_nud_state,
-				       ndn->ndn_expires - now,
+				       ndn->ndn_expires ? ndn->ndn_expires - now : 0,
 				       now - ndn->ndn_tstamp,
+				       nd_reachable_time,
+				       nd_gc_staletime,
 				       ndn->ndn_refcnt,
-				       ndn->ndn_flags);
+				       ndn->ndn_flags,
+				       ndn->ndn_dev ? ndn->ndn_dev->name : "NULLDEV");
 
-			if ((ndn->ndn_flags & NTF_COMPLETE))
-			{
-				for (j=0; j< neigh->dev->addr_len; j++)
-				{
+			if ((ndn->ndn_flags & NTF_COMPLETE)) {
+				for (j=0; j< neigh->dev->addr_len; j++) {
 					sprintf(buffer + len, "%02x",
 						neigh->ha[j]);
 					len += 2;
 				}
-			}
-			else
+			} else {
                                 len += sprintf(buffer + len, "000000000000");
+			}
 			len += sprintf(buffer + len, "\n");
 			
 			neigh = neigh->next;
-
 		} while (neigh != head);
 	}
 
@@ -1795,6 +1645,7 @@
         0, NULL,
         &ndisc_get_info
 };
+#endif	/* CONFIG_PROC_FS */
 
 void ndisc_init(struct net_proto_family *ops)
 {
@@ -1845,22 +1696,13 @@
 #ifdef CONFIG_PROC_FS
 	proc_net_register(&ndisc_proc_entry);
 #endif
-#ifdef CONFIG_IPV6_MODULE
-	ndisc_eth_hook = ndisc_eth_resolv;
-#endif
 }
 
-#ifdef CONFIG_IPV6_MODULE
+#ifdef MODULE
 void ndisc_cleanup(void)
 {
-	ndisc_eth_hook = NULL;
-
 #ifdef CONFIG_PROC_FS
-#ifdef CONFIG_IPV6_MODULE
-        proc_unregister(&proc_net, ndisc_proc_entry.low_ino);
-#else
-	proc_net_unregister(PROC_NET_NDISC);
-#endif
+        proc_net_unregister(ndisc_proc_entry.low_ino);
 #endif
 	del_timer(&ndisc_gc_timer);
 	del_timer(&ndisc_timer);

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