diff options
Diffstat (limited to 'nest/proto.c')
-rw-r--r-- | nest/proto.c | 426 |
1 files changed, 343 insertions, 83 deletions
diff --git a/nest/proto.c b/nest/proto.c index 09582d2e..cf448fd9 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -47,7 +47,7 @@ static char *c_states[] = { "DOWN", "START", "UP", "STOP", "RESTART" }; extern struct protocol proto_unix_iface; -static void channel_request_reload(struct channel *c); +static void channel_aux_request_refeed(struct channel_aux_table *cat); static void proto_shutdown_loop(timer *); static void proto_rethink_goal(struct proto *p); static char *proto_state_name(struct proto *p); @@ -88,7 +88,9 @@ channel_export_log_state_change(struct rt_export_request *req, u8 state) switch (state) { case TES_FEEDING: - if (c->proto->feed_begin) + if (c->out_table) + rt_refresh_begin(&c->out_table->push); + else if (c->proto->feed_begin) c->proto->feed_begin(c, !c->refeeding); break; case TES_READY: @@ -179,6 +181,7 @@ proto_find_channel_by_name(struct proto *p, const char *n) } 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); @@ -295,14 +298,10 @@ 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" : ""); + CD(c, "Reload triggered by RPKI change"); - if (!active) - channel_request_reload(c); - else - c->reload_pending = 1; + channel_request_reload(c); } static void @@ -444,7 +443,6 @@ channel_start_import(struct channel *c) .dump_req = channel_dump_import_req, .log_state_change = channel_import_log_state_change, .preimport = channel_preimport, - .rte_modify = c->proto->rte_modify, }; ASSERT(c->channel_state == CS_UP); @@ -463,7 +461,8 @@ channel_start_export(struct channel *c) { if (c->out_req.hook) { - log(L_WARN "%s.%s: Attempted to start channel's already started export", c->proto->name, c->name); + c->restart_export = 1; + log(L_WARN "%s.%s: Fast channel export restart", c->proto->name, c->name); return; } @@ -514,7 +513,7 @@ channel_check_stopped(struct channel *c) switch (c->channel_state) { case CS_STOP: - if (c->out_req.hook || c->in_req.hook) + if (c->out_req.hook || c->in_req.hook || c->out_table || c->in_table) return; channel_set_state(c, CS_DOWN); @@ -541,9 +540,6 @@ channel_import_stopped(struct rt_import_request *req) req->hook = NULL; - if (c->in_table) - rt_prune_sync(c->in_table, 1); - mb_free(c->in_req.name); c->in_req.name = NULL; @@ -566,14 +562,16 @@ channel_export_stopped(struct rt_export_request *req) return; } - /* Free the routes from out_table */ - if (c->out_table) - rt_prune_sync(c->out_table, 1); - mb_free(c->out_req.name); c->out_req.name = NULL; - channel_check_stopped(c); + if (c->restart_export) + { + c->restart_export = 0; + channel_start_export(c); + } + else + channel_check_stopped(c); } static void @@ -595,72 +593,296 @@ channel_feed_end(struct channel *c) return; } - if (c->proto->feed_end) + if (c->out_table) + rt_refresh_end(&c->out_table->push); + else 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; else - c->refeeding = 0; + bug("Channel aux table must be in_table or out_table"); } -/* Called by protocol for reload from in_table */ -void -channel_schedule_reload(struct channel *c) +static void +channel_aux_stopped(void *data) { - ASSERT(c->in_req.hook); + struct channel_aux_table *cat = data; + struct channel *c = cat->c; + + if (channel_aux_imex(cat)) + c->out_table = NULL; + else + c->in_table = NULL; + + mb_free(cat); + return channel_check_stopped(c); +} + +static void +channel_aux_import_stopped(struct rt_import_request *req) +{ + struct channel_aux_table *cat = SKIP_BACK(struct channel_aux_table, push, req); + ASSERT_DIE(cat->stop); +} + +static void +channel_aux_export_stopped(struct rt_export_request *req) +{ + struct channel_aux_table *cat = SKIP_BACK(struct channel_aux_table, push, req); + req->hook = NULL; + + if (cat->refeed_pending && !cat->stop) + { + cat->refeed_pending = 0; + rt_request_export(cat->tab, req); + } + else + ASSERT_DIE(cat->stop); +} + +static void +channel_aux_stop(struct channel_aux_table *cat) +{ + cat->stop = 1; - rt_reload_channel_abort(c); - ev_schedule_work(c->reload_event); + rt_stop_import(&cat->push, channel_aux_import_stopped); + rt_stop_export(&cat->get, channel_aux_export_stopped); + + rt_lock_table(cat->tab); + cat->tab->deleted = channel_aux_stopped; + cat->tab->del_data = cat; + rt_unlock_table(cat->tab); } static void -channel_reload_loop(void *ptr) +channel_push_log_state_change(struct rt_import_request *req, u8 state) { - struct channel *c = ptr; + 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)); +} - /* Start reload */ - if (!c->reload_active) - c->reload_pending = 0; +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)); - if (!rt_reload_channel(c)) + switch (state) { - ev_schedule_work(c->reload_event); + 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 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); + + if (!rpe->new && !rpe->old) + return; + + rte n0; + struct rte_src *src = rpe->new ? rpe->new->rte.src : rpe->old->rte.src; + rte_update_direct(cat->c, net, RTES_CLONE(rpe->new, &n0), src); +} + +static void +channel_in_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); + + if (!rpe->new && !rpe->old) return; + + rte n0; + struct rte_src *src = rpe->old_best ? rpe->old_best->rte.src : rpe->new_best->rte.src; + rte_update_direct(cat->c, net, RTES_CLONE(rpe->new_best, &n0), src); +} + +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++) + { + rte n0 = *feed[i]; + rte_update_direct(cat->c, net, &n0, n0.src); } +} - /* Restart reload */ - if (c->reload_pending) - channel_request_reload(c); +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; + do_rt_notify_direct(cat->c, net, RTES_CLONE(rpe->new, &n0), RTES_OR_NULL(rpe->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; + do_rt_notify_direct(cat->c, net, RTES_CLONE(rpe->new_best, &n0), RTES_OR_NULL(rpe->old_best)); +} + +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) +{ + 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); + } } /* Called by protocol to activate in_table */ void -channel_setup_in_table(struct channel *c) +channel_setup_in_table(struct channel *c, int best) { - struct rtable_config *cf = mb_allocz(c->proto->pool, sizeof(struct rtable_config)); + 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); - cf->name = "import"; - cf->addr_type = c->net_type; - cf->internal = 1; + cat->tab_cf.name = cat->name; + cat->tab_cf.addr_type = c->net_type; + + c->in_table = &cat->cat; + c->in_table->push = (struct rt_import_request) { + .name = cat->name, + .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, + .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 = rt_setup(c->proto->pool, cf); + c->in_table->c = c; + c->in_table->tab = rt_setup(c->proto->pool, &cat->tab_cf); + self_link(&c->in_table->tab->n); - c->reload_event = ev_new_init(c->proto->pool, channel_reload_loop, c); + 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) { - struct rtable_config *cf = mb_allocz(c->proto->pool, sizeof(struct rtable_config)); - cf->name = "export"; - cf->addr_type = c->net_type; - cf->internal = 1; + 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); - c->out_table = rt_setup(c->proto->pool, cf); + cat->tab_cf.name = cat->name; + cat->tab_cf.addr_type = c->net_type; + + c->out_table = &cat->cat; + c->out_table->push = (struct rt_import_request) { + .name = cat->name, + .trace_routes = c->debug | c->proto->debug, + .dump_req = channel_out_push_dump_req, + .log_state_change = channel_push_log_state_change, + }; + c->out_table->get = (struct rt_export_request) { + .name = cat->name, + .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); + self_link(&c->out_table->tab->n); + + 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) +{ + cat->refeed_pending = 1; + rt_stop_export(&cat->get, channel_aux_export_stopped); +} static void channel_do_start(struct channel *c) @@ -686,16 +908,12 @@ channel_do_up(struct channel *c) static void channel_do_pause(struct channel *c) { - /* Need to abort feeding */ - if (c->reload_event) - { - ev_postpone(c->reload_event); - rt_reload_channel_abort(c); - } - /* Stop export */ if (c->out_req.hook) + { rt_stop_export(&c->out_req, channel_export_stopped); + c->refeeding = 0; + } channel_roa_unsubscribe_all(c); @@ -706,6 +924,13 @@ channel_do_pause(struct channel *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) rt_stop_import(&c->in_req, channel_import_stopped); @@ -716,16 +941,13 @@ channel_do_stop(struct channel *c) CALL(c->channel->shutdown, c); - /* This have to be done in here, as channel pool is freed before channel_do_down() */ - c->in_table = NULL; - c->reload_event = NULL; - c->out_table = NULL; + channel_roa_unsubscribe_all(c); } static void channel_do_down(struct channel *c) { - ASSERT(!c->reload_active); + ASSERT(!c->out_req.hook && !c->in_req.hook && !c->out_table && !c->in_table); c->proto->active_channels--; @@ -733,14 +955,12 @@ channel_do_down(struct channel *c) memset(&c->import_stats, 0, sizeof(struct channel_import_stats)); memset(&c->export_stats, 0, sizeof(struct channel_export_stats)); - c->in_table = NULL; - c->reload_event = NULL; - 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); + /* This have to be done in here, as channel pool is freed before channel_do_down() */ + bmap_free(&c->export_map); + bmap_free(&c->export_reject_map); + /* Schedule protocol shutddown */ if (proto_is_done(c->proto)) ev_schedule(c->proto->event); @@ -769,7 +989,7 @@ channel_set_state(struct channel *c, uint state) break; case CS_UP: - ASSERT(cs == CS_DOWN || cs == CS_START); + ASSERT(cs == CS_DOWN || cs == CS_START || cs == CS_PAUSE); if (cs == CS_DOWN) channel_do_start(c); @@ -819,8 +1039,8 @@ 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. */ -void -channel_request_feeding(struct channel *c) +static void +channel_request_table_feeding(struct channel *c) { ASSERT(c->out_req.hook); @@ -828,7 +1048,18 @@ channel_request_feeding(struct channel *c) rt_stop_export(&c->out_req, channel_export_stopped); } -static void +void +channel_request_feeding(struct channel *c) +{ + ASSERT(c->out_req.hook); + + if (c->out_table) + channel_aux_request_refeed(c->out_table); + else + channel_request_table_feeding(c); +} + +void channel_request_reload(struct channel *c) { ASSERT(c->in_req.hook); @@ -836,14 +1067,29 @@ channel_request_reload(struct channel *c) CD(c, "Reload requested"); - c->proto->reload_routes(c); + if (c->in_table) + channel_aux_request_refeed(c->in_table); + else + c->proto->reload_routes(c); +} - /* - * Should this be done before reload_routes() hook? - * Perhaps, but routes are updated asynchronously. - */ - channel_reset_limit(c, &c->rx_limit, PLD_RX); - channel_reset_limit(c, &c->in_limit, PLD_IN); +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 = { @@ -1001,7 +1247,7 @@ channel_reconfigure(struct channel *c, struct channel_config *cf) channel_request_reload(c); if (export_changed) - channel_request_feeding(c); + channel_request_table_feeding(c); done: CD(c, "Reconfigured"); @@ -1714,7 +1960,7 @@ protos_dump_all(void) WALK_LIST(p, proto_list) { #define DPF(x) (p->x ? " " #x : "") - debug(" protocol %s (%p) state %s with %d active channels flags: %s%s%s%s%s\n", + debug(" protocol %s (%p) state %s with %d active channels flags: %s%s%s%s\n", p->name, p, p_states[p->proto_state], p->active_channels, DPF(disabled), DPF(active), DPF(do_start), DPF(do_stop), DPF(reconfiguring)); #undef DPF @@ -1730,6 +1976,20 @@ 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)) @@ -2151,11 +2411,11 @@ channel_show_stats(struct channel *c) 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 RX limit IN limit accepted"); - cli_msg(-1006, " Import updates: %10u %10u %10u %10u %10u %10u %10u", + cli_msg(-1006, " Route change stats: received rejected filtered ignored limited accepted"); + cli_msg(-1006, " Import updates: %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), |