]> git.puffer.fish Git - mirror/frr.git/commitdiff
zebra: Add recursive functionality to NHE's
authorStephen Worley <sworley@cumulusnetworks.com>
Tue, 14 May 2019 22:27:40 +0000 (15:27 -0700)
committerStephen Worley <sworley@cumulusnetworks.com>
Fri, 25 Oct 2019 15:13:40 +0000 (11:13 -0400)
Add the ability to recursively resolve nexthop group hash entries
and resolve them when sending to the kernel.

When copying over nexthops into an NHE, copy resolved info as well.

Signed-off-by: Stephen Worley <sworley@cumulusnetworks.com>
lib/nexthop.h
lib/nexthop_group.c
lib/nexthop_group.h
zebra/rt_netlink.c
zebra/zebra_dplane.c
zebra/zebra_nhg.c
zebra/zebra_nhg.h
zebra/zebra_rib.c

index 9dd5fc6fd3f39d63556e0bd14b72a12d7fcf5141..5558e857f664fae4826da896db8d7ca4008dd228 100644 (file)
@@ -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,
index 463ab0b881c9c06f456e80c2e9b1fe2c2f9c6581..c9a8f1af5102e0987cd4151302eb37b98563d6a6 100644 (file)
@@ -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;
        }
index be6f50d8a0d34f4a5358962cf10e036beffecbf3..57a5a975993e149d9f82a67beeccde0872e103f5 100644 (file)
@@ -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
 }
index 5d2d407688c5c8c3275d889f2aa4ca7b3e6fbd1f..89fed59f7614555e4efe65c043b7779b6b0a2031 100644 (file)
@@ -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:
index f818ed5bc6e9a205f187af2365a27e9af260e321..429d4c59a1a2e84c7e25dcfcb2af64557e3ff211 100644 (file)
@@ -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 */
index 94d41ba24611f3c63c3b3463d43c762caa0427e2..144decfbb59b4371273c4ec3fee22a85ff62d2a7 100644 (file)
@@ -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
  *
index e287dacfdb7e7048bc5b3a14173c8ca889cfeca4..c6ac7d4706aaf39382ae98c795ab8e6b0e134806 100644 (file)
@@ -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);
 
index 6276ed34785fb999854e6444c65ec1bb83a8c128..438923e232e27733ea2c684c7a8b0bd07a1f35d6 100644 (file)
@@ -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)