Import 2.3.18pre1
[davej-history.git] / net / decnet / dn_fib.c
blobe34f4b6aa657dfbb1fea08adbd894292af99591b
1 /*
2 * DECnet An implementation of the DECnet protocol suite for the LINUX
3 * operating system. DECnet is implemented using the BSD Socket
4 * interface as the means of communication with the user level.
6 * DECnet Routing Forwarding Information Base
8 * Author: Steve Whitehouse <SteveW@ACM.org>
11 * Changes:
12 * Alexey Kuznetsov : SMP locking changes
15 #include <linux/config.h>
16 #include <linux/string.h>
17 #include <linux/net.h>
18 #include <linux/socket.h>
19 #include <linux/sockios.h>
20 #include <linux/init.h>
21 #include <linux/skbuff.h>
22 #include <linux/netlink.h>
23 #include <linux/rtnetlink.h>
24 #include <linux/proc_fs.h>
25 #include <linux/netdevice.h>
26 #include <linux/timer.h>
27 #include <linux/spinlock.h>
28 #include <asm/atomic.h>
29 #include <asm/uaccess.h>
30 #include <net/neighbour.h>
31 #include <net/dst.h>
32 #include <net/dn.h>
33 #include <net/dn_fib.h>
34 #include <net/dn_neigh.h>
35 #include <net/dn_dev.h>
38 * N.B. Some of the functions here should really be inlines, but
39 * I'll sort out that when its all working properly, for now the
40 * stack frames will be useful for debugging.
42 #define DN_NUM_TABLES 255
43 #define DN_MIN_TABLE 1
44 #define DN_L1_TABLE 1
45 #define DN_L2_TABLE 2
47 #ifdef CONFIG_RTNETLINK
48 static int dn_fib_table_dump(struct dn_fib_table *t, struct sk_buff *skb, struct netlink_callback *cb);
49 static void dn_rtmsg_fib(int event, int table, struct dn_fib_action *fa, struct nlmsghdr *nlh, struct netlink_skb_parms *req);
50 extern int dn_cache_dump(struct sk_buff *skb, struct netlink_callback *cb);
51 #endif /* CONFIG_RTNETLINK */
53 static void dn_fib_del_tree(struct dn_fib_table *t);
55 static struct dn_fib_table *dn_fib_tables[DN_NUM_TABLES + 1];
56 static int dn_fib_allocs = 0;
57 static int dn_fib_actions = 0;
59 static struct dn_fib_node *dn_fib_alloc(void)
61 struct dn_fib_node *fn;
63 fn = kmalloc(sizeof(struct dn_fib_node), GFP_KERNEL);
65 if (fn) {
66 memset(fn, 0, sizeof(struct dn_fib_node));
67 dn_fib_allocs++;
70 return fn;
74 static __inline__ void dn_fib_free(struct dn_fib_node *fn)
76 kfree_s(fn, sizeof(struct dn_fib_node));
77 dn_fib_allocs--;
80 static struct dn_fib_action *dn_fib_new_action(void)
82 struct dn_fib_action *fa;
84 fa = kmalloc(sizeof(struct dn_fib_action), GFP_KERNEL);
86 if (fa) {
87 memset(fa, 0, sizeof(struct dn_fib_action));
88 dn_fib_actions++;
91 return fa;
94 static __inline__ void dn_fib_del_action(struct dn_fib_action *fa)
96 if ((fa->fa_type == RTN_UNICAST) && fa->fa_neigh)
97 neigh_release(fa->fa_neigh);
99 kfree_s(fa, sizeof(struct dn_fib_action));
100 dn_fib_actions--;
103 static struct dn_fib_node *dn_fib_follow(struct dn_fib_node *fn, dn_address key)
105 while(fn->fn_action == NULL)
106 fn = DN_FIB_NEXT(fn, key);
108 return fn;
112 static struct dn_fib_node *dn_fib_follow1(struct dn_fib_node *fn, dn_address key)
114 while((fn->fn_action == NULL) && (((key ^ fn->fn_key) >> fn->fn_shift) == 0))
115 fn = DN_FIB_NEXT(fn, key);
117 return fn;
121 static int dn_fib_table_insert1(struct dn_fib_table *t, struct dn_fib_node *leaf)
123 struct dn_fib_node *fn, *fn1, *fn2;
124 int shift = -1;
125 dn_address match;
126 dn_address cmpmask = 1;
128 if (!t->root) {
129 t->root = leaf;
130 t->count++;
131 return 0;
134 fn1 = dn_fib_follow1(t->root, leaf->fn_key);
135 fn2 = fn1->fn_up;
137 if (fn1->fn_key == leaf->fn_key)
138 return -EEXIST;
140 if ((fn = dn_fib_alloc()) == NULL)
141 return -ENOBUFS;
143 fn->fn_key = leaf->fn_key;
144 match = fn1->fn_key ^ fn->fn_key;
146 while(match) {
147 match >>= 1;
148 shift++;
150 cmpmask <<= shift;
152 fn->fn_cmpmask = cmpmask;
153 fn->fn_shift = shift;
155 if (fn2) {
156 DN_FIB_NEXT(fn2, fn->fn_key) = fn;
157 } else {
158 t->root = fn;
161 t->count++;
162 fn->fn_up = fn2;
163 DN_FIB_NEXT(fn, fn1->fn_key) = fn1;
164 DN_FIB_NEXT(fn, leaf->fn_key) = leaf;
166 return 0;
169 static __inline__ int dn_maskcmp(dn_address m1, dn_address m2)
171 int cmp = 0;
173 while(m1 || m2) {
174 if (m1 & 0x8000)
175 cmp++;
176 if (m2 & 0x8000)
177 cmp--;
178 m1 <<= 1;
179 m2 <<= 1;
182 return cmp;
186 static int dn_fib_table_insert(struct dn_fib_table *t, struct dn_fib_action *fa)
188 struct dn_fib_node *fn;
189 struct dn_fib_action **fap;
190 int err;
191 int cmp;
193 if (t->root && ((fn = dn_fib_follow(t->root, fa->fa_key)) != NULL) &&
194 (fn->fn_key == fa->fa_key))
195 goto add_action;
197 if ((fn = dn_fib_alloc()) == NULL)
198 return -ENOBUFS;
200 fn->fn_key = fa->fa_key;
201 fn->fn_action = fa;
203 if ((err = dn_fib_table_insert1(t, fn)) < 0)
204 dn_fib_free(fn);
206 #ifdef CONFIG_RTNETLINK
207 if (!err)
208 dn_rtmsg_fib(RTM_NEWROUTE, t->n, fa, NULL, NULL);
209 #endif /* CONFIG_RTNETLINK */
211 return err;
213 add_action:
214 fap = &fn->fn_action;
216 for(; *fap; fap = &((*fap)->fa_next)) {
217 if ((cmp = dn_maskcmp((*fap)->fa_mask, fa->fa_mask)) > 0)
218 break;
219 if (cmp < 0)
220 continue;
221 if ((*fap)->fa_cost > fa->fa_cost)
222 break;
225 fa->fa_next = *fap;
226 *fap = fa;
228 #ifdef CONFIG_RTNETLINK
229 dn_rtmsg_fib(RTM_NEWROUTE, t->n, fa, NULL, NULL);
230 #endif /* CONFIG_RTNETLINK */
232 return 0;
235 static int dn_fib_table_delete1(struct dn_fib_table *t, struct dn_fib_node *fn)
237 struct dn_fib_node *fn1 = fn->fn_up;
238 struct dn_fib_node *fn2;
239 struct dn_fib_node *fn3;
241 if (fn == t->root) {
242 t->root = NULL;
243 t->count--;
244 return 0;
247 if (fn1 == NULL)
248 return -EINVAL;
250 fn2 = fn1->fn_up;
251 fn3 = DN_FIB_NEXT(fn1, ~fn->fn_key);
253 if (fn2)
254 DN_FIB_NEXT(fn2, fn1->fn_key) = fn3;
255 else
256 t->root = fn3;
258 fn3->fn_up = fn2;
260 dn_fib_free(fn1);
261 t->count--;
262 return 0;
265 static int dn_fib_table_delete(struct dn_fib_table *t, struct dn_fib_action *fa)
267 struct dn_fib_res res;
268 struct dn_fib_node *fn;
269 struct dn_fib_action **fap, *old;
270 int err;
272 res.res_type = 0;
273 res.res_addr = fa->fa_key;
274 res.res_mask = fa->fa_mask;
275 res.res_ifindex = fa->fa_ifindex;
276 res.res_proto = fa->fa_proto;
277 res.res_cost = fa->fa_cost;
279 if ((err = t->lookup(t, &res)) < 0)
280 return err;
282 fn = res.res_fn;
283 fap = &fn->fn_action;
284 while((*fap) != res.res_fa)
285 fap = &((*fap)->fa_next);
286 old = *fap;
287 *fap = (*fap)->fa_next;
289 if (fn->fn_action == NULL)
290 dn_fib_table_delete1(t, fn);
292 if (t->root == NULL)
293 dn_fib_del_tree(t);
295 #ifdef CONFIG_RTNETLINK
296 dn_rtmsg_fib(RTM_DELROUTE, t->n, old, NULL, NULL);
297 #endif /* CONFIG_RTNETLINK */
299 dn_fib_del_action(old);
301 return 0;
304 static int dn_fib_search(struct dn_fib_node *fn, struct dn_fib_res *res)
306 struct dn_fib_action *fa = fn->fn_action;
308 for(; fa; fa = fa->fa_next) {
309 if ((fa->fa_key ^ res->res_addr) & fa->fa_mask)
310 continue;
311 if (res->res_ifindex && (res->res_ifindex != fa->fa_ifindex))
312 continue;
313 if (res->res_mask && (res->res_mask != fa->fa_mask))
314 continue;
315 if (res->res_proto && (res->res_proto != fa->fa_proto))
316 continue;
317 if (res->res_cost && (res->res_cost != fa->fa_cost))
318 continue;
320 res->res_fn = fn;
321 res->res_fa = fa;
322 return 1;
325 return 0;
328 static int dn_fib_recurse(struct dn_fib_node *fn, struct dn_fib_res *res)
330 struct dn_fib_node *fn1;
331 int err = -ENOENT;
333 fn1 = dn_fib_follow(fn, res->res_addr);
335 if (dn_fib_search(fn1, res))
336 return 0;
338 while((fn1 = fn1->fn_up) != fn)
339 if ((err = dn_fib_recurse(DN_FIB_NEXT(fn1, ~res->res_addr), res)) == 0)
340 break;
342 return err;
345 static int dn_fib_table_lookup(struct dn_fib_table *t, struct dn_fib_res *res)
347 struct dn_fib_node *fn = t->root;
348 int err = -ENOENT;
350 if (t->root == NULL)
351 return err;
353 fn = dn_fib_follow(t->root, res->res_addr);
355 if (dn_fib_search(fn, res))
356 return 0;
358 while((fn = fn->fn_up) != NULL)
359 if ((err = dn_fib_recurse(DN_FIB_NEXT(fn, ~res->res_addr), res)) == 0)
360 break;
362 return err;
365 static int dn_fib_table_walk_recurse(struct dn_fib_walker_t *fwt, struct dn_fib_node *fn)
367 struct dn_fib_table *t = fwt->table;
369 if (fn->fn_action) {
370 fwt->fxn(fwt, fn);
371 } else {
372 dn_fib_table_walk_recurse(fwt, t->root->fn_children[0]);
373 dn_fib_table_walk_recurse(fwt, t->root->fn_children[1]);
376 return 0;
379 static int dn_fib_table_walk(struct dn_fib_walker_t *fwt)
381 struct dn_fib_table *t = fwt->table;
383 if (t->root != NULL) {
384 if (t->root->fn_action) {
385 fwt->fxn(fwt, t->root);
386 } else {
387 dn_fib_table_walk_recurse(fwt, t->root->fn_children[0]);
388 dn_fib_table_walk_recurse(fwt, t->root->fn_children[1]);
392 return 0;
395 static struct dn_fib_table *dn_fib_get_tree(int n, int create)
397 struct dn_fib_table *t;
399 if (n < DN_MIN_TABLE)
400 return NULL;
402 if (n > DN_NUM_TABLES)
403 return NULL;
405 if (dn_fib_tables[n])
406 return dn_fib_tables[n];
408 if (!create)
409 return NULL;
411 if ((t = kmalloc(sizeof(struct dn_fib_table), GFP_KERNEL)) == NULL)
412 return NULL;
414 dn_fib_tables[n] = t;
415 memset(t, 0, sizeof(struct dn_fib_table));
417 t->n = n;
418 t->insert = dn_fib_table_insert;
419 t->delete = dn_fib_table_delete;
420 t->lookup = dn_fib_table_lookup;
421 t->walk = dn_fib_table_walk;
422 #ifdef CONFIG_RTNETLINK
423 t->dump = dn_fib_table_dump;
424 #endif
426 return t;
429 static void dn_fib_del_tree(struct dn_fib_table *t)
431 dn_fib_tables[t->n] = NULL;
433 if (t) {
434 kfree_s(t, sizeof(struct dn_fib_table));
439 int dn_fib_resolve(struct dn_fib_res *res)
441 int table = DN_L1_TABLE;
442 int count = 0;
443 struct dn_fib_action *fa;
444 int err;
446 if ((res->res_addr ^ dn_ntohs(decnet_address)) & 0xfc00)
447 table = DN_L2_TABLE;
449 for(;;) {
450 struct dn_fib_table *t = dn_fib_get_tree(table, 0);
452 if (t == NULL)
453 return -ENOENT;
455 if ((err = t->lookup(t, res)) < 0)
456 return err;
458 if ((fa = res->res_fa) == NULL)
459 return -ENOENT;
461 if (fa->fa_type != RTN_THROW)
462 break;
464 table = fa->fa_table;
466 if (count++ > DN_NUM_TABLES)
467 return -ENOENT;
470 switch(fa->fa_type) {
471 case RTN_PROHIBIT:
472 case RTN_UNREACHABLE:
473 return -fa->fa_error;
476 return 0;
480 * Punt to user via netlink for example, but for now
481 * we just drop it.
483 int dn_fib_rt_message(struct sk_buff *skb)
485 kfree_skb(skb);
487 return 0;
491 #ifdef CONFIG_RTNETLINK
492 static int dn_fib_convert_rtm(struct dn_fib_action *fa,
493 struct rtmsg *r, struct rtattr **rta,
494 struct nlmsghdr *n,
495 struct netlink_skb_parms *req)
497 dn_address dst, gw, mask = 0xffff;
498 int ifindex = 0;
499 struct neighbour *neigh;
500 struct net_device *dev;
501 unsigned char addr[ETH_ALEN];
503 if (r->rtm_family != AF_DECnet)
504 return -EINVAL;
506 if (rta[RTA_DST-1])
507 memcpy(&dst, RTA_DATA(rta[RTA_DST-1]), 2);
509 if (rta[RTA_OIF-1])
510 memcpy(&ifindex, RTA_DATA(rta[RTA_OIF-1]), sizeof(int));
512 if (rta[RTA_GATEWAY-1])
513 memcpy(&gw, RTA_DATA(rta[RTA_GATEWAY-1]), 2);
515 fa->fa_key = dn_ntohs(dst);
516 fa->fa_mask = dn_ntohs(mask);
517 fa->fa_ifindex = ifindex;
518 fa->fa_proto = r->rtm_protocol;
519 fa->fa_type = r->rtm_type;
521 switch(fa->fa_type) {
522 case RTN_UNICAST:
523 if ((dev = __dev_get_by_index(ifindex)) == NULL)
524 return -ENODEV;
525 dn_dn2eth(addr, dn_ntohs(gw));
526 if ((neigh = __neigh_lookup(&dn_neigh_table, &addr, dev, 1)) == NULL)
527 return -EHOSTUNREACH;
528 fa->fa_neigh = neigh;
529 break;
530 case RTN_THROW:
531 fa->fa_table = 0;
532 break;
533 case RTN_PROHIBIT:
534 fa->fa_error = EPERM;
535 break;
536 case RTN_UNREACHABLE:
537 fa->fa_error = EHOSTUNREACH;
538 break;
539 case RTN_BLACKHOLE:
540 fa->fa_error = EINVAL;
541 break;
544 return 0;
547 static int dn_fib_check_attr(struct rtmsg *r, struct rtattr **rta)
549 switch(r->rtm_type) {
550 case RTN_UNICAST:
551 case RTN_BLACKHOLE:
552 case RTN_PROHIBIT:
553 case RTN_UNREACHABLE:
554 case RTN_THROW:
555 break;
556 default:
557 return -1;
560 return 0;
563 int dn_fib_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
565 struct dn_fib_table *t;
566 struct rtattr **rta = arg;
567 struct rtmsg *r = NLMSG_DATA(nlh);
568 struct dn_fib_action *fa;
569 int err;
571 if (dn_fib_check_attr(r, rta))
572 return -EINVAL;
574 if ((fa = dn_fib_new_action()) == NULL)
575 return -ENOBUFS;
577 t = dn_fib_get_tree(r->rtm_table, 0);
578 if (t) {
579 if ((err = dn_fib_convert_rtm(fa, r, rta, nlh, &NETLINK_CB(skb))) < 0) {
580 dn_fib_del_action(fa);
581 return err;
583 err = t->delete(t, fa);
584 dn_fib_del_action(fa);
585 return err;
587 return -ESRCH;
590 int dn_fib_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
592 struct dn_fib_table *t;
593 struct rtattr **rta = arg;
594 struct rtmsg *r = NLMSG_DATA(nlh);
595 struct dn_fib_action *fa;
596 int err;
598 if (dn_fib_check_attr(r, rta))
599 return -EINVAL;
601 if ((fa = dn_fib_new_action()) == NULL)
602 return -ENOBUFS;
604 t = dn_fib_get_tree(r->rtm_table, 1);
605 if (t) {
606 if ((err = dn_fib_convert_rtm(fa, r, rta, nlh, &NETLINK_CB(skb))) < 0) {
607 dn_fib_del_action(fa);
608 return err;
610 return t->insert(t, fa);
612 return -ENOBUFS;
615 int dn_fib_dump_info(struct sk_buff *skb, u32 pid, u32 seq, int event,
616 int table, struct dn_fib_action *fa)
618 struct rtmsg *rtm;
619 struct nlmsghdr *nlh;
620 unsigned char *b = skb->tail;
622 nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*rtm));
623 rtm = NLMSG_DATA(nlh);
624 rtm->rtm_family = AF_DECnet;
625 rtm->rtm_dst_len = 16;
626 rtm->rtm_src_len = 16;
627 rtm->rtm_tos = 0;
628 rtm->rtm_table = table;
629 rtm->rtm_type = fa->fa_type;
630 rtm->rtm_flags = 0;
631 rtm->rtm_protocol = fa->fa_proto;
632 RTA_PUT(skb, RTA_DST, 2, &fa->fa_key);
633 if (fa->fa_ifindex)
634 RTA_PUT(skb, RTA_OIF, sizeof(int), &fa->fa_ifindex);
636 nlh->nlmsg_len = skb->tail - b;
637 return skb->len;
639 nlmsg_failure:
640 rtattr_failure:
641 skb_trim(skb, b - skb->data);
642 return -1;
645 static void dn_rtmsg_fib(int event, int table, struct dn_fib_action *fa,
646 struct nlmsghdr *nlh, struct netlink_skb_parms *req)
648 struct sk_buff *skb;
649 u32 pid = req ? req->pid : 0;
650 int size = NLMSG_SPACE(sizeof(struct rtmsg) + 256);
652 skb = alloc_skb(size, GFP_KERNEL);
653 if (!skb)
654 return;
656 if (dn_fib_dump_info(skb, pid, nlh->nlmsg_seq, event, table, fa) < 0) {
657 kfree_skb(skb);
658 return;
660 NETLINK_CB(skb).dst_groups = RTMGRP_DECnet_ROUTE;
661 if (nlh->nlmsg_flags & NLM_F_ECHO)
662 atomic_inc(&skb->users);
663 netlink_broadcast(rtnl, skb, pid, RTMGRP_DECnet_ROUTE, GFP_KERNEL);
664 if (nlh->nlmsg_flags & NLM_F_ECHO)
665 netlink_unicast(rtnl, skb, pid, MSG_DONTWAIT);
668 static int dn_fib_table_dump(struct dn_fib_table *t, struct sk_buff *skb, struct netlink_callback *cb)
671 return skb->len;
674 int dn_fib_dump(struct sk_buff *skb, struct netlink_callback *cb)
676 int t;
677 int s_t;
678 struct dn_fib_table *tb;
680 if (NLMSG_PAYLOAD(cb->nlh, 0) >= sizeof(struct rtmsg) &&
681 ((struct rtmsg *)NLMSG_DATA(cb->nlh))->rtm_flags&RTM_F_CLONED)
682 return dn_cache_dump(skb, cb);
684 s_t = cb->args[0];
685 if (s_t == 0)
686 s_t = cb->args[0] = DN_MIN_TABLE;
688 for(t = s_t; t < DN_NUM_TABLES; t++) {
689 if (t < s_t)
690 continue;
691 if (t > s_t)
692 memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(int));
693 tb = dn_fib_get_tree(t, 0);
694 if (tb == NULL)
695 continue;
696 if (tb->dump(tb, skb, cb) < 0)
697 break;
700 cb->args[0] = t;
702 return skb->len;
704 #endif /* CONFIG_RTNETLINK */
706 int dn_fib_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
709 if (!capable(CAP_NET_ADMIN))
710 return -EPERM;
712 switch(cmd) {
713 case SIOCADDRT:
714 case SIOCDELRT:
715 return 0;
718 return -EINVAL;
721 #ifdef CONFIG_PROC_FS
723 struct dn_fib_procfs {
724 int len;
725 off_t pos;
726 off_t begin;
727 off_t offset;
728 int length;
729 char *buffer;
732 static int dn_proc_action_list(struct dn_fib_walker_t *fwt, struct dn_fib_node *fn)
734 struct dn_fib_procfs *pinfo = (struct dn_fib_procfs *)fwt->arg;
735 struct dn_fib_action *fa;
736 char ab[DN_ASCBUF_LEN];
738 if (pinfo->pos > pinfo->offset + pinfo->length)
739 return 0;
741 for(fa = fn->fn_action; fa; fa = fa->fa_next) {
743 pinfo->len += sprintf(pinfo->buffer + pinfo->len,
744 "%s/%04hx %02x %02x\n",
745 dn_addr2asc(fa->fa_key, ab),
746 fa->fa_mask,
747 fa->fa_type,
748 fa->fa_proto);
750 pinfo->pos = pinfo->begin + pinfo->len;
751 if (pinfo->pos < pinfo->offset) {
752 pinfo->len = 0;
753 pinfo->begin = pinfo->pos;
755 if (pinfo->pos > pinfo->offset + pinfo->length)
756 break;
759 return 0;
762 static int decnet_rt_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
764 struct dn_fib_procfs pinfo;
765 int i;
766 struct dn_fib_table *t;
767 struct dn_fib_walker_t fwt;
769 pinfo.pos = 0;
770 pinfo.len = 0;
771 pinfo.begin = 0;
772 pinfo.offset = offset;
773 pinfo.length = length;
774 pinfo.buffer = buffer;
776 fwt.arg = &pinfo;
777 fwt.fxn = dn_proc_action_list;
779 start_bh_atomic();
780 for(i = 0; i < DN_NUM_TABLES; i++) {
781 if ((t = dn_fib_get_tree(i, 0)) == NULL)
782 continue;
784 fwt.table = t;
785 t->walk(&fwt);
787 if (pinfo.pos > pinfo.offset + pinfo.length)
788 break;
790 end_bh_atomic();
792 *start = pinfo.buffer + (pinfo.offset - pinfo.begin);
793 pinfo.len -= (pinfo.offset - pinfo.begin);
795 if (pinfo.len > pinfo.length)
796 pinfo.len = pinfo.length;
798 return pinfo.len;
801 static struct proc_dir_entry proc_net_decnet_route = {
802 PROC_NET_DN_ROUTE, 12, "decnet_route",
803 S_IFREG | S_IRUGO, 1, 0, 0,
804 0, &proc_net_inode_operations,
805 decnet_rt_get_info
808 #endif /* CONFIG_PROC_FS */
810 #ifdef CONFIG_DECNET_MODULE
811 void dn_fib_cleanup(void)
813 #ifdef CONFIG_PROC_FS
814 proc_net_unregister(PROC_NET_DN_ROUTE);
815 #endif /* CONFIG_PROC_FS */
817 #endif /* CONFIG_DECNET_MODULE */
820 void __init dn_fib_init(void)
822 memset(dn_fib_tables, 0, DN_NUM_TABLES * sizeof(struct dn_fib_table *));
824 #ifdef CONFIG_PROC_FS
825 proc_net_register(&proc_net_decnet_route);
826 #endif