KRT: Improve syncer code to avoid using temporary data in rtable
The old code stored route verdicts and temporary routes directly in rtable. The new code do not store received routes (it immediately compares them with exported routes and resolves conflicts) and uses internal bitmap to keep track of which routes were received and which needs to be reinstalled. By not putting 'invalid' temporary routes to rtable, we keep rtable in consistent state, therefore scan no longer needs to be atomic operation and could be splitted to multiple events.
This commit is contained in:
parent
ef8c45749c
commit
7d767c5a3d
5 changed files with 77 additions and 112 deletions
|
@ -37,7 +37,6 @@ struct cli;
|
||||||
struct fib_node {
|
struct fib_node {
|
||||||
struct fib_node *next; /* Next in hash chain */
|
struct fib_node *next; /* Next in hash chain */
|
||||||
struct fib_iterator *readers; /* List of readers of this node */
|
struct fib_iterator *readers; /* List of readers of this node */
|
||||||
byte flags; /* User-defined, will be removed */
|
|
||||||
net_addr addr[0];
|
net_addr addr[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -327,7 +327,6 @@ fib_get(struct fib *f, const net_addr *a)
|
||||||
|
|
||||||
struct fib_node *e = fib_user_to_node(f, b);
|
struct fib_node *e = fib_user_to_node(f, b);
|
||||||
e->readers = NULL;
|
e->readers = NULL;
|
||||||
e->flags = 0;
|
|
||||||
fib_insert(f, a, e);
|
fib_insert(f, a, e);
|
||||||
|
|
||||||
memset(b, 0, f->node_offset);
|
memset(b, 0, f->node_offset);
|
||||||
|
|
|
@ -1687,7 +1687,7 @@ rte_dump(rte *e)
|
||||||
{
|
{
|
||||||
net *n = e->net;
|
net *n = e->net;
|
||||||
debug("%-1N ", n->n.addr);
|
debug("%-1N ", n->n.addr);
|
||||||
debug("KF=%02x PF=%02x pref=%d ", n->n.flags, e->pflags, e->pref);
|
debug("PF=%02x pref=%d ", e->pflags, e->pref);
|
||||||
rta_dump(e->attrs);
|
rta_dump(e->attrs);
|
||||||
if (e->attrs->src->proto->proto->dump_attrs)
|
if (e->attrs->src->proto->proto->dump_attrs)
|
||||||
e->attrs->src->proto->proto->dump_attrs(e);
|
e->attrs->src->proto->proto->dump_attrs(e);
|
||||||
|
|
|
@ -625,19 +625,17 @@ krt_same_dest(rte *k, rte *e)
|
||||||
void
|
void
|
||||||
krt_got_route(struct krt_proto *p, rte *e)
|
krt_got_route(struct krt_proto *p, rte *e)
|
||||||
{
|
{
|
||||||
net *net = e->net;
|
rte *new = NULL, *rt_free = NULL;
|
||||||
int verdict;
|
net *n = e->net;
|
||||||
|
|
||||||
#ifdef KRT_ALLOW_LEARN
|
#ifdef KRT_ALLOW_LEARN
|
||||||
switch (e->u.krt.src)
|
switch (e->u.krt.src)
|
||||||
{
|
{
|
||||||
case KRT_SRC_KERNEL:
|
case KRT_SRC_KERNEL:
|
||||||
verdict = KRF_IGNORE;
|
goto ignore;
|
||||||
goto sentenced;
|
|
||||||
|
|
||||||
case KRT_SRC_REDIRECT:
|
case KRT_SRC_REDIRECT:
|
||||||
verdict = KRF_DELETE;
|
goto delete;
|
||||||
goto sentenced;
|
|
||||||
|
|
||||||
case KRT_SRC_ALIEN:
|
case KRT_SRC_ALIEN:
|
||||||
if (KRT_CF->learn)
|
if (KRT_CF->learn)
|
||||||
|
@ -652,58 +650,68 @@ krt_got_route(struct krt_proto *p, rte *e)
|
||||||
#endif
|
#endif
|
||||||
/* The rest is for KRT_SRC_BIRD (or KRT_SRC_UNKNOWN) */
|
/* The rest is for KRT_SRC_BIRD (or KRT_SRC_UNKNOWN) */
|
||||||
|
|
||||||
if (net->n.flags & KRF_VERDICT_MASK)
|
|
||||||
{
|
|
||||||
/* Route to this destination was already seen. Strange, but it happens... */
|
|
||||||
krt_trace_in(p, e, "already seen");
|
|
||||||
rte_free(e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/* We wait for the initial feed to have correct installed state */
|
||||||
if (!p->ready)
|
if (!p->ready)
|
||||||
{
|
goto ignore;
|
||||||
/* We wait for the initial feed to have correct installed state */
|
|
||||||
verdict = KRF_IGNORE;
|
|
||||||
goto sentenced;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (krt_is_installed(p, net))
|
if (!krt_is_installed(p, n))
|
||||||
{
|
goto delete;
|
||||||
rte *new, *rt_free;
|
|
||||||
|
|
||||||
new = krt_export_net(p, net, &rt_free);
|
new = krt_export_net(p, n, &rt_free);
|
||||||
|
|
||||||
/* TODO: There also may be changes in route eattrs, we ignore that for now. */
|
/* Rejected by filters */
|
||||||
|
if (!new)
|
||||||
|
goto delete;
|
||||||
|
|
||||||
if (!new)
|
/* Route to this destination was already seen. Strange, but it happens... */
|
||||||
verdict = KRF_DELETE;
|
if (bmap_test(&p->seen_map, new->id))
|
||||||
else if (!bmap_test(&p->sync_map, new->id) || !krt_same_dest(e, new))
|
goto aseen;
|
||||||
verdict = KRF_UPDATE;
|
|
||||||
else
|
|
||||||
verdict = KRF_SEEN;
|
|
||||||
|
|
||||||
if (rt_free)
|
/* Mark route as seen */
|
||||||
rte_free(rt_free);
|
bmap_set(&p->seen_map, new->id);
|
||||||
|
|
||||||
lp_flush(krt_filter_lp);
|
/* TODO: There also may be changes in route eattrs, we ignore that for now. */
|
||||||
}
|
if (!bmap_test(&p->sync_map, new->id) || !krt_same_dest(e, new))
|
||||||
else
|
goto update;
|
||||||
verdict = KRF_DELETE;
|
|
||||||
|
|
||||||
sentenced:
|
goto seen;
|
||||||
krt_trace_in(p, e, ((char *[]) { "?", "seen", "will be updated", "will be removed", "ignored" }) [verdict]);
|
|
||||||
net->n.flags = (net->n.flags & ~KRF_VERDICT_MASK) | verdict;
|
seen:
|
||||||
if (verdict == KRF_UPDATE || verdict == KRF_DELETE)
|
krt_trace_in(p, e, "seen");
|
||||||
{
|
goto done;
|
||||||
/* Get a cached copy of attributes and temporarily link the route */
|
|
||||||
rta *a = e->attrs;
|
aseen:
|
||||||
a->source = RTS_DUMMY;
|
krt_trace_in(p, e, "already seen");
|
||||||
e->attrs = rta_lookup(a);
|
goto done;
|
||||||
e->next = net->routes;
|
|
||||||
net->routes = e;
|
ignore:
|
||||||
}
|
krt_trace_in(p, e, "ignored");
|
||||||
else
|
goto done;
|
||||||
rte_free(e);
|
|
||||||
|
update:
|
||||||
|
krt_trace_in(p, new, "updating");
|
||||||
|
krt_replace_rte(p, n, new, e);
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
delete:
|
||||||
|
krt_trace_in(p, e, "deleting");
|
||||||
|
krt_replace_rte(p, n, NULL, e);
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
done:
|
||||||
|
rte_free(e);
|
||||||
|
|
||||||
|
if (rt_free)
|
||||||
|
rte_free(rt_free);
|
||||||
|
|
||||||
|
lp_flush(krt_filter_lp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
krt_init_scan(struct krt_proto *p)
|
||||||
|
{
|
||||||
|
bmap_reset(&p->seen_map, 1024);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -713,59 +721,24 @@ krt_prune(struct krt_proto *p)
|
||||||
|
|
||||||
KRT_TRACE(p, D_EVENTS, "Pruning table %s", t->name);
|
KRT_TRACE(p, D_EVENTS, "Pruning table %s", t->name);
|
||||||
FIB_WALK(&t->fib, net, n)
|
FIB_WALK(&t->fib, net, n)
|
||||||
|
{
|
||||||
|
if (p->ready && krt_is_installed(p, n) && !bmap_test(&p->seen_map, n->routes->id))
|
||||||
{
|
{
|
||||||
int verdict = n->n.flags & KRF_VERDICT_MASK;
|
rte *rt_free = NULL;
|
||||||
rte *new, *old, *rt_free = NULL;
|
rte *new = krt_export_net(p, n, &rt_free);
|
||||||
|
|
||||||
if (verdict == KRF_UPDATE || verdict == KRF_DELETE)
|
if (new)
|
||||||
{
|
{
|
||||||
/* Get a dummy route from krt_got_route() */
|
krt_trace_in(p, new, "installing");
|
||||||
old = n->routes;
|
krt_replace_rte(p, n, new, NULL);
|
||||||
n->routes = old->next;
|
}
|
||||||
}
|
|
||||||
else
|
|
||||||
old = NULL;
|
|
||||||
|
|
||||||
if (verdict == KRF_CREATE || verdict == KRF_UPDATE)
|
|
||||||
{
|
|
||||||
/* We have to run export filter to get proper 'new' route */
|
|
||||||
new = krt_export_net(p, n, &rt_free);
|
|
||||||
|
|
||||||
if (!new)
|
|
||||||
verdict = (verdict == KRF_CREATE) ? KRF_IGNORE : KRF_DELETE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
new = NULL;
|
|
||||||
|
|
||||||
switch (verdict)
|
|
||||||
{
|
|
||||||
case KRF_CREATE:
|
|
||||||
krt_trace_in(p, new, "reinstalling");
|
|
||||||
krt_replace_rte(p, n, new, NULL);
|
|
||||||
break;
|
|
||||||
case KRF_SEEN:
|
|
||||||
case KRF_IGNORE:
|
|
||||||
/* Nothing happens */
|
|
||||||
break;
|
|
||||||
case KRF_UPDATE:
|
|
||||||
krt_trace_in(p, new, "updating");
|
|
||||||
krt_replace_rte(p, n, new, old);
|
|
||||||
break;
|
|
||||||
case KRF_DELETE:
|
|
||||||
krt_trace_in(p, old, "deleting");
|
|
||||||
krt_replace_rte(p, n, NULL, old);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
bug("krt_prune: invalid route status");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (old)
|
|
||||||
rte_free(old);
|
|
||||||
if (rt_free)
|
if (rt_free)
|
||||||
rte_free(rt_free);
|
rte_free(rt_free);
|
||||||
|
|
||||||
lp_flush(krt_filter_lp);
|
lp_flush(krt_filter_lp);
|
||||||
n->n.flags &= ~KRF_VERDICT_MASK;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
FIB_WALK_END;
|
FIB_WALK_END;
|
||||||
|
|
||||||
#ifdef KRT_ALLOW_LEARN
|
#ifdef KRT_ALLOW_LEARN
|
||||||
|
@ -823,6 +796,7 @@ static void
|
||||||
krt_scan(timer *t UNUSED)
|
krt_scan(timer *t UNUSED)
|
||||||
{
|
{
|
||||||
struct krt_proto *p;
|
struct krt_proto *p;
|
||||||
|
node *n;
|
||||||
|
|
||||||
kif_force_scan();
|
kif_force_scan();
|
||||||
|
|
||||||
|
@ -830,14 +804,13 @@ krt_scan(timer *t UNUSED)
|
||||||
p = SKIP_BACK(struct krt_proto, krt_node, HEAD(krt_proto_list));
|
p = SKIP_BACK(struct krt_proto, krt_node, HEAD(krt_proto_list));
|
||||||
KRT_TRACE(p, D_EVENTS, "Scanning routing table");
|
KRT_TRACE(p, D_EVENTS, "Scanning routing table");
|
||||||
|
|
||||||
|
WALK_LIST2(p, n, krt_proto_list, krt_node)
|
||||||
|
krt_init_scan(p);
|
||||||
|
|
||||||
krt_do_scan(NULL);
|
krt_do_scan(NULL);
|
||||||
|
|
||||||
void *q;
|
WALK_LIST2(p, n, krt_proto_list, krt_node)
|
||||||
WALK_LIST(q, krt_proto_list)
|
|
||||||
{
|
|
||||||
p = SKIP_BACK(struct krt_proto, krt_node, q);
|
|
||||||
krt_prune(p);
|
krt_prune(p);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -879,6 +852,7 @@ krt_scan(timer *t)
|
||||||
kif_force_scan();
|
kif_force_scan();
|
||||||
|
|
||||||
KRT_TRACE(p, D_EVENTS, "Scanning routing table");
|
KRT_TRACE(p, D_EVENTS, "Scanning routing table");
|
||||||
|
krt_init_scan(p);
|
||||||
krt_do_scan(p);
|
krt_do_scan(p);
|
||||||
krt_prune(p);
|
krt_prune(p);
|
||||||
}
|
}
|
||||||
|
@ -1095,6 +1069,7 @@ krt_start(struct proto *P)
|
||||||
}
|
}
|
||||||
|
|
||||||
bmap_init(&p->sync_map, p->p.pool, 1024);
|
bmap_init(&p->sync_map, p->p.pool, 1024);
|
||||||
|
bmap_init(&p->seen_map, p->p.pool, 1024);
|
||||||
add_tail(&krt_proto_list, &p->krt_node);
|
add_tail(&krt_proto_list, &p->krt_node);
|
||||||
|
|
||||||
#ifdef KRT_ALLOW_LEARN
|
#ifdef KRT_ALLOW_LEARN
|
||||||
|
|
|
@ -19,15 +19,6 @@ struct kif_proto;
|
||||||
#include "sysdep/config.h"
|
#include "sysdep/config.h"
|
||||||
#include CONFIG_INCLUDE_KRTSYS_H
|
#include CONFIG_INCLUDE_KRTSYS_H
|
||||||
|
|
||||||
/* Flags stored in net->n.flags, rest are in nest/route.h */
|
|
||||||
|
|
||||||
#define KRF_VERDICT_MASK 0x0f
|
|
||||||
#define KRF_CREATE 0 /* Not seen in kernel table */
|
|
||||||
#define KRF_SEEN 1 /* Seen in kernel table during last scan */
|
|
||||||
#define KRF_UPDATE 2 /* Need to update this entry */
|
|
||||||
#define KRF_DELETE 3 /* Should be deleted */
|
|
||||||
#define KRF_IGNORE 4 /* To be ignored */
|
|
||||||
|
|
||||||
#define KRT_DEFAULT_ECMP_LIMIT 16
|
#define KRT_DEFAULT_ECMP_LIMIT 16
|
||||||
|
|
||||||
#define EA_KRT_SOURCE EA_CODE(PROTOCOL_KERNEL, 0)
|
#define EA_KRT_SOURCE EA_CODE(PROTOCOL_KERNEL, 0)
|
||||||
|
@ -66,6 +57,7 @@ struct krt_proto {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct bmap sync_map; /* Keeps track which exported routes were successfully written to kernel */
|
struct bmap sync_map; /* Keeps track which exported routes were successfully written to kernel */
|
||||||
|
struct bmap seen_map; /* Routes seen during last periodic scan */
|
||||||
node krt_node; /* Node in krt_proto_list */
|
node krt_node; /* Node in krt_proto_list */
|
||||||
byte af; /* Kernel address family (AF_*) */
|
byte af; /* Kernel address family (AF_*) */
|
||||||
byte ready; /* Initial feed has been finished */
|
byte ready; /* Initial feed has been finished */
|
||||||
|
|
Loading…
Reference in a new issue