This allows for filtering routes from specific interfaces and
neighbours. With the current internal route selection proto babel exports
only up to one route and an admin cannot do fine-grained filtering.
To fix this we rip out the internal route selection entirely and put them
all into the bird's nest instead.
This appears to not actually be a breaking change as route announcement was
already based on which routes bird would send back to babel_rt_notify
filters shouldn't be able to tell the difference between a single and
multiple routes AFAIK.
---
Changes in v2:
- Only announce a single unreachable route from the main_source and ensure
proper hold timing. Previously the differing lifetime of neighbours vs
unreachable hold time were a problem.
proto/babel/babel.c | 228 +++++++++++++++++---------------------------
proto/babel/babel.h | 5 +-
2 files changed, 94 insertions(+), 139 deletions(-)
diff --git a/proto/babel/babel.c b/proto/babel/babel.c
index a52125ff..4bc38540 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,6 +208,7 @@ 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
{
@@ -214,7 +219,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 +239,6 @@ loop:
rtable_lock(rtable);
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 +246,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 +445,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 +473,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,11 +635,40 @@ 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);
}
}
}
+static void
+babel_announce_rte_unreachable(struct babel_proto *p, struct babel_entry *e,
u8 unreachable)
+{
+ struct channel *c = (e->n.addr->type == NET_IP4) ? p->ip4_channel :
p->ip6_channel;
+ rte *rte = NULL;
+
+ if (unreachable) {
+ /* Unreachable */
+ rta a0 = {
+ .source = RTS_BABEL,
+ .scope = SCOPE_UNIVERSE,
+ .dest = RTD_UNREACHABLE,
+ .pref = 1,
+ };
+
+ rta *a = rta_lookup(&a0);
+ rte = rte_get_temp(a, p->p.main_source);
+ }
+
+ e->unreachable = unreachable;
+
+ /* Unlike the many neighbour routes we only want one unreachable route, for
+ * one it's ugly to have lots of unreachable routes (if we just did this
right
+ * in babel_announce_rte) but we also loose access to the neibour rte_src id
+ * when the neighbour is deallocated. This happens on a different schedule
+ * than unreachable route removal. */
+ rte_update2(c, e->n.addr, rte, p->p.main_source);
+}
+
/**
* babel_announce_rte - announce selected route to the core
* @p: Babel protocol instance
@@ -648,12 +679,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,124 +727,27 @@ 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);
- }
- else if (e->valid && (e->router_id != p->router_id))
- {
- /* Unreachable */
- rta a0 = {
- .source = RTS_BABEL,
- .scope = SCOPE_UNIVERSE,
- .dest = RTD_UNREACHABLE,
- .pref = 1,
- };
-
- rta *a = rta_lookup(&a0);
- rte *rte = rte_get_temp(a, p->p.main_source);
-
- e->unreachable = 1;
- rte_update2(c, e->n.addr, rte, p->p.main_source);
+ rte_update2(c, e->n.addr, rte, r->neigh->src);
+ babel_announce_rte_unreachable(p, e, 0);
}
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);
}
/* Special case of babel_announce_rte() just for retraction */
static inline void
babel_announce_retraction(struct babel_proto *p, struct babel_entry *e)
{
+ TRACE(D_EVENTS, "Retract unreachable route for %N",
+ e->n.addr);
+
struct channel *c = (e->n.addr->type == NET_IP4) ? p->ip4_channel :
p->ip6_channel;
e->unreachable = 0;
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 +1234,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 +1272,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 +1937,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 +1950,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 +1992,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 +2000,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 +2126,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 +2139,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 +2178,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 +2263,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 +2296,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 +2331,9 @@ 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_announce_rte_unreachable(p, e, 1);
babel_trigger_update(p);
e->updated = current_time();
@@ -2474,6 +2425,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