0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-09-19 20:05:21 +00:00

Secondary and merged exports get a whole feed instead of traversing the table structures directly

This commit is contained in:
Maria Matejka 2021-09-30 13:50:54 +02:00
parent 8f3942a97e
commit 0767a0c288
4 changed files with 178 additions and 107 deletions

View File

@ -335,7 +335,7 @@ static inline net *net_get(rtable *tab, const net_addr *addr) { return (net *) f
void *net_route(rtable *tab, const net_addr *n);
int net_roa_check(rtable *tab, const net_addr *n, u32 asn);
int rt_examine(rtable *t, net_addr *a, struct channel *c, const struct filter *filter);
rte *rt_export_merged(struct channel *c, net *net, linpool *pool, int silent);
rte *rt_export_merged_show(struct channel *c, net *n, linpool *pool);
void rt_refresh_begin(rtable *t, struct channel *c);
void rt_refresh_end(rtable *t, struct channel *c);
void rt_modify_stale(rtable *t, struct channel *c);

View File

@ -143,7 +143,7 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
{
/* Special case for merged export */
pass = 1;
rte *em = rt_export_merged(ec, n, c->show_pool, 1);
rte *em = rt_export_merged_show(ec, n, c->show_pool);
if (em)
e = *em;
else

View File

@ -51,6 +51,13 @@ static linpool *rte_update_pool;
list routing_tables;
struct rt_pending_export {
struct rte_storage *new; /* New route */
struct rte_storage *new_best; /* New best route */
struct rte_storage *old; /* Old route */
struct rte_storage *old_best; /* Old best route */
};
static void rt_free_hostcache(rtable *tab);
static void rt_notify_hostcache(rtable *tab, net *net);
static void rt_update_hostcache(rtable *tab);
@ -370,6 +377,29 @@ rte_trace_out(uint flag, struct channel *c, rte *e, const char *msg)
rte_trace(c, e, '<', msg);
}
static uint
rte_feed_count(net *n)
{
uint count = 0;
for (struct rte_storage *e = n->routes; e; e = e->next)
if (rte_is_valid(RTE_OR_NULL(e)))
count++;
return count;
}
static void
rte_feed_obtain(net *n, struct rte **feed, uint count)
{
uint i = 0;
for (struct rte_storage *e = n->routes; e; e = e->next)
if (rte_is_valid(RTE_OR_NULL(e)))
{
ASSERT_DIE(i < count);
feed[i++] = &e->rte;
}
ASSERT_DIE(i == count);
}
static rte *
export_filter_(struct channel *c, rte *rt, linpool *pool, int silent)
{
@ -523,81 +553,71 @@ rt_notify_basic(struct channel *c, const net_addr *net, rte *new, rte *old, int
}
static void
rt_notify_accepted(struct channel *c, net *net, rte *new_changed, rte *old_changed, int refeed)
rt_notify_accepted(struct channel *c, const net_addr *n, struct rt_pending_export *rpe,
struct rte **feed, uint count, int refeed)
{
// struct proto *p = c->proto;
rte nb0;
rte *new_best = NULL;
rte *old_best = NULL;
int new_first = 0;
rte nb0, *new_best = NULL, *old_best = NULL;
/*
* We assume that there are no changes in net route order except (added)
* new_changed and (removed) old_changed. Therefore, the function is not
* compatible with deterministic_med (where nontrivial reordering can happen
* as a result of a route change) and with recomputation of recursive routes
* due to next hop update (where many routes can be changed in one step).
*
* Note that we need this assumption just for optimizations, we could just
* run full new_best recomputation otherwise.
*
* There are three cases:
* feed or old_best is old_changed -> we need to recompute new_best
* old_best is before new_changed -> new_best is old_best, ignore
* old_best is after new_changed -> try new_changed, otherwise old_best
*/
if (net->routes)
c->export_stats.updates_received++;
else
c->export_stats.withdraws_received++;
/* Find old_best - either old_changed, or route for net->routes */
if (old_changed && bmap_test(&c->export_map, old_changed->id))
old_best = old_changed;
else
for (uint i = 0; i < count; i++)
{
for (struct rte_storage *r = net->routes; r && rte_is_valid(&r->rte); r = r->next)
if (!rte_is_valid(feed[i]))
continue;
/* Has been already rejected, won't bother with it */
if (!refeed && bmap_test(&c->export_reject_map, feed[i]->id))
continue;
/* Previously exported */
if (!old_best && bmap_test(&c->export_map, feed[i]->id))
{
if (bmap_test(&c->export_map, r->rte.id))
/* is still best */
if (!new_best)
{
old_best = &r->rte;
DBG("rt_notify_accepted: idempotent\n");
return;
}
/* is superseded */
old_best = feed[i];
break;
}
/* Have no new best route yet */
if (!new_best)
{
/* Try this route not seen before */
nb0 = *feed[i];
new_best = export_filter(c, &nb0, 0);
DBG("rt_notify_accepted: checking route id %u: %s\n", feed[i]->id, new_best ? "ok" : "no");
}
}
/* Check obsolete routes for previously exported */
if (!old_best)
if (rpe && rpe->old && bmap_test(&c->export_map, rpe->old->rte.id))
old_best = &rpe->old->rte;
/* for (; rpe; rpe = atomic_load_explicit(&rpe->next, memory_order_relaxed))
{
if (rpe->old && bmap_test(&hook->accept_map, rpe->old->id))
{
old_best = &rpe->old.rte;
break;
}
/* Note if new_changed found before old_best */
if (&r->rte == new_changed)
new_first = 1;
}
}
/* Find new_best */
if ((new_changed == old_changed) || (old_best == old_changed))
{
/* Feed or old_best changed -> find first accepted by filters */
for (struct rte_storage *r = net->routes; r && rte_is_valid(&r->rte); r = r->next)
{
/* Already rejected before */
if (!refeed && bmap_test(&c->export_reject_map, r->rte.id))
continue;
if (new_best = export_filter(c, ((nb0 = r->rte), &nb0), 0))
if (rpe == rpe_last)
break;
}
}
else
{
/* Other cases -> either new_changed, or old_best (and nothing changed) */
if (new_first && (new_changed = export_filter(c, new_changed, 0)))
new_best = new_changed;
else
return;
}
*/
/* Nothing to export */
if (!new_best && !old_best)
{
DBG("rt_notify_accepted: nothing to export\n");
return;
}
do_rt_notify(c, net->n.addr, new_best, old_best, refeed);
do_rt_notify(c, n, new_best, old_best, refeed);
}
@ -607,36 +627,45 @@ nexthop_merge_rta(struct nexthop *nhs, rta *a, linpool *pool, int max)
return nexthop_merge(nhs, &(a->nh), 1, 0, max, pool);
}
rte *
rt_export_merged(struct channel *c, net *net, linpool *pool, int silent)
static rte *
rt_export_merged(struct channel *c, struct rte **feed, uint count, linpool *pool, int silent, int refeed)
{
_Thread_local static rte rloc;
// struct proto *p = c->proto;
struct nexthop *nhs = NULL;
_Thread_local static rte rme;
struct rte_storage *best0 = net->routes;
rte *best;
rte *best0 = feed[0], *best = NULL;
if (!best0 || !rte_is_valid(&best0->rte))
if (!rte_is_valid(best0))
return NULL;
best = export_filter_(c, ((rme = best0->rte), &rme), pool, silent);
/* Already rejected, no need to re-run the filter */
if (!refeed && bmap_test(&c->export_reject_map, best0->id))
return NULL;
if (!best || !rte_is_reachable(best))
rloc = *best0;
best = export_filter_(c, &rloc, pool, silent);
if (!best)
/* Best route doesn't pass the filter */
return NULL;
if (!rte_is_reachable(best))
/* Unreachable routes can't be merged */
return best;
for (struct rte_storage *rt0 = best0->next; rt0; rt0 = rt0->next)
for (uint i = 1; i < count; i++)
{
if (!rte_mergable(best, &rt0->rte))
if (!rte_mergable(best0, feed[i]))
continue;
rte rnh = rt0->rte;
rte *rt = export_filter_(c, &rnh, pool, 1);
rte tmp0 = *feed[i];
rte *tmp = export_filter_(c, &tmp0, pool, 1);
if (!rt)
if (!tmp || !rte_is_reachable(tmp))
continue;
if (rte_is_reachable(rt))
nhs = nexthop_merge_rta(nhs, rt->attrs, pool, c->merge_limit);
nhs = nexthop_merge_rta(nhs, tmp->attrs, pool, c->merge_limit);
}
if (nhs)
@ -653,41 +682,77 @@ rt_export_merged(struct channel *c, net *net, linpool *pool, int silent)
return best;
}
rte *
rt_export_merged_show(struct channel *c, net *n, linpool *pool)
{
uint count = rte_feed_count(n);
rte **feed = alloca(count * sizeof(rte *));
rte_feed_obtain(n, feed, count);
return rt_export_merged(c, feed, count, pool, 1, 0);
}
static void
rt_notify_merged(struct channel *c, net *net, rte *new_changed, rte *old_changed,
rte *new_best, rte *old_best, int refeed)
rt_notify_merged(struct channel *c, const net_addr *n, struct rt_pending_export *rpe,
struct rte **feed, uint count, int refeed)
{
/* We assume that all rte arguments are either NULL or rte_is_valid() */
/* This check should be done by the caller */
if (!new_best && !old_best)
return;
// struct proto *p = c->proto;
#if 0 /* TODO: Find whether this check is possible when processing multiple changes at once. */
/* Check whether the change is relevant to the merged route */
if ((new_best == old_best) &&
(new_changed != old_changed) &&
!rte_mergable(new_best, new_changed) &&
!rte_mergable(old_best, old_changed))
return;
#endif
if (new_best)
c->export_stats.updates_received++;
else
c->export_stats.withdraws_received++;
rte *old_best = NULL;
/* Find old best route */
for (uint i = 0; i < count; i++)
if (bmap_test(&c->export_map, feed[i]->id))
{
old_best = feed[i];
break;
}
/* Check obsolete routes for previously exported */
if (!old_best)
if (rpe && rpe->old && bmap_test(&c->export_map, rpe->old->rte.id))
old_best = &rpe->old->rte;
/* for (; rpe; rpe = atomic_load_explicit(&rpe->next, memory_order_relaxed))
{
if (rpe->old && bmap_test(&hook->accept_map, rpe->old->id))
{
old_best = &rpe->old.rte;
break;
}
if (rpe == rpe_last)
break;
}
*/
/* Prepare new merged route */
if (new_best)
new_best = rt_export_merged(c, net, rte_update_pool, 0);
rte *new_merged = count ? rt_export_merged(c, feed, count, rte_update_pool, 0, refeed) : NULL;
/* Check old merged route */
if (old_best && !bmap_test(&c->export_map, old_best->id))
old_best = NULL;
if (!new_best && !old_best)
if (!new_merged && !old_best)
return;
do_rt_notify(c, net->n.addr, new_best, old_best, refeed);
do_rt_notify(c, n, new_merged, old_best, refeed);
}
static void
rt_notify_bulk(struct channel *c, const net_addr *n, struct rt_pending_export *rpe,
struct rte **feed, uint count, int refeed)
{
switch (c->ra_mode)
{
case RA_ACCEPTED:
return rt_notify_accepted(c, n, rpe, feed, count, refeed);
case RA_MERGED:
return rt_notify_merged(c, n, rpe, feed, count, refeed);
}
}
@ -774,12 +839,15 @@ rte_announce(rtable *tab, net *net, struct rte_storage *new, struct rte_storage
break;
case RA_ACCEPTED:
rt_notify_accepted(c, net, RTE_OR_NULL(new), RTE_OR_NULL(old), 0);
break;
case RA_MERGED:
rt_notify_merged(c, net, RTE_OR_NULL(new), RTE_OR_NULL(old), RTE_OR_NULL(new_best), RTE_OR_NULL(old_best), 0);
break;
{
struct rt_pending_export rpe = { .new = new, .old = old, .new_best = new_best, .old_best = old_best };
uint count = rte_feed_count(net);
rte **feed = alloca(count * sizeof(rte *));
rte_feed_obtain(net, feed, count);
rt_notify_bulk(c, net->n.addr, &rpe, feed, count, 0);
break;
}
}
/* Drop the old stored rejection if applicable.
@ -2127,10 +2195,13 @@ static inline void
do_feed_channel(struct channel *c, net *n, rte *e)
{
rte_update_lock();
if (c->ra_mode == RA_ACCEPTED)
rt_notify_accepted(c, n, NULL, NULL, c->refeeding);
else if (c->ra_mode == RA_MERGED)
rt_notify_merged(c, n, NULL, NULL, e, e, c->refeeding);
if ((c->ra_mode == RA_ACCEPTED) || (c->ra_mode == RA_MERGED))
{
uint count = rte_feed_count(n);
rte **feed = alloca(count * sizeof(rte *));
rte_feed_obtain(n, feed, count);
rt_notify_bulk(c, n->n.addr, NULL, feed, count, c->refeeding);
}
else /* RA_BASIC */
{
rte e0 = *e;

View File

@ -566,7 +566,7 @@ krt_export_net(struct krt_proto *p, net *net)
const struct filter *filter = c->out_filter;
if (c->ra_mode == RA_MERGED)
return rt_export_merged(c, net, krt_filter_lp, 1);
return rt_export_merged_show(c, net, krt_filter_lp);
static _Thread_local rte rt;
rt = net->routes->rte;