diff options
Diffstat (limited to 'nest/proto.c')
-rw-r--r-- | nest/proto.c | 894 |
1 files changed, 284 insertions, 610 deletions
diff --git a/nest/proto.c b/nest/proto.c index 2546e812..cd6a3faa 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -15,19 +15,17 @@ #include "lib/event.h" #include "lib/timer.h" #include "lib/string.h" -#include "lib/coro.h" #include "conf/conf.h" -#include "nest/route.h" +#include "nest/rt.h" #include "nest/iface.h" #include "nest/cli.h" #include "filter/filter.h" #include "filter/f-inst.h" pool *proto_pool; -list proto_list; +list STATIC_LIST_INIT(proto_list); -static list protocol_list; -struct protocol *class_to_protocol[PROTOCOL__MAX]; +static list STATIC_LIST_INIT(protocol_list); #define CD(c, msg, args...) ({ if (c->debug & D_STATES) log(L_TRACE "%s.%s: " msg, c->proto->name, c->name ?: "?", ## args); }) #define PD(p, msg, args...) ({ if (p->debug & D_STATES) log(L_TRACE "%s: " msg, p->name, ## args); }) @@ -47,28 +45,20 @@ static char *c_states[] = { "DOWN", "START", "UP", "STOP", "RESTART" }; extern struct protocol proto_unix_iface; -static void channel_aux_request_refeed(struct channel_aux_table *cat); +static void channel_request_reload(struct channel *c); static void proto_rethink_goal(struct proto *p); static char *proto_state_name(struct proto *p); static void channel_init_limit(struct channel *c, struct limit *l, int dir, struct channel_limit *cf); static void channel_update_limit(struct channel *c, struct limit *l, int dir, struct channel_limit *cf); static void channel_reset_limit(struct channel *c, struct limit *l, int dir); static void channel_feed_end(struct channel *c); +static void channel_stop_export(struct channel *c); static void channel_export_stopped(struct rt_export_request *req); +static void channel_check_stopped(struct channel *c); static inline int proto_is_done(struct proto *p) { return (p->proto_state == PS_DOWN) && proto_is_inactive(p); } -static inline event_list *proto_event_list(struct proto *p) -{ return p->loop == &main_birdloop ? &global_event_list : birdloop_event_list(p->loop); } - -static inline event_list *proto_work_list(struct proto *p) -{ return p->loop == &main_birdloop ? &global_work_list : birdloop_event_list(p->loop); } - -static inline void proto_send_event(struct proto *p) -{ ev_send(proto_event_list(p), p->event); } - - static inline int channel_is_active(struct channel *c) { return (c->channel_state != CS_DOWN); } @@ -97,9 +87,7 @@ channel_export_log_state_change(struct rt_export_request *req, u8 state) switch (state) { case TES_FEEDING: - if (c->out_table) - rt_refresh_begin(&c->out_table->push); - else if (c->proto->feed_begin) + if (c->proto->feed_begin) c->proto->feed_begin(c, !c->refeeding); break; case TES_READY: @@ -189,16 +177,6 @@ proto_find_channel_by_name(struct proto *p, const char *n) return NULL; } -rte * channel_preimport(struct rt_import_request *req, rte *new, rte *old); -rte * channel_in_preimport(struct rt_import_request *req, rte *new, rte *old); - -void rt_notify_optimal(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe); -void rt_notify_any(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe); -void rt_feed_any(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe, rte **feed, uint count); -void rt_notify_accepted(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe, rte **feed, uint count); -void rt_notify_merged(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe, rte **feed, uint count); - - /** * proto_add_channel - connect protocol to a routing table * @p: protocol instance @@ -223,25 +201,22 @@ proto_add_channel(struct proto *p, struct channel_config *cf) c->channel = cf->channel; c->proto = p; c->table = cf->table->table; - - RT_LOCKED(c->table, t) - rt_lock_table(t); + rt_lock_table(c->table); c->in_filter = cf->in_filter; c->out_filter = cf->out_filter; + c->out_subprefix = cf->out_subprefix; channel_init_limit(c, &c->rx_limit, PLD_RX, &cf->rx_limit); channel_init_limit(c, &c->in_limit, PLD_IN, &cf->in_limit); channel_init_limit(c, &c->out_limit, PLD_OUT, &cf->out_limit); - c->rte_update_pool = lp_new_default(proto_pool); - c->net_type = cf->net_type; c->ra_mode = cf->ra_mode; c->preference = cf->preference; c->debug = cf->debug; c->merge_limit = cf->merge_limit; - c->in_keep_filtered = cf->in_keep_filtered; + c->in_keep = cf->in_keep; c->rpki_reload = cf->rpki_reload; c->channel_state = CS_DOWN; @@ -266,9 +241,7 @@ proto_remove_channel(struct proto *p UNUSED, struct channel *c) CD(c, "Removed", c->name); - RT_LOCKED(c->table, t) - rt_unlock_table(t); - + rt_unlock_table(c->table); rem_node(&c->n); mb_free(c); } @@ -309,51 +282,76 @@ proto_remove_channels(struct proto *p) proto_remove_channel(p, c); } +struct roa_subscription { + node roa_node; + struct settle settle; + struct channel *c; + struct rt_export_request req; +}; + static void -channel_roa_in_changed(void *_data) +channel_roa_in_changed(struct settle *se) { - struct channel *c = _data; + struct roa_subscription *s = SKIP_BACK(struct roa_subscription, settle, se); + struct channel *c = s->c; CD(c, "Reload triggered by RPKI change"); - channel_request_reload(c); } static void -channel_roa_out_changed(void *_data) +channel_roa_out_changed(struct settle *se) { - struct channel *c = _data; + struct roa_subscription *s = SKIP_BACK(struct roa_subscription, settle, se); + struct channel *c = s->c; + CD(c, "Feeding triggered by RPKI change"); c->refeed_pending = 1; + channel_stop_export(c); +} - if (c->out_req.hook) - rt_stop_export(&c->out_req, channel_export_stopped); +static void +channel_export_one_roa(struct rt_export_request *req, const net_addr *net UNUSED, struct rt_pending_export *first) +{ + struct roa_subscription *s = SKIP_BACK(struct roa_subscription, req, req); + + /* TODO: use the information about what roa has changed */ + settle_kick(&s->settle, s->c->proto->loop); + + rpe_mark_seen_all(req->hook, first, NULL, NULL); } -/* Temporary code, subscriptions should be changed to resources */ -struct roa_subscription { - struct rt_subscription s; - node roa_node; -}; +static void +channel_dump_roa_req(struct rt_export_request *req) +{ + struct roa_subscription *s = SKIP_BACK(struct roa_subscription, req, req); + struct channel *c = s->c; + struct rtable_private *tab = SKIP_BACK(struct rtable_private, exporter.e, req->hook->table); + + debug(" Channel %s.%s ROA %s change notifier from table %s request %p\n", + c->proto->name, c->name, + (s->settle.hook == channel_roa_in_changed) ? "import" : "export", + tab->name, req); +} static int channel_roa_is_subscribed(struct channel *c, rtable *tab, int dir) { - void (*hook)(void *) = + void (*hook)(struct settle *) = 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.event->hook == hook)) + if ((tab == SKIP_BACK(rtable, priv.exporter.e, s->req.hook->table)) + && (s->settle.hook == hook)) return 1; return 0; } - static void channel_roa_subscribe(struct channel *c, rtable *tab, int dir) { @@ -361,21 +359,41 @@ channel_roa_subscribe(struct channel *c, rtable *tab, int dir) return; struct roa_subscription *s = mb_allocz(c->proto->pool, sizeof(struct roa_subscription)); - s->s.event = ev_new_init(c->proto->pool, dir ? channel_roa_in_changed : channel_roa_out_changed, c); - s->s.event->list = proto_work_list(c->proto); - rt_subscribe(tab, &s->s); + *s = (struct roa_subscription) { + .settle = SETTLE_INIT(&c->roa_settle, dir ? channel_roa_in_changed : channel_roa_out_changed, NULL), + .c = c, + .req = { + .name = mb_sprintf(c->proto->pool, "%s.%s.roa-%s.%s", + c->proto->name, c->name, dir ? "in" : "out", tab->name), + .list = proto_work_list(c->proto), + .trace_routes = c->debug | c->proto->debug, + .dump_req = channel_dump_roa_req, + .export_one = channel_export_one_roa, + }, + }; add_tail(&c->roa_subscriptions, &s->roa_node); + rt_request_export(tab, &s->req); } static void -channel_roa_unsubscribe(struct roa_subscription *s) +channel_roa_unsubscribed(struct rt_export_request *req) { - rt_unsubscribe(&s->s); + struct roa_subscription *s = SKIP_BACK(struct roa_subscription, req, req); + struct channel *c = s->c; + rem_node(&s->roa_node); - rfree(s->s.event); mb_free(s); + + channel_check_stopped(c); +} + +static void +channel_roa_unsubscribe(struct roa_subscription *s) +{ + rt_stop_export(&s->req, channel_roa_unsubscribed); + settle_cancel(&s->settle); } static void @@ -395,7 +413,7 @@ channel_roa_subscribe_filter(struct channel *c, int dir) #ifdef CONFIG_BGP /* No automatic reload for BGP channels without in_table / out_table */ if (c->channel == &channel_bgp) - valid = dir ? !!c->in_table : !!c->out_table; + valid = dir ? ((c->in_keep & RIK_PREFILTER) == RIK_PREFILTER) : !!c->out_table; #endif struct filter_iterator fit; @@ -405,14 +423,8 @@ channel_roa_subscribe_filter(struct channel *c, int dir) { switch (fi->fi_code) { - case FI_ROA_CHECK_IMPLICIT: - tab = fi->i_FI_ROA_CHECK_IMPLICIT.rtc->table; - if (valid) channel_roa_subscribe(c, tab, dir); - found = 1; - break; - - case FI_ROA_CHECK_EXPLICIT: - tab = fi->i_FI_ROA_CHECK_EXPLICIT.rtc->table; + case FI_ROA_CHECK: + tab = fi->i_FI_ROA_CHECK.rtc->table; if (valid) channel_roa_subscribe(c, tab, dir); found = 1; break; @@ -449,14 +461,10 @@ channel_start_import(struct channel *c) return; } - int nlen = strlen(c->name) + strlen(c->proto->name) + 2; - char *rn = mb_allocz(c->proto->pool, nlen); - bsprintf(rn, "%s.%s", c->proto->name, c->name); - c->in_req = (struct rt_import_request) { - .name = rn, - .list = proto_work_list(c->proto), + .name = mb_sprintf(c->proto->pool, "%s.%s", c->proto->name, c->name), .trace_routes = c->debug | c->proto->debug, + .list = proto_work_list(c->proto), .dump_req = channel_dump_import_req, .log_state_change = channel_import_log_state_change, .preimport = channel_preimport, @@ -478,26 +486,24 @@ channel_start_export(struct channel *c) { if (c->out_req.hook) { - c->restart_export = 1; - log(L_WARN "%s.%s: Fast channel export restart", c->proto->name, c->name); + log(L_WARN "%s.%s: Attempted to start channel's already started export", c->proto->name, c->name); return; } ASSERT(c->channel_state == CS_UP); - int nlen = strlen(c->name) + strlen(c->proto->name) + 2; - char *rn = mb_allocz(c->proto->pool, nlen); - bsprintf(rn, "%s.%s", c->proto->name, c->name); c->out_req = (struct rt_export_request) { - .name = rn, + .name = mb_sprintf(c->proto->pool, "%s.%s", c->proto->name, c->name), .list = proto_work_list(c->proto), + .addr = c->out_subprefix, + .addr_mode = c->out_subprefix ? TE_ADDR_IN : TE_ADDR_NONE, .trace_routes = c->debug | c->proto->debug, .dump_req = channel_dump_export_req, .log_state_change = channel_export_log_state_change, }; - bmap_init(&c->export_map, c->proto->pool, 1024); - bmap_init(&c->export_reject_map, c->proto->pool, 1024); + bmap_init(&c->export_map, c->proto->pool, 16); + bmap_init(&c->export_reject_map, c->proto->pool, 16); channel_reset_limit(c, &c->out_limit, PLD_OUT); @@ -531,32 +537,28 @@ channel_check_stopped(struct channel *c) switch (c->channel_state) { case CS_STOP: - if (c->out_req.hook || c->in_req.hook || c->out_table || c->in_table) + if (!EMPTY_LIST(c->roa_subscriptions) || c->out_req.hook || c->in_req.hook || c->reload_req.hook) return; channel_set_state(c, CS_DOWN); - proto_send_event(c->proto); + proto_send_event(c->proto, c->proto->event); break; case CS_PAUSE: - if (c->out_req.hook) + if (!EMPTY_LIST(c->roa_subscriptions) || c->out_req.hook || c->reload_req.hook) return; channel_set_state(c, CS_START); break; - default: - bug("Stopped channel in a bad state: %d", c->channel_state); } DBG("%s.%s: Channel requests/hooks stopped (in state %s)\n", c->proto->name, c->name, c_states[c->channel_state]); } void -channel_import_stopped(void *_c) +channel_import_stopped(struct rt_import_request *req) { - struct channel *c = _c; - - c->in_req.hook = NULL; + struct channel *c = SKIP_BACK(struct channel, in_req, req); mb_free(c->in_req.name); c->in_req.name = NULL; @@ -577,8 +579,10 @@ channel_export_stopped(struct rt_export_request *req) c->refeeding = 1; c->refeed_pending = 0; - bmap_reset(&c->export_map, 1024); - bmap_reset(&c->export_reject_map, 1024); + channel_reset_limit(c, &c->out_limit, PLD_OUT); + + bmap_reset(&c->export_map, 16); + bmap_reset(&c->export_reject_map, 16); rt_request_export(c->table, req); return; @@ -590,20 +594,12 @@ channel_export_stopped(struct rt_export_request *req) bmap_free(&c->export_map); bmap_free(&c->export_reject_map); - if (c->restart_export) - { - c->restart_export = 0; - channel_start_export(c); - } - else - channel_check_stopped(c); + channel_check_stopped(c); } static void channel_feed_end(struct channel *c) { - struct rt_export_request *req = &c->out_req; - /* Reset export limit if the feed ended with acceptable number of exported routes */ struct limit *l = &c->out_limit; if (c->refeeding && @@ -615,377 +611,96 @@ channel_feed_end(struct channel *c) channel_reset_limit(c, &c->out_limit, PLD_OUT); c->refeed_pending = 1; - rt_stop_export(req, channel_export_stopped); + channel_stop_export(c); return; } - if (c->out_table) - rt_refresh_end(&c->out_table->push); - else if (c->proto->feed_end) + if (c->proto->feed_end) c->proto->feed_end(c); if (c->refeed_pending) - rt_stop_export(req, channel_export_stopped); -} - -#define CHANNEL_AUX_TABLE_DUMP_REQ(inout, imex, pgimex, pushget) static void \ - channel_##inout##_##pushget##_dump_req(struct rt_##pgimex##_request *req) { \ - struct channel_aux_table *cat = SKIP_BACK(struct channel_aux_table, pushget, req); \ - debug(" Channel %s.%s " #imex " table " #pushget " request %p\n", cat->c->proto->name, cat->c->name, req); } - -CHANNEL_AUX_TABLE_DUMP_REQ(in, import, import, push) -CHANNEL_AUX_TABLE_DUMP_REQ(in, import, export, get) -CHANNEL_AUX_TABLE_DUMP_REQ(out, export, import, push) -CHANNEL_AUX_TABLE_DUMP_REQ(out, export, export, get) - -#undef CHANNEL_AUX_TABLE_DUMP_REQ - -static uint channel_aux_imex(struct channel_aux_table *cat) -{ - if (cat->c->in_table == cat) - return 0; - else if (cat->c->out_table == cat) - return 1; + channel_stop_export(c); else - bug("Channel aux table must be in_table or out_table"); -} - -static void -channel_aux_stopped(void *data) -{ - struct channel_aux_table *cat; - - RT_LOCKED((rtable *) data, t) - cat = t->config->owner; - - ASSERT_DIE(cat->push.hook == NULL); - ASSERT_DIE(cat->get.hook == NULL); - ASSERT_DIE(cat->stop_pending); - - struct channel *c = cat->c; - - if (channel_aux_imex(cat)) - c->out_table = NULL; - else - c->in_table = NULL; - - mb_free(cat); - channel_check_stopped(c); -} - -static void -channel_aux_import_stopped(void *_cat) -{ - struct channel_aux_table *cat = _cat; - - cat->push.hook = NULL; - - if (!cat->get.hook) - RT_LOCKED(cat->tab, t) - { - t->delete = channel_aux_stopped; - rt_unlock_table(t); - } + c->refeeding = 0; } -static void -channel_aux_export_stopped(struct rt_export_request *req) +/* Called by protocol for reload from in_table */ +void +channel_schedule_reload(struct channel *c) { - struct channel_aux_table *cat = SKIP_BACK(struct channel_aux_table, get, req); - req->hook = NULL; + ASSERT(c->in_req.hook); - if (cat->refeed_pending && !cat->stop_pending) + if (c->reload_req.hook) { - cat->refeed_pending = 0; - rt_request_export(cat->tab, req); - + CD(c, "Reload triggered before the previous one has finished"); + c->reload_pending = 1; return; } - if (!cat->push.hook) - RT_LOCKED(cat->tab, t) - { - t->delete = channel_aux_stopped; - rt_unlock_table(t); - } + rt_refresh_begin(&c->in_req); + rt_request_export(c->table, &c->reload_req); } static void -channel_aux_stop(struct channel_aux_table *cat) +channel_reload_stopped(struct rt_export_request *req) { - ASSERT_DIE(!cat->stop_pending); - - cat->stop_pending = 1; - - RT_LOCKED(cat->tab, t) - rt_lock_table(t); - - cat->push_stopped = (event) { - .hook = channel_aux_import_stopped, - .data = cat, - .list = proto_event_list(cat->c->proto), - }; - - rt_stop_import(&cat->push, &cat->push_stopped); - rt_stop_export(&cat->get, channel_aux_export_stopped); -} + struct channel *c = SKIP_BACK(struct channel, reload_req, req); -static void -channel_push_log_state_change(struct rt_import_request *req, u8 state) -{ - struct channel_aux_table *cat = SKIP_BACK(struct channel_aux_table, push, req); - const char *imex = channel_aux_imex(cat) ? "export" : "import"; - CD(cat->c, "Channel %s table import state changed to %s", imex, rt_import_state_name(state)); -} - -static void -channel_get_log_state_change(struct rt_export_request *req, u8 state) -{ - struct channel_aux_table *cat = SKIP_BACK(struct channel_aux_table, get, req); - const char *imex = channel_aux_imex(cat) ? "export" : "import"; - CD(cat->c, "Channel %s table export state changed to %s", imex, rt_export_state_name(state)); - - switch (state) + /* Restart reload */ + if (c->reload_pending) { - case TES_FEEDING: - if (imex && cat->c->proto->feed_begin) - cat->c->proto->feed_begin(cat->c, !cat->c->refeeding); - else if (!imex) - rt_refresh_begin(&cat->c->in_req); - break; - - case TES_READY: - if (imex && cat->c->proto->feed_end) - cat->c->proto->feed_end(cat->c); - else if (!imex) - rt_refresh_end(&cat->c->in_req); - - if (cat->refeed_pending) - rt_stop_export(&cat->get, channel_aux_export_stopped); - - break; - } -} - -void rte_update_direct(struct channel *c, const net_addr *n, rte *new, struct rte_src *src); - -static int -channel_aux_export_one_any(struct rt_export_request *req, struct rt_pending_export *rpe, rte **new, rte **old) -{ - struct rte_src *src = rpe->new ? rpe->new->rte.src : rpe->old->rte.src; - *old = RTES_OR_NULL(rpe->old); - struct rte_storage *new_stored; - - while (rpe) - { - new_stored = rpe->new; - rpe_mark_seen(req->hook, rpe); - rpe = rpe_next(rpe, src); - } - - *new = RTES_CLONE(new_stored, *new); - - return (*new || *old) && (&new_stored->rte != *old); -} - -static int -channel_aux_export_one_best(struct rt_export_request *req, struct rt_pending_export *rpe, rte **new, rte **old) -{ - *old = RTES_OR_NULL(rpe->old_best); - struct rte_storage *new_stored; - - while (rpe) - { - new_stored = rpe->new_best; - rpe_mark_seen(req->hook, rpe); - rpe = rpe_next(rpe, NULL); + c->reload_pending = 0; + channel_request_reload(c); } - *new = RTES_CLONE(new_stored, *new); - - return (*new || *old) && (&new_stored->rte != *old); -} - -static void -channel_in_export_one_any(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe) -{ - struct channel_aux_table *cat = SKIP_BACK(struct channel_aux_table, get, req); - - rte n0, *new = &n0, *old; - if (channel_aux_export_one_any(req, rpe, &new, &old)) - rte_update_direct(cat->c, net, new, old ? old->src : new->src); + if (c->channel_state != CS_UP) + channel_check_stopped(c); } static void -channel_in_export_one_best(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe) +channel_reload_log_state_change(struct rt_export_request *req, u8 state) { - struct channel_aux_table *cat = SKIP_BACK(struct channel_aux_table, get, req); - - rte n0, *new = &n0, *old; - if (channel_aux_export_one_best(req, rpe, &new, &old)) - rte_update_direct(cat->c, net, new, old ? old->src : new->src); -} + struct channel *c = SKIP_BACK(struct channel, reload_req, req); -static void -channel_in_export_bulk_any(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe UNUSED, rte **feed, uint count) -{ - struct channel_aux_table *cat = SKIP_BACK(struct channel_aux_table, get, req); - for (uint i=0; i<count; i++) + if (state == TES_READY) { - rte n0 = *feed[i]; - rte_update_direct(cat->c, net, &n0, n0.src); - } -} + if (c->channel_state == CS_UP) + rt_refresh_end(&c->in_req); -static void -channel_in_export_bulk_best(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe UNUSED, rte **feed, uint count) -{ - struct channel_aux_table *cat = SKIP_BACK(struct channel_aux_table, get, req); - if (!count) - return; - - rte n0 = *feed[0]; - rte_update_direct(cat->c, net, &n0, n0.src); -} - -void do_rt_notify_direct(struct channel *c, const net_addr *net, rte *new, const rte *old); - -static void -channel_out_export_one_any(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe) -{ - struct channel_aux_table *cat = SKIP_BACK(struct channel_aux_table, get, req); - rte n0, *new = &n0, *old; - if (channel_aux_export_one_any(req, rpe, &new, &old)) - do_rt_notify_direct(cat->c, net, new, old); -} - -static void -channel_out_export_one_best(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe) -{ - struct channel_aux_table *cat = SKIP_BACK(struct channel_aux_table, get, req); - rte n0, *new = &n0, *old; - if (channel_aux_export_one_best(req, rpe, &new, &old)) - do_rt_notify_direct(cat->c, net, new, old); + rt_stop_export(req, channel_reload_stopped); + } } static void -channel_out_export_bulk(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe UNUSED, rte **feed, uint count) +channel_reload_dump_req(struct rt_export_request *req) { - struct channel_aux_table *cat = SKIP_BACK(struct channel_aux_table, get, req); - if (cat->c->ra_mode != RA_ANY) - ASSERT_DIE(count <= 1); - - for (uint i=0; i<count; i++) - { - rte n0 = *feed[i]; - do_rt_notify_direct(cat->c, net, &n0, NULL); - } + struct channel *c = SKIP_BACK(struct channel, reload_req, req); + debug(" Channel %s.%s import reload request %p\n", c->proto->name, c->name, req); } /* Called by protocol to activate in_table */ -void -channel_setup_in_table(struct channel *c, int best) -{ - int nlen = sizeof("import") + strlen(c->name) + strlen(c->proto->name) + 3; - - struct { - struct channel_aux_table cat; - struct rtable_config tab_cf; - char name[0]; - } *cat = mb_allocz(c->proto->pool, sizeof(*cat) + nlen); - - bsprintf(cat->name, "%s.%s.import", c->proto->name, c->name); - - cat->tab_cf.owner = cat; - cat->tab_cf.name = cat->name; - cat->tab_cf.addr_type = c->net_type; - cat->tab_cf.cork_limit = 4 * page_size / sizeof(struct rt_pending_export); - - c->in_table = &cat->cat; - c->in_table->push = (struct rt_import_request) { - .name = cat->name, - .list = proto_work_list(c->proto), - .trace_routes = c->debug | c->proto->debug, - .dump_req = channel_in_push_dump_req, - .log_state_change = channel_push_log_state_change, - .preimport = channel_in_preimport, - }; - c->in_table->get = (struct rt_export_request) { - .name = cat->name, - .list = proto_work_list(c->proto), - .trace_routes = c->debug | c->proto->debug, - .dump_req = channel_in_get_dump_req, - .log_state_change = channel_get_log_state_change, - .export_one = best ? channel_in_export_one_best : channel_in_export_one_any, - .export_bulk = best ? channel_in_export_bulk_best : channel_in_export_bulk_any, - }; - - c->in_table->c = c; - c->in_table->tab = rt_setup(c->proto->pool, &cat->tab_cf); - - rt_request_import(c->in_table->tab, &c->in_table->push); - rt_request_export(c->in_table->tab, &c->in_table->get); -} - -/* Called by protocol to activate out_table */ -void -channel_setup_out_table(struct channel *c) +static void +channel_setup_in_table(struct channel *c) { - int nlen = sizeof("export") + strlen(c->name) + strlen(c->proto->name) + 3; - - struct { - struct channel_aux_table cat; - struct rtable_config tab_cf; - char name[0]; - } *cat = mb_allocz(c->proto->pool, sizeof(*cat) + nlen); - - bsprintf(cat->name, "%s.%s.export", c->proto->name, c->name); - - cat->tab_cf.owner = cat; - cat->tab_cf.name = cat->name; - cat->tab_cf.addr_type = c->net_type; - cat->tab_cf.cork_limit = 4 * page_size / sizeof(struct rt_pending_export); - - c->out_table = &cat->cat; - c->out_table->push = (struct rt_import_request) { - .name = cat->name, + c->reload_req = (struct rt_export_request) { + .name = mb_sprintf(c->proto->pool, "%s.%s.import", c->proto->name, c->name), .list = proto_work_list(c->proto), .trace_routes = c->debug | c->proto->debug, - .dump_req = channel_out_push_dump_req, - .log_state_change = channel_push_log_state_change, + .export_bulk = channel_reload_export_bulk, + .dump_req = channel_reload_dump_req, + .log_state_change = channel_reload_log_state_change, }; - c->out_table->get = (struct rt_export_request) { - .name = cat->name, - .list = proto_work_list(c->proto), - .trace_routes = c->debug | c->proto->debug, - .dump_req = channel_out_get_dump_req, - .log_state_change = channel_get_log_state_change, - .export_one = (c->ra_mode == RA_ANY) ? channel_out_export_one_any : channel_out_export_one_best, - .export_bulk = channel_out_export_bulk, - }; - - c->out_table->c = c; - c->out_table->tab = rt_setup(c->proto->pool, &cat->tab_cf); - - rt_request_import(c->out_table->tab, &c->out_table->push); - rt_request_export(c->out_table->tab, &c->out_table->get); } -static void -channel_aux_request_refeed(struct channel_aux_table *cat) -{ - if (cat->stop_pending) - return; - - cat->refeed_pending = 1; - rt_stop_export(&cat->get, channel_aux_export_stopped); -} static void channel_do_start(struct channel *c) { c->proto->active_channels++; + if ((c->in_keep & RIK_PREFILTER) == RIK_PREFILTER) + channel_setup_in_table(c); + CALL(c->channel->start, c); channel_start_import(c); @@ -1005,36 +720,26 @@ channel_do_up(struct channel *c) static void channel_do_pause(struct channel *c) { - /* Stop export */ - if (c->out_req.hook) - { - rt_stop_export(&c->out_req, channel_export_stopped); - c->refeeding = 0; - } - + /* Drop ROA subscriptions */ channel_roa_unsubscribe_all(c); + + /* Need to abort feeding */ + c->reload_pending = 0; + + if (c->reload_req.hook && c->reload_req.hook->export_state != TES_STOP) + rt_stop_export(&c->reload_req, channel_reload_stopped); + + /* Stop export */ + c->refeed_pending = 0; + channel_stop_export(c); } static void channel_do_stop(struct channel *c) { - /* Drop auxiliary tables */ - if (c->in_table) - channel_aux_stop(c->in_table); - - if (c->out_table) - channel_aux_stop(c->out_table); - /* Stop import */ if (c->in_req.hook) - { - c->in_stopped = (event) { - .hook = channel_import_stopped, - .data = c, - .list = proto_event_list(c->proto), - }; - rt_stop_import(&c->in_req, &c->in_stopped); - } + rt_stop_import(&c->in_req, channel_import_stopped); c->gr_wait = 0; if (c->gr_lock) @@ -1042,24 +747,27 @@ channel_do_stop(struct channel *c) CALL(c->channel->shutdown, c); - channel_roa_unsubscribe_all(c); } static void channel_do_down(struct channel *c) { - ASSERT(!c->out_req.hook && !c->in_req.hook && !c->out_table && !c->in_table); + ASSERT(!c->reload_req.hook); c->proto->active_channels--; memset(&c->import_stats, 0, sizeof(struct channel_import_stats)); memset(&c->export_stats, 0, sizeof(struct channel_export_stats)); + c->out_table = NULL; + + /* The in_table and out_table are going to be freed by freeing their resource pools. */ + CALL(c->channel->cleanup, c); /* Schedule protocol shutddown */ if (proto_is_done(c->proto)) - proto_send_event(c->proto); + proto_send_event(c->proto, c->proto->event); } void @@ -1085,7 +793,7 @@ channel_set_state(struct channel *c, uint state) break; case CS_UP: - ASSERT(cs == CS_DOWN || cs == CS_START || cs == CS_PAUSE); + ASSERT(cs == CS_DOWN || cs == CS_START); if (cs == CS_DOWN) channel_do_start(c); @@ -1135,32 +843,28 @@ channel_set_state(struct channel *c, uint state) * completed, it will switch back to ES_READY. This function can be called * even when feeding is already running, in that case it is restarted. */ -static void -channel_request_table_feeding(struct channel *c) +void +channel_request_feeding(struct channel *c) { ASSERT(c->out_req.hook); + if (c->refeed_pending) + return; + c->refeed_pending = 1; - rt_stop_export(&c->out_req, channel_export_stopped); + channel_stop_export(c); } -void -channel_request_feeding(struct channel *c) +static void +channel_stop_export(struct channel *c) { - if (c->gr_wait || !c->proto->rt_notify) + if (!c->out_req.hook || (c->out_req.hook->export_state == TES_STOP)) return; - CD(c, "Refeed requested"); - - ASSERT_DIE(c->out_req.hook); - - if (c->out_table) - channel_aux_request_refeed(c->out_table); - else - channel_request_table_feeding(c); + rt_stop_export(&c->out_req, channel_export_stopped); } -void +static void channel_request_reload(struct channel *c) { ASSERT(c->in_req.hook); @@ -1168,31 +872,12 @@ channel_request_reload(struct channel *c) CD(c, "Reload requested"); - if (c->in_table) - channel_aux_request_refeed(c->in_table); + if ((c->in_keep & RIK_PREFILTER) == RIK_PREFILTER) + channel_schedule_reload(c); else c->proto->reload_routes(c); } -void -channel_refresh_begin(struct channel *c) -{ - CD(c, "Channel route refresh begin"); - if (c->in_table) - rt_refresh_begin(&c->in_table->push); - else - rt_refresh_begin(&c->in_req); -} - -void -channel_refresh_end(struct channel *c) -{ - if (c->in_table) - rt_refresh_end(&c->in_table->push); - else - rt_refresh_end(&c->in_req); -} - const struct channel_class channel_basic = { .channel_size = sizeof(struct channel), .config_size = sizeof(struct channel_config) @@ -1212,7 +897,7 @@ channel_config_new(const struct channel_class *cc, const char *name, uint net_ty if (proto->net_type && (net_type != proto->net_type)) cf_error("Different channel type"); - tab = new_config->def_tables[net_type]; + tab = rt_get_default_table(new_config, net_type); } if (!cc) @@ -1231,6 +916,11 @@ 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; @@ -1250,6 +940,7 @@ channel_config_get(const struct channel_class *cc, const char *name, uint net_ty cf_error("Multiple %s channels", name); cf->parent = proto; + cf->copy = 1; return cf; } @@ -1275,8 +966,16 @@ static int reconfigure_type; /* Hack to propagate type info to channel_reconfig int channel_reconfigure(struct channel *c, struct channel_config *cf) { + /* Touched by reconfiguration */ + c->stale = 0; + /* FIXME: better handle these changes, also handle in_keep_filtered */ - if ((c->table != cf->table->table) || (cf->ra_mode && (c->ra_mode != cf->ra_mode))) + if ((c->table != cf->table->table) || + (cf->ra_mode && (c->ra_mode != cf->ra_mode)) || + (cf->in_keep != c->in_keep) || + cf->out_subprefix && c->out_subprefix && + !net_equal(cf->out_subprefix, c->out_subprefix) || + (!cf->out_subprefix != !c->out_subprefix)) return 0; /* Note that filter_same() requires arguments in (new, old) order */ @@ -1301,11 +1000,27 @@ channel_reconfigure(struct channel *c, struct channel_config *cf) // c->ra_mode = cf->ra_mode; c->merge_limit = cf->merge_limit; c->preference = cf->preference; + c->out_req.addr = c->out_subprefix = cf->out_subprefix; c->debug = cf->debug; c->in_req.trace_routes = c->out_req.trace_routes = c->debug | c->proto->debug; - c->in_keep_filtered = cf->in_keep_filtered; 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->channel->reconfigure && !c->channel->reconfigure(c, cf, &import_changed, &export_changed)) return 0; @@ -1348,7 +1063,7 @@ channel_reconfigure(struct channel *c, struct channel_config *cf) channel_request_reload(c); if (export_changed) - channel_request_table_feeding(c); + channel_request_feeding(c); done: CD(c, "Reconfigured"); @@ -1399,6 +1114,11 @@ proto_configure_channel(struct proto *p, struct channel **pc, struct channel_con static void proto_cleanup(struct proto *p) { + CALL(p->proto->cleanup, p); + + rfree(p->pool); + p->pool = NULL; + p->active = 0; proto_log_state_change(p); proto_rethink_goal(p); @@ -1409,13 +1129,13 @@ proto_loop_stopped(void *ptr) { struct proto *p = ptr; - ASSERT_DIE(birdloop_inside(&main_birdloop)); + birdloop_enter(&main_birdloop); + birdloop_free(p->loop); p->loop = &main_birdloop; - p->pool = NULL; - p->event->list = NULL; - proto_cleanup(p); + + birdloop_leave(&main_birdloop); } static void @@ -1425,8 +1145,7 @@ proto_event(void *ptr) if (p->do_stop) { - if (p->proto == &proto_unix_iface) - if_flush_ifaces(p); + iface_unsubscribe(&p->iface_sub); p->do_stop = 0; } @@ -1435,11 +1154,7 @@ proto_event(void *ptr) if (p->loop != &main_birdloop) birdloop_stop_self(p->loop, proto_loop_stopped, p); else - { - rp_free(p->pool, proto_pool); - p->pool = NULL; proto_cleanup(p); - } } @@ -1499,24 +1214,15 @@ proto_start(struct proto *p) DBG("Kicking %s up\n", p->name); PD(p, "Starting"); - int ns = strlen("Protocol ") + strlen(p->cf->name) + 1; - void *nb = mb_alloc(proto_pool, ns); - ASSERT_DIE(ns - 1 == bsnprintf(nb, ns, "Protocol %s", p->cf->name)); + p->pool = rp_newf(proto_pool, "Protocol %s", p->cf->name); if (graceful_restart_state == GRS_INIT) p->gr_recovery = 1; - if (p->cf->loop_order == DOMAIN_ORDER(the_bird)) - p->pool = rp_new(proto_pool, &main_birdloop, nb); - else - { - p->loop = birdloop_new(proto_pool, p->cf->loop_order, nb); - p->pool = birdloop_pool(p->loop); - } - - p->event->list = proto_event_list(p); + if (p->cf->loop_order != DOMAIN_ORDER(the_bird)) + p->loop = birdloop_new(p->pool, p->cf->loop_order, p->pool->name, p->cf->loop_max_latency); - mb_move(nb, p->pool); + p->iface_sub.target = proto_event_list(p); PROTO_LOCKED_FROM_MAIN(p) proto_notify_state(p, (p->proto->start ? p->proto->start(p) : PS_UP)); @@ -1674,7 +1380,7 @@ proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config (nc->vrf != oc->vrf)) return 0; - p->name = nc->name; + p->sources.name = p->name = nc->name; p->debug = nc->debug; p->mrtdump = nc->mrtdump; reconfigure_type = type; @@ -1735,6 +1441,8 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty p = oc->proto; sym = cf_find_symbol(new, oc->name); + struct birdloop *proto_loop = PROTO_ENTER_FROM_MAIN(p); + /* Handle dynamic protocols */ if (!sym && oc->parent && !new->shutdown) { @@ -1760,13 +1468,10 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty nc->proto = p; /* We will try to reconfigure protocol p */ - if (!force_reconfig) + if (!force_reconfig && proto_reconfigure(p, oc, nc, type)) { - int ok; - PROTO_LOCKED_FROM_MAIN(p) - ok = proto_reconfigure(p, oc, nc, type); - if (ok) - continue; + PROTO_LEAVE_FROM_MAIN(proto_loop); + continue; } if (nc->parent) @@ -1805,6 +1510,8 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty } p->reconfiguring = 1; + PROTO_LEAVE_FROM_MAIN(proto_loop); + config_add_obstacle(old); proto_rethink_goal(p); } @@ -2088,7 +1795,7 @@ protos_dump_all(void) debug("Protocols:\n"); struct proto *p; - WALK_LIST(p, proto_list) + WALK_LIST(p, proto_list) PROTO_LOCKED_FROM_MAIN(p) { #define DPF(x) (p->x ? " " #x : "") debug(" protocol %s (%p) state %s with %d active channels flags: %s%s%s%s\n", @@ -2107,20 +1814,6 @@ protos_dump_all(void) debug("\tChannel state: %s/%s/%s\n", c_states[c->channel_state], c->in_req.hook ? rt_import_state_name(rt_import_get_state(c->in_req.hook)) : "-", c->out_req.hook ? rt_export_state_name(rt_export_get_state(c->out_req.hook)) : "-"); - if (c->in_table) - { - debug("\tInput aux table:\n"); - rt_dump_hooks(c->in_table->tab); - rt_dump(c->in_table->tab); - debug("\tEnd of input aux table.\n"); - } - if (c->out_table) - { - debug("\tOutput aux table:\n"); - rt_dump_hooks(c->in_table->tab); - rt_dump(c->in_table->tab); - debug("\tEnd of output aux table.\n"); - } } if (p->proto->dump && (p->proto_state != PS_DOWN)) @@ -2140,14 +1833,13 @@ void proto_build(struct protocol *p) { add_tail(&protocol_list, &p->n); - ASSERT(p->class); - ASSERT(!class_to_protocol[p->class]); - class_to_protocol[p->class] = p; } /* FIXME: convert this call to some protocol hook */ extern void bfd_init_all(void); +void protos_build_gen(void); + /** * protos_build - build a protocol list * @@ -2160,46 +1852,9 @@ extern void bfd_init_all(void); void protos_build(void) { - init_list(&proto_list); - init_list(&protocol_list); + protos_build_gen(); - proto_build(&proto_device); -#ifdef CONFIG_RADV - proto_build(&proto_radv); -#endif -#ifdef CONFIG_RIP - proto_build(&proto_rip); -#endif -#ifdef CONFIG_STATIC - proto_build(&proto_static); -#endif -#ifdef CONFIG_MRT - proto_build(&proto_mrt); -#endif -#ifdef CONFIG_OSPF - proto_build(&proto_ospf); -#endif -#ifdef CONFIG_PIPE - proto_build(&proto_pipe); -#endif -#ifdef CONFIG_BGP - proto_build(&proto_bgp); -#endif -#ifdef CONFIG_BFD - proto_build(&proto_bfd); - bfd_init_all(); -#endif -#ifdef CONFIG_BABEL - proto_build(&proto_babel); -#endif -#ifdef CONFIG_RPKI - proto_build(&proto_rpki); -#endif -#ifdef CONFIG_PERF - proto_build(&proto_perf); -#endif - - proto_pool = rp_new(&root_pool, &main_birdloop, "Protocols"); + proto_pool = rp_new(&root_pool, "Protocols"); } @@ -2207,29 +1862,40 @@ protos_build(void) int proto_restart; static void -proto_shutdown_loop(void *data UNUSED) +proto_restart_event_hook(void *_p) { - struct proto *p, *p_next; + struct proto *p = _p; + if (!p->down_sched) + return; - WALK_LIST_DELSAFE(p, p_next, proto_list) - if (p->down_sched) - { - proto_restart = (p->down_sched == PDS_RESTART); + proto_restart = (p->down_sched == PDS_RESTART); + p->disabled = 1; + proto_rethink_goal(p); - p->disabled = 1; - proto_rethink_goal(p); - if (proto_restart) - { - p->disabled = 0; - proto_rethink_goal(p); - } - } + p->restart_event = NULL; + p->restart_timer = NULL; + + if (proto_restart) + /* No need to call proto_rethink_goal() here again as the proto_cleanup() routine will + * call it after the protocol stops ... and both these routines are fixed to main_birdloop. + */ + p->disabled = 0; } -static event proto_schedule_down_event = { - .hook = proto_shutdown_loop, - .list = &global_event_list, -}; +static void +proto_send_restart_event(struct proto *p) +{ + if (!p->restart_event) + p->restart_event = ev_new_init(p->pool, proto_restart_event_hook, p); + + ev_send(&global_event_list, p->restart_event); +} + +static void +proto_send_restart_event_from_timer(struct timer *t) +{ + proto_send_restart_event((struct proto *) t->data); +} static inline void proto_schedule_down(struct proto *p, byte restart, byte code) @@ -2244,7 +1910,20 @@ proto_schedule_down(struct proto *p, byte restart, byte code) p->down_sched = restart ? PDS_RESTART : PDS_DISABLE; p->down_code = code; - ev_send_self(&proto_schedule_down_event); + if (!restart) + { + if (p->restart_timer && tm_active(p->restart_timer)) + tm_stop(p->restart_timer); + + proto_send_restart_event(p); + } + else + { + if (!p->restart_timer) + p->restart_timer = tm_new_init(p->pool, proto_send_restart_event_from_timer, p, 0, 0); + + tm_start_max_in(p->restart_timer, 250 MS, p->loop); + } } /** @@ -2386,23 +2065,19 @@ static struct rte_owner_class default_rte_owner_class; static inline void proto_do_start(struct proto *p) { - ASSERT_DIE(birdloop_inside(p->loop)); - p->active = 1; - rt_init_sources(&p->sources, p->name, proto_work_list(p)); + rt_init_sources(&p->sources, p->name, proto_event_list(p)); if (!p->sources.class) p->sources.class = &default_rte_owner_class; if (!p->cf->late_if_feed) - if_feed_baby(p); + iface_subscribe(&p->iface_sub); } static void proto_do_up(struct proto *p) { - ASSERT_DIE(birdloop_inside(p->loop)); - if (!p->main_source) p->main_source = rt_get_source(p, 0); // Locked automaticaly @@ -2410,7 +2085,7 @@ proto_do_up(struct proto *p) proto_start_channels(p); if (p->cf->late_if_feed) - if_feed_baby(p); + iface_subscribe(&p->iface_sub); } static inline void @@ -2435,18 +2110,17 @@ proto_do_stop(struct proto *p) rt_destroy_sources(&p->sources, p->event); p->do_stop = 1; - proto_send_event(p); + proto_send_event(p, p->event); } static void proto_do_down(struct proto *p) { p->down_code = 0; - neigh_prune(p); /* Shutdown is finished in the protocol event */ if (proto_is_done(p)) - proto_send_event(p); + proto_send_event(p, p->event); } @@ -2552,18 +2226,18 @@ channel_show_stats(struct channel *c) u32 in_routes = c->in_limit.count; u32 out_routes = c->out_limit.count; - if (c->in_keep_filtered) + if (c->in_keep) cli_msg(-1006, " Routes: %u imported, %u filtered, %u exported, %u preferred", in_routes, (rx_routes - in_routes), out_routes, SRI(pref)); else cli_msg(-1006, " Routes: %u imported, %u exported, %u preferred", in_routes, out_routes, SRI(pref)); - cli_msg(-1006, " Route change stats: received rejected filtered ignored limited accepted"); - cli_msg(-1006, " Import updates: %10u %10u %10u %10u %10u %10u", + cli_msg(-1006, " Route change stats: received rejected filtered ignored RX limit IN limit accepted"); + cli_msg(-1006, " Import updates: %10u %10u %10u %10u %10u %10u %10u", SCI(updates_received), SCI(updates_invalid), SCI(updates_filtered), SRI(updates_ignored), - SCI(updates_limited_rx) + SCI(updates_limited_in), + SCI(updates_limited_rx), SCI(updates_limited_in), SRI(updates_accepted)); cli_msg(-1006, " Import withdraws: %10u %10u --- %10u --- %10u", SCI(withdraws_received), SCI(withdraws_invalid), |