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

Next file: linux/net/ipv6/raw.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.67/linux/net/ipv6/ndisc.c linux/net/ipv6/ndisc.c
@@ -6,8 +6,6 @@
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *	Mike Shaver		<shaver@ingenia.com>
  *
- *	$Id: ndisc.c,v 1.15 1997/04/29 09:38:48 mj Exp $
- *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
  *      as published by the Free Software Foundation; either version
@@ -24,7 +22,7 @@
  */
 
 /* Set to 3 to get tracing... */
-#define ND_DEBUG 2
+#define ND_DEBUG 1
 
 #if ND_DEBUG >= 3
 #define NDBG(x) printk x
@@ -396,7 +394,10 @@
 		struct in6_addr *daddr;
 
 		daddr = &skb->nh.ipv6h->daddr;
-		ipv6_mc_map(daddr, h_dest);
+		if (skb->dev->type == ARPHRD_ETHER)
+			ipv6_mc_map(daddr, h_dest);
+		else
+			memcpy(h_dest, skb->dev->broadcast, skb->dev->addr_len);
 		return 0;
 	}
 
@@ -434,6 +435,54 @@
 	return 1;
 }
 
+static int
+ndisc_build_ll_hdr(struct sk_buff *skb, struct device *dev,
+		   struct in6_addr *daddr, struct neighbour *neigh, int len)
+{
+	unsigned char ha[MAX_ADDR_LEN];
+	unsigned char *h_dest = NULL;
+
+	skb->arp = 1;
+	if (dev->hard_header_len) {
+		skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
+
+		if (dev->hard_header) {
+			if (ipv6_addr_type(daddr) & IPV6_ADDR_MULTICAST) {
+				nd_stats.snt_probes_mcast++;
+				if (dev->type == ARPHRD_ETHER)
+					ipv6_mc_map(daddr, ha);
+				else
+					memcpy(ha, dev->broadcast, dev->addr_len);
+				h_dest = ha;
+			} else if (neigh) {
+				h_dest = neigh->ha;
+				nd_stats.snt_probes_ucast++;
+			} else {
+				struct nd_neigh *ndn;
+
+				neigh_table_lock(&nd_tbl);
+
+				neigh = neigh_lookup(&nd_tbl, (void *) daddr,
+						     sizeof(struct in6_addr), dev);
+				if (neigh) {
+					ndn = (struct nd_neigh*)neigh;
+					if (ndn->ndn_flags&NTF_COMPLETE) {
+						memcpy(ha, ndn->ndn_ha, dev->addr_len);
+						h_dest = ha;
+					}
+				}
+				neigh_table_unlock(&nd_tbl);
+			}
+
+			if (dev->hard_header(skb, dev, ETH_P_IPV6, h_dest, NULL, len) < 0)
+				skb->arp = 0;
+		}
+	}
+
+	return skb->arp;
+}
+
+
 /*
  *	Send a Neighbour Advertisement
  */
@@ -486,17 +535,10 @@
 		printk(KERN_DEBUG "send_na: alloc skb failed\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;
-		}
+
+	if (ndisc_build_ll_hdr(skb, dev, daddr, (struct neighbour*)ndn, len) == 0) {
+		kfree_skb(skb, FREE_WRITE);
+		return;
 	}
 
 	ip6_nd_hdr(sk, skb, dev, solicited_addr, daddr, IPPROTO_ICMPV6, len);
@@ -540,12 +582,10 @@
 		   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;	
-	void *h_dest;
 	int err;
 
 	NDBG(("ndisc_send_ns(%s,%p): ", (dev ? dev->name : "[NULL]"), neigh));
@@ -581,7 +621,11 @@
 		return;
 	}
 
+#if 0
+	/* Why Pedro did it? Is it remnant of early
+	   attempts to avoid looping back? I have no idea. --ANK */
 	skb->pkt_type = PACKET_NDISC;
+#endif
 
 	if (saddr == NULL) {
 		struct inet6_ifaddr *ifa;
@@ -593,29 +637,9 @@
 			saddr = &ifa->addr;
 	}
 
-	if ((ipv6_addr_type(daddr) & IPV6_ADDR_MULTICAST)) {
-		nd_stats.snt_probes_mcast++;
-		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 (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;
-		}
+	if (ndisc_build_ll_hdr(skb, dev, daddr, neigh, len) == 0) {
+		kfree_skb(skb, FREE_WRITE);
+		return;
 	}
 
 	ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
@@ -684,15 +708,9 @@
 		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;
-		}
+	if (ndisc_build_ll_hdr(skb, dev, daddr, NULL, len) == 0) {
+		kfree_skb(skb, FREE_WRITE);
+		return;
 	}
 
 	ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
@@ -783,15 +801,19 @@
 					ntimer = min(ntimer, time);
 			}
 			ndn = (struct nd_neigh *) ndn->neigh.next;
-
 		} while (ndn != head);
 	}
 
 	if (ntimer != (~0UL)) {
-		ndisc_timer.expires = now + ntimer;
+		unsigned long tval = jiffies + ntimer;
+		if (del_timer(&ndisc_timer)) {
+			if (ndisc_timer.expires - tval < 0)
+				tval = ndisc_timer.expires;
+		}
+		ndisc_timer.expires = tval;
 		add_timer(&ndisc_timer);
 	}
-	
+
 	neigh_table_unlock(&nd_tbl);
 }
 
@@ -1238,14 +1260,12 @@
 	NDBG(("ndisc_redirect_rcv(%p)\n", skb));
 
 	if (skb->nh.ipv6h->hop_limit != 255) {
-		printk(KERN_WARNING
-		       "NDISC: fake ICMP redirect received\n");
+		printk(KERN_WARNING "NDISC: fake ICMP redirect received\n");
 		return;
 	}
 
 	if (!(ipv6_addr_type(&skb->nh.ipv6h->saddr) & IPV6_ADDR_LINKLOCAL)) {
-		printk(KERN_WARNING
-		       "ICMP redirect: source address is not linklocal\n");
+		printk(KERN_WARNING "ICMP redirect: source address is not linklocal\n");
 		return;
 	}
 
@@ -1269,19 +1289,15 @@
 	if (ipv6_addr_cmp(dest, target) == 0) {
 		on_link = 1;
 	} else if (!(ipv6_addr_type(target) & IPV6_ADDR_LINKLOCAL)) {
-		printk(KERN_WARNING
-		       "ICMP redirect: target address is not linklocal\n");
+		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 = rt6_redirect(dest, &skb->nh.ipv6h->saddr, target, skb->dev, on_link);
 
-	if (rt == NULL) {
-		printk(KERN_WARNING "ICMP redirect: no route to host\n");
+	if (rt == NULL)
 		return;
-	}
 
 	ndn = (struct nd_neigh *) rt->rt6i_nexthop;
 
@@ -1365,13 +1381,9 @@
 	
 	hlen = 0;
 
-	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;
-		}
+	if (ndisc_build_ll_hdr(buff, dev, &skb->nh.ipv6h->saddr, NULL, len) == 0) {
+		kfree_skb(buff, FREE_WRITE);
+		return;
 	}
 	
 	ip6_nd_hdr(sk, buff, dev, &ifp->addr, &skb->nh.ipv6h->saddr,
@@ -1471,25 +1483,32 @@
 	switch (msg->icmph.icmp6_type) {
 	case NDISC_NEIGHBOUR_SOLICITATION:
 		NDBG(("NS "));
-		if ((ifp = ipv6_chk_addr(&msg->target))) {
-			int addr_type;
+		if ((ifp = ipv6_chk_addr(&msg->target)) != NULL) {
+			int addr_type = ipv6_addr_type(saddr);
 
 			if (ifp->flags & DAD_INCOMPLETE) {
-				/*
-				 *	DAD failed
+				/* 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) {
+					printk(KERN_INFO "%s: duplicate address detected!\n",
+					       ifp->idev->dev->name);
+					del_timer(&ifp->timer);
+				}
+				return 0;
+			}
 
-				/* XXX Check if this came in over same interface
-				 * XXX we just sent an NS from!  That is valid! -DaveM
-				 */
+			if (addr_type == IPV6_ADDR_ANY) {
+				struct in6_addr maddr;
 
-				printk(KERN_DEBUG "%s: duplicate address\n",
-				       ifp->idev->dev->name);
-				del_timer(&ifp->timer);
+				ipv6_addr_all_nodes(&maddr);
+				ndisc_send_na(dev, NULL, &maddr, &ifp->addr, 
+					      ifp->idev->router, 0, 1, 1);
 				return 0;
 			}
 
-			addr_type = ipv6_addr_type(saddr);
 			if (addr_type & IPV6_ADDR_UNICAST) {
 				int inc;
 
@@ -1512,7 +1531,6 @@
 					      ifp->idev->router, 1, inc, inc);
 			} else {
 #if ND_DEBUG >= 1
-				/* FIXME */
 				printk(KERN_DEBUG "ns: non unicast saddr\n");
 #endif
 			}
@@ -1521,6 +1539,28 @@
 
 	case NDISC_NEIGHBOUR_ADVERTISEMENT:
 		NDBG(("NA "));
+		if ((ipv6_addr_type(saddr)&IPV6_ADDR_MULTICAST) &&
+		    msg->icmph.icmp6_solicited) {
+			printk(KERN_DEBUG "NDISC: solicited NA is multicasted\n");
+			return 0;
+		}
+		if ((ifp = ipv6_chk_addr(&msg->target))) {
+			if (ifp->flags & DAD_INCOMPLETE) {
+				/* Address is duplicate. */
+				printk(KERN_INFO "%s: duplicate address detected!\n",
+				       ifp->idev->dev->name);
+				del_timer(&ifp->timer);
+				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 :-)
+			 */
+			printk(KERN_DEBUG "%s: someone avertise our address!\n",
+			       ifp->idev->dev->name);
+			return 0;
+		}
 		neigh_table_lock(&nd_tbl);	       
 		ndn = (struct nd_neigh *) 
 			neigh_lookup(&nd_tbl, (void *) &msg->target,

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