0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-12-22 09:41:54 +00:00

Common parts of the ROA updater moved into the table

Channel is now just subscribing to yet another journal announcing
digested tries from the ROA table.

Creating tries in every channel on-the-fly was too slow to handle
and it ate obnoxious amounts of memory. Instead, the tries are
constructed directly in the table and the channels are notified
with the completed tries.

The delayed export-release mechanism is used to keep the tries allocated
until routes get reloaded.
This commit is contained in:
Maria Matejka 2024-06-03 14:23:41 +02:00
parent b287c13f21
commit d4ea561917
7 changed files with 194 additions and 132 deletions

View File

@ -41,6 +41,7 @@ static inline void bmap_clear(struct bmap *b, uint n)
BIT32_CLR(b->data, n);
}
#define BMAP_WALK(_b, _n) for (uint _max = bmap_max((_b)), _n = 0; _n < _max; _n++) if (!BIT32_TEST((_b)->data, _n)) ; else
struct hmap
{

View File

@ -283,6 +283,7 @@ table_opt:
this_table->cork_threshold.high = $4; }
| EXPORT SETTLE TIME settle { this_table->export_settle = $4; }
| ROUTE REFRESH EXPORT SETTLE TIME settle { this_table->export_rr_settle = $6; }
| ROA SETTLE TIME settle { this_table->roa_settle = $4; }
;
table_opts:
@ -370,7 +371,6 @@ channel_item_:
| RECEIVE LIMIT limit_spec { this_channel->rx_limit = $3; }
| IMPORT LIMIT limit_spec { this_channel->in_limit = $3; }
| EXPORT LIMIT limit_spec { this_channel->out_limit = $3; }
| ROA SETTLE TIME settle { this_channel->roa_settle = $4; }
| PREFERENCE expr { this_channel->preference = $2; check_u16($2); }
| IMPORT KEEP FILTERED bool {
if ($4)

View File

@ -389,89 +389,44 @@ proto_remove_channels(struct proto *p)
struct roa_subscription {
node roa_node;
struct settle settle;
struct channel *c;
rtable *tab;
struct rt_export_request reader;
event update_event;
struct f_trie *trie;
void (*refeed_hook)(struct channel *, struct rt_feeding_request *);
struct lfjour_recipient digest_recipient;
event update_event;
struct rt_feeding_request rfr;
};
static void
channel_roa_reload_done(struct rt_feeding_request *req)
{
rfree(req->prefilter.trie->lp);
SKIP_BACK_DECLARE(struct roa_subscription, s, rfr, req);
lfjour_release(&s->digest_recipient);
ev_send(proto_work_list(s->c->proto), &s->update_event);
/* FIXME: this should reset import/export filters if ACTION BLOCK */
}
static void
channel_roa_changed(struct settle *se)
channel_roa_changed(void *_s)
{
SKIP_BACK_DECLARE(struct roa_subscription, s, settle, se);
struct channel *c = s->c;
struct roa_subscription *s = _s;
CD(c, "Feeding triggered by RPKI change");
if (s->digest_recipient.cur)
return;
/* Setup feeding request */
struct rt_feeding_request *rfr = lp_alloc(s->trie->lp, sizeof *rfr);
*rfr = (struct rt_feeding_request) {
if (!lfjour_get(&s->digest_recipient))
return;
SKIP_BACK_DECLARE(struct roa_digest, rd, li, s->digest_recipient.cur);
s->rfr = (struct rt_feeding_request) {
.prefilter = {
.mode = TE_ADDR_TRIE,
.trie = s->trie,
.trie = rd->trie,
},
.done = channel_roa_reload_done,
};
/* Prepare new trie */
s->trie = f_new_trie(lp_new(c->proto->pool), 0);
/* Actually request the feed */
s->refeed_hook(c, rfr);
}
static void
channel_roa_update_net(struct roa_subscription *s, const net_addr *net)
{
switch (net->type)
{
case NET_ROA4:
trie_add_prefix(s->trie, net, net_pxlen(net), 32);
break;
case NET_ROA6:
trie_add_prefix(s->trie, net, net_pxlen(net), 128);
break;
default:
bug("ROA table sent us a non-roa export");
}
settle_kick(&s->settle, s->c->proto->loop);
}
static void
channel_roa_update(void *_s)
{
struct roa_subscription *s = _s;
RT_EXPORT_WALK(&s->reader, u)
switch (u->kind)
{
case RT_EXPORT_STOP:
bug("Main table export stopped");
break;
case RT_EXPORT_FEED:
if (u->feed->count_routes)
channel_roa_update_net(s, u->feed->block[0].net);
break;
case RT_EXPORT_UPDATE:
/* Only switched ROA from one source to another */
if (!u->update->new || !u->update->old)
channel_roa_update_net(s, u->update->new ? u->update->new->net : u->update->old->net);
break;
}
s->refeed_hook(s->c, &s->rfr);
}
static inline void (*channel_roa_reload_hook(int dir))(struct channel *, struct rt_feeding_request *)
@ -479,18 +434,6 @@ static inline void (*channel_roa_reload_hook(int dir))(struct channel *, struct
return dir ? channel_reimport : channel_refeed;
}
static void
channel_dump_roa_req(struct rt_export_request *req)
{
SKIP_BACK_DECLARE(struct roa_subscription, s, reader, req);
struct channel *c = s->c;
debug(" Channel %s.%s ROA %s change notifier request %p\n",
c->proto->name, c->name,
(s->refeed_hook == channel_roa_reload_hook(1)) ? "import" : "export",
req);
}
static int
channel_roa_is_subscribed(struct channel *c, rtable *tab, int dir)
{
@ -513,30 +456,23 @@ channel_roa_subscribe(struct channel *c, rtable *tab, int dir)
struct roa_subscription *s = mb_allocz(c->proto->pool, sizeof(struct roa_subscription));
*s = (struct roa_subscription) {
.settle = SETTLE_INIT(&c->roa_settle, channel_roa_changed, NULL),
.refeed_hook = channel_roa_reload_hook(dir),
.c = c,
.trie = f_new_trie(lp_new(c->proto->pool), 0),
.tab = tab,
.update_event = {
.hook = channel_roa_update,
.data = s,
},
.reader = {
.name = mb_sprintf(c->proto->pool, "%s.%s.roa-%s.%s",
c->proto->name, c->name, dir ? "in" : "out", tab->name),
.r = {
.refeed_hook = channel_roa_reload_hook(dir),
.digest_recipient = {
.target = proto_work_list(c->proto),
.event = &s->update_event,
},
.pool = c->proto->pool,
.trace_routes = c->debug | c->proto->debug,
.dump = channel_dump_roa_req,
.update_event = {
.hook = channel_roa_changed,
.data = s,
},
};
add_tail(&c->roa_subscriptions, &s->roa_node);
rt_export_subscribe(tab, best, &s->reader);
RT_LOCK(tab, t);
rt_lock_table(t);
lfjour_register(&t->roa_digest->digest, &s->digest_recipient);
}
static void
@ -544,15 +480,14 @@ channel_roa_unsubscribe(struct roa_subscription *s)
{
struct channel *c = s->c;
rt_export_unsubscribe(best, &s->reader);
settle_cancel(&s->settle);
s->settle.hook = NULL;
RT_LOCKED(s->tab, t)
{
lfjour_unregister(&s->digest_recipient);
rt_unlock_table(t);
}
ev_postpone(&s->update_event);
ASSERT_DIE(rt_export_get_state(&s->reader) == TES_DOWN);
rfree(s->trie->lp);
rem_node(&s->roa_node);
mb_free(s);
@ -1030,11 +965,6 @@ channel_config_new(const struct channel_class *cc, const char *name, uint net_ty
cf->debug = new_config->channel_default_debug;
cf->rpki_reload = 1;
cf->roa_settle = (struct settle_config) {
.min = 1 S,
.max = 20 S,
};
add_tail(&proto->channels, &cf->n);
return cf;
@ -1119,22 +1049,6 @@ channel_reconfigure(struct channel *c, struct channel_config *cf)
c->in_req.trace_routes = c->out_req.trace_routes = c->debug | c->proto->debug;
c->rpki_reload = cf->rpki_reload;
if ( (c->roa_settle.min != cf->roa_settle.min)
|| (c->roa_settle.max != cf->roa_settle.max))
{
c->roa_settle = cf->roa_settle;
struct roa_subscription *s;
node *n;
WALK_LIST2(s, n, c->roa_subscriptions, roa_node)
{
s->settle.cf = cf->roa_settle;
if (settle_active(&s->settle))
settle_kick(&s->settle, &main_birdloop);
}
}
/* Execute channel-specific reconfigure hook */
if (c->class->reconfigure && !c->class->reconfigure(c, cf, &import_changed, &export_changed))
return 0;

View File

@ -511,8 +511,6 @@ struct channel_config {
struct channel_limit in_limit; /* Limit for importing routes from protocol */
struct channel_limit out_limit; /* Limit for exporting routes to protocol */
struct settle_config roa_settle; /* Settle times for ROA-induced reload */
u8 net_type; /* Routing table network type (NET_*), 0 for undefined */
u8 ra_mode; /* Mode of received route advertisements (RA_*) */
u16 preference; /* Default route preference */
@ -541,8 +539,6 @@ struct channel {
struct limit in_limit; /* Input limit */
struct limit out_limit; /* Output limit */
struct settle_config roa_settle; /* Settle times for ROA-induced reload */
u8 limit_actions[PLD_MAX]; /* Limit actions enum */
u8 limit_active; /* Flags for active limits */

View File

@ -70,6 +70,8 @@ struct rtable_config {
struct settle_config export_settle; /* Export announcement settler */
struct settle_config export_rr_settle;/* Export announcement settler config valid when any
route refresh is running */
struct settle_config roa_settle; /* Settle times for ROA-induced reload */
};
/*
@ -194,7 +196,7 @@ struct rt_export_union {
} *update;
const struct rt_export_feed {
uint count_routes, count_exports;
const struct netindex *ni;
struct netindex *ni;
rte *block;
u64 *exports;
char data[0];
@ -213,7 +215,7 @@ struct rt_exporter {
netindex_hash *netindex; /* Table for net <-> id conversion */
void (*stopped)(struct rt_exporter *); /* Callback when exporter can stop */
void (*cleanup_done)(struct rt_exporter *, u64 end); /* Callback when cleanup has been done */
struct rt_export_feed *(*feed_net)(struct rt_exporter *, struct rcu_unwinder *, const struct netindex *, const struct rt_export_item *first);
struct rt_export_feed *(*feed_net)(struct rt_exporter *, struct rcu_unwinder *, struct netindex *, const struct rt_export_item *first);
void (*feed_cleanup)(struct rt_exporter *, struct rt_export_feeder *);
};
@ -404,6 +406,7 @@ struct rtable_private {
struct tbf rl_pipe; /* Rate limiting token buffer for pipe collisions */
struct f_trie *flowspec_trie; /* Trie for evaluation of flowspec notifications */
struct roa_digestor *roa_digest; /* Digest of changed ROAs export */
// struct mpls_domain *mpls_domain; /* Label allocator for MPLS */
};
@ -640,6 +643,20 @@ struct hostcache {
event source_event;
};
struct roa_digestor {
struct rt_export_request req; /* Notifier from the table */
struct lfjour digest; /* Digest journal of struct roa_digest */
struct settle settle; /* Settle timer before announcing digests */
struct f_trie *trie; /* Trie to be announced */
rtable *tab; /* Table this belongs to */
event event;
};
struct roa_digest {
LFJOUR_ITEM_INHERIT(li);
struct f_trie *trie; /* Trie marking all prefixes where ROA have changed */
};
#define rte_update channel_rte_import
/**
* rte_update - enter a new update to a routing table

View File

@ -2083,7 +2083,7 @@ rt_net_feed(rtable *t, const net_addr *a, const struct rt_pending_export *first)
}
static struct rt_export_feed *
rt_feed_net_all(struct rt_exporter *e, struct rcu_unwinder *u, const struct netindex *ni, const struct rt_export_item *_first)
rt_feed_net_all(struct rt_exporter *e, struct rcu_unwinder *u, struct netindex *ni, const struct rt_export_item *_first)
{
RT_READ_ANCHORED(SKIP_BACK(rtable, export_all, e), tr, u);
return rt_net_feed_internal(tr, ni, SKIP_BACK(const struct rt_pending_export, it, _first));
@ -2096,7 +2096,7 @@ rt_net_best(rtable *t, const net_addr *a)
RT_READ(t, tr);
const struct netindex *i = net_find_index(t->netindex, a);
struct netindex *i = net_find_index(t->netindex, a);
net *n = i ? net_find(tr, i) : NULL;
if (!n)
return rt;
@ -2111,7 +2111,7 @@ rt_net_best(rtable *t, const net_addr *a)
}
static struct rt_export_feed *
rt_feed_net_best(struct rt_exporter *e, struct rcu_unwinder *u, const struct netindex *ni, const struct rt_export_item *_first)
rt_feed_net_best(struct rt_exporter *e, struct rcu_unwinder *u, struct netindex *ni, const struct rt_export_item *_first)
{
SKIP_BACK_DECLARE(rtable, t, export_best, e);
SKIP_BACK_DECLARE(const struct rt_pending_export, first, it, _first);
@ -2690,6 +2690,85 @@ rt_flowspec_reset_trie(struct rtable_private *tab)
tab->flowspec_trie->ipv4 = ipv4;
}
/* ROA digestor */
static void
rt_dump_roa_digestor_req(struct rt_export_request *req)
{
debug(" ROA update digestor %s (%p)\n", req->name, req);
}
static void
rt_cleanup_roa_digest(struct lfjour *j UNUSED, struct lfjour_item *i)
{
SKIP_BACK_DECLARE(struct roa_digest, d, li, i);
rfree(d->trie->lp);
}
static void
rt_roa_announce_digest(struct settle *s)
{
SKIP_BACK_DECLARE(struct roa_digestor, d, settle, s);
RT_LOCK(d->tab, tab);
struct lfjour_item *it = lfjour_push_prepare(&d->digest);
if (it)
{
SKIP_BACK_DECLARE(struct roa_digest, dd, li, it);
dd->trie = d->trie;
lfjour_push_commit(&d->digest);
}
else
rfree(d->trie->lp);
d->trie = f_new_trie(lp_new(tab->rp), 0);
}
static void
rt_roa_update_net(struct roa_digestor *d, struct netindex *ni, uint maxlen)
{
trie_add_prefix(d->trie, ni->addr, net_pxlen(ni->addr), maxlen);
settle_kick(&d->settle, d->tab->loop);
}
static void
rt_roa_update(void *_d)
{
struct roa_digestor *d = _d;
RT_LOCK(d->tab, tab);
RT_EXPORT_WALK(&d->req, u)
{
struct netindex *ni = NULL;
switch (u->kind)
{
case RT_EXPORT_STOP:
bug("Main table export stopped");
case RT_EXPORT_FEED:
if (u->feed->count_routes)
ni = u->feed->ni;
break;
case RT_EXPORT_UPDATE:
/* Only switched ROA from one source to another? No change indicated. */
if (!u->update->new || !u->update->old)
ni = NET_TO_INDEX(u->update->new ? u->update->new->net : u->update->old->net);
break;
}
if (ni)
rt_roa_update_net(d, ni, (tab->addr_type == NET_ROA6) ? 128 : 32);
MAYBE_DEFER_TASK(birdloop_event_list(tab->loop), &d->event,
"ROA digestor update in %s", tab->name);
}
}
/* Routing table setup and free */
static void
rt_free(resource *_r)
{
@ -2845,6 +2924,44 @@ rt_setup(pool *pp, struct rtable_config *cf)
RT_EXPORT_WALK(&t->best_req, u)
ASSERT_DIE(u->kind == RT_EXPORT_FEED);
/* Prepare the ROA digestor */
if ((t->addr_type == NET_ROA6) || (t->addr_type == NET_ROA4))
{
struct roa_digestor *d = mb_alloc(p, sizeof *d);
*d = (struct roa_digestor) {
.tab = RT_PUB(t),
.req = {
.name = mb_sprintf(p, "%s.roa-digestor", t->name),
.r = {
.target = birdloop_event_list(t->loop),
.event = &d->event,
},
.pool = p,
.trace_routes = t->debug,
.dump = rt_dump_roa_digestor_req,
},
.digest = {
.loop = t->loop,
.domain = t->lock.rtable,
.item_size = sizeof(struct roa_digest),
.item_done = rt_cleanup_roa_digest,
},
.settle = SETTLE_INIT(&cf->roa_settle, rt_roa_announce_digest, NULL),
.event = {
.hook = rt_roa_update,
.data = d,
},
.trie = f_new_trie(lp_new(t->rp), 0),
};
struct settle_config digest_settle_config = {};
rtex_export_subscribe(&t->export_best, &d->req);
lfjour_init(&d->digest, &digest_settle_config);
t->roa_digest = d;
}
t->cork_threshold = cf->cork_threshold;
t->rl_pipe = (struct tbf) TBF_DEFAULT_LOG_LIMITS;
@ -3894,6 +4011,10 @@ rt_new_table(struct symbol *s, uint addr_type)
.min = 100 MS,
.max = 3 S,
};
c->roa_settle = (struct settle_config) {
.min = 1 S,
.max = 20 S,
};
c->debug = new_config->table_default_debug;
add_tail(&new_config->tables, &c->n);
@ -3943,6 +4064,14 @@ rt_shutdown(void *tab_)
rtable *t = tab_;
RT_LOCK(t, tab);
if (tab->roa_digest)
{
rtex_export_unsubscribe(&tab->roa_digest->req);
ASSERT_DIE(EMPTY_TLIST(lfjour_recipient, &tab->roa_digest->digest.recipients));
ev_postpone(&tab->roa_digest->event);
settle_cancel(&tab->roa_digest->settle);
}
rtex_export_unsubscribe(&tab->best_req);
rt_exporter_shutdown(&tab->export_best, NULL);
@ -4037,6 +4166,11 @@ rt_reconfigure(struct rtable_private *tab, struct rtable_config *new, struct rta
if (new->cork_threshold.low != old->cork_threshold.low)
rt_check_cork_low(tab);
if (tab->roa_digest && (
(new->roa_settle.min != tab->roa_digest->settle.cf.min)
|| (new->roa_settle.max != tab->roa_digest->settle.cf.max)))
tab->roa_digest->settle.cf = new->roa_settle;
return 1;
}

View File

@ -1945,7 +1945,7 @@ bgp_out_item_done(struct lfjour *j, struct lfjour_item *i)
{}
static struct rt_export_feed *
bgp_out_feed_net(struct rt_exporter *e, struct rcu_unwinder *u, const struct netindex *ni, const struct rt_export_item *_first)
bgp_out_feed_net(struct rt_exporter *e, struct rcu_unwinder *u, struct netindex *ni, const struct rt_export_item *_first)
{
struct rt_export_feed *feed = NULL;
SKIP_BACK_DECLARE(struct bgp_channel, c, prefix_exporter, e);