[PATCH] [RFC] Babel: Replace internal route selection by bird nest

Daniel Gröber dxld at darkboxed.org
Mon Jan 30 00:54:15 CET 2023


The main motivation for this change is to allow for ingress route
filtering. With the current internal route selection proto/babel exports
only the one route it selected and an admin cannot decide which
neighbours/interfaces to import certain routes from using filters.

To fix this we rip out the internal route selection entirely and let bird
do it's thing instead.

Note that this does very much represent a breaking change as now export
filters have to explicitly re-export babel routes which was implicit
before, using say `source = RTS_BABEL`.

It's unclear to me if we should support the old behaviour and how I would
circumvent the export filter for babel routes if so. Comments and
suggestions welcome.

TODO:
 - Unreachable routes are still kind of funky since rebasing on master from
   2.0.10. I see both a unicast routes and an unreachable route sometimes.
---
Note: this depends on "Babel: Remove unecessary FIB_ITERATE restart",
I thought it wouldn't or I would have sent it as a series.

 proto/babel/babel.c | 177 +++++++++++++++-----------------------------
 proto/babel/babel.h |   5 +-
 2 files changed, 62 insertions(+), 120 deletions(-)

diff --git a/proto/babel/babel.c b/proto/babel/babel.c
index bed2e1e4..1fe73cfa 100644
--- a/proto/babel/babel.c
+++ b/proto/babel/babel.c
@@ -29,10 +29,11 @@
  * possible routes for the prefix are tracked as &babel_route entries and the
  * feasibility distance is maintained through &babel_source structures.
  *
- * The main route selection is done in babel_select_route(). This is called when
- * an entry is updated by receiving updates from the network or when modified by
- * internal timers. The function selects from feasible and reachable routes the
- * one with the lowest metric to be announced to the core.
+ * The main route selection is done by the bird nest (heh). For each prefix we
+ * export all feasible routes to the core with a distinct source (rte_src) per
+ * neihbour. Bird will then notify us of the optimal one by calling
+ * babel_rt_notify and we remember which neighbour it selected in
+ * babel_entry.selected_nbr.
  */
 
 #include <stdlib.h>
@@ -50,7 +51,7 @@ static inline int ge_mod64k(uint a, uint b)
 { return (u16)(a - b) < 0x8000; }
 
 static void babel_expire_requests(struct babel_proto *p, struct babel_entry *e);
-static void babel_select_route(struct babel_proto *p, struct babel_entry *e, struct babel_route *mod);
+static void babel_announce_rte(struct babel_proto *p, struct babel_entry *e, struct babel_route *r);
 static inline void babel_announce_retraction(struct babel_proto *p, struct babel_entry *e);
 static void babel_send_route_request(struct babel_proto *p, struct babel_entry *e, struct babel_neighbor *n);
 static void babel_send_seqno_request(struct babel_proto *p, struct babel_entry *e, struct babel_seqno_request *sr, struct babel_neighbor *n);
@@ -168,13 +169,19 @@ babel_get_route(struct babel_proto *p, struct babel_entry *e, struct babel_neigh
   return r;
 }
 
+static struct babel_route *
+babel_get_selected_route(struct babel_proto *p, struct babel_entry *e)
+{
+  if (e->selected_nbr)
+    return babel_get_route(p, e, e->selected_nbr);
+  return NULL;
+}
+
 static inline void
 babel_retract_route(struct babel_proto *p, struct babel_route *r)
 {
   r->metric = r->advert_metric = BABEL_INFINITY;
-
-  if (r == r->e->selected)
-    babel_select_route(p, r->e, r);
+  babel_announce_rte(p, r->e, r);
 }
 
 static void
@@ -186,9 +193,6 @@ babel_flush_route(struct babel_proto *p UNUSED, struct babel_route *r)
   rem_node(NODE r);
   rem_node(&r->neigh_route);
 
-  if (r->e->selected == r)
-    r->e->selected = NULL;
-
   sl_free(r);
 }
 
@@ -204,9 +208,11 @@ babel_expire_route(struct babel_proto *p, struct babel_route *r)
   {
     r->metric = r->advert_metric = BABEL_INFINITY;
     r->expires = current_time() + cf->hold_time;
+    babel_announce_rte(p, r->e, r);
   }
   else
   {
+    babel_announce_rte(p, r->e, r);
     babel_flush_route(p, r);
   }
 }
@@ -214,7 +220,7 @@ babel_expire_route(struct babel_proto *p, struct babel_route *r)
 static void
 babel_refresh_route(struct babel_proto *p, struct babel_route *r)
 {
-  if (r == r->e->selected)
+  if (r == babel_get_selected_route(p, r->e))
     babel_send_route_request(p, r->e, r->neigh);
 
   r->refresh_time = 0;
@@ -234,8 +240,6 @@ babel_expire_routes_(struct babel_proto *p, struct babel_rtable *rtable)
 loop:
   FIB_ITERATE_START(&rtable->fib, &fit, struct babel_entry, e)
   {
-    int changed = 0;
-
     WALK_LIST_DELSAFE(r, rx, e->routes)
     {
       if (r->refresh_time && r->refresh_time <= now_)
@@ -243,14 +247,10 @@ loop:
 
       if (r->expires && r->expires <= now_)
       {
-	changed = changed || (r == e->selected);
 	babel_expire_route(p, r);
       }
     }
 
-    if (changed)
-      babel_select_route(p, e, NULL);
-
     /* Clean up stale entries */
     if ((e->valid == BABEL_ENTRY_STALE) && ((e->updated + cf->hold_time) <= now_))
       e->valid = BABEL_ENTRY_DUMMY;
@@ -446,6 +446,7 @@ babel_get_neighbor(struct babel_iface *ifa, ip_addr addr)
 
   nbr = mb_allocz(ifa->pool, sizeof(struct babel_neighbor));
   nbr->ifa = ifa;
+  nbr->src = rt_get_source(&p->p, idm_alloc(&p->src_ids));
   nbr->addr = addr;
   nbr->rxcost = BABEL_INFINITY;
   nbr->txcost = BABEL_INFINITY;
@@ -473,6 +474,8 @@ babel_flush_neighbor(struct babel_proto *p, struct babel_neighbor *nbr)
   }
 
   nbr->ifa = NULL;
+
+  idm_free(&p->src_ids, nbr->src->private_id);
   rem_node(NODE nbr);
   mb_free(nbr);
 }
@@ -633,7 +636,7 @@ done:
     WALK_LIST2(r, n, nbr->routes, neigh_route)
     {
       r->metric = babel_compute_metric(nbr, r->advert_metric);
-      babel_select_route(p, r->e, r);
+      babel_announce_rte(p, r->e, r);
     }
   }
 }
@@ -648,12 +651,11 @@ done:
  * the entry is valid and ours, the unreachable route is announced instead.
  */
 static void
-babel_announce_rte(struct babel_proto *p, struct babel_entry *e)
+babel_announce_rte(struct babel_proto *p, struct babel_entry *e, struct babel_route *r)
 {
-  struct babel_route *r = e->selected;
   struct channel *c = (e->n.addr->type == NET_IP4) ? p->ip4_channel : p->ip6_channel;
 
-  if (r)
+  if (r->metric != BABEL_INFINITY && r->feasible)
   {
     rta a0 = {
       .source = RTS_BABEL,
@@ -697,10 +699,10 @@ babel_announce_rte(struct babel_proto *p, struct babel_entry *e)
       a0.nh.flags = RNF_ONLINK;
 
     rta *a = rta_lookup(&a0);
-    rte *rte = rte_get_temp(a, p->p.main_source);
+    rte *rte = rte_get_temp(a, r->neigh->src);
 
     e->unreachable = 0;
-    rte_update2(c, e->n.addr, rte, p->p.main_source);
+    rte_update2(c, e->n.addr, rte, r->neigh->src);
   }
   else if (e->valid && (e->router_id != p->router_id))
   {
@@ -713,16 +715,16 @@ babel_announce_rte(struct babel_proto *p, struct babel_entry *e)
     };
 
     rta *a = rta_lookup(&a0);
-    rte *rte = rte_get_temp(a, p->p.main_source);
+    rte *rte = rte_get_temp(a, r->neigh->src);
 
     e->unreachable = 1;
-    rte_update2(c, e->n.addr, rte, p->p.main_source);
+    rte_update2(c, e->n.addr, rte, r->neigh->src);
   }
   else
   {
     /* Retraction */
     e->unreachable = 0;
-    rte_update2(c, e->n.addr, NULL, p->p.main_source);
+    rte_update2(c, e->n.addr, NULL, r->neigh->src);
   }
 }
 
@@ -735,86 +737,6 @@ babel_announce_retraction(struct babel_proto *p, struct babel_entry *e)
   rte_update2(c, e->n.addr, NULL, p->p.main_source);
 }
 
-
-/**
- * babel_select_route - select best route for given route entry
- * @p: Babel protocol instance
- * @e: Babel entry to select the best route for
- * @mod: Babel route that was modified or NULL if unspecified
- *
- * Select the best reachable and feasible route for a given prefix among the
- * routes received from peers, and propagate it to the nest. This just selects
- * the reachable and feasible route with the lowest metric, but keeps selected
- * the old one in case of tie.
- *
- * If no feasible route is available for a prefix that previously had a route
- * selected, a seqno request is sent to try to get a valid route. If the entry
- * is valid and not owned by us, the unreachable route is announced to the nest
- * (to blackhole packets going to it, as per section 2.8). It is later removed
- * by babel_expire_routes(). Otherwise, the route is just removed from the nest.
- *
- * Argument @mod is used to optimize best route calculation. When specified, the
- * function can assume that only the @mod route was modified to avoid full best
- * route selection and announcement when non-best route was modified in minor
- * way. The caller is advised to not call babel_select_route() when no change is
- * done (e.g. periodic route updates) to avoid unnecessary announcements of the
- * same best route. The caller is not required to call the function in case of a
- * retraction of a non-best route.
- *
- * Note that the function does not active triggered updates. That is done by
- * babel_rt_notify() when the change is propagated back to Babel.
- */
-static void
-babel_select_route(struct babel_proto *p, struct babel_entry *e, struct babel_route *mod)
-{
-  struct babel_route *r, *best = e->selected;
-
-  /* Shortcut if only non-best was modified */
-  if (mod && (mod != best))
-  {
-    /* Either select modified route, or keep old best route */
-    if ((mod->metric < (best ? best->metric : BABEL_INFINITY)) && mod->feasible)
-      best = mod;
-    else
-      return;
-  }
-  else
-  {
-    /* Selected route may be modified and no longer admissible */
-    if (!best || (best->metric == BABEL_INFINITY) || !best->feasible)
-      best = NULL;
-
-    /* Find the best feasible route from all routes */
-    WALK_LIST(r, e->routes)
-      if ((r->metric < (best ? best->metric : BABEL_INFINITY)) && r->feasible)
-	best = r;
-  }
-
-  if (best)
-  {
-    if (best != e->selected)
-      TRACE(D_EVENTS, "Picked new route for prefix %N: router-id %lR metric %d",
-	    e->n.addr, best->router_id, best->metric);
-  }
-  else if (e->selected)
-  {
-    /*
-     * We have lost all feasible routes. We have to broadcast seqno request
-     * (Section 3.8.2.1) and keep unreachable route for a while (section 2.8).
-     * The later is done automatically by babel_announce_rte().
-     */
-
-    TRACE(D_EVENTS, "Lost feasible route for prefix %N", e->n.addr);
-    if (e->valid && (e->selected->router_id == e->router_id))
-      babel_add_seqno_request(p, e, e->selected->router_id, e->selected->seqno + 1, 0, NULL);
-  }
-  else
-    return;
-
-  e->selected = best;
-  babel_announce_rte(p, e);
-}
-
 /*
  *	Functions to send replies
  */
@@ -1301,7 +1223,7 @@ babel_handle_update(union babel_msg *m, struct babel_iface *ifa)
   s = babel_find_source(e, msg->router_id); /* for feasibility */
   feasible = babel_is_feasible(s, msg->seqno, msg->metric);
   metric = babel_compute_metric(nbr, msg->metric);
-  best = e->selected;
+  best = babel_get_selected_route(p, e);
 
   /*
    * RFC section 3.8.2.2 - Dealing with unfeasible updates. Generate a one-off
@@ -1339,7 +1261,7 @@ babel_handle_update(union babel_msg *m, struct babel_iface *ifa)
     e->updated = current_time();
   }
 
-  babel_select_route(p, e, r);
+  babel_announce_rte(p, e, r);
 }
 
 void
@@ -2004,10 +1926,10 @@ babel_dump_route(struct babel_route *r)
 }
 
 static void
-babel_dump_entry(struct babel_entry *e)
+babel_dump_entry(struct babel_proto *p, struct babel_entry *e)
 {
   struct babel_source *s;
-  struct babel_route *r;
+  struct babel_route *r, *best = babel_get_selected_route(p, e);
 
   debug("Babel: Entry %N:\n", e->n.addr);
 
@@ -2017,7 +1939,7 @@ babel_dump_entry(struct babel_entry *e)
   WALK_LIST(r,e->routes)
   {
     debug(" ");
-    if (r == e->selected) debug("*");
+    if (r == best) debug("*");
     babel_dump_route(r);
   }
 }
@@ -2059,7 +1981,7 @@ babel_dump(struct proto *P)
   rtable_lock(&p->ip4_rtable);
   FIB_WALK(&p->ip4_rtable.fib, struct babel_entry, e)
   {
-    babel_dump_entry(e);
+    babel_dump_entry(p, e);
   }
   FIB_WALK_END;
   rtable_unlock(&p->ip4_rtable);
@@ -2067,7 +1989,7 @@ babel_dump(struct proto *P)
   rtable_lock(&p->ip6_rtable);
   FIB_WALK(&p->ip6_rtable.fib, struct babel_entry, e)
   {
-    babel_dump_entry(e);
+    babel_dump_entry(p, e);
   }
   FIB_WALK_END;
   rtable_unlock(&p->ip6_rtable);
@@ -2193,7 +2115,7 @@ babel_show_entries_(struct babel_proto *p, struct babel_rtable *rtable)
   rtable_lock(rtable);
   FIB_WALK(&rtable->fib, struct babel_entry, e)
   {
-    struct babel_route *r = NULL;
+    struct babel_route *r = NULL, *best = babel_get_selected_route(p, e);
     uint rts = 0, srcs = 0;
     node *n;
 
@@ -2206,7 +2128,7 @@ babel_show_entries_(struct babel_proto *p, struct babel_rtable *rtable)
     if (e->valid)
       cli_msg(-1025, "%-*N %-23lR %6u %5u %7u %7u", width,
 	      e->n.addr, e->router_id, e->metric, e->seqno, rts, srcs);
-    else if (r = e->selected)
+    else if (r = best)
       cli_msg(-1025, "%-*N %-23lR %6u %5u %7u %7u", width,
 	      e->n.addr, r->router_id, r->metric, r->seqno, rts, srcs);
     else
@@ -2245,10 +2167,10 @@ babel_show_routes_(struct babel_proto *p, struct babel_rtable *rtable)
   rtable_lock(rtable);
   FIB_WALK(&rtable->fib, struct babel_entry, e)
   {
-    struct babel_route *r;
+    struct babel_route *r, *best = babel_get_selected_route(p, e);
     WALK_LIST(r, e->routes)
     {
-      char c = (r == e->selected) ? '*' : (r->feasible ? '+' : ' ');
+      char c = (r == best) ? '*' : (r->feasible ? '+' : ' ');
       btime time = r->expires ? r->expires - current_time() : 0;
       cli_msg(-1025, "%-*N %-25I %-10s %5u %c %5u %7t", width,
 	      e->n.addr, r->next_hop, r->neigh->ifa->ifname,
@@ -2330,15 +2252,18 @@ babel_rt_notify(struct proto *P, struct channel *c UNUSED, struct network *net,
 {
   struct babel_proto *p = (void *) P;
   struct babel_entry *e;
+  struct babel_iface *ifa;
+  struct babel_neighbor *n;
 
   if (new)
   {
     /* Update */
+    uint internal = new->src->proto == P;
     uint rt_seqno;
     uint rt_metric = ea_get_int(new->attrs->eattrs, EA_BABEL_METRIC, 0);
     u64 rt_router_id = 0;
 
-    if (new->src->proto == P)
+    if (internal)
     {
       rt_seqno = ea_find(new->attrs->eattrs, EA_BABEL_SEQNO)->u.data;
       eattr *e = ea_find(new->attrs->eattrs, EA_BABEL_ROUTER_ID);
@@ -2360,6 +2285,18 @@ babel_rt_notify(struct proto *P, struct channel *c UNUSED, struct network *net,
 
     e = babel_get_entry(p, net->n.addr);
 
+    while (internal) {
+      ifa = babel_find_iface(p, new->attrs->nh.iface);
+      if (!ifa) break;
+      n = babel_find_neighbor(ifa, new->attrs->from);
+      if (!n) break;
+      e->selected_nbr = n;
+      break;
+    }
+    if (!internal) {
+      e->selected_nbr = NULL;
+    }
+
     /* Activate triggered updates */
     if ((e->valid != BABEL_ENTRY_VALID) ||
 	(e->router_id != rt_router_id))
@@ -2383,6 +2320,7 @@ babel_rt_notify(struct proto *P, struct channel *c UNUSED, struct network *net,
 
     e->valid = BABEL_ENTRY_STALE;
     e->metric = BABEL_INFINITY;
+    e->selected_nbr = NULL;
 
     babel_trigger_update(p);
     e->updated = current_time();
@@ -2474,6 +2412,7 @@ babel_start(struct proto *P)
   p->source_slab = sl_new(P->pool, sizeof(struct babel_source));
   p->msg_slab = sl_new(P->pool, sizeof(struct babel_msg_node));
   p->seqno_slab = sl_new(P->pool, sizeof(struct babel_seqno_request));
+  idm_init(&p->src_ids, P->pool, 8);
 
   p->log_pkt_tbf = (struct tbf){ .rate = 1, .burst = 5 };
 
diff --git a/proto/babel/babel.h b/proto/babel/babel.h
index e55523ed..8ebfd0b3 100644
--- a/proto/babel/babel.h
+++ b/proto/babel/babel.h
@@ -25,6 +25,7 @@
 #include "lib/socket.h"
 #include "lib/string.h"
 #include "lib/timer.h"
+#include "lib/idm.h"
 
 #define EA_BABEL_METRIC		EA_CODE(PROTOCOL_BABEL, 0)
 #define EA_BABEL_ROUTER_ID	EA_CODE(PROTOCOL_BABEL, 1)
@@ -176,6 +177,7 @@ struct babel_proto {
   slab *source_slab;
   slab *msg_slab;
   slab *seqno_slab;
+  struct idm src_ids;
 
   struct tbf log_pkt_tbf;		/* TBF for packet messages */
 };
@@ -218,6 +220,7 @@ struct babel_iface {
 struct babel_neighbor {
   node n;
   struct babel_iface *ifa;
+  struct rte_src *src;
 
   ip_addr addr;
   u16 rxcost;				/* Sent in last IHU */
@@ -283,7 +286,7 @@ struct babel_seqno_request {
 };
 
 struct babel_entry {
-  struct babel_route *selected;
+  struct babel_neighbor *selected_nbr;
 
   list routes;				/* Routes for this prefix (struct babel_route) */
   list sources;				/* Source entries for this prefix (struct babel_source). */
-- 
2.30.2



More information about the Bird-users mailing list