summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/bird.sgml12
-rw-r--r--sysdep/bsd/krt-sock.c13
-rw-r--r--sysdep/linux/netlink/netlink.c29
-rw-r--r--sysdep/unix/krt.Y5
-rw-r--r--sysdep/unix/krt.c158
-rw-r--r--sysdep/unix/krt.h8
-rw-r--r--sysdep/unix/main.c2
7 files changed, 186 insertions, 41 deletions
diff --git a/doc/bird.sgml b/doc/bird.sgml
index 4024f137..20738be3 100644
--- a/doc/bird.sgml
+++ b/doc/bird.sgml
@@ -1618,7 +1618,7 @@ kernel table.
<p>Because the kernel protocol is partially integrated with the
connected routing table, there are two limitations - it is not
possible to connect more kernel protocols to the same routing table
-and changing route attributes (even the kernel ones) in an export
+and changing route destination/gateway in an export
filter of a kernel protocol does not work. Both limitations can be
overcome using another routing table and the pipe protocol.
@@ -1653,6 +1653,16 @@ are translated to appropriate system (and OS-specific) route attributes.
We support these attributes:
<descrip>
+ <tag>int <cf/krt_source/</tag> The original source of the imported
+ kernel route. The value is system-dependent. On Linux, it is
+ a value of the protocol field of the route. See
+ /etc/iproute2/rt_protos for common values. On BSD, it is
+ based on STATIC and PROTOx flags. The attribute is read-only.
+
+ <tag>int <cf/krt_metric/</tag> The kernel metric of
+ the route. When multiple same routes are in a kernel routing
+ table, the Linux kernel chooses one with lower metric.
+
<tag>ip <cf/krt_prefsrc/</tag> (Linux) The preferred source address.
Used in source address selection for outgoing packets. Have to
be one of IP addresses of the router.
diff --git a/sysdep/bsd/krt-sock.c b/sysdep/bsd/krt-sock.c
index 4ee5495f..9ca36d83 100644
--- a/sysdep/bsd/krt-sock.c
+++ b/sysdep/bsd/krt-sock.c
@@ -189,7 +189,8 @@ krt_sock_send(int cmd, rte *e)
}
void
-krt_set_notify(struct krt_proto *p UNUSED, net *n, rte *new, rte *old)
+krt_set_notify(struct krt_proto *p UNUSED, net *n, rte *new, rte *old,
+ struct ea_list *eattrs UNUSED)
{
int err = 0;
@@ -255,10 +256,11 @@ krt_read_rt(struct ks_msg *msg, struct krt_proto *p, int scan)
ip_addr idst, igate, imask;
void *body = (char *)msg->buf;
int new = (msg->rtm.rtm_type == RTM_ADD);
- int src;
char *errmsg = "KRT: Invalid route received";
int flags = msg->rtm.rtm_flags;
int addrs = msg->rtm.rtm_addrs;
+ int src;
+ byte src2;
if (!(flags & RTF_UP) && scan)
SKIP("not up in scan\n");
@@ -302,12 +304,17 @@ krt_read_rt(struct ks_msg *msg, struct krt_proto *p, int scan)
u32 self_mask = RTF_PROTO1;
u32 alien_mask = RTF_STATIC | RTF_PROTO1 | RTF_GATEWAY;
+ src2 = (flags & RTF_STATIC) ? 1 : 0;
+ src2 |= (flags & RTF_PROTO1) ? 2 : 0;
+
#ifdef RTF_PROTO2
alien_mask |= RTF_PROTO2;
+ src2 |= (flags & RTF_PROTO2) ? 4 : 0;
#endif
#ifdef RTF_PROTO3
alien_mask |= RTF_PROTO3;
+ src2 |= (flags & RTF_PROTO3) ? 8 : 0;
#endif
#ifdef RTF_REJECT
@@ -397,9 +404,9 @@ krt_read_rt(struct ks_msg *msg, struct krt_proto *p, int scan)
e = rte_get_temp(&a);
e->net = net;
e->u.krt.src = src;
+ e->u.krt.proto = src2;
/* These are probably too Linux-specific */
- e->u.krt.proto = 0;
e->u.krt.type = 0;
e->u.krt.metric = 0;
diff --git a/sysdep/linux/netlink/netlink.c b/sysdep/linux/netlink/netlink.c
index 17c369ea..c8eed0f3 100644
--- a/sysdep/linux/netlink/netlink.c
+++ b/sysdep/linux/netlink/netlink.c
@@ -612,7 +612,7 @@ nh_bufsize(struct mpnh *nh)
}
static int
-nl_send_route(struct krt_proto *p, rte *e, int new)
+nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int new)
{
eattr *ea;
net *net = e->net;
@@ -639,10 +639,18 @@ nl_send_route(struct krt_proto *p, rte *e, int new)
r.r.rtm_scope = RT_SCOPE_UNIVERSE;
nl_add_attr_ipa(&r.h, sizeof(r), RTA_DST, net->n.prefix);
- if (ea = ea_find(a->eattrs, EA_KRT_PREFSRC))
+ u32 metric = 0;
+ if (new && e->attrs->source == RTS_INHERIT)
+ metric = e->u.krt.metric;
+ if (ea = ea_find(eattrs, EA_KRT_METRIC))
+ metric = ea->u.data;
+ if (metric != 0)
+ nl_add_attr_u32(&r.h, sizeof(r), RTA_PRIORITY, metric);
+
+ if (ea = ea_find(eattrs, EA_KRT_PREFSRC))
nl_add_attr_ipa(&r.h, sizeof(r), RTA_PREFSRC, *(ip_addr *)ea->u.ptr->data);
- if (ea = ea_find(a->eattrs, EA_KRT_REALM))
+ if (ea = ea_find(eattrs, EA_KRT_REALM))
nl_add_attr_u32(&r.h, sizeof(r), RTA_FLOW, ea->u.data);
switch (a->dest)
@@ -683,15 +691,22 @@ nl_send_route(struct krt_proto *p, rte *e, int new)
}
void
-krt_set_notify(struct krt_proto *p, net *n, rte *new, rte *old)
+krt_set_notify(struct krt_proto *p, net *n, rte *new, rte *old, struct ea_list *eattrs)
{
int err = 0;
+ /*
+ * NULL for eattr of the old route is a little hack, but we don't
+ * get proper eattrs for old in rt_notify() anyway. NULL means no
+ * extended route attributes and therefore matches if the kernel
+ * route has any of them.
+ */
+
if (old)
- nl_send_route(p, old, 0);
+ nl_send_route(p, old, NULL, 0);
if (new)
- err = nl_send_route(p, new, 1);
+ err = nl_send_route(p, new, eattrs, 1);
if (err < 0)
n->n.flags |= KRF_SYNC_ERROR;
@@ -886,7 +901,7 @@ nl_parse_route(struct nlmsghdr *h, int scan)
e->u.krt.type = i->rtm_type;
if (a[RTA_PRIORITY])
- memcpy(&e->u.krt.metric, RTA_DATA(a[RTA_PRIORITY]), sizeof(e->u.krt.metric));
+ memcpy(&e->u.krt.metric, RTA_DATA(a[RTA_PRIORITY]), sizeof(e->u.krt.metric));
else
e->u.krt.metric = 0;
diff --git a/sysdep/unix/krt.Y b/sysdep/unix/krt.Y
index 18e1e52d..c0141f57 100644
--- a/sysdep/unix/krt.Y
+++ b/sysdep/unix/krt.Y
@@ -17,7 +17,7 @@ CF_DEFINES
CF_DECLS
-CF_KEYWORDS(KERNEL, PERSIST, SCAN, TIME, LEARN, DEVICE, ROUTES)
+CF_KEYWORDS(KERNEL, PERSIST, SCAN, TIME, LEARN, DEVICE, ROUTES, KRT_SOURCE, KRT_METRIC)
CF_GRAMMAR
@@ -90,6 +90,9 @@ kif_item:
}
;
+CF_ADDTO(dynamic_attr, KRT_SOURCE { $$ = f_new_dynamic_attr(EAF_TYPE_INT | EAF_TEMP, T_INT, EA_KRT_SOURCE); })
+CF_ADDTO(dynamic_attr, KRT_METRIC { $$ = f_new_dynamic_attr(EAF_TYPE_INT | EAF_TEMP, T_INT, EA_KRT_METRIC); })
+
CF_CODE
CF_END
diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c
index 0fb8c4f9..9ef33c74 100644
--- a/sysdep/unix/krt.c
+++ b/sysdep/unix/krt.c
@@ -46,6 +46,7 @@
#include "nest/iface.h"
#include "nest/route.h"
#include "nest/protocol.h"
+#include "filter/filter.h"
#include "lib/timer.h"
#include "conf/conf.h"
#include "lib/string.h"
@@ -53,18 +54,18 @@
#include "unix.h"
#include "krt.h"
-static int krt_uptodate(rte *k, rte *e);
-
/*
* Global resources
*/
pool *krt_pool;
+static linpool *krt_filter_lp;
void
krt_io_init(void)
{
krt_pool = rp_new(&root_pool, "Kernel Syncer");
+ krt_filter_lp = lp_new(krt_pool, 4080);
krt_if_io_init();
}
@@ -278,12 +279,30 @@ krt_trace_in_rl(struct rate_limit *rl, struct krt_proto *p, rte *e, char *msg)
static struct rate_limit rl_alien_seen, rl_alien_updated, rl_alien_created, rl_alien_ignored;
+/*
+ * krt_same_key() specifies what (aside from the net) is the key in
+ * kernel routing tables. It should be OS-dependent, this is for
+ * Linux. It is important for asynchronous alien updates, because a
+ * positive update is implicitly a negative one for any old route with
+ * the same key.
+ */
+
static inline int
krt_same_key(rte *a, rte *b)
{
- return a->u.krt.proto == b->u.krt.proto &&
- a->u.krt.metric == b->u.krt.metric &&
- a->u.krt.type == b->u.krt.type;
+ return a->u.krt.metric == b->u.krt.metric;
+}
+
+static inline int
+krt_uptodate(rte *a, rte *b)
+{
+ if (a->attrs != b->attrs)
+ return 0;
+
+ if (a->u.krt.proto != b->u.krt.proto)
+ return 0;
+
+ return 1;
}
static void
@@ -308,6 +327,7 @@ krt_learn_announce_delete(struct krt_proto *p, net *n)
rte_update(p->p.table, n, &p->p, &p->p, NULL);
}
+/* Called when alien route is discovered during scan */
static void
krt_learn_scan(struct krt_proto *p, rte *e)
{
@@ -315,7 +335,7 @@ krt_learn_scan(struct krt_proto *p, rte *e)
net *n = net_get(&p->krt_table, n0->n.prefix, n0->n.pxlen);
rte *m, **mm;
- e->attrs->source = RTS_INHERIT;
+ e->attrs = rta_lookup(e->attrs);
for(mm=&n->routes; m = *mm; mm=&m->next)
if (krt_same_key(m, e))
@@ -340,7 +360,6 @@ krt_learn_scan(struct krt_proto *p, rte *e)
krt_trace_in_rl(&rl_alien_created, p, e, "[alien] created");
if (!m)
{
- e->attrs = rta_lookup(e->attrs);
e->next = n->routes;
n->routes = e;
e->u.krt.seen = 1;
@@ -416,7 +435,7 @@ krt_learn_async(struct krt_proto *p, rte *e, int new)
net *n = net_get(&p->krt_table, n0->n.prefix, n0->n.pxlen);
rte *g, **gg, *best, **bestp, *old_best;
- e->attrs->source = RTS_INHERIT;
+ e->attrs = rta_lookup(e->attrs);
old_best = n->routes;
for(gg=&n->routes; g = *gg; gg = &g->next)
@@ -438,7 +457,7 @@ krt_learn_async(struct krt_proto *p, rte *e, int new)
}
else
krt_trace_in(p, e, "[alien async] created");
- e->attrs = rta_lookup(e->attrs);
+
e->next = n->routes;
n->routes = e;
}
@@ -538,7 +557,8 @@ krt_flush_routes(struct krt_proto *p)
if ((n->n.flags & KRF_INSTALLED) &&
a->source != RTS_DEVICE && a->source != RTS_INHERIT)
{
- krt_set_notify(p, e->net, NULL, e);
+ /* FIXME: this does not work if gw is changed in export filter */
+ krt_set_notify(p, e->net, NULL, e, NULL);
n->n.flags &= ~KRF_INSTALLED;
}
}
@@ -547,7 +567,7 @@ krt_flush_routes(struct krt_proto *p)
}
static int
-krt_uptodate(rte *k, rte *e)
+krt_same_dest(rte *k, rte *e)
{
rta *ka = k->attrs, *ea = e->attrs;
@@ -559,6 +579,8 @@ krt_uptodate(rte *k, rte *e)
return ipa_equal(ka->gw, ea->gw);
case RTD_DEVICE:
return !strcmp(ka->iface->name, ea->iface->name);
+ case RTD_MULTIPATH:
+ return mpnh_same(ka->nexthops, ea->nexthops);
default:
return 1;
}
@@ -611,10 +633,12 @@ krt_got_route(struct krt_proto *p, rte *e)
old = net->routes;
if ((net->n.flags & KRF_INSTALLED) && old)
{
- if (krt_uptodate(e, old))
- verdict = KRF_SEEN;
- else
+ /* There may be changes in route attributes, we ignore that.
+ Also, this does not work well if gw is changed in export filter */
+ if ((net->n.flags & KRF_SYNC_ERROR) || ! krt_same_dest(e, old))
verdict = KRF_UPDATE;
+ else
+ verdict = KRF_SEEN;
}
else
verdict = KRF_DELETE;
@@ -624,7 +648,7 @@ krt_got_route(struct krt_proto *p, rte *e)
net->n.flags = (net->n.flags & ~KRF_VERDICT_MASK) | verdict;
if (verdict == KRF_UPDATE || verdict == KRF_DELETE)
{
- /* Get a cached copy of attributes and link the route */
+ /* Get a cached copy of attributes and temporarily link the route */
rta *a = e->attrs;
a->source = RTS_DUMMY;
e->attrs = rta_lookup(a);
@@ -635,6 +659,25 @@ krt_got_route(struct krt_proto *p, rte *e)
rte_free(e);
}
+static inline int
+krt_export_rte(struct krt_proto *p, rte **new, ea_list **tmpa)
+{
+ struct filter *filter = p->p.out_filter;
+
+ if (! *new)
+ return 0;
+
+ if (filter == FILTER_REJECT)
+ return 0;
+
+ if (filter == FILTER_ACCEPT)
+ return 1;
+
+ struct proto *src = (*new)->attrs->proto;
+ *tmpa = src->make_tmp_attrs ? src->make_tmp_attrs(*new, krt_filter_lp) : NULL;
+ return f_run(filter, new, tmpa, krt_filter_lp, FF_FORCE_TMPATTR) <= F_ACCEPT;
+}
+
static void
krt_prune(struct krt_proto *p)
{
@@ -645,16 +688,28 @@ krt_prune(struct krt_proto *p)
{
net *n = (net *) f;
int verdict = f->flags & KRF_VERDICT_MASK;
- rte *new, *old;
+ rte *new, *new0, *old;
+ ea_list *tmpa = NULL;
- if (verdict != KRF_CREATE && verdict != KRF_SEEN && verdict != KRF_IGNORE)
+ if (verdict == KRF_UPDATE || verdict == KRF_DELETE)
{
+ /* Get a dummy route from krt_got_route() */
old = n->routes;
n->routes = old->next;
}
else
old = NULL;
- new = n->routes;
+
+ new = new0 = n->routes;
+ if (verdict == KRF_CREATE || verdict == KRF_UPDATE)
+ {
+ /* We have to run export filter to get proper 'new' route */
+ if (! krt_export_rte(p, &new, &tmpa))
+ {
+ /* Route rejected, should not happen (KRF_INSTALLED) but to be sure .. */
+ verdict = (verdict == KRF_CREATE) ? KRF_IGNORE : KRF_DELETE;
+ }
+ }
switch (verdict)
{
@@ -662,7 +717,7 @@ krt_prune(struct krt_proto *p)
if (new && (f->flags & KRF_INSTALLED))
{
krt_trace_in(p, new, "reinstalling");
- krt_set_notify(p, n, new, NULL);
+ krt_set_notify(p, n, new, NULL, tmpa);
}
break;
case KRF_SEEN:
@@ -671,17 +726,21 @@ krt_prune(struct krt_proto *p)
break;
case KRF_UPDATE:
krt_trace_in(p, new, "updating");
- krt_set_notify(p, n, new, old);
+ krt_set_notify(p, n, new, old, tmpa);
break;
case KRF_DELETE:
krt_trace_in(p, old, "deleting");
- krt_set_notify(p, n, NULL, old);
+ krt_set_notify(p, n, NULL, old, NULL);
break;
default:
bug("krt_prune: invalid route status");
}
+
if (old)
rte_free(old);
+ if (new != new0)
+ rte_free(new);
+ lp_flush(krt_filter_lp);
f->flags &= ~KRF_VERDICT_MASK;
}
FIB_WALK_END;
@@ -707,7 +766,7 @@ krt_got_route_async(struct krt_proto *p, rte *e, int new)
if (new)
{
krt_trace_in(p, e, "[redirect] deleting");
- krt_set_notify(p, net, NULL, e);
+ krt_set_notify(p, net, NULL, e, NULL);
}
/* If !new, it is probably echo of our deletion */
break;
@@ -781,7 +840,7 @@ krt_import_control(struct proto *P, rte **new, ea_list **attrs, struct linpool *
static void
krt_notify(struct proto *P, struct rtable *table UNUSED, net *net,
- rte *new, rte *old, struct ea_list *attrs UNUSED)
+ rte *new, rte *old, struct ea_list *eattrs)
{
struct krt_proto *p = (struct krt_proto *) P;
@@ -793,8 +852,8 @@ krt_notify(struct proto *P, struct rtable *table UNUSED, net *net,
net->n.flags |= KRF_INSTALLED;
else
net->n.flags &= ~KRF_INSTALLED;
- if (p->initialized) /* Before first scan we don't touch the routes */
- krt_set_notify(p, net, new, old);
+ if (p->initialized) /* Before first scan we don't touch the routes */
+ krt_set_notify(p, net, new, old, eattrs);
}
/*
@@ -907,14 +966,53 @@ krt_shutdown(struct proto *P)
return PS_DOWN;
}
+static struct ea_list *
+krt_make_tmp_attrs(rte *rt, struct linpool *pool)
+{
+ struct ea_list *l = lp_alloc(pool, sizeof(struct ea_list) + 2 * sizeof(eattr));
+
+ l->next = NULL;
+ l->flags = EALF_SORTED;
+ l->count = 2;
+
+ l->attrs[0].id = EA_KRT_SOURCE;
+ l->attrs[0].flags = 0;
+ l->attrs[0].type = EAF_TYPE_INT | EAF_TEMP;
+ l->attrs[0].u.data = rt->u.krt.proto;
+
+ l->attrs[1].id = EA_KRT_METRIC;
+ l->attrs[1].flags = 0;
+ l->attrs[1].type = EAF_TYPE_INT | EAF_TEMP;
+ l->attrs[1].u.data = rt->u.krt.metric;
+
+ return l;
+}
+
+static void
+krt_store_tmp_attrs(rte *rt, struct ea_list *attrs)
+{
+ /* EA_KRT_SOURCE is read-only */
+ rt->u.krt.metric = ea_get_int(attrs, EA_KRT_METRIC, 0);
+}
+
+static int
+krt_rte_same(rte *a, rte *b)
+{
+ /* src is always KRT_SRC_ALIEN and type is irrelevant */
+ return (a->u.krt.proto == b->u.krt.proto) && (a->u.krt.metric == b->u.krt.metric);
+}
+
static struct proto *
krt_init(struct proto_config *c)
{
struct krt_proto *p = proto_new(c, sizeof(struct krt_proto));
p->p.accept_ra_types = RA_OPTIMAL;
+ p->p.make_tmp_attrs = krt_make_tmp_attrs;
+ p->p.store_tmp_attrs = krt_store_tmp_attrs;
p->p.import_control = krt_import_control;
p->p.rt_notify = krt_notify;
+ p->p.rte_same = krt_rte_same;
return &p->p;
}
@@ -952,12 +1050,22 @@ krt_get_attr(eattr * a, byte * buf, int buflen UNUSED)
{
switch (a->id)
{
+ case EA_KRT_SOURCE:
+ bsprintf(buf, "source");
+ return GA_NAME;
+
+ case EA_KRT_METRIC:
+ bsprintf(buf, "metric");
+ return GA_NAME;
+
case EA_KRT_PREFSRC:
bsprintf(buf, "prefsrc");
return GA_NAME;
+
case EA_KRT_REALM:
bsprintf(buf, "realm");
return GA_NAME;
+
default:
return GA_UNKNOWN;
}
diff --git a/sysdep/unix/krt.h b/sysdep/unix/krt.h
index b0c4dc5e..19b69e49 100644
--- a/sysdep/unix/krt.h
+++ b/sysdep/unix/krt.h
@@ -28,8 +28,10 @@ struct kif_proto;
#define KRF_DELETE 3 /* Should be deleted */
#define KRF_IGNORE 4 /* To be ignored */
-#define EA_KRT_PREFSRC EA_CODE(EAP_KRT, 0)
-#define EA_KRT_REALM EA_CODE(EAP_KRT, 1)
+#define EA_KRT_SOURCE EA_CODE(EAP_KRT, 0)
+#define EA_KRT_METRIC EA_CODE(EAP_KRT, 1)
+#define EA_KRT_PREFSRC EA_CODE(EAP_KRT, 2)
+#define EA_KRT_REALM EA_CODE(EAP_KRT, 3)
/* Whenever we recognize our own routes, we allow learing of foreign routes */
@@ -130,7 +132,7 @@ void krt_set_start(struct krt_proto *, int);
void krt_set_shutdown(struct krt_proto *, int);
int krt_capable(rte *e);
-void krt_set_notify(struct krt_proto *x, net *net, rte *new, rte *old);
+void krt_set_notify(struct krt_proto *p, net *n, rte *new, rte *old, struct ea_list *eattrs);
/* krt-iface.c */
diff --git a/sysdep/unix/main.c b/sysdep/unix/main.c
index a4e80154..9219da9b 100644
--- a/sysdep/unix/main.c
+++ b/sysdep/unix/main.c
@@ -191,7 +191,7 @@ sysdep_preconfig(struct config *c)
init_list(&c->logfiles);
#ifdef PATH_IPROUTE_DIR
- // read_iproute_table(PATH_IPROUTE_DIR "/rt_protos", "ipp_", 256);
+ read_iproute_table(PATH_IPROUTE_DIR "/rt_protos", "ipp_", 256);
read_iproute_table(PATH_IPROUTE_DIR "/rt_realms", "ipr_", 256);
read_iproute_table(PATH_IPROUTE_DIR "/rt_scopes", "ips_", 256);
read_iproute_table(PATH_IPROUTE_DIR "/rt_tables", "ipt_", 256);