patch-2.1.68 linux/net/ipv4/fib.c

Next file: linux/net/ipv4/fib_frontend.c
Previous file: linux/net/ipv4/devinet.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.67/linux/net/ipv4/fib.c linux/net/ipv4/fib.c
@@ -1,2077 +0,0 @@
-/*
- * INET		An implementation of the TCP/IP protocol suite for the LINUX
- *		operating system.  INET is implemented using the  BSD Socket
- *		interface as the means of communication with the user level.
- *
- *		IPv4 Forwarding Information Base.
- *
- * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
- *
- *		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.
- *
- *
- *	NOTE:	This file is scheduled to be removed from kernel.
- *		The natural place for router FIB is user level
- *		routing daemon (it has to keep its copy in any case)
- *		
- *		Kernel should keep only interface routes and,
- *		if host is not router, default gateway.
- *
- *		We have good proof that it is feasible and efficient -
- *		multicast routing.
- */
-
-#include <linux/config.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <asm/bitops.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/mm.h>
-#include <linux/string.h>
-#include <linux/socket.h>
-#include <linux/sockios.h>
-#include <linux/errno.h>
-#include <linux/in.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
-#include <linux/if_arp.h>
-#include <linux/proc_fs.h>
-#include <linux/skbuff.h>
-#include <linux/init.h>
-
-#include <net/ip.h>
-#include <net/protocol.h>
-#include <net/route.h>
-#include <net/tcp.h>
-#include <net/sock.h>
-#include <net/icmp.h>
-#include <net/arp.h>
-#include <net/netlink.h>
-#include <net/ip_fib.h>
-#include <net/dst.h>
-#include <linux/net_alias.h>
-
-static struct fib_class local_class = {RT_CLASS_LOCAL, };
-static struct fib_class default_class = {RT_CLASS_DEFAULT, };
-static struct fib_class main_class = {RT_CLASS_MAIN, };
-static struct fib_class *fib_classes[RT_CLASS_MAX+1];
-
-static struct fib_rule  *fib_rules;
-
-static struct fib_info 	*fib_info_list;
-
-static int fib_stamp;
-
-static int rtmsg_process(struct nlmsghdr *n, struct in_rtmsg *r);
-
-
-#ifdef CONFIG_RTNETLINK
-
-static unsigned rt_nl_flags;
-static int rt_nl_owner = -1;
-
-/*
- *	Default mode is delayed for 0.5sec batch delivery.
- *	If someone starts to use user->level calls,
- *	we turn on synchronous message passing.
- */
-
-#define RTMSG_DELAY (HZ/2)
-
-static struct nlmsg_ctl rtmsg_ctl = {
-	{ NULL, NULL, 0, 0L, NULL },
-	NULL,
-	NETLINK_ROUTE,
-	RTMSG_DELAY,
-	NLMSG_GOODSIZE,
-	0, 0, 0, 0
-};
-
-static void __rtmsg_ack(struct nlmsghdr *n, int err);
-
-static __inline__ void rtmsg_ack(struct nlmsghdr *n, int err)
-{
-	if (n->nlmsg_seq && rt_nl_flags&RTCTL_ACK)
-		__rtmsg_ack(n, err);
-}
-
-static void rtmsg_fib(unsigned long type, struct fib_node *f, int logmask,
-		      struct fib_class *class, struct nlmsghdr *n);
-static void rtmsg_dev(unsigned long type, struct device *dev, struct nlmsghdr *n);
-#define rtmsg_kick() ({ if (rtmsg_ctl.nlmsg_skb) nlmsg_transmit(&rtmsg_ctl); })
-
-#else
-#define rtmsg_fib(a,b,c,d,e)
-#define rtmsg_dev(a,b,c)
-#define rtmsg_ack(a,b)
-#define rtmsg_kick()
-#endif
-
-
-/*
- *	FIB locking.
- */
-
-static struct wait_queue *fib_wait;
-static atomic_t fib_users = ATOMIC_INIT(0);
-
-static void fib_lock(void)
-{
-	while (atomic_read(&fib_users))
-		sleep_on(&fib_wait);
-	atomic_inc(&fib_users);
-	dev_lock_list();
-}
-
-static void fib_unlock(void)
-{
-	dev_unlock_list();
-	if (atomic_dec_and_test(&fib_users)) {
-		rtmsg_kick();
-		wake_up(&fib_wait);
-	}
-}
-
-/*
- *	Check if a mask is acceptable.
- */
- 
-static __inline__ int bad_mask(u32 mask, u32 addr)
-{
-	if (addr & (mask = ~mask))
-		return 1;
-	mask = ntohl(mask);
-	if (mask & (mask+1))
-		return 1;
-	return 0;
-}
-
-/* 
- * Evaluate mask length.
- */
-
-static __inline__ int fib_logmask(u32 mask)
-{
-	if (!(mask = ntohl(mask)))
-		return 32;
-	return ffz(~mask);
-}
-
-/* 
- * Create mask from mask length.
- */
-
-static __inline__ u32 fib_mask(int logmask)
-{
-	if (logmask >= 32)
-		return 0;
-	return htonl(~((1<<logmask)-1));
-}
-
-static __inline__ u32 fib_netmask(int logmask)
-{
-	return fib_mask(32-logmask);
-}
-
-
-static struct fib_class *fib_alloc_class(int id)
-{
-	struct fib_class *class;
-
-	if (fib_classes[id])
-		return fib_classes[id];
-
-	class = kmalloc(sizeof(*class), GFP_KERNEL);
-	if (!class)
-		return NULL;
-	memset(class, 0, sizeof(*class));
-	class->cl_id = id;
-	fib_classes[id] = class;
-	return class;
-}
-
-static struct fib_class *fib_empty_class(void)
-{
-	int id;
-	for (id = 1; id <= RT_CLASS_MAX; id++)
-		if (fib_classes[id] == NULL)
-			return fib_alloc_class(id);
-	return NULL;
-}
-
-static int fib_rule_delete(struct in_rtrulemsg *r, struct device *dev, struct nlmsghdr *n)
-{
-	u32 src = r->rtrmsg_src.s_addr;
-	u32 dst = r->rtrmsg_dst.s_addr;
-	u32 srcmask = fib_netmask(r->rtrmsg_srclen);
-	u32 dstmask = fib_netmask(r->rtrmsg_dstlen);
-	struct fib_rule *cl, **clp;
-
-	for (clp=&fib_rules; (cl=*clp) != NULL; clp=&cl->cl_next) {
-		if (src == cl->cl_src &&
-		    srcmask == cl->cl_srcmask &&
-		    dst == cl->cl_dst &&
-		    dstmask == cl->cl_dstmask &&
-		    r->rtrmsg_tos == cl->cl_tos &&
-		    dev == cl->cl_dev &&
-		    r->rtrmsg_action == cl->cl_action &&
-		    (!r->rtrmsg_preference || r->rtrmsg_preference == cl->cl_preference) &&
-		    (!r->rtrmsg_class || (cl && r->rtrmsg_class == cl->cl_class->cl_id))) {
-			cli();
-			*clp = cl->cl_next;
-			sti();
-			if (cl->cl_class)
-				cl->cl_class->cl_users--;
-			kfree(cl);
-			return 0;
-		}
-	}
-	return -ESRCH;
-}
-
-static int fib_rule_add(struct in_rtrulemsg *r, struct device *dev, struct nlmsghdr *n)
-{
-	u32 src = r->rtrmsg_src.s_addr;
-	u32 dst = r->rtrmsg_dst.s_addr;
-	u32 srcmask = fib_netmask(r->rtrmsg_srclen);
-	u32 dstmask = fib_netmask(r->rtrmsg_dstlen);
-
-	struct fib_rule *cl, *new_cl, **clp;
-	struct fib_class *class = NULL;
-
-	if ((src&~srcmask) || (dst&~dstmask))
-		return -EINVAL;
-	if (dev && net_alias_main_dev(dev) != dev)
-		return -ENODEV;
-
-	if (!r->rtrmsg_class) {
-		if (r->rtrmsg_action==RTP_GO || r->rtrmsg_action==RTP_NAT
-		    || r->rtrmsg_action==RTP_MASQUERADE) {
-			if ((class = fib_empty_class()) == NULL)
-				return -ENOMEM;
-			class->cl_auto = 1;
-		} else if (r->rtrmsg_rtmsgs)
-			return -EINVAL;
-	} else if ((class = fib_alloc_class(r->rtrmsg_class)) == NULL)
-		return -ENOMEM;
-
-	new_cl = kmalloc(sizeof(*new_cl), GFP_KERNEL);
-	if (!new_cl)
-		return -ENOMEM;
-	new_cl->cl_src = src;
-	new_cl->cl_srcmask = srcmask;
-	new_cl->cl_dst = dst;
-	new_cl->cl_dstmask = dstmask;
-	new_cl->cl_dev = dev;
-	new_cl->cl_srcmap = r->rtrmsg_srcmap.s_addr;
-	new_cl->cl_tos = r->rtrmsg_tos;
-	new_cl->cl_action = r->rtrmsg_action;
-	new_cl->cl_flags = r->rtrmsg_flags;
-	new_cl->cl_preference = r->rtrmsg_preference;
-	new_cl->cl_class = class;
-	if (class)
-		class->cl_users++;
-
-	clp = &fib_rules;
-
-	if (!new_cl->cl_preference) {
-		cl = fib_rules;
-		if (cl && (cl = cl->cl_next) != NULL) {
-			clp = &fib_rules->cl_next;
-			if (cl->cl_preference)
-				new_cl->cl_preference = cl->cl_preference - 1;
-		}
-	}
-
-	while ( (cl = *clp) != NULL ) {
-		if (cl->cl_preference >= new_cl->cl_preference)
-			break;
-		clp = &cl->cl_next;
-	}
-
-	new_cl->cl_next = cl;
-	cli();
-	*clp = new_cl;
-	sti();
-
-	if (r->rtrmsg_rtmsgs) {
-		n->nlmsg_type = RTMSG_NEWROUTE;
-		r->rtrmsg_rtmsg->rtmsg_class = class->cl_id;
-		return rtmsg_process(n, r->rtrmsg_rtmsg);
-	}
-	return 0;
-}
-
-
-#define FZ_MAX_DIVISOR 1024
-
-static __inline__ u32 fib_hash(u32 key, u32 mask)
-{
-	u32 h;
-	h = key^(key>>20);
-	h = h^(h>>10);
-	h = h^(h>>5);
-	return h & mask;
-}
-
-static __inline__ struct fib_node ** fz_hash_p(u32 key, struct fib_zone *fz)
-{
-	return &fz->fz_hash[fib_hash(key, fz->fz_hashmask)];
-}
-
-static __inline__ struct fib_node * fz_hash(u32 key, struct fib_zone *fz)
-{
-	return fz->fz_hash[fib_hash(key, fz->fz_hashmask)];
-}
-
-/*
- * Free FIB node.
- */
-
-static void fib_free_node(struct fib_node * f)
-{
-	struct fib_info * fi = f->fib_info;
-	if (fi && !--fi->fib_refcnt) {
-#if RT_CACHE_DEBUG >= 2
-		printk("fib_free_node: fi %08x/%s is free\n", fi->fib_gateway, fi->fib_dev ? fi->fib_dev->name : "null");
-#endif
-		if (fi->fib_next)
-			fi->fib_next->fib_prev = fi->fib_prev;
-		if (fi->fib_prev)
-			fi->fib_prev->fib_next = fi->fib_next;
-		if (fi == fib_info_list)
-			fib_info_list = fi->fib_next;
-	}
-	kfree_s(f, sizeof(struct fib_node));
-}
-
-static __inline__ int fib_flags_trans(unsigned flags)
-{
-	if (flags & RTF_BROADCAST)
-		return IS_BROADCAST;
-	if (flags & RTF_MULTICAST)
-		return IS_MULTICAST;
-	if (flags & RTF_LOCAL)
-		return IS_MYADDR;
-	return 0;
-}
-
-unsigned ip_fib_chk_addr(u32 addr)
-{
-	struct fib_zone * fz;
-	struct fib_node * f;
-
-	/* 
-	 *	Accept both `all ones' and `all zeros' as BROADCAST. 
-	 *	(Support old BSD in other words). This old BSD 
-	 *	support will go very soon as it messes other things
-	 *	up.
-	 */
-
-	if (addr == INADDR_ANY || addr == 0xFFFFFFFF)
-		return RTF_LOCAL|RTF_BROADCAST;
-
-	if ((addr & htonl(0x7F000000L)) == htonl(0x7F000000L))
-		return RTF_LOCAL|RTF_INTERFACE;
-
-	if (MULTICAST(addr))
-		return RTF_MULTICAST;
-
-	addr = ntohl(addr);
-	for (fz = local_class.fib_zone_list; fz; fz = fz->fz_next) {
-		u32 key = (addr&fz->fz_mask)>>fz->fz_logmask;
-		for (f = fz_hash(key, fz); f; f = f->fib_next) {
-			if (key != f->fib_key || (f->fib_flag & FIBFLG_DOWN))
-				continue;
-			if (!f->fib_info)
-				return 0;
-			return f->fib_info->fib_flags&RTF_ADDRCLASSMASK;
-		}
-	}
-
-	return 0;
-}
-
-int __ip_chk_addr(unsigned long addr)
-{
-	return fib_flags_trans(ip_fib_chk_addr(addr));
-}
-
-/*
- *	Find the first device with a given source address.
- */
- 
-struct device *ip_dev_find(unsigned long addr, char *name)
-{
-	struct fib_zone * fz = local_class.fib_zones[0];
-	u32 key;
-	struct fib_node * f;
-
-	key = (ntohl(addr)&fz->fz_mask)>>fz->fz_logmask;
-	for (f = fz_hash(key, fz); f; f = f->fib_next) {
-		if (key == f->fib_key &&
-		    !(f->fib_flag & (FIBFLG_DOWN|FIBFLG_REJECT|FIBFLG_THROW)) &&
-		    f->fib_info->fib_flags == (RTF_IFLOCAL&~RTF_UP)) {
-			if (!name || strcmp(name, f->fib_info->fib_dev->name) == 0)
-				return f->fib_info->fib_dev;
-		}
-	}
-
-	return NULL;
-}
-
-/*
- *	Find tunnel with a given source and destination.
- */
- 
-struct device *ip_dev_find_tunnel(u32 daddr, u32 saddr)
-{
-	struct fib_zone * fz = local_class.fib_zones[0];
-	u32 key;
-	struct fib_node * f;
-
-	key = (ntohl(daddr)&fz->fz_mask)>>fz->fz_logmask;
-	for (f = fz_hash(key, fz); f; f = f->fib_next) {
-		if (key == f->fib_key &&
-		    !(f->fib_flag & (FIBFLG_DOWN|FIBFLG_REJECT|FIBFLG_THROW)) &&
-		    f->fib_info->fib_flags == (RTF_IFLOCAL&~RTF_UP)) {
-			struct device *dev = f->fib_info->fib_dev;
-			if (dev->type == ARPHRD_TUNNEL &&
-			    dev->pa_dstaddr == saddr)
-				return dev;
-		}
-		if (!f->fib_info)
-			return NULL;
-	}
-
-	return NULL;
-}
-
-
-int ip_fib_chk_default_gw(u32 addr, struct device *dev)
-{
-	struct fib_rule *cl;
-	struct fib_node * f;
-
-	for (cl = fib_rules; cl; cl = cl->cl_next) {
-		if (cl->cl_srcmask || cl->cl_dstmask || cl->cl_tos ||
-		    cl->cl_dev || cl->cl_action != RTP_GO || !cl->cl_class ||
-		    !cl->cl_class->fib_zones[32])
-			continue;
-		for (f = cl->cl_class->fib_zones[32]->fz_hash[0]; f; f = f->fib_next) {
-			struct fib_info *fi = f->fib_info;
-			if (!(f->fib_flag & (FIBFLG_DOWN|FIBFLG_REJECT|FIBFLG_THROW)) &&
-			    fi->fib_gateway == addr &&
-			    fi->fib_dev == dev &&
-			    fi->fib_flags&RTF_GATEWAY)
-				return 0;
-		}
-	}
-	return -1;
-}
-
-
-/*
- * Main lookup routine.
- */
-
-
-int
-fib_lookup(struct fib_result *res, u32 daddr, u32 src, u8 tos,
-	   struct device *devin, struct device *devout)
-{
-	struct fib_node * f;
-	struct fib_rule * cl;
-	u32		dst;
-	int		local = tos & 1;
-
-	tos &= IPTOS_TOS_MASK;
-	dst = ntohl(daddr);
-
-	for (cl = fib_rules; cl; cl=cl->cl_next) {
-		struct fib_zone * fz;
-
-		if (((src^cl->cl_src) & cl->cl_srcmask) ||
-		    ((daddr^cl->cl_dst) & cl->cl_dstmask) ||
-		    (cl->cl_tos && cl->cl_tos != tos) ||
-		    (cl->cl_dev && cl->cl_dev != devin))
-			continue;
-
-		switch (cl->cl_action) {
-		case RTP_GO:
-		case RTP_NAT:
-		case RTP_MASQUERADE:
-		default:
-			break;
-		case RTP_UNREACHABLE:
-			return -ENETUNREACH;
-		case RTP_DROP:
-			return -EINVAL;
-		case RTP_PROHIBIT:
-			return -EACCES;
-		}
-
-		for (fz = cl->cl_class->fib_zone_list; fz; fz = fz->fz_next) {
-			u32 key = (dst&fz->fz_mask)>>fz->fz_logmask;
-
-			for (f = fz_hash(key, fz); f; f = f->fib_next) {
-				if (key != f->fib_key ||
-				    (f->fib_flag & FIBFLG_DOWN) ||
-				    (f->fib_tos && f->fib_tos != tos))
-					continue;
-				if (f->fib_flag & FIBFLG_THROW)
-					goto next_class;
-				if (f->fib_flag & FIBFLG_REJECT)
-					return -ENETUNREACH;
-				if (devout && f->fib_info->fib_dev != devout)
-					continue;
-				if (!local || !(f->fib_info->fib_flags&RTF_GATEWAY)) {
-					res->f = f;
-					res->fr = cl;
-					res->fm = fz->fz_logmask;
-					return 0;
-				}
-			}
-		}
-next_class:
-	}
-	return -ENETUNREACH;
-}
-
-static int fib_autopublish(int op, struct fib_node *f, int logmask)
-{	
-	struct fib_zone *fz;
-	struct fib_node *f1;
-	struct arpreq r;
-	u32 addr = htonl(f->fib_key<<logmask);
-
-	if (f->fib_flag || LOOPBACK(addr) ||
-	    (!RT_LOCALADDR(f->fib_info->fib_flags) &&
-	     !(f->fib_info->fib_flags&RTF_NAT)))
-		return 0;
-
-	memset(&r, 0, sizeof(struct arpreq));
-	r.arp_flags = ATF_PUBL|ATF_PERM|ATF_MAGIC;
-	if (logmask)
-		r.arp_flags |= ATF_NETMASK;
-	((struct sockaddr_in*)&r.arp_pa)->sin_family = AF_INET;
-	((struct sockaddr_in*)&r.arp_pa)->sin_addr.s_addr = addr;
-	((struct sockaddr_in*)&r.arp_netmask)->sin_family = AF_INET;
-	((struct sockaddr_in*)&r.arp_netmask)->sin_addr.s_addr = fib_mask(logmask);
-
-	if (op)
-		return arp_req_set(&r, NULL);
-	
-	fz = local_class.fib_zones[logmask];
-
-	for (f1 = fz_hash(f->fib_key, fz); f1; f1=f1->fib_next)	{
-		if (f->fib_key != f1->fib_key || f1->fib_flag ||
-		    (!RT_LOCALADDR(f1->fib_info->fib_flags) &&
-		     !(f1->fib_info->fib_flags&RTF_NAT)))
-			continue;
-		return 0;
-	}
-
-	return arp_req_delete(&r, NULL);
-}
-
-#define FIB_SCAN(f, fp) \
-for ( ; ((f) = *(fp)) != NULL; (fp) = &(f)->fib_next)
-
-#define FIB_SCAN_KEY(f, fp, key) \
-for ( ; ((f) = *(fp)) != NULL && (f)->fib_key == (key); (fp) = &(f)->fib_next)
-
-#define FIB_CONTINUE(f, fp) \
-{ \
-	fp = &f->fib_next; \
-	continue; \
-}
-
-static int fib_delete(struct in_rtmsg * r, struct device *dev,
-		      struct fib_class *class, struct nlmsghdr *n)
-{
-	struct fib_node **fp, *f;
-	struct fib_zone *fz = class->fib_zones[32-r->rtmsg_prefixlen];
-	int logmask = 32 - r->rtmsg_prefixlen;
-	u32 dst = ntohl(r->rtmsg_prefix.s_addr);
-	u32 gw = r->rtmsg_gateway.s_addr;
-	short metric = r->rtmsg_metric;
-	u8 tos = r->rtmsg_tos;
-	u8 fibflg = 0;
-	int found=0;
-	unsigned flags;
-	u32 key;
-
-	flags = r->rtmsg_flags;
-	if (flags & RTF_REJECT)
-		fibflg |= FIBFLG_REJECT;
-	else if (flags & RTF_THROW)
-		fibflg |= FIBFLG_THROW;
-	flags &= ~(RTF_UP|RTF_REJECT|RTF_THROW);
-
-	if (fz != NULL)	{
-		key = (dst&fz->fz_mask)>>logmask;
-		fp = fz_hash_p(key, fz);
-
-		FIB_SCAN(f, fp) {
-			if (f->fib_key == key)
-				break;
-		}
-		FIB_SCAN_KEY(f, fp, key) {
-			if (f->fib_tos == tos)
-				break;
-		}
-
-		while ((f = *fp) != NULL && f->fib_key == key && f->fib_tos == tos) {
-			struct fib_info * fi = f->fib_info;
-
-			/*
-			 * If metric was not specified (<0), match all metrics.
-			 */
-			if (metric >= 0 && f->fib_metric != metric)
-				FIB_CONTINUE(f, fp);
-
-			if (flags & RTF_MAGIC) {
-				/* "Magic" deletions require exact match */
-				if (!fi || (fi->fib_flags^flags) ||
-				    fi->fib_dev != dev ||
-				    fi->fib_gateway != gw)
-					FIB_CONTINUE(f, fp);
-			} else {
-				/*
-				 * Device, gateway, reject and throw are
-				 * also checked if specified.
-				 */
-				if ((dev && fi && fi->fib_dev != dev) ||
-				    (gw  && fi && fi->fib_gateway != gw) ||
-				    (fibflg && (f->fib_flag^fibflg)&~FIBFLG_DOWN))
-					FIB_CONTINUE(f, fp);
-			}
-			cli();
-			/* It's interesting, can this operation be not atomic? */
-			*fp = f->fib_next;
-			sti();
-			if (class == &local_class)
-				fib_autopublish(0, f, logmask);
-			rtmsg_fib(RTMSG_DELROUTE, f, logmask, class, n);
-			fib_free_node(f);
-			found++;
-		}
-		fz->fz_nent -= found;
-	}
-
-	if (found) {
-		fib_stamp++;
-		rt_cache_flush(0);
-		rtmsg_ack(n, 0);
-		return 0;
-	}
-	rtmsg_ack(n, ESRCH);
-	return -ESRCH;
-}
-
-static struct fib_info * fib_create_info(struct device * dev, struct in_rtmsg *r)
-{
-	struct fib_info * fi;
-	unsigned flags = r->rtmsg_flags;
-	u32 gw = r->rtmsg_gateway.s_addr;
-	unsigned short mtu;
-	unsigned short irtt;
-	unsigned long  window;
-
-	mtu = dev ? dev->mtu : 0;
-	if (flags&RTF_MSS && r->rtmsg_mtu < mtu && r->rtmsg_mtu >= 68)
-		mtu = r->rtmsg_mtu;
-	window = (flags & RTF_WINDOW) ? r->rtmsg_window : 0;
-	irtt = (flags & RTF_IRTT) ? r->rtmsg_rtt : TCP_TIMEOUT_INIT;
-
-	flags &= RTF_FIB;
-
-	for (fi=fib_info_list; fi; fi = fi->fib_next) {
-		if (fi->fib_gateway != gw ||
-		    fi->fib_dev != dev  ||
-		    fi->fib_flags != flags ||
-		    fi->fib_mtu != mtu ||
-		    fi->fib_window != window ||
-		    fi->fib_irtt != irtt)
-			continue;
-		fi->fib_refcnt++;
-#if RT_CACHE_DEBUG >= 2
-		printk("fib_create_info: fi %08x/%s/%04x is duplicate\n", fi->fib_gateway, fi->fib_dev ? fi->fib_dev->name : "null", fi->fib_flags);
-#endif
-		return fi;
-	}
-	fi = (struct fib_info*)kmalloc(sizeof(struct fib_info), GFP_KERNEL);
-	if (!fi)
-		return NULL;
-	memset(fi, 0, sizeof(struct fib_info));
-	fi->fib_flags = flags;
-	fi->fib_dev = dev;
-	fi->fib_gateway = gw;
-	fi->fib_mtu = mtu;
-	fi->fib_window = window;
-	fi->fib_refcnt++;
-	fi->fib_next = fib_info_list;
-	fi->fib_prev = NULL;
-	fi->fib_irtt = irtt;
-	if (fib_info_list)
-		fib_info_list->fib_prev = fi;
-	fib_info_list = fi;
-#if RT_CACHE_DEBUG >= 2
-	printk("fib_create_info: fi %08x/%s/%04x is created\n", fi->fib_gateway, fi->fib_dev ? fi->fib_dev->name : "null", fi->fib_flags);
-#endif
-	return fi;
-}
-
-static __inline__ void fib_rebuild_zone(struct fib_zone *fz,
-					struct fib_node **old_ht,
-					int old_divisor)
-{
-	int i;
-	struct fib_node **ht = fz->fz_hash;
-	u32 hashmask = fz->fz_hashmask;
-	struct fib_node *f, **fp, *next;
-	unsigned hash;
-
-	for (i=0; i<old_divisor; i++) {
-		for (f=old_ht[i]; f; f=next) {
-			next = f->fib_next;
-			f->fib_next = NULL;
-			hash = fib_hash(f->fib_key, hashmask);
-			for (fp = &ht[hash]; *fp; fp = &(*fp)->fib_next)
-				/* NONE */;
-			*fp = f;
-		}
-	}
-}
-
-static void fib_rehash_zone(struct fib_zone *fz)
-{
-	struct fib_node **ht, **old_ht;
-	int old_divisor, new_divisor;
-	u32 new_hashmask;
-		
-	old_divisor = fz->fz_divisor;
-
-	switch (old_divisor) {
-	case 16:
-		new_divisor = 256;
-		new_hashmask = 0xFF;
-		break;
-	case 256:
-		new_divisor = 1024;
-		new_hashmask = 0x3FF;
-		break;
-	default:
-		printk(KERN_CRIT "route.c: bad divisor %d!\n", old_divisor);
-		return;
-	}
-#if RT_CACHE_DEBUG >= 2
-	printk("fib_rehash_zone: hash for zone %d grows from %d\n", fz->fz_logmask, old_divisor);
-#endif
-
-	ht = kmalloc(new_divisor*sizeof(struct rtable*), GFP_KERNEL);
-
-	if (ht)	{
-		memset(ht, 0, new_divisor*sizeof(struct fib_node*));
-		start_bh_atomic();
-		old_ht = fz->fz_hash;
-		fz->fz_hash = ht;
-		fz->fz_hashmask = new_hashmask;
-		fz->fz_divisor = new_divisor;
-		fib_rebuild_zone(fz, old_ht, old_divisor);
-		fib_stamp++;
-		end_bh_atomic();
-		kfree(old_ht);
-	}
-}
-
-static struct fib_zone *
-fib_new_zone(struct fib_class *class, int logmask)
-{
-	int i;
-	struct fib_zone *fz = kmalloc(sizeof(struct fib_zone), GFP_KERNEL);
-	if (!fz)
-		return NULL;
-
-	memset(fz, 0, sizeof(struct fib_zone));
-	if (logmask < 32) {
-		fz->fz_divisor = 16;
-		fz->fz_hashmask = 0xF;
-	} else {
-		fz->fz_divisor = 1;
-		fz->fz_hashmask = 0;
-	}
-	fz->fz_hash = kmalloc(fz->fz_divisor*sizeof(struct fib_node*), GFP_KERNEL);
-	if (!fz->fz_hash) {
-		kfree(fz);
-		return NULL;
-	}
-	memset(fz->fz_hash, 0, fz->fz_divisor*sizeof(struct fib_node*));
-	fz->fz_logmask = logmask;
-	fz->fz_mask = ntohl(fib_mask(logmask));
-	for (i=logmask-1; i>=0; i--)
-		if (class->fib_zones[i])
-			break;
-	start_bh_atomic();
-	if (i<0) {
-		fz->fz_next = class->fib_zone_list;
-		class->fib_zone_list = fz;
-	} else {
-		fz->fz_next = class->fib_zones[i]->fz_next;
-		class->fib_zones[i]->fz_next = fz;
-	}
-	class->fib_zones[logmask] = fz;
-	fib_stamp++;
-	end_bh_atomic();
-	return fz;
-}
-
-static int fib_create(struct in_rtmsg *r, struct device *dev,
-		      struct fib_class *class, struct nlmsghdr *n)
-{
-	struct fib_node *f, *f1, **fp;
-	struct fib_node **dup_fp = NULL;
-	struct fib_zone * fz;
-	struct fib_info * fi;
-
-	long logmask = 32L - r->rtmsg_prefixlen;	/* gcc bug work-around: must be "L" and "long" */
-	u32 dst = ntohl(r->rtmsg_prefix.s_addr);
-	u32 gw  = r->rtmsg_gateway.s_addr;
-	short metric = r->rtmsg_metric;
-	unsigned flags = r->rtmsg_flags;
-	u8 tos = r->rtmsg_tos;
-	u8 fibflg = 0;
-	u32 key;
-
-	/*
-	 *	Allocate an entry and fill it in.
-	 */
-	 
-	f = (struct fib_node *) kmalloc(sizeof(struct fib_node), GFP_KERNEL);
-	if (f == NULL) {
-		rtmsg_ack(n, ENOMEM);
-		return -ENOMEM;
-	}
-
-	memset(f, 0, sizeof(struct fib_node));
-
-	if (!(flags & RTF_UP))
-		fibflg = FIBFLG_DOWN;
-	if (flags & RTF_REJECT)
-		fibflg |= FIBFLG_REJECT;
-	else if (flags & RTF_THROW)
-		fibflg |= FIBFLG_THROW;
-
-	flags &= ~(RTF_UP|RTF_REJECT|RTF_THROW);
-	r->rtmsg_flags = flags;
-
-	fi = NULL;
-	if (!(fibflg & (FIBFLG_REJECT|FIBFLG_THROW))) {
-		if  ((fi = fib_create_info(dev, r)) == NULL) {
-			kfree_s(f, sizeof(struct fib_node));
-			rtmsg_ack(n, ENOMEM);
-			return -ENOMEM;
-		}
-		f->fib_info = fi;
-		flags = fi->fib_flags;
-	}
-
-	f->fib_key = key = dst>>logmask;
-	f->fib_metric = metric;
-	f->fib_tos    = tos;
-	f->fib_flag = fibflg;
-	fz = class->fib_zones[logmask];
-
-	if (!fz && !(fz = fib_new_zone(class, logmask))) {
-		fib_free_node(f);
-		rtmsg_ack(n, ENOMEM);
-		return -ENOMEM;
-	}
-
-	if (fz->fz_nent > (fz->fz_divisor<<2) &&
-	    fz->fz_divisor < FZ_MAX_DIVISOR &&
-	    (!logmask || (1<<(32-logmask)) > fz->fz_divisor))
-		fib_rehash_zone(fz);
-
-	fp = fz_hash_p(key, fz);
-
-	/*
-	 * Scan list to find the first route with the same destination
-	 */
-	FIB_SCAN(f1, fp) {
-		if (f1->fib_key == key)
-			break;
-	}
-
-	/*
-	 * Find route with the same destination and tos.
-	 */
-	FIB_SCAN_KEY(f1, fp, dst) {
-		if (f1->fib_tos <= tos)
-			break;
-	}
-
-	/*
-	 * Find route with the same destination/tos and less (or equal) metric.
-	 * "Magic" additions go to the end of list.
-	 */
-	for ( ; (f1 = *fp) != NULL && f1->fib_key == key && f1->fib_tos == tos;
-	     fp = &f1->fib_next) {
-		if (f1->fib_metric >= metric && metric != MAGIC_METRIC)
-			break;
-
-		/*
-		 *	Record route with the same destination/tos/gateway/dev,
-		 *	but less metric.
-		 */
-		if (!dup_fp) {
-			struct fib_info *fi1 = f1->fib_info;
-			
-			if ((fibflg^f1->fib_flag) & ~FIBFLG_DOWN)
-				continue;
-			if (fi == fi1 ||
-			    (fi && fi1 &&
-			     fi->fib_dev == fi1->fib_dev &&
-			     fi->fib_gateway == fi1->fib_gateway &&
-			     !(flags&RTF_MAGIC)))
-				dup_fp = fp;
-		}
-	}
-
-	/*
-	 * Is it already present?
-	 */
-
-	if (f1 && f1->fib_key == key && f1->fib_tos == tos &&
-	    f1->fib_metric == metric && f1->fib_info == fi) {
-		fib_free_node(f);
-
-		if (fibflg == f1->fib_flag) {
-			rtmsg_ack(n, EEXIST);
-			return -EEXIST;
-		} else {
-			fib_stamp++;
-			f1->fib_flag = fibflg;
-			rt_cache_flush(0);
-			rtmsg_ack(n, 0);
-			return 0;
-		}
-	}
-
-	/*
-	 * Do not add "magic" route, if better one is already present.
-	 */
-	if ((flags & RTF_MAGIC) && dup_fp) {
-		fib_free_node(f);
-		rtmsg_ack(n, EEXIST);
-		return -EEXIST;
-	}
-
-	/*
-	 * Insert new entry to the list.
-	 */
-
-	cli();
-	f->fib_next = f1;
-	*fp = f;
-	sti();
-	fz->fz_nent++;
-	if (class == &local_class && !dup_fp)
-		fib_autopublish(1, f, logmask);
-	rtmsg_fib(RTMSG_NEWROUTE, f, logmask, class, n);
-
-	if (flags & RTF_MAGIC) {
-		fib_stamp++;
-		rt_cache_flush(0);
-		rtmsg_ack(n, 0);
-		return 0;
-	}
-
-	/*
-	 *	Clean routes with the same destination,tos,gateway and device,
-	 *	but different metric.
-	 */
-	fp = dup_fp ? : &f->fib_next;
-
-	while ((f1 = *fp) != NULL && f1->fib_key == key && f1->fib_tos == tos) {
-		if (f1 == f || ((f1->fib_flag^fibflg)&~FIBFLG_DOWN))
-			FIB_CONTINUE(f1, fp);
-
-		if (f1->fib_info != fi &&
-		    (!fi || !f1->fib_info ||
-		     f1->fib_info->fib_gateway != gw ||
-		     f1->fib_info->fib_dev != dev))
-			FIB_CONTINUE(f1, fp);
-
-		cli();
-		*fp = f1->fib_next;
-		sti();
-		fz->fz_nent--;
-		rtmsg_fib(RTMSG_DELROUTE, f1, logmask, class, n);
-		fib_free_node(f1);
-	}
-	fib_stamp++;
-	rt_cache_flush(0);
-	rtmsg_ack(n, 0);
-	return 0;
-}
-
-static int fib_flush_list(struct fib_node ** fp, struct device *dev,
-			  int logmask, struct fib_class *class)
-{
-	int found = 0;
-	struct fib_node *f;
-
-	while ((f = *fp) != NULL) {
-		if (!f->fib_info || f->fib_info->fib_dev != dev)
-			FIB_CONTINUE(f, fp);
-		cli();
-		*fp = f->fib_next;
-		sti();
-		if (class == &local_class)
-			fib_autopublish(0, f, logmask);
-#ifdef CONFIG_RTNETLINK
-		if (rt_nl_flags&RTCTL_FLUSH)
-		    rtmsg_fib(RTMSG_DELROUTE, f, logmask, class, 0);
-#endif
-		fib_free_node(f);
-		found++;
-	}
-	return found;
-}
-
-static void fib_flush(struct device *dev)
-{
-	struct fib_class *class;
-	struct fib_rule *cl, **clp;
-	struct fib_zone *fz;
-	int found = 0;
-	int i, tmp, cl_id;
-
-
-	for (cl_id = RT_CLASS_MAX; cl_id>=0; cl_id--) {
-		if ((class = fib_classes[cl_id])==NULL)
-			continue;
-		for (fz = class->fib_zone_list; fz; fz = fz->fz_next) {
-			tmp = 0;
-			for (i=fz->fz_divisor-1; i>=0; i--)
-				tmp += fib_flush_list(&fz->fz_hash[i], dev,
-						      fz->fz_logmask, class);
-			fz->fz_nent -= tmp;
-			found += tmp;
-		}
-	}
-	
-	clp = &fib_rules;
-	while ( (cl=*clp) != NULL) {
-		if (cl->cl_dev != dev) {
-			clp = &cl->cl_next;
-			continue;
-		}
-		found++;
-		cli();
-		*clp = cl->cl_next;
-		sti();
-		kfree(cl);
-	}
-		
-	if (found) {
-		fib_stamp++;
-		rt_cache_flush(1);
-	}
-}
-
-#ifdef CONFIG_PROC_FS
-
-static unsigned __inline__ fib_flag_trans(u8 fibflg)
-{
-	unsigned ret = RTF_UP;
-	if (!fibflg)
-		return ret;
-	if (fibflg & FIBFLG_DOWN)
-		ret &= ~RTF_UP;
-	if (fibflg & FIBFLG_REJECT)
-		ret |= RTF_REJECT;
-	if (fibflg & FIBFLG_THROW)
-		ret |= RTF_THROW;
-	return ret;
-}
-
-/* 
- *	Called from the PROCfs module. This outputs /proc/net/route.
- *
- *	We preserve the old format but pad the buffers out. This means that
- *	we can spin over the other entries as we read them. Remember the
- *	gated BGP4 code could need to read 60,000+ routes on occasion (that's
- *	about 7Mb of data). To do that ok we will need to also cache the
- *	last route we got to (reads will generally be following on from
- *	one another without gaps).
- */
- 
-static int fib_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
-{
-	struct fib_class *class;
-	struct fib_zone *fz;
-	struct fib_node *f;
-	int len=0;
-	off_t pos=0;
-	char temp[129];
-	int i;
-	int cl_id;
-	
-	pos = 128;
-
-	if (offset<128)
-	{
-		sprintf(buffer,"%-127s\n","Iface\tDestination\tGateway \tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU\tWindow\tIRTT\tTOS\tClass");
-		len = 128;
-  	}
-  	
-	fib_lock();
-
-	for (cl_id=RT_CLASS_MAX-1; cl_id >= 0; cl_id--) {
-		class = fib_classes[cl_id];
-		if (!class)
-			continue;
-		for (fz=class->fib_zone_list; fz; fz = fz->fz_next)
-		{
-		int maxslot;
-		struct fib_node ** fp;
-
-		if (fz->fz_nent == 0)
-			continue;
-
-		if (pos + 128*fz->fz_nent <= offset) {
-			pos += 128*fz->fz_nent;
-			len = 0;
-			continue;
-		}
-
-		maxslot = fz->fz_divisor;
-		fp	= fz->fz_hash;
-			
-		for (i=0; i < maxslot; i++, fp++) {
-			
-			for (f = *fp; f; f = f->fib_next) 
-			{
-				struct fib_info * fi;
-				unsigned	flags;
-
-				/*
-				 *	Spin through entries until we are ready
-				 */
-				pos += 128;
-
-				if (pos <= offset)
-				{
-					len=0;
-					continue;
-				}
-					
-				fi = f->fib_info;
-				flags = fib_flag_trans(f->fib_flag);
-
-				if (fi)
-					flags |= fi->fib_flags;
-				sprintf(temp, "%s\t%08lX\t%08X\t%04X\t%d\t%u\t%d\t%08lX\t%d\t%lu\t%u\t%02x\t%02x",
-					fi && fi->fib_dev ? fi->fib_dev->name : "*", htonl(f->fib_key<<fz->fz_logmask), fi ? fi->fib_gateway : 0,
-					flags, 0, 0, f->fib_metric,
-					htonl(fz->fz_mask), fi ? (int)fi->fib_mtu : 0, fi ? fi->fib_window : 0, fi ? (int)fi->fib_irtt : 0, f->fib_tos, class->cl_id);
-				sprintf(buffer+len,"%-127s\n",temp);
-
-				len += 128;
-				if (pos >= offset+length)
-					goto done;
-			}
-		}
-        }
-	}
-
-done:
-	fib_unlock();
-  	
-  	*start = buffer+len-(pos-offset);
-  	len = pos - offset;
-  	if (len>length)
-  		len = length;
-  	return len;
-}
-
-static int fib_local_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
-{
-	struct fib_zone *fz;
-	struct fib_node *f;
-	int len=0;
-	off_t pos=0;
-	char temp[129];
-	int i;
-	
-	pos = 128;
-
-	if (offset<128)
-	{
-		sprintf(buffer,"%-127s\n","Iface\tDestination\tGateway \tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU\tWindow\tIRTT\tTOS\tClass");
-		len = 128;
-  	}
-  	
-	fib_lock();
-
-	for (fz=local_class.fib_zone_list; fz; fz = fz->fz_next)
-	{
-		int maxslot;
-		struct fib_node ** fp;
-
-		if (fz->fz_nent == 0)
-			continue;
-
-		if (pos + 128*fz->fz_nent <= offset)
-		{
-			pos += 128*fz->fz_nent;
-			len = 0;
-			continue;
-		}
-
-		maxslot = fz->fz_divisor;
-		fp	= fz->fz_hash;
-			
-		for (i=0; i < maxslot; i++, fp++)
-		{
-			
-			for (f = *fp; f; f = f->fib_next) 
-			{
-				unsigned	flags;
-				struct fib_info * fi;
-
-				/*
-				 *	Spin through entries until we are ready
-				 */
-				pos += 128;
-
-				if (pos <= offset)
-				{
-					len=0;
-					continue;
-				}
-					
-				fi = f->fib_info;
-				flags = fib_flag_trans(f->fib_flag);
-
-				if (fi)
-					flags |= fi->fib_flags;
-				sprintf(temp, "%s\t%08lX\t%08X\t%X\t%d\t%u\t%d\t%08lX\t%d\t%lu\t%u\t%02x\t%02x",
-					fi && fi->fib_dev ? fi->fib_dev->name : "*",
-					htonl(f->fib_key<<fz->fz_logmask),
-					fi ? fi->fib_gateway : 0,
-					flags, 0, 0, f->fib_metric,
-					htonl(fz->fz_mask), fi ? (int)fi->fib_mtu : 0, fi ? fi->fib_window : 0, fi ? (int)fi->fib_irtt : 0, f->fib_tos, RT_CLASS_LOCAL);
-				sprintf(buffer+len,"%-127s\n",temp);
-
-				len += 128;
-				if (pos >= offset+length)
-					goto done;
-			}
-		}
-        }
-
-done:
-	fib_unlock();
-  	
-  	*start = buffer+len-(pos-offset);
-  	len = pos - offset;
-  	if (len>length)
-  		len = length;
-  	return len;
-}
-
-static int fib_rules_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
-{
-	int len=0;
-	off_t pos=0;
-	char temp[129];
-	struct fib_rule *cl;
-
-	pos = 128;
-
-	if (offset<128) {
-		sprintf(buffer,"%-127s\n","Pref\tSource\t\tSrcMask\t\tDst\t\tDstMask\t\tIface\tTOS\tClass\tFlags\tSrcMap\n");
-		len = 128;
-  	}
-	
-  	
-	fib_lock();
-
-	for (cl = fib_rules; cl; cl = cl->cl_next) {
-		/*
-		 *	Spin through entries until we are ready
-		 */
-		pos += 128;
-
-		if (pos <= offset) {
-			len = 0;
-			continue;
-		}
-					
-		sprintf(temp, "%d\t%08X\t%08X\t%08X\t%08X\t%s\t%02X\t%02x\t%02X\t%02X\t%08X",
-			cl->cl_preference,
-			cl->cl_src, cl->cl_srcmask,
-			cl->cl_dst, cl->cl_dstmask,
-			cl->cl_dev ? cl->cl_dev->name : "*",
-			cl->cl_tos, cl->cl_class ? cl->cl_class->cl_id : 0,
-			cl->cl_flags, cl->cl_action, cl->cl_srcmap
-			);
-		sprintf(buffer+len,"%-127s\n",temp);
-		len += 128;
-		if (pos >= offset+length)
-			goto done;
-	}
-
-done:
-	fib_unlock();
-  	
-  	*start = buffer+len-(pos-offset);
-  	len = pos-offset;
-  	if (len>length)
-  		len = length;
-  	return len;
-}
-
-static int fib_class_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
-{
-	int len=0;
-	off_t pos=0;
-	char temp[129];
-	int i;
-	struct fib_class *cl;
-
-	pos = 128;
-
-	if (offset<128)
-	{
-		sprintf(buffer,"%-127s\n","Class\tSize\n");
-		len = 128;
-  	}
-	
-  	
-	fib_lock();
-
-	for (i = RT_CLASS_MAX; i>=0; i--) 
-	{
-		int sz = 0;
-		struct fib_zone *fz;
-
-		if ((cl=fib_classes[i])==NULL)
-			continue;
-
-		for (fz=cl->fib_zone_list; fz; fz=fz->fz_next)
-			sz += fz->fz_nent;
-
-		/*
-		 *	Spin through entries until we are ready
-		 */
-		pos += 128;
-
-		if (pos <= offset)
-		{
-			len = 0;
-			continue;
-		}
-					
-		sprintf(temp, "%d\t%d\n", cl->cl_id, sz);
-		sprintf(buffer+len,"%-127s\n",temp);
-		len += 128;
-		if (pos >= offset+length)
-			goto done;
-	}
-
-done:
-	fib_unlock();
-  	
-  	*start = buffer+len-(pos-offset);
-  	len = pos-offset;
-  	if (len>length)
-  		len = length;
-  	return len;
-}
-
-#endif
-
-static int rtmsg_process(struct nlmsghdr *n, struct in_rtmsg *r)
-{
-	unsigned long cmd=n->nlmsg_type;
-	struct device * dev = NULL;
-	struct fib_class *class;
-
-	if ((cmd != RTMSG_NEWROUTE && cmd != RTMSG_DELROUTE) ||
-	    (r->rtmsg_flags & (RTF_MAGIC|RTF_XRESOLVE|RTF_REINSTATE)) ||
-	    r->rtmsg_prefixlen > 32 ||
-	    (r->rtmsg_tos & ~IPTOS_TOS_MASK)) {
-		rtmsg_ack(n, EINVAL);
-		return -EINVAL;
-	}
-
-	/* Reject/throw directives have no interface/gateway specification */
-
-	if (r->rtmsg_flags & (RTF_REJECT|RTF_THROW)) {
-		r->rtmsg_ifindex = 0;
-		r->rtmsg_gateway.s_addr = 0;
-		r->rtmsg_flags &= ~RTF_GATEWAY;
-	}
-
-	/* Silly metric hack, it is preserved for "compatibility",
-	 * though I do not know any program using it.
-	 */
-
-	r->rtmsg_metric--;
-	if (cmd == RTMSG_NEWROUTE && r->rtmsg_metric < 0)
-		r->rtmsg_metric = 0;
-
-	if (cmd == RTMSG_DELROUTE)
-		r->rtmsg_flags &= RTF_FIB;
-
-	if (r->rtmsg_ifindex) {
-		dev = dev_get_by_index(r->rtmsg_ifindex);
-		if (!dev) {
-			rtmsg_ack(n, ENODEV);
-			return -ENODEV;
-		}
-	}
-
-	if (r->rtmsg_gateway.s_addr && !(r->rtmsg_flags&RTF_NAT)) {
-		struct fib_info *fi;
-
-		fi = fib_lookup_info(r->rtmsg_gateway.s_addr, 0, 1,
-				     &loopback_dev, dev);
-		if (fi) {
-			if (fi->fib_flags&(RTF_BROADCAST|RTF_MULTICAST) &&
-			    cmd != RTMSG_DELROUTE)
-				return -EINVAL;
-			dev = fi->fib_dev;
-			if (fi->fib_flags&RTF_LOCAL) {
-				r->rtmsg_flags &= ~RTF_GATEWAY;
-				r->rtmsg_gateway.s_addr = 0;
-			}
-		} else if (cmd != RTMSG_DELROUTE)
-			return -ENETUNREACH;
-
-		/* If gateway is not found in routing table,
-		 * we could assume that user knows that he does.
-		 * It is link layer problem to decide reachable
-		 * this gateway or not. Good example is tunnel interface.
-		 * Another example is ethernet, ARP could (in theory)
-		 * resolve addresses, even if we had no routes.
-		 */
-	}
-
-	if (dev && (dev->flags&IFF_LOOPBACK)) {
-		if (r->rtmsg_flags&RTF_GATEWAY)
-			return -EINVAL;
-		/*
-		 * Loopback routes: we declare them local addresses.
-		 * It is the only reasonable solution to avoid
-		 * loopback routing loops.
-		 */
-		r->rtmsg_flags |= RTF_LOCAL|RTF_INTERFACE;
-	}
-
-	if (r->rtmsg_flags&RTF_GATEWAY) {
-		if (!dev && cmd != RTMSG_DELROUTE) {
-			rtmsg_ack(n, ENETUNREACH);
-			return -ENETUNREACH;
-		}
-	} else {
-		if (!dev && !(r->rtmsg_flags & (RTF_NAT|RTF_REJECT|RTF_THROW)) &&
-		    cmd != RTMSG_DELROUTE) {
-			rtmsg_ack(n, ENODEV);
-			return -ENODEV;
-		}
-	}
-
-	if (dev && dev->family != AF_INET)
-	{
-		rtmsg_ack(n, ENODEV);
-		return -ENODEV;
-	}
-
-	if (r->rtmsg_class == 0) {
-		if (r->rtmsg_flags&(RTF_LOCAL|RTF_NAT))
-			r->rtmsg_class = RT_CLASS_LOCAL;
-		else if ((r->rtmsg_flags&RTF_GATEWAY) &&
-			 (ipv4_config.fib_model==2 ||
-			  (ipv4_config.fib_model==1 && !r->rtmsg_prefixlen)))
-			r->rtmsg_class = RT_CLASS_DEFAULT;
-		else
-			r->rtmsg_class = RT_CLASS_MAIN;
-	}
-
-	if ((class = fib_classes[r->rtmsg_class]) == NULL)
-	{
-		rtmsg_ack(n, EINVAL);
-		return -EINVAL;
-	}
-
-	return (cmd == RTMSG_NEWROUTE ? fib_create : fib_delete)(r, dev, class, n);
-}
-
-
-static int rtrulemsg_process(struct nlmsghdr *n, struct in_rtrulemsg *r)
-{
-	unsigned long cmd=n->nlmsg_type;
-	struct device * dev = NULL;
-
-	if ((cmd != RTMSG_NEWRULE && cmd != RTMSG_DELRULE) ||
-	    r->rtrmsg_srclen > 32 || r->rtrmsg_dstlen > 32 ||
-	    (r->rtrmsg_tos & ~IPTOS_TOS_MASK))
-		return -EINVAL;
-
-	if (r->rtrmsg_ifindex) {
-		dev = dev_get_by_index(r->rtrmsg_ifindex);
-		if (!dev)
-			return -ENODEV;
-		if (dev->family != AF_INET)
-			return -ENODEV;
-	}
-
-	if (cmd == RTMSG_DELRULE)
-		return fib_rule_delete(r, dev, n);
-
-	return fib_rule_add(r, dev, n);
-}
-
-
-static int ifmsg_process(struct nlmsghdr *n, struct in_ifmsg *r)
-{
-	unsigned long cmd=n->nlmsg_type;
-
-	if (cmd != RTMSG_NEWDEVICE && cmd != RTMSG_DELDEVICE) {
-		rtmsg_ack(n, EINVAL);
-		return -EINVAL;
-	}
-	rtmsg_ack(n, EINVAL);
-	return -EINVAL;
-}
-
-static int rtcmsg_process(struct nlmsghdr *n, struct in_rtctlmsg *r)
-{
-#ifdef CONFIG_RTNETLINK
-    if (r->rtcmsg_flags&RTCTL_DELAY)
-	rtmsg_ctl.nlmsg_delay = r->rtcmsg_delay;
-    if (r->rtcmsg_flags&RTCTL_OWNER)
-	rt_nl_owner = n->nlmsg_pid;
-    rt_nl_flags = r->rtcmsg_flags;
-    return 0;
-#else
-    return -EINVAL;
-#endif
-}
-
-static int get_rt_from_user(struct in_rtmsg *rtm, void *arg)
-{
-	struct rtentry r;
-
-	if (copy_from_user(&r, arg, sizeof(struct rtentry)))
-		return -EFAULT;
-	if (r.rt_dev) {
-		struct device *dev;
-		char   devname[16];
-
-		if (copy_from_user(devname, r.rt_dev, 15))
-			return -EFAULT;
-		devname[15] = 0;
-		dev = dev_get(devname);
-		if (!dev)
-			return -ENODEV;
-		rtm->rtmsg_ifindex = dev->ifindex;
-	}
-
-	rtm->rtmsg_flags = r.rt_flags;
-
-	if (r.rt_dst.sa_family != AF_INET)
-		return -EAFNOSUPPORT;
-	rtm->rtmsg_prefix = ((struct sockaddr_in*)&r.rt_dst)->sin_addr;
-
-	if (rtm->rtmsg_flags&RTF_HOST) {
-		rtm->rtmsg_flags &= ~RTF_HOST;
-		rtm->rtmsg_prefixlen = 32;
-	} else {
-		u32 mask = ((struct sockaddr_in*)&r.rt_genmask)->sin_addr.s_addr;
-		if (r.rt_genmask.sa_family != AF_INET) {
-			printk(KERN_DEBUG "%s forgot to specify route netmask.\n", current->comm);
-			if (r.rt_genmask.sa_family)
-				return -EAFNOSUPPORT;
-		}
-		if (bad_mask(mask, rtm->rtmsg_prefix.s_addr))
-			return -EINVAL;
-		rtm->rtmsg_prefixlen = 32 - fib_logmask(mask);
-	}
-	if ((rtm->rtmsg_flags & RTF_GATEWAY) &&
-	    r.rt_gateway.sa_family != AF_INET)
-		return -EAFNOSUPPORT;
-	rtm->rtmsg_gateway = ((struct sockaddr_in*)&r.rt_gateway)->sin_addr;
-	rtm->rtmsg_rtt = r.rt_irtt;
-	rtm->rtmsg_window = r.rt_window;
-	rtm->rtmsg_mtu = r.rt_mtu;
-	rtm->rtmsg_class = r.rt_class;
-	rtm->rtmsg_metric = r.rt_metric;
-        rtm->rtmsg_tos = r.rt_tos;
-	return 0;
-}
-
-
-/*
- *	Handle IP routing ioctl calls. These are used to manipulate the routing tables
- */
- 
-int ip_rt_ioctl(unsigned int cmd, void *arg)
-{
-	int err;
-	union
-	{
-		struct in_rtmsg rtmsg;
-		struct in_ifmsg ifmsg;
-		struct in_rtrulemsg rtrmsg;
-	        struct in_rtctlmsg rtcmsg;
-	} m;
-	struct nlmsghdr dummy_nlh;
-
-	memset(&m, 0, sizeof(m));
-	dummy_nlh.nlmsg_seq = 0;
-	dummy_nlh.nlmsg_pid = current->pid;
-
-	switch (cmd)
-	{
-		case SIOCADDRT:		/* Add a route */
-		case SIOCDELRT:		/* Delete a route */
-			if (!suser())
-				return -EPERM;
-			err = get_rt_from_user(&m.rtmsg, arg);
-			if (err)
-				return err;
-			fib_lock();
-			dummy_nlh.nlmsg_type = cmd == SIOCDELRT ? RTMSG_DELROUTE
-					    : RTMSG_NEWROUTE;
-			err = rtmsg_process(&dummy_nlh, &m.rtmsg);
-			fib_unlock();
-			return err;
-		case SIOCRTMSG:
-			if (!suser())
-				return -EPERM;
-			if (copy_from_user(&dummy_nlh, arg, sizeof(dummy_nlh)))
-				return -EFAULT;
-			switch (dummy_nlh.nlmsg_type)
-			{
-			case RTMSG_NEWROUTE:
-			case RTMSG_DELROUTE:
-				if (dummy_nlh.nlmsg_len < sizeof(m.rtmsg) + sizeof(dummy_nlh))
-					return -EINVAL;
-				if (copy_from_user(&m.rtmsg, arg+sizeof(dummy_nlh), sizeof(m.rtmsg)))
-					return -EFAULT;
-				fib_lock();
-				err = rtmsg_process(&dummy_nlh, &m.rtmsg);
-				fib_unlock();
-				return err;
-			case RTMSG_NEWRULE:
-			case RTMSG_DELRULE:
-				if (dummy_nlh.nlmsg_len < sizeof(m.rtrmsg) + sizeof(dummy_nlh))
-					return -EINVAL;
-				if (copy_from_user(&m.rtrmsg, arg+sizeof(dummy_nlh), sizeof(m.rtrmsg)))
-					return -EFAULT;
-				fib_lock();
-				err = rtrulemsg_process(&dummy_nlh, &m.rtrmsg);
-				fib_unlock();
-				return err;
-			case RTMSG_NEWDEVICE:
-			case RTMSG_DELDEVICE:
-				if (dummy_nlh.nlmsg_len < sizeof(m.ifmsg) + sizeof(dummy_nlh))
-					return -EINVAL;
-				if (copy_from_user(&m.ifmsg, arg+sizeof(dummy_nlh), sizeof(m.ifmsg)))
-					return -EFAULT;
-				fib_lock();
-				err = ifmsg_process(&dummy_nlh, &m.ifmsg);
-				fib_unlock();
-				return err;
-			case RTMSG_CONTROL:
-				if (dummy_nlh.nlmsg_len < sizeof(m.rtcmsg) + sizeof(dummy_nlh))
-					return -EINVAL;
-				if (copy_from_user(&m.rtcmsg, arg+sizeof(dummy_nlh), sizeof(m.rtcmsg)))
-					return -EFAULT;
-				fib_lock();
-				err = rtcmsg_process(&dummy_nlh, &m.rtcmsg);
-				fib_unlock();
-				return err;
-			default:
-				return -EINVAL;
-			}
-	}
-
-	return -EINVAL;
-}
-
-#ifdef CONFIG_RTNETLINK
-
-/*
- *	Netlink hooks for IP
- */
-
-
-static void
-rtmsg_fib(unsigned long type, struct fib_node *f, int logmask,
-	  struct fib_class *class, struct nlmsghdr *n)
-{
-	struct in_rtmsg *r;
-	struct fib_info *fi;
-
-	if (n && !(rt_nl_flags&RTCTL_ECHO) && rt_nl_owner == n->nlmsg_pid)
-	        return;
-
-	start_bh_atomic();
-	r = nlmsg_send(&rtmsg_ctl, type, sizeof(*r), n ? n->nlmsg_seq : 0,
-		       n ? n->nlmsg_pid : 0);
-	if (r) {
-		r->rtmsg_prefix.s_addr = htonl(f->fib_key<<logmask);
-		r->rtmsg_prefixlen = 32 - logmask;
-		r->rtmsg_metric= f->fib_metric;
-		r->rtmsg_tos = f->fib_tos;
-		r->rtmsg_class=class->cl_id;
-		r->rtmsg_flags = fib_flag_trans(f->fib_flag);
-
-		if ((fi = f->fib_info) != NULL)	{
-			r->rtmsg_gateway.s_addr = fi->fib_gateway;
-			r->rtmsg_flags |= fi->fib_flags;
-			r->rtmsg_mtu = fi->fib_mtu;
-			r->rtmsg_window = fi->fib_window;
-			r->rtmsg_rtt = fi->fib_irtt;
-			r->rtmsg_ifindex = fi->fib_dev ? fi->fib_dev->ifindex : 0;
-		}
-	}
-	end_bh_atomic();
-}
-
-static void
-__rtmsg_ack(struct nlmsghdr *n, int err)
-{
-	nlmsg_ack(&rtmsg_ctl, n->nlmsg_seq, n->nlmsg_pid, err);
-}
-
-
-static void
-rtmsg_dev(unsigned long type, struct device *dev, struct nlmsghdr *n)
-{
-	struct in_ifmsg *r;
-
-	start_bh_atomic();
-	r = nlmsg_send(&rtmsg_ctl, type, sizeof(*r), n ? n->nlmsg_seq : 0,
-		       n ? n->nlmsg_pid : 0);
-	if (r)
-	{
-		memset(r, 0, sizeof(*r));
-		r->ifmsg_lladdr.sa_family = dev->type;
-		memcpy(&r->ifmsg_lladdr.sa_data, dev->dev_addr, dev->addr_len);
-		r->ifmsg_prefix.s_addr = dev->pa_addr;
-		if (dev->flags & IFF_POINTOPOINT || dev->type == ARPHRD_TUNNEL)
-			r->ifmsg_brd.s_addr = dev->pa_dstaddr;
-		else
-			r->ifmsg_brd.s_addr = dev->pa_brdaddr;
-		r->ifmsg_flags = dev->flags;
-		r->ifmsg_mtu = dev->mtu;
-		r->ifmsg_metric = dev->metric;
-		r->ifmsg_prefixlen = 32 - fib_logmask(dev->pa_mask);
-		r->ifmsg_index = dev->ifindex;
-		strcpy(r->ifmsg_name, dev->name);
-	}
-	end_bh_atomic();
-}
-
-static int fib_netlink_call(int minor, struct sk_buff *skb)
-{
-	struct nlmsghdr *nlh;
-	int    totlen = 0;
-	int    err = 0;
-
-	fib_lock();
-	while (skb->len >= sizeof(*nlh)) {
-		int rlen;
-		nlh = (struct nlmsghdr *)skb->data;
-		rlen = NLMSG_ALIGN(nlh->nlmsg_len);
-		if (skb->len < rlen)
-			break;
-		totlen += rlen;
-		err = 0;
-		skb_pull(skb, rlen);
-		switch (nlh->nlmsg_type) {
-		case RTMSG_NEWROUTE:
-		case RTMSG_DELROUTE:
-			if (nlh->nlmsg_len < sizeof(*nlh)+sizeof(struct in_rtmsg)) {
-				rtmsg_ack(nlh, EINVAL);
-				err = -EINVAL;
-				break;
-			}
-			err = rtmsg_process(nlh, (struct in_rtmsg*)nlh->nlmsg_data);
-			break;
-		case RTMSG_NEWRULE:
-		case RTMSG_DELRULE:
-			if (nlh->nlmsg_len < sizeof(*nlh)+sizeof(struct in_rtrulemsg)) {
-				rtmsg_ack(nlh, EINVAL);
-				err = -EINVAL;
-				break;
-			}
-			err = rtrulemsg_process(nlh, (struct in_rtrulemsg*)nlh->nlmsg_data);
-			break;
-		case RTMSG_NEWDEVICE:
-		case RTMSG_DELDEVICE:
-			if (nlh->nlmsg_len < sizeof(*nlh)+sizeof(struct in_ifmsg)) {
-				rtmsg_ack(nlh, EINVAL);
-				err = -EINVAL;
-				break;
-			}
-			err = ifmsg_process(nlh, (struct in_ifmsg*)nlh->nlmsg_data);
-			break;
-		case RTMSG_CONTROL:
-		        if (nlh->nlmsg_len < sizeof(*nlh)+sizeof(struct in_rtctlmsg)) {
-				rtmsg_ack(nlh, EINVAL);
-				err = -EINVAL;
-				break;
-			}
-			err = rtcmsg_process(nlh, (struct in_rtctlmsg*)nlh->nlmsg_data);
-			break;
-		default:
-			break;
-		}
-	}
-	kfree_skb(skb, FREE_READ);
-	fib_unlock();
-	if (!err || rt_nl_flags&RTCTL_ACK)
-	    return totlen;
-	return err;
-}
-
-#endif
-
-
-static int fib_magic(int op, unsigned flags, u32 dst, u32 mask, struct device *dev)
-{
-	struct nlmsghdr n;
-	struct in_rtmsg r;
-	memset(&r, 0, sizeof(r));
-	n.nlmsg_seq=0;
-	n.nlmsg_pid=0;
-	r.rtmsg_metric = MAGIC_METRIC;
-	r.rtmsg_prefix.s_addr = dst;
-	if (dev->flags&IFF_LOOPBACK)
-		flags |= RTF_LOCAL;
-	r.rtmsg_flags = flags;
-	r.rtmsg_prefixlen = 32 - fib_logmask(mask);
-
-	return (op == RTMSG_NEWROUTE ? fib_create : fib_delete)
-		(&r, dev, (flags&RTF_LOCAL) ? &local_class : &main_class, &n);
-}
-
-static void ip_rt_del_broadcasts(struct device *dev)
-{
-	u32 net = dev->pa_addr&dev->pa_mask;
-
-	fib_magic(RTMSG_DELROUTE, RTF_IFBRD, dev->pa_brdaddr, ~0, dev);
-	fib_magic(RTMSG_DELROUTE, RTF_IFBRD, net, ~0, dev);
-	fib_magic(RTMSG_DELROUTE, RTF_IFBRD, net|~dev->pa_mask, ~0, dev);
-}
-
-static void ip_rt_add_broadcasts(struct device *dev, u32 brd, u32 mask)
-{
-	u32 net = dev->pa_addr&mask;
-
-	if (dev->flags&IFF_BROADCAST)
-		fib_magic(RTMSG_NEWROUTE, RTF_IFBRD, brd, ~0, dev);
-
-	if (net && !(mask&htonl(1))) {
-		fib_magic(RTMSG_NEWROUTE, RTF_IFBRD, net, ~0, dev);
-		fib_magic(RTMSG_NEWROUTE, RTF_IFBRD, net|~mask, ~0, dev);
-	}
-}
-
-void ip_rt_change_broadcast(struct device *dev, u32 new_brd)
-{
-	fib_lock();
-	printk(KERN_DEBUG "%s changes brd %08X -> %08X\n",
-	       dev->name, (u32)dev->pa_brdaddr, new_brd);
-	if (!ZERONET(dev->pa_addr) && dev->flags&IFF_BROADCAST) {
-		fib_magic(RTMSG_DELROUTE, RTF_IFBRD, dev->pa_brdaddr, ~0, dev);
-		rtmsg_dev(RTMSG_DELDEVICE, dev, NULL);
-		rtmsg_dev(RTMSG_NEWDEVICE, dev, NULL);
-		ip_rt_add_broadcasts(dev, new_brd, dev->pa_mask);
-	}
-	fib_unlock();
-}
-
-void ip_rt_change_dstaddr(struct device *dev, u32 dstaddr)
-{
-	fib_lock();
-	if (!ZERONET(dev->pa_addr) && (dev->flags&IFF_POINTOPOINT) && dev->type != ARPHRD_TUNNEL) {
-		printk(KERN_DEBUG "%s changes dst %08X -> %08X\n",
-		       dev->name, (u32)dev->pa_dstaddr, dstaddr);
-		fib_magic(RTMSG_DELROUTE, RTF_IFPREFIX, dev->pa_dstaddr, ~0, dev);
-		rtmsg_dev(RTMSG_DELDEVICE, dev, NULL);
-		rtmsg_dev(RTMSG_NEWDEVICE, dev, NULL);
-		if (dstaddr)
-			fib_magic(RTMSG_NEWROUTE, RTF_IFPREFIX, dstaddr, ~0, dev);
-	}
-	fib_unlock();
-}
-
-void ip_rt_change_netmask(struct device *dev, u32 mask)
-{
-	u32 net;
-
-	fib_lock();
-	printk(KERN_DEBUG "%s changes netmask %08X -> %08X\n",
-	       dev->name, (u32)dev->pa_mask, mask);
-	if (ZERONET(dev->pa_addr)) {
-		fib_unlock();
-		return;
-	}
-	net = dev->pa_addr&dev->pa_mask;
-	fib_magic(RTMSG_DELROUTE, RTF_IFPREFIX, net, dev->pa_mask, dev);
-	ip_rt_del_broadcasts(dev);
-	if (mask != 0xFFFFFFFF && dev->flags&IFF_POINTOPOINT)
-		fib_magic(RTMSG_DELROUTE, RTF_IFPREFIX, dev->pa_dstaddr, ~0, dev);
-	rtmsg_dev(RTMSG_DELDEVICE, dev, NULL);
-
-	if (mask != 0xFFFFFFFF)
-		dev->flags &= ~IFF_POINTOPOINT;
-
-	rtmsg_dev(RTMSG_NEWDEVICE, dev, NULL);
-	net = dev->pa_addr&mask;
-	if (net)
-		fib_magic(RTMSG_NEWROUTE, RTF_IFPREFIX, net, mask, dev);
-	ip_rt_add_broadcasts(dev, dev->pa_addr, mask);
-	fib_unlock();
-}
-
-int ip_rt_event(int event, struct device *dev)
-{
-	fib_lock();
-	if (event == NETDEV_DOWN) {
-		fib_flush(dev);
-		rtmsg_dev(RTMSG_DELDEVICE, dev, NULL);
-		fib_unlock();
-		return NOTIFY_DONE;
-	}
-	if (event == NETDEV_CHANGE) {
-		printk(KERN_DEBUG "%s(%s) changes state fl=%08x pa=%08X/%08X brd=%08X dst=%08X\n",
-		       dev->name, current->comm, dev->flags, (u32)dev->pa_addr, (u32)dev->pa_mask,
-		       (u32)dev->pa_brdaddr, (u32)dev->pa_dstaddr);
-		if (!(dev->flags&IFF_BROADCAST))
-			fib_magic(RTMSG_DELROUTE, RTF_IFBRD, dev->pa_brdaddr, ~0, dev);
-		if (!(dev->flags&IFF_POINTOPOINT))
-			fib_magic(RTMSG_DELROUTE, RTF_IFPREFIX, dev->pa_dstaddr, ~0, dev);
-		else {
-			u32 net = dev->pa_addr&dev->pa_mask;
-			fib_magic(RTMSG_DELROUTE, RTF_IFPREFIX, net, dev->pa_mask, dev);
-			ip_rt_del_broadcasts(dev);
-		}
-		rtmsg_dev(RTMSG_DELDEVICE, dev, NULL);
-	}
-
-	if ((event == NETDEV_UP || event == NETDEV_CHANGE) && !ZERONET(dev->pa_addr)) {
-		if (dev->flags&IFF_POINTOPOINT) {
-			dev->pa_mask = 0xFFFFFFFF;
-			dev->ip_flags &= ~IFF_IP_MASK_OK;
-			dev->flags &= ~IFF_BROADCAST;
-			dev->pa_brdaddr = 0;
-		}
-
-		if (event == NETDEV_UP)
-			printk(KERN_DEBUG "%s UP fl=%08x pa=%08X/%08X brd=%08X dst=%08X\n",
-			       dev->name, dev->flags, (u32)dev->pa_addr,
-			       (u32)dev->pa_mask, (u32)dev->pa_brdaddr, (u32)dev->pa_dstaddr);
-
-		rtmsg_dev(RTMSG_NEWDEVICE, dev, NULL);
-
-		if (dev->flags&IFF_POINTOPOINT) {
-			if (dev->pa_dstaddr && dev->type != ARPHRD_TUNNEL)
-				fib_magic(RTMSG_NEWROUTE, RTF_IFPREFIX, dev->pa_dstaddr, ~0, dev);
-		} else {
-			u32 net = dev->pa_addr&dev->pa_mask;
-
-			if (net)
-				fib_magic(RTMSG_NEWROUTE, RTF_IFPREFIX, net, dev->pa_mask, dev);
-			ip_rt_add_broadcasts(dev, dev->pa_brdaddr, dev->pa_mask);
-		}
-		fib_magic(RTMSG_NEWROUTE, RTF_IFLOCAL, dev->pa_addr, ~0, dev);
-		if (dev == &loopback_dev) {
-			if (dev->pa_addr != htonl(INADDR_LOOPBACK)) {
-				u32 mask = htonl(0xFF000000);
-				fib_magic(RTMSG_NEWROUTE, RTF_IFPREFIX,
-					  htonl(INADDR_LOOPBACK)&mask,
-					  mask, dev);
-				fib_magic(RTMSG_NEWROUTE, RTF_IFLOCAL,
-					  htonl(INADDR_LOOPBACK),
-					  mask, dev);
-			}
-		}
-	}
-	if (event == NETDEV_CHANGEMTU || event == NETDEV_CHANGEADDR)
-		rtmsg_dev(RTMSG_NEWDEVICE, dev, NULL);
-	fib_unlock();
-	return NOTIFY_DONE;
-}
-
-
-__initfunc(void ip_fib_init(void))
-{
-	struct in_rtrulemsg r;
-
-#ifdef CONFIG_PROC_FS
-	proc_net_register(&(struct proc_dir_entry) {
-		PROC_NET_ROUTE, 5, "route",
-		S_IFREG | S_IRUGO, 1, 0, 0,
-		0, &proc_net_inode_operations,
-		fib_get_info
-	});
-	proc_net_register(&(struct proc_dir_entry) {
-		PROC_NET_RTCLASSES, 10, "rt_classes",
-		S_IFREG | S_IRUGO, 1, 0, 0,
-		0, &proc_net_inode_operations,
-		fib_class_get_info
-	});
-	proc_net_register(&(struct proc_dir_entry) {
-		PROC_NET_RTLOCAL, 8, "rt_local",
-		S_IFREG | S_IRUGO, 1, 0, 0,
-		0, &proc_net_inode_operations,
-		fib_local_get_info
-	});
-	proc_net_register(&(struct proc_dir_entry) {
-		PROC_NET_RTRULES, 8, "rt_rules",
-		S_IFREG | S_IRUGO, 1, 0, 0,
-		0, &proc_net_inode_operations,
-		fib_rules_get_info
-	});
-#endif		/* CONFIG_PROC_FS */
-
-	fib_classes[RT_CLASS_LOCAL] = &local_class;
-	fib_classes[RT_CLASS_MAIN] = &main_class;
-	fib_classes[RT_CLASS_DEFAULT] = &default_class;
-
-	memset(&r, 0, sizeof(r));
-	r.rtrmsg_class = RT_CLASS_LOCAL;
-	r.rtrmsg_preference = 0;
-	fib_rule_add(&r, NULL, NULL);
-
-	memset(&r, 0, sizeof(r));
-	r.rtrmsg_class = RT_CLASS_DEFAULT;
-	r.rtrmsg_preference = 255;
-	fib_rule_add(&r, NULL, NULL);
-
-	memset(&r, 0, sizeof(r));
-	r.rtrmsg_class = RT_CLASS_MAIN;
-	r.rtrmsg_preference = 254;
-	fib_rule_add(&r, NULL, NULL);
-
-#ifdef  CONFIG_RTNETLINK
-	netlink_attach(NETLINK_ROUTE, fib_netlink_call);
-#endif
-}

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