summaryrefslogtreecommitdiff
path: root/sysdep/unix/krt.c
diff options
context:
space:
mode:
Diffstat (limited to 'sysdep/unix/krt.c')
-rw-r--r--sysdep/unix/krt.c291
1 files changed, 291 insertions, 0 deletions
diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c
new file mode 100644
index 00000000..d44a57d8
--- /dev/null
+++ b/sysdep/unix/krt.c
@@ -0,0 +1,291 @@
+/*
+ * 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"
+
+struct proto_config *cf_krt;
+
+/*
+ * 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, e, NULL);
+ }
+ }
+ 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 verdict;
+
+ if (net->n.flags)
+ {
+ /* Route to this destination was already seen. Strange, but it happens... */
+ DBG("Already seen.\n");
+ return;
+ }
+
+ 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 (KRT_CF->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 */
+ 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
+ {
+ 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;
+}
+
+/*
+ * Periodic scanning
+ */
+
+static timer *krt_scan_timer;
+
+static void
+krt_scan(timer *t)
+{
+ struct krt_proto *p = t->data;
+
+ DBG("KRT: It's scan time...\n");
+ krt_if_scan(p);
+
+ p->accum_time += KRT_CF->scan_time;
+ if (KRT_CF->route_scan_time && p->accum_time >= KRT_CF->route_scan_time)
+ {
+ p->accum_time %= KRT_CF->route_scan_time;
+ DBG("Scanning kernel routing table...\n");
+ krt_scan_fire(p);
+ krt_prune(p);
+ }
+}
+
+/*
+ * Protocol glue
+ */
+
+static int
+krt_start(struct proto *P)
+{
+ struct krt_proto *p = (struct krt_proto *) P;
+
+ p->accum_time = 0;
+
+ krt_if_start(p);
+ krt_scan_start(p);
+ krt_set_start(p);
+
+ /* Start periodic interface 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;
+
+ if (!KRT_CF->persist)
+ krt_flush_routes(p);
+
+ krt_set_shutdown(p);
+ krt_scan_shutdown(p);
+
+ /* Stop periodic interface scans */
+ tm_stop(krt_scan_timer);
+ krt_if_shutdown(p);
+ /* FIXME: What should we do with interfaces? */
+
+ return PS_DOWN;
+}
+
+static void
+krt_preconfig(struct protocol *x, struct config *c)
+{
+ struct krt_config *z = proto_config_new(&proto_unix_kernel, sizeof(struct krt_config));
+
+ cf_krt = &z->c;
+ z->c.preference = DEF_PREF_UKR;
+ z->scan_time = z->route_scan_time = 60;
+ z->learn = z->persist = 0;
+
+ krt_scan_preconfig(z);
+ krt_set_preconfig(z);
+ krt_if_preconfig(z);
+}
+
+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: 90,
+ preconfig: krt_preconfig,
+ init: krt_init,
+ start: krt_start,
+ shutdown: krt_shutdown,
+};