diff --git a/conf/confbase.Y b/conf/confbase.Y index 8e5da9e3..c764f9e4 100644 --- a/conf/confbase.Y +++ b/conf/confbase.Y @@ -81,6 +81,7 @@ CF_DECLS const struct filter *f; struct f_tree *e; struct f_trie *trie; + const struct f_trie *const_trie; struct f_val v; struct password_item *p; struct rt_show_data *ra; diff --git a/nest/config.Y b/nest/config.Y index 2e70461a..e0329d40 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -107,6 +107,17 @@ proto_postconfig(void) this_proto = NULL; } +static inline void +proto_call_cmd_reload(struct proto_spec ps, int dir, const struct f_trie *trie) +{ + struct proto_reload_request *prr = cfg_alloc(sizeof *prr); + *prr = (struct proto_reload_request) { + .trie = trie, + .dir = dir, + }; + + proto_apply_cmd(ps, proto_cmd_reload, 1, (uintptr_t) prr); +} #define DIRECT_CFG ((struct rt_dev_config *) this_proto) @@ -120,7 +131,7 @@ CF_KEYWORDS(PASSWORD, KEY, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, CH CF_KEYWORDS(ALGORITHM, KEYED, HMAC, MD5, SHA1, SHA256, SHA384, SHA512, BLAKE2S128, BLAKE2S256, BLAKE2B256, BLAKE2B512) CF_KEYWORDS(PRIMARY, STATS, COUNT, FOR, IN, COMMANDS, PREEXPORT, NOEXPORT, EXPORTED, GENERATE) CF_KEYWORDS(BGP, PASSWORDS, DESCRIPTION) -CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, CLASS, DSCP) +CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, CLASS, DSCP, PARTIAL) CF_KEYWORDS(TIMEFORMAT, ISO, SHORT, LONG, ROUTE, PROTOCOL, BASE, LOG, S, MS, US) CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, AS) CF_KEYWORDS(MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, PASSIVE) @@ -151,6 +162,7 @@ CF_ENUM_PX(T_ENUM_AF, AF_, AFI_, IPV4, IPV6) %type r_args_for %type channel_sym %type channel_arg +%type partial_opt CF_GRAMMAR @@ -892,18 +904,28 @@ CF_CLI(DUMP FILTER ALL,,, [[Dump all filters in linearized form]]) CF_CLI(EVAL, term, , [[Evaluate an expression]]) { cmd_eval(f_linearize($2, 1)); } ; +partial_opt: + PARTIAL term { + struct f_val val; + if (f_eval(f_linearize($2, 1), &val) > F_RETURN) cf_error("Runtime error"); + if (val.type != T_PREFIX_SET) cf_error("Partial spec must be trie"); + $$ = val.val.ti; + } + | /* empty */ { $$ = NULL; } + ; + CF_CLI(DISABLE, proto_patt opttext, ( | \"\" | all) [message], [[Disable protocol]]) { proto_apply_cmd($2, proto_cmd_disable, 1, (uintptr_t) $3); } ; CF_CLI(ENABLE, proto_patt opttext, ( | \"\" | all) [message], [[Enable protocol]]) { proto_apply_cmd($2, proto_cmd_enable, 1, (uintptr_t) $3); } ; CF_CLI(RESTART, proto_patt opttext, ( | \"\" | all) [message], [[Restart protocol]]) { proto_apply_cmd($2, proto_cmd_restart, 1, (uintptr_t) $3); } ; -CF_CLI(RELOAD, proto_patt, | \"\" | all, [[Reload protocol]]) -{ proto_apply_cmd($2, proto_cmd_reload, 1, CMD_RELOAD); } ; -CF_CLI(RELOAD IN, proto_patt, | \"\" | all, [[Reload protocol (just imported routes)]]) -{ proto_apply_cmd($3, proto_cmd_reload, 1, CMD_RELOAD_IN); } ; -CF_CLI(RELOAD OUT, proto_patt, | \"\" | all, [[Reload protocol (just exported routes)]]) -{ proto_apply_cmd($3, proto_cmd_reload, 1, CMD_RELOAD_OUT); } ; +CF_CLI(RELOAD, proto_patt partial_opt, | \"\" | all, [[Reload protocol]]) +{ proto_call_cmd_reload($2, CMD_RELOAD, $3); } ; +CF_CLI(RELOAD IN, proto_patt partial_opt, | \"\" | all, [[Reload protocol (just imported routes)]]) +{ proto_call_cmd_reload($3, CMD_RELOAD_IN, $4); } ; +CF_CLI(RELOAD OUT, proto_patt partial_opt, | \"\" | all, [[Reload protocol (just exported routes)]]) +{ proto_call_cmd_reload($3, CMD_RELOAD_OUT, $4); } ; CF_CLI_HELP(DEBUG, ..., [[Control protocol debugging via BIRD logs]]) CF_CLI(DEBUG, debug_args, ( | | \"\" | all) (all | off | { states|routes|filters|interfaces|events|packets [, ...] }), [[Control protocol debugging via BIRD logs]]) diff --git a/nest/proto.c b/nest/proto.c index 76739556..f62d5cf2 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -344,8 +344,7 @@ struct roa_subscription { struct settle settle; struct channel *c; struct rt_export_request req; - struct f_trie* trie; - struct channel_feeding_request cfr[2]; + struct f_trie *trie; }; static void @@ -371,6 +370,8 @@ channel_roa_out_changed(struct settle *se) struct channel *c = s->c; CD(c, "Feeding triggered by RPKI change"); + + /* Get config.Y global var */ struct channel_feeding_request *cfr = lp_alloc(s->trie->lp, sizeof *cfr); *cfr = (struct channel_feeding_request) { @@ -380,7 +381,6 @@ channel_roa_out_changed(struct settle *se) }; channel_request_feeding(c, cfr); - s->trie = f_new_trie(lp_new(c->proto->pool), 0); } static void @@ -623,7 +623,6 @@ channel_start_export(struct channel *c) c->refeed_req.dump_req = channel_dump_refeed_req; c->refeed_req.log_state_change = channel_refeed_log_state_change; c->refeed_req.mark_seen = channel_rpe_mark_seen_refeed; - c->refeed_req.prefilter.hook = channel_refeed_prefilter; DBG("%s.%s: Channel start export req=%p\n", c->proto->name, c->name, &c->out_req); rt_request_export(c->table, &c->out_req); @@ -709,6 +708,8 @@ channel_refeed_stopped(struct rt_export_request *req) static void channel_init_feeding(struct channel *c) { + int no_trie = 0; + for (struct channel_feeding_request *cfrp = c->refeed_pending; cfrp; cfrp = cfrp->next) if (cfrp->type == CFRT_DIRECT) { @@ -716,12 +717,25 @@ channel_init_feeding(struct channel *c) channel_stop_export(c); return; } + else if (!cfrp->trie) + no_trie = 1; /* No direct feeding, running auxiliary refeed. */ c->refeeding = c->refeed_pending; c->refeed_pending = NULL; c->refeed_trie = f_new_trie(lp_new(c->proto->pool), 0); + if (no_trie) + { + c->refeed_req.prefilter.mode = TE_ADDR_NONE; + c->refeed_req.prefilter.hook = NULL; + } + else + { + c->refeed_req.prefilter.mode = TE_ADDR_HOOK; + c->refeed_req.prefilter.hook = channel_refeed_prefilter; + } + rt_request_export(c->table, &c->refeed_req); } @@ -1013,7 +1027,11 @@ channel_set_state(struct channel *c, uint state) void channel_request_feeding(struct channel *c, struct channel_feeding_request *cfr) { - ASSERT(c->out_req.hook); + ASSERT_DIE(c->out_req.hook); + + CD(c, "Feeding requested (%s)", + cfr->type == CFRT_DIRECT ? "direct" : + (cfr->trie ? "partial" : "auxiliary")); /* Enqueue the request */ cfr->next = c->refeed_pending; @@ -2655,9 +2673,32 @@ proto_cmd_restart(struct proto *p, uintptr_t arg, int cnt UNUSED) cli_msg(-12, "%s: restarted", p->name); } -void -proto_cmd_reload(struct proto *p, uintptr_t dir, int cnt UNUSED) +struct channel_cmd_reload_feeding_request { + struct channel_feeding_request cfr; + struct proto_reload_request *prr; +}; + +static void +channel_reload_out_done_main(void *_prr) { + struct proto_reload_request *prr = _prr; + ASSERT_THE_BIRD_LOCKED; + + rfree(prr->trie->lp); +} + +static void +channel_reload_out_done(struct channel_feeding_request *cfr) +{ + struct channel_cmd_reload_feeding_request *ccrfr = SKIP_BACK(struct channel_cmd_reload_feeding_request, cfr, cfr); + if (atomic_fetch_sub_explicit(&ccrfr->prr->counter, 1, memory_order_acq_rel) == 1) + ev_send_loop(&main_birdloop, &ccrfr->prr->ev); +} + +void +proto_cmd_reload(struct proto *p, uintptr_t _prr, int cnt UNUSED) +{ + struct proto_reload_request *prr = (void *) _prr; struct channel *c; if (p->disabled) @@ -2671,7 +2712,7 @@ proto_cmd_reload(struct proto *p, uintptr_t dir, int cnt UNUSED) return; /* All channels must support reload */ - if (dir != CMD_RELOAD_OUT) + if (prr->dir != CMD_RELOAD_OUT) WALK_LIST(c, p->channels) if ((c->channel_state == CS_UP) && !channel_reloadable(c)) { @@ -2682,16 +2723,48 @@ proto_cmd_reload(struct proto *p, uintptr_t dir, int cnt UNUSED) log(L_INFO "Reloading protocol %s", p->name); /* re-importing routes */ - if (dir != CMD_RELOAD_OUT) + if (prr->dir != CMD_RELOAD_OUT) WALK_LIST(c, p->channels) if (c->channel_state == CS_UP) channel_request_reload(c); /* re-exporting routes */ - if (dir != CMD_RELOAD_IN) + if (prr->dir != CMD_RELOAD_IN) WALK_LIST(c, p->channels) if (c->channel_state == CS_UP) - channel_request_feeding_dynamic(c, CFRT_AUXILIARY); + if (prr->trie) + { + /* Increase the refeed counter */ + if (atomic_fetch_add_explicit(&prr->counter, 1, memory_order_relaxed) == 0) + { + /* First occurence */ + ASSERT_DIE(this_cli->parser_pool == prr->trie->lp); + rmove(this_cli->parser_pool, &root_pool); + this_cli->parser_pool = lp_new(this_cli->pool); + prr->ev = (event) { + .hook = channel_reload_out_done_main, + .data = prr, + }; + } + else + ASSERT_DIE(this_cli->parser_pool != prr->trie->lp); + + /* Request actually the feeding */ + + struct channel_cmd_reload_feeding_request *req = lp_alloc(prr->trie->lp, sizeof *req); + *req = (struct channel_cmd_reload_feeding_request) { + .cfr = { + .type = CFRT_AUXILIARY, + .done = channel_reload_out_done, + .trie = prr->trie, + }, + .prr = prr, + }; + + channel_request_feeding(c, &req->cfr); + } + else + channel_request_feeding_dynamic(c, CFRT_AUXILIARY); cli_msg(-15, "%s: reloading", p->name); } diff --git a/nest/protocol.h b/nest/protocol.h index 227201dc..a95e1c04 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -280,6 +280,14 @@ struct proto *proto_iterate_named(struct symbol *sym, struct protocol *proto, st #define PROTO_WALK_CMD(sym,pr,p) for(struct proto *p = NULL; p = proto_iterate_named(sym, pr, p); ) +/* Request from CLI to reload multiple protocols */ +struct proto_reload_request { + const struct f_trie *trie; /* Trie to apply */ + _Atomic uint counter; /* How many channels remaining */ + uint dir; /* Direction of reload */ + event ev; /* Event to run when finished */ +}; + #define PROTO_ENTER_FROM_MAIN(p) ({ \ ASSERT_DIE(birdloop_inside(&main_birdloop)); \ struct birdloop *_loop = (p)->loop; \ @@ -678,7 +686,7 @@ static inline void channel_close(struct channel *c) { channel_set_state(c, CS_ST struct channel_feeding_request { struct channel_feeding_request *next; /* Next in request chain */ void (*done)(struct channel_feeding_request *); /* Called when refeed finishes */ - struct f_trie *trie; /* Reload only matching nets */ + const struct f_trie *trie; /* Reload only matching nets */ PACKED enum channel_feeding_request_type { CFRT_DIRECT = 1, /* Refeed by export restart */ CFRT_AUXILIARY, /* Refeed by auxiliary request */ diff --git a/reload_test.conf b/reload_test.conf new file mode 100644 index 00000000..f7463983 --- /dev/null +++ b/reload_test.conf @@ -0,0 +1,38 @@ +log "bird.log" all; + +debug protocols all; +debug channels all; +debug tables all; + +ipv4 table master1; +ipv4 table master2; + +protocol device { + scan time 10; +} + +protocol static static1 { + ipv4 { table master1; }; + route 10.0.0.0/16 unreachable; + route 12.0.0.0/16 unreachable; + route 127.0.0.0/8 unreachable; + route 192.0.0.0/8 unreachable; + route 192.168.0.0/16 unreachable; + route 195.113.26.206/32 unreachable; + route 1.1.1.1/32 unreachable; +} + +ipv4 table ct_4; +protocol pipe { + table master1; + peer table master2; + import filter { + print net; + accept; + }; + export filter { + print net; + accept; + }; +} +