patch-2.3.4 linux/net/decnet/dn_fib.c
Next file: linux/net/decnet/dn_neigh.c
Previous file: linux/net/decnet/dn_dev.c
Back to the patch index
Back to the overall index
- Lines: 806
- Date:
Wed May 26 09:36:36 1999
- Orig file:
v2.3.3/linux/net/decnet/dn_fib.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.3.3/linux/net/decnet/dn_fib.c linux/net/decnet/dn_fib.c
@@ -0,0 +1,805 @@
+/*
+ * DECnet An implementation of the DECnet protocol suite for the LINUX
+ * operating system. DECnet is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * DECnet Routing Forwarding Information Base
+ *
+ * Author: Steve Whitehouse <SteveW@ACM.org>
+ *
+ *
+ * Changes:
+ *
+ */
+#include <linux/config.h>
+#include <linux/string.h>
+#include <linux/net.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/proc_fs.h>
+#include <linux/netdevice.h>
+#include <linux/timer.h>
+#include <linux/rtnetlink.h>
+#include <asm/spinlock.h>
+#include <asm/atomic.h>
+#include <asm/uaccess.h>
+#include <net/neighbour.h>
+#include <net/dst.h>
+#include <net/dn.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);
+#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;
+
+static struct dn_fib_node *dn_fib_alloc(void)
+{
+ struct dn_fib_node *fn;
+
+ fn = kmalloc(sizeof(struct dn_fib_node), GFP_KERNEL);
+
+ if (fn) {
+ memset(fn, 0, sizeof(struct dn_fib_node));
+ dn_fib_allocs++;
+ }
+
+ return fn;
+}
+
+
+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_action *dn_fib_new_action(void)
+{
+ 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++;
+ }
+
+ 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;
+}
+
+
+static int dn_fib_table_insert1(struct dn_fib_table *t, struct dn_fib_node *leaf)
+{
+ 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;
+
+ return 0;
+}
+
+static __inline__ int dn_maskcmp(dn_address m1, dn_address m2)
+{
+ int cmp = 0;
+
+ while(m1 || m2) {
+ if (m1 & 0x8000)
+ cmp++;
+ if (m2 & 0x8000)
+ cmp--;
+ m1 <<= 1;
+ m2 <<= 1;
+ }
+
+ return cmp;
+}
+
+
+static int dn_fib_table_insert(struct dn_fib_table *t, struct dn_fib_action *fa)
+{
+ 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);
+
+#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;
+ }
+
+ fa->fa_next = *fap;
+ *fap = fa;
+
+#ifdef CONFIG_RTNETLINK
+ dn_rtmsg_fib(RTM_NEWROUTE, t->n, fa, NULL, NULL);
+#endif /* CONFIG_RTNETLINK */
+
+ return 0;
+}
+
+static int dn_fib_table_delete1(struct dn_fib_table *t, struct dn_fib_node *fn)
+{
+ 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;
+
+ fn2 = fn1->fn_up;
+ fn3 = DN_FIB_NEXT(fn1, ~fn->fn_key);
+
+ if (fn2)
+ DN_FIB_NEXT(fn2, fn1->fn_key) = fn3;
+ else
+ t->root = fn3;
+
+ fn3->fn_up = fn2;
+
+ dn_fib_free(fn1);
+ t->count--;
+ return 0;
+}
+
+static int dn_fib_table_delete(struct dn_fib_table *t, struct dn_fib_action *fa)
+{
+ 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 (fn->fn_action == NULL)
+ dn_fib_table_delete1(t, fn);
+
+ 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);
+
+ return 0;
+}
+
+static int dn_fib_search(struct dn_fib_node *fn, struct dn_fib_res *res)
+{
+ struct dn_fib_action *fa = fn->fn_action;
+
+ 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;
+
+ res->res_fn = fn;
+ res->res_fa = fa;
+ return 1;
+ }
+
+ 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_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;
+
+ return err;
+}
+
+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 (fn->fn_action) {
+ fwt->fxn(fwt, fn);
+ } else {
+ dn_fib_table_walk_recurse(fwt, t->root->fn_children[0]);
+ dn_fib_table_walk_recurse(fwt, t->root->fn_children[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]);
+ }
+ }
+
+ 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 (dn_fib_tables[n])
+ return dn_fib_tables[n];
+
+ 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));
+
+ 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;
+#endif
+
+ return t;
+}
+
+static void dn_fib_del_tree(struct dn_fib_table *t)
+{
+ dn_fib_tables[t->n] = NULL;
+
+ if (t) {
+ kfree_s(t, sizeof(struct dn_fib_table));
+ }
+}
+
+
+int dn_fib_resolve(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 -ENOBUFS;
+
+ if ((err = t->lookup(t, res)) < 0)
+ return err;
+
+ if ((fa = res->res_fa) == NULL)
+ return -ENOENT;
+
+ if (fa->fa_type != RTN_THROW)
+ break;
+
+ table = fa->fa_table;
+
+ if (count++ > DN_NUM_TABLES)
+ return -ENOENT;
+ }
+
+ return (fa->fa_type == RTN_PROHIBIT) ? -fa->fa_error : 0;
+}
+
+/*
+ * Punt to user via netlink for example, but for now
+ * we just drop it.
+ */
+int dn_fib_rt_message(struct sk_buff *skb)
+{
+ kfree_skb(skb);
+
+ return 0;
+}
+
+
+#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;
+ struct neighbour *neigh;
+ struct 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 = 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, 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 = 0;
+ break;
+ case RTN_UNREACHABLE:
+ fa->fa_error = EHOSTUNREACH;
+ 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;
+ }
+
+ return 0;
+}
+
+int dn_fib_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+ struct dn_fib_table *t;
+ 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;
+
+ 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 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;
+
+ 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)
+{
+ int s_t;
+ struct dn_fib_table *t;
+
+ for(s_t = cb->args[0]; s_t < DN_NUM_TABLES; s_t++) {
+ if (s_t > cb->args[0])
+ memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(int));
+ t = dn_fib_get_tree(s_t, 0);
+ if (t == NULL)
+ continue;
+ if (t->dump(t, skb, cb) < 0)
+ break;
+ }
+
+ cb->args[0] = s_t;
+
+ return skb->len;
+}
+#endif /* CONFIG_RTNETLINK */
+
+int dn_fib_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ switch(cmd) {
+ case SIOCADDRT:
+ case SIOCDELRT:
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+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, int dummy)
+{
+ 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;
+
+ fwt.table = t;
+ t->walk(&fwt);
+
+ if (pinfo.pos > pinfo.offset + pinfo.length)
+ break;
+ }
+ end_bh_atomic();
+
+ *start = pinfo.buffer + (pinfo.offset - pinfo.begin);
+ pinfo.len -= (pinfo.offset - pinfo.begin);
+
+ if (pinfo.len > pinfo.length)
+ pinfo.len = pinfo.length;
+
+ return pinfo.len;
+}
+
+static struct proc_dir_entry proc_net_decnet_route = {
+ PROC_NET_DN_ROUTE, 12, "decnet_route",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ decnet_rt_get_info
+};
+
+#ifdef CONFIG_DECNET_MODULE
+void dn_fib_cleanup(void)
+{
+#ifdef CONFIG_PROC_FS
+ proc_net_unregister(PROC_NET_DN_ROUTE);
+#endif /* CONFIG_PROC_FS */
+}
+#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_register(&proc_net_decnet_route);
+#endif
+
+}
+
+
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)