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

Next file: linux-2.4.21/net/ipv6/netfilter/Config.in
Previous file: linux-2.4.21/net/ipv6/mcast.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.20/net/ipv6/ndisc.c linux-2.4.21/net/ipv6/ndisc.c
@@ -42,7 +42,6 @@
 #define ND_PRINTK2 ND_PRINTK
 #endif
 
-#define __NO_VERSION__
 #include <linux/module.h>
 #include <linux/config.h>
 #include <linux/errno.h>
@@ -373,7 +372,10 @@
 		   struct in6_addr *daddr, struct in6_addr *solicited_addr,
 		   int router, int solicited, int override, int inc_opt) 
 {
+	static struct in6_addr tmpaddr;
+	struct inet6_ifaddr *ifp;
         struct sock *sk = ndisc_socket->sk;
+	struct in6_addr *src_addr;
         struct nd_msg *msg;
         int len;
         struct sk_buff *skb;
@@ -389,19 +391,29 @@
 	}
 
 	skb = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,
-				  0, &err);
+				  1, &err);
 
 	if (skb == NULL) {
 		ND_PRINTK1("send_na: alloc skb failed\n");
 		return;
 	}
+	/* for anycast or proxy, solicited_addr != src_addr */
+	ifp = ipv6_get_ifaddr(solicited_addr, dev);
+	if (ifp) {
+		src_addr = solicited_addr;
+		in6_ifa_put(ifp);
+	} else {
+		if (ipv6_dev_get_saddr(dev, daddr, &tmpaddr, 0))
+			return;
+		src_addr = &tmpaddr;
+	}
 
 	if (ndisc_build_ll_hdr(skb, dev, daddr, neigh, len) == 0) {
 		kfree_skb(skb);
 		return;
 	}
 
-	ip6_nd_hdr(sk, skb, dev, solicited_addr, daddr, IPPROTO_ICMPV6, len);
+	ip6_nd_hdr(sk, skb, dev, src_addr, daddr, IPPROTO_ICMPV6, len);
 
 	msg = (struct nd_msg *) skb_put(skb, len);
 
@@ -421,7 +433,7 @@
 		ndisc_fill_option(msg->opt, ND_OPT_TARGET_LL_ADDR, dev->dev_addr, dev->addr_len);
 
 	/* checksum */
-	msg->icmph.icmp6_cksum = csum_ipv6_magic(solicited_addr, daddr, len, 
+	msg->icmph.icmp6_cksum = csum_ipv6_magic(src_addr, daddr, len, 
 						 IPPROTO_ICMPV6,
 						 csum_partial((__u8 *) msg, 
 							      len, 0));
@@ -456,7 +468,7 @@
 		len += NDISC_OPT_SPACE(dev->addr_len);
 
 	skb = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,
-				  0, &err);
+				  1, &err);
 	if (skb == NULL) {
 		ND_PRINTK1("send_ns: alloc skb failed\n");
 		return;
@@ -509,7 +521,7 @@
 		len += NDISC_OPT_SPACE(dev->addr_len);
 
         skb = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,
-				  0, &err);
+				  1, &err);
 	if (skb == NULL) {
 		ND_PRINTK1("send_ns: alloc skb failed\n");
 		return;
@@ -583,6 +595,297 @@
 	}
 }
 
+void ndisc_recv_ns(struct sk_buff *skb)
+{
+	struct nd_msg *msg = (struct nd_msg *)skb->h.raw;
+	struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
+	struct in6_addr *daddr = &skb->nh.ipv6h->daddr;
+	u8 *lladdr = NULL;
+	int lladdrlen = 0;
+	u32 ndoptlen = skb->tail - msg->opt;
+	struct ndisc_options ndopts;
+	struct net_device *dev = skb->dev;
+	struct inet6_ifaddr *ifp;
+	struct neighbour *neigh;
+
+	if (skb->len < sizeof(struct nd_msg)) {
+		if (net_ratelimit())
+			printk(KERN_WARNING "ICMP NS: packet too short\n");
+		return;
+	}
+
+	if (ipv6_addr_type(&msg->target)&IPV6_ADDR_MULTICAST) {
+		if (net_ratelimit())
+			printk(KERN_WARNING "ICMP NS: target address is multicast\n");
+		return;
+	}
+
+	if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
+		if (net_ratelimit())
+			printk(KERN_WARNING "ICMP NS: invalid ND option, ignored.\n");
+		return;
+	}
+
+	if (ndopts.nd_opts_src_lladdr) {
+		lladdr = (u8*)(ndopts.nd_opts_src_lladdr + 1);
+		lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3;
+		if (lladdrlen != NDISC_OPT_SPACE(dev->addr_len)) {
+			if (net_ratelimit())
+				printk(KERN_WARNING "ICMP NS: bad lladdr length.\n");
+			return;
+		}
+	}
+
+	/* XXX: RFC2461 7.1.1:
+	 * 	If the IP source address is the unspecified address, there
+	 *	MUST NOT be source link-layer address option in the message.
+	 *
+	 *	NOTE! Linux kernel < 2.4.4 broke this rule.
+	 */
+		 	
+	/* XXX: RFC2461 7.1.1:
+	 *	If the IP source address is the unspecified address, the IP
+      	 *	destination address MUST be a solicited-node multicast address.
+	 */
+
+	if ((ifp = ipv6_get_ifaddr(&msg->target, dev)) != NULL) {
+		int addr_type = ipv6_addr_type(saddr);
+
+		if (ifp->flags & IFA_F_TENTATIVE) {
+			/* Address is tentative. If the source
+			   is unspecified address, it is someone
+			   does DAD, otherwise we ignore solicitations
+			   until DAD timer expires.
+			 */
+			if (addr_type == IPV6_ADDR_ANY) {
+				if (dev->type == ARPHRD_IEEE802_TR) { 
+					unsigned char *sadr = skb->mac.raw ;
+					if (((sadr[8] &0x7f) != (dev->dev_addr[0] & 0x7f)) ||
+					(sadr[9] != dev->dev_addr[1]) ||
+					(sadr[10] != dev->dev_addr[2]) ||
+					(sadr[11] != dev->dev_addr[3]) ||
+					(sadr[12] != dev->dev_addr[4]) ||
+					(sadr[13] != dev->dev_addr[5])) 
+					{
+						addrconf_dad_failure(ifp) ; 
+					}
+				} else {
+					addrconf_dad_failure(ifp);
+				}
+			} else
+				in6_ifa_put(ifp);
+			return;
+		}
+	
+		if (addr_type == IPV6_ADDR_ANY) {
+			struct in6_addr maddr;
+
+			ipv6_addr_all_nodes(&maddr);
+			ndisc_send_na(dev, NULL, &maddr, &ifp->addr, 
+				      ifp->idev->cnf.forwarding, 0, 
+				      ipv6_addr_type(&ifp->addr)&IPV6_ADDR_ANYCAST ? 0 : 1, 
+				      1);
+			in6_ifa_put(ifp);
+			return;
+		}
+
+		if (addr_type & IPV6_ADDR_UNICAST) {
+			int inc = ipv6_addr_type(daddr)&IPV6_ADDR_MULTICAST;
+
+			if (inc)
+				nd_tbl.stats.rcv_probes_mcast++;
+			else
+				nd_tbl.stats.rcv_probes_ucast++;
+
+			/* 
+			 *	update / create cache entry
+			 *	for the source adddress
+			 */
+
+			neigh = neigh_event_ns(&nd_tbl, lladdr, saddr, dev);
+
+			if (neigh || !dev->hard_header) {
+				ndisc_send_na(dev, neigh, saddr, &ifp->addr, 
+					      ifp->idev->cnf.forwarding, 1, 
+					      ipv6_addr_type(&ifp->addr)&IPV6_ADDR_ANYCAST ? 0 : 1, 
+					      1);
+				if (neigh)
+					neigh_release(neigh);
+			}
+		}
+		in6_ifa_put(ifp);
+	} else if (ipv6_chk_acast_addr(dev, &msg->target)) {
+		struct inet6_dev *idev = in6_dev_get(dev);
+		int addr_type = ipv6_addr_type(saddr);
+
+		/* anycast */
+
+		if (!idev) {
+			/* XXX: count this drop? */
+			return;
+		}
+
+		if (addr_type == IPV6_ADDR_ANY) {
+			struct in6_addr maddr;
+
+			ipv6_addr_all_nodes(&maddr);
+			ndisc_send_na(dev, NULL, &maddr, &msg->target,
+				      idev->cnf.forwarding, 0, 0, 1);
+			in6_dev_put(idev);
+			return;
+		}
+
+		if (addr_type & IPV6_ADDR_UNICAST) {
+			int inc = ipv6_addr_type(daddr)&IPV6_ADDR_MULTICAST;
+			if (inc)  
+				nd_tbl.stats.rcv_probes_mcast++;
+ 			else
+				nd_tbl.stats.rcv_probes_ucast++;
+
+			/*
+			 *   update / create cache entry
+			 *   for the source adddress
+			 */
+
+			neigh = neigh_event_ns(&nd_tbl, lladdr, saddr, skb->dev);
+
+			if (neigh || !dev->hard_header) {
+				ndisc_send_na(dev, neigh, saddr,
+					&msg->target, 
+				        idev->cnf.forwarding, 1, 0, inc);
+				if (neigh)
+					neigh_release(neigh);
+			}
+		}
+		in6_dev_put(idev);
+	} else {
+		struct inet6_dev *in6_dev = in6_dev_get(dev);
+		int addr_type = ipv6_addr_type(saddr);
+
+		if (in6_dev && in6_dev->cnf.forwarding &&
+		    (addr_type & IPV6_ADDR_UNICAST) &&
+		    pneigh_lookup(&nd_tbl, &msg->target, dev, 0)) {
+			int inc = ipv6_addr_type(daddr)&IPV6_ADDR_MULTICAST;
+
+			if (skb->stamp.tv_sec == 0 ||
+			    skb->pkt_type == PACKET_HOST ||
+			    inc == 0 ||
+			    in6_dev->nd_parms->proxy_delay == 0) {
+				if (inc)
+					nd_tbl.stats.rcv_probes_mcast++;
+				else
+					nd_tbl.stats.rcv_probes_ucast++;
+					
+				neigh = neigh_event_ns(&nd_tbl, lladdr, saddr, dev);
+
+				if (neigh) {
+					ndisc_send_na(dev, neigh, saddr, &msg->target,
+						      0, 1, 0, 1);
+					neigh_release(neigh);
+				}
+			} else {
+				struct sk_buff *n = skb_clone(skb, GFP_ATOMIC);
+				if (n)
+					pneigh_enqueue(&nd_tbl, in6_dev->nd_parms, n);
+				in6_dev_put(in6_dev);
+				return;
+			}
+		}
+		if (in6_dev)
+			in6_dev_put(in6_dev);
+	}
+	return;
+}
+
+void ndisc_recv_na(struct sk_buff *skb)
+{
+	struct nd_msg *msg = (struct nd_msg *)skb->h.raw;
+	struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
+	struct in6_addr *daddr = &skb->nh.ipv6h->daddr;
+	u8 *lladdr = NULL;
+	int lladdrlen = 0;
+	u32 ndoptlen = skb->tail - msg->opt;
+	struct ndisc_options ndopts;
+	struct net_device *dev = skb->dev;
+	struct inet6_ifaddr *ifp;
+	struct neighbour *neigh;
+
+	if (skb->len < sizeof(struct nd_msg)) {
+		if (net_ratelimit())
+			printk(KERN_WARNING "ICMP NA: packet too short\n");
+		return;
+	}
+
+	if (ipv6_addr_type(&msg->target)&IPV6_ADDR_MULTICAST) {
+		if (net_ratelimit())
+			printk(KERN_WARNING "NDISC NA: target address is multicast\n");
+		return;
+	}
+
+	if ((ipv6_addr_type(daddr)&IPV6_ADDR_MULTICAST) &&
+	    msg->icmph.icmp6_solicited) {
+		ND_PRINTK0("NDISC: solicited NA is multicasted\n");
+		return;
+	}
+		
+	if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
+		if (net_ratelimit())
+			printk(KERN_WARNING "ICMP NS: invalid ND option, ignored.\n");
+		return;
+	}
+	if (ndopts.nd_opts_tgt_lladdr) {
+		lladdr = (u8*)(ndopts.nd_opts_tgt_lladdr + 1);
+		lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3;
+		if (lladdrlen != NDISC_OPT_SPACE(dev->addr_len)) {
+			if (net_ratelimit())
+				printk(KERN_WARNING "NDISC NA: invalid lladdr length.\n");
+			return;
+		}
+	}
+	if ((ifp = ipv6_get_ifaddr(&msg->target, dev))) {
+		if (ifp->flags & IFA_F_TENTATIVE) {
+			addrconf_dad_failure(ifp);
+			return;
+		}
+		/* What should we make now? The advertisement
+		   is invalid, but ndisc specs say nothing
+		   about it. It could be misconfiguration, or
+		   an smart proxy agent tries to help us :-)
+		 */
+		ND_PRINTK0("%s: someone advertises our address!\n",
+			   ifp->idev->dev->name);
+		in6_ifa_put(ifp);
+		return;
+	}
+	neigh = neigh_lookup(&nd_tbl, &msg->target, dev);
+
+	if (neigh) {
+		if (neigh->flags & NTF_ROUTER) {
+			if (msg->icmph.icmp6_router == 0) {
+				/*
+				 *	Change: router to host
+				 */
+				struct rt6_info *rt;
+				rt = rt6_get_dflt_router(saddr, dev);
+				if (rt) {
+					/* It is safe only because
+					   we aer in BH */
+					dst_release(&rt->u.dst);
+					ip6_del_rt(rt, NULL);
+				}
+			}
+		} else {
+			if (msg->icmph.icmp6_router)
+				neigh->flags |= NTF_ROUTER;
+		}
+
+		neigh_update(neigh, lladdr,
+			     msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE,
+			     msg->icmph.icmp6_override, 1);
+		neigh_release(neigh);
+	}
+}
+
 static void ndisc_router_discovery(struct sk_buff *skb)
 {
         struct ra_msg *ra_msg = (struct ra_msg *) skb->h.raw;
@@ -623,6 +926,7 @@
 	}
 
 	if (!ndisc_parse_options(opt, optlen, &ndopts)) {
+		in6_dev_put(in6_dev);
 		if (net_ratelimit())
 			ND_PRINTK2(KERN_WARNING
 				   "ICMP6 RA: invalid ND option, ignored.\n");
@@ -642,7 +946,7 @@
 	rt = rt6_get_dflt_router(&skb->nh.ipv6h->saddr, skb->dev);
 
 	if (rt && lifetime == 0) {
-		ip6_del_rt(rt);
+		ip6_del_rt(rt, NULL);
 		rt = NULL;
 	}
 
@@ -922,7 +1226,7 @@
  	}
 
 	buff = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,
-				   0, &err);
+				   1, &err);
 	if (buff == NULL) {
 		ND_PRINTK1("ndisc_send_redirect: alloc_skb failed\n");
 		return;
@@ -990,12 +1294,7 @@
 
 int ndisc_rcv(struct sk_buff *skb)
 {
-	struct net_device *dev = skb->dev;
-	struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
-	struct in6_addr *daddr = &skb->nh.ipv6h->daddr;
 	struct nd_msg *msg = (struct nd_msg *) skb->h.raw;
-	struct neighbour *neigh;
-	struct inet6_ifaddr *ifp;
 
 	__skb_push(skb, skb->data-skb->h.raw);
 
@@ -1015,241 +1314,12 @@
 
 	switch (msg->icmph.icmp6_type) {
 	case NDISC_NEIGHBOUR_SOLICITATION:
-	    {
-		struct nd_msg *msg = (struct nd_msg *)skb->h.raw;
-		u8 *lladdr = NULL;
-		int lladdrlen = 0;
-		u32 ndoptlen = skb->tail - msg->opt;
-		struct ndisc_options ndopts;
-
-		if (skb->len < sizeof(struct nd_msg)) {
-			if (net_ratelimit())
-				printk(KERN_WARNING "ICMP NS: packet too short\n");
-			return 0;
-		}
-
-		if (ipv6_addr_type(&msg->target)&IPV6_ADDR_MULTICAST) {
-			if (net_ratelimit())
-				printk(KERN_WARNING "ICMP NS: target address is multicast\n");
-			return 0;
-		}
-
-		if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
-			if (net_ratelimit())
-				printk(KERN_WARNING "ICMP NS: invalid ND option, ignored.\n");
-			return 0;
-		}
-
-		if (ndopts.nd_opts_src_lladdr) {
-			lladdr = (u8*)(ndopts.nd_opts_src_lladdr + 1);
-			lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3;
-			if (lladdrlen != NDISC_OPT_SPACE(skb->dev->addr_len)) {
-				if (net_ratelimit())
-					printk(KERN_WARNING "ICMP NS: bad lladdr length.\n");
-				return 0;
-			}
-		}
-
-		/* XXX: RFC2461 7.1.1:
-		 * 	If the IP source address is the unspecified address, there
-		 *	MUST NOT be source link-layer address option in the message.
-		 *
-		 *	NOTE! Linux kernel < 2.4.4 broke this rule.
-		 */
-		 	
-		/* XXX: RFC2461 7.1.1:
-		 *	If the IP source address is the unspecified address, the IP
-      		 *	destination address MUST be a solicited-node multicast address.
-		 */
-
-		if ((ifp = ipv6_get_ifaddr(&msg->target, dev)) != NULL) {
-			int addr_type = ipv6_addr_type(saddr);
-
-			if (ifp->flags & IFA_F_TENTATIVE) {
-				/* Address is tentative. If the source
-				   is unspecified address, it is someone
-				   does DAD, otherwise we ignore solicitations
-				   until DAD timer expires.
-				 */
-				if (addr_type == IPV6_ADDR_ANY) {
-					if (dev->type == ARPHRD_IEEE802_TR) { 
-						unsigned char *sadr = skb->mac.raw ;
-						if (((sadr[8] &0x7f) != (dev->dev_addr[0] & 0x7f)) ||
-						(sadr[9] != dev->dev_addr[1]) ||
-						(sadr[10] != dev->dev_addr[2]) ||
-						(sadr[11] != dev->dev_addr[3]) ||
-						(sadr[12] != dev->dev_addr[4]) ||
-						(sadr[13] != dev->dev_addr[5])) 
-						{
-							addrconf_dad_failure(ifp) ; 
-						}
-					} else {
-						addrconf_dad_failure(ifp);
-					}
-				} else
-					in6_ifa_put(ifp);
-				return 0;
-			}
-
-			if (addr_type == IPV6_ADDR_ANY) {
-				struct in6_addr maddr;
-
-				ipv6_addr_all_nodes(&maddr);
-				ndisc_send_na(dev, NULL, &maddr, &ifp->addr, 
-					      ifp->idev->cnf.forwarding, 0, 
-					      ipv6_addr_type(&ifp->addr)&IPV6_ADDR_ANYCAST ? 0 : 1, 
-					      1);
-				in6_ifa_put(ifp);
-				return 0;
-			}
-
-			if (addr_type & IPV6_ADDR_UNICAST) {
-				int inc = ipv6_addr_type(daddr)&IPV6_ADDR_MULTICAST;
-
-				if (inc)
-					nd_tbl.stats.rcv_probes_mcast++;
-				else
-					nd_tbl.stats.rcv_probes_ucast++;
-
-				/* 
-				 *	update / create cache entry
-				 *	for the source adddress
-				 */
-
-				neigh = neigh_event_ns(&nd_tbl, lladdr, saddr, skb->dev);
-
-				if (neigh || !dev->hard_header) {
-					ndisc_send_na(dev, neigh, saddr, &ifp->addr, 
-						      ifp->idev->cnf.forwarding, 1, 
-						      ipv6_addr_type(&ifp->addr)&IPV6_ADDR_ANYCAST ? 0 : 1, 
-						      1);
-					if (neigh)
-						neigh_release(neigh);
-				}
-			}
-			in6_ifa_put(ifp);
-		} else {
-			struct inet6_dev *in6_dev = in6_dev_get(dev);
-			int addr_type = ipv6_addr_type(saddr);
-
-			if (in6_dev && in6_dev->cnf.forwarding &&
-			    (addr_type & IPV6_ADDR_UNICAST) &&
-			    pneigh_lookup(&nd_tbl, &msg->target, dev, 0)) {
-				int inc = ipv6_addr_type(daddr)&IPV6_ADDR_MULTICAST;
-
-				if (skb->stamp.tv_sec == 0 ||
-				    skb->pkt_type == PACKET_HOST ||
-				    inc == 0 ||
-				    in6_dev->nd_parms->proxy_delay == 0) {
-					if (inc)
-						nd_tbl.stats.rcv_probes_mcast++;
-					else
-						nd_tbl.stats.rcv_probes_ucast++;
-
-					
-					neigh = neigh_event_ns(&nd_tbl, lladdr, saddr, skb->dev);
-
-					if (neigh) {
-						ndisc_send_na(dev, neigh, saddr, &msg->target,
-							      0, 1, 0, 1);
-						neigh_release(neigh);
-					}
-				} else {
-					struct sk_buff *n = skb_clone(skb, GFP_ATOMIC);
-					if (n)
-						pneigh_enqueue(&nd_tbl, in6_dev->nd_parms, n);
-					in6_dev_put(in6_dev);
-					return 0;
-				}
-			}
-			if (in6_dev)
-				in6_dev_put(in6_dev);
-			
-		}
-		return 0;
-	    }
+		ndisc_recv_ns(skb);
+		break;
 
 	case NDISC_NEIGHBOUR_ADVERTISEMENT:
-	    {
-		struct nd_msg *msg = (struct nd_msg *)skb->h.raw;
-		u8 *lladdr = NULL;
-		int lladdrlen = 0;
-		u32 ndoptlen = skb->tail - msg->opt;
-		struct ndisc_options ndopts;
-
-		if (skb->len < sizeof(struct nd_msg)) {
-			if (net_ratelimit())
-				printk(KERN_WARNING "ICMP NA: packet too short\n");
-			return 0;
-		}
-
-		if (ipv6_addr_type(&msg->target)&IPV6_ADDR_MULTICAST) {
-			if (net_ratelimit())
-				printk(KERN_WARNING "NDISC NA: target address is multicast\n");
-			return 0;
-		}
-
-		if ((ipv6_addr_type(daddr)&IPV6_ADDR_MULTICAST) &&
-		    msg->icmph.icmp6_solicited) {
-			ND_PRINTK0("NDISC: solicited NA is multicasted\n");
-			return 0;
-		}
-		
-		if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
-			if (net_ratelimit())
-				printk(KERN_WARNING "ICMP NS: invalid ND option, ignored.\n");
-			return 0;
-		}
-		if (ndopts.nd_opts_tgt_lladdr) {
-			lladdr = (u8*)(ndopts.nd_opts_tgt_lladdr + 1);
-			lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3;
-			if (lladdrlen != NDISC_OPT_SPACE(skb->dev->addr_len)) {
-				if (net_ratelimit())
-					printk(KERN_WARNING "NDISC NA: invalid lladdr length.\n");
-				return 0;
-			}
-		}
-		if ((ifp = ipv6_get_ifaddr(&msg->target, dev))) {
-			if (ifp->flags & IFA_F_TENTATIVE) {
-				addrconf_dad_failure(ifp);
-				return 0;
-			}
-			/* What should we make now? The advertisement
-			   is invalid, but ndisc specs say nothing
-			   about it. It could be misconfiguration, or
-			   an smart proxy agent tries to help us :-)
-			 */
-			ND_PRINTK0("%s: someone advertises our address!\n",
-				   ifp->idev->dev->name);
-			in6_ifa_put(ifp);
-			return 0;
-		}
-		neigh = neigh_lookup(&nd_tbl, &msg->target, skb->dev);
-
-		if (neigh) {
-			if (neigh->flags & NTF_ROUTER) {
-				if (msg->icmph.icmp6_router == 0) {
-					/*
-					 *	Change: router to host
-					 */
-					struct rt6_info *rt;
-					rt = rt6_get_dflt_router(saddr, skb->dev);
-					if (rt) {
-						ip6_del_rt(rt);
-					}
-				}
-			} else {
-				if (msg->icmph.icmp6_router)
-					neigh->flags |= NTF_ROUTER;
-			}
-
-			neigh_update(neigh, lladdr,
-				     msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE,
-				     msg->icmph.icmp6_override, 1);
-			neigh_release(neigh);
-		}
+		ndisc_recv_na(skb);
 		break;
-	    }
 
 	case NDISC_ROUTER_ADVERTISEMENT:
 		ndisc_router_discovery(skb);

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