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

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

diff -u --recursive --new-file v2.1.67/linux/net/ipv6/route.c linux/net/ipv6/route.c
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: route.c,v 1.13 1997/07/19 11:11:35 davem Exp $
+ *	$Id: route.c,v 1.18 1997/10/17 00:15:05 freitag Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -23,6 +23,8 @@
 #include <linux/netdevice.h>
 #include <linux/in6.h>
 #include <linux/init.h>
+#include <linux/netlink.h>
+#include <linux/if_arp.h>
 
 #ifdef 	CONFIG_PROC_FS
 #include <linux/proc_fs.h>
@@ -34,7 +36,7 @@
 #include <net/ip6_route.h>
 #include <net/ndisc.h>
 #include <net/addrconf.h>
-#include <net/netlink.h>
+#include <linux/netlink.h>
 
 #include <asm/uaccess.h>
 
@@ -64,7 +66,7 @@
 
 struct rt6_info ip6_null_entry = {
 	{{NULL, ATOMIC_INIT(0), ATOMIC_INIT(0), NULL,
-	  0, 0, 0, 0, 0, 0, 0, 0, -ENETUNREACH, NULL, NULL,
+	  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -ENETUNREACH, NULL, NULL,
 	  ip6_pkt_discard, ip6_pkt_discard, &ip6_dst_ops}},
 	NULL, {{{0}}}, 256, RTF_REJECT|RTF_NONEXTHOP, ~0UL,
 	0, {NULL}, {{{{0}}}, 128}, {{{{0}}}, 128}
@@ -297,7 +299,7 @@
 	rt6_lock();
 	fn = fib6_lookup(&ip6_routing_table, daddr, saddr);
 
-	rt = rt6_device_match(fn->leaf, dev, 0);
+	rt = rt6_device_match(fn->leaf, dev, flags&RTF_LINKRT);
 	rt6_unlock();
 	return rt;
 }
@@ -314,6 +316,9 @@
 	if (rt) {
 		ipv6_addr_copy(&rt->rt6i_dst.addr, daddr);
 
+		if (!(rt->rt6i_flags&RTF_GATEWAY))
+			ipv6_addr_copy(&rt->rt6i_gateway, daddr);
+
 		rt->rt6i_dst.plen = 128;
 		rt->rt6i_flags |= RTF_CACHE;
 
@@ -322,7 +327,7 @@
 			rt->rt6i_src.plen = 128;
 		}
 
-		rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, daddr);
+		rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway);
 
 		rtreq_add(rt, RT_OPER_ADD);
 	} else {
@@ -556,6 +561,23 @@
 	return NULL;
 }
 
+/* Clean host part of a prefix. Not necessary in radix tree,
+   but results in cleaner routing tables.
+
+   Remove it only when all the things will work!
+ */
+
+static void ipv6_wash_prefix(struct in6_addr *pfx, int plen)
+{
+	int b = plen&0x7;
+	int o = (plen + 7)>>3;
+
+	if (o < 16)
+		memset(pfx->s6_addr + o, 0, 16 - o);
+	if (b != 0)
+		pfx->s6_addr[plen>>3] &= (0xFF<<(8-b));
+}
+
 /*
  *
  */
@@ -566,7 +588,11 @@
 	struct device *dev = NULL;
 	int addr_type;
 	
-	RDBG(("ip6_route_add(%p)[%p] ", rtmsg, __builtin_return_address(0)));
+	if (rtmsg->rtmsg_dst_len > 128 || rtmsg->rtmsg_src_len > 128) {
+		*err = -EINVAL;
+		return NULL;
+	}
+
 	*err = 0;
 	
 	rt = dst_alloc(sizeof(struct rt6_info), &ip6_dst_ops);
@@ -577,29 +603,6 @@
 		goto out;
 	}
 
-	/*
-	 *	default... this should be chosen according to route flags
-	 */
-
-#if RT6_DEBUG >= 3
-	{
-		struct in6_addr *addr = &rtmsg->rtmsg_dst;
-		int i;
-
-		RDBG(("daddr["));
-		for(i = 0; i < 8; i++) {
-			RDBG(("%04x%c", addr->s6_addr16[i],
-			      i == 7 ? ']' : ':'));
-		}
-		addr = &rtmsg->rtmsg_src;
-		RDBG(("saddr["));
-		for(i = 0; i < 8; i++) {
-			RDBG(("%04x%c", addr->s6_addr16[i],
-			      i == 7 ? ']' : ':'));
-		}
-	}
-#endif
-
 	addr_type = ipv6_addr_type(&rtmsg->rtmsg_dst);
 	
 	if (addr_type & IPV6_ADDR_MULTICAST) {
@@ -609,71 +612,58 @@
 		RDBG(("!MCAST "));
 		rt->u.dst.input = ip6_forward;
 	}
-	
+
 	rt->u.dst.output = dev_queue_xmit;
-	
-	if (rtmsg->rtmsg_ifindex)
+
+	if (rtmsg->rtmsg_ifindex) {
 		dev = dev_get_by_index(rtmsg->rtmsg_ifindex);
-	if(dev)
-		RDBG(("d[%s] ", dev->name));
+		if (dev == NULL) {
+			*err = -ENODEV;
+			goto out;
+		}
+	}
 
 	ipv6_addr_copy(&rt->rt6i_dst.addr, &rtmsg->rtmsg_dst);
 	rt->rt6i_dst.plen = rtmsg->rtmsg_dst_len;
+	ipv6_wash_prefix(&rt->rt6i_dst.addr, rt->rt6i_dst.plen);
 
-	/* XXX Figure out what really is supposed to be happening here -DaveM */
 	ipv6_addr_copy(&rt->rt6i_src.addr, &rtmsg->rtmsg_src);
 	rt->rt6i_src.plen = rtmsg->rtmsg_src_len;
-	
-	if ((rt->rt6i_src.plen = rtmsg->rtmsg_src_len)) {
-		RDBG(("splen, "));
-		ipv6_addr_copy(&rt->rt6i_src.addr, &rtmsg->rtmsg_src);
-	} else {
-		RDBG(("!splen, "));
-	}
-	/* XXX */
+	ipv6_wash_prefix(&rt->rt6i_src.addr, rt->rt6i_src.plen);
 
-	if (rtmsg->rtmsg_flags & (RTF_GATEWAY | RTF_NONEXTHOP)) {
-		struct rt6_info *grt;
+	if (rtmsg->rtmsg_flags & RTF_GATEWAY) {
 		struct in6_addr *gw_addr;
-		u32 flags = 0;
-
-		RDBG(("RTF_GATEWAY, "));
-		/*
-		 *	1. gateway route lookup
-		 *	2. ndisc_get_neigh
-		 */
+		int gwa_type;
 
 		gw_addr = &rtmsg->rtmsg_gateway;
+		ipv6_addr_copy(&rt->rt6i_gateway, &rtmsg->rtmsg_gateway);
+		gwa_type = ipv6_addr_type(gw_addr);
 
-#if RT6_DEBUG >= 3
-		{
-			struct in6_addr *addr = gw_addr;
-			int i;
-
-			RDBG(("gwaddr["));
-			for(i = 0; i < 8; i++) {
-				RDBG(("%04x%c", addr->s6_addr16[i],
-				      i == 7 ? ']' : ':'));
+		if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) {
+			struct rt6_info *grt;
+
+			/* IPv6 strictly inhibits using not link-local
+			   addresses as nexthop address.
+			   It is very good, but in some (rare!) curcumstances
+			   (SIT, NBMA NOARP links) it is handy to allow
+			   some exceptions.
+			 */
+			if (!(gwa_type&IPV6_ADDR_UNICAST)) {
+				*err = -EINVAL;
+				goto out;
 			}
-		}
-#endif
 
-		if ((rtmsg->rtmsg_flags & RTF_GATEWAY) &&
-		    (rtmsg->rtmsg_flags & RTF_ADDRCONF) == 0) {
-			RDBG(("RTF_GATEWAY && !RTF_ADDRCONF, "));
-			if (dev)
-				flags |= RTF_LINKRT;
-
-			grt = rt6_lookup(gw_addr, NULL, dev, flags);
-
-			if (grt == NULL)
-			{
-				RDBG(("!grt, "));
+			grt = rt6_lookup(gw_addr, NULL, dev, RTF_LINKRT);
+
+			if (grt == NULL || (grt->rt6i_flags&RTF_GATEWAY)) {
 				*err = -EHOSTUNREACH;
 				goto out;
 			}
 			dev = grt->rt6i_dev;
-			RDBG(("grt(d=%s), ", dev ? dev->name : "NULL"));
+		}
+		if (dev == NULL) {
+			*err = -EINVAL;
+			goto out;
 		}
 
 		rt->rt6i_nexthop = ndisc_get_neigh(dev, gw_addr);
@@ -739,20 +729,26 @@
 	/*
 	 *	Find device
 	 */
-	if(rtmsg->rtmsg_ifindex)
+	if(rtmsg->rtmsg_ifindex) {
 		dev=dev_get_by_index(rtmsg->rtmsg_ifindex);
+		if (dev == NULL)
+			return -ENODEV;
+	}
 	/*
 	 *	Find route
 	 */
-	rt=rt6_lookup(&rtmsg->rtmsg_dst, &rtmsg->rtmsg_src, dev, rtmsg->rtmsg_flags);
-	
+	rt=rt6_lookup(&rtmsg->rtmsg_dst, &rtmsg->rtmsg_src, dev, dev ? RTF_LINKRT : 0);
+
 	/*
 	 *	Blow it away
 	 */
-	if(rt)
+	if(rt && rt->rt6i_dst.plen == rtmsg->rtmsg_dst_len &&
+	   rt->rt6i_src.plen == rtmsg->rtmsg_src_len) {
 		ip6_del_rt(rt);
+		return 0;
+	}
 
-	return 0;
+	return -ESRCH;
 }
 
 
@@ -777,6 +773,7 @@
 	rt6_bh_mask = 0;
 }
 
+#ifdef CONFIG_NETLINK
 /*
  *	NETLINK interface
  *	routing socket moral equivalent
@@ -815,6 +812,7 @@
 	kfree_skb(skb, FREE_READ);	
 	return count;
 }
+#endif /* CONFIG_NETLINK */
 
 static void rt6_sndrtmsg(struct in6_rtmsg *rtmsg)
 {
@@ -827,7 +825,9 @@
 	memcpy(skb_put(skb, sizeof(struct in6_rtmsg)), &rtmsg,
 	       sizeof(struct in6_rtmsg));
 	
+#ifdef CONFIG_NETLINK
 	if (netlink_post(NETLINK_ROUTE6, skb))
+#endif
 		kfree_skb(skb, FREE_WRITE);
 }
 
@@ -867,7 +867,9 @@
 
 	msg->rtmsg_flags = flags;
 
+#ifdef CONFIG_NETLINK
 	if (netlink_post(NETLINK_ROUTE6, skb))
+#endif
 		kfree_skb(skb, FREE_WRITE);
 }
 
@@ -878,54 +880,28 @@
 			      struct in6_addr *target, struct device *dev,
 			      int on_link)
 {
-	struct rt6_info *rt, *tgtr, *nrt;
+	struct rt6_info *rt, *nrt;
 
-	RDBG(("rt6_redirect(%s)[%p]: ",
-	      dev ? dev->name : "NULL",
-	      __builtin_return_address(0)));
+	/* Locate old route to this destination. */
 	rt = rt6_lookup(dest, NULL, dev, 0);
 
-	if (rt == NULL || rt->u.dst.error) {
-		RDBG(("!rt\n"));
-		printk(KERN_DEBUG "rt6_redirect: no route to destination\n");
+	if (rt == NULL || rt->u.dst.error)
 		return NULL;
-	}
 
-	if (rt->rt6i_flags & RTF_GATEWAY) {
-		/*
-		 *	This can happen due to misconfiguration
-		 *	if we are dealing with an "on link" redirect.
-		 */
-		RDBG(("RTF_GATEWAY\n"));
-		printk(KERN_DEBUG "rt6_redirect: destination not directly "
-		       "connected\n");
-		return NULL;
-	}
-	RDBG(("tgt_lkup, "));
-	tgtr = rt6_lookup(target, NULL, dev, 0);
-
-	if (tgtr == NULL || tgtr->u.dst.error) {
-		/*
-		 *	duh?! no route to redirect target.
-		 *	How where we talking to it in the first place ?
-		 */
-		RDBG(("!tgtr||dsterr\n"));
-		printk(KERN_DEBUG "rt6_redirect: no route to target\n");
+	/* Duplicate redirect: silently ignore. */
+	if (ipv6_addr_cmp(target, &rt->rt6i_gateway) == 0)
 		return NULL;
-	}
 
-	if ((tgtr->rt6i_flags & RTF_GATEWAY) &&
-	    ipv6_addr_cmp(dest, &tgtr->rt6i_gateway) == 0) {
-		RDBG(("tgt RTF_GATEWAY && dstmatch, dup\n"));
-		/*
-		 *	Check if we already have the right route.
-		 */
-#if RT6_DEBUG >= 1
-		printk(KERN_DEBUG "rt6_redirect: duplicate\n");
-#endif
+	/* Current route is on-link; redirect is always invalid. */
+	if (!(rt->rt6i_flags&RTF_GATEWAY))
 		return NULL;
-	}
 
+#if !defined(CONFIG_IPV6_EUI64) || defined(CONFIG_IPV6_NO_PB)
+	/*
+	 *	During transition gateways have more than
+	 *	one link local address. Certainly, it is violation
+	 *	of basic principles, but it is temparary.
+	 */
 	/*
 	 *	RFC 1970 specifies that redirects should only be
 	 *	accepted if they come from the nexthop to the target.
@@ -934,62 +910,57 @@
 	 *	routers.
 	 */
 
-	if (ipv6_addr_cmp(saddr, &tgtr->rt6i_gateway)) {
-		RDBG(("saddr/tgt->gway match, "));
-		if (tgtr->rt6i_flags & RTF_DEFAULT) {
-			tgtr = ip6_routing_table.leaf;
-
-			for (; tgtr; tgtr = tgtr->u.next) {
-				if (!ipv6_addr_cmp(saddr, &tgtr->rt6i_gateway)) {
-					RDBG(("found srcok, "));
+	if (ipv6_addr_cmp(saddr, &rt->rt6i_gateway)) {
+		if (rt->rt6i_flags & RTF_DEFAULT) {
+			rt = ip6_routing_table.leaf;
+
+			for (; rt; rt = rt->u.next) {
+				if (!ipv6_addr_cmp(saddr, &rt->rt6i_gateway))
 					goto source_ok;
-				}
 			}
 		}
-		RDBG(("!dflt||!srcok, "));
 		printk(KERN_DEBUG "rt6_redirect: source isn't a valid nexthop "
-		       "for redirect target\n");
+			       "for redirect target\n");
+		return NULL;
 	}
 
 source_ok:
+#endif
 
 	/*
 	 *	We have finally decided to accept it.
 	 */
-	RDBG(("srcok: "));
-	if ((tgtr->rt6i_flags & RTF_HOST)) {
+	if (rt->rt6i_dst.plen == 128) {
 		/*
 		 *	Already a host route.
 		 *
 		 */
-		RDBG(("hralready, "));
-		if (tgtr->rt6i_nexthop) {
-			RDBG(("nrel(nxthop) "));
-			neigh_release(tgtr->rt6i_nexthop);
-		}
+		if (rt->rt6i_nexthop)
+			neigh_release(rt->rt6i_nexthop);
 		/*
 		 *	purge hh_cache
 		 */
-		tgtr->rt6i_flags |= RTF_MODIFIED | RTF_CACHE;
-		ipv6_addr_copy(&tgtr->rt6i_gateway, dest);
-		tgtr->rt6i_nexthop = ndisc_get_neigh(tgtr->rt6i_dev, dest);
-		RDBG(("hhpurge, getnewneigh, ret(%p)\n", tgtr));
-		return tgtr;
+		rt->rt6i_flags |= RTF_MODIFIED | RTF_CACHE;
+		if (on_link)
+			rt->rt6i_flags &= ~RTF_GATEWAY;
+		ipv6_addr_copy(&rt->rt6i_gateway, target);
+		rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, target);
+		return rt;
 	}
 
-	nrt = ip6_rt_copy(tgtr);
-	nrt->rt6i_flags = RTF_GATEWAY|RTF_HOST|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
+	nrt = ip6_rt_copy(rt);
+	nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
+	if (on_link)
+		nrt->rt6i_flags &= ~RTF_GATEWAY;
 
-	ipv6_addr_copy(&nrt->rt6i_dst.addr, target);
+	ipv6_addr_copy(&nrt->rt6i_dst.addr, dest);
 	nrt->rt6i_dst.plen = 128;
 
-	ipv6_addr_copy(&nrt->rt6i_gateway, dest);
-	nrt->rt6i_nexthop = ndisc_get_neigh(nrt->rt6i_dev, dest);
+	ipv6_addr_copy(&nrt->rt6i_gateway, target);
+	nrt->rt6i_nexthop = ndisc_get_neigh(nrt->rt6i_dev, target);
 	nrt->rt6i_dev = dev;
 	nrt->u.dst.pmtu = dev->mtu;
 
-	RDBG(("rt6_ins(%p)\n", nrt));
-
 	rt6_lock();
 	rt6_ins(nrt);
 	rt6_unlock();
@@ -1023,7 +994,15 @@
 		return;
 	}
 
-	if (rt->rt6i_flags & RTF_HOST) {
+	/* It is wrong, but I plugged the hole here.
+	   On-link routes are cloned differently,
+	   look at rt6_redirect --ANK
+	 */
+	if (!(rt->rt6i_flags&RTF_GATEWAY)) {
+		return;
+	}
+
+	if (rt->rt6i_dst.plen == 128) {
 		/*
 		 *	host route
 		 */
@@ -1037,7 +1016,7 @@
 	ipv6_addr_copy(&rt->rt6i_dst.addr, addr);
 	rt->rt6i_dst.plen = 128;
 
-	rt->rt6i_flags |= (RTF_HOST | RTF_DYNAMIC | RTF_CACHE);
+	rt->rt6i_flags |= (RTF_DYNAMIC | RTF_CACHE);
 
 	rt6_lock();
 	rt6_ins(rt);
@@ -1065,7 +1044,7 @@
 		rt->rt6i_keylen = ort->rt6i_keylen;
 		rt->rt6i_flags = ort->rt6i_flags;
 		rt->rt6i_metric = ort->rt6i_metric;
-		
+
 		memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
 		memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
 	}
@@ -1257,7 +1236,7 @@
 	rt->rt6i_dev = dev_get("lo");
 	rt->u.dst.pmtu = rt->rt6i_dev->mtu;
 
-	rt->rt6i_flags = RTF_HOST | RTF_LOCAL | RTF_UP | RTF_NONEXTHOP;
+	rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
 	
 	ipv6_addr_copy(&rt->rt6i_dst.addr, addr);
 	rt->rt6i_dst.plen = 128;
@@ -1600,7 +1579,9 @@
 	proc_net_register(&proc_rt6_stats);
 	proc_net_register(&proc_rt6_tree);
 #endif
+#ifdef CONFIG_NETLINK
 	netlink_attach(NETLINK_ROUTE6, rt6_msgrcv);
+#endif
 }
 
 #ifdef MODULE
@@ -1611,7 +1592,9 @@
 	proc_net_unregister(PROC_NET_RT6_TREE);
 	proc_net_unregister(PROC_NET_RT6_STATS);
 #endif
+#ifdef CONFIG_NETLINK
 	netlink_detach(NETLINK_ROUTE6);
+#endif
 #if 0
 	fib6_flush();
 #endif

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