0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-12-22 09:41:54 +00:00

Partial reloads of channels

Now it's possible to reload only part of routes, e.g. when ROA has
changed.
This commit is contained in:
Katerina Kubecova 2023-10-03 14:07:11 +02:00 committed by Maria Matejka
parent 898cc7ee9b
commit b3e33da83d
5 changed files with 161 additions and 19 deletions

View File

@ -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;

View File

@ -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 <net_ptr> r_args_for
%type <t> channel_sym
%type <c> channel_arg
%type <const_trie> partial_opt
CF_GRAMMAR
@ -892,18 +904,28 @@ CF_CLI(DUMP FILTER ALL,,, [[Dump all filters in linearized form]])
CF_CLI(EVAL, term, <expr>, [[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, (<protocol> | \"<pattern>\" | all) [message], [[Disable protocol]])
{ proto_apply_cmd($2, proto_cmd_disable, 1, (uintptr_t) $3); } ;
CF_CLI(ENABLE, proto_patt opttext, (<protocol> | \"<pattern>\" | all) [message], [[Enable protocol]])
{ proto_apply_cmd($2, proto_cmd_enable, 1, (uintptr_t) $3); } ;
CF_CLI(RESTART, proto_patt opttext, (<protocol> | \"<pattern>\" | all) [message], [[Restart protocol]])
{ proto_apply_cmd($2, proto_cmd_restart, 1, (uintptr_t) $3); } ;
CF_CLI(RELOAD, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol]])
{ proto_apply_cmd($2, proto_cmd_reload, 1, CMD_RELOAD); } ;
CF_CLI(RELOAD IN, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol (just imported routes)]])
{ proto_apply_cmd($3, proto_cmd_reload, 1, CMD_RELOAD_IN); } ;
CF_CLI(RELOAD OUT, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol (just exported routes)]])
{ proto_apply_cmd($3, proto_cmd_reload, 1, CMD_RELOAD_OUT); } ;
CF_CLI(RELOAD, proto_patt partial_opt, <protocol> | \"<pattern>\" | all, [[Reload protocol]])
{ proto_call_cmd_reload($2, CMD_RELOAD, $3); } ;
CF_CLI(RELOAD IN, proto_patt partial_opt, <protocol> | \"<pattern>\" | all, [[Reload protocol (just imported routes)]])
{ proto_call_cmd_reload($3, CMD_RELOAD_IN, $4); } ;
CF_CLI(RELOAD OUT, proto_patt partial_opt, <protocol> | \"<pattern>\" | 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, (<protocol> | <channel> | \"<pattern>\" | all) (all | off | { states|routes|filters|interfaces|events|packets [, ...] }), [[Control protocol debugging via BIRD logs]])

View File

@ -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
@ -372,6 +371,8 @@ channel_roa_out_changed(struct settle *se)
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) {
.type = CFRT_AUXILIARY,
@ -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);
}

View File

@ -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 */

38
reload_test.conf Normal file
View File

@ -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;
};
}