summaryrefslogtreecommitdiffstats
path: root/zebra/zebra_nhg.c
diff options
context:
space:
mode:
authorMark Stapp <mjs@voltanet.io>2021-02-22 21:09:07 +0100
committerMark Stapp <mjs@voltanet.io>2021-03-16 17:14:53 +0100
commit5530d55d3c489d6e2a5e270fac93aff97ac4cdac (patch)
tree8dff6f3e08ab7a357aecc927050d05d77e5de886 /zebra/zebra_nhg.c
parentzebra: add ui control for use of backup nexthops in resolution (diff)
downloadfrr-5530d55d3c489d6e2a5e270fac93aff97ac4cdac.tar.xz
frr-5530d55d3c489d6e2a5e270fac93aff97ac4cdac.zip
zebra: capture backup nexthop info with recursive resolution
When resolving a recursive route, capture backup nexthop info along with the resolving nexthops. Signed-off-by: Mark Stapp <mjs@voltanet.io>
Diffstat (limited to 'zebra/zebra_nhg.c')
-rw-r--r--zebra/zebra_nhg.c195
1 files changed, 166 insertions, 29 deletions
diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c
index 244bbc65e..9246283fd 100644
--- a/zebra/zebra_nhg.c
+++ b/zebra/zebra_nhg.c
@@ -49,6 +49,16 @@ DEFINE_MTYPE_STATIC(ZEBRA, NHG, "Nexthop Group Entry");
DEFINE_MTYPE_STATIC(ZEBRA, NHG_CONNECTED, "Nexthop Group Connected");
DEFINE_MTYPE_STATIC(ZEBRA, NHG_CTX, "Nexthop Group Context");
+/* Map backup nexthop indices between two nhes */
+struct backup_nh_map_s {
+ int map_count;
+
+ struct {
+ uint8_t orig_idx;
+ uint8_t new_idx;
+ } map[MULTIPATH_NUM];
+};
+
/* id counter to keep in sync with kernel */
uint32_t id_counter;
@@ -1627,9 +1637,10 @@ void zebra_nhg_increment_ref(struct nhg_hash_entry *nhe)
nhg_connected_tree_increment_ref(&nhe->nhg_depends);
}
-static void nexthop_set_resolved(afi_t afi, const struct nexthop *newhop,
- struct nexthop *nexthop,
- struct zebra_sr_policy *policy)
+static struct nexthop *nexthop_set_resolved(afi_t afi,
+ const struct nexthop *newhop,
+ struct nexthop *nexthop,
+ struct zebra_sr_policy *policy)
{
struct nexthop *resolved_hop;
uint8_t num_labels = 0;
@@ -1747,6 +1758,8 @@ static void nexthop_set_resolved(afi_t afi, const struct nexthop *newhop,
resolved_hop->rparent = nexthop;
_nexthop_add(&nexthop->resolved, resolved_hop);
+
+ return resolved_hop;
}
/* Checks if nexthop we are trying to resolve to is valid */
@@ -1785,12 +1798,107 @@ static bool nexthop_valid_resolve(const struct nexthop *nexthop,
}
/*
- * Given a nexthop we need to properly recursively resolve
- * the route. As such, do a table lookup to find and match
- * if at all possible. Set the nexthop->ifindex and resolved_id
- * as appropriate
+ * When resolving a recursive nexthop, capture backup nexthop(s) also
+ * so they can be conveyed through the dataplane to the FIB. We'll look
+ * at the backups in the resolving nh 'nexthop' and its nhe, and copy them
+ * into the route's resolved nh 'resolved' and its nhe 'nhe'.
+ */
+static int resolve_backup_nexthops(const struct nexthop *nexthop,
+ const struct nhg_hash_entry *nhe,
+ struct nexthop *resolved,
+ struct nhg_hash_entry *resolve_nhe,
+ struct backup_nh_map_s *map)
+{
+ int i, j, idx;
+ const struct nexthop *bnh;
+ struct nexthop *nh, *newnh;
+
+ assert(nexthop->backup_num <= NEXTHOP_MAX_BACKUPS);
+
+ if (resolve_nhe->backup_info->nhe == NULL)
+ resolve_nhe->backup_info->nhe = zebra_nhg_alloc();
+
+ /* Locate backups from the original nexthop's backup index and nhe */
+ for (i = 0; i < nexthop->backup_num; i++) {
+ idx = nexthop->backup_idx[i];
+
+ /* Do we already know about this particular backup? */
+ for (j = 0; j < map->map_count; j++) {
+ if (map->map[j].orig_idx == idx)
+ break;
+ }
+
+ if (j < map->map_count) {
+ resolved->backup_idx[resolved->backup_num] =
+ map->map[j].new_idx;
+ resolved->backup_num++;
+
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+ zlog_debug("%s: found map idx orig %d, new %d",
+ __func__, map->map[j].orig_idx,
+ map->map[j].new_idx);
+
+ continue;
+ }
+
+ /* We can't handle any new map entries at this point. */
+ if (map->map_count == MULTIPATH_NUM)
+ break;
+
+ /* Need to create/copy a new backup */
+ bnh = nhe->backup_info->nhe->nhg.nexthop;
+ for (j = 0; j < idx; j++) {
+ if (bnh == NULL)
+ break;
+ bnh = bnh->next;
+ }
+
+ /* Whoops - bad index in the nexthop? */
+ if (bnh == NULL)
+ continue;
+
+ /* Update backup info in the resolving nexthop and its nhe */
+ newnh = nexthop_dup_no_recurse(bnh, NULL);
+
+ /* Need to compute the new backup index in the new
+ * backup list, and add to map struct.
+ */
+ j = 0;
+ nh = resolve_nhe->backup_info->nhe->nhg.nexthop;
+ if (nh) {
+ while (nh->next) {
+ nh = nh->next;
+ j++;
+ }
+
+ nh->next = newnh;
+
+ } else /* First one */
+ resolve_nhe->backup_info->nhe->nhg.nexthop = newnh;
+
+ /* Capture index */
+ resolved->backup_idx[resolved->backup_num] = j;
+ resolved->backup_num++;
+
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+ zlog_debug("%s: added idx orig %d, new %d",
+ __func__, idx, j);
+
+ /* Update map/cache */
+ map->map[map->map_count].orig_idx = idx;
+ map->map[map->map_count].new_idx = j;
+ map->map_count++;
+ }
+
+ return 0;
+}
+
+/*
+ * Given a nexthop we need to properly recursively resolve,
+ * do a table lookup to find and match if at all possible.
+ * Set the nexthop->ifindex and resolution info as appropriate.
*/
-static int nexthop_active(afi_t afi, struct nexthop *nexthop,
+static int nexthop_active(struct nexthop *nexthop, struct nhg_hash_entry *nhe,
const struct prefix *top, int type, uint32_t flags,
uint32_t *pmtu)
{
@@ -1806,6 +1914,7 @@ static int nexthop_active(afi_t afi, struct nexthop *nexthop,
struct zebra_vrf *zvrf;
struct in_addr local_ipv4;
struct in_addr *ipv4;
+ afi_t afi = AFI_IP;
/* Reset some nexthop attributes that we'll recompute if necessary */
if ((nexthop->type == NEXTHOP_TYPE_IPV4)
@@ -1817,11 +1926,13 @@ static int nexthop_active(afi_t afi, struct nexthop *nexthop,
nexthop->resolved = NULL;
/*
+ * Set afi based on nexthop type.
* Some nexthop types get special handling, possibly skipping
* the normal processing.
*/
switch (nexthop->type) {
case NEXTHOP_TYPE_IFINDEX:
+
ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id);
/*
* If the interface exists and its operative or its a kernel
@@ -1839,6 +1950,8 @@ static int nexthop_active(afi_t afi, struct nexthop *nexthop,
break;
case NEXTHOP_TYPE_IPV6_IFINDEX:
+ afi = AFI_IP6;
+
if (IN6_IS_ADDR_LINKLOCAL(&nexthop->gate.ipv6)) {
ifp = if_lookup_by_index(nexthop->ifindex,
nexthop->vrf_id);
@@ -1849,14 +1962,16 @@ static int nexthop_active(afi_t afi, struct nexthop *nexthop,
}
break;
- case NEXTHOP_TYPE_BLACKHOLE:
- return 1;
-
case NEXTHOP_TYPE_IPV4:
case NEXTHOP_TYPE_IPV4_IFINDEX:
+ afi = AFI_IP;
+ break;
case NEXTHOP_TYPE_IPV6:
- default:
+ afi = AFI_IP6;
break;
+
+ case NEXTHOP_TYPE_BLACKHOLE:
+ return 1;
}
/*
@@ -2053,6 +2168,8 @@ static int nexthop_active(afi_t afi, struct nexthop *nexthop,
return 1;
} else if (CHECK_FLAG(flags, ZEBRA_FLAG_ALLOW_RECURSION)) {
struct nexthop_group *nhg;
+ struct nexthop *resolver;
+ struct backup_nh_map_s map = {};
resolved = 0;
@@ -2082,9 +2199,19 @@ static int nexthop_active(afi_t afi, struct nexthop *nexthop,
SET_FLAG(nexthop->flags,
NEXTHOP_FLAG_RECURSIVE);
- nexthop_set_resolved(afi, newhop, nexthop,
- NULL);
+ resolver = nexthop_set_resolved(afi, newhop,
+ nexthop, NULL);
resolved = 1;
+
+ /* If there are backup nexthops, capture
+ * that info with the resolving nexthop.
+ */
+ if (resolver && newhop->backup_num > 0) {
+ resolve_backup_nexthops(newhop,
+ match->nhe,
+ resolver, nhe,
+ &map);
+ }
}
/* Examine installed backup nexthops, if any. There
@@ -2147,14 +2274,17 @@ done_with_match:
/* This function verifies reachability of one given nexthop, which can be
* numbered or unnumbered, IPv4 or IPv6. The result is unconditionally stored
* in nexthop->flags field. The nexthop->ifindex will be updated
- * appropriately as well. An existing route map can turn an
- * otherwise active nexthop into inactive, but not vice versa.
+ * appropriately as well.
+ *
+ * An existing route map can turn an otherwise active nexthop into inactive,
+ * but not vice versa.
*
* The return value is the final value of 'ACTIVE' flag.
*/
static unsigned nexthop_active_check(struct route_node *rn,
struct route_entry *re,
- struct nexthop *nexthop)
+ struct nexthop *nexthop,
+ struct nhg_hash_entry *nhe)
{
route_map_result_t ret = RMAP_PERMITMATCH;
afi_t family;
@@ -2191,7 +2321,7 @@ static unsigned nexthop_active_check(struct route_node *rn,
switch (nexthop->type) {
case NEXTHOP_TYPE_IFINDEX:
- if (nexthop_active(AFI_IP, nexthop, &rn->p, re->type,
+ if (nexthop_active(nexthop, nhe, &rn->p, re->type,
re->flags, &mtu))
SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
else
@@ -2200,7 +2330,7 @@ static unsigned nexthop_active_check(struct route_node *rn,
case NEXTHOP_TYPE_IPV4:
case NEXTHOP_TYPE_IPV4_IFINDEX:
family = AFI_IP;
- if (nexthop_active(AFI_IP, nexthop, &rn->p, re->type,
+ if (nexthop_active(nexthop, nhe, &rn->p, re->type,
re->flags, &mtu))
SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
else
@@ -2208,7 +2338,7 @@ static unsigned nexthop_active_check(struct route_node *rn,
break;
case NEXTHOP_TYPE_IPV6:
family = AFI_IP6;
- if (nexthop_active(AFI_IP6, nexthop, &rn->p, re->type,
+ if (nexthop_active(nexthop, nhe, &rn->p, re->type,
re->flags, &mtu))
SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
else
@@ -2219,7 +2349,7 @@ static unsigned nexthop_active_check(struct route_node *rn,
if (rn->p.family != AF_INET)
family = AFI_IP6;
- if (nexthop_active(AFI_IP6, nexthop, &rn->p, re->type,
+ if (nexthop_active(nexthop, nhe, &rn->p, re->type,
re->flags, &mtu))
SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
else
@@ -2329,18 +2459,20 @@ done:
}
/*
- * Process a list of nexthops, given an nhg, determining
+ * Process a list of nexthops, given an nhe, determining
* whether each one is ACTIVE/installable at this time.
*/
static uint32_t nexthop_list_active_update(struct route_node *rn,
struct route_entry *re,
- struct nexthop_group *nhg)
+ struct nhg_hash_entry *nhe,
+ bool is_backup)
{
union g_addr prev_src;
unsigned int prev_active, new_active;
ifindex_t prev_index;
uint32_t counter = 0;
struct nexthop *nexthop;
+ struct nexthop_group *nhg = &nhe->nhg;
nexthop = nhg->nexthop;
@@ -2356,15 +2488,20 @@ static uint32_t nexthop_list_active_update(struct route_node *rn,
prev_src = nexthop->rmap_src;
prev_active = CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
prev_index = nexthop->ifindex;
+
+ /* Include the containing nhe for primary nexthops: if there's
+ * recursive resolution, we capture the backup info also.
+ */
+ new_active =
+ nexthop_active_check(rn, re, nexthop,
+ (is_backup ? NULL : nhe));
+
/*
* We need to respect the multipath_num here
* as that what we should be able to install from
* a multipath perspective should not be a data plane
* decision point.
*/
- new_active =
- nexthop_active_check(rn, re, nexthop);
-
if (new_active && counter >= zrouter.multipath_num) {
struct nexthop *nh;
@@ -2378,7 +2515,7 @@ static uint32_t nexthop_list_active_update(struct route_node *rn,
if (new_active)
counter++;
- /* Don't allow src setting on IPv6 addr for now */
+ /* Check for changes to the nexthop - set ROUTE_ENTRY_CHANGED */
if (prev_active != new_active || prev_index != nexthop->ifindex
|| ((nexthop->type >= NEXTHOP_TYPE_IFINDEX
&& nexthop->type < NEXTHOP_TYPE_IPV6)
@@ -2447,7 +2584,7 @@ int nexthop_active_update(struct route_node *rn, struct route_entry *re)
curr_nhe->id = 0;
/* Process nexthops */
- curr_active = nexthop_list_active_update(rn, re, &curr_nhe->nhg);
+ curr_active = nexthop_list_active_update(rn, re, curr_nhe, false);
if (IS_ZEBRA_DEBUG_NHG_DETAIL)
zlog_debug("%s: re %p curr_active %u", __func__, re,
@@ -2458,7 +2595,7 @@ int nexthop_active_update(struct route_node *rn, struct route_entry *re)
goto backups_done;
backup_active = nexthop_list_active_update(
- rn, re, zebra_nhg_get_backup_nhg(curr_nhe));
+ rn, re, curr_nhe->backup_info->nhe, true /*is_backup*/);
if (IS_ZEBRA_DEBUG_NHG_DETAIL)
zlog_debug("%s: re %p backup_active %u", __func__, re,