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

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

diff -u --recursive --new-file v2.1.78/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.18 1997/10/17 00:15:05 freitag Exp $
+ *	$Id: route.c,v 1.19 1997/12/13 21:53:16 kuznet Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -37,6 +37,7 @@
 #include <net/ndisc.h>
 #include <net/addrconf.h>
 #include <linux/netlink.h>
+#include <linux/rtnetlink.h>
 
 #include <asm/uaccess.h>
 
@@ -51,24 +52,29 @@
 #define RDBG(x)
 #endif
 
+static struct rt6_info * ip6_rt_copy(struct rt6_info *ort);
 static struct dst_entry	*ip6_dst_check(struct dst_entry *dst, u32 cookie);
 static struct dst_entry	*ip6_dst_reroute(struct dst_entry *dst,
 					 struct sk_buff *skb);
+static struct dst_entry *ip6_negative_advice(struct dst_entry *);
 
 static int		ip6_pkt_discard(struct sk_buff *skb);
 
 struct dst_ops ip6_dst_ops = {
 	AF_INET6,
+	__constant_htons(ETH_P_IPV6),
 	ip6_dst_check,
 	ip6_dst_reroute,
-	NULL
+	NULL,
+	ip6_negative_advice
 };
 
 struct rt6_info ip6_null_entry = {
 	{{NULL, ATOMIC_INIT(0), ATOMIC_INIT(0), NULL,
-	  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -ENETUNREACH, NULL, NULL,
+	  -1, 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,
+	NULL, {{{0}}}, 256, RTF_REJECT|RTF_NONEXTHOP, ~0U,
 	0, {NULL}, {{{{0}}}, 128}, {{{{0}}}, 128}
 };
 
@@ -220,14 +226,14 @@
 
 	RDBG(("rt6_best_dflt(%p,%p): ", rt, dev));
 	for (sprt = rt; sprt; sprt = sprt->u.next) {
-		struct nd_neigh *ndn;
+		struct neighbour *neigh;
 
 		RDBG(("sprt(%p): ", sprt));
-		if ((ndn = (struct nd_neigh *) sprt->rt6i_nexthop)) {
+		if ((neigh = sprt->rt6i_nexthop)) {
 			int m = -1;
 
-			RDBG(("nxthop(%p,%d) ", ndn, ndn->ndn_nud_state));
-			switch (ndn->ndn_nud_state) {
+			RDBG(("nxthop(%p,%d) ", neigh, neigh->nud_state));
+			switch (neigh->nud_state) {
 			case NUD_REACHABLE:
 				RDBG(("NUD_REACHABLE "));
 				if (sprt != rt6_dflt_pointer) {
@@ -304,14 +310,16 @@
 	return rt;
 }
 
-static struct rt6_info *rt6_cow(struct rt6_info *rt, struct in6_addr *daddr,
+static struct rt6_info *rt6_cow(struct rt6_info *ort, struct in6_addr *daddr,
 				struct in6_addr *saddr)
 {
+	struct rt6_info *rt;
+
 	/*
 	 *	Clone the route.
 	 */
 
-	rt = ip6_rt_copy(rt);
+	rt = ip6_rt_copy(ort);
 
 	if (rt) {
 		ipv6_addr_copy(&rt->rt6i_dst.addr, daddr);
@@ -432,7 +440,7 @@
 
 	RDBG(("ip6_route_output(%p,%p) from(%p)", sk, fl,
 	      __builtin_return_address(0)));
-	strict = ipv6_addr_type(fl->nl_u.ip6_u.daddr) & IPV6_ADDR_MULTICAST;
+	strict = ipv6_addr_type(fl->nl_u.ip6_u.daddr) & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL);
 
 	rt6_lock();
 #if RT6_DEBUG >= 3
@@ -461,12 +469,28 @@
 
 	RDBG(("-->(%p[%s])) ", fn, fn == &ip6_routing_table ? "ROOT" : "!ROOT"));
 
+restart:
 	rt = fn->leaf;
 
 	if ((rt->rt6i_flags & RTF_CACHE)) {
 		RDBG(("RTF_CACHE "));
 		if (ip6_rt_policy == 0) {
 			rt = rt6_device_match(rt, fl->dev, strict);
+
+			/* BUGGGG! It is capital bug, that was hidden
+			   by not-cloning multicast routes. However,
+			   the same problem was with link-local addresses.
+			   Fix is the following if-statement,
+			   but it will not properly handle Pedro's subtrees --ANK
+			 */
+			if (rt == &ip6_null_entry && strict) {
+				while ((fn = fn->parent) != NULL) {
+					if (fn->fn_flags & RTN_ROOT)
+						goto out;
+					if (fn->fn_flags & RTN_RTINFO)
+						goto restart;
+				}
+			}
 			RDBG(("devmatch(%p) ", rt));
 			goto out;
 		}
@@ -517,7 +541,7 @@
 }
 
 
-void rt6_ins(struct rt6_info *rt)
+static void rt6_ins(struct rt6_info *rt)
 {
 	start_bh_atomic();
 	if (atomic_read(&rt6_tbl_lock) == 1)
@@ -529,29 +553,33 @@
 
 /*
  *	Destination cache support functions
+ *
+ *	BUGGG! This function is absolutely wrong.
+ *	First of all it is never called. (look at include/net/dst.h)
+ *	Second, even when it is called rt->rt6i_node == NULL
+ *	  ** partially fixed: now dst->obsolete = -1 for IPv6 not cache routes.
+ *	Third, even we fixed previous bugs,
+ *	it will not work because sernum is incorrectly checked/updated and
+ *	it does not handle change of the parent of cloned route.
+ *	Purging stray clones is not easy task, it would require
+ *	massive remake of ip6_fib.c. Alas...
+ *							--ANK
  */
 
-struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
+static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
 {
 	struct rt6_info *rt;
 
-	RDBG(("ip6dstchk(%p,%08x)[%p]\n", dst, cookie,
-	      __builtin_return_address(0)));
-
 	rt = (struct rt6_info *) dst;
 
-	if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie)) {
-		if (rt->rt6i_nexthop)
-			ndisc_event_send(rt->rt6i_nexthop, NULL);
-
+	if (rt && rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie))
 		return dst;
-	}
 
 	dst_release(dst);
 	return NULL;
 }
 
-struct dst_entry *ip6_dst_reroute(struct dst_entry *dst, struct sk_buff *skb)
+static struct dst_entry *ip6_dst_reroute(struct dst_entry *dst, struct sk_buff *skb)
 {
 	/*
 	 *	FIXME
@@ -561,6 +589,13 @@
 	return NULL;
 }
 
+static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
+{
+	dst_release(dst);
+	return NULL;
+}
+
+
 /* Clean host part of a prefix. Not necessary in radix tree,
    but results in cleaner routing tables.
 
@@ -592,6 +627,8 @@
 		*err = -EINVAL;
 		return NULL;
 	}
+	if (rtmsg->rtmsg_metric == 0)
+		rtmsg->rtmsg_metric = IP6_RT_PRIO_USER;
 
 	*err = 0;
 	
@@ -603,6 +640,9 @@
 		goto out;
 	}
 
+	rt->u.dst.obsolete = -1;
+	rt->rt6i_expires = rtmsg->rtmsg_info;
+
 	addr_type = ipv6_addr_type(&rtmsg->rtmsg_dst);
 	
 	if (addr_type & IPV6_ADDR_MULTICAST) {
@@ -613,7 +653,7 @@
 		rt->u.dst.input = ip6_forward;
 	}
 
-	rt->u.dst.output = dev_queue_xmit;
+	rt->u.dst.output = ip6_output;
 
 	if (rtmsg->rtmsg_ifindex) {
 		dev = dev_get_by_index(rtmsg->rtmsg_ifindex);
@@ -665,9 +705,16 @@
 			*err = -EINVAL;
 			goto out;
 		}
+	}
 
-		rt->rt6i_nexthop = ndisc_get_neigh(dev, gw_addr);
+	if (dev == NULL) {
+		RDBG(("!dev, "));
+		*err = -ENODEV;
+		goto out;
+	}
 
+	if (rtmsg->rtmsg_flags & (RTF_GATEWAY|RTF_NONEXTHOP)) {
+		rt->rt6i_nexthop = ndisc_get_neigh(dev, &rt->rt6i_gateway);
 		if (rt->rt6i_nexthop == NULL) {
 			RDBG(("!nxthop, "));
 			*err = -ENOMEM;
@@ -676,12 +723,6 @@
 		RDBG(("nxthop, "));
 	}
 
-	if (dev == NULL) {
-		RDBG(("!dev, "));
-		*err = -ENODEV;
-		goto out;
-	}
-
 	rt->rt6i_metric = rtmsg->rtmsg_metric;
 
 	rt->rt6i_dev = dev;
@@ -694,6 +735,29 @@
 	rt6_ins(rt);
 	rt6_unlock();
 
+	/* BUGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG!
+
+	   If rt6_ins will fail (and it occurs regularly f.e. if route
+	   already existed), the route will be freed -> Finita.
+	   Crash. No recovery. NO FIX. Unfortunately, it is not the only
+	   place will it is fatal. It is sad, I believed this
+	   code is a bit more accurate :-(
+
+	   Really, the problem can be solved in two ways:
+
+	   * As I did in old 2.0 IPv4: to increase use count and force
+	     user to destroy stray route. It requires some care,
+	     well, much more care.
+	   * Second and the best: to get rid of this damn backlogging
+	     system. I wonder why Pedro so liked it. It was the most
+	     unhappy day when I invented it (well, by a strange reason
+	     I believed that it is very clever :-)),
+	     and when I managed to clean IPv4 of this crap,
+	     it was really great win.
+	     BTW I forgot how 2.0 route/arp works :-) :-)
+	                                                               --ANK
+	 */
+
 out:
 	if (*err) {
 		RDBG(("dfree(%p) ", rt));
@@ -701,7 +765,17 @@
 		rt = NULL;
 	}
 	RDBG(("ret(%p)\n", rt));
+#if 0
 	return rt;
+#else
+	/* BUGGG! For now always return NULL. (see above)
+
+	   Really, it was used only in two places, and one of them
+	   (rt6_add_dflt_router) is repaired, ip6_fw is not essential
+	   at all. --ANK
+	 */
+	return NULL;
+#endif
 }
 
 int ip6_del_rt(struct rt6_info *rt)
@@ -710,6 +784,12 @@
 
 	start_bh_atomic();
 
+	/* I'd add here couple of cli()
+	   cli(); cli(); cli();
+
+	   Now it is really LOCKED. :-) :-) --ANK
+	 */
+
 	rt6_dflt_pointer = NULL;
 
 	if (atomic_read(&rt6_tbl_lock) == 1)
@@ -737,15 +817,34 @@
 	/*
 	 *	Find route
 	 */
-	rt=rt6_lookup(&rtmsg->rtmsg_dst, &rtmsg->rtmsg_src, dev, dev ? RTF_LINKRT : 0);
+	rt=rt6_lookup(&rtmsg->rtmsg_dst, &rtmsg->rtmsg_src, dev, RTF_LINKRT);
 
 	/*
 	 *	Blow it away
+	 *
+	 *	BUGGGG It will not help with Pedro's subtrees.
+	 *	We urgently need fib6_locate_node function, and
+	 *	it is not the only place where rt6_lookup is used
+	 *	for wrong purpose.
+	 *							--ANK
 	 */
-	if(rt && rt->rt6i_dst.plen == rtmsg->rtmsg_dst_len &&
-	   rt->rt6i_src.plen == rtmsg->rtmsg_src_len) {
-		ip6_del_rt(rt);
-		return 0;
+restart:
+	if (rt && rt->rt6i_src.plen == rtmsg->rtmsg_src_len) {
+		if (rt->rt6i_dst.plen > rtmsg->rtmsg_dst_len) {
+			struct fib6_node *fn = rt->rt6i_node;
+			while ((fn = fn->parent) != NULL) {
+				if (fn->fn_flags & RTN_ROOT)
+					break;
+				if (fn->fn_flags & RTN_RTINFO) {
+					rt = rt6_device_match(rt, dev, RTF_LINKRT);
+					goto restart;
+				}
+			}
+		}
+		if (rt->rt6i_dst.plen == rtmsg->rtmsg_dst_len) {
+			ip6_del_rt(rt);
+			return 0;
+		}
 	}
 
 	return -ESRCH;
@@ -773,7 +872,7 @@
 	rt6_bh_mask = 0;
 }
 
-#ifdef CONFIG_NETLINK
+#ifdef CONFIG_IPV6_NETLINK
 /*
  *	NETLINK interface
  *	routing socket moral equivalent
@@ -785,6 +884,7 @@
 	struct in6_rtmsg *rtmsg;
 	int err;
 
+	rtnl_lock();
 	while (skb->len) {
 		if (skb->len < sizeof(struct in6_rtmsg)) {
 			count = -EINVAL;
@@ -809,10 +909,10 @@
 	}
 
 out:
+	rtnl_unlock();
 	kfree_skb(skb, FREE_READ);	
 	return count;
 }
-#endif /* CONFIG_NETLINK */
 
 static void rt6_sndrtmsg(struct in6_rtmsg *rtmsg)
 {
@@ -825,9 +925,7 @@
 	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,11 +965,10 @@
 
 	msg->rtmsg_flags = flags;
 
-#ifdef CONFIG_NETLINK
 	if (netlink_post(NETLINK_ROUTE6, skb))
-#endif
 		kfree_skb(skb, FREE_WRITE);
 }
+#endif /* CONFIG_IPV6_NETLINK */
 
 /*
  *	Handle redirects
@@ -888,6 +985,12 @@
 	if (rt == NULL || rt->u.dst.error)
 		return NULL;
 
+	/* Redirect received -> path was valid.
+	   Look, redirects are sent only in response to data packets,
+	   so that this nexthop apparently is reachable. --ANK
+	 */
+	dst_confirm(&rt->u.dst);
+
 	/* Duplicate redirect: silently ignore. */
 	if (ipv6_addr_cmp(target, &rt->rt6i_gateway) == 0)
 		return NULL;
@@ -931,21 +1034,32 @@
 	 *	We have finally decided to accept it.
 	 */
 	if (rt->rt6i_dst.plen == 128) {
+		/* BUGGGG! Very bad bug. Fast path code does not protect
+		 * itself of changing nexthop on the fly, it was supposed
+		 * that crucial parameters (dev, nexthop, hh) ARE VOLATILE.
+		 *                                                   --ANK
+		 * Not fixed!! I plugged it to avoid random crashes
+		 * (they are very unlikely, but I do not want to shrug
+		 *  every time when redirect arrives)
+		 * but the plug must be removed. --ANK
+		 */
+
+#if 0
 		/*
 		 *	Already a host route.
 		 *
 		 */
 		if (rt->rt6i_nexthop)
 			neigh_release(rt->rt6i_nexthop);
-		/*
-		 *	purge hh_cache
-		 */
 		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;
+#else
+		return NULL;
+#endif
 	}
 
 	nrt = ip6_rt_copy(rt);
@@ -965,6 +1079,7 @@
 	rt6_ins(nrt);
 	rt6_unlock();
 
+	/* BUGGGGGGG! nrt can point to nowhere. */
 	return nrt;
 }
 
@@ -975,7 +1090,7 @@
 
 void rt6_pmtu_discovery(struct in6_addr *addr, struct device *dev, int pmtu)
 {
-	struct rt6_info *rt;
+	struct rt6_info *rt, *nrt;
 
 	if (pmtu < 576 || pmtu > 65536) {
 #if RT6_DEBUG >= 1
@@ -994,13 +1109,21 @@
 		return;
 	}
 
+	if (pmtu >= rt->u.dst.pmtu)
+		return;
+
+	/* New mtu received -> path was valid.
+	   They are sent only in response to data packets,
+	   so that this nexthop apparently is reachable. --ANK
+	 */
+	dst_confirm(&rt->u.dst);
+
 	/* 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)) {
+	if (!(rt->rt6i_flags&RTF_GATEWAY))
 		return;
-	}
 
 	if (rt->rt6i_dst.plen == 128) {
 		/*
@@ -1012,11 +1135,18 @@
 		return;
 	}
 
-	rt = ip6_rt_copy(rt);
-	ipv6_addr_copy(&rt->rt6i_dst.addr, addr);
-	rt->rt6i_dst.plen = 128;
+	nrt = ip6_rt_copy(rt);
+	ipv6_addr_copy(&nrt->rt6i_dst.addr, addr);
+	nrt->rt6i_dst.plen = 128;
+
+	nrt->rt6i_flags |= (RTF_DYNAMIC | RTF_CACHE);
 
-	rt->rt6i_flags |= (RTF_DYNAMIC | RTF_CACHE);
+	/* It was missing. :-) :-)
+	   I wonder, kernel was deemed to crash after pkt_too_big
+	   and nobody noticed it. Hey, guys, do someone really
+	   use it? --ANK
+	 */
+	nrt->rt6i_nexthop = neigh_clone(rt->rt6i_nexthop);
 
 	rt6_lock();
 	rt6_ins(rt);
@@ -1027,7 +1157,7 @@
  *	Misc support functions
  */
 
-struct rt6_info * ip6_rt_copy(struct rt6_info *ort)
+static struct rt6_info * ip6_rt_copy(struct rt6_info *ort)
 {
 	struct rt6_info *rt;
 
@@ -1076,7 +1206,7 @@
 
 	for (rt = fn->leaf; rt; rt=rt->u.next) {
 		if (dev == rt->rt6i_dev &&
-		    ipv6_addr_cmp(&rt->rt6i_dst.addr, addr) == 0)
+		    ipv6_addr_cmp(&rt->rt6i_gateway, addr) == 0)
 			break;
 	}
 
@@ -1117,6 +1247,10 @@
 
 	rt = ip6_route_add(&rtmsg, &err);
 
+	/* BUGGGGGGGGGGGGGGGGGGGG!
+	   rt can be not NULL, but point to heavens.
+	 */
+
 	if (err) {
 		printk(KERN_DEBUG "rt6_add_dflt: ip6_route_add error %d\n",
 		       err);
@@ -1172,6 +1306,7 @@
 		if (err)
 			return -EFAULT;
 			
+		rtnl_lock();
 		switch (cmd) {
 		case SIOCADDRT:
 			ip6_route_add(&rtmsg, &err);
@@ -1182,9 +1317,12 @@
 		default:
 			err = -EINVAL;
 		};
+		rtnl_unlock();
 
+#ifdef CONFIG_IPV6_NETLINK
 		if (err == 0)
 				rt6_sndrtmsg(&rtmsg);
+#endif
 		return err;
 	};
 
@@ -1229,15 +1367,19 @@
 	if (rt == NULL)
 		return -ENOMEM;
 	
-	memset(rt, 0, sizeof(struct rt6_info));
-	
 	rt->u.dst.input = ip6_input;
-	rt->u.dst.output = dev_queue_xmit;
+	rt->u.dst.output = ip6_output;
 	rt->rt6i_dev = dev_get("lo");
 	rt->u.dst.pmtu = rt->rt6i_dev->mtu;
+	rt->u.dst.obsolete = -1;
 
 	rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
-	
+	rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway);
+	if (rt->rt6i_nexthop == NULL) {
+		dst_free((struct dst_entry *) rt);
+		return -ENOMEM;
+	}
+
 	ipv6_addr_copy(&rt->rt6i_dst.addr, addr);
 	rt->rt6i_dst.plen = 128;
 
@@ -1248,6 +1390,21 @@
 	return 0;
 }
 
+/* Delete address. Warning: you should check that this address
+   disappeared before calling this function.
+ */
+
+int ip6_rt_addr_del(struct in6_addr *addr, struct device *dev)
+{
+	struct rt6_info *rt;
+
+	rt = rt6_lookup(addr, NULL, dev_get("lo"), RTF_LINKRT);
+	if (rt && rt->rt6i_dst.plen == 128)
+		return ip6_del_rt(rt);
+
+	return 0;
+}
+
 #ifdef CONFIG_RT6_POLICY
 
 static int rt6_flow_match_in(struct rt6_info *rt, struct sk_buff *skb)
@@ -1355,18 +1512,260 @@
 		goto error;
 
 	nrt->rt6i_flags |= RTF_CACHE;
+	/* BUGGGG! nrt can point to nowhere! */
 	rt6_ins(nrt);
 
 	return nrt;
 }
 #endif
 
+/* 
+ * Nope, I am not idiot. I see that it is the ugliest of ugly routines.
+ * Anyone is advertised to write better one. --ANK
+ */
+
+struct rt6_ifdown_arg {
+	struct device *dev;
+	struct rt6_info *rt;
+};
+
+
+static void rt6_ifdown_node(struct fib6_node *fn, void *p_arg)
+{
+	struct rt6_info *rt;
+	struct rt6_ifdown_arg *arg = (struct rt6_ifdown_arg *) p_arg;
+
+	if (arg->rt != NULL)
+		return;
+
+	for (rt = fn->leaf; rt; rt = rt->u.next) {
+		if (rt->rt6i_dev == arg->dev || arg->dev == NULL) {
+			arg->rt = rt;
+			return;
+		}
+	}
+}
+
+void rt6_ifdown(struct device *dev)
+{
+	int count = 0;
+	struct rt6_ifdown_arg arg;
+	struct rt6_info *rt;
+
+	do {
+		arg.dev = dev;
+		arg.rt = NULL;
+		fib6_walk_tree(&ip6_routing_table, rt6_ifdown_node, &arg,
+			       RT6_FILTER_RTNODES);
+		if (arg.rt != NULL)
+			ip6_del_rt(arg.rt);
+		count++;
+	} while (arg.rt != NULL);
+
+	/* And default routes ... */
+
+	for (rt = ip6_routing_table.leaf; rt; ) {
+		if (rt != &ip6_null_entry && (rt->rt6i_dev == dev || dev == NULL)) {
+			struct rt6_info *deleting = rt;
+			rt = rt->u.next;
+			ip6_del_rt(deleting);
+			continue;
+		}
+		rt = rt->u.next;
+	}
+}
+
+#ifdef CONFIG_RTNETLINK
+
+static void inet6_rtm_to_rtmsg(struct rtmsg *r, struct kern_rta *rta,
+			       struct in6_rtmsg *rtmsg)
+{
+	memset(rtmsg, 0, sizeof(*rtmsg));
+	rtmsg->rtmsg_dst_len = r->rtm_dst_len;
+	rtmsg->rtmsg_src_len = r->rtm_src_len;
+	rtmsg->rtmsg_flags = RTF_UP;
+	rtmsg->rtmsg_metric = IP6_RT_PRIO_USER;
+	if (rta->rta_gw) {
+		memcpy(&rtmsg->rtmsg_gateway, rta->rta_gw, 16);
+		rtmsg->rtmsg_flags |= RTF_GATEWAY;
+	}
+	if (rta->rta_dst)
+		memcpy(&rtmsg->rtmsg_dst, rta->rta_dst, 16);
+	if (rta->rta_src)
+		memcpy(&rtmsg->rtmsg_src, rta->rta_src, 16);
+	if (rta->rta_oif)
+		memcpy(&rtmsg->rtmsg_ifindex, rta->rta_oif, 4);
+	if (rta->rta_priority)
+		memcpy(&rtmsg->rtmsg_metric, rta->rta_priority, 4);
+}
+
+int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
+{
+	struct kern_rta *rta = arg;
+	struct rtmsg *r = NLMSG_DATA(nlh);
+	struct in6_rtmsg rtmsg;
+
+	inet6_rtm_to_rtmsg(r, rta, &rtmsg);
+	return ip6_route_del(&rtmsg);
+}
+
+int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
+{
+	struct kern_rta *rta = arg;
+	struct rtmsg *r = NLMSG_DATA(nlh);
+	struct in6_rtmsg rtmsg;
+	int err = 0;
+
+	inet6_rtm_to_rtmsg(r, rta, &rtmsg);
+	ip6_route_add(&rtmsg, &err);
+	return err;
+}
+
+
+struct rt6_rtnl_dump_arg
+{
+	struct sk_buff *skb;
+	struct netlink_callback *cb;
+	int skip;
+	int count;
+	int stop;
+};
+
+static int rt6_fill_node(struct sk_buff *skb, struct rt6_info *rt,
+			 int type, pid_t pid, u32 seq)
+{
+	struct rtmsg *rtm;
+	struct nlmsghdr  *nlh;
+	unsigned char	 *b = skb->tail;
+	unsigned char 	 *o;
+	struct rta_cacheinfo ci;
+
+	nlh = NLMSG_PUT(skb, pid, seq, type, sizeof(*rtm));
+	rtm = NLMSG_DATA(nlh);
+	rtm->rtm_family = AF_INET6;
+	rtm->rtm_dst_len = rt->rt6i_dst.plen;
+	rtm->rtm_src_len = rt->rt6i_src.plen;
+	rtm->rtm_tos = 0;
+	rtm->rtm_table = RT_TABLE_MAIN;
+	rtm->rtm_type = RTN_UNICAST;
+	rtm->rtm_flags = 0;
+	rtm->rtm_scope = RT_SCOPE_UNIVERSE;
+	rtm->rtm_nhs = 0;
+	rtm->rtm_protocol = RTPROT_BOOT;
+	if (rt->rt6i_flags&RTF_DYNAMIC)
+		rtm->rtm_protocol = RTPROT_REDIRECT;
+	else if (rt->rt6i_flags&(RTF_ADDRCONF|RTF_ALLONLINK))
+		rtm->rtm_protocol = RTPROT_KERNEL;
+	else if (rt->rt6i_flags&RTF_DEFAULT)
+		rtm->rtm_protocol = RTPROT_RA;
+
+	if (rt->rt6i_flags&RTF_CACHE)
+		rtm->rtm_flags |= RTM_F_CLONED;
+
+	o = skb->tail;
+	if (rtm->rtm_dst_len)
+		RTA_PUT(skb, RTA_DST, 16, &rt->rt6i_dst.addr);
+	if (rtm->rtm_src_len)
+		RTA_PUT(skb, RTA_SRC, 16, &rt->rt6i_src.addr);
+	if (rt->u.dst.pmtu)
+		RTA_PUT(skb, RTA_MTU, sizeof(unsigned), &rt->u.dst.pmtu);
+	if (rt->u.dst.window)
+		RTA_PUT(skb, RTA_WINDOW, sizeof(unsigned), &rt->u.dst.window);
+	if (rt->u.dst.rtt)
+		RTA_PUT(skb, RTA_RTT, sizeof(unsigned), &rt->u.dst.rtt);
+	if (rt->u.dst.neighbour)
+		RTA_PUT(skb, RTA_GATEWAY, 16, &rt->u.dst.neighbour->primary_key);
+	if (rt->u.dst.dev)
+		RTA_PUT(skb, RTA_OIF, sizeof(int), &rt->rt6i_dev->ifindex);
+	RTA_PUT(skb, RTA_PRIORITY, 4, &rt->rt6i_metric);
+	ci.rta_lastuse = jiffies - rt->u.dst.lastuse;
+	if (rt->rt6i_expires)
+		ci.rta_expires = rt->rt6i_expires - jiffies;
+	else
+		ci.rta_expires = 0;
+	ci.rta_used = 0;
+	ci.rta_clntref = atomic_read(&rt->u.dst.use);
+	ci.rta_error = rt->u.dst.error;
+	RTA_PUT(skb, RTA_CACHEINFO, sizeof(ci), &ci);
+	rtm->rtm_optlen = skb->tail - o;
+	nlh->nlmsg_len = skb->tail - b;
+	return skb->len;
+
+nlmsg_failure:
+rtattr_failure:
+	skb_put(skb, b - skb->tail);
+	return -1;
+}
+
+static void rt6_dump_node(struct fib6_node *fn, void *p_arg)
+{
+	struct rt6_info *rt;
+	struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
+
+	if (arg->stop)
+		return;
+
+	for (rt = fn->leaf; rt; rt = rt->u.next) {
+		if (arg->count < arg->skip) {
+			arg->count++;
+			continue;
+		}
+		if (rt6_fill_node(arg->skb, rt, RTM_NEWROUTE,
+				  NETLINK_CB(arg->cb->skb).pid, arg->cb->nlh->nlmsg_seq) <= 0) {
+			arg->stop = 1;
+			break;
+		}
+		arg->count++;
+	}
+}
+
+
+int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	struct rt6_rtnl_dump_arg arg;
+
+	arg.skb = skb;
+	arg.cb = cb;
+	arg.skip = cb->args[0];
+	arg.count = 0;
+	arg.stop = 0;
+	start_bh_atomic();
+	fib6_walk_tree(&ip6_routing_table, rt6_dump_node, &arg, RT6_FILTER_RTNODES);
+	if (arg.stop == 0)
+		rt6_dump_node(&ip6_routing_table, &arg);
+	end_bh_atomic();
+	cb->args[0] = arg.count;
+	return skb->len;
+}
+
+void inet6_rt_notify(int event, struct rt6_info *rt)
+{
+	struct sk_buff *skb;
+	int size = NLMSG_SPACE(sizeof(struct rtmsg)+256);
+
+	skb = alloc_skb(size, GFP_KERNEL);
+	if (!skb) {
+		netlink_set_err(rtnl, 0, RTMGRP_IPV6_ROUTE, ENOBUFS);
+		return;
+	}
+	if (rt6_fill_node(skb, rt, event, 0, 0) < 0) {
+		kfree_skb(skb, 0);
+		netlink_set_err(rtnl, 0, RTMGRP_IPV6_ROUTE, EINVAL);
+		return;
+	}
+	NETLINK_CB(skb).dst_groups = RTMGRP_IPV6_ROUTE;
+	netlink_broadcast(rtnl, skb, 0, RTMGRP_IPV6_ROUTE, GFP_ATOMIC);
+}
+
+#endif
+
 /*
  *	/proc
  */
 
 #ifdef CONFIG_PROC_FS
 
+
 #define RT6_INFO_LEN (32 + 4 + 32 + 4 + 32 + 40 + 5 + 1)
 
 struct rt6_proc_arg {
@@ -1411,11 +1810,8 @@
 		
 		if (rt->rt6i_nexthop) {
 			for (i=0; i<16; i++) {
-				struct nd_neigh *ndn;
-
-				ndn = (struct nd_neigh *) rt->rt6i_nexthop;
 				sprintf(arg->buffer + arg->len, "%02x",
-					ndn->ndn_addr.s6_addr[i]);
+					rt->rt6i_nexthop->primary_key[i]);
 				arg->len += 2;
 			}
 		} else {
@@ -1424,7 +1820,7 @@
 			arg->len += 32;
 		}
 		arg->len += sprintf(arg->buffer + arg->len,
-				    " %08lx %08x %08x %08lx %8s\n",
+				    " %08x %08x %08x %08x %8s\n",
 				    rt->rt6i_metric, atomic_read(&rt->rt6i_use),
 				    atomic_read(&rt->rt6i_ref), rt->rt6i_flags, 
 				    rt->rt6i_dev ? rt->rt6i_dev->name : "");
@@ -1528,6 +1924,7 @@
 	return arg.len;
 }
 
+
 extern struct rt6_statistics rt6_stats;
 
 static int rt6_proc_stats(char *buffer, char **start, off_t offset, int length,
@@ -1558,28 +1955,28 @@
 	0, &proc_net_inode_operations,
 	rt6_proc_info
 };
-static struct proc_dir_entry proc_rt6_stats = {
-	PROC_NET_RT6_STATS, 9, "rt6_stats",
-	S_IFREG | S_IRUGO, 1, 0, 0,
-	0, &proc_net_inode_operations,
-	rt6_proc_stats
-};
 static struct proc_dir_entry proc_rt6_tree = {
 	PROC_NET_RT6_TREE, 7, "ip6_fib",
 	S_IFREG | S_IRUGO, 1, 0, 0,
 	0, &proc_net_inode_operations,
 	rt6_proc_tree
 };
+static struct proc_dir_entry proc_rt6_stats = {
+	PROC_NET_RT6_STATS, 9, "rt6_stats",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_net_inode_operations,
+	rt6_proc_stats
+};
 #endif	/* CONFIG_PROC_FS */
 
 __initfunc(void ip6_route_init(void))
 {
 #ifdef 	CONFIG_PROC_FS
 	proc_net_register(&proc_rt6_info);
-	proc_net_register(&proc_rt6_stats);
 	proc_net_register(&proc_rt6_tree);
+	proc_net_register(&proc_rt6_stats);
 #endif
-#ifdef CONFIG_NETLINK
+#ifdef CONFIG_IPV6_NETLINK
 	netlink_attach(NETLINK_ROUTE6, rt6_msgrcv);
 #endif
 }
@@ -1592,11 +1989,9 @@
 	proc_net_unregister(PROC_NET_RT6_TREE);
 	proc_net_unregister(PROC_NET_RT6_STATS);
 #endif
-#ifdef CONFIG_NETLINK
+#ifdef CONFIG_IPV6_NETLINK
 	netlink_detach(NETLINK_ROUTE6);
 #endif
-#if 0
-	fib6_flush();
-#endif
+	rt6_ifdown(NULL);
 }
 #endif	/* MODULE */

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