/* * BIRD -- UNIX Kernel Synchronization * * (c) 1998--1999 Martin Mares <mj@ucw.cz> * * Can be freely distributed and used under the terms of the GNU GPL. */ #define LOCAL_DEBUG #include "nest/bird.h" #include "nest/iface.h" #include "nest/route.h" #include "nest/protocol.h" #include "lib/timer.h" #include "unix.h" #include "krt.h" /* * Global resources */ void krt_io_init(void) { krt_if_io_init(); } /* * Interfaces */ struct proto_config *cf_kif; static struct kif_proto *kif_proto; static timer *kif_scan_timer; static bird_clock_t kif_last_shot; static void kif_scan(timer *t) { struct kif_proto *p = t->data; DBG("KIF: It's interface scan time...\n"); kif_last_shot = now; krt_if_scan(p); } static void kif_force_scan(void) { if (kif_proto && kif_last_shot + 2 < now) { kif_scan(kif_scan_timer); tm_start(kif_scan_timer, ((struct kif_config *) kif_proto->p.cf)->scan_time); } } static struct proto * kif_init(struct proto_config *c) { struct kif_proto *p = proto_new(c, sizeof(struct kif_proto)); return &p->p; } static int kif_start(struct proto *P) { struct kif_proto *p = (struct kif_proto *) P; kif_proto = p; krt_if_start(p); /* Start periodic interface scanning */ kif_scan_timer = tm_new(P->pool); kif_scan_timer->hook = kif_scan; kif_scan_timer->data = p; kif_scan_timer->recurrent = KIF_CF->scan_time; kif_scan(kif_scan_timer); tm_start(kif_scan_timer, KIF_CF->scan_time); return PS_UP; } static int kif_shutdown(struct proto *P) { struct kif_proto *p = (struct kif_proto *) P; tm_stop(kif_scan_timer); krt_if_shutdown(p); kif_proto = NULL; if_start_update(); /* Remove all interfaces */ if_end_update(); return PS_DOWN; } struct protocol proto_unix_iface = { name: "Device", priority: 100, init: kif_init, start: kif_start, shutdown: kif_shutdown, }; /* * Routes */ static void krt_flush_routes(struct krt_proto *p) { struct rtable *t = &master_table; DBG("Flushing kernel routes...\n"); while (t && t->tos) t = t->sibling; if (!t) return; FIB_WALK(&t->fib, f) { net *n = (net *) f; rte *e = n->routes; if (e) { rta *a = e->attrs; if (a->source != RTS_DEVICE && a->source != RTS_INHERIT) krt_set_notify(&p->p, e->net, NULL, e); } } FIB_WALK_END; } /* FIXME: Inbound/outbound route filtering? */ /* FIXME: Synchronization of multiple routing tables? */ static int krt_uptodate(rte *k, rte *e) { rta *ka = k->attrs, *ea = e->attrs; if (ka->dest != ea->dest) return 0; switch (ka->dest) { case RTD_ROUTER: return ipa_equal(ka->gw, ea->gw); case RTD_DEVICE: return !strcmp(ka->iface->name, ea->iface->name); default: return 1; } } /* * This gets called back when the low-level scanning code discovers a route. * We expect that the route is a temporary rte and its attributes are uncached. */ void krt_got_route(struct krt_proto *p, rte *e) { rte *old; net *net = e->net; int src = e->u.krt_sync.src; int verdict; if (net->n.flags) { /* Route to this destination was already seen. Strange, but it happens... */ DBG("Already seen.\n"); return; } if (old = net->routes) { if (!krt_capable(old)) verdict = krt_capable(e) ? KRF_DELETE : KRF_SEEN; else if (krt_uptodate(e, net->routes)) verdict = KRF_SEEN; else verdict = KRF_UPDATE; } else if (KRT_CF->learn && !net->routes && (src == KRT_SRC_ALIEN || src < 0)) verdict = KRF_LEARN; else verdict = KRF_DELETE; DBG("krt_parse_entry: verdict=%s\n", ((char *[]) { "CREATE", "SEEN", "UPDATE", "DELETE", "LEARN" }) [verdict]); net->n.flags = verdict; if (verdict != KRF_SEEN) { /* Get a cached copy of attributes and link the route */ rta *a = e->attrs; a->source = RTS_DUMMY; e->attrs = rta_lookup(a); e->next = net->routes; net->routes = e; } else rte_free(e); } static void krt_prune(struct krt_proto *p) { struct proto *pp = &p->p; struct rtable *t = &master_table; struct fib_node *f; DBG("Pruning routes...\n"); while (t && t->tos) t = t->sibling; if (!t) return; FIB_WALK(&t->fib, f) { net *n = (net *) f; int verdict = f->flags; rte *new, *old; if (verdict != KRF_CREATE && verdict != KRF_SEEN) { old = n->routes; n->routes = old->next; } else old = NULL; new = n->routes; switch (verdict) { case KRF_CREATE: if (new) { if (new->attrs->source == RTS_INHERIT) { DBG("krt_prune: removing inherited %I/%d\n", n->n.prefix, n->n.pxlen); rte_update(n, pp, NULL); } else if (krt_capable(new)) { DBG("krt_prune: reinstalling %I/%d\n", n->n.prefix, n->n.pxlen); krt_set_notify(pp, n, new, NULL); } } break; case KRF_SEEN: /* Nothing happens */ break; case KRF_UPDATE: DBG("krt_prune: updating %I/%d\n", n->n.prefix, n->n.pxlen); krt_set_notify(pp, n, new, old); break; case KRF_DELETE: DBG("krt_prune: deleting %I/%d\n", n->n.prefix, n->n.pxlen); krt_set_notify(pp, n, NULL, old); break; case KRF_LEARN: DBG("krt_prune: learning %I/%d\n", n->n.prefix, n->n.pxlen); rte_update(n, pp, new); break; default: bug("krt_prune: invalid route status"); } if (old) rte_free(old); f->flags = 0; } FIB_WALK_END; } void krt_got_route_async(struct krt_proto *p, rte *e, int new) { net *net = e->net; rte *old = net->routes; int src = e->u.krt_sync.src; switch (src) { case KRT_SRC_BIRD: ASSERT(0); case KRT_SRC_REDIRECT: DBG("It's a redirect, kill him! Kill! Kill!\n"); krt_set_notify(&p->p, net, NULL, e); break; default: /* Alien or unspecified */ if (KRT_CF->learn && new) { /* * FIXME: This is limited to one inherited route per destination as we * use single protocol for all inherited routes. Probably leave it * as-is (and document it :)), because the correct solution is to * use multiple kernel tables anyway. */ DBG("Learning\n"); rte_update(net, &p->p, e); } else { DBG("Discarding\n"); rte_update(net, &p->p, NULL); } } } /* * Periodic scanning */ static timer *krt_scan_timer; static void krt_scan(timer *t) { struct krt_proto *p = t->data; kif_force_scan(); DBG("KRT: It's route scan time...\n"); krt_scan_fire(p); krt_prune(p); } /* * Protocol glue */ struct proto_config *cf_krt; static int krt_start(struct proto *P) { struct krt_proto *p = (struct krt_proto *) P; krt_scan_start(p); krt_set_start(p); /* Start periodic routing table scanning */ krt_scan_timer = tm_new(P->pool); krt_scan_timer->hook = krt_scan; krt_scan_timer->data = p; krt_scan_timer->recurrent = KRT_CF->scan_time; krt_scan(krt_scan_timer); tm_start(krt_scan_timer, KRT_CF->scan_time); return PS_UP; } int krt_shutdown(struct proto *P) { struct krt_proto *p = (struct krt_proto *) P; tm_stop(krt_scan_timer); if (!KRT_CF->persist) krt_flush_routes(p); krt_set_shutdown(p); krt_scan_shutdown(p); return PS_DOWN; } static struct proto * krt_init(struct proto_config *c) { struct krt_proto *p = proto_new(c, sizeof(struct krt_proto)); p->p.rt_notify = krt_set_notify; return &p->p; } struct protocol proto_unix_kernel = { name: "Kernel", priority: 80, init: krt_init, start: krt_start, shutdown: krt_shutdown, };