]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
BGP: implement Adj-RIB-Out
authorOndrej Zajicek (work) <santiago@crfreenet.org>
Tue, 13 Aug 2019 16:43:36 +0000 (18:43 +0200)
committerOndrej Zajicek (work) <santiago@crfreenet.org>
Tue, 13 Aug 2019 16:43:36 +0000 (18:43 +0200)
The patch implements optional internal export table to a channel and
hooks it to BGP so it can be used as Adj-RIB-Out. When enabled, all
exported (post-filtered) routes are stored there. An export table can be
examined using e.g. 'show route export table bgp1.ipv4'.

doc/bird.sgml
nest/config.Y
nest/proto.c
nest/protocol.h
nest/route.h
nest/rt-table.c
proto/bgp/bgp.c
proto/bgp/bgp.h
proto/bgp/config.Y

index a251450067c649eca914e1f474ed4506fa525b8f..46073bc666e5af598067b224d95235ca89755211 100644 (file)
@@ -2563,6 +2563,16 @@ be used in explicit configuration.
        be examined later by <cf/show route/, and can be used to reconfigure
        import filters without full route refresh. Default: off.
 
+       <tag><label id="bgp-export-table">export table <m/switch/</tag>
+       A BGP export table contains all routes sent to given BGP neighbor, after
+       application of export filters. It is also called <em/Adj-RIB-Out/ in BGP
+       terminology. BIRD BGP by default operates without export tables, in
+       which case routes from master table are just processed by export filters
+       and then announced by BGP. Enabling <cf/export table/ allows to store
+       routes after export filter processing, so they can be examined later by
+       <cf/show route/, and can be used to eliminate unnecessary updates or
+       withdraws. Default: off.
+
        <tag><label id="bgp-secondary">secondary <m/switch/</tag>
        Usually, if an export filter rejects a selected route, no other route is
        propagated for that network. This option allows to try the next route in
index 959802824a0633b4609d9b5341f56111671177e5..98f631d697967ae53b70a9f4078cd15e2c88b3af 100644 (file)
@@ -573,6 +573,16 @@ r_args:
      rt_show_add_table($$, c->in_table);
      $$->tables_defined_by = RSD_TDB_DIRECT;
    }
+ | r_args EXPORT TABLE SYM '.' r_args_channel {
+     $$ = $1;
+     struct proto_config *cf = (void *) $4->def;
+     if ($4->class != SYM_PROTO || !cf->proto) cf_error("%s is not a protocol", $4->name);
+     struct channel *c = proto_find_channel_by_name(cf->proto, $6);
+     if (!c) cf_error("Channel %s.%s not found", $4->name, $6);
+     if (!c->out_table) cf_error("No export table in channel %s.%s", $4->name, $6);
+     rt_show_add_table($$, c->out_table);
+     $$->tables_defined_by = RSD_TDB_DIRECT;
+   }
  | r_args FILTER filter {
      $$ = $1;
      if ($$->filter != FILTER_ACCEPT) cf_error("Filter specified twice");
index 0e82bf6908f36b80aa1394407851408c13d00051..3d55f57b9fc580ceb3088d2c20ca6abae3972588 100644 (file)
@@ -315,6 +315,13 @@ channel_reset_import(struct channel *c)
   rt_prune_sync(c->in_table, 1);
 }
 
+static void
+channel_reset_export(struct channel *c)
+{
+  /* Just free the routes */
+  rt_prune_sync(c->out_table, 1);
+}
+
 /* Called by protocol to activate in_table */
 void
 channel_setup_in_table(struct channel *c)
@@ -329,6 +336,18 @@ channel_setup_in_table(struct channel *c)
   c->reload_event = ev_new_init(c->proto->pool, channel_reload_loop, c);
 }
 
+/* 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;
+
+  c->out_table = mb_allocz(c->proto->pool, sizeof(struct rtable));
+  rt_setup(c->proto->pool, c->out_table, cf);
+}
+
 
 static void
 channel_do_start(struct channel *c)
@@ -376,6 +395,7 @@ channel_do_down(struct channel *c)
 
   c->in_table = NULL;
   c->reload_event = NULL;
+  c->out_table = NULL;
 
   CALL(c->channel->cleanup, c);
 
@@ -411,6 +431,9 @@ channel_set_state(struct channel *c, uint state)
     if (c->in_table && (cs == CS_UP))
       channel_reset_import(c);
 
+    if (c->out_table && (cs == CS_UP))
+      channel_reset_export(c);
+
     break;
 
   case CS_UP:
@@ -433,6 +456,9 @@ channel_set_state(struct channel *c, uint state)
     if (c->in_table && (cs == CS_UP))
       channel_reset_import(c);
 
+    if (c->out_table && (cs == CS_UP))
+      channel_reset_export(c);
+
     channel_do_flush(c);
     break;
 
index 0132addf42f78d47484b835bbfe7ae6ac7354da4..4d8584f2b7b29c17e4cc7c51299099ace30536d8 100644 (file)
@@ -520,6 +520,8 @@ struct channel {
   struct event *reload_event;          /* Event responsible for reloading from in_table */
   struct fib_iterator reload_fit;      /* Iterator in in_table used during reloading */
   u8 reload_active;                    /* Iterator reload_fit is linked */
+
+  struct rtable *out_table;            /* Internal table for exported routes */
 };
 
 
@@ -588,6 +590,7 @@ int proto_configure_channel(struct proto *p, struct channel **c, struct channel_
 
 void channel_set_state(struct channel *c, uint state);
 void channel_setup_in_table(struct channel *c);
+void channel_setup_out_table(struct channel *c);
 void channel_schedule_reload(struct channel *c);
 
 static inline void channel_init(struct channel *c) { channel_set_state(c, CS_START); }
index f0f4a6dd2219f09b48ba227d0e131091798ed4e2..cbc84934a886634c64cb28936ca4dcc6784ec8b8 100644 (file)
@@ -329,6 +329,7 @@ int rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src
 int rt_reload_channel(struct channel *c);
 void rt_reload_channel_abort(struct channel *c);
 void rt_prune_sync(rtable *t, int all);
+int rte_update_out(struct channel *c, const net_addr *n, rte *new, rte *old0, int refeed);
 struct rtable_config *rt_new_table(struct symbol *s, uint addr_type);
 void cmd_show_table_stats(struct rtable_config *tab);
 
index 31cdb55918d85066a0f751dd4aaabf5f47952928..429df0ba5ccd01bc0a6d69a53154b231c007279e 100644 (file)
@@ -566,7 +566,6 @@ do_rt_notify(struct channel *c, net *net, rte *new, rte *old, ea_list *tmpa, int
   struct proto *p = c->proto;
   struct proto_stats *stats = &c->stats;
 
-
   /*
    * First, apply export limit.
    *
@@ -612,6 +611,8 @@ do_rt_notify(struct channel *c, net *net, rte *new, rte *old, ea_list *tmpa, int
        }
     }
 
+  if (c->out_table && !rte_update_out(c, net->n.addr, new, old, refeed))
+    return;
 
   if (new)
     stats->exp_updates_accepted++;
@@ -2416,6 +2417,10 @@ ptr_hash(void *ptr)
 }
 
 
+/*
+ *     Import table
+ */
+
 int
 rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
 {
@@ -2568,6 +2573,89 @@ rt_prune_sync(rtable *t, int all)
 }
 
 
+/*
+ *     Export table
+ */
+
+int
+rte_update_out(struct channel *c, const net_addr *n, rte *new, rte *old0, int refeed)
+{
+  struct rtable *tab = c->out_table;
+  struct rte_src *src;
+  rte *old, **pos;
+  net *net;
+
+  if (new)
+  {
+    net = net_get(tab, n);
+    src = new->attrs->src;
+  }
+  else
+  {
+    net = net_find(tab, n);
+    src = old0->attrs->src;
+
+    if (!net)
+      goto drop_withdraw;
+  }
+
+  /* Find the old rte */
+  for (pos = &net->routes; old = *pos; pos = &old->next)
+    if (old->attrs->src == src)
+    {
+      if (new && rte_same(old, new))
+      {
+       /* REF_STALE / REF_DISCARD not used in export table */
+       /*
+       if (old->flags & (REF_STALE | REF_DISCARD | REF_MODIFY))
+       {
+         old->flags &= ~(REF_STALE | REF_DISCARD | REF_MODIFY);
+         return 1;
+       }
+       */
+
+       goto drop_update;
+      }
+
+      /* Remove the old rte */
+      *pos = old->next;
+      rte_free_quick(old);
+      tab->route_count--;
+
+      break;
+    }
+
+  if (!new)
+  {
+    if (!old)
+      goto drop_withdraw;
+
+    return 1;
+  }
+
+  /* Insert the new rte */
+  rte *e = rte_do_cow(new);
+  e->flags |= REF_COW;
+  e->net = net;
+  e->sender = c;
+  e->lastmod = current_time();
+  e->next = *pos;
+  *pos = e;
+  tab->route_count++;
+  return 1;
+
+drop_update:
+  return refeed;
+
+drop_withdraw:
+  return 0;
+}
+
+
+/*
+ *     Hostcache
+ */
+
 static inline u32
 hc_hash(ip_addr a, rtable *dep)
 {
index 98fdd6d4bdac857c68065896b94f9011487faab2..c2b3bd1899c415b40a40f227523844023860aa23 100644 (file)
@@ -1539,6 +1539,9 @@ bgp_channel_start(struct channel *C)
   if (c->cf->import_table)
    channel_setup_in_table(C);
 
+  if (c->cf->export_table)
+    channel_setup_out_table(C);
+
   c->next_hop_addr = c->cf->next_hop_addr;
   c->link_addr = IPA_NONE;
   c->packets_to_send = 0;
@@ -1841,8 +1844,9 @@ bgp_channel_reconfigure(struct channel *C, struct channel_config *CC, int *impor
       (new->ext_next_hop != old->ext_next_hop) ||
       (new->add_path != old->add_path) ||
       (new->import_table != old->import_table) ||
-      (IGP_TABLE(old, ip4) != IGP_TABLE(new, ip4)) ||
-      (IGP_TABLE(old, ip6) != IGP_TABLE(new, ip6)))
+      (new->export_table != old->export_table) ||
+      (IGP_TABLE(new, ip4) != IGP_TABLE(old, ip4)) ||
+      (IGP_TABLE(new, ip6) != IGP_TABLE(old, ip6)))
     return 0;
 
   if (new->gw_mode != old->gw_mode)
index 3f8ac90b61c6eb09becdcff5ba9707874542b660..0fcce87833a425f98c3f4b08090775123e94099c 100644 (file)
@@ -143,6 +143,7 @@ struct bgp_channel_config {
   u8 ext_next_hop;                     /* Allow both IPv4 and IPv6 next hops */
   u8 add_path;                         /* Use ADD-PATH extension [RFC 7911] */
   u8 import_table;                     /* Use c.in_table as Adj-RIB-In */
+  u8 export_table;                     /* Use c.out_table as Adj-RIB-Out */
 
   struct rtable_config *igp_table_ip4; /* Table for recursive IPv4 next hop lookups */
   struct rtable_config *igp_table_ip6; /* Table for recursive IPv6 next hop lookups */
index 634d01b26f1f97d9c1d1d6d46ed5944d997e2754..c20cb67945ea5b11ffa82e1ef6ffda2e4cdb4c17 100644 (file)
@@ -219,6 +219,7 @@ bgp_channel_item:
  | ADD PATHS TX { BGP_CC->add_path = BGP_ADD_PATH_TX; }
  | ADD PATHS bool { BGP_CC->add_path = $3 ? BGP_ADD_PATH_FULL : 0; }
  | IMPORT TABLE bool { BGP_CC->import_table = $3; }
+ | EXPORT TABLE bool { BGP_CC->export_table = $3; }
  | IGP TABLE rtable {
     if (BGP_CC->desc->no_igp)
       cf_error("IGP table not allowed here");