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)
{
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);
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;
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;
}
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);
}
}
nhe = zebra_nhg_find(id, nhg, &nhg_depends, nhg_vrf_id, nhg_afi, false);
-
return 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);
}
}
/*
* 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;
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__);
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",
* 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;
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);
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);
*/
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;
* 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
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
*