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

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

diff -u --recursive --new-file v2.1.29/linux/net/ipv6/ipv6_route.c linux/net/ipv6/ipv6_route.c
@@ -1,2058 +0,0 @@
-/*
- *	IPv6 routing table
- *	Linux INET6 implementation 
- *
- *	Authors:
- *	Pedro Roque		<[email protected]>	
- *
- *	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
- *      2 of the License, or (at your option) any later version.
- */
-
-/*
- *	Changes:
- *
- *	Masaki Hirabaru			:	Fix for /proc info > pagesize
- *	<[email protected]>
- */
-
-#include <linux/config.h>
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/sockios.h>
-#include <linux/sched.h>
-#include <linux/net.h>
-#include <linux/route.h>
-#include <linux/netdevice.h>
-#include <linux/in6.h>
-
-#ifdef 	CONFIG_PROC_FS
-#include <linux/proc_fs.h>
-#endif
-
-#include <net/tcp.h>
-#include <net/sock.h>
-#include <net/snmp.h>
-
-#include <net/ipv6.h>
-#include <net/ndisc.h>
-#include <net/protocol.h>
-#include <net/ipv6_route.h>
-#include <net/addrconf.h>
-
-#include <net/netlink.h>
-
-#include <asm/uaccess.h>
-
-/*
- *	Routing Table
- *
- *	simplified version of a radix tree
- *
- *	- every node shares it's acestors prefix
- *	- the tree is ordered from less to most specific mask
- *	- default routes are handled apart
- *
- *	this facilitates recursion a lot
- */
-
-static struct rt6_info null_entry = {
-	NULL, NULL, 
-	{{{0}}},
-	0, 1,
-	NULL, NULL,
-	0, 0, RTF_REJECT
-};
-
-struct fib6_node routing_table = {
-	NULL, NULL, NULL, &null_entry, 
-	0, RTN_ROOT, 0
-};
-
-struct rt6_info		*default_rt_list = NULL;
-struct rt6_info		*loopback_rt = NULL;
-
-/*
- *	last_resort_rt - no routers present.
- *	Assume all destinations on link.
- */
-struct rt6_info		*last_resort_rt = NULL;
-
-static struct rt6_req request_queue = {
-	0, NULL, &request_queue, &request_queue
-};
-
-
-/*
- *	A routing update causes an increase of the serial number on the
- *	afected subtree. This allows for cached routes to be asynchronously
- *	tested when modifications are made to the destination cache as a
- *	result of redirects, path MTU changes, etc.
- */
-
-static __u32	rt_sernum	= 0;
-
-static atomic_t rt6_lock	= 0;
-static int	rt6_bh_mask	= 0;
-
-#define RT_BH_REQUEST	1
-#define RT_BH_GC	2
-
-static void __rt6_run_bh(void);
-
-typedef void (*f_pnode)(struct fib6_node *fn, void *);
-
-static void	rt6_walk_tree(f_pnode func, void * arg, int filter);
-static void	rt6_rt_timeout(struct fib6_node *fn, void *arg);
-static int	rt6_msgrcv(int unit, struct sk_buff *skb);
-
-struct rt6_statistics rt6_stats = {
-	1, 0, 1, 1, 0
-};
-
-static atomic_t	rt_clients = 0;
-
-void rt6_timer_handler(unsigned long data);
-
-struct timer_list rt6_gc_timer = {
-	NULL,
-	NULL,
-	0,
-	0,
-	rt6_timer_handler
-};
-
-static __inline__ void rt6_run_bh(void)
-{
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
-
-	if (rt6_lock == 0 && rt6_bh_mask)
-	{
-		__rt6_run_bh();
-	}
-	restore_flags(flags);
-}
-
-/*
- *	request queue operations
- *	FIFO queue/dequeue
- */
-static __inline__ void rtreq_queue(struct rt6_req * req)
-{
-	unsigned long flags;
-	struct rt6_req *next = &request_queue;
-
-	save_flags(flags);
-	cli();
-
-	req->prev = next->prev;
-	req->prev->next = req;
-	next->prev = req;
-	req->next = next;
-	restore_flags(flags);
-}
-
-static __inline__ struct rt6_req * rtreq_dequeue(void)
-{
-	struct rt6_req *next = &request_queue;
-	struct rt6_req *head;
-
-	head = next->next;
-
-	if (head == next)
-	{
-		return NULL;
-	}
-
-	head->next->prev = head->prev;
-	next->next = head->next;
-
-	head->next = NULL;
-	head->prev = NULL;
-
-	return head;
-}
-
-/*
- *	compare "prefix length" bits of an address
- */
-static __inline__ int addr_match(struct in6_addr *a1, struct in6_addr *a2,
-				 int prefixlen)
-{
-	int pdw;
-	int pbi;
-
-	pdw = prefixlen >> 0x05;  /* num of whole __u32 in prefix */
-	pbi = prefixlen &  0x1f;  /* num of bits in incomplete u32 in prefix */
-
-	if (pdw) 
-	{
-		if (memcmp(a1, a2, pdw << 2))
-			return 0;
-	}
-
-	if (pbi) 
-	{
-		__u32 w1, w2;
-		__u32 mask;
-
-		w1 = a1->s6_addr32[pdw];
-		w2 = a2->s6_addr32[pdw];
-
-		mask = htonl((0xffffffff) << (0x20 - pbi));
-
-		if ((w1 ^ w2) & mask)
-			return 0;
-	}
-
-	return 1;
-}
-
-/*
- *	test bit. range [0-127]
- */
-
-static __inline__ int addr_bit_set(struct in6_addr *addr, int fn_bit)
-{
-	int dw;
-	__u32 b1;
-	__u32 mask;
-	int bit = fn_bit;
-
-	dw = bit >> 0x05;
-
-	b1 = addr->s6_addr32[dw];
-	
-	bit = ~bit;
-	bit &= 0x1f;
-	mask = htonl(1 << bit);
-	return (b1 & mask);
-}
-
-static __inline__ int addr_bit_equal(struct in6_addr *a1, struct in6_addr *a2,
-				     int fn_bit)
-{
-	int dw;
-	__u32 b1, b2;
-	__u32 mask;
-	int bit = fn_bit;
-
-	dw = bit >> 0x05;
-
-	b1 = a1->s6_addr32[dw];
-	b2 = a2->s6_addr32[dw];
-	
-	bit = ~bit;
-	bit &= 0x1f;
-	mask = htonl(1 << bit);
-	return !((b1 ^ b2) & mask);
-}
-
-/*
- *	find the first different bit between two addresses
- */
-static __inline__ int addr_diff(struct in6_addr *a1, struct in6_addr *a2)
-{
-	int i;
-
-	for (i = 0; i<4; i++)
-	{
-		__u32 b1, b2;
-		__u32 xb;
-		
-		b1 = a1->s6_addr32[i];
-		b2 = a2->s6_addr32[i];
-		
-		xb = b1 ^ b2;
-
-		if (xb)
-		{
-			int res = 0;
-			int j=31;
-
-			xb = ntohl(xb);
-
-			while (test_bit(j, &xb) == 0)
-			{
-				res++;
-				j--;
-			}
-			
-			return (i * 32 + res);
-		}
-	}
-
-	/*
-	 *	bit values are in range [0-127]
-	 *	128 is an ilegal value as we should *never* get to
-	 *	this point since that would mean the addrs are equal
-	 */
-	return 128;
-}
-
-/*
- *	add a rt to a node that may already contain routes
- *	sort routes in ascending metric order so that fib lookup
- *	returns the smallest metric by default
- */
-
-static __inline__ void fib6_add_rt2node(struct fib6_node *fn,
-					struct rt6_info *rt)
-{
-	struct rt6_info *iter, **back;
-
-	rt->fib_node = fn;
-	back = &fn->leaf;
-	
-	for (iter = fn->leaf; iter; iter=iter->next)
-	{
-		if (iter->rt_metric > rt->rt_metric)
-		{
-			break;
-		}
-
-		back = &iter->next;
-	}
-
-	rt->next = iter;
-	*back = rt;
-}
-
-/*
- *	Routing Table
- */
-
-static int fib6_add_1(struct rt6_info *rt)
-{
-	struct fib6_node *fn;
-	struct fib6_node *pn = NULL;
-	struct fib6_node *in;
-	struct fib6_node *ln;
-	struct in6_addr *addr;
-	__u32	bit;
-	__u32	dir = 0;
-	__u32	sernum = ++rt_sernum;
-	int pbit = rt->rt_prefixlen - 1;
-
-	addr = &rt->rt_dst;
-
-	/* insert node in tree */
-
-	fn = &routing_table;
-
-	for (;;)
-	{
-		if (fn == NULL)
-		{			
-			ln = kmalloc(sizeof(struct fib6_node), GFP_ATOMIC);
-
-			if (ln == NULL)
-				return (-ENOMEM);
-
-			memset(ln, 0, sizeof(struct fib6_node));
-			ln->fn_bit   = pbit;
-			ln->fn_flags = RTN_BACKTRACK;
-			
-			ln->parent = pn;
-			ln->leaf = rt;
-			ln->fn_sernum = sernum;
-			rt->fib_node = ln;
-
-			atomic_inc(&rt->rt_ref);
-
-			if (dir)
-				pn->right = ln;
-			else
-				pn->left  = ln;
-
-			rt6_stats.fib_nodes++;
-			rt6_stats.fib_route_nodes++;
-			rt6_stats.fib_rt_entries++;
-
-			return(0);
-		}
-
-		if (addr_match(&fn->leaf->rt_dst, addr, fn->fn_bit))
-		{
-			if (pbit == fn->fn_bit && pbit &&
-			    addr_bit_equal(addr, &fn->leaf->rt_dst,
-					   rt->rt_prefixlen))
-			{
-				/* clean up an intermediate node */
-				if ((fn->fn_flags & RTN_BACKTRACK) == 0) 
-				{
-					rt_release(fn->leaf);
-					fn->leaf = NULL;
-					fn->fn_flags |= RTN_BACKTRACK;
-				}
-			
-				fib6_add_rt2node(fn, rt);
-				fn->fn_sernum = sernum;
-				atomic_inc(&rt->rt_ref);
-				
-				rt6_stats.fib_route_nodes++;
-				rt6_stats.fib_rt_entries++;
-				
-				return 0;
-			}
-
-			if (pbit > fn->fn_bit || pbit == 0)
-			{
-				/* walk down on tree */
-
-				fn->fn_sernum = sernum;
-
-				dir = addr_bit_set(addr, fn->fn_bit); 
-				pn = fn;
-				fn = dir ? fn->right: fn->left;
-
-				continue;
-			}
-		}
-
-		/*
-		 * split since we don't have a common prefix anymore or 
-		 * we have a less significant route.
-		 * we've to insert an intermediate node on the list
-		 * this new node will point to the one we need to create
-		 * and the current
-		 */
-
-		pn = fn->parent;
-
-		/* find 1st bit in difference between the 2 addrs */
-		bit = addr_diff(addr, &fn->leaf->rt_dst);
-
-
-		/* 
-		 *		(intermediate)	
-		 *	          /	   \
-		 *	(new leaf node)    (old node)
-		 */
-		if (rt->rt_prefixlen > bit)
-		{
-			in = kmalloc(sizeof(struct fib6_node), GFP_ATOMIC);
-		
-			if (in == NULL)
-				return (-ENOMEM);
-
-			memset(in, 0, sizeof(struct fib6_node));
-
-			/* 
-			 * new intermediate node. 
-			 * RTN_BACKTRACK will
-			 * be off since that an address that chooses one of
-			 * the branches would not match less specific routes
-			 * int the other branch
-			 */
-
-			in->fn_bit = bit;
-			in->parent = pn;
-			in->leaf = rt;
-			in->fn_sernum = sernum;
-			atomic_inc(&rt->rt_ref);
-
-			/* leaf node */
-			ln = kmalloc(sizeof(struct fib6_node), GFP_ATOMIC);
-
-			if (ln == NULL)
-			{
-				kfree(in);
-				return (-ENOMEM);
-			}
-
-			/* update parent pointer */
-			if (dir)
-				pn->right = in;
-			else
-				pn->left  = in;
-
-			memset(ln, 0, sizeof(struct fib6_node));
-			ln->fn_bit   = pbit;
-			ln->fn_flags = RTN_BACKTRACK;
-			
-			ln->parent = in;
-			fn->parent = in;
-
-			ln->leaf = rt;
-			ln->fn_sernum = sernum;
-			atomic_inc(&rt->rt_ref);
-
-			rt->fib_node = ln;
-
-			if (addr_bit_set(addr, bit))
-			{
-				in->right = ln;
-				in->left  = fn;
-			}
-			else
-			{
-				in->left  = ln;
-				in->right = fn;
-			}
-
-			rt6_stats.fib_nodes += 2;
-			rt6_stats.fib_route_nodes++;
-			rt6_stats.fib_rt_entries++;
-
-			return 0;
-		}
-
-		/* 
-		 *		(new leaf node)
-		 *	          /	   \
-		 *	     (old node)    NULL
-		 */
-
-		ln = kmalloc(sizeof(struct fib6_node), GFP_ATOMIC);
-
-		if (ln == NULL)
-			return (-ENOMEM);
-
-		memset(ln, 0, sizeof(struct fib6_node));
-		ln->fn_bit   = pbit;
-		ln->fn_flags = RTN_BACKTRACK;
-			
-
-		ln->parent = pn;
-		ln->leaf = rt;
-		ln->fn_sernum = sernum;
-		atomic_inc(&rt->rt_ref);
-
-		rt->fib_node = ln;
-
-		if (dir)
-			pn->right = ln;
-		else
-			pn->left  = ln;
-		
-
-		if (addr_bit_set(&fn->leaf->rt_dst, pbit))
-			ln->right = fn;
-		else
-			ln->left  = fn; 
-
-		fn->parent = ln;
-
-		rt6_stats.fib_nodes++;
-		rt6_stats.fib_route_nodes++;
-		rt6_stats.fib_rt_entries++;
-
-		return(0);
-	}
-
-	return (-1);
-}
-
-static struct rt6_info * fib6_lookup_1(struct in6_addr *addr, int flags)
-{
-	struct fib6_node *fn, *next;
-	int dir;
-
-	fn = &routing_table;
-
-	for (;;)
-	{
-		dir = addr_bit_set(addr, fn->fn_bit);
-
-		next = dir ? fn->right: fn->left;
-
-		if (next)
-		{
-			fn = next;
-			continue;
-		}
-
-		break;
-	}
-
-
-	while ((fn->fn_flags & RTN_ROOT) == 0)
-	{
-		if (fn->fn_flags & RTN_BACKTRACK)
-		{
-			if (addr_match(&fn->leaf->rt_dst, addr, 
-				       fn->leaf->rt_prefixlen))
-			{
-				struct rt6_info *rt;
-				
-				for (rt = fn->leaf; rt; rt = rt->next)
-				{
-					if ((rt->rt_flags & flags) == 0)
-						return rt;
-				}
-			}
-		}
-		
-		fn = fn->parent;
-	}
-
-	return NULL;
-}
-
-
-
-/*
- *	called to trim the tree of intermediate nodes when possible
- */
-
-static void fib6_del_3(struct fib6_node *fn)
-{
-	int children = 0;
-	int dir = 0;
-	int bit;
-
-	/*
-	 *	0 or one children:
-	 *		delete the node
-	 *
-	 *	2 children:
-	 *		move the bit down
-	 */
-
-	if (fn->left)
-	{
-		children++;
-		dir = 0;
-	}
-
-	if (fn->right)
-	{
-		children++;
-		dir = 1;
-	}
-
-	if (children < 2)
-	{
-		struct fib6_node *child;
-		struct fib6_node *pn;
-
-		child = dir ? fn->right : fn->left;
-
-		if (fn->parent->left == fn)
-		{
-			fn->parent->left = child;
-		}
-		else
-		{
-			fn->parent->right = child;
-		}
-
-		if (child)
-		{
-			child->parent = fn->parent;
-		}
-
-		/*
-		 *	try to collapse on top
-		 */
-		pn = fn->parent;
-		fn->parent = NULL;
-		
-		if ((pn->fn_flags & (RTN_BACKTRACK | RTN_ROOT)) == 0)
-		{
-			if (pn->leaf)
-			{
-				rt_release(pn->leaf);
-				pn->leaf = NULL;
-			}
-			fib6_del_3(pn);
-		}
-
-		if (fn->fn_flags & RTN_BACKTRACK)
-		{
-			rt6_stats.fib_route_nodes--;
-		}
-		rt6_stats.fib_nodes--;
-		kfree(fn);
-		return;
-	}
-
-	bit = addr_diff(&fn->left->leaf->rt_dst, &fn->right->leaf->rt_dst);
-	
-	fn->fn_bit = bit;
-	fn->fn_flags &= ~RTN_BACKTRACK;
-	
-	fn->leaf = fn->left->leaf;
-	atomic_inc(&fn->leaf->rt_ref);
-
-	rt6_stats.fib_route_nodes--;
-}
-
-static struct fib6_node * fib6_del_2(struct in6_addr *addr, __u32 prefixlen, 
-				     struct in6_addr *gw, struct device *dev)
-{
-	struct fib6_node *fn;
-
-	for (fn = &routing_table; fn;) 
-	{
-		int dir;
-
-		if ((fn->fn_flags & RTN_BACKTRACK) &&
-		    prefixlen == fn->leaf->rt_prefixlen &&
-		    addr_match(&fn->leaf->rt_dst, addr, fn->leaf->rt_prefixlen)
-		    )
-		{
-			break;
-		}
-
-		dir = addr_bit_set(addr, fn->fn_bit);
-
-		fn = dir ? fn->right: fn->left;
-	}
-
-	/* 
-	 *	if route tree node found
-	 *	search among it's entries
-	 */
-
-	if (fn)
-	{
-		struct rt6_info *back = NULL;
-		struct rt6_info *lf;
-
-		for(lf = fn->leaf; lf; lf=lf->next)
-		{
-			if ((gw && (ipv6_addr_cmp(addr, &lf->rt_dst) == 0)) ||
-			    (dev && dev == lf->rt_dev))
-			{
-				/* delete this entry */
-				if (back == NULL)
-					fn->leaf = lf->next;
-				else
-					back->next = lf->next;
-
-				lf->fib_node = NULL;
-				rt_release(lf);
-				return fn;		
-			}
-			back = lf;
-		}
-	}
-
-	return NULL;
-}
-
-static struct fib6_node * fib6_del_rt_2(struct rt6_info *rt)
-{
-	struct fib6_node *fn;
-	struct in6_addr *addr = &rt->rt_dst;
-	int prefixlen = rt->rt_prefixlen;
-	
-	for (fn = &routing_table; fn;) 
-	{
-		int dir;
-
-		if ((fn->fn_flags & RTN_BACKTRACK) &&
-		    prefixlen == fn->leaf->rt_prefixlen &&
-		    addr_match(&fn->leaf->rt_dst, addr, fn->leaf->rt_prefixlen)
-		    )
-		{
-			break;
-		}
-
-		dir = addr_bit_set(addr, fn->fn_bit);
-
-		fn = dir ? fn->right: fn->left;
-	}
-
-	/* 
-	 *	if route tree node found
-	 *	search among its entries
-	 */
-
-	if (fn)
-	{
-		struct rt6_info **back;
-		struct rt6_info *lf;
-
-		back = &fn->leaf;
-		
-		for(lf = fn->leaf; lf; lf=lf->next)
-		{
-			if (rt == lf)
-			{
-				/*
-				 *	delete this entry
-				 */
-				
-				*back = lf->next;
-				rt_release(lf);
-				return fn;
-			}
-			back = &lf->next;
-		}
-	}
-
-	return NULL;
-}
-
-int fib6_del_1(struct in6_addr *addr, __u32 prefixlen, struct in6_addr *gw, 
-	       struct device *dev)
-{
-	struct fib6_node *fn;
-
-	fn = fib6_del_2(addr, prefixlen, gw, dev);
-
-	if (fn == NULL)
-		return -ENOENT;
-	
-	if (fn->leaf == NULL)
-	{
-		fib6_del_3(fn);
-	}
-
-	return 0;
-}
-
-int fib6_del_rt(struct rt6_info *rt)
-{
-	struct fib6_node *fn;
-
-	fn = fib6_del_rt_2(rt);
-
-	if (fn == NULL)
-		return -ENOENT;
-	
-	if (fn->leaf == NULL)
-	{
-		fib6_del_3(fn);
-	}
-
-	return 0;
-}
-
-static void fib6_flush_1(struct fib6_node *fn, void *p_arg)
-{
-	struct rt6_info *rt;
-
-	for (rt = fn->leaf; rt;)
-	{
-		struct rt6_info *itr;
-
-		itr = rt;
-		rt = rt->next;
-		itr->fib_node = NULL;
-		rt_release(itr);
-	}
-	
-	if (fn->fn_flags & RTN_BACKTRACK)
-	{
-		rt6_stats.fib_route_nodes--;
-	}
-	rt6_stats.fib_nodes--;
-	kfree(fn);
-}
-
-void fib6_flush(void)
-{
-	rt6_walk_tree(fib6_flush_1, NULL, RT6_FILTER_NONE);
-}
-
-int ipv6_route_add(struct in6_rtmsg *rtmsg)
-{
-	struct rt6_info *rt;
-	struct device * dev = NULL;
-	struct inet6_dev *idev;
-	struct rt6_req *request;
-	int flags = rtmsg->rtmsg_flags;
-
-	idev = ipv6_dev_by_index(rtmsg->rtmsg_ifindex);
-	if (idev)
-	{
-		dev = idev->dev; 
-	}
-	
-	rt = (struct rt6_info *) kmalloc(sizeof(struct rt6_info),
-					 GFP_ATOMIC);
-
-	rt6_stats.fib_rt_alloc++;
-
-	memset(rt, 0, sizeof(struct rt6_info));
-		
-	memcpy(&rt->rt_dst, &rtmsg->rtmsg_dst, sizeof(struct in6_addr));
-	rt->rt_prefixlen = rtmsg->rtmsg_prefixlen;
-
-	if (rt->rt_prefixlen == 0)
-	{
-		printk(KERN_DEBUG "ip6_fib: zero length route not allowed\n");
-		return -EINVAL;
-	}
-	
-	if (flags & (RTF_GATEWAY | RTF_NEXTHOP)) 
-	{
-		/* check to see if its an acceptable gateway */
-		if (flags & RTF_GATEWAY)
-		{
-			struct rt6_info *gw_rt;
-
-			gw_rt = fibv6_lookup(&rtmsg->rtmsg_gateway, dev,
-					     RTI_GATEWAY);
-
-			if (gw_rt == NULL)
-			{
-				return -EHOSTUNREACH;
-			}
-
-			dev = gw_rt->rt_dev;
-		}
-
-		rt->rt_nexthop = ndisc_get_neigh(dev, &rtmsg->rtmsg_gateway);
-
-		if (rt->rt_nexthop == NULL)
-		{
-			printk(KERN_DEBUG "ipv6_route_add: no nexthop\n");
-			kfree(rt);
-			return -EINVAL;
-		}
-
-		rt->rt_dev = dev;
-
-		if (loopback_rt == NULL && (dev->flags & IFF_LOOPBACK))
-		{
-			loopback_rt = rt;
-		}
-
-	}
-	else
-	{
-		if (dev == NULL)
-		{
-			printk(KERN_DEBUG "ipv6_route_add: NULL dev\n");
-			kfree(rt);
-			return -EINVAL;
-		}
-
-		rt->rt_dev = dev;
-		rt->rt_nexthop = NULL;
-	}
-	
-	rt->rt_metric = rtmsg->rtmsg_metric;
-	rt->rt_flags = rtmsg->rtmsg_flags;
-
-	if (rt->rt_flags & RTF_ADDRCONF)
-	{
-		rt->rt_expires = rtmsg->rtmsg_info;
-	}
-
-	request = kmalloc(sizeof(struct rt6_req), GFP_ATOMIC);
-	if (request == NULL)
-	{
-		printk(KERN_WARNING "ipv6_route_add: kmalloc failed\n");
-		return -ENOMEM;
-	}
-
-	request->operation = RT_OPER_ADD;
-	request->ptr = rt;
-	request->next = request->prev = NULL;
-	rtreq_queue(request);
-	rt6_bh_mask |= RT_BH_REQUEST;
-
-	rt6_run_bh();
-
-	return 0;
-}
-
-int ipv6_route_del(struct in6_rtmsg *rtmsg)
-{
-	struct rt6_info * rt;
-	int res = -ENOENT;
-	
-	atomic_inc(&rt6_lock);
-	
-	rt = fib6_lookup_1(&rtmsg->rtmsg_dst, 0);
-	
-	if (rt && (rt->rt_prefixlen == rtmsg->rtmsg_prefixlen))
-	{
-		int test;
-		
-		start_bh_atomic();
-		
-		test = (rt6_lock == 1);
-		
-		if (test)
-		{
-			res = fib6_del_rt(rt);
-		}
-		end_bh_atomic();
-
-		if (!test)
-		{
-			struct rt6_req *request;
-			
-			request = kmalloc(sizeof(struct rt6_req), GFP_KERNEL);
-			
-			if (!request)
-			{
-				res = -ENOMEM;
-				goto out;
-			}
-			request->operation = RT_OPER_DEL;
-			request->ptr = rt;
-			request->next = request->prev = NULL;
-			rtreq_queue(request);
-			rt6_bh_mask |= RT_BH_REQUEST;
-			res = 0;
-		}
-	}
-  out:
-	atomic_dec(&rt6_lock);
-	rt6_run_bh();
-	return res;
-}
-
-/*
- *	search the routing table
- *	the flags parameter restricts the search to entries where
- *	the flag is *not* set
- */
-struct rt6_info * fibv6_lookup(struct in6_addr *addr, struct device *src_dev,
-			       int flags)
-{
-	struct rt6_info *rt;
-
-	atomic_inc(&rt6_lock);
-	
-	if ((rt = fib6_lookup_1(addr, flags)))
-	{
-		if (src_dev)
-		{
-			struct rt6_info *sprt;
-
-			for (sprt=rt; sprt; sprt=sprt->next)
-			{
-				if (sprt->rt_dev == src_dev)
-				{
-					rt = sprt;
-					goto out;
-				}
-			}
-			
-			if (flags & RTI_DEVRT)
-			{
-				rt = NULL;
-			}
-		}
-
-		goto out;
-	}
-
-	if (!(flags & RTI_GATEWAY))
-	{
-		if ((rt = dflt_rt_lookup()))
-		{
-			goto out;
-		}
-
-		rt = last_resort_rt;
-	}
-  out:
-	atomic_dec(&rt6_lock);
-	return rt;
-}
-
-/*
- *	Destination Cache
- */
-
-struct dest_entry * ipv6_dst_route(struct in6_addr * daddr,
-				   struct device *src_dev,
-				   int flags)
-{
-	struct dest_entry * dc = NULL;
-	struct rt6_info * rt;
-
-	atomic_inc(&rt6_lock);
-	
-	rt = fibv6_lookup(daddr, src_dev, flags);
-
-	if (rt == NULL)
-	{
-		goto exit;
-	}
-	
-	if (rt->rt_nexthop)
-	{
-		/*
-		 *	We can use the generic route
-		 *	(warning: the pmtu value maybe invalid)
-		 */
-
-		dc = (struct dest_entry *) rt;
-		atomic_inc(&rt->rt_use);
-	}
-	else
-	{
-		struct rt6_req *request;
-
-		if (ipv6_chk_addr(daddr) && !(rt->rt_dev->flags & IFF_LOOPBACK))
-		{
-			rt = loopback_rt;
-
-			if (rt == NULL)
-			{
-				goto exit;
-			}
-		}
-
-		/*
-		 *	dynamicly allocate a new route
-		 */
-		
-		dc = (struct dest_entry *) kmalloc(sizeof(struct dest_entry), 
-					   GFP_ATOMIC);
-
-		if (dc == NULL)
-		{
-			printk(KERN_WARNING "dst_route: kmalloc failed\n");
-			goto exit;
-		}
-
-		rt6_stats.fib_rt_alloc++;
-		rt6_stats.fib_dc_alloc++;
-
-		memset(dc, 0, sizeof(struct dest_entry));
-
-		memcpy(&dc->dc_addr, daddr, sizeof(struct in6_addr));
-		dc->rt.rt_prefixlen = 128;
-		dc->dc_usecnt = 1;
-		dc->rt.rt_metric = rt->rt_metric;
-
-		dc->dc_flags = (rt->rt_flags | RTF_HOST | RTI_DYNAMIC |
-				RTI_DCACHE | DCF_PMTU);
-
-		dc->dc_pmtu = rt->rt_dev->mtu;
-		dc->rt.rt_dev = rt->rt_dev;
-		dc->rt.rt_output_method = rt->rt_output_method;
-		dc->dc_tstamp = jiffies;
-		/* add it to the request queue */
-		
-		request = kmalloc(sizeof(struct rt6_req), GFP_ATOMIC);
-
-		if (request == NULL)
-		{
-			printk(KERN_WARNING "dst_route: kmalloc failed\n");
-			dc = NULL;
-			goto exit;
-		}
-
-		dc->dc_nexthop = ndisc_get_neigh(rt->rt_dev, daddr);
-
-		rt6_bh_mask |= RT_BH_REQUEST;
-		
-		request->operation = RT_OPER_ADD;
-		request->ptr = (struct rt6_info *) dc;
-		request->next = request->prev = NULL;
-		rtreq_queue(request);
-	}
-
-	atomic_inc(&rt_clients);
-
-  exit:
-       
-	atomic_dec(&rt6_lock);
-	rt6_run_bh();
-
-	return dc;
-}
-
-/*
- *	check cache entry for vality...
- *	this needs to be done as a inline func that calls
- *	ipv6_slow_dst_check if entry is invalid
- */
-
-struct dest_entry * ipv6_dst_check(struct dest_entry *dc,
-				   struct in6_addr *daddr,
-				   __u32 sernum, int flags)
-{
-	int uptodate = 0;
-
-	/*
-	 *	destination cache becomes invalid when routing
-	 *	changes or a more specific dynamic entry is
-	 *	created.
-	 *	if route is removed from table fib_node will
-	 *	become NULL
-	 */
-
-	if (dc->rt.fib_node && (dc->rt.fib_node->fn_sernum == sernum))
-		uptodate = 1;
-
-	if (uptodate && ((dc->dc_flags & DCF_INVALID) == 0))
-	{
-		if (dc->dc_nexthop && !(dc->dc_nexthop->flags & NCF_NOARP))
-		{
-			ndisc_event_send(dc->dc_nexthop, NULL);
-		}
-		return dc;
-	}
-
-	/* route for destination may have changed */
-
-	ipv6_dst_unlock(dc);
-
-	return ipv6_dst_route(daddr, NULL, flags);
-}
-
-void ipv6_dst_unlock(struct dest_entry *dc)
-{
-	/*
-	 *	decrement counter and mark entry for deletion
-	 *	if counter reaches 0. we delay deletions in hope
-	 *	we can reuse cache entries.
-	 */
-
-	atomic_dec(&dc->dc_usecnt);
-	
-	if (dc->dc_usecnt == 0)
-	{
-
-		if (dc->dc_flags & RTI_DCACHE)
-		{
-			/*
-			 *	update last usage tstamp
-			 */
-
-			dc->dc_tstamp = jiffies;
-			rt6_bh_mask |= RT_BH_GC;
-		}
-
-		if (dc->rt.rt_ref == 0)
-		{
-			/*
-			 *	entry out of the routing table
-			 *	pending to be released on last deref
-			 */
-
-			if (dc->dc_nexthop)
-			{
-				neighbour_unlock(dc->dc_nexthop);
-			}
-			
-			if (dc->dc_flags & RTI_DCACHE)
-			{
-				rt6_stats.fib_dc_alloc--;
-			}
-
-			rt6_stats.fib_rt_alloc--;
-			kfree(dc);
-		}
-
-	}
-
-	atomic_dec(&rt_clients);
-}
-
-/*
- *	Received a packet too big icmp that lowers the mtu for this
- *	address. If the route for the destination is genric we create
- *	a new route with the apropriate MTU info. The route_add
- *	procedure will update the serial number on the generic routes
- *	belonging to the afected tree forcing clients to request a route
- *	lookup.
- */
-void rt6_handle_pmtu(struct in6_addr *addr, int pmtu)
-{
-	struct rt6_info *rt;
-	struct rt6_req *req;
-	struct dest_entry *dc;
-
-	printk(KERN_DEBUG "rt6_handle_pmtu\n");
-
-	if (pmtu < 0 || pmtu > 65536)
-	{
-		printk(KERN_DEBUG "invalid MTU value\n");
-		return;
-	}
-
-	rt = fibv6_lookup(addr, NULL, 0);
-
-	if (rt == NULL)
-	{
-		printk(KERN_DEBUG "rt6_handle_pmtu: route not found\n");
-		return;
-	}
-
-	if (rt->rt_flags & RTI_DCACHE)
-	{
-		/*
-		 *	we do have a destination cache entry for this
-		 *	address.
-		 */
-		
-		dc = (struct dest_entry *) rt;
-		
-		/*
-		 *	fixme: some sanity checks are likely to be needed
-		 *	 here
-		 */
-
-		dc->dc_pmtu = pmtu;
-		dc->dc_flags |= DCF_PMTU;
-		return;
-	}
-
-	req = (struct rt6_req *) kmalloc(sizeof(struct rt6_req), GFP_ATOMIC);
-
-	/* now add the new destination cache entry	*/
-	
-	dc = (struct dest_entry *) kmalloc(sizeof(struct dest_entry),
-					   GFP_ATOMIC);
-	
-	rt6_stats.fib_rt_alloc++;
-	rt6_stats.fib_dc_alloc++;
-
-	memset(dc, 0, sizeof(struct dest_entry));
-	
-	memcpy(&dc->dc_addr, addr, sizeof(struct in6_addr));
-	dc->rt.rt_prefixlen = 128;
-	dc->rt.rt_metric = rt->rt_metric;
-
-	dc->dc_flags = (rt->rt_flags | RTI_DYNAMIC | RTI_DCACHE | DCF_PMTU |
-			RTF_HOST);
-
-	dc->dc_pmtu = pmtu;
-	dc->dc_tstamp = jiffies;
-	
-	dc->dc_nexthop = rt->rt_nexthop;
-	atomic_inc(&dc->dc_nexthop->refcnt);
-
-	dc->rt.rt_dev = rt->rt_dev;
-	dc->rt.rt_output_method = rt->rt_output_method;
-
-	req->operation = RT_OPER_ADD;
-	req->ptr = (struct rt6_info *) dc;
-	req->next = req->prev = NULL;
-
-	rtreq_queue(req);
-
-	rt6_bh_mask |= RT_BH_REQUEST;
-
-	rt6_run_bh();
-}
-
-/*
- *	Redirect received: target is nexthop for dest
- */
-struct rt6_info * ipv6_rt_redirect(struct device *dev, struct in6_addr *dest,
-				   struct in6_addr *target, int on_link)
-				       
-{
-	struct rt6_info *rt;
-	struct rt6_req *req;
-	int metric;
-
-	rt = fibv6_lookup(dest, dev, 0);
-
-	if (rt == NULL)
-	{
-		printk(KERN_WARNING "rt_redirect: unable to locate route\n");
-		return NULL;
-	}
-
-	metric = rt->rt_metric;
-
-	if ((rt->rt_flags & RTF_HOST) == 0)
-	{
-		/* Need to create an host route for this address */
-		
-		rt = (struct rt6_info *) kmalloc(sizeof(struct rt6_info),
-						 GFP_ATOMIC);
-		memset(rt, 0, sizeof(struct rt6_info));
-		ipv6_addr_copy(&rt->rt_dst, dest);
-		rt->rt_prefixlen = 128;
-		rt->rt_flags = RTF_HOST | RTF_UP;
-		rt->rt_dev = dev;
-
-		/*
-		 *	clone rt->rt_output_method ?
-		 */
-
-		rt->rt_metric = metric;
-
-		rt6_stats.fib_rt_alloc++;
-
-		req = (struct rt6_req *) kmalloc(sizeof(struct rt6_req),
-						 GFP_ATOMIC);
-		req->operation = RT_OPER_ADD;
-		req->ptr  = rt;
-		req->next = req->prev = NULL;
-		
-		rtreq_queue(req);
-		rt6_bh_mask |= RT_BH_REQUEST;
-	}
-	else
-	{
-		rt->rt_flags |= RTF_MODIFIED;
-	}
-
-	rt->rt_flags |= RTF_DYNAMIC;
- 
-	if (on_link)
-	{
-		rt->rt_flags &= ~RTF_GATEWAY;
-	}
-	else
-	{
-		rt->rt_flags |= RTF_GATEWAY;
-	}
-
-	if (rt->rt_nexthop)
-	{
-		struct nd_neigh *ndn = (struct nd_neigh *) rt->rt_nexthop;
-		
-		if (ipv6_addr_cmp(&ndn->ndn_addr, target) == 0)
-		{
-			rt->rt_nexthop = neighbour_clone(rt->rt_nexthop);
-			goto exit;
-		}
-		else
-		{
-			neighbour_unlock(rt->rt_nexthop);
-		}
-	}
-	
-	rt->rt_nexthop = ndisc_get_neigh(dev, target);
-
-  exit:
-	rt6_run_bh();
-	return rt;
-}
-
-static int dcache_gc_node(struct fib6_node *fn, int timeout)
-{
-	struct rt6_info *rt, *back;
-	int more = 0;
-	unsigned long now = jiffies;
-
-	back = NULL;
-
-	for (rt = fn->leaf; rt;)
-	{
-		if ((rt->rt_flags & RTI_DCACHE) && rt->rt_use == 0)
-		{
-			struct dest_entry *dc;
-			
-			dc = (struct dest_entry *) rt;
-			
-			if (now - dc->dc_tstamp > timeout)
-			{
-				struct rt6_info *old;
-
-				old = rt;
-
-				rt = rt->next;
-
-				if (back == NULL)
-				{
-					fn->leaf = rt;
-				}
-				else
-				{
-					back->next = rt;
-				}
-
-				old->fib_node = NULL;
-				rt_release(old);
-				rt6_stats.fib_rt_entries--;
-				continue;
-			}
-			else
-			{
-				more++;
-			}
-		}
-
-		back = rt;
-		rt = rt->next;
-	}
-
-	if (fn->leaf == NULL)
-	{
-		return -1;
-	}
-	return more;
-}
-
-struct dc_gc_args {
-	unsigned long	timeout;
-	int		more;
-};
-
-static void dc_garbage_collect(struct fib6_node *fn, void *p_arg)
-{
-	struct dc_gc_args * args = (struct dc_gc_args *) p_arg;
-	
-	if (fn->fn_flags & RTN_BACKTRACK)
-	{
-		if (fn->fn_bit == 127)
-		{
-			int more;
-						
-			more = dcache_gc_node(fn, args->timeout);
-
-			if (more == -1)
-			{
-				if (fn->parent->left == fn)
-					fn->parent->left = NULL;
-				else
-					fn->parent->right = NULL;
-				
-				kfree(fn);
-
-				rt6_stats.fib_nodes--;
-				rt6_stats.fib_route_nodes--;
-				
-				return;
-			}
-			args->more += more;
-		}
-	}
-	else if (!(fn->fn_flags & RTN_ROOT))
-	{
-		int children = 0;
-		struct fib6_node *chld = NULL;
-
-		if (fn->left)
-		{
-			children++;
-			chld = fn->left;			
-		}
-			
-		if (fn->right)
-		{
-			children++;
-			chld = fn->right;
-		}
-		
-		if (children <= 1)
-		{			
-			struct fib6_node *pn = fn->parent;
-			
-			if (pn->left == fn)
-			{
-				pn->left = chld;
-			}
-			else
-			{
-				pn->right = chld;
-			}
-			
-			if (chld)
-			{
-				chld->parent = pn;
-			}
-			
-			rt_release(fn->leaf);
-			
-			rt6_stats.fib_nodes--;
-			kfree(fn);
-		}	       
-	}
-}
-
-/*
- *	called with ints off
- */
-
-static void __rt6_run_bh(void)
-{
-	static last_gc_run = 0;
-
-	if (rt6_bh_mask & RT_BH_REQUEST)
-	{
-		struct rt6_req *request;
-
-		while ((request = rtreq_dequeue()))
-		{
-			struct rt6_info *rt;
-
-			rt = request->ptr;
-
-			switch (request->operation) {
-			case RT_OPER_ADD:
-				fib6_add_1(rt);
-				break;
-
-			case RT_OPER_DEL:
-				fib6_del_rt(rt);
-				break;
-
-			default:
-				printk(KERN_WARNING
-				       "rt6_run_bh: bad request in queue\n");
-			}
-
-			kfree(request);
-		}
-
-		rt6_bh_mask &= ~RT_BH_REQUEST;
-	}
-
-	if (rt6_bh_mask & RT_BH_GC)
-	{
-		if (jiffies - last_gc_run > DC_TIME_RUN)
-		{
-			struct dc_gc_args args;
-
-			if (rt6_stats.fib_dc_alloc >= DC_WATER_MARK)
-				args.timeout = DC_SHORT_TIMEOUT;
-			else
-				args.timeout = DC_LONG_TIMEOUT;
-
-			args.more = 0;
-			rt6_walk_tree(dc_garbage_collect, &args, RT6_FILTER_NONE);
-
-			last_gc_run = jiffies;
-			
-			if (!args.more)
-			{
-				rt6_bh_mask &= ~RT_BH_GC;
-			}
-		}
-	}
-}
-
-/*
- *	Timer for expiring routes learned via addrconf and stale DC 
- *	entries when there is no network actuvity
- */
-
-void rt6_timer_handler(unsigned long data)
-{
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
-
-	if (rt6_lock == 0)
-	{
-		if (rt_clients == 0 && rt6_bh_mask)
-		{
-			__rt6_run_bh();
-		}
-
-		/*
-		 *	route expiry
-		 */
-		
-		rt6_walk_tree(rt6_rt_timeout, NULL, RT6_FILTER_RTNODES);
-	}
-
-	restore_flags(flags);
-
-	rt6_gc_timer.expires = jiffies + 4 * DC_LONG_TIMEOUT;
-	add_timer(&rt6_gc_timer);
-}
-
-/*
- *	Check if routes should be timed out.
- *	Called from rt6_walk_tree for every node.
- */
-
-static void rt6_rt_timeout(struct fib6_node *fn, void *arg)
-{
-	struct rt6_info *rt;
-	unsigned long now = jiffies;
-
-	for (rt = fn->leaf; rt; rt = rt->next)
-	{
-		if ((rt->rt_flags & RTF_ADDRCONF) && now > rt->rt_expires)
-		{
-			struct rt6_req *req;
-
-			/*
-			 *	request route deletion. routes will only
-			 *	be deleted after walk_tree completes
-			 */
-
-			req = (struct rt6_req *) kmalloc(sizeof(struct rt6_req),
-							 GFP_ATOMIC);
-			req->operation = RT_OPER_DEL;
-			req->ptr  = rt;
-			req->next = req->prev = NULL;
-		}
-	}
-}
-
-static void rt6_sndrtmsg(struct in6_rtmsg *rtmsg)
-{
-	struct sk_buff *skb;
-	
-	skb = alloc_skb(sizeof(struct in6_rtmsg), GFP_ATOMIC);
-	if (skb == NULL)
-		return;
-
-	memcpy(skb_put(skb, sizeof(struct in6_rtmsg)), &rtmsg,
-	       sizeof(struct in6_rtmsg));
-	
-	if (netlink_post(NETLINK_ROUTE6, skb))
-	{
-		kfree_skb(skb, FREE_WRITE);
-	}
-}
-
-int ipv6_route_ioctl(unsigned int cmd, void *arg)
-{
-	struct in6_rtmsg rtmsg;
-	int err;
-
-	switch(cmd) 
-	{
-		case SIOCADDRT:		/* Add a route */
-		case SIOCDELRT:		/* Delete a route */
-			if (!suser())
-				return -EPERM;
-			err = copy_from_user(&rtmsg, arg,
-					     sizeof(struct in6_rtmsg));
-			if (err)
-				return -EFAULT;
-			
-			err = (cmd == SIOCDELRT) ? ipv6_route_del(&rtmsg) :
-			      ipv6_route_add(&rtmsg);
-
-			if (err == 0)
-			{
-				rt6_sndrtmsg(&rtmsg);
-			}
-			return err;
-	}
-
-	return -EINVAL;
-}
-
-static void rt6_ifdown_scan(struct fib6_node *fn, void *arg)
-{
-	struct rt6_info *rt;
-	struct device *dev = (struct device *) arg;
-
-	for (rt = fn->leaf; rt; rt=rt->next)
-	{
-		if (((rt->rt_flags & RTI_DCACHE) == 0) && rt->rt_dev == dev)
-		{
-			struct rt6_req *req;
-
-			req = kmalloc(sizeof(struct rt6_req), GFP_ATOMIC);
-			req->operation = RT_OPER_DEL;
-			req->ptr  = rt;
-			req->next = req->prev = NULL;
-			rt6_bh_mask |= RT_BH_REQUEST;
-		}
-	}
-}
-
-void rt6_ifdown(struct device *dev)
-{
-	rt6_walk_tree(rt6_ifdown_scan, (void *) dev, RT6_FILTER_RTNODES);
-}
-
-static void rt6_walk_tree(f_pnode func, void * arg, int filter)
-{
-	struct fib6_node *fn;
-	/*
-	 *	adquire lock
-	 *	this warranties that the operation will be atomic with
-	 *	respect to the garbage collect routine that also does
-	 *	a tree transversal and tags nodes with the RTN_TAG flag
-	 */
-	atomic_inc(&rt6_lock);
-
-	fn = &routing_table;
-
-	do {
-		if (!(fn->fn_flags & RTN_TAG))
-		{
-			fn->fn_flags |= RTN_TAG;
-
-			if (fn->left)
-			{
-				fn = fn->left;
-				continue;
-			}
-		}
-
-		fn->fn_flags &= ~RTN_TAG;
-
-		if (fn->right)
-		{
-			fn = fn->right;
-			continue;
-		}
-	       		
-		do {
-			struct fib6_node *node;
-			
-			if (fn->fn_flags & RTN_ROOT)
-				break;
-			node = fn;
-			fn = fn->parent;
-			
-			if (!(node->fn_flags & RTN_TAG) && 
-			    (!filter || (node->fn_flags & RTN_BACKTRACK)))
-			{
-				(*func)(node, arg);
-			}
-
-		} while (!(fn->fn_flags & RTN_TAG));
-		
-	} while (!(fn->fn_flags & RTN_ROOT) || (fn->fn_flags & RTN_TAG));
-
-	atomic_dec(&rt6_lock);
-}
-
-#ifdef CONFIG_PROC_FS
-#define RT6_INFO_LEN (32 + 2 + 32 + 2 + 2 + 2 + 4 + 8 + 7 + 1)
-
-struct rt6_proc_arg {
-	char *buffer;
-	int offset;
-	int length;
-	int skip;
-	int len;
-};
-
-static void rt6_info_node(struct fib6_node *fn, void *p_arg)
-{
-	struct rt6_info *rt;
-	struct rt6_proc_arg *arg = (struct rt6_proc_arg *) p_arg;
-
-	for (rt = fn->leaf; rt; rt = rt->next)
-	{
-		int i;
-
-		if (arg->skip < arg->offset / RT6_INFO_LEN)
-		{
-			arg->skip++;
-			continue;
-		}
-
-		if (arg->len >= arg->length)
-			return;
-		
-		for (i=0; i<16; i++)
-		{
-			sprintf(arg->buffer + arg->len, "%02x",
-				rt->rt_dst.s6_addr[i]);
-			arg->len += 2;
-		}
-		arg->len += sprintf(arg->buffer + arg->len, " %02x ",
-				    rt->rt_prefixlen);
-		if (rt->rt_nexthop)
-		{
-			for (i=0; i<16; i++)
-			{
-				struct nd_neigh *ndn;
-
-				ndn = (struct nd_neigh *) rt->rt_nexthop;
-				sprintf(arg->buffer + arg->len, "%02x",
-					ndn->ndn_addr.s6_addr[i]);
-				arg->len += 2;
-			}
-		}
-		else
-		{
-			sprintf(arg->buffer + arg->len,
-				"00000000000000000000000000000000");
-			arg->len += 32;
-		}
-		arg->len += sprintf(arg->buffer + arg->len,
-				    " %02x %02x %02x %04x %8s\n",
-				    rt->rt_metric, rt->rt_use,
-				    rt->rt_ref, rt->rt_flags, 
-				    rt->rt_dev ? rt->rt_dev->name : "");
-	}
-}
-
-static int rt6_proc_info(char *buffer, char **start, off_t offset, int length,
-			 int dummy)
-{
-	struct rt6_proc_arg arg;
-	struct fib6_node sfn;
-	arg.buffer = buffer;
-	arg.offset = offset;
-	arg.length = length;
-	arg.skip = 0;
-	arg.len = 0;
-
-	rt6_walk_tree(rt6_info_node, &arg, RT6_FILTER_RTNODES);
-	
-	sfn.leaf = default_rt_list;
-	rt6_info_node(&sfn, &arg);
-
-	sfn.leaf = last_resort_rt;
-	rt6_info_node(&sfn, &arg);
-			     
-	*start = buffer;
-
-	if (offset)
-		*start += offset % RT6_INFO_LEN;
-
-	arg.len -= offset % RT6_INFO_LEN;
-
-	if (arg.len > length)
-		arg.len = length;
-
-	return arg.len;
-}
-
-
-static int rt6_proc_stats(char *buffer, char **start, off_t offset, int length,
-			  int dummy)
-{
-	int len;
-
-	len = sprintf(buffer, "%04x %04x %04x %04x %04x\n",
-		      rt6_stats.fib_nodes, rt6_stats.fib_route_nodes,
-		      rt6_stats.fib_rt_alloc, rt6_stats.fib_rt_entries,
-		      rt6_stats.fib_dc_alloc);
-
-	len -= offset;
-
-	if (len > length)
-		len = length;
-
-	*start = buffer + offset;
-
-	return len;
-}
-
-#endif			/* CONFIG_PROC_FS */
-
-/*
- *	init/cleanup code
- *
- */
-
-void ipv6_route_init(void)
-{
-#ifdef 	CONFIG_PROC_FS
-	proc_net_register(&(struct proc_dir_entry) {
-		PROC_NET_RT6, 10, "ipv6_route",
-		S_IFREG | S_IRUGO, 1, 0, 0,
-		0, &proc_net_inode_operations,
-		rt6_proc_info
-	});
-	proc_net_register(&(struct proc_dir_entry) {
-		PROC_NET_RT6_STATS, 9, "rt6_stats",
-		S_IFREG | S_IRUGO, 1, 0, 0,
-		0, &proc_net_inode_operations,
-		rt6_proc_stats
-	});
-
-#endif
-	rt6_gc_timer.expires = jiffies + 4 * DC_LONG_TIMEOUT;
-	add_timer(&rt6_gc_timer);
-	netlink_attach(NETLINK_ROUTE6, rt6_msgrcv);
-}
-
-#ifdef MODULE
-void ipv6_route_cleanup(void)
-{
-	proc_net_unregister(PROC_NET_RT6);
-	proc_net_unregister(PROC_NET_RT6_STATS);
-	netlink_detach(NETLINK_ROUTE6);
-	del_timer(&rt6_gc_timer);
-	fib6_flush();
-}
-#endif
-
-/*
- *	NETLINK interface
- *	routing socket moral equivalent
- */
-
-static int rt6_msgrcv(int unit, struct sk_buff *skb)
-{
-	int count = 0;
-	struct in6_rtmsg *rtmsg;
-	
-	while (skb->len)
-	{
-		if (skb->len < sizeof(struct in6_rtmsg))
-		{
-			count = -EINVAL;
-			goto out;
-		}
-		
-		rtmsg = (struct in6_rtmsg *) skb->data;
-		skb_pull(skb, sizeof(struct in6_rtmsg));
-		count += sizeof(struct in6_rtmsg);
-
-		switch (rtmsg->rtmsg_type) {
-		case RTMSG_NEWROUTE:
-			ipv6_route_add(rtmsg);
-			break;
-		case RTMSG_DELROUTE:
-			ipv6_route_del(rtmsg);
-			break;
-		default:
-			count = -EINVAL;
-			goto out;
-		}
-	}
-
-  out:
-	kfree_skb(skb, FREE_READ);	
-	return count;
-}
-
-void rt6_sndmsg(__u32 type, struct in6_addr *dst, struct in6_addr *gw,
-		__u16 plen, struct device *dev, __u16 metric, __u16 flags)
-{
-	struct sk_buff *skb;
-	struct in6_rtmsg *msg;
-	int ifindex = 0;
-	
-	skb = alloc_skb(sizeof(struct in6_rtmsg), GFP_ATOMIC);
-	if (skb == NULL)
-		return;
-
-
-	msg = (struct in6_rtmsg *) skb_put(skb, sizeof(struct in6_rtmsg));
-	
-	msg->rtmsg_type = type;
-
-	if (dst)
-	{
-		ipv6_addr_copy(&msg->rtmsg_dst, dst);
-	}
-	else
-		memset(&msg->rtmsg_dst, 0, sizeof(struct in6_addr));
-
-	if (gw)
-	{
-		ipv6_addr_copy(&msg->rtmsg_gateway, gw);
-	}
-	else
-		memset(&msg->rtmsg_gateway, 0, sizeof(struct in6_addr));
-
-	msg->rtmsg_prefixlen = plen;
-	msg->rtmsg_metric = metric;
-
-	if (dev)
-	{
-		struct inet6_dev *idev;
-
-		idev = ipv6_get_idev(dev);
-		if (idev)
-		{
-			ifindex = idev->if_index;
-		}
-	}
-	
-	msg->rtmsg_ifindex = ifindex;
-
-	msg->rtmsg_flags = flags;
-
-	if (netlink_post(NETLINK_ROUTE6, skb))
-	{
-		kfree_skb(skb, FREE_WRITE);
-	}
-}

FUNET's LINUX-ADM group, [email protected]
TCL-scripts by Sam Shen, [email protected]