summaryrefslogtreecommitdiffstats
path: root/net/decnet/dn_fib.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-02-16 01:07:24 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-02-16 01:07:24 +0000
commit95db6b748fc86297827fbd9c9ef174d491c9ad89 (patch)
tree27a92a942821cde1edda9a1b088718d436b3efe4 /net/decnet/dn_fib.c
parent45b27b0a0652331d104c953a5b192d843fff88f8 (diff)
Merge with Linux 2.3.40.
Diffstat (limited to 'net/decnet/dn_fib.c')
-rw-r--r--net/decnet/dn_fib.c1027
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();
}