summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/nexthop.h1
-rw-r--r--lib/nexthop_group.c30
-rw-r--r--lib/nexthop_group.h4
-rw-r--r--zebra/rt_netlink.c3
-rw-r--r--zebra/zebra_dplane.c27
-rw-r--r--zebra/zebra_nhg.c310
-rw-r--r--zebra/zebra_nhg.h14
-rw-r--r--zebra/zebra_rib.c44
8 files changed, 328 insertions, 105 deletions
diff --git a/lib/nexthop.h b/lib/nexthop.h
index 9dd5fc6fd3..5558e857f6 100644
--- a/lib/nexthop.h
+++ b/lib/nexthop.h
@@ -154,6 +154,7 @@ extern int nexthop_same_firsthop(struct nexthop *next1, struct nexthop *next2);
extern const char *nexthop2str(const struct nexthop *nexthop,
char *str, int size);
extern struct nexthop *nexthop_next(struct nexthop *nexthop);
+extern struct nexthop *nexthop_recursive_next(struct nexthop *nexthop);
extern unsigned int nexthop_level(struct nexthop *nexthop);
/* Copies to an already allocated nexthop struct */
extern void nexthop_copy(struct nexthop *copy, const struct nexthop *nexthop,
diff --git a/lib/nexthop_group.c b/lib/nexthop_group.c
index 463ab0b881..c9a8f1af51 100644
--- a/lib/nexthop_group.c
+++ b/lib/nexthop_group.c
@@ -81,6 +81,17 @@ uint8_t nexthop_group_nexthop_num(const struct nexthop_group *nhg)
return num;
}
+uint8_t nexthop_group_nexthop_num_no_recurse(const struct nexthop_group *nhg)
+{
+ struct nexthop *nhop;
+ uint8_t num = 0;
+
+ for (nhop = nhg->nexthop; nhop; nhop = nhop->next)
+ num++;
+
+ return num;
+}
+
uint8_t nexthop_group_active_nexthop_num(const struct nexthop_group *nhg)
{
struct nexthop *nhop;
@@ -94,6 +105,20 @@ uint8_t nexthop_group_active_nexthop_num(const struct nexthop_group *nhg)
return num;
}
+uint8_t
+nexthop_group_active_nexthop_num_no_recurse(const struct nexthop_group *nhg)
+{
+ struct nexthop *nhop;
+ uint8_t num = 0;
+
+ for (nhop = nhg->nexthop; nhop; nhop = nhop->next) {
+ if (CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_ACTIVE))
+ num++;
+ }
+
+ return num;
+}
+
struct nexthop *nexthop_exists(const struct nexthop_group *nhg,
const struct nexthop *nh)
{
@@ -118,10 +143,11 @@ bool nexthop_group_equal(const struct nexthop_group *nhg1,
if (!nhg1 && !nhg2)
return false;
- if (nexthop_group_nexthop_num(nhg1) != nexthop_group_nexthop_num(nhg2))
+ if (nexthop_group_nexthop_num_no_recurse(nhg1)
+ != nexthop_group_nexthop_num_no_recurse(nhg2))
return false;
- for (ALL_NEXTHOPS_PTR(nhg1, nh)) {
+ for (nh = nhg1->nexthop; nh; nh = nh->next) {
if (!nexthop_exists(nhg2, nh))
return false;
}
diff --git a/lib/nexthop_group.h b/lib/nexthop_group.h
index be6f50d8a0..57a5a97599 100644
--- a/lib/nexthop_group.h
+++ b/lib/nexthop_group.h
@@ -123,7 +123,11 @@ extern void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh);
/* Return the number of nexthops in this nhg */
extern uint8_t nexthop_group_nexthop_num(const struct nexthop_group *nhg);
extern uint8_t
+nexthop_group_nexthop_num_no_recurse(const struct nexthop_group *nhg);
+extern uint8_t
nexthop_group_active_nexthop_num(const struct nexthop_group *nhg);
+extern uint8_t
+nexthop_group_active_nexthop_num_no_recurse(const struct nexthop_group *nhg);
#ifdef __cplusplus
}
diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c
index 5d2d407688..89fed59f76 100644
--- a/zebra/rt_netlink.c
+++ b/zebra/rt_netlink.c
@@ -2025,7 +2025,8 @@ static int netlink_nexthop(int cmd, struct zebra_dplane_ctx *ctx)
*/
enum zebra_dplane_result kernel_nexthop_update(struct zebra_dplane_ctx *ctx)
{
- int cmd, ret = 0;
+ int cmd = 0;
+ int ret = 0;
switch (dplane_ctx_get_op(ctx)) {
case DPLANE_OP_NH_DELETE:
diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c
index f818ed5bc6..429d4c59a1 100644
--- a/zebra/zebra_dplane.c
+++ b/zebra/zebra_dplane.c
@@ -1525,8 +1525,9 @@ static int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx,
zns = zvrf->zns;
dplane_ctx_ns_init(ctx, zns, (op == DPLANE_OP_ROUTE_UPDATE));
- if (re->nhe_id && zns->supports_nh)
- ctx->u.rinfo.nhe.id = re->nhe_id;
+ if (re->nhe_id && zns->supports_nh) {
+ ctx->u.rinfo.nhe.id = zebra_nhg_get_resolved_id(re->nhe_id);
+ }
/* Trying out the sequence number idea, so we can try to detect
* when a result is stale.
@@ -1571,23 +1572,11 @@ static int dplane_ctx_nexthop_init(struct zebra_dplane_ctx *ctx,
nexthop_group_copy(&(ctx->u.rinfo.nhe.ng), nhe->nhg);
- if (!zebra_nhg_depends_is_empty(nhe)) {
- struct nhg_connected *rb_node_dep = NULL;
- uint8_t i = 0;
-
- // TODO: This doesn't work with depends being recursive
- // resolved nh's as well. Yea, good luck future stephen
- // this one...
-
- RB_FOREACH (rb_node_dep, nhg_connected_head,
- &nhe->nhg_depends) {
- ctx->u.rinfo.nhe.nh_grp[i].id = rb_node_dep->nhe->id;
- /* We aren't using weights for anything right now */
- ctx->u.rinfo.nhe.nh_grp[i].weight = 0;
- i++;
- }
- ctx->u.rinfo.nhe.nh_grp_count = i;
- }
+ /* If its a group, convert it to a grp array of ids */
+ if (!zebra_nhg_depends_is_empty(nhe)
+ && !CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_RECURSIVE))
+ ctx->u.rinfo.nhe.nh_grp_count =
+ zebra_nhg_nhe2grp(ctx->u.rinfo.nhe.nh_grp, nhe);
/* Extract ns info - can't use pointers to 'core'
structs */
diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c
index 94d41ba246..144decfbb5 100644
--- a/zebra/zebra_nhg.c
+++ b/zebra/zebra_nhg.c
@@ -101,6 +101,12 @@ bool nhg_connected_head_is_empty(const struct nhg_connected_head *head)
return RB_EMPTY(nhg_connected_head, head);
}
+struct nhg_connected *
+nhg_connected_head_root(const struct nhg_connected_head *head)
+{
+ return RB_ROOT(nhg_connected_head, head);
+}
+
void nhg_connected_head_del(struct nhg_connected_head *head,
struct nhg_hash_entry *depend)
{
@@ -128,6 +134,37 @@ void nhg_connected_head_add(struct nhg_connected_head *head,
RB_INSERT(nhg_connected_head, head, new);
}
+struct nhg_hash_entry *zebra_nhg_resolve(struct nhg_hash_entry *nhe)
+{
+ if (CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_RECURSIVE)
+ && !zebra_nhg_depends_is_empty(nhe)) {
+ nhe = nhg_connected_head_root(&nhe->nhg_depends)->nhe;
+ return zebra_nhg_resolve(nhe);
+ }
+
+ return nhe;
+}
+
+uint32_t zebra_nhg_get_resolved_id(uint32_t id)
+{
+ struct nhg_hash_entry *nhe = NULL;
+
+ nhe = zebra_nhg_lookup_id(id);
+
+ if (!nhe) {
+ flog_err(
+ EC_ZEBRA_TABLE_LOOKUP_FAILED,
+ "Zebra failed to lookup a resolved nexthop hash entry id=%u",
+ id);
+ return id;
+ }
+
+ if (CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_RECURSIVE))
+ nhe = zebra_nhg_resolve(nhe);
+
+ return nhe->id;
+}
+
unsigned int zebra_nhg_depends_count(const struct nhg_hash_entry *nhe)
{
return nhg_connected_head_count(&nhe->nhg_depends);
@@ -352,6 +389,10 @@ bool zebra_nhg_hash_equal(const void *arg1, const void *arg2)
const struct nhg_hash_entry *nhe1 = arg1;
const struct nhg_hash_entry *nhe2 = arg2;
+ /* No matter what if they equal IDs, assume equal */
+ if (nhe1->id && nhe2->id && (nhe1->id == nhe2->id))
+ return true;
+
if (nhe1->vrf_id != nhe2->vrf_id)
return false;
@@ -361,6 +402,10 @@ bool zebra_nhg_hash_equal(const void *arg1, const void *arg2)
if (!nexthop_group_equal(nhe1->nhg, nhe2->nhg))
return false;
+ if (nexthop_group_active_nexthop_num_no_recurse(nhe1->nhg)
+ != nexthop_group_active_nexthop_num_no_recurse(nhe2->nhg))
+ return false;
+
return true;
}
@@ -651,32 +696,44 @@ int zebra_nhg_kernel_find(uint32_t id, struct nexthop *nh, struct nh_grp *grp,
return 0;
}
+static struct nhg_hash_entry *depends_find(struct nexthop *nh, afi_t afi)
+{
+ struct nexthop lookup = {0};
+
+ lookup = *nh;
+ /* Clear it, in case its a group */
+ lookup.next = NULL;
+ lookup.prev = NULL;
+ return zebra_nhg_find_nexthop(0, &lookup, afi, false);
+}
+
/* Rib-side, you get a nexthop group struct */
struct nhg_hash_entry *zebra_nhg_rib_find(uint32_t id,
struct nexthop_group *nhg,
vrf_id_t rt_vrf_id, afi_t rt_afi)
{
struct nhg_hash_entry *nhe = NULL;
+ struct nhg_hash_entry *depend = NULL;
struct nhg_connected_head nhg_depends = {};
+
// Defualt the nhe to the afi and vrf of the route
afi_t nhg_afi = rt_afi;
vrf_id_t nhg_vrf_id = rt_vrf_id;
- /* If its a group, create a dependency list */
- if (nhg && nhg->nexthop->next) {
- struct nexthop *nh = NULL;
- struct nexthop lookup = {0};
- struct nhg_hash_entry *depend = NULL;
+ if (!nhg) {
+ flog_err(EC_ZEBRA_TABLE_LOOKUP_FAILED,
+ "No nexthop passed to zebra_nhg_rib_find()");
+ return NULL;
+ }
+ if (nhg->nexthop->next) {
nhg_connected_head_init(&nhg_depends);
- for (ALL_NEXTHOPS_PTR(nhg, nh)) {
- lookup = *nh;
- /* Clear it, since its a group */
- lookup.next = NULL;
- /* Use the route afi here, since a single nh */
- depend = zebra_nhg_find_nexthop(0, &lookup, rt_afi,
- false);
+ /* If its a group, create a dependency tree */
+ struct nexthop *nh = NULL;
+
+ for (nh = nhg->nexthop; nh; nh = nh->next) {
+ depend = depends_find(nh, rt_afi);
nhg_connected_head_add(&nhg_depends, depend);
}
@@ -686,7 +743,6 @@ struct nhg_hash_entry *zebra_nhg_rib_find(uint32_t id,
}
nhe = zebra_nhg_find(id, nhg, &nhg_depends, nhg_vrf_id, nhg_afi, false);
-
return nhe;
}
@@ -755,9 +811,10 @@ void zebra_nhg_decrement_ref(struct nhg_hash_entry *nhe)
if (!zebra_nhg_depends_is_empty(nhe)) {
struct nhg_connected *rb_node_dep = NULL;
+ struct nhg_connected *tmp = NULL;
- RB_FOREACH (rb_node_dep, nhg_connected_head,
- &nhe->nhg_depends) {
+ RB_FOREACH_SAFE (rb_node_dep, nhg_connected_head,
+ &nhe->nhg_depends, tmp) {
zebra_nhg_decrement_ref(rb_node_dep->nhe);
}
}
@@ -945,10 +1002,12 @@ 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 as appropriate
+ * if at all possible. Set the nexthop->ifindex and resolved_id
+ * as appropriate
*/
static int nexthop_active(afi_t afi, struct route_entry *re,
- struct nexthop *nexthop, struct route_node *top)
+ struct nexthop *nexthop, struct route_node *top,
+ uint32_t *resolved_id)
{
struct prefix p;
struct route_table *table;
@@ -1121,8 +1180,10 @@ static int nexthop_active(afi_t afi, struct route_entry *re,
nexthop_set_resolved(afi, newhop, nexthop);
resolved = 1;
}
- if (resolved)
+ if (resolved) {
re->nexthop_mtu = match->mtu;
+ *resolved_id = match->nhe_id;
+ }
if (!resolved && IS_ZEBRA_DEBUG_RIB_DETAILED)
zlog_debug("\t%s: Recursion failed to find",
__PRETTY_FUNCTION__);
@@ -1141,9 +1202,10 @@ static int nexthop_active(afi_t afi, struct route_entry *re,
nexthop_set_resolved(afi, newhop, nexthop);
resolved = 1;
}
- if (resolved)
+ if (resolved) {
re->nexthop_mtu = match->mtu;
-
+ *resolved_id = match->nhe_id;
+ }
if (!resolved && IS_ZEBRA_DEBUG_RIB_DETAILED)
zlog_debug(
"\t%s: Static route unable to resolve",
@@ -1175,11 +1237,15 @@ static int nexthop_active(afi_t afi, struct route_entry *re,
* appropriately as well. An existing route map can turn
* (otherwise active) nexthop into inactive, but not vice versa.
*
+ * If it finds a nexthop recursivedly, set the resolved_id
+ * to match that nexthop's nhg_hash_entry ID;
+ *
* 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,
+ uint32_t *resolved_id)
{
struct interface *ifp;
route_map_result_t ret = RMAP_PERMITMATCH;
@@ -1207,14 +1273,14 @@ 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, re, nexthop, rn))
+ if (nexthop_active(AFI_IP, re, nexthop, rn, resolved_id))
SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
else
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
break;
case NEXTHOP_TYPE_IPV6:
family = AFI_IP6;
- if (nexthop_active(AFI_IP6, re, nexthop, rn))
+ if (nexthop_active(AFI_IP6, re, nexthop, rn, resolved_id))
SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
else
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
@@ -1231,7 +1297,8 @@ static unsigned nexthop_active_check(struct route_node *rn,
else
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
} else {
- if (nexthop_active(AFI_IP6, re, nexthop, rn))
+ if (nexthop_active(AFI_IP6, re, nexthop, rn,
+ resolved_id))
SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
else
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
@@ -1305,41 +1372,24 @@ static unsigned nexthop_active_check(struct route_node *rn,
*/
int nexthop_active_update(struct route_node *rn, struct route_entry *re)
{
+ struct nexthop_group new_grp = {};
struct nexthop *nexthop;
union g_addr prev_src;
unsigned int prev_active, new_active;
ifindex_t prev_index;
uint8_t curr_active = 0;
- afi_t rt_afi = AFI_UNSPEC;
- // TODO: Temporary until we get this function sorted out
- // a little better.
- //
- if (re->nhe_id) {
- struct nhg_hash_entry *nhe = NULL;
+ afi_t rt_afi = family2afi(rn->p.family);
- nhe = zebra_nhg_lookup_id(re->nhe_id);
-
- if (nhe) {
- if (!re->ng) {
- /* This is its first time getting attached */
- zebra_nhg_increment_ref(nhe);
- re->ng = nhe->nhg;
- }
+ UNSET_FLAG(re->status, ROUTE_ENTRY_CHANGED);
- if (CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_VALID)) {
- return 1;
- }
- } else
- flog_err(
- EC_ZEBRA_TABLE_LOOKUP_FAILED,
- "Zebra failed to find the nexthop hash entry for id=%u in a route entry",
- re->nhe_id);
- }
+ /* Copy over the nexthops in current state */
+ nexthop_group_copy(&new_grp, re->ng);
- UNSET_FLAG(re->status, ROUTE_ENTRY_CHANGED);
+ for (nexthop = new_grp.nexthop; nexthop; nexthop = nexthop->next) {
+ struct nhg_hash_entry *nhe = NULL;
+ uint32_t resolved_id = 0;
- for (nexthop = re->ng->nexthop; nexthop; nexthop = nexthop->next) {
/* No protocol daemon provides src and so we're skipping
* tracking it */
prev_src = nexthop->rmap_src;
@@ -1351,17 +1401,71 @@ int nexthop_active_update(struct route_node *rn, struct route_entry *re)
* a multipath perpsective should not be a data plane
* decision point.
*/
- new_active = nexthop_active_check(rn, re, nexthop);
+ new_active =
+ nexthop_active_check(rn, re, nexthop, &resolved_id);
+
+ /*
+ * Create the individual nexthop hash entries
+ * for the nexthops in the group
+ */
+
+ nhe = depends_find(nexthop, rt_afi);
+
+ if (nhe && resolved_id) {
+ struct nhg_hash_entry *old_resolved = NULL;
+ struct nhg_hash_entry *new_resolved = NULL;
+
+ /* If this was already resolved, get its resolved nhe */
+ if (CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_RECURSIVE))
+ old_resolved = zebra_nhg_resolve(nhe);
+
+ /*
+ * We are going to do what is done in nexthop_active
+ * and clear whatever resolved nexthop may already be
+ * there.
+ */
+
+ zebra_nhg_depends_release(nhe);
+ nhg_connected_head_free(&nhe->nhg_depends);
+
+ new_resolved = zebra_nhg_lookup_id(resolved_id);
+
+ if (new_resolved) {
+ /* Add new resolved */
+ zebra_nhg_depends_add(nhe, new_resolved);
+ zebra_nhg_dependents_add(new_resolved, nhe);
+ /*
+ * In case the new == old, we increment
+ * first and then decrement
+ */
+ zebra_nhg_increment_ref(new_resolved);
+ if (old_resolved)
+ zebra_nhg_decrement_ref(old_resolved);
+
+ SET_FLAG(nhe->flags, NEXTHOP_GROUP_RECURSIVE);
+ } else
+ flog_err(
+ EC_ZEBRA_TABLE_LOOKUP_FAILED,
+ "Zebra failed to lookup a resolved nexthop hash entry id=%u",
+ resolved_id);
+ }
+
if (new_active
- && nexthop_group_active_nexthop_num(re->ng)
+ && nexthop_group_active_nexthop_num(&new_grp)
>= zrouter.multipath_num) {
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
new_active = 0;
}
- if (new_active)
+ if (nhe && new_active) {
curr_active++;
+ SET_FLAG(nhe->flags, NEXTHOP_GROUP_VALID);
+ if (!nhe->is_kernel_nh
+ && !CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_RECURSIVE))
+ zebra_nhg_install_kernel(nhe);
+ }
+
/* Don't allow src setting on IPv6 addr for now */
if (prev_active != new_active || prev_index != nexthop->ifindex
|| ((nexthop->type >= NEXTHOP_TYPE_IFINDEX
@@ -1376,43 +1480,83 @@ int nexthop_active_update(struct route_node *rn, struct route_entry *re)
SET_FLAG(re->status, ROUTE_ENTRY_CHANGED);
}
- // TODO: Update this when we have this function
- // figured out a little better.
- //
- struct nhg_hash_entry *new_nhe = NULL;
-
- rt_afi = family2afi(rn->p.family);
- // TODO: Add proto type here
-
- // TODO: Maybe make this a UPDATE message?
- // Right now we are just creating a new one
- // and deleting the old.
- new_nhe = zebra_nhg_rib_find(0, re->ng, re->vrf_id, rt_afi);
-
- if (new_nhe && (re->nhe_id != new_nhe->id)) {
- struct nhg_hash_entry *old_nhe =
- zebra_nhg_lookup_id(re->nhe_id);
-
- /* It should point to the nhe nexthop group now */
- if (re->ng)
- nexthop_group_free_delete(&re->ng);
- re->ng = new_nhe->nhg;
- re->nhe_id = new_nhe->id;
-
- zebra_nhg_increment_ref(new_nhe);
- if (old_nhe)
- zebra_nhg_decrement_ref(old_nhe);
-
- if (curr_active) {
- SET_FLAG(new_nhe->flags, NEXTHOP_GROUP_VALID);
- if (!new_nhe->is_kernel_nh)
- zebra_nhg_install_kernel(new_nhe);
+ if (CHECK_FLAG(re->status, ROUTE_ENTRY_NEXTHOPS_CHANGED)) {
+ struct nhg_hash_entry *new_nhe = NULL;
+ // TODO: Add proto type here
+
+ new_nhe = zebra_nhg_rib_find(0, &new_grp, re->vrf_id, rt_afi);
+
+ if (new_nhe && (re->nhe_id != new_nhe->id)) {
+ struct nhg_hash_entry *old_nhe =
+ zebra_nhg_lookup_id(re->nhe_id);
+
+ re->ng = new_nhe->nhg;
+ re->nhe_id = new_nhe->id;
+
+ zebra_nhg_increment_ref(new_nhe);
+ if (old_nhe)
+ zebra_nhg_decrement_ref(old_nhe);
}
}
+ if (curr_active) {
+ struct nhg_hash_entry *nhe = NULL;
+
+ nhe = zebra_nhg_lookup_id(re->nhe_id);
+
+ if (nhe) {
+ SET_FLAG(nhe->flags, NEXTHOP_GROUP_VALID);
+ if (!nhe->is_kernel_nh
+ && !CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_RECURSIVE))
+ zebra_nhg_install_kernel(nhe);
+ } else
+ flog_err(
+ EC_ZEBRA_TABLE_LOOKUP_FAILED,
+ "Active update on NHE id=%u that we do not have in our tables",
+ re->nhe_id);
+ }
+
+ /*
+ * Do not need these nexthops anymore since they
+ * were either copied over into an nhe or not
+ * used at all.
+ */
+ nexthops_free(new_grp.nexthop);
return curr_active;
}
+/* Convert a nhe into a group array */
+uint8_t zebra_nhg_nhe2grp(struct nh_grp *grp, struct nhg_hash_entry *nhe)
+{
+ struct nhg_connected *rb_node_dep = NULL;
+ struct nhg_hash_entry *depend = NULL;
+ uint8_t i = 0;
+
+ RB_FOREACH (rb_node_dep, nhg_connected_head, &nhe->nhg_depends) {
+ depend = rb_node_dep->nhe;
+
+ /*
+ * If its recursive, use its resolved nhe in the group
+ */
+ if (CHECK_FLAG(depend->flags, NEXTHOP_GROUP_RECURSIVE)) {
+ depend = zebra_nhg_resolve(depend);
+ if (!depend) {
+ flog_err(
+ EC_ZEBRA_NHG_FIB_UPDATE,
+ "Failed to recursively resolve Nexthop Hash Entry id=%u in the group id=%u",
+ depend->id, nhe->id);
+ continue;
+ }
+ }
+
+ grp[i].id = depend->id;
+ /* We aren't using weights for anything right now */
+ grp[i].weight = 0;
+ i++;
+ }
+ return i;
+}
+
/**
* zebra_nhg_install_kernel() - Install Nexthop Group hash entry into kernel
*
diff --git a/zebra/zebra_nhg.h b/zebra/zebra_nhg.h
index e287dacfdb..c6ac7d4706 100644
--- a/zebra/zebra_nhg.h
+++ b/zebra/zebra_nhg.h
@@ -88,6 +88,10 @@ struct nhg_hash_entry {
* The NEXTHOP_GROUP_VALID flag should also be set by this point.
*/
#define NEXTHOP_GROUP_QUEUED 0x4
+/*
+ * Is this a nexthop that is recursively resolved?
+ */
+#define NEXTHOP_GROUP_RECURSIVE 0x8
};
/* Abstraction for connected trees */
@@ -151,6 +155,8 @@ extern unsigned int
nhg_connected_head_count(const struct nhg_connected_head *head);
extern void nhg_connected_head_free(struct nhg_connected_head *head);
extern bool nhg_connected_head_is_empty(const struct nhg_connected_head *head);
+extern struct nhg_connected *
+nhg_connected_head_root(const struct nhg_connected_head *head);
extern void nhg_connected_head_del(struct nhg_connected_head *head,
struct nhg_hash_entry *nhe);
extern void nhg_connected_head_add(struct nhg_connected_head *head,
@@ -160,6 +166,11 @@ extern void nhg_connected_head_add(struct nhg_connected_head *head,
* NHE abstracted tree functions.
* Use these where possible instead of the direct ones access ones.
*/
+
+
+extern struct nhg_hash_entry *zebra_nhg_resolve(struct nhg_hash_entry *nhe);
+extern uint32_t zebra_nhg_get_resolved_id(uint32_t id);
+
/* Depends */
extern unsigned int zebra_nhg_depends_count(const struct nhg_hash_entry *nhe);
extern bool zebra_nhg_depends_is_empty(const struct nhg_hash_entry *nhe);
@@ -218,6 +229,9 @@ void zebra_nhg_set_if(struct nhg_hash_entry *nhe, struct interface *ifp);
extern int nexthop_active_update(struct route_node *rn, struct route_entry *re);
+extern uint8_t zebra_nhg_nhe2grp(struct nh_grp *grp,
+ struct nhg_hash_entry *nhe);
+
void zebra_nhg_install_kernel(struct nhg_hash_entry *nhe);
void zebra_nhg_uninstall_kernel(struct nhg_hash_entry *nhe);
diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c
index 6276ed3478..438923e232 100644
--- a/zebra/zebra_rib.c
+++ b/zebra/zebra_rib.c
@@ -2717,6 +2717,7 @@ void rib_lookup_and_pushup(struct prefix_ipv4 *p, vrf_id_t vrf_id)
int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p,
struct prefix_ipv6 *src_p, struct route_entry *re)
{
+ struct nhg_hash_entry *nhe = NULL;
struct route_table *table;
struct route_node *rn;
struct route_entry *same = NULL;
@@ -2736,6 +2737,49 @@ int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p,
return 0;
}
+ if (re->nhe_id) {
+ nhe = zebra_nhg_lookup_id(re->nhe_id);
+
+ if (!nhe) {
+ flog_err(
+ EC_ZEBRA_TABLE_LOOKUP_FAILED,
+ "Zebra failed to find the nexthop hash entry for id=%u in a route entry",
+ re->nhe_id);
+ XFREE(MTYPE_RE, re);
+ return -1;
+ }
+ } else {
+ nhe = zebra_nhg_rib_find(0, re->ng, re->vrf_id, afi);
+
+ /*
+ * The nexthops got copied over into an nhe,
+ * so free them now.
+ */
+ nexthop_group_free_delete(&re->ng);
+
+ if (!nhe) {
+ char buf[PREFIX_STRLEN] = "";
+ char buf2[PREFIX_STRLEN] = "";
+
+ flog_err(
+ EC_ZEBRA_TABLE_LOOKUP_FAILED,
+ "Zebra failed to find or create a nexthop hash entry for %s%s%s",
+ prefix2str(p, buf, sizeof(buf)),
+ src_p ? " from " : "",
+ src_p ? prefix2str(src_p, buf2, sizeof(buf2))
+ : "");
+
+ XFREE(MTYPE_RE, re);
+ return -1;
+ }
+
+ re->nhe_id = nhe->id;
+ }
+
+ /* Attach the re to the nhe's nexthop group */
+ zebra_nhg_increment_ref(nhe);
+ re->ng = nhe->nhg;
+
/* Make it sure prefixlen is applied to the prefix. */
apply_mask(p);
if (src_p)