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