]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * BIRD -- UNIX Kernel Synchronization | |
3 | * | |
4 | * (c) 1998--1999 Martin Mares <mj@ucw.cz> | |
5 | * | |
6 | * Can be freely distributed and used under the terms of the GNU GPL. | |
7 | */ | |
8 | ||
9 | #define LOCAL_DEBUG | |
10 | ||
11 | #include "nest/bird.h" | |
12 | #include "nest/iface.h" | |
13 | #include "nest/route.h" | |
14 | #include "nest/protocol.h" | |
15 | #include "lib/timer.h" | |
16 | ||
17 | #include "unix.h" | |
18 | #include "krt.h" | |
19 | ||
20 | static int krt_uptodate(rte *k, rte *e); | |
21 | ||
22 | /* | |
23 | * Global resources | |
24 | */ | |
25 | ||
26 | void | |
27 | krt_io_init(void) | |
28 | { | |
29 | krt_if_io_init(); | |
30 | } | |
31 | ||
32 | /* | |
33 | * Interfaces | |
34 | */ | |
35 | ||
36 | struct proto_config *cf_kif; | |
37 | ||
38 | static struct kif_proto *kif_proto; | |
39 | static timer *kif_scan_timer; | |
40 | static bird_clock_t kif_last_shot; | |
41 | ||
42 | static void | |
43 | kif_scan(timer *t) | |
44 | { | |
45 | struct kif_proto *p = t->data; | |
46 | ||
47 | DBG("KIF: It's interface scan time...\n"); | |
48 | kif_last_shot = now; | |
49 | krt_if_scan(p); | |
50 | } | |
51 | ||
52 | static void | |
53 | kif_force_scan(void) | |
54 | { | |
55 | if (kif_proto && kif_last_shot + 2 < now) | |
56 | { | |
57 | kif_scan(kif_scan_timer); | |
58 | tm_start(kif_scan_timer, ((struct kif_config *) kif_proto->p.cf)->scan_time); | |
59 | } | |
60 | } | |
61 | ||
62 | static struct proto * | |
63 | kif_init(struct proto_config *c) | |
64 | { | |
65 | struct kif_proto *p = proto_new(c, sizeof(struct kif_proto)); | |
66 | return &p->p; | |
67 | } | |
68 | ||
69 | static int | |
70 | kif_start(struct proto *P) | |
71 | { | |
72 | struct kif_proto *p = (struct kif_proto *) P; | |
73 | ||
74 | kif_proto = p; | |
75 | krt_if_start(p); | |
76 | ||
77 | /* Start periodic interface scanning */ | |
78 | kif_scan_timer = tm_new(P->pool); | |
79 | kif_scan_timer->hook = kif_scan; | |
80 | kif_scan_timer->data = p; | |
81 | kif_scan_timer->recurrent = KIF_CF->scan_time; | |
82 | kif_scan(kif_scan_timer); | |
83 | tm_start(kif_scan_timer, KIF_CF->scan_time); | |
84 | ||
85 | return PS_UP; | |
86 | } | |
87 | ||
88 | static int | |
89 | kif_shutdown(struct proto *P) | |
90 | { | |
91 | struct kif_proto *p = (struct kif_proto *) P; | |
92 | ||
93 | tm_stop(kif_scan_timer); | |
94 | krt_if_shutdown(p); | |
95 | kif_proto = NULL; | |
96 | ||
97 | if_start_update(); /* Remove all interfaces */ | |
98 | if_end_update(); | |
99 | ||
100 | return PS_DOWN; | |
101 | } | |
102 | ||
103 | struct protocol proto_unix_iface = { | |
104 | name: "Device", | |
105 | priority: 100, | |
106 | init: kif_init, | |
107 | start: kif_start, | |
108 | shutdown: kif_shutdown, | |
109 | }; | |
110 | ||
111 | /* | |
112 | * Inherited Routes | |
113 | */ | |
114 | ||
115 | #ifdef KRT_ALLOW_LEARN | |
116 | ||
117 | static inline int | |
118 | krt_same_key(rte *a, rte *b) | |
119 | { | |
120 | return a->u.krt.proto == b->u.krt.proto && | |
121 | a->u.krt.metric == b->u.krt.metric && | |
122 | a->u.krt.type == b->u.krt.type; | |
123 | } | |
124 | ||
125 | static void | |
126 | krt_learn_announce_update(struct krt_proto *p, rte *e) | |
127 | { | |
128 | net *n = e->net; | |
129 | rta *aa = rta_clone(e->attrs); | |
130 | rte *ee = rte_get_temp(aa); | |
131 | net *nn = net_get(p->p.table, 0, n->n.prefix, n->n.pxlen); /* FIXME: TOS */ | |
132 | ee->net = nn; | |
133 | ee->pflags = 0; | |
134 | ee->u.krt = e->u.krt; | |
135 | rte_update(nn, &p->p, ee); | |
136 | } | |
137 | ||
138 | static void | |
139 | krt_learn_announce_delete(struct krt_proto *p, net *n) | |
140 | { | |
141 | n = net_find(p->p.table, 0, n->n.prefix, n->n.pxlen); /* FIXME: TOS */ | |
142 | if (n) | |
143 | rte_update(n, &p->p, NULL); | |
144 | } | |
145 | ||
146 | static void | |
147 | krt_learn_scan(struct krt_proto *p, rte *e) | |
148 | { | |
149 | net *n0 = e->net; | |
150 | net *n = net_get(&p->krt_table, 0, n0->n.prefix, n0->n.pxlen); /* FIXME: TOS */ | |
151 | rte *m, **mm; | |
152 | ||
153 | e->attrs->source = RTS_INHERIT; | |
154 | ||
155 | for(mm=&n->routes; m = *mm; mm=&m->next) | |
156 | if (krt_same_key(m, e)) | |
157 | break; | |
158 | if (m) | |
159 | { | |
160 | if (krt_uptodate(m, e)) | |
161 | { | |
162 | DBG("krt_learn_scan: SEEN\n"); | |
163 | rte_free(e); | |
164 | m->u.krt.seen = 1; | |
165 | } | |
166 | else | |
167 | { | |
168 | DBG("krt_learn_scan: OVERRIDE\n"); | |
169 | *mm = m->next; | |
170 | rte_free(m); | |
171 | m = NULL; | |
172 | } | |
173 | } | |
174 | else | |
175 | DBG("krt_learn_scan: CREATE\n"); | |
176 | if (!m) | |
177 | { | |
178 | e->attrs = rta_lookup(e->attrs); | |
179 | e->next = n->routes; | |
180 | n->routes = e; | |
181 | e->u.krt.seen = 1; | |
182 | } | |
183 | } | |
184 | ||
185 | /* FIXME: Add dump function */ | |
186 | ||
187 | static void | |
188 | krt_learn_prune(struct krt_proto *p) | |
189 | { | |
190 | struct fib *fib = &p->krt_table.fib; | |
191 | struct fib_iterator fit; | |
192 | ||
193 | DBG("Pruning inheritance data...\n"); | |
194 | ||
195 | FIB_ITERATE_INIT(&fit, fib); | |
196 | again: | |
197 | FIB_ITERATE_START(fib, &fit, f) | |
198 | { | |
199 | net *n = (net *) f; | |
200 | rte *e, **ee, *best, **pbest, *old_best; | |
201 | ||
202 | old_best = n->routes; | |
203 | best = NULL; | |
204 | pbest = NULL; | |
205 | ee = &n->routes; | |
206 | while (e = *ee) | |
207 | { | |
208 | if (!e->u.krt.seen) | |
209 | { | |
210 | *ee = e->next; | |
211 | rte_free(e); | |
212 | continue; | |
213 | } | |
214 | if (!best || best->u.krt.metric > e->u.krt.metric) | |
215 | { | |
216 | best = e; | |
217 | pbest = ee; | |
218 | } | |
219 | e->u.krt.seen = 0; | |
220 | ee = &e->next; | |
221 | } | |
222 | if (!n->routes) | |
223 | { | |
224 | DBG("%I/%d: deleting\n", n->n.prefix, n->n.pxlen); | |
225 | if (old_best) | |
226 | { | |
227 | krt_learn_announce_delete(p, n); | |
228 | n->n.flags &= ~KRF_INSTALLED; | |
229 | } | |
230 | FIB_ITERATE_PUT(&fit, f); | |
231 | fib_delete(fib, f); | |
232 | goto again; | |
233 | } | |
234 | *pbest = best->next; | |
235 | best->next = n->routes; | |
236 | n->routes = best; | |
237 | if (best != old_best || !(n->n.flags & KRF_INSTALLED)) | |
238 | { | |
239 | DBG("%I/%d: announcing (metric=%d)\n", n->n.prefix, n->n.pxlen, best->u.krt.metric); | |
240 | krt_learn_announce_update(p, best); | |
241 | n->n.flags |= KRF_INSTALLED; | |
242 | } | |
243 | else | |
244 | DBG("%I/%d: uptodate (metric=%d)\n", n->n.prefix, n->n.pxlen, best->u.krt.metric); | |
245 | } | |
246 | FIB_ITERATE_END(f); | |
247 | } | |
248 | ||
249 | static void | |
250 | krt_learn_async(struct krt_proto *p, rte *e, int new) | |
251 | { | |
252 | net *n0 = e->net; | |
253 | net *n = net_get(&p->krt_table, 0, n0->n.prefix, n0->n.pxlen); /* FIXME: TOS */ | |
254 | rte *g, **gg, *best, **bestp, *old_best; | |
255 | ||
256 | e->attrs->source = RTS_INHERIT; | |
257 | ||
258 | old_best = n->routes; | |
259 | for(gg=&n->routes; g = *gg; gg = &g->next) | |
260 | if (krt_same_key(g, e)) | |
261 | break; | |
262 | if (new) | |
263 | { | |
264 | if (g) | |
265 | { | |
266 | if (krt_uptodate(g, e)) | |
267 | { | |
268 | DBG("krt_learn_async: same\n"); | |
269 | rte_free(e); | |
270 | return; | |
271 | } | |
272 | DBG("krt_learn_async: update\n"); | |
273 | *gg = g->next; | |
274 | rte_free(g); | |
275 | } | |
276 | else | |
277 | DBG("krt_learn_async: create\n"); | |
278 | e->attrs = rta_lookup(e->attrs); | |
279 | e->next = n->routes; | |
280 | n->routes = e; | |
281 | } | |
282 | else if (!g) | |
283 | { | |
284 | DBG("krt_learn_async: not found\n"); | |
285 | rte_free(e); | |
286 | return; | |
287 | } | |
288 | else | |
289 | { | |
290 | DBG("krt_learn_async: delete\n"); | |
291 | *gg = g->next; | |
292 | rte_free(e); | |
293 | rte_free(g); | |
294 | } | |
295 | best = n->routes; | |
296 | bestp = &n->routes; | |
297 | for(gg=&n->routes; g=*gg; gg=&g->next) | |
298 | if (best->u.krt.metric > g->u.krt.metric) | |
299 | { | |
300 | best = g; | |
301 | bestp = gg; | |
302 | } | |
303 | if (best) | |
304 | { | |
305 | *bestp = best->next; | |
306 | best->next = n->routes; | |
307 | n->routes = best; | |
308 | } | |
309 | if (best != old_best) | |
310 | { | |
311 | DBG("krt_learn_async: distributing change\n"); | |
312 | if (best) | |
313 | { | |
314 | krt_learn_announce_update(p, best); | |
315 | n->n.flags |= KRF_INSTALLED; | |
316 | } | |
317 | else | |
318 | { | |
319 | n->routes = NULL; | |
320 | krt_learn_announce_delete(p, n); | |
321 | n->n.flags &= ~KRF_INSTALLED; | |
322 | } | |
323 | } | |
324 | } | |
325 | ||
326 | static void | |
327 | krt_learn_init(struct krt_proto *p) | |
328 | { | |
329 | if (KRT_CF->learn) | |
330 | rt_setup(p->p.pool, &p->krt_table, "Inherited"); | |
331 | } | |
332 | ||
333 | static void | |
334 | krt_dump(struct proto *P) | |
335 | { | |
336 | struct krt_proto *p = (struct krt_proto *) P; | |
337 | ||
338 | if (!KRT_CF->learn) | |
339 | return; | |
340 | debug("KRT: Table of inheritable routes\n"); | |
341 | rt_dump(&p->krt_table); | |
342 | } | |
343 | ||
344 | static void | |
345 | krt_dump_attrs(rte *e) | |
346 | { | |
347 | debug(" [m=%d,p=%d,t=%d]", e->u.krt.metric, e->u.krt.proto, e->u.krt.type); | |
348 | } | |
349 | ||
350 | #endif | |
351 | ||
352 | /* | |
353 | * Routes | |
354 | */ | |
355 | ||
356 | static void | |
357 | krt_flush_routes(struct krt_proto *p) | |
358 | { | |
359 | struct rtable *t = &master_table; | |
360 | ||
361 | DBG("Flushing kernel routes...\n"); | |
362 | while (t && t->tos) | |
363 | t = t->sibling; | |
364 | if (!t) | |
365 | return; | |
366 | FIB_WALK(&t->fib, f) | |
367 | { | |
368 | net *n = (net *) f; | |
369 | rte *e = n->routes; | |
370 | if (e) | |
371 | { | |
372 | rta *a = e->attrs; | |
373 | if (a->source != RTS_DEVICE && a->source != RTS_INHERIT) | |
374 | krt_set_notify(p, e->net, NULL, e); | |
375 | } | |
376 | } | |
377 | FIB_WALK_END; | |
378 | } | |
379 | ||
380 | /* FIXME: Synchronization of multiple routing tables? */ | |
381 | ||
382 | static int | |
383 | krt_uptodate(rte *k, rte *e) | |
384 | { | |
385 | rta *ka = k->attrs, *ea = e->attrs; | |
386 | ||
387 | if (ka->dest != ea->dest) | |
388 | return 0; | |
389 | switch (ka->dest) | |
390 | { | |
391 | case RTD_ROUTER: | |
392 | return ipa_equal(ka->gw, ea->gw); | |
393 | case RTD_DEVICE: | |
394 | return !strcmp(ka->iface->name, ea->iface->name); | |
395 | default: | |
396 | return 1; | |
397 | } | |
398 | } | |
399 | ||
400 | /* | |
401 | * This gets called back when the low-level scanning code discovers a route. | |
402 | * We expect that the route is a temporary rte and its attributes are uncached. | |
403 | */ | |
404 | ||
405 | void | |
406 | krt_got_route(struct krt_proto *p, rte *e) | |
407 | { | |
408 | rte *old; | |
409 | net *net = e->net; | |
410 | int src = e->u.krt.src; | |
411 | int verdict; | |
412 | ||
413 | #ifdef CONFIG_AUTO_ROUTES | |
414 | if (e->attrs->dest == RTD_DEVICE) | |
415 | { | |
416 | /* It's a device route. Probably a kernel-generated one. */ | |
417 | verdict = KRF_IGNORE; | |
418 | goto sentenced; | |
419 | } | |
420 | #endif | |
421 | ||
422 | #ifdef KRT_ALLOW_LEARN | |
423 | if (src == KRT_SRC_ALIEN) | |
424 | { | |
425 | if (KRT_CF->learn) | |
426 | krt_learn_scan(p, e); | |
427 | else | |
428 | DBG("krt_parse_entry: Alien route, ignoring\n"); | |
429 | return; | |
430 | } | |
431 | #endif | |
432 | ||
433 | if (net->n.flags & KRF_VERDICT_MASK) | |
434 | { | |
435 | /* Route to this destination was already seen. Strange, but it happens... */ | |
436 | DBG("Already seen.\n"); | |
437 | return; | |
438 | } | |
439 | ||
440 | if (net->n.flags & KRF_INSTALLED) | |
441 | { | |
442 | old = net->routes; | |
443 | ASSERT(old); | |
444 | if (krt_uptodate(e, old)) | |
445 | verdict = KRF_SEEN; | |
446 | else | |
447 | verdict = KRF_UPDATE; | |
448 | } | |
449 | else | |
450 | verdict = KRF_DELETE; | |
451 | ||
452 | sentenced: | |
453 | DBG("krt_parse_entry: verdict=%s\n", ((char *[]) { "CREATE", "SEEN", "UPDATE", "DELETE", "IGNORE" }) [verdict]); | |
454 | ||
455 | net->n.flags = (net->n.flags & ~KRF_VERDICT_MASK) | verdict; | |
456 | if (verdict == KRF_UPDATE || verdict == KRF_DELETE) | |
457 | { | |
458 | /* Get a cached copy of attributes and link the route */ | |
459 | rta *a = e->attrs; | |
460 | a->source = RTS_DUMMY; | |
461 | e->attrs = rta_lookup(a); | |
462 | e->next = net->routes; | |
463 | net->routes = e; | |
464 | } | |
465 | else | |
466 | rte_free(e); | |
467 | } | |
468 | ||
469 | static void | |
470 | krt_prune(struct krt_proto *p) | |
471 | { | |
472 | struct proto *pp = &p->p; | |
473 | struct rtable *t = &master_table; | |
474 | struct fib_node *f; | |
475 | ||
476 | DBG("Pruning routes...\n"); | |
477 | while (t && t->tos) | |
478 | t = t->sibling; | |
479 | if (!t) | |
480 | return; | |
481 | FIB_WALK(&t->fib, f) | |
482 | { | |
483 | net *n = (net *) f; | |
484 | int verdict = f->flags & KRF_VERDICT_MASK; | |
485 | rte *new, *old; | |
486 | ||
487 | if (verdict != KRF_CREATE && verdict != KRF_SEEN && verdict != KRF_IGNORE) | |
488 | { | |
489 | old = n->routes; | |
490 | n->routes = old->next; | |
491 | } | |
492 | else | |
493 | old = NULL; | |
494 | new = n->routes; | |
495 | ||
496 | switch (verdict) | |
497 | { | |
498 | case KRF_CREATE: | |
499 | if (new && (f->flags & KRF_INSTALLED)) | |
500 | { | |
501 | DBG("krt_prune: reinstalling %I/%d\n", n->n.prefix, n->n.pxlen); | |
502 | krt_set_notify(p, n, new, NULL); | |
503 | } | |
504 | break; | |
505 | case KRF_SEEN: | |
506 | case KRF_IGNORE: | |
507 | /* Nothing happens */ | |
508 | break; | |
509 | case KRF_UPDATE: | |
510 | DBG("krt_prune: updating %I/%d\n", n->n.prefix, n->n.pxlen); | |
511 | krt_set_notify(p, n, new, old); | |
512 | break; | |
513 | case KRF_DELETE: | |
514 | DBG("krt_prune: deleting %I/%d\n", n->n.prefix, n->n.pxlen); | |
515 | krt_set_notify(p, n, NULL, old); | |
516 | break; | |
517 | default: | |
518 | bug("krt_prune: invalid route status"); | |
519 | } | |
520 | if (old) | |
521 | rte_free(old); | |
522 | f->flags &= ~KRF_VERDICT_MASK; | |
523 | } | |
524 | FIB_WALK_END; | |
525 | ||
526 | #ifdef KRT_ALLOW_LEARN | |
527 | if (KRT_CF->learn) | |
528 | krt_learn_prune(p); | |
529 | #endif | |
530 | } | |
531 | ||
532 | void | |
533 | krt_got_route_async(struct krt_proto *p, rte *e, int new) | |
534 | { | |
535 | net *net = e->net; | |
536 | rte *old = net->routes; | |
537 | int src = e->u.krt.src; | |
538 | ||
539 | switch (src) | |
540 | { | |
541 | case KRT_SRC_BIRD: | |
542 | ASSERT(0); | |
543 | case KRT_SRC_REDIRECT: | |
544 | DBG("It's a redirect, kill him! Kill! Kill!\n"); | |
545 | krt_set_notify(p, net, NULL, e); | |
546 | break; | |
547 | case KRT_SRC_ALIEN: | |
548 | #ifdef KRT_ALLOW_LEARN | |
549 | if (KRT_CF->learn) | |
550 | { | |
551 | krt_learn_async(p, e, new); | |
552 | return; | |
553 | } | |
554 | #endif | |
555 | /* Fall-thru */ | |
556 | default: | |
557 | DBG("Discarding\n"); | |
558 | rte_update(net, &p->p, NULL); | |
559 | } | |
560 | rte_free(e); | |
561 | } | |
562 | ||
563 | /* | |
564 | * Periodic scanning | |
565 | */ | |
566 | ||
567 | static timer *krt_scan_timer; | |
568 | ||
569 | static void | |
570 | krt_scan(timer *t) | |
571 | { | |
572 | struct krt_proto *p = t->data; | |
573 | ||
574 | kif_force_scan(); | |
575 | DBG("KRT: It's route scan time...\n"); | |
576 | krt_scan_fire(p); | |
577 | krt_prune(p); | |
578 | } | |
579 | ||
580 | /* | |
581 | * Updates | |
582 | */ | |
583 | ||
584 | static void | |
585 | krt_notify(struct proto *P, net *net, rte *new, rte *old) | |
586 | { | |
587 | struct krt_proto *p = (struct krt_proto *) P; | |
588 | ||
589 | if (new && (!krt_capable(new) || new->attrs->source == RTS_INHERIT)) | |
590 | new = NULL; | |
591 | if (!(net->n.flags & KRF_INSTALLED)) | |
592 | old = NULL; | |
593 | if (new) | |
594 | net->n.flags |= KRF_INSTALLED; | |
595 | else | |
596 | net->n.flags &= ~KRF_INSTALLED; | |
597 | krt_set_notify(p, net, new, old); | |
598 | } | |
599 | ||
600 | /* | |
601 | * Protocol glue | |
602 | */ | |
603 | ||
604 | struct proto_config *cf_krt; | |
605 | ||
606 | static int | |
607 | krt_start(struct proto *P) | |
608 | { | |
609 | struct krt_proto *p = (struct krt_proto *) P; | |
610 | ||
611 | #ifdef KRT_ALLOW_LEARN | |
612 | krt_learn_init(p); | |
613 | #endif | |
614 | ||
615 | krt_scan_start(p); | |
616 | krt_set_start(p); | |
617 | ||
618 | /* Start periodic routing table scanning */ | |
619 | krt_scan_timer = tm_new(P->pool); | |
620 | krt_scan_timer->hook = krt_scan; | |
621 | krt_scan_timer->data = p; | |
622 | krt_scan_timer->recurrent = KRT_CF->scan_time; | |
623 | krt_scan(krt_scan_timer); | |
624 | tm_start(krt_scan_timer, KRT_CF->scan_time); | |
625 | ||
626 | return PS_UP; | |
627 | } | |
628 | ||
629 | int | |
630 | krt_shutdown(struct proto *P) | |
631 | { | |
632 | struct krt_proto *p = (struct krt_proto *) P; | |
633 | ||
634 | tm_stop(krt_scan_timer); | |
635 | ||
636 | if (!KRT_CF->persist) | |
637 | krt_flush_routes(p); | |
638 | ||
639 | krt_set_shutdown(p); | |
640 | krt_scan_shutdown(p); | |
641 | ||
642 | return PS_DOWN; | |
643 | } | |
644 | ||
645 | static struct proto * | |
646 | krt_init(struct proto_config *c) | |
647 | { | |
648 | struct krt_proto *p = proto_new(c, sizeof(struct krt_proto)); | |
649 | ||
650 | p->p.rt_notify = krt_notify; | |
651 | return &p->p; | |
652 | } | |
653 | ||
654 | struct protocol proto_unix_kernel = { | |
655 | name: "Kernel", | |
656 | priority: 80, | |
657 | init: krt_init, | |
658 | start: krt_start, | |
659 | shutdown: krt_shutdown, | |
660 | #ifdef KRT_ALLOW_LEARN | |
661 | dump: krt_dump, | |
662 | dump_attrs: krt_dump_attrs, | |
663 | #endif | |
664 | }; |