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
{
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:
| 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)
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;
+
+ if (!lfjour_get(&s->digest_recipient))
+ return;
- /* Setup feeding request */
- struct rt_feeding_request *rfr = lp_alloc(s->trie->lp, sizeof *rfr);
- *rfr = (struct rt_feeding_request) {
+ 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 *)
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)
{
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,
+ .refeed_hook = channel_roa_reload_hook(dir),
+ .digest_recipient = {
+ .target = proto_work_list(c->proto),
+ .event = &s->update_event,
+ },
.update_event = {
- .hook = channel_roa_update,
+ .hook = channel_roa_changed,
.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 = {
- .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,
- },
};
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
{
struct channel *c = s->c;
- rt_export_unsubscribe(best, &s->reader);
- settle_cancel(&s->settle);
- s->settle.hook = NULL;
- ev_postpone(&s->update_event);
-
- ASSERT_DIE(rt_export_get_state(&s->reader) == TES_DOWN);
+ RT_LOCKED(s->tab, t)
+ {
+ lfjour_unregister(&s->digest_recipient);
+ rt_unlock_table(t);
+ }
- rfree(s->trie->lp);
+ ev_postpone(&s->update_event);
rem_node(&s->roa_node);
mb_free(s);
-
+
channel_check_stopped(c);
}
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;
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;
{
if (!prr->trie)
return NULL;
-
+
/* Increase the refeed counter */
atomic_fetch_add_explicit(&prr->counter, 1, memory_order_relaxed);
ASSERT_DIE(this_cli->parser_pool != prr->trie->lp);
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 */
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 */
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 */
+
};
/*
} *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];
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 *);
};
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 */
};
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
}
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));
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;
}
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);
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)
{
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;
.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);
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);
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;
}
{}
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);