/* * BIRD -- Linux Routing Table Scanning * * (c) 1998 Martin Mares * * Can be freely distributed and used under the terms of the GNU GPL. */ #include #include #include #include #include #include #define LOCAL_DEBUG #include "nest/bird.h" #include "nest/route.h" #include "nest/protocol.h" #include "nest/iface.h" #include "lib/timer.h" #include "lib/unix.h" #include "lib/krt.h" #define SCANOPT struct krt_scan_params *p = &x->scanopt static int krt_scan_fd = -1; /* FIXME: Filtering */ struct iface * krt_temp_iface(struct krt_proto *x, char *name) { SCANOPT; struct iface *i; WALK_LIST(i, p->temp_ifs) if (!strcmp(i->name, name)) return i; i = mb_alloc(x->p.pool, sizeof(struct iface)); bzero(i, sizeof(*i)); strcpy(i->name, name); add_tail(&p->temp_ifs, &i->n); return i; } 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; } } static void krt_parse_entry(byte *ent, struct krt_proto *p) { u32 dest0, gw0, mask0; ip_addr dest, gw, mask; unsigned int flags, verdict; int masklen; net *net; byte *iface = ent; rta a; rte *e, *old; if (sscanf(ent, "%*s\t%x\t%x\t%x\t%*d\t%*d\t%*d\t%x\t", &dest0, &gw0, &flags, &mask0) != 4) { log(L_ERR "krt read: unable to parse `%s'", ent); return; } while (*ent != '\t') ent++; *ent = 0; dest = ipa_from_u32(dest0); ipa_ntoh(dest); gw = ipa_from_u32(gw0); ipa_ntoh(gw); mask = ipa_from_u32(mask0); ipa_ntoh(mask); if ((masklen = ipa_mklen(mask)) < 0) { log(L_ERR "krt read: invalid netmask %08x", mask0); return; } DBG("Got %I/%d via %I flags %x\n", dest, masklen, gw, flags); if (!(flags & RTF_UP)) { DBG("Down.\n"); return; } if (flags & RTF_HOST) masklen = 32; if (flags & (RTF_DYNAMIC | RTF_MODIFIED)) /* Redirect route */ { log(L_WARN "krt: Ignoring redirect to %I/%d via %I", dest, masklen, gw); return; } net = net_get(&master_table, 0, dest, masklen); if (net->n.flags) { /* Route to this destination was already seen. Strange, but it happens... */ DBG("Already seen.\n"); return; } a.proto = &p->p; a.source = RTS_INHERIT; a.scope = SCOPE_UNIVERSE; a.cast = RTC_UNICAST; a.tos = a.flags = a.aflags = 0; a.from = IPA_NONE; a.iface = NULL; a.attrs = NULL; if (flags & RTF_GATEWAY) { neighbor *ng = neigh_find(&p->p, &gw, 0); if (ng) a.iface = ng->iface; else /* FIXME: Remove this warning? */ log(L_WARN "Kernel told us to use non-neighbor %I for %I/%d", gw, net->n.prefix, net->n.pxlen); a.dest = RTD_ROUTER; a.gw = gw; } else if (flags & RTF_REJECT) { a.dest = RTD_UNREACHABLE; a.gw = IPA_NONE; } else if (isalpha(iface[0])) { a.dest = RTD_DEVICE; a.gw = IPA_NONE; a.iface = krt_temp_iface(p, iface); } else { log(L_WARN "Kernel reporting unknown route type to %I/%d", net->n.prefix, net->n.pxlen); return; } e = rte_get_temp(&a); e->net = net; old = net->routes; if (old && !krt_capable(old)) old = NULL; if (old) { if (krt_uptodate(e, net->routes)) verdict = KRF_SEEN; else verdict = KRF_UPDATE; } else if (p->scanopt.learn && !net->routes) 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 */ a.source = RTS_DUMMY; e->attrs = rta_lookup(&a); e->next = net->routes; net->routes = e; } else rte_free(e); } static int krt_scan_proc(struct krt_proto *p) { byte buf[32768]; int l, seen_hdr; DBG("Scanning kernel routing table...\n"); if (krt_scan_fd < 0) { krt_scan_fd = open("/proc/net/route", O_RDONLY); if (krt_scan_fd < 0) die("/proc/net/route: %m"); } else if (lseek(krt_scan_fd, 0, SEEK_SET) < 0) { log(L_ERR "krt seek: %m"); return 0; } seen_hdr = 0; while ((l = read(krt_scan_fd, buf, sizeof(buf))) > 0) { byte *z = buf; if (l & 127) { log(L_ERR "krt read: misaligned entry: l=%d", l); return 0; } while (l >= 128) { if (seen_hdr++) krt_parse_entry(z, p); z += 128; l -= 128; } } if (l < 0) { log(L_ERR "krt read: %m"); return 0; } DBG("KRT scan done, seen %d lines\n", seen_hdr); return 1; } static void krt_prune(struct krt_proto *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, &p->p, NULL); } else { DBG("krt_prune: reinstalling %I/%d\n", n->n.prefix, n->n.pxlen); krt_add_route(new); } } break; case KRF_SEEN: /* Nothing happens */ break; case KRF_UPDATE: DBG("krt_prune: updating %I/%d\n", n->n.prefix, n->n.pxlen); krt_remove_route(old); krt_add_route(new); break; case KRF_DELETE: DBG("krt_prune: deleting %I/%d\n", n->n.prefix, n->n.pxlen); krt_remove_route(old); break; case KRF_LEARN: DBG("krt_prune: learning %I/%d\n", n->n.prefix, n->n.pxlen); rte_update(n, &p->p, new); break; default: die("krt_prune: invalid route status"); } if (old) rte_free(old); f->flags = 0; } FIB_WALK_END; } void krt_scan_ifaces_done(struct krt_proto *x) { SCANOPT; p->accum_time += x->ifopt.scan_time; if (p->scan_time && p->accum_time >= p->scan_time) { p->accum_time %= p->scan_time; if (krt_scan_proc(x)) krt_prune(x); } } void krt_scan_preconfig(struct krt_proto *x) { SCANOPT; p->scan_time = 1; p->learn = 0; init_list(&p->temp_ifs); } void krt_scan_start(struct krt_proto *x) { SCANOPT; /* Force krt scan after first interface scan */ p->accum_time = p->scan_time - x->ifopt.scan_time; } void krt_scan_shutdown(struct krt_proto *x) { }