mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-12-22 17:51:53 +00:00
345 lines
6.8 KiB
C
345 lines
6.8 KiB
C
/*
|
|
* BIRD -- Linux Routing Table Scanning
|
|
*
|
|
* (c) 1998--1999 Martin Mares <mj@ucw.cz>
|
|
*
|
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <net/route.h>
|
|
|
|
#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 = &(((struct krt_config *)(x->p.cf))->scanopt)
|
|
#define SCANSTAT struct krt_scan_status *s = &x->scanstat
|
|
|
|
static int krt_scan_fd = -1;
|
|
|
|
/* FIXME: Filtering */
|
|
|
|
struct iface *
|
|
krt_temp_iface(struct krt_proto *x, char *name)
|
|
{
|
|
SCANOPT;
|
|
SCANSTAT;
|
|
struct iface *i;
|
|
|
|
WALK_LIST(i, s->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(&s->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 *x)
|
|
{
|
|
SCANOPT;
|
|
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 = &x->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(&x->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(x, 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->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:
|
|
bug("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;
|
|
SCANSTAT;
|
|
|
|
s->accum_time += p->scan_time;
|
|
if (p->scan_time && s->accum_time >= p->scan_time)
|
|
{
|
|
s->accum_time %= p->scan_time;
|
|
if (krt_scan_proc(x))
|
|
krt_prune(x);
|
|
}
|
|
}
|
|
|
|
void
|
|
krt_scan_preconfig(struct krt_config *c)
|
|
{
|
|
c->scanopt.scan_time = 1;
|
|
c->scanopt.learn = 0;
|
|
}
|
|
|
|
void
|
|
krt_scan_start(struct krt_proto *x)
|
|
{
|
|
SCANOPT;
|
|
SCANSTAT;
|
|
|
|
/* Force krt scan after first interface scan */
|
|
s->accum_time = p->scan_time - ((struct krt_config *) x->p.cf)->ifopt.scan_time;
|
|
|
|
init_list(&s->temp_ifs);
|
|
}
|
|
|
|
void
|
|
krt_scan_shutdown(struct krt_proto *x)
|
|
{
|
|
}
|