diff options
Diffstat (limited to 'pfinet/linux-src/net/ipv4/fib_hash.c')
-rw-r--r-- | pfinet/linux-src/net/ipv4/fib_hash.c | 885 |
1 files changed, 885 insertions, 0 deletions
diff --git a/pfinet/linux-src/net/ipv4/fib_hash.c b/pfinet/linux-src/net/ipv4/fib_hash.c new file mode 100644 index 00000000..d9e029ce --- /dev/null +++ b/pfinet/linux-src/net/ipv4/fib_hash.c @@ -0,0 +1,885 @@ +/* + * 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 FIB: lookup engine and maintenance routines. + * + * Version: $Id: fib_hash.c,v 1.8 1999/03/25 10:04:17 davem Exp $ + * + * 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. + */ + +#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/netlink.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/ip_fib.h> + +#define FTprint(a...) +/* + printk(KERN_DEBUG a) + */ + +/* + These bizarre types are just to force strict type checking. + When I reversed order of bytes and changed to natural mask lengths, + I forgot to make fixes in several places. Now I am lazy to return + it back. + */ + +typedef struct { + u32 datum; +} fn_key_t; + +typedef struct { + u32 datum; +} fn_hash_idx_t; + +struct fib_node +{ + struct fib_node *fn_next; + struct fib_info *fn_info; +#define FIB_INFO(f) ((f)->fn_info) + fn_key_t fn_key; + u8 fn_tos; + u8 fn_type; + u8 fn_scope; + u8 fn_state; +}; + +#define FN_S_ZOMBIE 1 +#define FN_S_ACCESSED 2 + +static int fib_hash_zombies; + +struct fn_zone +{ + struct fn_zone *fz_next; /* Next not empty zone */ + struct fib_node **fz_hash; /* Hash table pointer */ + int fz_nent; /* Number of entries */ + + int fz_divisor; /* Hash divisor */ + u32 fz_hashmask; /* (1<<fz_divisor) - 1 */ +#define FZ_HASHMASK(fz) ((fz)->fz_hashmask) + + int fz_order; /* Zone order */ + u32 fz_mask; +#define FZ_MASK(fz) ((fz)->fz_mask) +}; + +/* NOTE. On fast computers evaluation of fz_hashmask and fz_mask + can be cheaper than memory lookup, so that FZ_* macros are used. + */ + +struct fn_hash +{ + struct fn_zone *fn_zones[33]; + struct fn_zone *fn_zone_list; +}; + +static __inline__ fn_hash_idx_t fn_hash(fn_key_t key, struct fn_zone *fz) +{ + u32 h = ntohl(key.datum)>>(32 - fz->fz_order); + h ^= (h>>20); + h ^= (h>>10); + h ^= (h>>5); + h &= FZ_HASHMASK(fz); + return *(fn_hash_idx_t*)&h; +} + +#define fz_key_0(key) ((key).datum = 0) +#define fz_prefix(key,fz) ((key).datum) + +static __inline__ fn_key_t fz_key(u32 dst, struct fn_zone *fz) +{ + fn_key_t k; + k.datum = dst & FZ_MASK(fz); + return k; +} + +static __inline__ struct fib_node ** fz_chain_p(fn_key_t key, struct fn_zone *fz) +{ + return &fz->fz_hash[fn_hash(key, fz).datum]; +} + +static __inline__ struct fib_node * fz_chain(fn_key_t key, struct fn_zone *fz) +{ + return fz->fz_hash[fn_hash(key, fz).datum]; +} + +extern __inline__ int fn_key_eq(fn_key_t a, fn_key_t b) +{ + return a.datum == b.datum; +} + +extern __inline__ int fn_key_leq(fn_key_t a, fn_key_t b) +{ + return a.datum <= b.datum; +} + +#define FZ_MAX_DIVISOR 1024 + +#ifdef CONFIG_IP_ROUTE_LARGE_TABLES + +static __inline__ void fn_rebuild_zone(struct fn_zone *fz, + struct fib_node **old_ht, + int old_divisor) +{ + int i; + struct fib_node *f, **fp, *next; + + for (i=0; i<old_divisor; i++) { + for (f=old_ht[i]; f; f=next) { + next = f->fn_next; + for (fp = fz_chain_p(f->fn_key, fz); + *fp && fn_key_leq((*fp)->fn_key, f->fn_key); + fp = &(*fp)->fn_next) + /* NONE */; + f->fn_next = *fp; + *fp = f; + } + } +} + +static void fn_rehash_zone(struct fn_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("fn_rehash_zone: hash for zone %d grows from %d\n", fz->fz_order, old_divisor); +#endif + + ht = kmalloc(new_divisor*sizeof(struct fib_node*), 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; + fn_rebuild_zone(fz, old_ht, old_divisor); + end_bh_atomic(); + kfree(old_ht); + } +} +#endif /* CONFIG_IP_ROUTE_LARGE_TABLES */ + +static void fn_free_node(struct fib_node * f) +{ + fib_release_info(FIB_INFO(f)); + kfree_s(f, sizeof(struct fib_node)); +} + + +static struct fn_zone * +fn_new_zone(struct fn_hash *table, int z) +{ + int i; + struct fn_zone *fz = kmalloc(sizeof(struct fn_zone), GFP_KERNEL); + if (!fz) + return NULL; + + memset(fz, 0, sizeof(struct fn_zone)); + if (z) { + 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_order = z; + fz->fz_mask = inet_make_mask(z); + + /* Find the first not empty zone with more specific mask */ + for (i=z+1; i<=32; i++) + if (table->fn_zones[i]) + break; + if (i>32) { + /* No more specific masks, we are the first. */ + fz->fz_next = table->fn_zone_list; + table->fn_zone_list = fz; + } else { + fz->fz_next = table->fn_zones[i]->fz_next; + table->fn_zones[i]->fz_next = fz; + } + table->fn_zones[z] = fz; + return fz; +} + +static int +fn_hash_lookup(struct fib_table *tb, const struct rt_key *key, struct fib_result *res) +{ + int err; + struct fn_zone *fz; + struct fn_hash *t = (struct fn_hash*)tb->tb_data; + + for (fz = t->fn_zone_list; fz; fz = fz->fz_next) { + struct fib_node *f; + fn_key_t k = fz_key(key->dst, fz); + + for (f = fz_chain(k, fz); f; f = f->fn_next) { + if (!fn_key_eq(k, f->fn_key)) { + if (fn_key_leq(k, f->fn_key)) + break; + else + continue; + } +#ifdef CONFIG_IP_ROUTE_TOS + if (f->fn_tos && f->fn_tos != key->tos) + continue; +#endif + f->fn_state |= FN_S_ACCESSED; + + if (f->fn_state&FN_S_ZOMBIE) + continue; + if (f->fn_scope < key->scope) + continue; + + err = fib_semantic_match(f->fn_type, FIB_INFO(f), key, res); + if (err == 0) { + res->type = f->fn_type; + res->scope = f->fn_scope; + res->prefixlen = fz->fz_order; + res->prefix = &fz_prefix(f->fn_key, fz); + return 0; + } + if (err < 0) + return err; + } + } + return 1; +} + +static int fn_hash_last_dflt=-1; + +static int fib_detect_death(struct fib_info *fi, int order, + struct fib_info **last_resort, int *last_idx) +{ + struct neighbour *n; + int state = NUD_NONE; + + n = neigh_lookup(&arp_tbl, &fi->fib_nh[0].nh_gw, fi->fib_dev); + if (n) { + state = n->nud_state; + neigh_release(n); + } + if (state==NUD_REACHABLE) + return 0; + if ((state&NUD_VALID) && order != fn_hash_last_dflt) + return 0; + if ((state&NUD_VALID) || + (*last_idx<0 && order > fn_hash_last_dflt)) { + *last_resort = fi; + *last_idx = order; + } + return 1; +} + +static void +fn_hash_select_default(struct fib_table *tb, const struct rt_key *key, struct fib_result *res) +{ + int order, last_idx; + struct fib_node *f; + struct fib_info *fi = NULL; + struct fib_info *last_resort; + struct fn_hash *t = (struct fn_hash*)tb->tb_data; + struct fn_zone *fz = t->fn_zones[0]; + + if (fz == NULL) + return; + + last_idx = -1; + last_resort = NULL; + order = -1; + + for (f = fz->fz_hash[0]; f; f = f->fn_next) { + struct fib_info *next_fi = FIB_INFO(f); + + if ((f->fn_state&FN_S_ZOMBIE) || + f->fn_scope != res->scope || + f->fn_type != RTN_UNICAST) + continue; + + if (next_fi->fib_priority > res->fi->fib_priority) + break; + if (!next_fi->fib_nh[0].nh_gw || next_fi->fib_nh[0].nh_scope != RT_SCOPE_LINK) + continue; + f->fn_state |= FN_S_ACCESSED; + + if (fi == NULL) { + if (next_fi != res->fi) + break; + } else if (!fib_detect_death(fi, order, &last_resort, &last_idx)) { + res->fi = fi; + fn_hash_last_dflt = order; + return; + } + fi = next_fi; + order++; + } + + if (order<=0 || fi==NULL) { + fn_hash_last_dflt = -1; + return; + } + + if (!fib_detect_death(fi, order, &last_resort, &last_idx)) { + res->fi = fi; + fn_hash_last_dflt = order; + return; + } + + if (last_idx >= 0) + res->fi = last_resort; + fn_hash_last_dflt = last_idx; +} + +#define FIB_SCAN(f, fp) \ +for ( ; ((f) = *(fp)) != NULL; (fp) = &(f)->fn_next) + +#define FIB_SCAN_KEY(f, fp, key) \ +for ( ; ((f) = *(fp)) != NULL && fn_key_eq((f)->fn_key, (key)); (fp) = &(f)->fn_next) + +#ifndef CONFIG_IP_ROUTE_TOS +#define FIB_SCAN_TOS(f, fp, key, tos) FIB_SCAN_KEY(f, fp, key) +#else +#define FIB_SCAN_TOS(f, fp, key, tos) \ +for ( ; ((f) = *(fp)) != NULL && fn_key_eq((f)->fn_key, (key)) && \ + (f)->fn_tos == (tos) ; (fp) = &(f)->fn_next) +#endif + + +#ifdef CONFIG_RTNETLINK +static void rtmsg_fib(int, struct fib_node*, int, int, + struct nlmsghdr *n, + struct netlink_skb_parms *); +#else +#define rtmsg_fib(a, b, c, d, e, f) +#endif + + +static int +fn_hash_insert(struct fib_table *tb, struct rtmsg *r, struct kern_rta *rta, + struct nlmsghdr *n, struct netlink_skb_parms *req) +{ + struct fn_hash *table = (struct fn_hash*)tb->tb_data; + struct fib_node *new_f, *f, **fp, **del_fp; + struct fn_zone *fz; + struct fib_info *fi; + + int z = r->rtm_dst_len; + int type = r->rtm_type; +#ifdef CONFIG_IP_ROUTE_TOS + u8 tos = r->rtm_tos; +#endif + fn_key_t key; + int err; + +FTprint("tb(%d)_insert: %d %08x/%d %d %08x\n", tb->tb_id, r->rtm_type, rta->rta_dst ? +*(u32*)rta->rta_dst : 0, z, rta->rta_oif ? *rta->rta_oif : -1, +rta->rta_prefsrc ? *(u32*)rta->rta_prefsrc : 0); + if (z > 32) + return -EINVAL; + fz = table->fn_zones[z]; + if (!fz && !(fz = fn_new_zone(table, z))) + return -ENOBUFS; + + fz_key_0(key); + if (rta->rta_dst) { + u32 dst; + memcpy(&dst, rta->rta_dst, 4); + if (dst & ~FZ_MASK(fz)) + return -EINVAL; + key = fz_key(dst, fz); + } + + if ((fi = fib_create_info(r, rta, n, &err)) == NULL) + return err; + +#ifdef CONFIG_IP_ROUTE_LARGE_TABLES + if (fz->fz_nent > (fz->fz_divisor<<2) && + fz->fz_divisor < FZ_MAX_DIVISOR && + (z==32 || (1<<z) > fz->fz_divisor)) + fn_rehash_zone(fz); +#endif + + fp = fz_chain_p(key, fz); + + /* + * Scan list to find the first route with the same destination + */ + FIB_SCAN(f, fp) { + if (fn_key_leq(key,f->fn_key)) + break; + } + +#ifdef CONFIG_IP_ROUTE_TOS + /* + * Find route with the same destination and tos. + */ + FIB_SCAN_KEY(f, fp, key) { + if (f->fn_tos <= tos) + break; + } +#endif + + del_fp = NULL; + + if (f && (f->fn_state&FN_S_ZOMBIE) && +#ifdef CONFIG_IP_ROUTE_TOS + f->fn_tos == tos && +#endif + fn_key_eq(f->fn_key, key)) { + del_fp = fp; + fp = &f->fn_next; + f = *fp; + goto create; + } + + FIB_SCAN_TOS(f, fp, key, tos) { + if (fi->fib_priority <= FIB_INFO(f)->fib_priority) + break; + } + + /* Now f==*fp points to the first node with the same + keys [prefix,tos,priority], if such key already + exists or to the node, before which we will insert new one. + */ + + if (f && +#ifdef CONFIG_IP_ROUTE_TOS + f->fn_tos == tos && +#endif + fn_key_eq(f->fn_key, key) && + fi->fib_priority == FIB_INFO(f)->fib_priority) { + struct fib_node **ins_fp; + + err = -EEXIST; + if (n->nlmsg_flags&NLM_F_EXCL) + goto out; + + if (n->nlmsg_flags&NLM_F_REPLACE) { + del_fp = fp; + fp = &f->fn_next; + f = *fp; + goto replace; + } + + ins_fp = fp; + err = -EEXIST; + + FIB_SCAN_TOS(f, fp, key, tos) { + if (fi->fib_priority != FIB_INFO(f)->fib_priority) + break; + if (f->fn_type == type && f->fn_scope == r->rtm_scope + && FIB_INFO(f) == fi) + goto out; + } + + if (!(n->nlmsg_flags&NLM_F_APPEND)) { + fp = ins_fp; + f = *fp; + } + } + +create: + err = -ENOENT; + if (!(n->nlmsg_flags&NLM_F_CREATE)) + goto out; + +replace: + err = -ENOBUFS; + new_f = (struct fib_node *) kmalloc(sizeof(struct fib_node), GFP_KERNEL); + if (new_f == NULL) + goto out; + + memset(new_f, 0, sizeof(struct fib_node)); + + new_f->fn_key = key; +#ifdef CONFIG_IP_ROUTE_TOS + new_f->fn_tos = tos; +#endif + new_f->fn_type = type; + new_f->fn_scope = r->rtm_scope; + FIB_INFO(new_f) = fi; + + /* + * Insert new entry to the list. + */ + + new_f->fn_next = f; + *fp = new_f; + fz->fz_nent++; + + if (del_fp) { + f = *del_fp; + /* Unlink replaced node */ + *del_fp = f->fn_next; + synchronize_bh(); + + if (!(f->fn_state&FN_S_ZOMBIE)) + rtmsg_fib(RTM_DELROUTE, f, z, tb->tb_id, n, req); + if (f->fn_state&FN_S_ACCESSED) + rt_cache_flush(-1); + fn_free_node(f); + fz->fz_nent--; + } else { + rt_cache_flush(-1); + } + rtmsg_fib(RTM_NEWROUTE, new_f, z, tb->tb_id, n, req); + return 0; + +out: + fib_release_info(fi); + return err; +} + + +static int +fn_hash_delete(struct fib_table *tb, struct rtmsg *r, struct kern_rta *rta, + struct nlmsghdr *n, struct netlink_skb_parms *req) +{ + struct fn_hash *table = (struct fn_hash*)tb->tb_data; + struct fib_node **fp, **del_fp, *f; + int z = r->rtm_dst_len; + struct fn_zone *fz; + fn_key_t key; + int matched; +#ifdef CONFIG_IP_ROUTE_TOS + u8 tos = r->rtm_tos; +#endif + +FTprint("tb(%d)_delete: %d %08x/%d %d\n", tb->tb_id, r->rtm_type, rta->rta_dst ? + *(u32*)rta->rta_dst : 0, z, rta->rta_oif ? *rta->rta_oif : -1); + if (z > 32) + return -EINVAL; + if ((fz = table->fn_zones[z]) == NULL) + return -ESRCH; + + fz_key_0(key); + if (rta->rta_dst) { + u32 dst; + memcpy(&dst, rta->rta_dst, 4); + if (dst & ~FZ_MASK(fz)) + return -EINVAL; + key = fz_key(dst, fz); + } + + fp = fz_chain_p(key, fz); + + FIB_SCAN(f, fp) { + if (fn_key_eq(f->fn_key, key)) + break; + if (fn_key_leq(key, f->fn_key)) + return -ESRCH; + } +#ifdef CONFIG_IP_ROUTE_TOS + FIB_SCAN_KEY(f, fp, key) { + if (f->fn_tos == tos) + break; + } +#endif + + matched = 0; + del_fp = NULL; + FIB_SCAN_TOS(f, fp, key, tos) { + struct fib_info * fi = FIB_INFO(f); + + if (f->fn_state&FN_S_ZOMBIE) + return -ESRCH; + + matched++; + + if (del_fp == NULL && + (!r->rtm_type || f->fn_type == r->rtm_type) && + (r->rtm_scope == RT_SCOPE_NOWHERE || f->fn_scope == r->rtm_scope) && + (!r->rtm_protocol || fi->fib_protocol == r->rtm_protocol) && + fib_nh_match(r, n, rta, fi) == 0) + del_fp = fp; + } + + if (del_fp) { + f = *del_fp; + rtmsg_fib(RTM_DELROUTE, f, z, tb->tb_id, n, req); + + if (matched != 1) { + *del_fp = f->fn_next; + synchronize_bh(); + + if (f->fn_state&FN_S_ACCESSED) + rt_cache_flush(-1); + fn_free_node(f); + fz->fz_nent--; + } else { + f->fn_state |= FN_S_ZOMBIE; + if (f->fn_state&FN_S_ACCESSED) { + f->fn_state &= ~FN_S_ACCESSED; + rt_cache_flush(-1); + } + if (++fib_hash_zombies > 128) + fib_flush(); + } + + return 0; + } + return -ESRCH; +} + +extern __inline__ int +fn_flush_list(struct fib_node ** fp, int z, struct fn_hash *table) +{ + int found = 0; + struct fib_node *f; + + while ((f = *fp) != NULL) { + struct fib_info *fi = FIB_INFO(f); + + if (fi && ((f->fn_state&FN_S_ZOMBIE) || (fi->fib_flags&RTNH_F_DEAD))) { + *fp = f->fn_next; + synchronize_bh(); + + fn_free_node(f); + found++; + continue; + } + fp = &f->fn_next; + } + return found; +} + +static int fn_hash_flush(struct fib_table *tb) +{ + struct fn_hash *table = (struct fn_hash*)tb->tb_data; + struct fn_zone *fz; + int found = 0; + + fib_hash_zombies = 0; + for (fz = table->fn_zone_list; fz; fz = fz->fz_next) { + int i; + int tmp = 0; + for (i=fz->fz_divisor-1; i>=0; i--) + tmp += fn_flush_list(&fz->fz_hash[i], fz->fz_order, table); + fz->fz_nent -= tmp; + found += tmp; + } + return found; +} + + +#ifdef CONFIG_PROC_FS + +static int fn_hash_get_info(struct fib_table *tb, char *buffer, int first, int count) +{ + struct fn_hash *table = (struct fn_hash*)tb->tb_data; + struct fn_zone *fz; + int pos = 0; + int n = 0; + + for (fz=table->fn_zone_list; fz; fz = fz->fz_next) { + int i; + struct fib_node *f; + int maxslot = fz->fz_divisor; + struct fib_node **fp = fz->fz_hash; + + if (fz->fz_nent == 0) + continue; + + if (pos + fz->fz_nent <= first) { + pos += fz->fz_nent; + continue; + } + + for (i=0; i < maxslot; i++, fp++) { + for (f = *fp; f; f = f->fn_next) { + if (++pos <= first) + continue; + fib_node_get_info(f->fn_type, + f->fn_state&FN_S_ZOMBIE, + FIB_INFO(f), + fz_prefix(f->fn_key, fz), + FZ_MASK(fz), buffer); + buffer += 128; + if (++n >= count) + return n; + } + } + } + return n; +} +#endif + + +#ifdef CONFIG_RTNETLINK + +extern __inline__ int +fn_hash_dump_bucket(struct sk_buff *skb, struct netlink_callback *cb, + struct fib_table *tb, + struct fn_zone *fz, + struct fib_node *f) +{ + int i, s_i; + + s_i = cb->args[3]; + for (i=0; f; i++, f=f->fn_next) { + if (i < s_i) continue; + if (f->fn_state&FN_S_ZOMBIE) continue; + if (fib_dump_info(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, + RTM_NEWROUTE, + tb->tb_id, (f->fn_state&FN_S_ZOMBIE) ? 0 : f->fn_type, f->fn_scope, + &f->fn_key, fz->fz_order, f->fn_tos, + f->fn_info) < 0) { + cb->args[3] = i; + return -1; + } + } + cb->args[3] = i; + return skb->len; +} + +extern __inline__ int +fn_hash_dump_zone(struct sk_buff *skb, struct netlink_callback *cb, + struct fib_table *tb, + struct fn_zone *fz) +{ + int h, s_h; + + s_h = cb->args[2]; + for (h=0; h < fz->fz_divisor; h++) { + if (h < s_h) continue; + if (h > s_h) + memset(&cb->args[3], 0, sizeof(cb->args) - 3*sizeof(cb->args[0])); + if (fz->fz_hash == NULL || fz->fz_hash[h] == NULL) + continue; + if (fn_hash_dump_bucket(skb, cb, tb, fz, fz->fz_hash[h]) < 0) { + cb->args[2] = h; + return -1; + } + } + cb->args[2] = h; + return skb->len; +} + +static int fn_hash_dump(struct fib_table *tb, struct sk_buff *skb, struct netlink_callback *cb) +{ + int m, s_m; + struct fn_zone *fz; + struct fn_hash *table = (struct fn_hash*)tb->tb_data; + + s_m = cb->args[1]; + for (fz = table->fn_zone_list, m=0; fz; fz = fz->fz_next, m++) { + if (m < s_m) continue; + if (m > s_m) + memset(&cb->args[2], 0, sizeof(cb->args) - 2*sizeof(cb->args[0])); + if (fn_hash_dump_zone(skb, cb, tb, fz) < 0) { + cb->args[1] = m; + return -1; + } + } + cb->args[1] = m; + return skb->len; +} + +static void rtmsg_fib(int event, struct fib_node* f, int z, int tb_id, + struct nlmsghdr *n, struct netlink_skb_parms *req) +{ + struct sk_buff *skb; + u32 pid = req ? req->pid : 0; + int size = NLMSG_SPACE(sizeof(struct rtmsg)+256); + + skb = alloc_skb(size, GFP_KERNEL); + if (!skb) + return; + + if (fib_dump_info(skb, pid, n->nlmsg_seq, event, tb_id, + f->fn_type, f->fn_scope, &f->fn_key, z, f->fn_tos, + FIB_INFO(f)) < 0) { + kfree_skb(skb); + return; + } + NETLINK_CB(skb).dst_groups = RTMGRP_IPV4_ROUTE; + if (n->nlmsg_flags&NLM_F_ECHO) + atomic_inc(&skb->users); + netlink_broadcast(rtnl, skb, pid, RTMGRP_IPV4_ROUTE, GFP_KERNEL); + if (n->nlmsg_flags&NLM_F_ECHO) + netlink_unicast(rtnl, skb, pid, MSG_DONTWAIT); +} + +#endif /* CONFIG_RTNETLINK */ + +#ifdef CONFIG_IP_MULTIPLE_TABLES +struct fib_table * fib_hash_init(int id) +#else +__initfunc(struct fib_table * fib_hash_init(int id)) +#endif +{ + struct fib_table *tb; + tb = kmalloc(sizeof(struct fib_table) + sizeof(struct fn_hash), GFP_KERNEL); + if (tb == NULL) + return NULL; + tb->tb_id = id; + tb->tb_lookup = fn_hash_lookup; + tb->tb_insert = fn_hash_insert; + tb->tb_delete = fn_hash_delete; + tb->tb_flush = fn_hash_flush; + tb->tb_select_default = fn_hash_select_default; +#ifdef CONFIG_RTNETLINK + tb->tb_dump = fn_hash_dump; +#endif +#ifdef CONFIG_PROC_FS + tb->tb_get_info = fn_hash_get_info; +#endif + memset(tb->tb_data, 0, sizeof(struct fn_hash)); + return tb; +} |