diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-02-16 01:07:24 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-02-16 01:07:24 +0000 |
commit | 95db6b748fc86297827fbd9c9ef174d491c9ad89 (patch) | |
tree | 27a92a942821cde1edda9a1b088718d436b3efe4 /net/decnet/dn_fib.c | |
parent | 45b27b0a0652331d104c953a5b192d843fff88f8 (diff) |
Merge with Linux 2.3.40.
Diffstat (limited to 'net/decnet/dn_fib.c')
-rw-r--r-- | net/decnet/dn_fib.c | 1027 |
1 files changed, 437 insertions, 590 deletions
diff --git a/net/decnet/dn_fib.c b/net/decnet/dn_fib.c index 358e9a184..aff2dc05d 100644 --- a/net/decnet/dn_fib.c +++ b/net/decnet/dn_fib.c @@ -3,13 +3,15 @@ * operating system. DECnet is implemented using the BSD Socket * interface as the means of communication with the user level. * - * DECnet Routing Forwarding Information Base + * DECnet Routing Forwarding Information Base (Glue/Info List) * * Author: Steve Whitehouse <SteveW@ACM.org> * * * Changes: * Alexey Kuznetsov : SMP locking changes + * Steve Whitehouse : Rewrote it... Well to be more correct, I + * copied most of it from the ipv4 fib code. * */ #include <linux/config.h> @@ -30,451 +32,376 @@ #include <net/neighbour.h> #include <net/dst.h> #include <net/dn.h> +#include <net/dn_route.h> #include <net/dn_fib.h> #include <net/dn_neigh.h> #include <net/dn_dev.h> -/* - * N.B. Some of the functions here should really be inlines, but - * I'll sort out that when its all working properly, for now the - * stack frames will be useful for debugging. - */ -#define DN_NUM_TABLES 255 -#define DN_MIN_TABLE 1 -#define DN_L1_TABLE 1 -#define DN_L2_TABLE 2 - -#ifdef CONFIG_RTNETLINK -static int dn_fib_table_dump(struct dn_fib_table *t, struct sk_buff *skb, struct netlink_callback *cb); -static void dn_rtmsg_fib(int event, int table, struct dn_fib_action *fa, struct nlmsghdr *nlh, struct netlink_skb_parms *req); -extern int dn_cache_dump(struct sk_buff *skb, struct netlink_callback *cb); -#endif /* CONFIG_RTNETLINK */ - -static void dn_fib_del_tree(struct dn_fib_table *t); -static struct dn_fib_table *dn_fib_tables[DN_NUM_TABLES + 1]; -static int dn_fib_allocs = 0; -static int dn_fib_actions = 0; +#define for_fib_info() { struct dn_fib_info *fi;\ + for(fi = dn_fib_info_list; fi; fi = fi->fib_next) +#define endfor_fib_info() } -static struct dn_fib_node *dn_fib_alloc(void) -{ - struct dn_fib_node *fn; +#define for_nexthops(fi) { int nhsel; const struct dn_fib_nh *nh;\ + for(nhsel = 0, nh = (fi)->fib_nh; nhsel < (fi)->fib_nhs; nh++, nhsel++) - fn = kmalloc(sizeof(struct dn_fib_node), GFP_KERNEL); +#define change_nexthops(fi) { int nhsel; struct dn_fib_nh *nh;\ + for(nhsel = 0, nh = (struct dn_fib_nh *)((fi)->fib_nh); nhsel < (fi)->fib_nhs; nh++, nhsel++) - if (fn) { - memset(fn, 0, sizeof(struct dn_fib_node)); - dn_fib_allocs++; - } +#define endfor_nexthops(fi) } - return fn; -} +#ifdef CONFIG_RTNETLINK +extern int dn_cache_dump(struct sk_buff *skb, struct netlink_callback *cb); +#endif /* CONFIG_RTNETLINK */ -static __inline__ void dn_fib_free(struct dn_fib_node *fn) -{ - kfree_s(fn, sizeof(struct dn_fib_node)); - dn_fib_allocs--; -} +static struct dn_fib_info *dn_fib_info_list = NULL; +static rwlock_t dn_fib_info_lock = RW_LOCK_UNLOCKED; +int dn_fib_info_cnt; + +static struct +{ + int error; + u8 scope; +} dn_fib_props[RTA_MAX+1] = { + { 0, RT_SCOPE_NOWHERE }, /* RTN_UNSPEC */ + { 0, RT_SCOPE_UNIVERSE }, /* RTN_UNICAST */ + { 0, RT_SCOPE_HOST }, /* RTN_LOCAL */ + { -EINVAL, RT_SCOPE_NOWHERE }, /* RTN_BROADCAST */ + { -EINVAL, RT_SCOPE_NOWHERE }, /* RTN_ANYCAST */ + { -EINVAL, RT_SCOPE_NOWHERE }, /* RTN_MULTICAST */ + { -EINVAL, RT_SCOPE_UNIVERSE }, /* RTN_BLACKHOLE */ + { -EHOSTUNREACH, RT_SCOPE_UNIVERSE }, /* RTN_UNREACHABLE */ + { -EACCES, RT_SCOPE_UNIVERSE }, /* RTN_PROHIBIT */ + { -EAGAIN, RT_SCOPE_UNIVERSE }, /* RTN_THROW */ + { -EINVAL, RT_SCOPE_NOWHERE }, /* RTN_NAT */ + { -EINVAL, RT_SCOPE_NOWHERE } /* RTN_XRESOLVE */ +}; -static struct dn_fib_action *dn_fib_new_action(void) +void dn_fib_free_info(struct dn_fib_info *fi) { - struct dn_fib_action *fa; - - fa = kmalloc(sizeof(struct dn_fib_action), GFP_KERNEL); - - if (fa) { - memset(fa, 0, sizeof(struct dn_fib_action)); - dn_fib_actions++; + if (fi->fib_dead == 0) { + printk(KERN_DEBUG "DECnet: BUG! Attempt to free alive dn_fib_info\n"); + return; } - return fa; -} - -static __inline__ void dn_fib_del_action(struct dn_fib_action *fa) -{ - if ((fa->fa_type == RTN_UNICAST) && fa->fa_neigh) - neigh_release(fa->fa_neigh); - - kfree_s(fa, sizeof(struct dn_fib_action)); - dn_fib_actions--; -} - -static struct dn_fib_node *dn_fib_follow(struct dn_fib_node *fn, dn_address key) -{ - while(fn->fn_action == NULL) - fn = DN_FIB_NEXT(fn, key); - - return fn; -} - - -static struct dn_fib_node *dn_fib_follow1(struct dn_fib_node *fn, dn_address key) -{ - while((fn->fn_action == NULL) && (((key ^ fn->fn_key) >> fn->fn_shift) == 0)) - fn = DN_FIB_NEXT(fn, key); - - return fn; + change_nexthops(fi) { + if (nh->nh_dev) + dev_put(nh->nh_dev); + nh->nh_dev = NULL; + } endfor_nexthops(fi); + dn_fib_info_cnt--; + kfree(fi); +} + +void dn_fib_release_info(struct dn_fib_info *fi) +{ + write_lock(&dn_fib_info_lock); + if (fi && --fi->fib_treeref == 0) { + 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 == dn_fib_info_list) + dn_fib_info_list = fi->fib_next; + fi->fib_dead = 1; + dn_fib_info_put(fi); + } + write_unlock(&dn_fib_info_lock); } - -static int dn_fib_table_insert1(struct dn_fib_table *t, struct dn_fib_node *leaf) +static __inline__ int dn_fib_nh_comp(const struct dn_fib_info *fi, const struct dn_fib_info *ofi) { - struct dn_fib_node *fn, *fn1, *fn2; - int shift = -1; - dn_address match; - dn_address cmpmask = 1; - - if (!t->root) { - t->root = leaf; - t->count++; - return 0; - } - - fn1 = dn_fib_follow1(t->root, leaf->fn_key); - fn2 = fn1->fn_up; - - if (fn1->fn_key == leaf->fn_key) - return -EEXIST; - - if ((fn = dn_fib_alloc()) == NULL) - return -ENOBUFS; - - fn->fn_key = leaf->fn_key; - match = fn1->fn_key ^ fn->fn_key; - - while(match) { - match >>= 1; - shift++; - } - cmpmask <<= shift; - - fn->fn_cmpmask = cmpmask; - fn->fn_shift = shift; - - if (fn2) { - DN_FIB_NEXT(fn2, fn->fn_key) = fn; - } else { - t->root = fn; - } - - t->count++; - fn->fn_up = fn2; - DN_FIB_NEXT(fn, fn1->fn_key) = fn1; - DN_FIB_NEXT(fn, leaf->fn_key) = leaf; + const struct dn_fib_nh *onh = ofi->fib_nh; + for_nexthops(fi) { + if (nh->nh_oif != onh->nh_oif || + nh->nh_gw != onh->nh_gw || + nh->nh_scope != onh->nh_scope || + nh->nh_weight != onh->nh_weight || + ((nh->nh_flags^onh->nh_flags)&~RTNH_F_DEAD)) + return -1; + onh++; + } endfor_nexthops(fi); return 0; } -static __inline__ int dn_maskcmp(dn_address m1, dn_address m2) +static __inline__ struct dn_fib_info *dn_fib_find_info(const struct dn_fib_info *nfi) { - int cmp = 0; - - while(m1 || m2) { - if (m1 & 0x8000) - cmp++; - if (m2 & 0x8000) - cmp--; - m1 <<= 1; - m2 <<= 1; + for_fib_info() { + if (fi->fib_nhs != nfi->fib_nhs) + continue; + if (nfi->fib_protocol == fi->fib_protocol && + nfi->fib_prefsrc == fi->fib_prefsrc && + nfi->fib_priority == fi->fib_priority && + ((nfi->fib_flags^fi->fib_flags)&~RTNH_F_DEAD) == 0 && + (nfi->fib_nhs == 0 || dn_fib_nh_comp(fi, nfi) == 0)) + return fi; + } endfor_fib_info(); + return NULL; +} + +u16 dn_fib_get_attr16(struct rtattr *attr, int attrlen, int type) +{ + while(RTA_OK(attr,attrlen)) { + if (attr->rta_type == type) + return *(u16*)RTA_DATA(attr); + attr = RTA_NEXT(attr, attrlen); } - return cmp; + return 0; } - -static int dn_fib_table_insert(struct dn_fib_table *t, struct dn_fib_action *fa) +static int dn_fib_count_nhs(struct rtattr *rta) { - struct dn_fib_node *fn; - struct dn_fib_action **fap; - int err; - int cmp; - - if (t->root && ((fn = dn_fib_follow(t->root, fa->fa_key)) != NULL) && - (fn->fn_key == fa->fa_key)) - goto add_action; - - if ((fn = dn_fib_alloc()) == NULL) - return -ENOBUFS; - - fn->fn_key = fa->fa_key; - fn->fn_action = fa; - - if ((err = dn_fib_table_insert1(t, fn)) < 0) - dn_fib_free(fn); + int nhs = 0; + struct rtnexthop *nhp = RTA_DATA(rta); + int nhlen = RTA_PAYLOAD(rta); -#ifdef CONFIG_RTNETLINK - if (!err) - dn_rtmsg_fib(RTM_NEWROUTE, t->n, fa, NULL, NULL); -#endif /* CONFIG_RTNETLINK */ - - return err; - -add_action: - fap = &fn->fn_action; - - for(; *fap; fap = &((*fap)->fa_next)) { - if ((cmp = dn_maskcmp((*fap)->fa_mask, fa->fa_mask)) > 0) - break; - if (cmp < 0) - continue; - if ((*fap)->fa_cost > fa->fa_cost) - break; + while(nhlen >= (int)sizeof(struct rtnexthop)) { + if ((nhlen -= nhp->rtnh_len) < 0) + return 0; + nhs++; + nhp = RTNH_NEXT(nhp); } - fa->fa_next = *fap; - *fap = fa; - -#ifdef CONFIG_RTNETLINK - dn_rtmsg_fib(RTM_NEWROUTE, t->n, fa, NULL, NULL); -#endif /* CONFIG_RTNETLINK */ - - return 0; + return nhs; } -static int dn_fib_table_delete1(struct dn_fib_table *t, struct dn_fib_node *fn) +static int dn_fib_get_nhs(struct dn_fib_info *fi, const struct rtattr *rta, const struct rtmsg *r) { - struct dn_fib_node *fn1 = fn->fn_up; - struct dn_fib_node *fn2; - struct dn_fib_node *fn3; - - if (fn == t->root) { - t->root = NULL; - t->count--; - return 0; - } - - if (fn1 == NULL) - return -EINVAL; + struct rtnexthop *nhp = RTA_DATA(rta); + int nhlen = RTA_PAYLOAD(rta); - fn2 = fn1->fn_up; - fn3 = DN_FIB_NEXT(fn1, ~fn->fn_key); + change_nexthops(fi) { + int attrlen = nhlen - sizeof(struct rtnexthop); + if (attrlen < 0 || (nhlen -= nhp->rtnh_len) < 0) + return -EINVAL; - if (fn2) - DN_FIB_NEXT(fn2, fn1->fn_key) = fn3; - else - t->root = fn3; + nh->nh_flags = (r->rtm_flags&~0xFF) | nhp->rtnh_flags; + nh->nh_oif = nhp->rtnh_ifindex; + nh->nh_weight = nhp->rtnh_hops + 1; - fn3->fn_up = fn2; + if (attrlen) { + nh->nh_gw = dn_fib_get_attr16(RTNH_DATA(nhp), attrlen, RTA_GATEWAY); + } + nhp = RTNH_NEXT(nhp); + } endfor_nexthops(fi); - dn_fib_free(fn1); - t->count--; return 0; } -static int dn_fib_table_delete(struct dn_fib_table *t, struct dn_fib_action *fa) + +static int dn_fib_check_nh(const struct rtmsg *r, struct dn_fib_info *fi, struct dn_fib_nh *nh) { - struct dn_fib_res res; - struct dn_fib_node *fn; - struct dn_fib_action **fap, *old; int err; - res.res_type = 0; - res.res_addr = fa->fa_key; - res.res_mask = fa->fa_mask; - res.res_ifindex = fa->fa_ifindex; - res.res_proto = fa->fa_proto; - res.res_cost = fa->fa_cost; - - if ((err = t->lookup(t, &res)) < 0) - return err; - - fn = res.res_fn; - fap = &fn->fn_action; - while((*fap) != res.res_fa) - fap = &((*fap)->fa_next); - old = *fap; - *fap = (*fap)->fa_next; + if (nh->nh_gw) { + struct dn_fib_key key; + struct dn_fib_res res; - if (fn->fn_action == NULL) - dn_fib_table_delete1(t, fn); + if (nh->nh_flags&RTNH_F_ONLINK) { + struct net_device *dev; - if (t->root == NULL) - dn_fib_del_tree(t); - -#ifdef CONFIG_RTNETLINK - dn_rtmsg_fib(RTM_DELROUTE, t->n, old, NULL, NULL); -#endif /* CONFIG_RTNETLINK */ - - dn_fib_del_action(old); + if (r->rtm_scope >= RT_SCOPE_LINK) + return -EINVAL; + if ((dev = __dev_get_by_index(nh->nh_oif)) == NULL) + return -ENODEV; + if (!(dev->flags&IFF_UP)) + return -ENETDOWN; + nh->nh_dev = dev; + atomic_inc(&dev->refcnt); + nh->nh_scope = RT_SCOPE_LINK; + return 0; + } - return 0; -} + memset(&key, 0, sizeof(key)); + key.dst = nh->nh_gw; + key.oif = nh->nh_oif; + key.scope = r->rtm_scope + 1; -static int dn_fib_search(struct dn_fib_node *fn, struct dn_fib_res *res) -{ - struct dn_fib_action *fa = fn->fn_action; + if (key.scope < RT_SCOPE_LINK) + key.scope = RT_SCOPE_LINK; - for(; fa; fa = fa->fa_next) { - if ((fa->fa_key ^ res->res_addr) & fa->fa_mask) - continue; - if (res->res_ifindex && (res->res_ifindex != fa->fa_ifindex)) - continue; - if (res->res_mask && (res->res_mask != fa->fa_mask)) - continue; - if (res->res_proto && (res->res_proto != fa->fa_proto)) - continue; - if (res->res_cost && (res->res_cost != fa->fa_cost)) - continue; + if ((err = dn_fib_lookup(&key, &res)) != 0) + return err; - res->res_fn = fn; - res->res_fa = fa; - return 1; + nh->nh_scope = res.scope; + nh->nh_oif = DN_FIB_RES_OIF(res); + nh->nh_dev = DN_FIB_RES_DEV(res); + if (nh->nh_dev) + atomic_inc(&nh->nh_dev->refcnt); + dn_fib_res_put(&res); + } else { + struct net_device *dev; + + if (nh->nh_flags&(RTNH_F_PERVASIVE|RTNH_F_ONLINK)) + return -EINVAL; + + dev = __dev_get_by_index(nh->nh_oif); + if (dev == NULL || dev->dn_ptr == NULL) + return -ENODEV; + if (!(dev->flags&IFF_UP)) + return -ENETDOWN; + nh->nh_dev = dev; + atomic_inc(&nh->nh_dev->refcnt); + nh->nh_scope = RT_SCOPE_HOST; } return 0; } -static int dn_fib_recurse(struct dn_fib_node *fn, struct dn_fib_res *res) -{ - struct dn_fib_node *fn1; - int err = -ENOENT; - - fn1 = dn_fib_follow(fn, res->res_addr); - - if (dn_fib_search(fn1, res)) - return 0; - while((fn1 = fn1->fn_up) != fn) - if ((err = dn_fib_recurse(DN_FIB_NEXT(fn1, ~res->res_addr), res)) == 0) - break; - - return err; -} - -static int dn_fib_table_lookup(struct dn_fib_table *t, struct dn_fib_res *res) +struct dn_fib_info *dn_fib_create_info(const struct rtmsg *r, struct dn_kern_rta *rta, const struct nlmsghdr *nlh, int *errp) { - struct dn_fib_node *fn = t->root; - int err = -ENOENT; - - if (t->root == NULL) - return err; - - fn = dn_fib_follow(t->root, res->res_addr); - - if (dn_fib_search(fn, res)) - return 0; - - while((fn = fn->fn_up) != NULL) - if ((err = dn_fib_recurse(DN_FIB_NEXT(fn, ~res->res_addr), res)) == 0) - break; + int err; + struct dn_fib_info *fi = NULL; + struct dn_fib_info *ofi; + int nhs = 1; - return err; -} + if (dn_fib_props[r->rtm_type].scope > r->rtm_scope) + goto err_inval; -static int dn_fib_table_walk_recurse(struct dn_fib_walker_t *fwt, struct dn_fib_node *fn) -{ - struct dn_fib_table *t = fwt->table; + if (rta->rta_mp) { + nhs = dn_fib_count_nhs(rta->rta_mp); + if (nhs == 0) + goto err_inval; + } - if (fn->fn_action) { - fwt->fxn(fwt, fn); + fi = kmalloc(sizeof(*fi)+nhs*sizeof(struct dn_fib_nh), GFP_KERNEL); + err = -ENOBUFS; + if (fi == NULL) + goto failure; + memset(fi, 0, sizeof(*fi)+nhs*sizeof(struct dn_fib_nh)); + + fi->fib_protocol = r->rtm_protocol; + fi->fib_nhs = nhs; + fi->fib_flags = r->rtm_flags; + if (rta->rta_priority) + fi->fib_priority = *rta->rta_priority; + if (rta->rta_prefsrc) + memcpy(&fi->fib_prefsrc, rta->rta_prefsrc, 2); + + if (rta->rta_mp) { + if ((err = dn_fib_get_nhs(fi, rta->rta_mp, r)) != 0) + goto failure; + if (rta->rta_oif && fi->fib_nh->nh_oif != *rta->rta_oif) + goto err_inval; + if (rta->rta_gw && memcmp(&fi->fib_nh->nh_gw, rta->rta_gw, 2)) + goto err_inval; } else { - dn_fib_table_walk_recurse(fwt, t->root->fn_children[0]); - dn_fib_table_walk_recurse(fwt, t->root->fn_children[1]); + struct dn_fib_nh *nh = fi->fib_nh; + if (rta->rta_oif) + nh->nh_oif = *rta->rta_oif; + if (rta->rta_gw) + memcpy(&nh->nh_gw, rta->rta_gw, 2); + nh->nh_flags = r->rtm_flags; + nh->nh_weight = 1; } - return 0; -} - -static int dn_fib_table_walk(struct dn_fib_walker_t *fwt) -{ - struct dn_fib_table *t = fwt->table; - - if (t->root != NULL) { - if (t->root->fn_action) { - fwt->fxn(fwt, t->root); - } else { - dn_fib_table_walk_recurse(fwt, t->root->fn_children[0]); - dn_fib_table_walk_recurse(fwt, t->root->fn_children[1]); - } + if (dn_fib_props[r->rtm_type].error) { + if (rta->rta_gw || rta->rta_oif || rta->rta_mp) + goto err_inval; + goto link_it; } - return 0; -} - -static struct dn_fib_table *dn_fib_get_tree(int n, int create) -{ - struct dn_fib_table *t; - - if (n < DN_MIN_TABLE) - return NULL; - - if (n > DN_NUM_TABLES) - return NULL; + if (r->rtm_scope > RT_SCOPE_HOST) + goto err_inval; - if (dn_fib_tables[n]) - return dn_fib_tables[n]; + if (r->rtm_scope == RT_SCOPE_HOST) { + struct dn_fib_nh *nh = fi->fib_nh; - if (!create) - return NULL; - - if ((t = kmalloc(sizeof(struct dn_fib_table), GFP_KERNEL)) == NULL) - return NULL; - - dn_fib_tables[n] = t; - memset(t, 0, sizeof(struct dn_fib_table)); + /* Local address is added */ + if (nhs != 1 || nh->nh_gw) + goto err_inval; + nh->nh_scope = RT_SCOPE_NOWHERE; + nh->nh_dev = dev_get_by_index(fi->fib_nh->nh_oif); + err = -ENODEV; + if (nh->nh_dev == NULL) + goto failure; + } else { + change_nexthops(fi) { + if ((err = dn_fib_check_nh(r, fi, nh)) != 0) + goto failure; + } endfor_nexthops(fi) + } - t->n = n; - t->insert = dn_fib_table_insert; - t->delete = dn_fib_table_delete; - t->lookup = dn_fib_table_lookup; - t->walk = dn_fib_table_walk; -#ifdef CONFIG_RTNETLINK - t->dump = dn_fib_table_dump; +#if I_GET_AROUND_TO_FIXING_PREFSRC + if (fi->fib_prefsrc) { + if (r->rtm_type != RTN_LOCAL || rta->rta_dst == NULL || + memcmp(&fi->fib_prefsrc, rta->rta_dst, 2)) + if (dn_addr_type(fi->fib_prefsrc) != RTN_LOCAL) + goto err_inval; + } #endif - return t; -} - -static void dn_fib_del_tree(struct dn_fib_table *t) -{ - dn_fib_tables[t->n] = NULL; +link_it: + if ((ofi = dn_fib_find_info(fi)) != NULL) { + fi->fib_dead = 1; + dn_fib_free_info(fi); + ofi->fib_treeref++; + return ofi; + } - if (t) { - kfree_s(t, sizeof(struct dn_fib_table)); + fi->fib_treeref++; + atomic_inc(&fi->fib_clntref); + write_lock(&dn_fib_info_lock); + fi->fib_next = dn_fib_info_list; + fi->fib_prev = NULL; + if (dn_fib_info_list) + dn_fib_info_list->fib_prev = fi; + dn_fib_info_list = fi; + dn_fib_info_cnt++; + write_unlock(&dn_fib_info_lock); + return fi; + +err_inval: + err = -EINVAL; + +failure: + *errp = err; + if (fi) { + fi->fib_dead = 1; + dn_fib_free_info(fi); } + + return NULL; } -int dn_fib_resolve(struct dn_fib_res *res) +void dn_fib_select_multipath(const struct dn_fib_key *key, struct dn_fib_res *res) { - int table = DN_L1_TABLE; - int count = 0; - struct dn_fib_action *fa; - int err; - - if ((res->res_addr ^ dn_ntohs(decnet_address)) & 0xfc00) - table = DN_L2_TABLE; - - for(;;) { - struct dn_fib_table *t = dn_fib_get_tree(table, 0); - - if (t == NULL) - return -ENOENT; - - if ((err = t->lookup(t, res)) < 0) - return err; + struct dn_fib_info *fi = res->fi; + int w; - if ((fa = res->res_fa) == NULL) - return -ENOENT; + if (fi->fib_power <= 0) { + int power = 0; + change_nexthops(fi) { + if (!(nh->nh_flags&RTNH_F_DEAD)) { + power += nh->nh_weight; + nh->nh_power = nh->nh_weight; + } + } endfor_nexthops(fi); + fi->fib_power = power; + } - if (fa->fa_type != RTN_THROW) - break; + w = jiffies % fi->fib_power; - table = fa->fa_table; + change_nexthops(fi) { + if (!(nh->nh_flags&RTNH_F_DEAD) && nh->nh_power) { + if ((w -= nh->nh_power) <= 0) { + nh->nh_power--; + fi->fib_power--; + res->nh_sel = nhsel; + return; + } + } + } endfor_nexthops(fi); - if (count++ > DN_NUM_TABLES) - return -ENOENT; - } + printk(KERN_DEBUG "DECnet: BUG! dn_fib_select_multipath\n"); +} - switch(fa->fa_type) { - case RTN_PROHIBIT: - case RTN_UNREACHABLE: - return -fa->fa_error; - } - return 0; -} /* * Punt to user via netlink for example, but for now @@ -489,72 +416,19 @@ int dn_fib_rt_message(struct sk_buff *skb) #ifdef CONFIG_RTNETLINK -static int dn_fib_convert_rtm(struct dn_fib_action *fa, - struct rtmsg *r, struct rtattr **rta, - struct nlmsghdr *n, - struct netlink_skb_parms *req) -{ - dn_address dst, gw, mask = 0xffff; - int ifindex = 0; - struct neighbour *neigh; - struct net_device *dev; - unsigned char addr[ETH_ALEN]; - - if (r->rtm_family != AF_DECnet) - return -EINVAL; - - if (rta[RTA_DST-1]) - memcpy(&dst, RTA_DATA(rta[RTA_DST-1]), 2); - - if (rta[RTA_OIF-1]) - memcpy(&ifindex, RTA_DATA(rta[RTA_OIF-1]), sizeof(int)); - - if (rta[RTA_GATEWAY-1]) - memcpy(&gw, RTA_DATA(rta[RTA_GATEWAY-1]), 2); - - fa->fa_key = dn_ntohs(dst); - fa->fa_mask = dn_ntohs(mask); - fa->fa_ifindex = ifindex; - fa->fa_proto = r->rtm_protocol; - fa->fa_type = r->rtm_type; - - switch(fa->fa_type) { - case RTN_UNICAST: - if ((dev = __dev_get_by_index(ifindex)) == NULL) - return -ENODEV; - dn_dn2eth(addr, dn_ntohs(gw)); - if ((neigh = __neigh_lookup(&dn_neigh_table, &addr, dev, 1)) == NULL) - return -EHOSTUNREACH; - fa->fa_neigh = neigh; - break; - case RTN_THROW: - fa->fa_table = 0; - break; - case RTN_PROHIBIT: - fa->fa_error = EPERM; - break; - case RTN_UNREACHABLE: - fa->fa_error = EHOSTUNREACH; - break; - case RTN_BLACKHOLE: - fa->fa_error = EINVAL; - break; - } - - return 0; -} static int dn_fib_check_attr(struct rtmsg *r, struct rtattr **rta) { - switch(r->rtm_type) { - case RTN_UNICAST: - case RTN_BLACKHOLE: - case RTN_PROHIBIT: - case RTN_UNREACHABLE: - case RTN_THROW: - break; - default: - return -1; + int i; + + for(i = 1; i <= RTA_MAX; i++) { + struct rtattr *attr = rta[i-1]; + if (attr) { + if (RTA_PAYLOAD(attr) < 4 && RTA_PAYLOAD(attr) != 2) + return -EINVAL; + if (i != RTA_MULTIPATH && i != RTA_METRICS) + rta[i-1] = (struct rtattr *)RTA_DATA(attr); + } } return 0; @@ -562,114 +436,36 @@ static int dn_fib_check_attr(struct rtmsg *r, struct rtattr **rta) int dn_fib_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) { - struct dn_fib_table *t; + struct dn_fib_table *tb; struct rtattr **rta = arg; struct rtmsg *r = NLMSG_DATA(nlh); - struct dn_fib_action *fa; - int err; if (dn_fib_check_attr(r, rta)) return -EINVAL; - if ((fa = dn_fib_new_action()) == NULL) - return -ENOBUFS; + tb = dn_fib_get_table(r->rtm_table, 0); + if (tb) + return tb->delete(tb, r, (struct dn_kern_rta *)rta, nlh, &NETLINK_CB(skb)); - t = dn_fib_get_tree(r->rtm_table, 0); - if (t) { - if ((err = dn_fib_convert_rtm(fa, r, rta, nlh, &NETLINK_CB(skb))) < 0) { - dn_fib_del_action(fa); - return err; - } - err = t->delete(t, fa); - dn_fib_del_action(fa); - return err; - } return -ESRCH; } int dn_fib_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) { - struct dn_fib_table *t; + struct dn_fib_table *tb; struct rtattr **rta = arg; struct rtmsg *r = NLMSG_DATA(nlh); - struct dn_fib_action *fa; - int err; if (dn_fib_check_attr(r, rta)) return -EINVAL; - if ((fa = dn_fib_new_action()) == NULL) - return -ENOBUFS; + tb = dn_fib_get_table(r->rtm_table, 1); + if (tb) + return tb->insert(tb, r, (struct dn_kern_rta *)rta, nlh, &NETLINK_CB(skb)); - t = dn_fib_get_tree(r->rtm_table, 1); - if (t) { - if ((err = dn_fib_convert_rtm(fa, r, rta, nlh, &NETLINK_CB(skb))) < 0) { - dn_fib_del_action(fa); - return err; - } - return t->insert(t, fa); - } return -ENOBUFS; } -int dn_fib_dump_info(struct sk_buff *skb, u32 pid, u32 seq, int event, - int table, struct dn_fib_action *fa) -{ - struct rtmsg *rtm; - struct nlmsghdr *nlh; - unsigned char *b = skb->tail; - - nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*rtm)); - rtm = NLMSG_DATA(nlh); - rtm->rtm_family = AF_DECnet; - rtm->rtm_dst_len = 16; - rtm->rtm_src_len = 16; - rtm->rtm_tos = 0; - rtm->rtm_table = table; - rtm->rtm_type = fa->fa_type; - rtm->rtm_flags = 0; - rtm->rtm_protocol = fa->fa_proto; - RTA_PUT(skb, RTA_DST, 2, &fa->fa_key); - if (fa->fa_ifindex) - RTA_PUT(skb, RTA_OIF, sizeof(int), &fa->fa_ifindex); - - nlh->nlmsg_len = skb->tail - b; - return skb->len; - -nlmsg_failure: -rtattr_failure: - skb_trim(skb, b - skb->data); - return -1; -} - -static void dn_rtmsg_fib(int event, int table, struct dn_fib_action *fa, - struct nlmsghdr *nlh, 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 (dn_fib_dump_info(skb, pid, nlh->nlmsg_seq, event, table, fa) < 0) { - kfree_skb(skb); - return; - } - NETLINK_CB(skb).dst_groups = RTMGRP_DECnet_ROUTE; - if (nlh->nlmsg_flags & NLM_F_ECHO) - atomic_inc(&skb->users); - netlink_broadcast(rtnl, skb, pid, RTMGRP_DECnet_ROUTE, GFP_KERNEL); - if (nlh->nlmsg_flags & NLM_F_ECHO) - netlink_unicast(rtnl, skb, pid, MSG_DONTWAIT); -} - -static int dn_fib_table_dump(struct dn_fib_table *t, struct sk_buff *skb, struct netlink_callback *cb) -{ - - return skb->len; -} int dn_fib_dump(struct sk_buff *skb, struct netlink_callback *cb) { @@ -690,7 +486,7 @@ int dn_fib_dump(struct sk_buff *skb, struct netlink_callback *cb) continue; if (t > s_t) memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(int)); - tb = dn_fib_get_tree(t, 0); + tb = dn_fib_get_table(t, 0); if (tb == NULL) continue; if (tb->dump(tb, skb, cb) < 0) @@ -703,6 +499,96 @@ int dn_fib_dump(struct sk_buff *skb, struct netlink_callback *cb) } #endif /* CONFIG_RTNETLINK */ +int dn_fib_sync_down(dn_address local, struct net_device *dev, int force) +{ + int ret = 0; + int scope = RT_SCOPE_NOWHERE; + + if (force) + scope = -1; + + for_fib_info() { + /* + * This makes no sense for DECnet.... we will almost + * certainly have more than one local address the same + * over all our interfaces. It needs thinking about + * some more. + */ + if (local && fi->fib_prefsrc == local) { + fi->fib_flags |= RTNH_F_DEAD; + ret++; + } else if (dev && fi->fib_nhs) { + int dead = 0; + + change_nexthops(fi) { + if (nh->nh_flags&RTNH_F_DEAD) + dead++; + else if (nh->nh_dev == dev && + nh->nh_scope != scope) { + nh->nh_flags |= RTNH_F_DEAD; + fi->fib_power -= nh->nh_power; + nh->nh_power = 0; + dead++; + } + } endfor_nexthops(fi) + if (dead == fi->fib_nhs) { + fi->fib_flags |= RTNH_F_DEAD; + ret++; + } + } + } endfor_fib_info(); + return ret; +} + + +int dn_fib_sync_up(struct net_device *dev) +{ + int ret = 0; + + if (!(dev->flags&IFF_UP)) + return 0; + + for_fib_info() { + int alive = 0; + + change_nexthops(fi) { + if (!(nh->nh_flags&RTNH_F_DEAD)) { + alive++; + continue; + } + if (nh->nh_dev == NULL || !(nh->nh_dev->flags&IFF_UP)) + continue; + if (nh->nh_dev != dev || dev->dn_ptr == NULL) + continue; + alive++; + nh->nh_power = 0; + nh->nh_flags &= ~RTNH_F_DEAD; + } endfor_nexthops(fi); + + if (alive == fi->fib_nhs) { + fi->fib_flags &= ~RTNH_F_DEAD; + ret++; + } + } endfor_fib_info(); + return ret; +} + +void dn_fib_flush(void) +{ + int flushed = 0; + struct dn_fib_table *tb; + int id; + + for(id = DN_NUM_TABLES; id > 0; id--) { + if ((tb = dn_fib_get_table(id, 0)) == NULL) + continue; + flushed += tb->flush(tb); + } + + if (flushed) + dn_rt_cache_flush(-1); +} + int dn_fib_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { @@ -720,104 +606,65 @@ int dn_fib_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) #ifdef CONFIG_PROC_FS -struct dn_fib_procfs { - int len; - off_t pos; - off_t begin; - off_t offset; - int length; - char *buffer; -}; - -static int dn_proc_action_list(struct dn_fib_walker_t *fwt, struct dn_fib_node *fn) -{ - struct dn_fib_procfs *pinfo = (struct dn_fib_procfs *)fwt->arg; - struct dn_fib_action *fa; - char ab[DN_ASCBUF_LEN]; - - if (pinfo->pos > pinfo->offset + pinfo->length) - return 0; - - for(fa = fn->fn_action; fa; fa = fa->fa_next) { - - pinfo->len += sprintf(pinfo->buffer + pinfo->len, - "%s/%04hx %02x %02x\n", - dn_addr2asc(fa->fa_key, ab), - fa->fa_mask, - fa->fa_type, - fa->fa_proto); - - pinfo->pos = pinfo->begin + pinfo->len; - if (pinfo->pos < pinfo->offset) { - pinfo->len = 0; - pinfo->begin = pinfo->pos; - } - if (pinfo->pos > pinfo->offset + pinfo->length) - break; - } - - return 0; -} - static int decnet_rt_get_info(char *buffer, char **start, off_t offset, int length) { - struct dn_fib_procfs pinfo; - int i; - struct dn_fib_table *t; - struct dn_fib_walker_t fwt; - - pinfo.pos = 0; - pinfo.len = 0; - pinfo.begin = 0; - pinfo.offset = offset; - pinfo.length = length; - pinfo.buffer = buffer; - - fwt.arg = &pinfo; - fwt.fxn = dn_proc_action_list; - - start_bh_atomic(); - for(i = 0; i < DN_NUM_TABLES; i++) { - if ((t = dn_fib_get_tree(i, 0)) == NULL) - continue; + int first = offset / 128; + char *ptr = buffer; + int count = (length + 127) / 128; + int len; + int i; + struct dn_fib_table *tb; - fwt.table = t; - t->walk(&fwt); + *start = buffer + (offset % 128); + + if (--first < 0) { + sprintf(buffer, "%-127s\n", "Iface\tDest\tGW \tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU\tWindow\tIRTT"); + --count; + ptr += 128; + first = 0; + } - if (pinfo.pos > pinfo.offset + pinfo.length) - break; - } - end_bh_atomic(); - *start = pinfo.buffer + (pinfo.offset - pinfo.begin); - pinfo.len -= (pinfo.offset - pinfo.begin); + for(i = DN_MIN_TABLE; (i <= DN_NUM_TABLES) && (count > 0); i++) { + if ((tb = dn_fib_get_table(i, 0)) != NULL) { + int n = tb->get_info(tb, ptr, first, count); + count -= n; + ptr += n * 128; + } + } - if (pinfo.len > pinfo.length) - pinfo.len = pinfo.length; + len = ptr - *start; + if (len >= length) + return length; + if (len >= 0) + return len; - return pinfo.len; + return 0; } #endif /* CONFIG_PROC_FS */ -#ifdef CONFIG_DECNET_MODULE -void dn_fib_cleanup(void) + +void __exit dn_fib_cleanup(void) { #ifdef CONFIG_PROC_FS - proc_net_create("decnet_route",0,decnet_rt_get_info); -#endif /* CONFIG_PROC_FS */ + proc_net_remove("decnet_route"); +#endif + + dn_fib_table_cleanup(); + dn_fib_rules_cleanup(); } -#endif /* CONFIG_DECNET_MODULE */ void __init dn_fib_init(void) { - memset(dn_fib_tables, 0, DN_NUM_TABLES * sizeof(struct dn_fib_table *)); #ifdef CONFIG_PROC_FS - proc_net_remove("decnet_route"); + proc_net_create("decnet_route", 0, decnet_rt_get_info); #endif + dn_fib_table_init(); + dn_fib_rules_init(); } |