7e5f5ffdda
o Nothing is configured automatically. You _need_ to specify the kernel syncer in config file in order to get it started. o Syncing has been split to route syncer (protocol "Kernel") and interface syncer (protocol "Device"), device routes are generated by protocol "Direct" (now can exist in multiple instances, so that it will be possible to feed different device routes to different routing tables once multiple tables get supported). See doc/bird.conf.example for a living example of these shiny features.
386 lines
7.3 KiB
C
386 lines
7.3 KiB
C
/*
|
|
* 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,
|
|
};
|