ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / net / decnet / dn_fib.c
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.
5  *
6  *              DECnet Routing Forwarding Information Base (Glue/Info List)
7  *
8  * Author:      Steve Whitehouse <SteveW@ACM.org>
9  *
10  *
11  * Changes:
12  *              Alexey Kuznetsov : SMP locking changes
13  *              Steve Whitehouse : Rewrote it... Well to be more correct, I
14  *                                 copied most of it from the ipv4 fib code.
15  *              Steve Whitehouse : Updated it in style and fixed a few bugs
16  *                                 which were fixed in the ipv4 code since
17  *                                 this code was copied from it.
18  *
19  */
20 #include <linux/config.h>
21 #include <linux/string.h>
22 #include <linux/net.h>
23 #include <linux/socket.h>
24 #include <linux/sockios.h>
25 #include <linux/init.h>
26 #include <linux/skbuff.h>
27 #include <linux/netlink.h>
28 #include <linux/rtnetlink.h>
29 #include <linux/proc_fs.h>
30 #include <linux/netdevice.h>
31 #include <linux/timer.h>
32 #include <linux/spinlock.h>
33 #include <asm/atomic.h>
34 #include <asm/uaccess.h>
35 #include <net/neighbour.h>
36 #include <net/dst.h>
37 #include <net/flow.h>
38 #include <net/dn.h>
39 #include <net/dn_route.h>
40 #include <net/dn_fib.h>
41 #include <net/dn_neigh.h>
42 #include <net/dn_dev.h>
43
44 #define RT_MIN_TABLE 1
45
46 #define for_fib_info() { struct dn_fib_info *fi;\
47         for(fi = dn_fib_info_list; fi; fi = fi->fib_next)
48 #define endfor_fib_info() }
49
50 #define for_nexthops(fi) { int nhsel; const struct dn_fib_nh *nh;\
51         for(nhsel = 0, nh = (fi)->fib_nh; nhsel < (fi)->fib_nhs; nh++, nhsel++)
52
53 #define change_nexthops(fi) { int nhsel; struct dn_fib_nh *nh;\
54         for(nhsel = 0, nh = (struct dn_fib_nh *)((fi)->fib_nh); nhsel < (fi)->fib_nhs; nh++, nhsel++)
55
56 #define endfor_nexthops(fi) }
57
58 extern int dn_cache_dump(struct sk_buff *skb, struct netlink_callback *cb);
59
60 static spinlock_t dn_fib_multipath_lock = SPIN_LOCK_UNLOCKED;
61 static struct dn_fib_info *dn_fib_info_list;
62 static rwlock_t dn_fib_info_lock = RW_LOCK_UNLOCKED;
63 int dn_fib_info_cnt;
64
65 static struct
66 {
67         int error;
68         u8 scope;
69 } dn_fib_props[RTA_MAX+1] = {
70         [RTN_UNSPEC] =      { .error = 0,       .scope = RT_SCOPE_NOWHERE },
71         [RTN_UNICAST] =     { .error = 0,       .scope = RT_SCOPE_UNIVERSE },
72         [RTN_LOCAL] =       { .error = 0,       .scope = RT_SCOPE_HOST },
73         [RTN_BROADCAST] =   { .error = -EINVAL, .scope = RT_SCOPE_NOWHERE },
74         [RTN_ANYCAST] =     { .error = -EINVAL, .scope = RT_SCOPE_NOWHERE },
75         [RTN_MULTICAST] =   { .error = -EINVAL, .scope = RT_SCOPE_NOWHERE },
76         [RTN_BLACKHOLE] =   { .error = -EINVAL, .scope = RT_SCOPE_UNIVERSE },
77         [RTN_UNREACHABLE] = { .error = -EHOSTUNREACH, .scope = RT_SCOPE_UNIVERSE },
78         [RTN_PROHIBIT] =    { .error = -EACCES, .scope = RT_SCOPE_UNIVERSE },
79         [RTN_THROW] =       { .error = -EAGAIN, .scope = RT_SCOPE_UNIVERSE },
80         [RTN_NAT] =         { .error = 0,       .scope = RT_SCOPE_NOWHERE },
81         [RTN_XRESOLVE] =    { .error = -EINVAL, .scope = RT_SCOPE_NOWHERE },
82 };
83
84 void dn_fib_free_info(struct dn_fib_info *fi)
85 {
86         if (fi->fib_dead == 0) {
87                 printk(KERN_DEBUG "DECnet: BUG! Attempt to free alive dn_fib_info\n");
88                 return;
89         }
90
91         change_nexthops(fi) {
92                 if (nh->nh_dev)
93                         dev_put(nh->nh_dev);
94                 nh->nh_dev = NULL;
95         } endfor_nexthops(fi);
96         dn_fib_info_cnt--;
97         kfree(fi);
98 }
99
100 void dn_fib_release_info(struct dn_fib_info *fi)
101 {
102         write_lock(&dn_fib_info_lock);
103         if (fi && --fi->fib_treeref == 0) {
104                 if (fi->fib_next)
105                         fi->fib_next->fib_prev = fi->fib_prev;
106                 if (fi->fib_prev)
107                         fi->fib_prev->fib_next = fi->fib_next;
108                 if (fi == dn_fib_info_list)
109                         dn_fib_info_list = fi->fib_next;
110                 fi->fib_dead = 1;
111                 dn_fib_info_put(fi);
112         }
113         write_unlock(&dn_fib_info_lock);
114 }
115
116 static inline int dn_fib_nh_comp(const struct dn_fib_info *fi, const struct dn_fib_info *ofi)
117 {
118         const struct dn_fib_nh *onh = ofi->fib_nh;
119
120         for_nexthops(fi) {
121                 if (nh->nh_oif != onh->nh_oif ||
122                         nh->nh_gw != onh->nh_gw ||
123                         nh->nh_scope != onh->nh_scope ||
124                         nh->nh_weight != onh->nh_weight ||
125                         ((nh->nh_flags^onh->nh_flags)&~RTNH_F_DEAD))
126                                 return -1;
127                 onh++;
128         } endfor_nexthops(fi);
129         return 0;
130 }
131
132 static inline struct dn_fib_info *dn_fib_find_info(const struct dn_fib_info *nfi)
133 {
134         for_fib_info() {
135                 if (fi->fib_nhs != nfi->fib_nhs)
136                         continue;
137                 if (nfi->fib_protocol == fi->fib_protocol &&
138                         nfi->fib_prefsrc == fi->fib_prefsrc &&
139                         nfi->fib_priority == fi->fib_priority &&
140                         memcmp(nfi->fib_metrics, fi->fib_metrics, sizeof(fi->fib_metrics)) == 0 &&
141                         ((nfi->fib_flags^fi->fib_flags)&~RTNH_F_DEAD) == 0 &&
142                         (nfi->fib_nhs == 0 || dn_fib_nh_comp(fi, nfi) == 0))
143                                 return fi;
144         } endfor_fib_info();
145         return NULL;
146 }
147
148 u16 dn_fib_get_attr16(struct rtattr *attr, int attrlen, int type)
149 {
150         while(RTA_OK(attr,attrlen)) {
151                 if (attr->rta_type == type)
152                         return *(u16*)RTA_DATA(attr);
153                 attr = RTA_NEXT(attr, attrlen);
154         }
155
156         return 0;
157 }
158
159 static int dn_fib_count_nhs(struct rtattr *rta)
160 {
161         int nhs = 0;
162         struct rtnexthop *nhp = RTA_DATA(rta);
163         int nhlen = RTA_PAYLOAD(rta);
164
165         while(nhlen >= (int)sizeof(struct rtnexthop)) {
166                 if ((nhlen -= nhp->rtnh_len) < 0)
167                         return 0;
168                 nhs++;
169                 nhp = RTNH_NEXT(nhp);
170         }
171
172         return nhs;
173 }
174
175 static int dn_fib_get_nhs(struct dn_fib_info *fi, const struct rtattr *rta, const struct rtmsg *r)
176 {
177         struct rtnexthop *nhp = RTA_DATA(rta);
178         int nhlen = RTA_PAYLOAD(rta);
179
180         change_nexthops(fi) {
181                 int attrlen = nhlen - sizeof(struct rtnexthop);
182                 if (attrlen < 0 || (nhlen -= nhp->rtnh_len) < 0)
183                         return -EINVAL;
184
185                 nh->nh_flags  = (r->rtm_flags&~0xFF) | nhp->rtnh_flags;
186                 nh->nh_oif    = nhp->rtnh_ifindex;
187                 nh->nh_weight = nhp->rtnh_hops + 1;
188
189                 if (attrlen) {
190                         nh->nh_gw = dn_fib_get_attr16(RTNH_DATA(nhp), attrlen, RTA_GATEWAY);
191                 }
192                 nhp = RTNH_NEXT(nhp);
193         } endfor_nexthops(fi);
194
195         return 0;
196 }
197
198
199 static int dn_fib_check_nh(const struct rtmsg *r, struct dn_fib_info *fi, struct dn_fib_nh *nh)
200 {
201         int err;
202
203         if (nh->nh_gw) {
204                 struct flowi fl;
205                 struct dn_fib_res res;
206
207                 memset(&fl, 0, sizeof(fl));
208
209                 if (nh->nh_flags&RTNH_F_ONLINK) {
210                         struct net_device *dev;
211
212                         if (r->rtm_scope >= RT_SCOPE_LINK)
213                                 return -EINVAL;
214                         if (dnet_addr_type(nh->nh_gw) != RTN_UNICAST)
215                                 return -EINVAL;
216                         if ((dev = __dev_get_by_index(nh->nh_oif)) == NULL)
217                                 return -ENODEV;
218                         if (!(dev->flags&IFF_UP))
219                                 return -ENETDOWN;
220                         nh->nh_dev = dev;
221                         dev_hold(dev);
222                         nh->nh_scope = RT_SCOPE_LINK;
223                         return 0;
224                 }
225
226                 memset(&fl, 0, sizeof(fl));
227                 fl.fld_dst = nh->nh_gw;
228                 fl.oif = nh->nh_oif;
229                 fl.fld_scope = r->rtm_scope + 1;
230
231                 if (fl.fld_scope < RT_SCOPE_LINK)
232                         fl.fld_scope = RT_SCOPE_LINK;
233
234                 if ((err = dn_fib_lookup(&fl, &res)) != 0)
235                         return err;
236
237                 err = -EINVAL;
238                 if (res.type != RTN_UNICAST && res.type != RTN_LOCAL)
239                         goto out;
240                 nh->nh_scope = res.scope;
241                 nh->nh_oif = DN_FIB_RES_OIF(res);
242                 nh->nh_dev = DN_FIB_RES_DEV(res);
243                 if (nh->nh_dev == NULL)
244                         goto out;
245                 dev_hold(nh->nh_dev);
246                 err = -ENETDOWN;
247                 if (!(nh->nh_dev->flags & IFF_UP))
248                         goto out;
249                 err = 0;
250 out:
251                 dn_fib_res_put(&res);
252                 return err;
253         } else {
254                 struct net_device *dev;
255
256                 if (nh->nh_flags&(RTNH_F_PERVASIVE|RTNH_F_ONLINK))
257                         return -EINVAL;
258
259                 dev = __dev_get_by_index(nh->nh_oif);
260                 if (dev == NULL || dev->dn_ptr == NULL)
261                         return -ENODEV;
262                 if (!(dev->flags&IFF_UP))
263                         return -ENETDOWN;
264                 nh->nh_dev = dev;
265                 dev_hold(nh->nh_dev);
266                 nh->nh_scope = RT_SCOPE_HOST;
267         }
268
269         return 0;
270 }
271
272
273 struct dn_fib_info *dn_fib_create_info(const struct rtmsg *r, struct dn_kern_rta *rta, const struct nlmsghdr *nlh, int *errp)
274 {
275         int err;
276         struct dn_fib_info *fi = NULL;
277         struct dn_fib_info *ofi;
278         int nhs = 1;
279
280         if (dn_fib_props[r->rtm_type].scope > r->rtm_scope)
281                 goto err_inval;
282
283         if (rta->rta_mp) {
284                 nhs = dn_fib_count_nhs(rta->rta_mp);
285                 if (nhs == 0)
286                         goto err_inval;
287         }
288
289         fi = kmalloc(sizeof(*fi)+nhs*sizeof(struct dn_fib_nh), GFP_KERNEL);
290         err = -ENOBUFS;
291         if (fi == NULL)
292                 goto failure;
293         memset(fi, 0, sizeof(*fi)+nhs*sizeof(struct dn_fib_nh));
294
295         fi->fib_protocol = r->rtm_protocol;
296         fi->fib_nhs = nhs;
297         fi->fib_flags = r->rtm_flags;
298         if (rta->rta_priority)
299                 fi->fib_priority = *rta->rta_priority;
300         if (rta->rta_mx) {
301                 int attrlen = RTA_PAYLOAD(rta->rta_mx);
302                 struct rtattr *attr = RTA_DATA(rta->rta_mx);
303
304                 while(RTA_OK(attr, attrlen)) {
305                         unsigned flavour = attr->rta_type;
306                         if (flavour) {
307                                 if (flavour > RTAX_MAX)
308                                         goto err_inval;
309                                 fi->fib_metrics[flavour-1] = *(unsigned*)RTA_DATA(attr);
310                         }
311                         attr = RTA_NEXT(attr, attrlen);
312                 }
313         }
314         if (rta->rta_prefsrc)
315                 memcpy(&fi->fib_prefsrc, rta->rta_prefsrc, 2);
316
317         if (rta->rta_mp) {
318                 if ((err = dn_fib_get_nhs(fi, rta->rta_mp, r)) != 0)
319                         goto failure;
320                 if (rta->rta_oif && fi->fib_nh->nh_oif != *rta->rta_oif)
321                         goto err_inval;
322                 if (rta->rta_gw && memcmp(&fi->fib_nh->nh_gw, rta->rta_gw, 2))
323                         goto err_inval;
324         } else {
325                 struct dn_fib_nh *nh = fi->fib_nh;
326                 if (rta->rta_oif)
327                         nh->nh_oif = *rta->rta_oif;
328                 if (rta->rta_gw)
329                         memcpy(&nh->nh_gw, rta->rta_gw, 2);
330                 nh->nh_flags = r->rtm_flags;
331                 nh->nh_weight = 1;
332         }
333
334         if (r->rtm_type == RTN_NAT) {
335                 if (rta->rta_gw == NULL || nhs != 1 || rta->rta_oif)
336                         goto err_inval;
337                 memcpy(&fi->fib_nh->nh_gw, rta->rta_gw, 2);
338                 goto link_it;
339         }
340
341         if (dn_fib_props[r->rtm_type].error) {
342                 if (rta->rta_gw || rta->rta_oif || rta->rta_mp)
343                         goto err_inval;
344                 goto link_it;
345         }
346
347         if (r->rtm_scope > RT_SCOPE_HOST)
348                 goto err_inval;
349
350         if (r->rtm_scope == RT_SCOPE_HOST) {
351                 struct dn_fib_nh *nh = fi->fib_nh;
352
353                 /* Local address is added */
354                 if (nhs != 1 || nh->nh_gw)
355                         goto err_inval;
356                 nh->nh_scope = RT_SCOPE_NOWHERE;
357                 nh->nh_dev = dev_get_by_index(fi->fib_nh->nh_oif);
358                 err = -ENODEV;
359                 if (nh->nh_dev == NULL)
360                         goto failure;
361         } else {
362                 change_nexthops(fi) {
363                         if ((err = dn_fib_check_nh(r, fi, nh)) != 0)
364                                 goto failure;
365                 } endfor_nexthops(fi)
366         }
367
368         if (fi->fib_prefsrc) {
369                 if (r->rtm_type != RTN_LOCAL || rta->rta_dst == NULL ||
370                     memcmp(&fi->fib_prefsrc, rta->rta_dst, 2))
371                         if (dnet_addr_type(fi->fib_prefsrc) != RTN_LOCAL)
372                                 goto err_inval;
373         }
374
375 link_it:
376         if ((ofi = dn_fib_find_info(fi)) != NULL) {
377                 fi->fib_dead = 1;
378                 dn_fib_free_info(fi);
379                 ofi->fib_treeref++;
380                 return ofi;
381         }
382
383         fi->fib_treeref++;
384         atomic_inc(&fi->fib_clntref);
385         write_lock(&dn_fib_info_lock);
386         fi->fib_next = dn_fib_info_list;
387         fi->fib_prev = NULL;
388         if (dn_fib_info_list)
389                 dn_fib_info_list->fib_prev = fi;
390         dn_fib_info_list = fi;
391         dn_fib_info_cnt++;
392         write_unlock(&dn_fib_info_lock);
393         return fi;
394
395 err_inval:
396         err = -EINVAL;
397
398 failure:
399         *errp = err;
400         if (fi) {
401                 fi->fib_dead = 1;
402                 dn_fib_free_info(fi);
403         }
404
405         return NULL;
406 }
407
408 int dn_fib_semantic_match(int type, struct dn_fib_info *fi, const struct flowi *fl, struct dn_fib_res *res)
409 {
410         int err = dn_fib_props[type].error;
411
412         if (err == 0) {
413                 if (fi->fib_flags & RTNH_F_DEAD)
414                         return 1;
415
416                 res->fi = fi;
417
418                 switch(type) {
419                         case RTN_NAT:
420                                 DN_FIB_RES_RESET(*res);
421                                 atomic_inc(&fi->fib_clntref);
422                                 return 0;
423                         case RTN_UNICAST:
424                         case RTN_LOCAL:
425                                 for_nexthops(fi) {
426                                         if (nh->nh_flags & RTNH_F_DEAD)
427                                                 continue;
428                                         if (!fl->oif || fl->oif == nh->nh_oif)
429                                                 break;
430                                 }
431                                 if (nhsel < fi->fib_nhs) {
432                                         res->nh_sel = nhsel;
433                                         atomic_inc(&fi->fib_clntref);
434                                         return 0;
435                                 }
436                                 endfor_nexthops(fi);
437                                 res->fi = NULL;
438                                 return 1;
439                         default:
440                                 if (net_ratelimit())
441                                          printk("DECnet: impossible routing event : dn_fib_semantic_match type=%d\n", type);
442                                 res->fi = NULL;
443                                 return -EINVAL;
444                 }
445         }
446         return err;
447 }
448
449 void dn_fib_select_multipath(const struct flowi *fl, struct dn_fib_res *res)
450 {
451         struct dn_fib_info *fi = res->fi;
452         int w;
453
454         spin_lock_bh(&dn_fib_multipath_lock);
455         if (fi->fib_power <= 0) {
456                 int power = 0;
457                 change_nexthops(fi) {
458                         if (!(nh->nh_flags&RTNH_F_DEAD)) {
459                                 power += nh->nh_weight;
460                                 nh->nh_power = nh->nh_weight;
461                         }
462                 } endfor_nexthops(fi);
463                 fi->fib_power = power;
464                 if (power < 0) {
465                         spin_unlock_bh(&dn_fib_multipath_lock);
466                         res->nh_sel = 0;
467                         return;
468                 }
469         }
470
471         w = jiffies % fi->fib_power;
472
473         change_nexthops(fi) {
474                 if (!(nh->nh_flags&RTNH_F_DEAD) && nh->nh_power) {
475                         if ((w -= nh->nh_power) <= 0) {
476                                 nh->nh_power--;
477                                 fi->fib_power--;
478                                 res->nh_sel = nhsel;
479                                 spin_unlock_bh(&dn_fib_multipath_lock);
480                                 return;
481                         }
482                 }
483         } endfor_nexthops(fi);
484         res->nh_sel = 0;
485         spin_unlock_bh(&dn_fib_multipath_lock);
486 }
487
488
489 /*
490  * Punt to user via netlink for example, but for now
491  * we just drop it.
492  */
493 int dn_fib_rt_message(struct sk_buff *skb)
494 {
495         kfree_skb(skb);
496
497         return 0;
498 }
499
500
501 static int dn_fib_check_attr(struct rtmsg *r, struct rtattr **rta)
502 {
503         int i;
504
505         for(i = 1; i <= RTA_MAX; i++) {
506                 struct rtattr *attr = rta[i-1];
507                 if (attr) {
508                         if (RTA_PAYLOAD(attr) < 4 && RTA_PAYLOAD(attr) != 2)
509                                 return -EINVAL;
510                         if (i != RTA_MULTIPATH && i != RTA_METRICS)
511                                 rta[i-1] = (struct rtattr *)RTA_DATA(attr);
512                 }
513         }
514
515         return 0;
516 }
517
518 int dn_fib_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
519 {
520         struct dn_fib_table *tb;
521         struct rtattr **rta = arg;
522         struct rtmsg *r = NLMSG_DATA(nlh);
523
524         if (dn_fib_check_attr(r, rta))
525                 return -EINVAL;
526
527         tb = dn_fib_get_table(r->rtm_table, 0);
528         if (tb)
529                 return tb->delete(tb, r, (struct dn_kern_rta *)rta, nlh, &NETLINK_CB(skb));
530
531         return -ESRCH;
532 }
533
534 int dn_fib_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
535 {
536         struct dn_fib_table *tb;
537         struct rtattr **rta = arg;
538         struct rtmsg *r = NLMSG_DATA(nlh);
539
540         if (dn_fib_check_attr(r, rta))
541                 return -EINVAL;
542
543         tb = dn_fib_get_table(r->rtm_table, 1);
544         if (tb) 
545                 return tb->insert(tb, r, (struct dn_kern_rta *)rta, nlh, &NETLINK_CB(skb));
546
547         return -ENOBUFS;
548 }
549
550
551 int dn_fib_dump(struct sk_buff *skb, struct netlink_callback *cb)
552 {
553         int t;
554         int s_t;
555         struct dn_fib_table *tb;
556
557         if (NLMSG_PAYLOAD(cb->nlh, 0) >= sizeof(struct rtmsg) &&
558                 ((struct rtmsg *)NLMSG_DATA(cb->nlh))->rtm_flags&RTM_F_CLONED)
559                         return dn_cache_dump(skb, cb);
560
561         s_t = cb->args[0];
562         if (s_t == 0)
563                 s_t = cb->args[0] = RT_MIN_TABLE;
564
565         for(t = s_t; t <= RT_TABLE_MAX; t++) {
566                 if (t < s_t)
567                         continue;
568                 if (t > s_t)
569                         memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(int));
570                 tb = dn_fib_get_table(t, 0);
571                 if (tb == NULL)
572                         continue;
573                 if (tb->dump(tb, skb, cb) < 0)
574                         break;
575         }
576
577         cb->args[0] = t;
578
579         return skb->len;
580 }
581
582 static void fib_magic(int cmd, int type, __u16 dst, int dst_len, struct dn_ifaddr *ifa)
583 {
584         struct dn_fib_table *tb;
585         struct {
586                 struct nlmsghdr nlh;
587                 struct rtmsg rtm;
588         } req;
589         struct dn_kern_rta rta;
590
591         memset(&req.rtm, 0, sizeof(req.rtm));
592         memset(&rta, 0, sizeof(rta));
593
594         if (type == RTN_UNICAST)
595                 tb = dn_fib_get_table(RT_MIN_TABLE, 1);
596         else
597                 tb = dn_fib_get_table(RT_TABLE_LOCAL, 1);
598
599         if (tb == NULL)
600                 return;
601
602         req.nlh.nlmsg_len = sizeof(req);
603         req.nlh.nlmsg_type = cmd;
604         req.nlh.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_APPEND;
605         req.nlh.nlmsg_pid = 0;
606         req.nlh.nlmsg_seq = 0;
607
608         req.rtm.rtm_dst_len = dst_len;
609         req.rtm.rtm_table = tb->n;
610         req.rtm.rtm_protocol = RTPROT_KERNEL;
611         req.rtm.rtm_scope = (type != RTN_LOCAL ? RT_SCOPE_LINK : RT_SCOPE_HOST);
612         req.rtm.rtm_type = type;
613
614         rta.rta_dst = &dst;
615         rta.rta_prefsrc = &ifa->ifa_local;
616         rta.rta_oif = &ifa->ifa_dev->dev->ifindex;
617
618         if (cmd == RTM_NEWROUTE)
619                 tb->insert(tb, &req.rtm, &rta, &req.nlh, NULL);
620         else
621                 tb->delete(tb, &req.rtm, &rta, &req.nlh, NULL);
622 }
623
624 static void dn_fib_add_ifaddr(struct dn_ifaddr *ifa)
625 {
626
627         fib_magic(RTM_NEWROUTE, RTN_LOCAL, ifa->ifa_local, 16, ifa);
628
629 #if 0
630         if (!(dev->flags&IFF_UP))
631                 return;
632         /* In the future, we will want to add default routes here */
633
634 #endif
635 }
636
637 static void dn_fib_del_ifaddr(struct dn_ifaddr *ifa)
638 {
639         int found_it = 0;
640         struct net_device *dev;
641         struct dn_dev *dn_db;
642         struct dn_ifaddr *ifa2;
643
644         ASSERT_RTNL();
645
646         /* Scan device list */
647         read_lock(&dev_base_lock);
648         for(dev = dev_base; dev; dev = dev->next) {
649                 dn_db = dev->dn_ptr;
650                 if (dn_db == NULL)
651                         continue;
652                 for(ifa2 = dn_db->ifa_list; ifa2; ifa2 = ifa2->ifa_next) {
653                         if (ifa2->ifa_local == ifa->ifa_local) {
654                                 found_it = 1;
655                                 break;
656                         }
657                 }
658         }
659         read_unlock(&dev_base_lock);
660
661         if (found_it == 0) {
662                 fib_magic(RTM_DELROUTE, RTN_LOCAL, ifa->ifa_local, 16, ifa);
663
664                 if (dnet_addr_type(ifa->ifa_local) != RTN_LOCAL) {
665                         if (dn_fib_sync_down(ifa->ifa_local, NULL, 0))
666                                 dn_fib_flush();
667                 }
668         }
669 }
670
671 static void dn_fib_disable_addr(struct net_device *dev, int force)
672 {
673         if (dn_fib_sync_down(0, dev, force))
674                 dn_fib_flush();
675         dn_rt_cache_flush(0);
676         neigh_ifdown(&dn_neigh_table, dev);
677 }
678
679 static int dn_fib_dnaddr_event(struct notifier_block *this, unsigned long event, void *ptr)
680 {
681         struct dn_ifaddr *ifa = (struct dn_ifaddr *)ptr;
682
683         switch(event) {
684                 case NETDEV_UP:
685                         dn_fib_add_ifaddr(ifa);
686                         dn_fib_sync_up(ifa->ifa_dev->dev);
687                         dn_rt_cache_flush(-1);
688                         break;
689                 case NETDEV_DOWN:
690                         dn_fib_del_ifaddr(ifa);
691                         if (ifa->ifa_dev && ifa->ifa_dev->ifa_list == NULL) {
692                                 dn_fib_disable_addr(ifa->ifa_dev->dev, 1);
693                         } else {
694                                 dn_rt_cache_flush(-1);
695                         }
696                         break;
697         }
698         return NOTIFY_DONE;
699 }
700
701 int dn_fib_sync_down(dn_address local, struct net_device *dev, int force)
702 {
703         int ret = 0;
704         int scope = RT_SCOPE_NOWHERE;
705
706         if (force)
707                 scope = -1;
708
709         for_fib_info() {
710                 /* 
711                  * This makes no sense for DECnet.... we will almost
712                  * certainly have more than one local address the same
713                  * over all our interfaces. It needs thinking about
714                  * some more.
715                  */
716                 if (local && fi->fib_prefsrc == local) {
717                         fi->fib_flags |= RTNH_F_DEAD;
718                         ret++;
719                 } else if (dev && fi->fib_nhs) {
720                         int dead = 0;
721
722                         change_nexthops(fi) {
723                                 if (nh->nh_flags&RTNH_F_DEAD)
724                                         dead++;
725                                 else if (nh->nh_dev == dev &&
726                                                 nh->nh_scope != scope) {
727                                         spin_lock_bh(&dn_fib_multipath_lock);
728                                         nh->nh_flags |= RTNH_F_DEAD;
729                                         fi->fib_power -= nh->nh_power;
730                                         nh->nh_power = 0;
731                                         spin_unlock_bh(&dn_fib_multipath_lock);
732                                         dead++;
733                                 }
734                         } endfor_nexthops(fi)
735                         if (dead == fi->fib_nhs) {
736                                 fi->fib_flags |= RTNH_F_DEAD;
737                                 ret++;
738                         }
739                 }
740         } endfor_fib_info();
741         return ret;
742 }
743
744
745 int dn_fib_sync_up(struct net_device *dev)
746 {
747         int ret = 0;
748
749         if (!(dev->flags&IFF_UP))
750                 return 0;
751
752         for_fib_info() {
753                 int alive = 0;
754
755                 change_nexthops(fi) {
756                         if (!(nh->nh_flags&RTNH_F_DEAD)) {
757                                 alive++;
758                                 continue;
759                         }
760                         if (nh->nh_dev == NULL || !(nh->nh_dev->flags&IFF_UP))
761                                 continue;
762                         if (nh->nh_dev != dev || dev->dn_ptr == NULL)
763                                 continue;
764                         alive++;
765                         spin_lock_bh(&dn_fib_multipath_lock);
766                         nh->nh_power = 0;
767                         nh->nh_flags &= ~RTNH_F_DEAD;
768                         spin_unlock_bh(&dn_fib_multipath_lock);
769                 } endfor_nexthops(fi);
770
771                 if (alive > 0) {
772                         fi->fib_flags &= ~RTNH_F_DEAD;
773                         ret++;
774                 }
775         } endfor_fib_info();
776         return ret;
777 }
778
779 void dn_fib_flush(void)
780 {
781         int flushed = 0;
782         struct dn_fib_table *tb;
783         int id;
784
785         for(id = RT_TABLE_MAX; id > 0; id--) {
786                 if ((tb = dn_fib_get_table(id, 0)) == NULL)
787                         continue;
788                 flushed += tb->flush(tb);
789         }
790
791         if (flushed)
792                 dn_rt_cache_flush(-1);
793 }
794
795 static struct notifier_block dn_fib_dnaddr_notifier = {
796         .notifier_call = dn_fib_dnaddr_event,
797 };
798
799 void __exit dn_fib_cleanup(void)
800 {
801         dn_fib_table_cleanup();
802         dn_fib_rules_cleanup();
803
804         unregister_dnaddr_notifier(&dn_fib_dnaddr_notifier);
805 }
806
807
808 void __init dn_fib_init(void)
809 {
810
811         dn_fib_table_init();
812         dn_fib_rules_init();
813
814         register_dnaddr_notifier(&dn_fib_dnaddr_notifier);
815 }
816
817