Fixes several minor bugs in kernel syncer.

This commit is contained in:
Ondrej Zajicek 2012-03-25 19:44:14 +02:00
parent 9ba2798c65
commit c9df01d321
5 changed files with 117 additions and 38 deletions

View file

@ -1618,7 +1618,7 @@ kernel table.
<p>Because the kernel protocol is partially integrated with the <p>Because the kernel protocol is partially integrated with the
connected routing table, there are two limitations - it is not connected routing table, there are two limitations - it is not
possible to connect more kernel protocols to the same routing table 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 filter of a kernel protocol does not work. Both limitations can be
overcome using another routing table and the pipe protocol. overcome using another routing table and the pipe protocol.

View file

@ -189,7 +189,8 @@ krt_sock_send(int cmd, rte *e)
} }
void 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; int err = 0;

View file

@ -612,7 +612,7 @@ nh_bufsize(struct mpnh *nh)
} }
static int 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; eattr *ea;
net *net = e->net; net *net = e->net;
@ -639,13 +639,18 @@ nl_send_route(struct krt_proto *p, rte *e, int new)
r.r.rtm_scope = RT_SCOPE_UNIVERSE; r.r.rtm_scope = RT_SCOPE_UNIVERSE;
nl_add_attr_ipa(&r.h, sizeof(r), RTA_DST, net->n.prefix); nl_add_attr_ipa(&r.h, sizeof(r), RTA_DST, net->n.prefix);
if (ea = ea_find(a->eattrs, EA_KRT_METRIC)) u32 metric = 0;
nl_add_attr_u32(&r.h, sizeof(r), RTA_PRIORITY, ea->u.data); 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(a->eattrs, EA_KRT_PREFSRC)) if (ea = ea_find(eattrs, EA_KRT_PREFSRC))
nl_add_attr_ipa(&r.h, sizeof(r), RTA_PREFSRC, *(ip_addr *)ea->u.ptr->data); 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); nl_add_attr_u32(&r.h, sizeof(r), RTA_FLOW, ea->u.data);
switch (a->dest) switch (a->dest)
@ -686,15 +691,22 @@ nl_send_route(struct krt_proto *p, rte *e, int new)
} }
void 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; 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) if (old)
nl_send_route(p, old, 0); nl_send_route(p, old, NULL, 0);
if (new) if (new)
err = nl_send_route(p, new, 1); err = nl_send_route(p, new, eattrs, 1);
if (err < 0) if (err < 0)
n->n.flags |= KRF_SYNC_ERROR; n->n.flags |= KRF_SYNC_ERROR;

View file

@ -46,6 +46,7 @@
#include "nest/iface.h" #include "nest/iface.h"
#include "nest/route.h" #include "nest/route.h"
#include "nest/protocol.h" #include "nest/protocol.h"
#include "filter/filter.h"
#include "lib/timer.h" #include "lib/timer.h"
#include "conf/conf.h" #include "conf/conf.h"
#include "lib/string.h" #include "lib/string.h"
@ -53,18 +54,18 @@
#include "unix.h" #include "unix.h"
#include "krt.h" #include "krt.h"
static int krt_uptodate(rte *k, rte *e);
/* /*
* Global resources * Global resources
*/ */
pool *krt_pool; pool *krt_pool;
static linpool *krt_filter_lp;
void void
krt_io_init(void) krt_io_init(void)
{ {
krt_pool = rp_new(&root_pool, "Kernel Syncer"); krt_pool = rp_new(&root_pool, "Kernel Syncer");
krt_filter_lp = lp_new(krt_pool, 4080);
krt_if_io_init(); 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; 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 static inline int
krt_same_key(rte *a, rte *b) krt_same_key(rte *a, rte *b)
{ {
return a->u.krt.proto == b->u.krt.proto && return a->u.krt.metric == b->u.krt.metric;
a->u.krt.metric == b->u.krt.metric && }
a->u.krt.type == b->u.krt.type;
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 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); rte_update(p->p.table, n, &p->p, &p->p, NULL);
} }
/* Called when alien route is discovered during scan */
static void static void
krt_learn_scan(struct krt_proto *p, rte *e) 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); net *n = net_get(&p->krt_table, n0->n.prefix, n0->n.pxlen);
rte *m, **mm; rte *m, **mm;
e->attrs->source = RTS_INHERIT; e->attrs = rta_lookup(e->attrs);
for(mm=&n->routes; m = *mm; mm=&m->next) for(mm=&n->routes; m = *mm; mm=&m->next)
if (krt_same_key(m, e)) 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"); krt_trace_in_rl(&rl_alien_created, p, e, "[alien] created");
if (!m) if (!m)
{ {
e->attrs = rta_lookup(e->attrs);
e->next = n->routes; e->next = n->routes;
n->routes = e; n->routes = e;
e->u.krt.seen = 1; 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); net *n = net_get(&p->krt_table, n0->n.prefix, n0->n.pxlen);
rte *g, **gg, *best, **bestp, *old_best; rte *g, **gg, *best, **bestp, *old_best;
e->attrs->source = RTS_INHERIT; e->attrs = rta_lookup(e->attrs);
old_best = n->routes; old_best = n->routes;
for(gg=&n->routes; g = *gg; gg = &g->next) 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 else
krt_trace_in(p, e, "[alien async] created"); krt_trace_in(p, e, "[alien async] created");
e->attrs = rta_lookup(e->attrs);
e->next = n->routes; e->next = n->routes;
n->routes = e; n->routes = e;
} }
@ -538,7 +557,8 @@ krt_flush_routes(struct krt_proto *p)
if ((n->n.flags & KRF_INSTALLED) && if ((n->n.flags & KRF_INSTALLED) &&
a->source != RTS_DEVICE && a->source != RTS_INHERIT) 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; n->n.flags &= ~KRF_INSTALLED;
} }
} }
@ -547,7 +567,7 @@ krt_flush_routes(struct krt_proto *p)
} }
static int static int
krt_uptodate(rte *k, rte *e) krt_same_dest(rte *k, rte *e)
{ {
rta *ka = k->attrs, *ea = e->attrs; rta *ka = k->attrs, *ea = e->attrs;
@ -559,6 +579,8 @@ krt_uptodate(rte *k, rte *e)
return ipa_equal(ka->gw, ea->gw); return ipa_equal(ka->gw, ea->gw);
case RTD_DEVICE: case RTD_DEVICE:
return !strcmp(ka->iface->name, ea->iface->name); return !strcmp(ka->iface->name, ea->iface->name);
case RTD_MULTIPATH:
return mpnh_same(ka->nexthops, ea->nexthops);
default: default:
return 1; return 1;
} }
@ -611,10 +633,12 @@ krt_got_route(struct krt_proto *p, rte *e)
old = net->routes; old = net->routes;
if ((net->n.flags & KRF_INSTALLED) && old) if ((net->n.flags & KRF_INSTALLED) && old)
{ {
if (krt_uptodate(e, old)) /* There may be changes in route attributes, we ignore that.
verdict = KRF_SEEN; Also, this does not work well if gw is changed in export filter */
else if ((net->n.flags & KRF_SYNC_ERROR) || ! krt_same_dest(e, old))
verdict = KRF_UPDATE; verdict = KRF_UPDATE;
else
verdict = KRF_SEEN;
} }
else else
verdict = KRF_DELETE; 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; net->n.flags = (net->n.flags & ~KRF_VERDICT_MASK) | verdict;
if (verdict == KRF_UPDATE || verdict == KRF_DELETE) 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; rta *a = e->attrs;
a->source = RTS_DUMMY; a->source = RTS_DUMMY;
e->attrs = rta_lookup(a); e->attrs = rta_lookup(a);
@ -635,6 +659,25 @@ krt_got_route(struct krt_proto *p, rte *e)
rte_free(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 static void
krt_prune(struct krt_proto *p) krt_prune(struct krt_proto *p)
{ {
@ -645,16 +688,28 @@ krt_prune(struct krt_proto *p)
{ {
net *n = (net *) f; net *n = (net *) f;
int verdict = f->flags & KRF_VERDICT_MASK; 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; old = n->routes;
n->routes = old->next; n->routes = old->next;
} }
else else
old = NULL; 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) switch (verdict)
{ {
@ -662,7 +717,7 @@ krt_prune(struct krt_proto *p)
if (new && (f->flags & KRF_INSTALLED)) if (new && (f->flags & KRF_INSTALLED))
{ {
krt_trace_in(p, new, "reinstalling"); krt_trace_in(p, new, "reinstalling");
krt_set_notify(p, n, new, NULL); krt_set_notify(p, n, new, NULL, tmpa);
} }
break; break;
case KRF_SEEN: case KRF_SEEN:
@ -671,17 +726,21 @@ krt_prune(struct krt_proto *p)
break; break;
case KRF_UPDATE: case KRF_UPDATE:
krt_trace_in(p, new, "updating"); krt_trace_in(p, new, "updating");
krt_set_notify(p, n, new, old); krt_set_notify(p, n, new, old, tmpa);
break; break;
case KRF_DELETE: case KRF_DELETE:
krt_trace_in(p, old, "deleting"); krt_trace_in(p, old, "deleting");
krt_set_notify(p, n, NULL, old); krt_set_notify(p, n, NULL, old, NULL);
break; break;
default: default:
bug("krt_prune: invalid route status"); bug("krt_prune: invalid route status");
} }
if (old) if (old)
rte_free(old); rte_free(old);
if (new != new0)
rte_free(new);
lp_flush(krt_filter_lp);
f->flags &= ~KRF_VERDICT_MASK; f->flags &= ~KRF_VERDICT_MASK;
} }
FIB_WALK_END; FIB_WALK_END;
@ -707,7 +766,7 @@ krt_got_route_async(struct krt_proto *p, rte *e, int new)
if (new) if (new)
{ {
krt_trace_in(p, e, "[redirect] deleting"); 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 */ /* If !new, it is probably echo of our deletion */
break; break;
@ -781,7 +840,7 @@ krt_import_control(struct proto *P, rte **new, ea_list **attrs, struct linpool *
static void static void
krt_notify(struct proto *P, struct rtable *table UNUSED, net *net, 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; struct krt_proto *p = (struct krt_proto *) P;
@ -794,7 +853,7 @@ krt_notify(struct proto *P, struct rtable *table UNUSED, net *net,
else else
net->n.flags &= ~KRF_INSTALLED; net->n.flags &= ~KRF_INSTALLED;
if (p->initialized) /* Before first scan we don't touch the routes */ if (p->initialized) /* Before first scan we don't touch the routes */
krt_set_notify(p, net, new, old); krt_set_notify(p, net, new, old, eattrs);
} }
/* /*
@ -908,7 +967,7 @@ krt_shutdown(struct proto *P)
} }
static struct ea_list * static struct ea_list *
krt_make_tmp_attrs(struct rte *rt, struct linpool *pool) krt_make_tmp_attrs(rte *rt, struct linpool *pool)
{ {
struct ea_list *l = lp_alloc(pool, sizeof(struct ea_list) + 2 * sizeof(eattr)); struct ea_list *l = lp_alloc(pool, sizeof(struct ea_list) + 2 * sizeof(eattr));
@ -930,12 +989,18 @@ krt_make_tmp_attrs(struct rte *rt, struct linpool *pool)
} }
static void static void
krt_store_tmp_attrs(struct rte *rt, struct ea_list *attrs) krt_store_tmp_attrs(rte *rt, struct ea_list *attrs)
{ {
/* EA_KRT_SOURCE is read-only */ /* EA_KRT_SOURCE is read-only */
rt->u.krt.metric = ea_get_int(attrs, EA_KRT_METRIC, 0); 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 * static struct proto *
krt_init(struct proto_config *c) krt_init(struct proto_config *c)
@ -947,6 +1012,7 @@ krt_init(struct proto_config *c)
p->p.store_tmp_attrs = krt_store_tmp_attrs; p->p.store_tmp_attrs = krt_store_tmp_attrs;
p->p.import_control = krt_import_control; p->p.import_control = krt_import_control;
p->p.rt_notify = krt_notify; p->p.rt_notify = krt_notify;
p->p.rte_same = krt_rte_same;
return &p->p; return &p->p;
} }

View file

@ -132,7 +132,7 @@ void krt_set_start(struct krt_proto *, int);
void krt_set_shutdown(struct krt_proto *, int); void krt_set_shutdown(struct krt_proto *, int);
int krt_capable(rte *e); 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 */ /* krt-iface.c */