diff options
author | Ondrej Zajicek (work) <santiago@crfreenet.org> | 2021-02-10 03:09:57 +0100 |
---|---|---|
committer | Ondrej Zajicek (work) <santiago@crfreenet.org> | 2021-02-10 03:09:57 +0100 |
commit | 00b85905b9f5081eb2fce0ed79542085278e9f42 (patch) | |
tree | 964728bcef7dfb03136898a12e1ebbaccc13409c /nest/proto.c | |
parent | d06a875b042b608e61b2d5a2bb594641d3e1322f (diff) |
Nest: Automatic channel reloads based on RPKI changes
If there are roa_check() calls in channel filters, then the channel
subscribes to ROA table notifications, which are sent when ROA tables
are updated (subject to settle time) and trigger channel reload or
refeed.
Diffstat (limited to 'nest/proto.c')
-rw-r--r-- | nest/proto.c | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/nest/proto.c b/nest/proto.c index 7b359152..6bd2427b 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -48,6 +48,7 @@ static char *e_states[] = { "DOWN", "FEEDING", "READY" }; extern struct protocol proto_unix_iface; +static void channel_request_reload(struct channel *c); static void proto_shutdown_loop(timer *); static void proto_rethink_goal(struct proto *p); static char *proto_state_name(struct proto *p); @@ -180,6 +181,8 @@ proto_add_channel(struct proto *p, struct channel_config *cf) c->last_state_change = current_time(); c->reloadable = 1; + init_list(&c->roa_subscriptions); + CALL(c->channel->init, c, cf); add_tail(&p->channels, &c->n); @@ -256,10 +259,15 @@ channel_feed_loop(void *ptr) if (c->export_state != ES_FEEDING) return; + /* Start feeding */ if (!c->feed_active) + { if (c->proto->feed_begin) c->proto->feed_begin(c, !c->refeeding); + c->refeed_pending = 0; + } + // DBG("Feeding protocol %s continued\n", p->name); if (!rt_feed_channel(c)) { @@ -289,10 +297,133 @@ channel_feed_loop(void *ptr) if (c->proto->feed_end) c->proto->feed_end(c); + + /* Restart feeding */ + if (c->refeed_pending) + channel_request_feeding(c); +} + + +static void +channel_roa_in_changed(struct rt_subscription *s) +{ + struct channel *c = s->data; + int active = c->reload_event && ev_active(c->reload_event); + + CD(c, "Reload triggered by RPKI change%s", active ? " - already active" : ""); + + if (!active) + channel_request_reload(c); + else + c->reload_pending = 1; +} + +static void +channel_roa_out_changed(struct rt_subscription *s) +{ + struct channel *c = s->data; + int active = (c->export_state == ES_FEEDING); + + CD(c, "Feeding triggered by RPKI change%s", active ? " - already active" : ""); + + if (!active) + channel_request_feeding(c); + else + c->refeed_pending = 1; +} + +/* Temporary code, subscriptions should be changed to resources */ +struct roa_subscription { + struct rt_subscription s; + node roa_node; +}; + +static int +channel_roa_is_subscribed(struct channel *c, rtable *tab, int dir) +{ + void (*hook)(struct rt_subscription *) = + dir ? channel_roa_in_changed : channel_roa_out_changed; + + struct roa_subscription *s; + node *n; + + WALK_LIST2(s, n, c->roa_subscriptions, roa_node) + if ((s->s.tab == tab) && (s->s.hook == hook)) + return 1; + + return 0; } static void +channel_roa_subscribe(struct channel *c, rtable *tab, int dir) +{ + if (channel_roa_is_subscribed(c, tab, dir)) + return; + + struct roa_subscription *s = mb_allocz(c->proto->pool, sizeof(struct roa_subscription)); + + s->s.hook = dir ? channel_roa_in_changed : channel_roa_out_changed; + s->s.data = c; + rt_subscribe(tab, &s->s); + + add_tail(&c->roa_subscriptions, &s->roa_node); +} + +static void +channel_roa_unsubscribe(struct roa_subscription *s) +{ + rt_unsubscribe(&s->s); + rem_node(&s->roa_node); + mb_free(s); +} + +static void +channel_roa_subscribe_filter(struct channel *c, int dir) +{ + const struct filter *f = dir ? c->in_filter : c->out_filter; + struct rtable *tab; + + if ((f == FILTER_ACCEPT) || (f == FILTER_REJECT)) + return; + + struct filter_iterator fit; + FILTER_ITERATE_INIT(&fit, f, c->proto->pool); + + FILTER_ITERATE(&fit, fi) + { + switch (fi->fi_code) + { + case FI_ROA_CHECK_IMPLICIT: + tab = fi->i_FI_ROA_CHECK_IMPLICIT.rtc->table; + channel_roa_subscribe(c, tab, dir); + break; + + case FI_ROA_CHECK_EXPLICIT: + tab = fi->i_FI_ROA_CHECK_EXPLICIT.rtc->table; + channel_roa_subscribe(c, tab, dir); + break; + + default: + break; + } + } + FILTER_ITERATE_END; + + FILTER_ITERATE_CLEANUP(&fit); +} + +static void +channel_roa_unsubscribe_all(struct channel *c) +{ + struct roa_subscription *s; + node *n, *x; + + WALK_LIST2_DELSAFE(s, n, x, c->roa_subscriptions, roa_node) + channel_roa_unsubscribe(s); +} + +static void channel_start_export(struct channel *c) { ASSERT(c->channel_state == CS_UP); @@ -329,11 +460,19 @@ channel_reload_loop(void *ptr) { struct channel *c = ptr; + /* Start reload */ + if (!c->reload_active) + c->reload_pending = 0; + if (!rt_reload_channel(c)) { ev_schedule(c->reload_event); return; } + + /* Restart reload */ + if (c->reload_pending) + channel_request_reload(c); } static void @@ -400,6 +539,14 @@ channel_do_start(struct channel *c) } static void +channel_do_up(struct channel *c) +{ + /* Register RPKI/ROA subscriptions */ + channel_roa_subscribe_filter(c, 1); + channel_roa_subscribe_filter(c, 0); +} + +static void channel_do_flush(struct channel *c) { rt_schedule_prune(c->table); @@ -415,6 +562,8 @@ channel_do_flush(struct channel *c) c->in_table = NULL; c->reload_event = NULL; c->out_table = NULL; + + channel_roa_unsubscribe_all(c); } static void @@ -484,6 +633,7 @@ channel_set_state(struct channel *c, uint state) if (!c->gr_wait && c->proto->rt_notify) channel_start_export(c); + channel_do_up(c); break; case CS_FLUSHING: @@ -694,6 +844,14 @@ channel_reconfigure(struct channel *c, struct channel_config *cf) if (c->channel_state != CS_UP) goto done; + /* Update RPKI/ROA subscriptions */ + if (import_changed || export_changed) + { + channel_roa_unsubscribe_all(c); + channel_roa_subscribe_filter(c, 1); + channel_roa_subscribe_filter(c, 0); + } + if (reconfigure_type == RECONFIG_SOFT) { if (import_changed) |