]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
BGP: implement Adj-RIB-In
authorOndrej Zajicek (work) <santiago@crfreenet.org>
Sun, 17 Feb 2019 17:46:28 +0000 (18:46 +0100)
committerOndrej Zajicek (work) <santiago@crfreenet.org>
Sun, 17 Feb 2019 22:02:05 +0000 (23:02 +0100)
The patch implements optional internal import table to a channel and
hooks it to BGP so it can be used as Adj-RIB-In. When enabled, all
received (pre-filtered) routes are stored there and import filters can
be re-evaluated without explicit route refresh. An import table can be
examined using e.g. 'show route import table bgp1.ipv4'.

doc/bird.sgml
lib/event.h
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
proto/bgp/packets.c

index 903456f00d7928bb833f2e0bb2fb1e7815646cfa..153469120bd6aaf26055fc1cb07722a52783a94f 100644 (file)
@@ -2553,6 +2553,16 @@ be used in explicit configuration.
        for every allowed table type. Default: the same as the main table
        the channel is connected to (if eligible).
 
+       <tag><label id="bgp-import-table">import table <m/switch/</tag>
+       A BGP import table contain all received routes from given BGP neighbor,
+       before application of import filters. It is also called <em/Adj-RIB-In/
+       in BGP terminology. BIRD BGP by default operates without import tables,
+       in which case received routes are just processed by import filters,
+       accepted ones are stored in the master table, and the rest is forgotten.
+       Enabling <cf/import table/ allows to store unprocessed routes, which can
+       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-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 d59752220d00241296e5916157d5a548fc3c0e98..03735c3fcc2166046dc1c3bdb3deb5ec470cb574 100644 (file)
@@ -36,5 +36,14 @@ ev_active(event *e)
   return e->n.next != NULL;
 }
 
+static inline event*
+ev_new_init(pool *p, void (*hook)(void *), void *data)
+{
+  event *e = ev_new(p);
+  e->hook = hook;
+  e->data = data;
+  return e;
+}
+
 
 #endif
index 20a62b7f0a6e970f98c62c1d7a60a9a1fa86685b..5cfadecf08e2675145d9a94d0efcb5bf87c876ff 100644 (file)
@@ -562,6 +562,16 @@ r_args:
      $$ = $1;
      $$->fit.flags |= FIF_ORDERED;
    }
+ | r_args IMPORT 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->in_table) cf_error("No import table in channel %s.%s", $4->name, $6);
+     rt_show_add_table($$, c->in_table);
+     $$->tables_defined_by = RSD_TDB_DIRECT;
+   }
  | r_args FILTER filter {
      $$ = $1;
      if ($$->filter != FILTER_ACCEPT) cf_error("Filter specified twice");
index ca22526da2331807903bf30086565bd9288832c7..af9991139e6b9f0493e5fefabf22f2f9904b55b6 100644 (file)
@@ -282,6 +282,54 @@ channel_stop_export(struct channel *c)
   c->stats.exp_routes = 0;
 }
 
+
+/* Called by protocol for reload from in_table */
+void
+channel_schedule_reload(struct channel *c)
+{
+  ASSERT(c->channel_state == CS_UP);
+
+  rt_reload_channel_abort(c);
+  ev_schedule(c->reload_event);
+}
+
+static void
+channel_reload_loop(void *ptr)
+{
+  struct channel *c = ptr;
+
+  if (!rt_reload_channel(c))
+  {
+    ev_schedule(c->reload_event);
+    return;
+  }
+}
+
+static void
+channel_reset_import(struct channel *c)
+{
+  /* Need to abort feeding */
+  ev_postpone(c->reload_event);
+  rt_reload_channel_abort(c);
+
+  rt_prune_sync(c->in_table, 1);
+}
+
+/* Called by protocol to activate in_table */
+void
+channel_setup_in_table(struct channel *c)
+{
+  struct rtable_config *cf = mb_allocz(c->proto->pool, sizeof(struct rtable_config));
+  cf->name = "import";
+  cf->addr_type = c->net_type;
+
+  c->in_table = mb_allocz(c->proto->pool, sizeof(struct rtable));
+  rt_setup(c->proto->pool, c->in_table, cf);
+
+  c->reload_event = ev_new_init(c->proto->pool, channel_reload_loop, c);
+}
+
+
 static void
 channel_do_start(struct channel *c)
 {
@@ -315,6 +363,8 @@ channel_do_flush(struct channel *c)
 static void
 channel_do_down(struct channel *c)
 {
+  ASSERT(!c->feed_active && !c->reload_active);
+
   rem_node(&c->table_node);
   rt_unlock_table(c->table);
   c->proto->active_channels--;
@@ -324,6 +374,9 @@ channel_do_down(struct channel *c)
 
   memset(&c->stats, 0, sizeof(struct proto_stats));
 
+  c->in_table = NULL;
+  c->reload_event = NULL;
+
   CALL(c->channel->cleanup, c);
 
   /* Schedule protocol shutddown */
@@ -355,6 +408,9 @@ channel_set_state(struct channel *c, uint state)
     if (es != ES_DOWN)
       channel_stop_export(c);
 
+    if (c->in_table && (cs == CS_UP))
+      channel_reset_import(c);
+
     break;
 
   case CS_UP:
@@ -374,6 +430,9 @@ channel_set_state(struct channel *c, uint state)
     if (es != ES_DOWN)
       channel_stop_export(c);
 
+    if (c->in_table && (cs == CS_UP))
+      channel_reset_import(c);
+
     channel_do_flush(c);
     break;
 
index 16d1c51147bf0d2d2ff56114b51c9b3ebbf8401d..058f362d346c10de499358d271e284b52de31f79 100644 (file)
@@ -282,8 +282,6 @@ rte_make_tmp_attrs(struct rte *rt, struct linpool *pool)
   return mta ? mta(rt, pool) : NULL;
 }
 
-/* Moved from route.h to avoid dependency conflicts */
-static inline void rte_update(struct proto *p, const net_addr *n, rte *new) { rte_update2(p->main_channel, n, new, p->main_source); }
 
 extern pool *proto_pool;
 extern list proto_list;
@@ -514,6 +512,11 @@ struct channel {
   u8 gr_wait;                          /* Route export to channel is postponed until graceful restart */
 
   btime last_state_change;             /* Time of last state transition */
+
+  struct rtable *in_table;             /* Internal table for received routes */
+  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 */
 };
 
 
@@ -581,6 +584,8 @@ struct channel *proto_add_channel(struct proto *p, struct channel_config *cf);
 int proto_configure_channel(struct proto *p, struct channel **c, struct channel_config *cf);
 
 void channel_set_state(struct channel *c, uint state);
+void channel_setup_in_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); }
 static inline void channel_open(struct channel *c) { channel_set_state(c, CS_UP); }
@@ -592,4 +597,17 @@ void *channel_config_get(const struct channel_class *cc, const char *name, uint
 int channel_reconfigure(struct channel *c, struct channel_config *cf);
 
 
+/* Moved from route.h to avoid dependency conflicts */
+static inline void rte_update(struct proto *p, const net_addr *n, rte *new) { rte_update2(p->main_channel, n, new, p->main_source); }
+
+static inline void
+rte_update3(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;
+
+  rte_update2(c, n, new, src);
+}
+
+
 #endif
index cb0a70836885c83f7ca060613b72613e85fc9807..c7c44eccf15bd146a9dee6336d37ab34cca29774 100644 (file)
@@ -322,6 +322,10 @@ void rt_dump(rtable *);
 void rt_dump_all(void);
 int rt_feed_channel(struct channel *c);
 void rt_feed_channel_abort(struct channel *c);
+int rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *src);
+int rt_reload_channel(struct channel *c);
+void rt_reload_channel_abort(struct channel *c);
+void rt_prune_sync(rtable *t, int all);
 struct rtable_config *rt_new_table(struct symbol *s, uint addr_type);
 void cmd_show_table_stats(struct rtable_config *tab);
 
index c6a7b7065aa85578055c032da9c0bbbe7491b27c..efc1f1d78cde544e069c7e5f6b8aeb701d073f70 100644 (file)
@@ -1080,7 +1080,7 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src)
   int old_ok = rte_is_ok(old);
 
   struct channel_limit *l = &c->rx_limit;
-  if (l->action && !old && new)
+  if (l->action && !old && new && !c->in_table)
     {
       u32 all_routes = stats->imp_routes + stats->filt_routes;
 
@@ -2262,6 +2262,159 @@ ptr_hash(void *ptr)
   return p ^ (p << 8) ^ (p >> 16);
 }
 
+
+int
+rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
+{
+  struct rtable *tab = c->in_table;
+  rte *old, **pos;
+  net *net;
+
+  if (new)
+  {
+    net = net_get(tab, n);
+
+    if (!new->pref)
+      new->pref = c->preference;
+
+    if (!rta_is_cached(new->attrs))
+      new->attrs = rta_lookup(new->attrs);
+  }
+  else
+  {
+    net = net_find(tab, n);
+
+    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))
+       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;
+  }
+
+  struct channel_limit *l = &c->rx_limit;
+  if (l->action && !old)
+  {
+    if (tab->route_count >= l->limit)
+      channel_notify_limit(c, l, PLD_RX, tab->route_count);
+
+    if (l->state == PLS_BLOCKED)
+    {
+      rte_trace_in(D_FILTERS, c->proto, new, "ignored [limit]");
+      goto drop_update;
+    }
+  }
+
+  /* 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:
+  c->stats.imp_updates_received++;
+  c->stats.imp_updates_ignored++;
+  rte_free(new);
+  return 0;
+
+drop_withdraw:
+  c->stats.imp_withdraws_received++;
+  c->stats.imp_withdraws_ignored++;
+  return 0;
+}
+
+int
+rt_reload_channel(struct channel *c)
+{
+  struct rtable *tab = c->in_table;
+  struct fib_iterator *fit = &c->reload_fit;
+  int max_feed = 64;
+
+  ASSERT(c->channel_state == CS_UP);
+
+  if (!c->reload_active)
+  {
+    FIB_ITERATE_INIT(fit, &tab->fib);
+    c->reload_active = 1;
+  }
+
+  FIB_ITERATE_START(&tab->fib, fit, net, n)
+  {
+    if (max_feed <= 0)
+    {
+      FIB_ITERATE_PUT(fit);
+      return 0;
+    }
+
+    for (rte *e = n->routes; e; e = e->next)
+    {
+      rte_update2(c, n->n.addr, rte_do_cow(e), e->attrs->src);
+      max_feed--;
+    }
+  }
+  FIB_ITERATE_END;
+
+  c->reload_active = 0;
+  return 1;
+}
+
+void
+rt_reload_channel_abort(struct channel *c)
+{
+  if (c->reload_active)
+  {
+    /* Unlink the iterator */
+    fit_get(&c->in_table->fib, &c->reload_fit);
+    c->reload_active = 0;
+  }
+}
+
+void
+rt_prune_sync(rtable *t, int all)
+{
+  FIB_WALK(&t->fib, net, n)
+  {
+    rte *e, **ee = &n->routes;
+    while (e = *ee)
+    {
+      if (all || (e->flags & (REF_STALE | REF_DISCARD)))
+      {
+       *ee = e->next;
+       rte_free_quick(e);
+       t->route_count--;
+      }
+      else
+       ee = &e->next;
+    }
+  }
+  FIB_WALK_END;
+}
+
+
 static inline u32
 hc_hash(ip_addr a, rtable *dep)
 {
index 9eb24bec95d78f141e9bb39071f41fb2ae92ac15..1c6bbf238381b682d2286a890a98d3d239baa0d8 100644 (file)
@@ -539,7 +539,7 @@ bgp_conn_enter_established_state(struct bgp_conn *conn)
 
     int active = loc->ready && rem->ready;
     c->c.disabled = !active;
-    c->c.reloadable = p->route_refresh;
+    c->c.reloadable = p->route_refresh || c->cf->import_table;
 
     c->index = active ? num++ : 0;
 
@@ -762,6 +762,9 @@ bgp_refresh_begin(struct bgp_channel *c)
 
   c->load_state = BFS_REFRESHING;
   rt_refresh_begin(c->c.table, &c->c);
+
+  if (c->c.in_table)
+    rt_refresh_begin(c->c.in_table, &c->c);
 }
 
 /**
@@ -783,6 +786,9 @@ bgp_refresh_end(struct bgp_channel *c)
 
   c->load_state = BFS_NONE;
   rt_refresh_end(c->c.table, &c->c);
+
+  if (c->c.in_table)
+    rt_prune_sync(c->c.in_table, 0);
 }
 
 
@@ -1199,9 +1205,12 @@ bgp_reload_routes(struct channel *C)
   struct bgp_proto *p = (void *) C->proto;
   struct bgp_channel *c = (void *) C;
 
-  ASSERT(p->conn && p->route_refresh);
+  ASSERT(p->conn && (p->route_refresh || c->c.in_table));
 
-  bgp_schedule_packet(p->conn, c, PKT_ROUTE_REFRESH);
+  if (c->c.in_table)
+    channel_schedule_reload(C);
+  else
+    bgp_schedule_packet(p->conn, c, PKT_ROUTE_REFRESH);
 }
 
 static void
@@ -1505,6 +1514,9 @@ bgp_channel_start(struct channel *C)
   bgp_init_bucket_table(c);
   bgp_init_prefix_table(c);
 
+  if (c->cf->import_table)
+   channel_setup_in_table(C);
+
   c->next_hop_addr = c->cf->next_hop_addr;
   c->link_addr = IPA_NONE;
   c->packets_to_send = 0;
index 27d9350a28924c3f7c6fc61978b4ea01f788044f..8e7fe15e91632437fed53adf2b219e9ca9563534 100644 (file)
@@ -142,6 +142,7 @@ struct bgp_channel_config {
   u8 gr_able;                          /* Allow full graceful restart for the channel */
   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 */
 
   uint rest[0];                                /* Remaining items are reconfigured separately */
   struct rtable_config *igp_table_ip4; /* Table for recursive IPv4 next hop lookups */
index 41eaa729bc9155a737756cae57ce2707d53d9202..634d01b26f1f97d9c1d1d6d46ed5944d997e2754 100644 (file)
@@ -28,7 +28,7 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY, KEEPALIVE,
        BGP_CLUSTER_LIST, IGP, TABLE, GATEWAY, DIRECT, RECURSIVE, MED, TTL,
        SECURITY, DETERMINISTIC, SECONDARY, ALLOW, BFD, ADD, PATHS, RX, TX,
        GRACEFUL, RESTART, AWARE, CHECK, LINK, PORT, EXTENDED, MESSAGES, SETKEY,
-       STRICT, BIND, CONFEDERATION, MEMBER, MULTICAST, FLOW4, FLOW6)
+       STRICT, BIND, CONFEDERATION, MEMBER, MULTICAST, FLOW4, FLOW6, IMPORT)
 
 %type <i32> bgp_afi
 
@@ -218,6 +218,7 @@ bgp_channel_item:
  | ADD PATHS RX { BGP_CC->add_path = BGP_ADD_PATH_RX; }
  | 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; }
  | IGP TABLE rtable {
     if (BGP_CC->desc->no_igp)
       cf_error("IGP table not allowed here");
index 1376ba6736a9557d8d08d92a2a03526919f9f67a..a0f8d9178462a0c4452008a76275b45bab0a5bf8 100644 (file)
@@ -1108,7 +1108,7 @@ bgp_rte_update(struct bgp_parse_state *s, net_addr *n, u32 path_id, rta *a0)
   if (!a0)
   {
     /* Route withdraw */
-    rte_update2(&s->channel->c, n, NULL, s->last_src);
+    rte_update3(&s->channel->c, n, NULL, s->last_src);
     return;
   }
 
@@ -1128,7 +1128,7 @@ bgp_rte_update(struct bgp_parse_state *s, net_addr *n, u32 path_id, rta *a0)
 
   e->pflags = 0;
   e->u.bgp.suppressed = 0;
-  rte_update2(&s->channel->c, n, e, s->last_src);
+  rte_update3(&s->channel->c, n, e, s->last_src);
 }
 
 static void