diff options
author | Maria Matejka <mq@ucw.cz> | 2021-10-06 15:10:33 +0200 |
---|---|---|
committer | Maria Matejka <mq@ucw.cz> | 2021-11-22 19:05:43 +0100 |
commit | 44f26c49f966ca842ff9af55468de0b98c44b73e (patch) | |
tree | 058c474e3f82ac0a26b77cf1c62f2ffa07212145 | |
parent | f81702b7e44ba7f2b264fe14f0f4e1e30913e475 (diff) |
Special table hooks rectified.
* internal tables are now more standalone, having their own import and
export hooks
* route refresh/reload uses stale counter instead of stale flag,
allowing to drop walking the table at the beginning
* route modify (by BGP LLGR) is now done by a special refeed hook,
reimporting the modified routes directly without filters
-rw-r--r-- | lib/lists.c | 9 | ||||
-rw-r--r-- | lib/lists.h | 1 | ||||
-rw-r--r-- | nest/config.Y | 4 | ||||
-rw-r--r-- | nest/proto.c | 426 | ||||
-rw-r--r-- | nest/protocol.h | 23 | ||||
-rw-r--r-- | nest/route.h | 35 | ||||
-rw-r--r-- | nest/rt-attr.c | 3 | ||||
-rw-r--r-- | nest/rt-show.c | 7 | ||||
-rw-r--r-- | nest/rt-table.c | 593 | ||||
-rw-r--r-- | proto/bgp/attrs.c | 50 | ||||
-rw-r--r-- | proto/bgp/bgp.c | 94 | ||||
-rw-r--r-- | proto/bgp/bgp.h | 3 | ||||
-rw-r--r-- | proto/bgp/packets.c | 2 | ||||
-rw-r--r-- | proto/rpki/packets.c | 8 | ||||
-rw-r--r-- | sysdep/unix/krt.c | 256 | ||||
-rw-r--r-- | sysdep/unix/krt.h | 4 |
16 files changed, 677 insertions, 841 deletions
diff --git a/lib/lists.c b/lib/lists.c index 200576cf..fe2b692b 100644 --- a/lib/lists.c +++ b/lib/lists.c @@ -109,6 +109,15 @@ add_head(list *l, node *n) l->head = n; } +LIST_INLINE void +self_link(node *n) +{ + ASSUME(n->prev == NULL); + ASSUME(n->next == NULL); + + n->prev = n->next = n; +} + /** * insert_node - insert a node to a list * @n: a new list node diff --git a/lib/lists.h b/lib/lists.h index 479f4ed1..64b4a981 100644 --- a/lib/lists.h +++ b/lib/lists.h @@ -78,6 +78,7 @@ typedef union list { /* In fact two overlayed nodes */ #define LIST_INLINE void add_tail(list *, node *); void add_head(list *, node *); +void self_link(node *); void rem_node(node *); void add_tail_list(list *, list *); void init_list(list *); diff --git a/nest/config.Y b/nest/config.Y index 29d6b0db..a56b25be 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -644,12 +644,12 @@ r_args: } | r_args IMPORT TABLE channel_arg { if (!$4->in_table) cf_error("No import table in channel %s.%s", $4->proto->name, $4->name); - rt_show_add_table($$, $4->in_table); + rt_show_add_table($$, $4->in_table->tab); $$->tables_defined_by = RSD_TDB_DIRECT; } | r_args EXPORT TABLE channel_arg { if (!$4->out_table) cf_error("No export table in channel %s.%s", $4->proto->name, $4->name); - rt_show_add_table($$, $4->out_table); + rt_show_add_table($$, $4->out_table->tab); $$->tables_defined_by = RSD_TDB_DIRECT; } | r_args FILTER filter { 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), diff --git a/nest/protocol.h b/nest/protocol.h index 7447cbf0..f9996b18 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -207,7 +207,6 @@ struct proto { int (*rte_recalculate)(struct rtable *, struct network *, struct rte *, struct rte *, struct rte *); int (*rte_better)(struct rte *, struct rte *); int (*rte_mergable)(struct rte *, struct rte *); - struct rte *(*rte_modify)(struct rte *, struct linpool *); void (*rte_insert)(struct network *, struct rte *); void (*rte_remove)(struct network *, struct rte *); u32 (*rte_igp_metric)(struct rte *); @@ -544,24 +543,29 @@ struct channel { u8 reloadable; /* Hook reload_routes() is allowed on the channel */ u8 gr_lock; /* Graceful restart mechanism should wait for this channel */ u8 gr_wait; /* Route export to channel is postponed until graceful restart */ + u8 restart_export; /* Route export should restart as soon as it stops */ btime last_state_change; /* Time of last state transition */ - struct rtable *in_table; /* Internal table for received routes */ - struct event *reload_event; /* Event responsible for reloading from in_table */ - struct fib_iterator reload_fit; /* FIB iterator in in_table used during reloading */ - struct rte_storage *reload_next_rte; /* Route iterator in in_table used during reloading */ - u8 reload_active; /* Iterator reload_fit is linked */ + struct channel_aux_table *in_table; /* Internal table for received routes */ u8 reload_pending; /* Reloading and another reload is scheduled */ u8 refeed_pending; /* Refeeding and another refeed is scheduled */ u8 rpki_reload; /* RPKI changes trigger channel reload */ - struct rtable *out_table; /* Internal table for exported routes */ + struct channel_aux_table *out_table; /* Internal table for exported routes */ list roa_subscriptions; /* List of active ROA table subscriptions based on filters roa_check() */ }; +struct channel_aux_table { + struct channel *c; + struct rt_import_request push; + struct rt_export_request get; + rtable *tab; + u8 stop; + u8 refeed_pending; +}; /* * Channel states @@ -627,7 +631,7 @@ struct channel *proto_add_channel(struct proto *p, struct channel_config *cf); int proto_configure_channel(struct proto *p, struct channel **c, struct channel_config *cf); void channel_set_state(struct channel *c, uint state); -void channel_setup_in_table(struct channel *c); +void channel_setup_in_table(struct channel *c, int best); void channel_setup_out_table(struct channel *c); void channel_schedule_reload(struct channel *c); @@ -636,6 +640,9 @@ static inline void channel_open(struct channel *c) { channel_set_state(c, CS_UP) static inline void channel_close(struct channel *c) { channel_set_state(c, CS_STOP); } void channel_request_feeding(struct channel *c); +void channel_request_reload(struct channel *c); +void channel_refresh_begin(struct channel *c); +void channel_refresh_end(struct channel *c); void *channel_config_new(const struct channel_class *cc, const char *name, uint net_type, struct proto_config *proto); void *channel_config_get(const struct channel_class *cc, const char *name, uint net_type, struct proto_config *proto); int channel_reconfigure(struct channel *c, struct channel_config *cf); diff --git a/nest/route.h b/nest/route.h index d0568133..cb66be2a 100644 --- a/nest/route.h +++ b/nest/route.h @@ -150,7 +150,6 @@ struct rtable_config { int gc_max_ops; /* Maximum number of operations before GC is run */ int gc_min_time; /* Minimum time between two consecutive GC runs */ byte sorted; /* Routes of network are sorted according to rte_better() */ - byte internal; /* Internal table of a protocol */ btime min_settle_time; /* Minimum settle time for notifications */ btime max_settle_time; /* Maximum settle time for notifications */ }; @@ -172,10 +171,8 @@ typedef struct rtable { struct hmap id_map; struct hostcache *hostcache; struct rtable_config *config; /* Configuration of this table */ - struct config *deleted; /* Table doesn't exist in current configuration, - * delete as soon as use_count becomes 0 and remove - * obstacle from this routing table. - */ + void (*deleted)(void *); /* Table should free itself. Call this when it is done. */ + void *del_data; struct event *rt_event; /* Routing table event */ btime last_rt_change; /* Last time when route changed */ btime base_settle_time; /* Start time of rtable settling interval */ @@ -184,7 +181,6 @@ typedef struct rtable { byte prune_state; /* Table prune state, 1 -> scheduled, 2-> running */ byte hcu_scheduled; /* Hostcache update is scheduled */ byte nhu_state; /* Next Hop Update state */ - byte internal; /* This table is internal for some other object */ struct fib_iterator prune_fit; /* Rtable prune FIB iterator */ struct fib_iterator nhu_fit; /* Next Hop Update FIB iterator */ struct tbf rl_pipe; /* Rate limiting token buffer for pipe collisions */ @@ -249,6 +245,7 @@ typedef struct rte { u8 generation; /* If this route import is based on other previously exported route, this value should be 1 + MAX(generation of the parent routes). Otherwise the route is independent and this value is zero. */ + u8 stale_cycle; /* Auxiliary value for route refresh */ } rte; struct rte_storage { @@ -256,13 +253,11 @@ struct rte_storage { struct rte rte; /* Route data */ }; -#define RTE_COPY(r, l) ((r) ? (((*(l)) = (r)->rte), (l)) : NULL) -#define RTE_OR_NULL(r) ((r) ? &((r)->rte) : NULL) +#define RTES_CLONE(r, l) ((r) ? (((*(l)) = (r)->rte), (l)) : NULL) +#define RTES_OR_NULL(r) ((r) ? &((r)->rte) : NULL) #define REF_FILTERED 2 /* Route is rejected by import filter */ -#define REF_STALE 4 /* Route is stale in a refresh cycle */ -#define REF_DISCARD 8 /* Route is scheduled for discard */ -#define REF_MODIFY 16 /* Route is scheduled for modify */ +#define REF_USE_STALE 4 /* Do not reset route's stale_cycle to the actual value */ /* Route is valid for propagation (may depend on other flags in the future), accepts NULL */ static inline int rte_is_valid(const rte *r) { return r && !(r->flags & REF_FILTERED); } @@ -283,7 +278,6 @@ struct rt_import_request { /* Preimport is called when the @new route is just-to-be inserted, replacing @old. * Return a route (may be different or modified in-place) to continue or NULL to withdraw. */ struct rte *(*preimport)(struct rt_import_request *req, struct rte *new, struct rte *old); - struct rte *(*rte_modify)(struct rte *, struct linpool *); }; struct rt_import_hook { @@ -303,6 +297,10 @@ struct rt_import_hook { btime last_state_change; /* Time of last state transition */ u8 import_state; /* IS_* */ + u8 stale_set; /* Set this stale_cycle to imported routes */ + u8 stale_valid; /* Routes with this stale_cycle and bigger are considered valid */ + u8 stale_pruned; /* Last prune finished when this value was set at stale_valid */ + u8 stale_pruning; /* Last prune started when this value was set at stale_valid */ void (*stopped)(struct rt_import_request *); /* Stored callback when import is stopped */ }; @@ -455,9 +453,9 @@ void *net_route(rtable *tab, const net_addr *n); int net_roa_check(rtable *tab, const net_addr *n, u32 asn); int rt_examine(rtable *t, net_addr *a, struct channel *c, const struct filter *filter); rte *rt_export_merged(struct channel *c, rte ** feed, uint count, linpool *pool, int silent); -void rt_refresh_begin(rtable *t, struct rt_import_request *); -void rt_refresh_end(rtable *t, struct rt_import_request *); -void rt_modify_stale(rtable *t, struct rt_import_request *); + +void rt_refresh_begin(struct rt_import_request *); +void rt_refresh_end(struct rt_import_request *); void rt_schedule_prune(rtable *t); void rte_dump(struct rte_storage *); void rte_free(struct rte_storage *, rtable *); @@ -466,15 +464,9 @@ void rt_dump(rtable *); void rt_dump_all(void); void rt_dump_hooks(rtable *); void rt_dump_hooks_all(void); -int rt_reload_channel(struct channel *c); -void rt_reload_channel_abort(struct channel *c); -void rt_refeed_channel(struct channel *c); void rt_prune_sync(rtable *t, int all); -int rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *src); -int rte_update_out(struct channel *c, const net_addr *n, rte *new, const rte *old, struct rte_storage **old_exported); struct rtable_config *rt_new_table(struct symbol *s, uint addr_type); - /* Default limit for ECMP next hops, defined in sysdep code */ extern const int rt_default_ecmp; @@ -789,6 +781,7 @@ void rta__free(rta *r); static inline void rta_free(rta *r) { if (r && !--r->uc) rta__free(r); } rta *rta_do_cow(rta *o, linpool *lp); static inline rta * rta_cow(rta *r, linpool *lp) { return rta_is_cached(r) ? rta_do_cow(r, lp) : r; } +static inline void rta_uncache(rta *r) { r->cached = 0; r->uc = 0; } void rta_dump(rta *); void rta_dump_all(void); void rta_show(struct cli *, rta *); diff --git a/nest/rt-attr.c b/nest/rt-attr.c index 1bece201..77fd3c3b 100644 --- a/nest/rt-attr.c +++ b/nest/rt-attr.c @@ -1245,8 +1245,7 @@ rta_do_cow(rta *o, linpool *lp) memcpy(*nhn, nho, nexthop_size(nho)); nhn = &((*nhn)->next); } - r->cached = 0; - r->uc = 0; + rta_uncache(r); return r; } diff --git a/nest/rt-show.c b/nest/rt-show.c index 235d72e4..d942b8e1 100644 --- a/nest/rt-show.c +++ b/nest/rt-show.c @@ -95,7 +95,10 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, int primary } if (d->verbose) + { + cli_printf(c, -1008, "\tInternal route ID: %uL %uG %uS", e->src->private_id, e->src->global_id, e->stale_cycle); rta_show(c, a); + } } static uint @@ -103,7 +106,7 @@ rte_feed_count(net *n) { uint count = 0; for (struct rte_storage *e = n->routes; e; e = e->next) - if (rte_is_valid(RTE_OR_NULL(e))) + if (rte_is_valid(RTES_OR_NULL(e))) count++; return count; } @@ -113,7 +116,7 @@ rte_feed_obtain(net *n, rte **feed, uint count) { uint i = 0; for (struct rte_storage *e = n->routes; e; e = e->next) - if (rte_is_valid(RTE_OR_NULL(e))) + if (rte_is_valid(RTES_OR_NULL(e))) { ASSERT_DIE(i < count); feed[i++] = &e->rte; diff --git a/nest/rt-table.c b/nest/rt-table.c index e7ff2816..2f480992 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -50,7 +50,6 @@ pool *rt_table_pool; static linpool *rte_update_pool; list routing_tables; -list deleted_routing_tables; static void rt_free_hostcache(rtable *tab); static void rt_notify_hostcache(rtable *tab, net *net); @@ -386,9 +385,11 @@ rte_mergable(rte *pri, rte *sec) static void rte_trace(const char *name, const rte *e, int dir, const char *msg) { - log(L_TRACE "%s %c %s %N %uL %uG %s", - name, dir, msg, e->net, e->src->private_id, e->src->global_id, - rta_dest_name(e->attrs->dest)); + log(L_TRACE "%s %c %s %N src %uL %uG %uS %s%s", + name, dir, msg, e->net, + e->src->private_id, e->src->global_id, e->stale_cycle, + rta_dest_name(e->attrs->dest), + rte_is_filtered(e) ? " (filtered)" : ""); } static inline void @@ -427,7 +428,7 @@ rte_feed_count(net *n) { uint count = 0; for (struct rte_storage *e = n->routes; e; e = e->next) - if (rte_is_valid(RTE_OR_NULL(e))) + if (rte_is_valid(RTES_OR_NULL(e))) count++; return count; } @@ -437,7 +438,7 @@ rte_feed_obtain(net *n, struct rte **feed, uint count) { uint i = 0; for (struct rte_storage *e = n->routes; e; e = e->next) - if (rte_is_valid(RTE_OR_NULL(e))) + if (rte_is_valid(RTES_OR_NULL(e))) { ASSERT_DIE(i < count); feed[i++] = &e->rte; @@ -508,10 +509,11 @@ export_filter(struct channel *c, rte *rt, int silent) return export_filter_(c, rt, rte_update_pool, silent); } +void do_rt_notify_direct(struct channel *c, const net_addr *net, rte *new, const rte *old); + static void do_rt_notify(struct channel *c, const net_addr *net, rte *new, const rte *old) { - struct proto *p = c->proto; struct channel_export_stats *stats = &c->export_stats; if (c->refeeding && new) @@ -528,28 +530,31 @@ do_rt_notify(struct channel *c, const net_addr *net, rte *new, const rte *old) if (!new && old) CHANNEL_LIMIT_POP(c, OUT); + /* Store route export state */ + if (old) + bmap_clear(&c->export_map, old->id); + + if (new) + bmap_set(&c->export_map, new->id); + /* Apply export table */ - struct rte_storage *old_exported = NULL; if (c->out_table) - { - if (!rte_update_out(c, net, new, old, &old_exported)) - { - channel_rte_trace_out(D_ROUTES, c, new, "idempotent"); - return; - } - } + rte_import(&c->out_table->push, net, new, old ? old->src : new->src); + else + do_rt_notify_direct(c, net, new, old); +} + +void +do_rt_notify_direct(struct channel *c, const net_addr *net, rte *new, const rte *old) +{ + struct proto *p = c->proto; + struct channel_export_stats *stats = &c->export_stats; if (new) stats->updates_accepted++; else stats->withdraws_accepted++; - if (old) - bmap_clear(&c->export_map, old->id); - - if (new) - bmap_set(&c->export_map, new->id); - if (p->debug & D_ROUTES) { if (new && old) @@ -560,10 +565,7 @@ do_rt_notify(struct channel *c, const net_addr *net, rte *new, const rte *old) channel_rte_trace_out(D_ROUTES, c, old, "removed"); } - p->rt_notify(p, c, net, new, old_exported ? &old_exported->rte : old); - - if (c->out_table && old_exported) - rte_free(old_exported, c->out_table); + p->rt_notify(p, c, net, new, old); } static void @@ -784,7 +786,7 @@ rt_notify_optimal(struct rt_export_request *req, const net_addr *net, struct rt_ rte n0; if (rpe->new_best != rpe->old_best) - rt_notify_basic(c, net, RTE_COPY(rpe->new_best, &n0), RTE_OR_NULL(rpe->old_best)); + rt_notify_basic(c, net, RTES_CLONE(rpe->new_best, &n0), RTES_OR_NULL(rpe->old_best)); /* Drop the old stored rejection if applicable. * new->id == old->id happens when updating hostentries. */ @@ -799,7 +801,7 @@ rt_notify_any(struct rt_export_request *req, const net_addr *net, struct rt_pend rte n0; if (rpe->new != rpe->old) - rt_notify_basic(c, net, RTE_COPY(rpe->new, &n0), RTE_OR_NULL(rpe->old)); + rt_notify_basic(c, net, RTES_CLONE(rpe->new, &n0), RTES_OR_NULL(rpe->old)); /* Drop the old stored rejection if applicable. * new->id == old->id happens when updating hostentries. */ @@ -974,6 +976,10 @@ rte_recalculate(struct rt_import_hook *c, net *net, rte *new, struct rte_src *sr rte *old_best = old_best_stored ? &old_best_stored->rte : NULL; rte *old = NULL; + /* Set the stale cycle unless already set */ + if (new && !(new->flags & REF_USE_STALE)) + new->stale_cycle = c->stale_set; + /* Find and remove original route from the same protocol */ struct rte_storage **before_old = rte_find(net, src); @@ -999,8 +1005,7 @@ rte_recalculate(struct rt_import_hook *c, net *net, rte *new, struct rte_src *sr if (new && rte_same(old, new)) { /* No changes, ignore the new route and refresh the old one */ - - old->flags &= ~(REF_STALE | REF_DISCARD | REF_MODIFY); + old->stale_cycle = new->stale_cycle; if (!rte_is_filtered(new)) { @@ -1138,18 +1143,23 @@ rte_recalculate(struct rt_import_hook *c, net *net, rte *new, struct rte_src *sr new_stored->rte.id = old->id; } + _Bool nb = (new_stored == net->routes); + _Bool ob = (old_best == old); + /* Log the route change */ - if (new_ok) - rt_rte_trace_in(D_ROUTES, req, &new_stored->rte, new_stored == net->routes ? "added [best]" : "added"); - else if (old_ok) + if (new_ok && old_ok) { - if (old != old_best) - rt_rte_trace_in(D_ROUTES, req, old, "removed"); - else if (net->routes && rte_is_ok(&net->routes->rte)) - rt_rte_trace_in(D_ROUTES, req, old, "removed [replaced]"); - else - rt_rte_trace_in(D_ROUTES, req, old, "removed [sole]"); + const char *best_indicator[2][2] = { { "updated", "updated [-best]" }, { "updated [+best]", "updated [best]" } }; + rt_rte_trace_in(D_ROUTES, req, &new_stored->rte, best_indicator[nb][ob]); } + else if (new_ok) + rt_rte_trace_in(D_ROUTES, req, &new_stored->rte, + (!net->routes->next || !rte_is_ok(&net->routes->next->rte)) ? "added [sole]" : + nb ? "added [best]" : "added"); + else if (old_ok) + rt_rte_trace_in(D_ROUTES, req, old, + (!net->routes || !rte_is_ok(&net->routes->rte)) ? "removed [sole]" : + ob ? "removed [best]" : "removed"); /* Propagate the route change */ rte_announce(table, net, new_stored, old_stored, @@ -1197,12 +1207,15 @@ channel_preimport(struct rt_import_request *req, rte *new, rte *old) { struct channel *c = SKIP_BACK(struct channel, in_req, req); - if (new && !old) - if (CHANNEL_LIMIT_PUSH(c, RX)) - return NULL; + if (!c->in_table) + { + if (new && !old) + if (CHANNEL_LIMIT_PUSH(c, RX)) + return NULL; - if (!new && old) - CHANNEL_LIMIT_POP(c, RX); + if (!new && old) + CHANNEL_LIMIT_POP(c, RX); + } int new_in = new && !rte_is_filtered(new); int old_in = old && !rte_is_filtered(old); @@ -1223,7 +1236,22 @@ channel_preimport(struct rt_import_request *req, rte *new, rte *old) return new; } -static void rte_update_direct(struct channel *c, const net_addr *n, rte *new, struct rte_src *src); +rte * +channel_in_preimport(struct rt_import_request *req, rte *new, rte *old) +{ + struct channel_aux_table *cat = SKIP_BACK(struct channel_aux_table, push, req); + + if (new && !old) + if (CHANNEL_LIMIT_PUSH(cat->c, RX)) + return NULL; + + if (!new && old) + CHANNEL_LIMIT_POP(cat->c, RX); + + return new; +} + +void rte_update_direct(struct channel *c, const net_addr *n, rte *new, struct rte_src *src); void rte_update(struct channel *c, const net_addr *n, rte *new, struct rte_src *src) @@ -1233,13 +1261,14 @@ rte_update(struct channel *c, const net_addr *n, rte *new, struct rte_src *src) ASSERT(c->channel_state == CS_UP); - if (c->in_table && !rte_update_in(c, n, new, src)) - return; - return rte_update_direct(c, n, new, src); + if (c->in_table) + rte_import(&c->in_table->push, n, new, src); + else + rte_update_direct(c, n, new, src); } -static void +void rte_update_direct(struct channel *c, const net_addr *n, rte *new, struct rte_src *src) { const struct filter *filter = c->in_filter; @@ -1323,24 +1352,6 @@ rte_discard(net *net, rte *old) /* Non-filtered route deletion, used during garb rte_update_unlock(); } -/* Modify existing route by protocol hook, used for long-lived graceful restart */ -static inline void -rte_modify(net *net, rte *old) -{ - rte_update_lock(); - - rte *new = old->sender->req->rte_modify(old, rte_update_pool); - if (new != old) - { - if (new) - new->flags = old->flags & ~REF_MODIFY; - - rte_recalculate(old->sender, net, new, old->src); - } - - rte_update_unlock(); -} - /* Check rtable for best route to given net whether it would be exported do p */ int rt_examine(rtable *t, net_addr *a, struct channel *c, const struct filter *filter) @@ -1419,6 +1430,9 @@ rt_request_import(rtable *tab, struct rt_import_request *req) hook->req = req; hook->table = tab; + if (!hook->stale_set) + hook->stale_set = hook->stale_valid = hook->stale_pruning = hook->stale_pruned = 1; + rt_set_import_state(hook, TIS_UP); hook->n = (node) {}; @@ -1499,20 +1513,41 @@ rt_stop_export(struct rt_export_request *req, void (*stopped)(struct rt_export_r * routes to the routing table (by rte_update()). After that, all protocol * routes (more precisely routes with @c as @sender) not sent during the * refresh cycle but still in the table from the past are pruned. This is - * implemented by marking all related routes as stale by REF_STALE flag in - * rt_refresh_begin(), then marking all related stale routes with REF_DISCARD - * flag in rt_refresh_end() and then removing such routes in the prune loop. - */ + * implemented by setting rte->stale_cycle to req->stale_set in rte_update() + * and then dropping all routes with old stale_cycle values in table prune loop. */ void -rt_refresh_begin(rtable *t, struct rt_import_request *req) +rt_refresh_begin(struct rt_import_request *req) { - FIB_WALK(&t->fib, net, n) - { - for (struct rte_storage *e = n->routes; e; e = e->next) - if (e->rte.sender == req->hook) - e->rte.flags |= REF_STALE; - } - FIB_WALK_END; + struct rt_import_hook *hook = req->hook; + ASSERT_DIE(hook); + + ASSERT_DIE(hook->stale_set == hook->stale_valid); + + /* If the pruning routine is too slow */ + if ((hook->stale_pruned < hook->stale_valid) && (hook->stale_pruned + 128 < hook->stale_valid) + || (hook->stale_pruned > hook->stale_valid) && (hook->stale_pruned > hook->stale_valid + 128)) + { + log(L_WARN "Route refresh flood in table %s", hook->table->name); + FIB_WALK(&hook->table->fib, net, n) + { + for (struct rte_storage *e = n->routes; e; e = e->next) + if (e->rte.sender == req->hook) + e->rte.stale_cycle = 0; + } + FIB_WALK_END; + hook->stale_set = 1; + hook->stale_valid = 0; + hook->stale_pruned = 0; + } + else if (!++hook->stale_set) + { + /* Let's reserve the stale_cycle zero value for always-invalid routes */ + hook->stale_set = 1; + hook->stale_valid = 0; + } + + if (req->trace_routes & D_STATES) + log(L_TRACE "%s: route refresh begin [%u]", req->name, hook->stale_set); } /** @@ -1524,43 +1559,18 @@ rt_refresh_begin(rtable *t, struct rt_import_request *req) * hook. See rt_refresh_begin() for description of refresh cycles. */ void -rt_refresh_end(rtable *t, struct rt_import_request *req) +rt_refresh_end(struct rt_import_request *req) { - int prune = 0; - - FIB_WALK(&t->fib, net, n) - { - for (struct rte_storage *e = n->routes; e; e = e->next) - if ((e->rte.sender == req->hook) && (e->rte.flags & REF_STALE)) - { - e->rte.flags |= REF_DISCARD; - prune = 1; - } - } - FIB_WALK_END; - - if (prune) - rt_schedule_prune(t); -} + struct rt_import_hook *hook = req->hook; + ASSERT_DIE(hook); -void -rt_modify_stale(rtable *t, struct rt_import_request *req) -{ - int prune = 0; + hook->stale_valid++; + ASSERT_DIE(hook->stale_set == hook->stale_valid); - FIB_WALK(&t->fib, net, n) - { - for (struct rte_storage *e = n->routes; e; e = e->next) - if ((e->rte.sender == req->hook) && (e->rte.flags & REF_STALE) && !(e->rte.flags & REF_FILTERED)) - { - e->rte.flags |= REF_MODIFY; - prune = 1; - } - } - FIB_WALK_END; + rt_schedule_prune(hook->table); - if (prune) - rt_schedule_prune(t); + if (req->trace_routes & D_STATES) + log(L_TRACE "%s: route refresh end [%u]", req->name, hook->stale_valid); } /** @@ -1613,9 +1623,6 @@ rt_dump_all(void) WALK_LIST2(t, n, routing_tables, n) rt_dump(t); - - WALK_LIST2(t, n, deleted_routing_tables, n) - rt_dump(t); } void @@ -1658,9 +1665,6 @@ rt_dump_hooks_all(void) WALK_LIST2(t, n, routing_tables, n) rt_dump_hooks(t); - - WALK_LIST2(t, n, deleted_routing_tables, n) - rt_dump_hooks(t); } static inline void @@ -1796,9 +1800,7 @@ rt_free(resource *_r) DBG("Deleting routing table %s\n", r->name); ASSERT_DIE(r->use_count == 0); - - if (r->internal) - return; + ASSERT_DIE(r->deleted); r->config->table = NULL; rem_node(&r->n); @@ -1853,20 +1855,17 @@ rt_setup(pool *pp, struct rtable_config *cf) fib_init(&t->fib, p, t->addr_type, sizeof(net), OFFSETOF(net, n), 0, NULL); - if (!(t->internal = cf->internal)) - { - init_list(&t->imports); - init_list(&t->exports); - hmap_init(&t->id_map, p, 1024); - hmap_set(&t->id_map, 0); + init_list(&t->imports); + init_list(&t->exports); + hmap_init(&t->id_map, p, 1024); + hmap_set(&t->id_map, 0); - init_list(&t->subscribers); + init_list(&t->subscribers); - t->rt_event = ev_new_init(p, rt_event, t); - t->last_rt_change = t->gc_time = current_time(); + t->rt_event = ev_new_init(p, rt_event, t); + t->last_rt_change = t->gc_time = current_time(); - t->rl_pipe = (struct tbf) TBF_DEFAULT_LOG_LIMITS; - } + t->rl_pipe = (struct tbf) TBF_DEFAULT_LOG_LIMITS; return t; } @@ -1884,7 +1883,6 @@ rt_init(void) rt_table_pool = rp_new(&root_pool, "Routing tables"); rte_update_pool = lp_new_default(rt_table_pool); init_list(&routing_tables); - init_list(&deleted_routing_tables); } @@ -1925,6 +1923,13 @@ rt_prune_table(rtable *tab) WALK_LIST2(ih, n, tab->imports, n) if (ih->import_state == TIS_STOP) rt_set_import_state(ih, TIS_FLUSHING); + else if ((ih->stale_valid != ih->stale_pruning) && (ih->stale_pruning == ih->stale_pruned)) + { + ih->stale_pruning = ih->stale_valid; + + if (ih->req->trace_routes & D_STATES) + log(L_TRACE "%s: table prune after refresh begin [%u]", ih->req->name, ih->stale_pruning); + } FIB_ITERATE_INIT(fit, &tab->fib); tab->prune_state = 2; @@ -1936,22 +1941,11 @@ again: rescan: for (struct rte_storage *e=n->routes; e; e=e->next) { - if ((e->rte.sender->import_state == TIS_FLUSHING) || (e->rte.flags & REF_DISCARD)) - { - if (limit <= 0) - { - FIB_ITERATE_PUT(fit); - ev_schedule(tab->rt_event); - return; - } - - rte_discard(n, &e->rte); - limit--; - - goto rescan; - } + struct rt_import_hook *s = e->rte.sender; - if (e->rte.flags & REF_MODIFY) + if ((s->import_state == TIS_FLUSHING) || + (e->rte.stale_cycle < s->stale_valid) || + (e->rte.stale_cycle > s->stale_set)) { if (limit <= 0) { @@ -1960,7 +1954,7 @@ again: return; } - rte_modify(n, &e->rte); + rte_discard(n, &e->rte); limit--; goto rescan; @@ -1998,6 +1992,13 @@ again: mb_free(ih); rt_unlock_table(tab); } + else if (ih->stale_pruning != ih->stale_pruned) + { + ih->stale_pruned = ih->stale_pruning; + + if (ih->req->trace_routes & D_STATES) + log(L_TRACE "%s: table prune after refresh end [%u]", ih->req->name, ih->stale_pruned); + } } void @@ -2204,7 +2205,10 @@ rt_next_hop_update_net(rtable *tab, net *n) for (int i=0; i<count; i++) { _Bool nb = (new == updates[i].new), ob = (old_best == updates[i].old); - const char *best_indicator[2][2] = { { "updated", "updated [-best]" }, { "updated [+best]", "updated [best]" } }; + const char *best_indicator[2][2] = { + { "autoupdated", "autoupdated [-best]" }, + { "autoupdated [+best]", "autoupdated [best]" } + }; rt_rte_trace_in(D_ROUTES, updates[i].new->rte.sender->req, &updates[i].new->rte, best_indicator[nb][ob]); rte_announce_i(tab, n, updates[i].new, updates[i].old, new, old_best); } @@ -2308,11 +2312,12 @@ rt_unlock_table(rtable *r) { if (!--r->use_count && r->deleted) { - struct config *conf = r->deleted; + void *del_data = r->del_data; + void (*deleted)(void *) = r->deleted; /* Delete the routing table by freeing its pool */ rt_shutdown(r); - config_del_obstacle(conf); + deleted(del_data); } } @@ -2323,6 +2328,8 @@ rt_find_table_config(struct config *cf, char *name) return (sym && (sym->class == SYM_TABLE)) ? sym->table : NULL; } +static void rt_config_del_obstacle(void *data) { config_del_obstacle(data); } + /** * rt_commit - commit new routing table configuration * @new: new configuration @@ -2361,9 +2368,10 @@ rt_commit(struct config *new, struct config *old) else { DBG("\t%s: deleted\n", o->name); - ot->deleted = old; - config_add_obstacle(old); rt_lock_table(ot); + ot->deleted = rt_config_del_obstacle; + ot->del_data = old; + config_add_obstacle(old); rt_unlock_table(ot); } } @@ -2442,285 +2450,6 @@ done: /* - * Import table - */ - -int -rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *src) -{ - struct rtable *tab = c->in_table; - net *net; - - if (new) - net = net_get(tab, n); - else - { - net = net_find(tab, n); - - if (!net) - goto drop_withdraw; - } - - /* Find the old rte */ - struct rte_storage **pos = rte_find(net, src); - if (*pos) - { - rte *old = &(*pos)->rte; - if (new && rte_same(old, new)) - { - /* Refresh the old rte, continue with update to main rtable */ - if (old->flags & (REF_STALE | REF_DISCARD | REF_MODIFY)) - { - old->flags &= ~(REF_STALE | REF_DISCARD | REF_MODIFY); - return 1; - } - - goto drop_update; - } - - if (!new) - CHANNEL_LIMIT_POP(c, RX); - - /* Move iterator if needed */ - if (*pos == c->reload_next_rte) - c->reload_next_rte = (*pos)->next; - - /* Remove the old rte */ - struct rte_storage *del = *pos; - *pos = (*pos)->next; - rte_free(del, tab); - tab->rt_count--; - } - else if (new) - { - if (CHANNEL_LIMIT_PUSH(c, RX)) - { - /* Required by rte_trace_in() */ - new->net = n; - - channel_rte_trace_in(D_FILTERS, c, new, "ignored [limit]"); - goto drop_update; - } - } - else - goto drop_withdraw; - - if (!new) - { - if (!net->routes) - fib_delete(&tab->fib, net); - - return 1; - } - - /* Insert the new rte */ - struct rte_storage *e = rte_store(new, net, tab); - e->rte.lastmod = current_time(); - e->next = *pos; - *pos = e; - tab->rt_count++; - return 1; - -drop_update: - c->import_stats.updates_received++; - c->in_req.hook->stats.updates_ignored++; - - if (!net->routes) - fib_delete(&tab->fib, net); - - return 0; - -drop_withdraw: - c->import_stats.withdraws_received++; - c->in_req.hook->stats.withdraws_ignored++; - return 0; -} - -int -rt_reload_channel(struct channel *c) -{ - struct rtable *tab = c->in_table; - struct fib_iterator *fit = &c->reload_fit; - int max_feed = 64; - - ASSERT(c->channel_state == CS_UP); - - if (!c->reload_active) - { - FIB_ITERATE_INIT(fit, &tab->fib); - c->reload_active = 1; - } - - do { - for (struct rte_storage *e = c->reload_next_rte; e; e = e->next) - { - if (max_feed-- <= 0) - { - c->reload_next_rte = e; - debug("%s channel reload burst split (max_feed=%d)", c->proto->name, max_feed); - return 0; - } - - rte r = e->rte; - rte_update_direct(c, r.net, &r, r.src); - } - - c->reload_next_rte = NULL; - - FIB_ITERATE_START(&tab->fib, fit, net, n) - { - if (c->reload_next_rte = n->routes) - { - FIB_ITERATE_PUT_NEXT(fit, &tab->fib); - break; - } - } - FIB_ITERATE_END; - } - while (c->reload_next_rte); - - c->reload_active = 0; - return 1; -} - -void -rt_reload_channel_abort(struct channel *c) -{ - if (c->reload_active) - { - /* Unlink the iterator */ - fit_get(&c->in_table->fib, &c->reload_fit); - c->reload_next_rte = NULL; - c->reload_active = 0; - } -} - -void -rt_prune_sync(rtable *t, int all) -{ - struct fib_iterator fit; - - FIB_ITERATE_INIT(&fit, &t->fib); - -again: - FIB_ITERATE_START(&t->fib, &fit, net, n) - { - struct rte_storage *e, **ee = &n->routes; - - while (e = *ee) - { - if (all || (e->rte.flags & (REF_STALE | REF_DISCARD))) - { - *ee = e->next; - rte_free(e, t); - t->rt_count--; - } - else - ee = &e->next; - } - - if (all || !n->routes) - { - FIB_ITERATE_PUT(&fit); - fib_delete(&t->fib, n); - goto again; - } - } - FIB_ITERATE_END; -} - - -/* - * Export table - */ - -int -rte_update_out(struct channel *c, const net_addr *n, rte *new, const rte *old0, struct rte_storage **old_exported) -{ - struct rtable *tab = c->out_table; - struct rte_src *src; - net *net; - - if (new) - { - net = net_get(tab, n); - src = new->src; - } - else - { - net = net_find(tab, n); - src = old0->src; - - if (!net) - goto drop; - } - - /* Find the old rte */ - struct rte_storage **pos = (c->ra_mode == RA_ANY) ? rte_find(net, src) : &net->routes; - struct rte_storage *old = NULL; - - if (old = *pos) - { - if (new && rte_same(&(*pos)->rte, new)) - goto drop; - - /* Remove the old rte */ - *pos = old->next; - *old_exported = old; - tab->rt_count--; - } - - if (!new) - { - if (!old) - goto drop; - - if (!net->routes) - fib_delete(&tab->fib, net); - - return 1; - } - - /* Insert the new rte */ - struct rte_storage *e = rte_store(new, net, tab); - e->rte.lastmod = current_time(); - e->next = *pos; - *pos = e; - tab->rt_count++; - return 1; - -drop: - return 0; -} - -void -rt_refeed_channel(struct channel *c) -{ - if (!c->out_table) - { - channel_request_feeding(c); - return; - } - - ASSERT_DIE(c->ra_mode != RA_ANY); - - c->proto->feed_begin(c, 0); - - FIB_WALK(&c->out_table->fib, net, n) - { - if (!n->routes) - continue; - - rte e = n->routes->rte; - c->proto->rt_notify(c->proto, c, n->n.addr, &e, NULL); - } - FIB_WALK_END; - - c->proto->feed_end(c); -} - - -/* * Hostcache */ diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c index 90490b4f..892b26e3 100644 --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@ -2267,30 +2267,44 @@ bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best) return !old_suppressed; } -rte * -bgp_rte_modify_stale(struct rte *r, struct linpool *pool) +void +bgp_rte_modify_stale(struct rt_export_request *req, const net_addr *n, struct rt_pending_export *rpe UNUSED, rte **feed, uint count) { - eattr *ea = ea_find(r->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_COMMUNITY)); - const struct adata *ad = ea ? ea->u.ptr : NULL; - uint flags = ea ? ea->flags : BAF_PARTIAL; + struct bgp_channel *c = SKIP_BACK(struct bgp_channel, stale_feed, req); - if (ad && int_set_contains(ad, BGP_COMM_NO_LLGR)) - return NULL; + do { + rte *r = feed[--count]; + if (r->sender != c->c.in_req.hook) + continue; + + /* A new route, do not mark as stale */ + if (r->stale_cycle == c->c.in_req.hook->stale_set) + continue; + + eattr *ea = ea_find(r->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_COMMUNITY)); + const struct adata *ad = ea ? ea->u.ptr : NULL; + uint flags = ea ? ea->flags : BAF_PARTIAL; - if (ad && int_set_contains(ad, BGP_COMM_LLGR_STALE)) - return r; + rte e0 = *r; + e0.flags |= REF_USE_STALE; - rta *a = rta_do_cow(r->attrs, pool); - - _Thread_local static rte e0; - e0 = *r; - e0.attrs = a; + if (ad && int_set_contains(ad, BGP_COMM_NO_LLGR)) + rte_import(&c->c.in_req, n, NULL, r->src); - bgp_set_attr_ptr(&(a->eattrs), pool, BA_COMMUNITY, flags, - int_set_add(pool, ad, BGP_COMM_LLGR_STALE)); - e0.pflags |= BGP_REF_STALE; + else if (ad && int_set_contains(ad, BGP_COMM_LLGR_STALE)) + rte_import(&c->c.in_req, n, &e0, r->src); - return &e0; + else { + rta *a = e0.attrs = rta_do_cow(r->attrs, bgp_linpool); + + bgp_set_attr_ptr(&(a->eattrs), bgp_linpool, BA_COMMUNITY, flags, + int_set_add(bgp_linpool, ad, BGP_COMM_LLGR_STALE)); + e0.pflags |= BGP_REF_STALE; + + rte_import(&c->c.in_req, n, &e0, r->src); + lp_flush(bgp_linpool); + } + } while (count); } diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index 78c36bc7..35e9ea59 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -140,6 +140,15 @@ static void bgp_update_bfd(struct bgp_proto *p, const struct bfd_options *bfd); static int bgp_incoming_connection(sock *sk, uint dummy UNUSED); static void bgp_listen_sock_err(sock *sk UNUSED, int err); +static void bgp_graceful_restart_feed(struct bgp_channel *c); +static inline void channel_refresh_end_reload(struct channel *c) +{ + channel_refresh_end(c); + + if (c->in_table) + channel_request_reload(c); +} + /** * bgp_open - open a BGP instance * @p: BGP instance @@ -775,25 +784,25 @@ bgp_handle_graceful_restart(struct bgp_proto *p) { case BGP_GRS_NONE: c->gr_active = BGP_GRS_ACTIVE; - rt_refresh_begin(c->c.table, &c->c.in_req); + channel_refresh_begin(&c->c); break; case BGP_GRS_ACTIVE: - rt_refresh_end(c->c.table, &c->c.in_req); - rt_refresh_begin(c->c.table, &c->c.in_req); + channel_refresh_end(&c->c); + channel_refresh_begin(&c->c); break; case BGP_GRS_LLGR: - rt_refresh_begin(c->c.table, &c->c.in_req); - rt_modify_stale(c->c.table, &c->c.in_req); + channel_refresh_begin(&c->c); + bgp_graceful_restart_feed(c); break; } } else { /* Just flush the routes */ - rt_refresh_begin(c->c.table, &c->c.in_req); - rt_refresh_end(c->c.table, &c->c.in_req); + channel_refresh_begin(&c->c); + channel_refresh_end(&c->c); } /* Reset bucket and prefix tables */ @@ -811,6 +820,50 @@ bgp_handle_graceful_restart(struct bgp_proto *p) tm_start(p->gr_timer, p->conn->remote_caps->gr_time S); } +static void +bgp_graceful_restart_feed_done(struct rt_export_request *req) +{ + req->hook = NULL; +} + +static void +bgp_graceful_restart_feed_dump_req(struct rt_export_request *req) +{ + struct bgp_channel *c = SKIP_BACK(struct bgp_channel, stale_feed, req); + debug(" BGP-GR %s.%s export request %p\n", c->c.proto->name, c->c.name, req); +} + +static void +bgp_graceful_restart_feed_log_state_change(struct rt_export_request *req, u8 state) +{ + struct bgp_channel *c = SKIP_BACK(struct bgp_channel, stale_feed, req); + struct bgp_proto *p = (void *) c->c.proto; + BGP_TRACE(D_EVENTS, "Long-lived graceful restart export state changed to %s", rt_export_state_name(state)); + + if (state == TES_READY) + rt_stop_export(req, bgp_graceful_restart_feed_done); +} + +static void +bgp_graceful_restart_drop_export(struct rt_export_request *req UNUSED, const net_addr *n UNUSED, struct rt_pending_export *rpe UNUSED) +{ /* Nothing to do */ } + +static void +bgp_graceful_restart_feed(struct bgp_channel *c) +{ + c->stale_feed = (struct rt_export_request) { + .name = "BGP-GR", + .trace_routes = c->c.debug | c->c.proto->debug, + .dump_req = bgp_graceful_restart_feed_dump_req, + .log_state_change = bgp_graceful_restart_feed_log_state_change, + .export_bulk = bgp_rte_modify_stale, + .export_one = bgp_graceful_restart_drop_export, + }; + + rt_request_export(c->c.table, &c->stale_feed); +} + + /** * bgp_graceful_restart_done - finish active BGP graceful restart * @c: BGP channel @@ -833,8 +886,11 @@ bgp_graceful_restart_done(struct bgp_channel *c) if (!p->gr_active_num) BGP_TRACE(D_EVENTS, "Neighbor graceful restart done"); + if (c->stale_feed.hook) + rt_stop_export(&c->stale_feed, bgp_graceful_restart_feed_done); + tm_stop(c->stale_timer); - rt_refresh_end(c->c.table, &c->c.in_req); + channel_refresh_end_reload(&c->c); } /** @@ -876,7 +932,7 @@ bgp_graceful_restart_timeout(timer *t) /* Channel is in GR, and supports LLGR -> start LLGR */ c->gr_active = BGP_GRS_LLGR; tm_start(c->stale_timer, c->stale_time S); - rt_modify_stale(c->c.table, &c->c.in_req); + bgp_graceful_restart_feed(c); } } else @@ -914,10 +970,7 @@ bgp_refresh_begin(struct bgp_channel *c) { log(L_WARN "%s: BEGIN-OF-RR received before END-OF-RIB, ignoring", p->p.name); return; } c->load_state = BFS_REFRESHING; - rt_refresh_begin(c->c.table, &c->c.in_req); - - if (c->c.in_table) - rt_refresh_begin(c->c.in_table, &c->c.in_req); + channel_refresh_begin(&c->c); } /** @@ -938,10 +991,7 @@ bgp_refresh_end(struct bgp_channel *c) { log(L_WARN "%s: END-OF-RR received without prior BEGIN-OF-RR, ignoring", p->p.name); return; } c->load_state = BFS_NONE; - rt_refresh_end(c->c.table, &c->c.in_req); - - if (c->c.in_table) - rt_prune_sync(c->c.in_table, 0); + channel_refresh_end_reload(&c->c); } @@ -1408,12 +1458,9 @@ bgp_reload_routes(struct channel *C) struct bgp_proto *p = (void *) C->proto; struct bgp_channel *c = (void *) C; - ASSERT(p->conn && (p->route_refresh || c->c.in_table)); + ASSERT(p->conn && (p->route_refresh)); - if (c->c.in_table) - channel_schedule_reload(C); - else - bgp_schedule_packet(p->conn, c, PKT_ROUTE_REFRESH); + bgp_schedule_packet(p->conn, c, PKT_ROUTE_REFRESH); } static void @@ -1693,7 +1740,6 @@ bgp_init(struct proto_config *CF) P->rte_better = bgp_rte_better; P->rte_mergable = bgp_rte_mergable; P->rte_recalculate = cf->deterministic_med ? bgp_rte_recalculate : NULL; - P->rte_modify = bgp_rte_modify_stale; P->rte_igp_metric = bgp_rte_igp_metric; p->cf = cf; @@ -1756,7 +1802,7 @@ bgp_channel_start(struct channel *C) bgp_init_prefix_table(c); if (c->cf->import_table) - channel_setup_in_table(C); + channel_setup_in_table(C, 0); if (c->cf->export_table) channel_setup_out_table(C); diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index c79dd1b2..342dc023 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -366,6 +366,7 @@ struct bgp_channel { timer *stale_timer; /* Long-lived stale timer for LLGR */ u32 stale_time; /* Stored LLGR stale time from last session */ + struct rt_export_request stale_feed; /* Feeder request for stale route modification */ u8 add_path_rx; /* Session expects receive of ADD-PATH extended NLRI */ u8 add_path_tx; /* Session expects transmit of ADD-PATH extended NLRI */ @@ -585,7 +586,7 @@ void bgp_free_prefix(struct bgp_channel *c, struct bgp_prefix *bp); int bgp_rte_better(struct rte *, struct rte *); int bgp_rte_mergable(rte *pri, rte *sec); int bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best); -struct rte *bgp_rte_modify_stale(struct rte *r, struct linpool *pool); +void bgp_rte_modify_stale(struct rt_export_request *, const net_addr *, struct rt_pending_export *, rte **, uint); u32 bgp_rte_igp_metric(struct rte *); void bgp_rt_notify(struct proto *P, struct channel *C, const net_addr *n, rte *new, const rte *old); int bgp_preexport(struct channel *, struct rte *); diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index f1e6d7d2..647551e5 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -2695,7 +2695,7 @@ bgp_rx_route_refresh(struct bgp_conn *conn, byte *pkt, uint len) { case BGP_RR_REQUEST: BGP_TRACE(D_PACKETS, "Got ROUTE-REFRESH"); - rt_refeed_channel(&c->c); + channel_request_feeding(&c->c); break; case BGP_RR_BEGIN: diff --git a/proto/rpki/packets.c b/proto/rpki/packets.c index 943485d7..897edc09 100644 --- a/proto/rpki/packets.c +++ b/proto/rpki/packets.c @@ -661,9 +661,9 @@ rpki_handle_cache_response_pdu(struct rpki_cache *cache, const struct pdu_cache_ * a refresh cycle. */ if (cache->p->roa4_channel) - rt_refresh_begin(cache->p->roa4_channel->table, &cache->p->roa4_channel->in_req); + rt_refresh_begin(&cache->p->roa4_channel->in_req); if (cache->p->roa6_channel) - rt_refresh_begin(cache->p->roa6_channel->table, &cache->p->roa6_channel->in_req); + rt_refresh_begin(&cache->p->roa6_channel->in_req); cache->p->refresh_channels = 1; } @@ -819,9 +819,9 @@ rpki_handle_end_of_data_pdu(struct rpki_cache *cache, const struct pdu_end_of_da { cache->p->refresh_channels = 0; if (cache->p->roa4_channel) - rt_refresh_end(cache->p->roa4_channel->table, &cache->p->roa4_channel->in_req); + rt_refresh_end(&cache->p->roa4_channel->in_req); if (cache->p->roa6_channel) - rt_refresh_end(cache->p->roa6_channel->table, &cache->p->roa6_channel->in_req); + rt_refresh_end(&cache->p->roa6_channel->in_req); } cache->last_update = current_time(); diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c index 40a58442..609ee921 100644 --- a/sysdep/unix/krt.c +++ b/sysdep/unix/krt.c @@ -285,249 +285,24 @@ krt_metric(rte *a) } static inline int -krt_same_key(rte *a, rte *b) +krt_rte_better(rte *a, rte *b) { - return (krt_metric(a) == krt_metric(b)); -} - -static inline int -krt_uptodate(rte *a, rte *b) -{ - return (a->attrs == b->attrs); -} - -static void -krt_learn_announce_update(struct krt_proto *p, rte *e) -{ - rte e0 = { - .attrs = rta_clone(e->attrs), - .src = p->p.main_source, - }; - - rte_update(p->p.main_channel, e->net, &e0, p->p.main_source); -} - -static void -krt_learn_announce_delete(struct krt_proto *p, net_addr *n) -{ - rte_update(p->p.main_channel, n, NULL, p->p.main_source); + return (krt_metric(a) > krt_metric(b)); } /* Called when alien route is discovered during scan */ static void -krt_learn_scan(struct krt_proto *p, rte *e) -{ - net *n = net_get(p->krt_table, e->net); - struct rte_storage *m, **mm; - - struct rte_storage *ee = rte_store(e, n, p->krt_table); - - for(mm = &n->routes; m = *mm; mm = &m->next) - if (krt_same_key(&m->rte, e)) - break; - if (m) - { - if (krt_uptodate(&m->rte, e)) - { - krt_trace_in_rl(&rl_alien, p, e, "[alien] seen"); - rte_free(ee, p->krt_table); - m->rte.pflags |= KRT_REF_SEEN; - } - else - { - krt_trace_in(p, e, "[alien] updated"); - *mm = m->next; - rte_free(m, p->krt_table); - m = NULL; - } - } - else - krt_trace_in(p, e, "[alien] created"); - - if (!m) - { - ee->next = n->routes; - n->routes = ee; - ee->rte.pflags |= KRT_REF_SEEN; - } -} - -static void -krt_learn_prune(struct krt_proto *p) +krt_learn_rte(struct krt_proto *p, rte *e) { - struct fib *fib = &p->krt_table->fib; - struct fib_iterator fit; - - KRT_TRACE(p, D_EVENTS, "Pruning inherited routes"); - - FIB_ITERATE_INIT(&fit, fib); -again: - FIB_ITERATE_START(fib, &fit, net, n) - { - struct rte_storage *e, **ee, *best, **pbest, *old_best; - - /* - * Note that old_best may be NULL even if there was an old best route in - * the previous step, because it might be replaced in krt_learn_scan(). - * But in that case there is a new valid best route. - */ - - old_best = NULL; - best = NULL; - pbest = NULL; - ee = &n->routes; - while (e = *ee) - { - if (e->rte.pflags & KRT_REF_BEST) - old_best = e; - - if (!(e->rte.pflags & KRT_REF_SEEN)) - { - *ee = e->next; - rte_free(e, p->krt_table); - continue; - } - - if (!best || krt_metric(&best->rte) > krt_metric(&e->rte)) - { - best = e; - pbest = ee; - } - - e->rte.pflags &= ~(KRT_REF_SEEN | KRT_REF_BEST); - ee = &e->next; - } - if (!n->routes) - { - DBG("%I/%d: deleting\n", n->n.prefix, n->n.pxlen); - if (old_best) - krt_learn_announce_delete(p, n->n.addr); - - FIB_ITERATE_PUT(&fit); - fib_delete(fib, n); - goto again; - } - - best->rte.pflags |= KRT_REF_BEST; - *pbest = best->next; - best->next = n->routes; - n->routes = best; - - if ((best != old_best) || p->reload) - { - DBG("%I/%d: announcing (metric=%d)\n", n->n.prefix, n->n.pxlen, krt_metric(&best->rte)); - krt_learn_announce_update(p, &best->rte); - } - else - DBG("%I/%d: uptodate (metric=%d)\n", n->n.prefix, n->n.pxlen, krt_metric(&best->rte)); - } - FIB_ITERATE_END; - - p->reload = 0; -} - -static void -krt_learn_async(struct krt_proto *p, rte *e, int new) -{ - net *n = net_get(p->krt_table, e->net); - struct rte_storage *g, **gg, *best, **bestp, *old_best; - - ASSERT(!e->attrs->cached); - e->attrs->pref = p->p.main_channel->preference; - - struct rte_storage *ee = rte_store(e, n, p->krt_table); - - old_best = n->routes; - for(gg=&n->routes; g = *gg; gg = &g->next) - if (krt_same_key(&g->rte, e)) - break; - if (new) - { - if (g) - { - if (krt_uptodate(&g->rte, e)) - { - krt_trace_in(p, e, "[alien async] same"); - rte_free(ee, p->krt_table); - return; - } - krt_trace_in(p, e, "[alien async] updated"); - *gg = g->next; - rte_free(g, p->krt_table); - } - else - krt_trace_in(p, e, "[alien async] created"); - - ee->next = n->routes; - n->routes = ee; - } - else if (!g) - { - krt_trace_in(p, e, "[alien async] delete failed"); - rte_free(ee, p->krt_table); - return; - } - else - { - krt_trace_in(p, e, "[alien async] removed"); - *gg = g->next; - rte_free(ee, p->krt_table); - rte_free(g, p->krt_table); - } - best = n->routes; - bestp = &n->routes; - for(gg=&n->routes; g=*gg; gg=&g->next) - { - if (krt_metric(&best->rte) > krt_metric(&g->rte)) - { - best = g; - bestp = gg; - } - - g->rte.pflags &= ~KRT_REF_BEST; - } - - if (best) - { - best->rte.pflags |= KRT_REF_BEST; - *bestp = best->next; - best->next = n->routes; - n->routes = best; - } - - if (best != old_best) - { - DBG("krt_learn_async: distributing change\n"); - if (best) - krt_learn_announce_update(p, &best->rte); - else - krt_learn_announce_delete(p, n->n.addr); - } + e->src = rt_get_source(&p->p, krt_metric(e)); + rte_update(p->p.main_channel, e->net, e, e->src); } static void krt_learn_init(struct krt_proto *p) { if (KRT_CF->learn) - { - struct rtable_config *cf = mb_allocz(p->p.pool, sizeof(struct rtable_config)); - cf->name = "Inherited"; - cf->addr_type = p->p.net_type; - cf->internal = 1; - - p->krt_table = rt_setup(p->p.pool, cf); - } -} - -static void -krt_dump(struct proto *P) -{ - struct krt_proto *p = (struct krt_proto *) P; - - if (!KRT_CF->learn) - return; - debug("KRT: Table of inheritable routes\n"); - rt_dump(p->krt_table); + channel_setup_in_table(p->p.main_channel, 1); } #endif @@ -547,7 +322,7 @@ rte_feed_count(net *n) { uint count = 0; for (struct rte_storage *e = n->routes; e; e = e->next) - if (rte_is_valid(RTE_OR_NULL(e))) + if (rte_is_valid(RTES_OR_NULL(e))) count++; return count; } @@ -557,7 +332,7 @@ rte_feed_obtain(net *n, rte **feed, uint count) { uint i = 0; for (struct rte_storage *e = n->routes; e; e = e->next) - if (rte_is_valid(RTE_OR_NULL(e))) + if (rte_is_valid(RTES_OR_NULL(e))) { ASSERT_DIE(i < count); feed[i++] = &e->rte; @@ -643,7 +418,7 @@ krt_got_route(struct krt_proto *p, rte *e, s8 src) case KRT_SRC_ALIEN: if (KRT_CF->learn) - krt_learn_scan(p, e); + krt_learn_rte(p, e); else krt_trace_in_rl(&rl_alien, p, e, "[alien] ignored"); return; @@ -712,6 +487,11 @@ static void krt_init_scan(struct krt_proto *p) { bmap_reset(&p->seen_map, 1024); + +#ifdef KRT_ALLOW_LEARN + if (KRT_CF->learn) + channel_refresh_begin(p->p.main_channel); +#endif } static void @@ -739,7 +519,7 @@ krt_prune(struct krt_proto *p) #ifdef KRT_ALLOW_LEARN if (KRT_CF->learn) - krt_learn_prune(p); + channel_refresh_end(p->p.main_channel); #endif if (p->ready) @@ -781,7 +561,7 @@ krt_got_route_async(struct krt_proto *p, rte *e, int new, s8 src) case KRT_SRC_ALIEN: if (KRT_CF->learn) { - krt_learn_async(p, e, new); + krt_learn_rte(p, e); return; } #endif @@ -1027,6 +807,7 @@ krt_init(struct proto_config *CF) p->p.if_notify = krt_if_notify; p->p.reload_routes = krt_reload_routes; p->p.feed_end = krt_feed_end; + p->p.rte_better = krt_rte_better; krt_sys_init(p); return &p->p; @@ -1182,7 +963,4 @@ struct protocol proto_unix_kernel = { .reconfigure = krt_reconfigure, .copy_config = krt_copy_config, .get_attr = krt_get_attr, -#ifdef KRT_ALLOW_LEARN - .dump = krt_dump, -#endif }; diff --git a/sysdep/unix/krt.h b/sysdep/unix/krt.h index f6ad6fde..968c5b16 100644 --- a/sysdep/unix/krt.h +++ b/sysdep/unix/krt.h @@ -51,10 +51,6 @@ struct krt_proto { struct proto p; struct krt_state sys; /* Sysdep state */ -#ifdef KRT_ALLOW_LEARN - struct rtable *krt_table; /* Internal table of inherited routes */ -#endif - #ifndef CONFIG_ALL_TABLES_AT_ONCE timer *scan_timer; #endif |