]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Partial reloads of channels
authorKaterina Kubecova <katerina.kubecova@nic.cz>
Tue, 3 Oct 2023 12:07:11 +0000 (14:07 +0200)
committerMaria Matejka <mq@ucw.cz>
Thu, 5 Oct 2023 10:08:32 +0000 (12:08 +0200)
Now it's possible to reload only part of routes, e.g. when ROA has
changed.

conf/confbase.Y
nest/config.Y
nest/proto.c
nest/protocol.h
reload_test.conf [new file with mode: 0644]

index 8e5da9e3f792e86f5ecea4dbf5ef13808fe5bfdc..c764f9e4f2cb9fc7e2048698918e0e4abc28d8e0 100644 (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;
index 2e70461ae309b8480991fc96732a59634ff023e5..e0329d400f2db0167d3634ff91450877bb125c6d 100644 (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]])
index 767395565dd0e3001697dc709f77d00511f2b427..f62d5cf210dc13b36394bae677461703ee8f9321 100644 (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
@@ -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);
 }
 
+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 dir, int cnt UNUSED)
+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);
 }
index 227201dc83e715bd1285f0d9fc8cbf87b75180c4..a95e1c0461d498d97ea311c8edb5472784685f8b 100644 (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 */
diff --git a/reload_test.conf b/reload_test.conf
new file mode 100644 (file)
index 0000000..f746398
--- /dev/null
@@ -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;
+       };
+}
+