mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-11-10 05:08:42 +00:00
Secondary and merged exports get a whole feed instead of traversing the table structures directly
This commit is contained in:
parent
8f3942a97e
commit
0767a0c288
@ -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);
|
void *net_route(rtable *tab, const net_addr *n);
|
||||||
int net_roa_check(rtable *tab, const net_addr *n, u32 asn);
|
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);
|
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_begin(rtable *t, struct channel *c);
|
||||||
void rt_refresh_end(rtable *t, struct channel *c);
|
void rt_refresh_end(rtable *t, struct channel *c);
|
||||||
void rt_modify_stale(rtable *t, struct channel *c);
|
void rt_modify_stale(rtable *t, struct channel *c);
|
||||||
|
@ -143,7 +143,7 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
|
|||||||
{
|
{
|
||||||
/* Special case for merged export */
|
/* Special case for merged export */
|
||||||
pass = 1;
|
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)
|
if (em)
|
||||||
e = *em;
|
e = *em;
|
||||||
else
|
else
|
||||||
|
279
nest/rt-table.c
279
nest/rt-table.c
@ -51,6 +51,13 @@ static linpool *rte_update_pool;
|
|||||||
|
|
||||||
list routing_tables;
|
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_free_hostcache(rtable *tab);
|
||||||
static void rt_notify_hostcache(rtable *tab, net *net);
|
static void rt_notify_hostcache(rtable *tab, net *net);
|
||||||
static void rt_update_hostcache(rtable *tab);
|
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);
|
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 *
|
static rte *
|
||||||
export_filter_(struct channel *c, rte *rt, linpool *pool, int silent)
|
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
|
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, *new_best = NULL, *old_best = NULL;
|
||||||
rte nb0;
|
|
||||||
rte *new_best = NULL;
|
|
||||||
rte *old_best = NULL;
|
|
||||||
int new_first = 0;
|
|
||||||
|
|
||||||
/*
|
for (uint i = 0; i < count; i++)
|
||||||
* 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 (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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Note if new_changed found before old_best */
|
if (rpe == rpe_last)
|
||||||
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))
|
|
||||||
break;
|
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)
|
if (!new_best && !old_best)
|
||||||
|
{
|
||||||
|
DBG("rt_notify_accepted: nothing to export\n");
|
||||||
return;
|
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);
|
return nexthop_merge(nhs, &(a->nh), 1, 0, max, pool);
|
||||||
}
|
}
|
||||||
|
|
||||||
rte *
|
static rte *
|
||||||
rt_export_merged(struct channel *c, net *net, linpool *pool, int silent)
|
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 proto *p = c->proto;
|
||||||
struct nexthop *nhs = NULL;
|
struct nexthop *nhs = NULL;
|
||||||
_Thread_local static rte rme;
|
rte *best0 = feed[0], *best = NULL;
|
||||||
struct rte_storage *best0 = net->routes;
|
|
||||||
rte *best;
|
|
||||||
|
|
||||||
if (!best0 || !rte_is_valid(&best0->rte))
|
if (!rte_is_valid(best0))
|
||||||
return NULL;
|
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;
|
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;
|
continue;
|
||||||
|
|
||||||
rte rnh = rt0->rte;
|
rte tmp0 = *feed[i];
|
||||||
rte *rt = export_filter_(c, &rnh, pool, 1);
|
rte *tmp = export_filter_(c, &tmp0, pool, 1);
|
||||||
|
|
||||||
if (!rt)
|
if (!tmp || !rte_is_reachable(tmp))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (rte_is_reachable(rt))
|
nhs = nexthop_merge_rta(nhs, tmp->attrs, pool, c->merge_limit);
|
||||||
nhs = nexthop_merge_rta(nhs, rt->attrs, pool, c->merge_limit);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nhs)
|
if (nhs)
|
||||||
@ -653,41 +682,77 @@ rt_export_merged(struct channel *c, net *net, linpool *pool, int silent)
|
|||||||
return best;
|
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
|
static void
|
||||||
rt_notify_merged(struct channel *c, net *net, rte *new_changed, rte *old_changed,
|
rt_notify_merged(struct channel *c, const net_addr *n, struct rt_pending_export *rpe,
|
||||||
rte *new_best, rte *old_best, int refeed)
|
struct rte **feed, uint count, int refeed)
|
||||||
{
|
{
|
||||||
/* We assume that all rte arguments are either NULL or rte_is_valid() */
|
// struct proto *p = c->proto;
|
||||||
|
|
||||||
/* This check should be done by the caller */
|
|
||||||
if (!new_best && !old_best)
|
|
||||||
return;
|
|
||||||
|
|
||||||
|
#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 */
|
/* Check whether the change is relevant to the merged route */
|
||||||
if ((new_best == old_best) &&
|
if ((new_best == old_best) &&
|
||||||
(new_changed != old_changed) &&
|
(new_changed != old_changed) &&
|
||||||
!rte_mergable(new_best, new_changed) &&
|
!rte_mergable(new_best, new_changed) &&
|
||||||
!rte_mergable(old_best, old_changed))
|
!rte_mergable(old_best, old_changed))
|
||||||
return;
|
return;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (new_best)
|
rte *old_best = NULL;
|
||||||
c->export_stats.updates_received++;
|
/* Find old best route */
|
||||||
else
|
for (uint i = 0; i < count; i++)
|
||||||
c->export_stats.withdraws_received++;
|
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 */
|
/* Prepare new merged route */
|
||||||
if (new_best)
|
rte *new_merged = count ? rt_export_merged(c, feed, count, rte_update_pool, 0, refeed) : NULL;
|
||||||
new_best = rt_export_merged(c, net, rte_update_pool, 0);
|
|
||||||
|
|
||||||
/* Check old merged route */
|
if (!new_merged && !old_best)
|
||||||
if (old_best && !bmap_test(&c->export_map, old_best->id))
|
|
||||||
old_best = NULL;
|
|
||||||
|
|
||||||
if (!new_best && !old_best)
|
|
||||||
return;
|
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;
|
break;
|
||||||
|
|
||||||
case RA_ACCEPTED:
|
case RA_ACCEPTED:
|
||||||
rt_notify_accepted(c, net, RTE_OR_NULL(new), RTE_OR_NULL(old), 0);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RA_MERGED:
|
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.
|
/* Drop the old stored rejection if applicable.
|
||||||
@ -2127,10 +2195,13 @@ static inline void
|
|||||||
do_feed_channel(struct channel *c, net *n, rte *e)
|
do_feed_channel(struct channel *c, net *n, rte *e)
|
||||||
{
|
{
|
||||||
rte_update_lock();
|
rte_update_lock();
|
||||||
if (c->ra_mode == RA_ACCEPTED)
|
if ((c->ra_mode == RA_ACCEPTED) || (c->ra_mode == RA_MERGED))
|
||||||
rt_notify_accepted(c, n, NULL, NULL, c->refeeding);
|
{
|
||||||
else if (c->ra_mode == RA_MERGED)
|
uint count = rte_feed_count(n);
|
||||||
rt_notify_merged(c, n, NULL, NULL, e, e, c->refeeding);
|
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 */
|
else /* RA_BASIC */
|
||||||
{
|
{
|
||||||
rte e0 = *e;
|
rte e0 = *e;
|
||||||
|
@ -566,7 +566,7 @@ krt_export_net(struct krt_proto *p, net *net)
|
|||||||
const struct filter *filter = c->out_filter;
|
const struct filter *filter = c->out_filter;
|
||||||
|
|
||||||
if (c->ra_mode == RA_MERGED)
|
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;
|
static _Thread_local rte rt;
|
||||||
rt = net->routes->rte;
|
rt = net->routes->rte;
|
||||||
|
Loading…
Reference in New Issue
Block a user