]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Nest: Route generations and explicit tracking route propagion through pipes
authorMaria Matejka <mq@ucw.cz>
Fri, 1 May 2020 20:26:24 +0000 (22:26 +0200)
committerMaria Matejka <mq@ucw.cz>
Tue, 9 Nov 2021 18:20:41 +0000 (19:20 +0100)
conf/conf.h
doc/bird.sgml
nest/config.Y
nest/route.h
nest/rt-table.c
proto/pipe/config.Y
proto/pipe/pipe.c
proto/pipe/pipe.h

index 55cb9c58780617007bdcb8b1bbe37cfbe39e7928..69ef8a103f30055efce93206eda43d66b8257790 100644 (file)
@@ -45,6 +45,7 @@ struct config {
 
   int cli_debug;                       /* Tracing of CLI connections and commands */
   int latency_debug;                   /* I/O loop tracks duration of each event */
+  int pipe_debug;                      /* Track route propagation through pipes */
   u32 latency_limit;                   /* Events with longer duration are logged (us) */
   u32 watchdog_warning;                        /* I/O loop watchdog limit for warning (us) */
   u32 watchdog_timeout;                        /* Watchdog timeout (in seconds, 0 = disabled) */
index a2138b55cd3a1f25bfd3c53bc7f9bfa2cbc38927..d1a3b70f29cd18e2108ef3583cfbe38420cdbf10 100644 (file)
@@ -4124,6 +4124,14 @@ include standard channel config options; see the example below.
        <tag><label id="pipe-peer-table">peer table <m/table/</tag>
        Defines secondary routing table to connect to. The primary one is
        selected by the <cf/table/ keyword.
+
+       <tag><label id="pipe-max-generation">max generation <m/expr/</tag>
+       Sets maximal generation of route that may pass through this pipe.
+       The generation value is increased by one by each pipe on its path.
+       Not meeting this requirement causes an error message complaining about
+       an overpiped route. If you have long chains of pipes, you probably want
+       to raise this value; anyway the default of 16 should be enough for even
+       most strange uses. Maximum is 254.
 </descrip>
 
 <sect1>Attributes
index ef3e27c05081979c00913c91c3f489ff1f736c0b..a1d901ab1a417ca5911441c37208fc8a5d161094 100644 (file)
@@ -111,7 +111,7 @@ proto_postconfig(void)
 
 CF_DECLS
 
-CF_KEYWORDS(ROUTER, ID, HOSTNAME, PROTOCOL, TEMPLATE, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT)
+CF_KEYWORDS(ROUTER, ID, HOSTNAME, PROTOCOL, TEMPLATE, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT, PIPE)
 CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, VRF, DEFAULT, TABLE, STATES, ROUTES, FILTERS)
 CF_KEYWORDS(IPV4, IPV6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6, SADR, MPLS)
 CF_KEYWORDS(RECEIVE, LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE, KEEP, FILTERED, RPKI)
@@ -348,6 +348,7 @@ debug_default:
    DEBUG PROTOCOLS debug_mask { new_config->proto_default_debug = $3; }
  | DEBUG CHANNELS debug_mask { new_config->channel_default_debug = $3; }
  | DEBUG COMMANDS expr { new_config->cli_debug = $3; }
+ | DEBUG PIPE bool { new_config->pipe_debug = $3; }
  ;
 
 /* MRTDUMP PROTOCOLS is in systep/unix/config.Y */
index 98d605c8f319685043e1299cfaac8128ad298003..9baaeda09b08eb2990df0ad0e95492f9eefead6a 100644 (file)
@@ -162,7 +162,6 @@ typedef struct rtable {
   char *name;                          /* Name of this table */
   list channels;                       /* List of attached channels (struct channel) */
   uint addr_type;                      /* Type of address data stored in table (NET_*) */
-  int pipe_busy;                       /* Pipe loop detection */
   int use_count;                       /* Number of protocols using this table */
   u32 rt_count;                                /* Number of routes in the table */
 
@@ -185,6 +184,7 @@ typedef struct rtable {
   byte nhu_state;                      /* Next Hop Update state */
   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 */
 
   list subscribers;                    /* Subscribers for notifications */
   struct timer *settle_timer;          /* Settle time for notifications */
@@ -243,6 +243,9 @@ typedef struct rte {
   u32 id;                              /* Table specific route id */
   byte flags;                          /* Table-specific flags */
   byte pflags;                         /* Protocol-specific flags */
+  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. */
 } rte;
 
 struct rte_storage {
index b005f6f30a52232508ea5c95356ce100f6f4ca1b..6851b4bce0e64eb62f8b990688b19b975117f18f 100644 (file)
@@ -837,7 +837,6 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src)
   struct proto *p = c->proto;
   struct rtable *table = c->table;
   struct proto_stats *stats = &c->stats;
-  static struct tbf rl_pipe = TBF_DEFAULT_LOG_LIMITS;
   struct rte_storage *old_best_stored = net->routes, *old_stored = NULL;
   rte *old_best = old_best_stored ? &old_best_stored->rte : NULL;
   rte *old = NULL;
@@ -849,22 +848,22 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src)
     {
       old = &(old_stored = (*before_old))->rte;
 
-         /* If there is the same route in the routing table but from
-          * a different sender, then there are two paths from the
-          * source protocol to this routing table through transparent
-          * pipes, which is not allowed.
-          *
-          * We log that and ignore the route. If it is withdraw, we
-          * ignore it completely (there might be 'spurious withdraws',
-          * see FIXME in do_rte_announce())
-          */
-         if (old->sender->proto != p)
-           {
-             if (new)
-                 log_rl(&rl_pipe, L_ERR "Pipe collision detected when sending %N to table %s",
-                     net->n.addr, table->name);
-             return;
-           }
+      /* If there is the same route in the routing table but from
+       * a different sender, then there are two paths from the
+       * source protocol to this routing table through transparent
+       * pipes, which is not allowed.
+       * We log that and ignore the route. */
+      if (old->sender->proto != p)
+       {
+         if (!old->generation && !new->generation)
+           bug("Two protocols claim to author a route with the same rte_src in table %s: %N %s/%u:%u",
+               c->table->name, net->n.addr, old->src->proto->name, old->src->private_id, old->src->global_id);
+
+         log_rl(&table->rl_pipe, L_ERR "Route source collision in table %s: %N %s/%u:%u",
+               c->table->name, net->n.addr, old->src->proto->name, old->src->private_id, old->src->global_id);
+
+         return;
+       }
 
          if (new && rte_same(old, new))
            {
@@ -875,14 +874,15 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src)
              if (!rte_is_filtered(new))
                {
                  stats->imp_updates_ignored++;
-                 rte_trace_in(D_ROUTES, c, new, "ignored");
+                 rte_trace_in(D_ROUTES, c, new, "ignored");
                }
 
-             return;
-           }
+           return;
+         }
 
-         *before_old = (*before_old)->next;
-         table->rt_count--;
+
+       *before_old = (*before_old)->next;
+       table->rt_count--;
     }
 
   if (!old && !new)
@@ -1128,7 +1128,6 @@ rte_update(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
   if (c->in_table && !rte_update_in(c, n, new, src))
     return;
 
-  // struct proto *p = c->proto;
   struct proto_stats *stats = &c->stats;
   const struct filter *filter = c->in_filter;
   net *nn;
@@ -1595,6 +1594,8 @@ rt_setup(pool *pp, struct rtable_config *cf)
 
     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;
   }
 
   return t;
index 1202c169d2d5d77cd3638003c8b0dfdfca92bdb4..c869de9f72754e614da9d2231cae92d9c6c065b6 100644 (file)
@@ -16,7 +16,7 @@ CF_DEFINES
 
 CF_DECLS
 
-CF_KEYWORDS(PIPE, PEER, TABLE)
+CF_KEYWORDS(PIPE, PEER, TABLE, MAX, GENERATION)
 
 CF_GRAMMAR
 
@@ -25,6 +25,7 @@ proto: pipe_proto '}' { this_channel = NULL; }  ;
 pipe_proto_start: proto_start PIPE
 {
   this_proto = proto_config_new(&proto_pipe, $1);
+  PIPE_CFG->max_generation = 16;
 }
 proto_name
 {
@@ -41,6 +42,10 @@ pipe_proto:
  | pipe_proto proto_item ';'
  | pipe_proto channel_item_ ';'
  | pipe_proto PEER TABLE rtable ';' { PIPE_CFG->peer = $4; }
+ | pipe_proto MAX GENERATION expr ';' {
+    if (($4 < 1) || ($4 > 254)) cf_error("Max generation must be in range 1..254, got %u", $4);
+    PIPE_CFG->max_generation = $4;
+  }
  ;
 
 CF_CODE
index 3caa85d0496c9071ea12b4b3e2d6fcfd3b19b676..7604cc796c08860f83b483ef80a7757af851ea83 100644 (file)
@@ -56,15 +56,6 @@ pipe_rt_notify(struct proto *P, struct channel *src_ch, const net_addr *n, rte *
   if (!new && !old)
     return;
 
-  if (dst->table->pipe_busy)
-    {
-      log(L_ERR "Pipe loop detected when sending %N to table %s",
-         n, dst->table->name);
-      return;
-    }
-
-  src_ch->table->pipe_busy = 1;
-
   if (new)
     {
       rta *a = alloca(rta_size(new->attrs));
@@ -76,23 +67,34 @@ pipe_rt_notify(struct proto *P, struct channel *src_ch, const net_addr *n, rte *
       rte e0 = {
        .attrs = a,
        .src = new->src,
+       .generation = new->generation + 1,
       };
 
       rte_update(dst, n, &e0, new->src);
     }
   else
     rte_update(dst, n, NULL, old->src);
-
-  src_ch->table->pipe_busy = 0;
 }
 
 static int
 pipe_preexport(struct channel *c, rte *e)
 {
-  struct proto *pp = e->sender->proto;
+  struct pipe_proto *p = (void *) c->proto;
+
+  /* Avoid direct loopbacks */
+  if (e->sender == c)
+    return -1;
 
-  if (pp == c->proto)
-    return -1; /* Avoid local loops automatically */
+  /* Indirection check */
+  uint max_generation = ((struct pipe_config *) p->p.cf)->max_generation;
+  if (e->generation >= max_generation)
+  {
+    log_rl(&p->rl_gen, L_ERR "Route overpiped (%u hops of %u configured in %s) in table %s: %N %s/%u:%u",
+       e->generation, max_generation, c->proto->name,
+       c->table->name, e->net, e->src->proto->name, e->src->private_id, e->src->global_id);
+
+    return -1;
+  }
 
   return 0;
 }
@@ -177,6 +179,8 @@ pipe_init(struct proto_config *CF)
   P->preexport = pipe_preexport;
   P->reload_routes = pipe_reload_routes;
 
+  p->rl_gen = (struct tbf) TBF_DEFAULT_LOG_LIMITS;
+
   pipe_configure_channels(p, cf);
 
   return P;
index 038c66667448b0f506ac7de32453b5d40adb70ca..60c857eb620bced417abc4106f6b58dfc81ed279 100644 (file)
 struct pipe_config {
   struct proto_config c;
   struct rtable_config *peer;          /* Table we're connected to */
+  u8 max_generation;
 };
 
 struct pipe_proto {
   struct proto p;
   struct channel *pri;
   struct channel *sec;
+  struct tbf rl_gen;
 };
 
 #endif