patch-2.1.15 linux/net/ipv4/fib.c
Next file: linux/net/ipv4/icmp.c
Previous file: linux/net/ipv4/devinet.c
Back to the patch index
Back to the overall index
- Lines: 2057
- Date:
Thu Dec 12 16:54:23 1996
- Orig file:
v2.1.14/linux/net/ipv4/fib.c
- Orig date:
Thu Jan 1 02:00:00 1970
diff -u --recursive --new-file v2.1.14/linux/net/ipv4/fib.c linux/net/ipv4/fib.c
@@ -0,0 +1,2056 @@
+/*
+ * 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 <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;
+atomic_t fib_users;
+
+static void fib_lock(void)
+{
+ while (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) ||
+ ((dst^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_netmask(logmask);
+
+ if (op)
+ return arp_req_set(&r, NULL);
+
+ fz = &local_class.fib_zone_list[logmask];
+
+ for (f1 = fz_hash(f->fib_key, fz); f1; f1=f1->fib_next) {
+ if (f->fib_key != f1->fib_key || f1->fib_flag ||
+ f->fib_info->fib_flags != f1->fib_info->fib_flags)
+ 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;
+
+ 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;
+ 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();
+#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_device[0] = 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_device[0]) {
+ dev = dev_get(r->rtmsg_device);
+ 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_device[0]) {
+ dev = dev_get(r->rtrmsg_device);
+ 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)
+{
+ int err;
+ struct rtentry r;
+
+ err = copy_from_user(&r, arg, sizeof(struct rtentry));
+ if (err)
+ return -EFAULT;
+ if (r.rt_dev)
+ if (copy_from_user(&rtm->rtmsg_device, r.rt_dev, 15))
+ return -EFAULT;
+
+ 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_WARNING "%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;
+ err = copy_from_user(&dummy_nlh, arg, sizeof(dummy_nlh));
+ if (err)
+ return err;
+ switch (dummy_nlh.nlmsg_type)
+ {
+ case RTMSG_NEWROUTE:
+ case RTMSG_DELROUTE:
+ if (dummy_nlh.nlmsg_len < sizeof(m.rtmsg) + sizeof(dummy_nlh))
+ return -EINVAL;
+ err = copy_from_user(&m.rtmsg, arg+sizeof(dummy_nlh), sizeof(m.rtmsg));
+ if (err)
+ return err;
+ 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;
+ err = copy_from_user(&m.rtrmsg, arg+sizeof(dummy_nlh), sizeof(m.rtrmsg));
+ if (err)
+ return err;
+ 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;
+ err = copy_from_user(&m.ifmsg, arg+sizeof(dummy_nlh), sizeof(m.ifmsg));
+ if (err)
+ return err;
+ 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;
+ err = copy_from_user(&m.rtcmsg, arg+sizeof(dummy_nlh), sizeof(m.rtcmsg));
+ if (err)
+ return err;
+ 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;
+ memset(r->rtmsg_device, 0, sizeof(r->rtmsg_device));
+ if (fi->fib_dev)
+ strcpy(r->rtmsg_device, fi->fib_dev->name);
+ }
+ }
+ 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);
+ strcpy(r->ifmsg_device, 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);
+}
+
+void ip_rt_change_broadcast(struct device *dev, u32 new_brd)
+{
+ fib_lock();
+ printk(KERN_DEBUG "%s changes brd %08lX -> %08X\n",
+ dev->name, 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);
+ fib_magic(RTMSG_NEWROUTE, RTF_IFBRD, new_brd, ~0, dev);
+ }
+ 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 %08lX -> %08X\n",
+ dev->name, 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 %08lX -> %08X\n",
+ dev->name, 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);
+ fib_magic(RTMSG_DELROUTE, RTF_IFBRD, net, ~0, dev);
+ fib_magic(RTMSG_DELROUTE, RTF_IFBRD, net|~dev->pa_mask, ~0, 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);
+
+ 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);
+ if (net && mask != 0xFFFFFFFF) {
+ fib_magic(RTMSG_NEWROUTE, RTF_IFBRD, net, ~0, dev);
+ fib_magic(RTMSG_NEWROUTE, RTF_IFBRD, net|~mask, ~0, dev);
+ }
+ 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=%08lX/%08lX brd=%08lX dst=%08lX\n",
+ dev->name, current->comm, dev->flags, dev->pa_addr, dev->pa_mask,
+ dev->pa_brdaddr, 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);
+ fib_magic(RTMSG_DELROUTE, RTF_IFBRD, net, ~0, dev);
+ fib_magic(RTMSG_DELROUTE, RTF_IFBRD, net|~dev->pa_mask, ~0, 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=%08lX/%08lX brd=%08lX dst=%08lX\n",
+ dev->name, dev->flags, dev->pa_addr,
+ dev->pa_mask, dev->pa_brdaddr, 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 if (dev->pa_addr&dev->pa_mask) {
+ u32 net = dev->pa_addr&dev->pa_mask;
+
+ fib_magic(RTMSG_NEWROUTE, RTF_IFPREFIX, net, dev->pa_mask, dev);
+ if (dev->pa_mask != 0xFFFFFFFF) {
+ fib_magic(RTMSG_NEWROUTE, RTF_IFBRD, net, ~0, dev);
+ fib_magic(RTMSG_NEWROUTE, RTF_IFBRD, net|~dev->pa_mask, ~0, dev);
+ }
+ }
+ 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 (dev->flags&IFF_BROADCAST)
+ fib_magic(RTMSG_NEWROUTE, RTF_IFBRD, dev->pa_brdaddr, ~0, dev);
+ }
+ fib_unlock();
+ return NOTIFY_DONE;
+}
+
+
+void ip_fib_init()
+{
+ 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_RTRULES, 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