/* id counter to keep in sync with kernel */
uint32_t id_counter;
-static struct nhg_hash_entry *depends_find(struct nexthop *nh, afi_t afi);
+/* */
+static bool g_nexthops_enabled = true;
+
+static struct nhg_hash_entry *depends_find(const struct nexthop *nh,
+ afi_t afi);
static void depends_add(struct nhg_connected_tree_head *head,
struct nhg_hash_entry *depend);
static struct nhg_hash_entry *
return nhg_connected_tree_first(head);
}
-void nhg_connected_tree_del_nhe(struct nhg_connected_tree_head *head,
- struct nhg_hash_entry *depend)
+struct nhg_hash_entry *
+nhg_connected_tree_del_nhe(struct nhg_connected_tree_head *head,
+ struct nhg_hash_entry *depend)
{
struct nhg_connected lookup = {};
struct nhg_connected *remove = NULL;
+ struct nhg_hash_entry *removed_nhe;
lookup.nhe = depend;
/* Lookup to find the element, then remove it */
remove = nhg_connected_tree_find(head, &lookup);
- remove = nhg_connected_tree_del(head, remove);
-
if (remove)
+ /* Re-returning here just in case this API changes..
+ * the _del list api's are a bit undefined at the moment.
+ *
+ * So hopefully returning here will make it fail if the api
+ * changes to something different than currently expected.
+ */
+ remove = nhg_connected_tree_del(head, remove);
+
+ /* If the entry was sucessfully removed, free the 'connected` struct */
+ if (remove) {
+ removed_nhe = remove->nhe;
nhg_connected_free(remove);
+ return removed_nhe;
+ }
+
+ return NULL;
}
-void nhg_connected_tree_add_nhe(struct nhg_connected_tree_head *head,
- struct nhg_hash_entry *depend)
+/* Assuming UNIQUE RB tree. If this changes, assumptions here about
+ * insertion need to change.
+ */
+struct nhg_hash_entry *
+nhg_connected_tree_add_nhe(struct nhg_connected_tree_head *head,
+ struct nhg_hash_entry *depend)
{
struct nhg_connected *new = NULL;
new = nhg_connected_new(depend);
- if (new)
- nhg_connected_tree_add(head, new);
+ /* On success, NULL will be returned from the
+ * RB code.
+ */
+ if (new && (nhg_connected_tree_add(head, new) == NULL))
+ return NULL;
+
+ /* If it wasn't successful, it must be a duplicate. We enforce the
+ * unique property for the `nhg_connected` tree.
+ */
+ nhg_connected_free(new);
+
+ return depend;
}
static void
struct interface *ifp = NULL;
ifp = if_lookup_by_index(nhe->nhg->nexthop->ifindex,
- nhe->vrf_id);
+ nhe->nhg->nexthop->vrf_id);
if (ifp)
zebra_nhg_set_if(nhe, ifp);
else
flog_err(
EC_ZEBRA_IF_LOOKUP_FAILED,
"Zebra failed to lookup an interface with ifindex=%d in vrf=%u for NHE id=%u",
- nhe->nhg->nexthop->ifindex, nhe->vrf_id,
- nhe->id);
+ nhe->nhg->nexthop->ifindex,
+ nhe->nhg->nexthop->vrf_id, nhe->id);
}
}
-static struct nhg_hash_entry *zebra_nhg_copy(struct nhg_hash_entry *copy,
- uint32_t id)
+struct nhg_hash_entry *zebra_nhg_alloc(void)
{
struct nhg_hash_entry *nhe;
nhe = XCALLOC(MTYPE_NHG, sizeof(struct nhg_hash_entry));
+ return nhe;
+}
+
+static struct nhg_hash_entry *zebra_nhg_copy(const struct nhg_hash_entry *copy,
+ uint32_t id)
+{
+ struct nhg_hash_entry *nhe;
+
+ nhe = zebra_nhg_alloc();
+
nhe->id = id;
nhe->nhg = nexthop_group_new();
struct nhg_hash_entry *depend = NULL;
struct nexthop_group resolved_ng = {};
- _nexthop_group_add_sorted(&resolved_ng, nh);
+ resolved_ng.nexthop = nh;
depend = zebra_nhg_rib_find(0, &resolved_ng, afi);
- depends_add(nhg_depends, depend);
+
+ if (depend)
+ depends_add(nhg_depends, depend);
}
static bool zebra_nhg_find(struct nhg_hash_entry **nhe, uint32_t id,
lookup.type = type ? type : ZEBRA_ROUTE_NHG;
lookup.nhg = nhg;
+ lookup.vrf_id = vrf_id;
if (lookup.nhg->nexthop->next) {
/* Groups can have all vrfs and AF's in them */
lookup.afi = AFI_UNSPEC;
- lookup.vrf_id = 0;
} else {
switch (lookup.nhg->nexthop->type) {
case (NEXTHOP_TYPE_IFINDEX):
lookup.afi = AFI_IP6;
break;
}
-
- lookup.vrf_id = vrf_id;
}
if (id)
{
struct nhg_hash_entry *nhe = NULL;
struct nexthop_group nhg = {};
+ vrf_id_t vrf_id = !vrf_is_backend_netns() ? VRF_DEFAULT : nh->vrf_id;
- _nexthop_group_add_sorted(&nhg, nh);
+ nexthop_group_add_sorted(&nhg, nh);
- zebra_nhg_find(&nhe, id, &nhg, NULL, nh->vrf_id, afi, 0);
+ zebra_nhg_find(&nhe, id, &nhg, NULL, vrf_id, afi, type);
return nhe;
}
}
/* Kernel-side, received delete message */
-int zebra_nhg_kernel_del(uint32_t id)
+int zebra_nhg_kernel_del(uint32_t id, vrf_id_t vrf_id)
{
struct nhg_ctx *ctx = NULL;
- ctx = nhg_ctx_init(id, NULL, NULL, 0, 0, 0, 0);
+ ctx = nhg_ctx_init(id, NULL, NULL, vrf_id, 0, 0, 0);
nhg_ctx_set_op(ctx, NHG_CTX_OP_DEL);
}
/* Some dependency helper functions */
-static struct nhg_hash_entry *depends_find(struct nexthop *nh, afi_t afi)
+static struct nhg_hash_entry *depends_find_recursive(const struct nexthop *nh,
+ afi_t afi)
{
+ struct nhg_hash_entry *nhe;
struct nexthop *lookup = NULL;
- struct nhg_hash_entry *nhe = NULL;
- if (!nh)
- goto done;
-
- copy_nexthops(&lookup, nh, NULL);
-
- /* Clear it, in case its a group */
- nexthops_free(lookup->next);
- nexthops_free(lookup->prev);
- lookup->next = NULL;
- lookup->prev = NULL;
+ lookup = nexthop_dup(nh, NULL);
nhe = zebra_nhg_find_nexthop(0, lookup, afi, 0);
nexthops_free(lookup);
+ return nhe;
+}
+
+static struct nhg_hash_entry *depends_find_singleton(const struct nexthop *nh,
+ afi_t afi)
+{
+ struct nhg_hash_entry *nhe;
+ struct nexthop lookup = {};
+
+ /* Capture a snapshot of this single nh; it might be part of a list,
+ * so we need to make a standalone copy.
+ */
+ nexthop_copy_no_recurse(&lookup, nh, NULL);
+
+ nhe = zebra_nhg_find_nexthop(0, &lookup, afi, 0);
+
+ /* The copy may have allocated labels; free them if necessary. */
+ nexthop_del_labels(&lookup);
+
+ return nhe;
+}
+
+static struct nhg_hash_entry *depends_find(const struct nexthop *nh, afi_t afi)
+{
+ struct nhg_hash_entry *nhe = NULL;
+
+ if (!nh)
+ goto done;
+
+ /* We are separating these functions out to increase handling speed
+ * in the non-recursive case (by not alloc/freeing)
+ */
+ if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_RECURSIVE))
+ nhe = depends_find_recursive(nh, afi);
+ else
+ nhe = depends_find_singleton(nh, afi);
+
done:
return nhe;
}
static void depends_add(struct nhg_connected_tree_head *head,
struct nhg_hash_entry *depend)
{
- nhg_connected_tree_add_nhe(head, depend);
- zebra_nhg_increment_ref(depend);
+ /* If NULL is returned, it was successfully added and
+ * needs to have its refcnt incremented.
+ *
+ * Else the NHE is already present in the tree and doesn't
+ * need to increment the refcnt.
+ */
+ if (nhg_connected_tree_add_nhe(head, depend) == NULL)
+ zebra_nhg_increment_ref(depend);
}
static struct nhg_hash_entry *
zebra_nhg_rib_find(uint32_t id, struct nexthop_group *nhg, afi_t rt_afi)
{
struct nhg_hash_entry *nhe = NULL;
+ vrf_id_t vrf_id;
+
+ /*
+ * CLANG SA is complaining that nexthop may be NULL
+ * Make it happy but this is ridonc
+ */
+ assert(nhg->nexthop);
+ vrf_id = !vrf_is_backend_netns() ? VRF_DEFAULT : nhg->nexthop->vrf_id;
if (!(nhg && nhg->nexthop)) {
flog_err(EC_ZEBRA_TABLE_LOOKUP_FAILED,
return NULL;
}
- zebra_nhg_find(&nhe, id, nhg, NULL, nhg->nexthop->vrf_id, rt_afi, 0);
+ zebra_nhg_find(&nhe, id, nhg, NULL, vrf_id, rt_afi, 0);
return nhe;
}
nhg_connected_tree_free(&nhe->nhg_dependents);
}
-void zebra_nhg_free(void *arg)
+void zebra_nhg_free(struct nhg_hash_entry *nhe)
{
- struct nhg_hash_entry *nhe = NULL;
-
- nhe = (struct nhg_hash_entry *)arg;
-
if (nhe->refcnt)
zlog_debug("nhe_id=%u hash refcnt=%d", nhe->id, nhe->refcnt);
XFREE(MTYPE_NHG, nhe);
}
+void zebra_nhg_hash_free(void *p)
+{
+ zebra_nhg_free((struct nhg_hash_entry *)p);
+}
+
void zebra_nhg_decrement_ref(struct nhg_hash_entry *nhe)
{
nhe->refcnt--;
}
}
+ if ((top->p.family == AF_INET && top->p.prefixlen == 32
+ && nexthop->gate.ipv4.s_addr == top->p.u.prefix4.s_addr)
+ || (top->p.family == AF_INET6 && top->p.prefixlen == 128
+ && memcmp(&nexthop->gate.ipv6, &top->p.u.prefix6, 16) == 0)) {
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+ zlog_debug(
+ "\t:%s: Attempting to install a max prefixlength route through itself",
+ __PRETTY_FUNCTION__);
+ return 0;
+ }
+
/* Make lookup prefix. */
memset(&p, 0, sizeof(struct prefix));
switch (afi) {
if (match->type == ZEBRA_ROUTE_CONNECT) {
/* Directly point connected route. */
- newhop = match->ng->nexthop;
+ newhop = match->nhe->nhg->nexthop;
if (newhop) {
if (nexthop->type == NEXTHOP_TYPE_IPV4
|| nexthop->type == NEXTHOP_TYPE_IPV6)
return 1;
} else if (CHECK_FLAG(re->flags, ZEBRA_FLAG_ALLOW_RECURSION)) {
resolved = 0;
- for (ALL_NEXTHOPS_PTR(match->ng, newhop)) {
+ for (ALL_NEXTHOPS_PTR(match->nhe->nhg, newhop)) {
if (!CHECK_FLAG(match->status,
ROUTE_ENTRY_INSTALLED))
continue;
return resolved;
} else if (re->type == ZEBRA_ROUTE_STATIC) {
resolved = 0;
- for (ALL_NEXTHOPS_PTR(match->ng, newhop)) {
+ for (ALL_NEXTHOPS_PTR(match->nhe->nhg, newhop)) {
if (!CHECK_FLAG(match->status,
ROUTE_ENTRY_INSTALLED))
continue;
UNSET_FLAG(re->status, ROUTE_ENTRY_CHANGED);
/* Copy over the nexthops in current state */
- nexthop_group_copy(&new_grp, re->ng);
+ nexthop_group_copy(&new_grp, re->nhe->nhg);
for (nexthop = new_grp.nexthop; nexthop; nexthop = nexthop->next) {
new_active =
nexthop_active_check(rn, re, nexthop);
- if (new_active
- && nexthop_group_active_nexthop_num(&new_grp)
- >= zrouter.multipath_num) {
- UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ if (new_active && curr_active >= zrouter.multipath_num) {
+ struct nexthop *nh;
+
+ /* Set it and its resolved nexthop as inactive. */
+ for (nh = nexthop; nh; nh = nh->resolved)
+ UNSET_FLAG(nh->flags, NEXTHOP_FLAG_ACTIVE);
+
new_active = 0;
}
new_nhe = zebra_nhg_rib_find(0, &new_grp, rt_afi);
- zebra_nhg_re_update_ref(re, new_nhe);
+ route_entry_update_nhe(re, new_nhe);
}
if (curr_active) {
return curr_active;
}
-static void zebra_nhg_re_attach_ref(struct route_entry *re,
- struct nhg_hash_entry *new)
-{
- re->ng = new->nhg;
- re->nhe_id = new->id;
-
- zebra_nhg_increment_ref(new);
-}
-
-int zebra_nhg_re_update_ref(struct route_entry *re, struct nhg_hash_entry *new)
-{
- struct nhg_hash_entry *old = NULL;
- int ret = 0;
-
- if (new == NULL) {
- re->ng = NULL;
- goto done;
- }
-
- if (re->nhe_id != new->id) {
- old = zebra_nhg_lookup_id(re->nhe_id);
-
- zebra_nhg_re_attach_ref(re, new);
-
- if (old)
- zebra_nhg_decrement_ref(old);
- } else if (!re->ng)
- /* This is the first time it's being attached */
- zebra_nhg_re_attach_ref(re, new);
-
-done:
- return ret;
-}
-
/* Convert a nhe into a group array */
uint8_t zebra_nhg_nhe2grp(struct nh_grp *grp, struct nhg_hash_entry *nhe,
int max_num)
if (!duplicate) {
grp[i].id = depend->id;
/* We aren't using weights for anything right now */
- grp[i].weight = 0;
+ grp[i].weight = depend->nhg->nexthop->weight;
i++;
}
{
hash_iterate(hash, zebra_nhg_sweep_entry, NULL);
}
+
+/* Global control to disable use of kernel nexthops, if available. We can't
+ * force the kernel to support nexthop ids, of course, but we can disable
+ * zebra's use of them, for testing e.g. By default, if the kernel supports
+ * nexthop ids, zebra uses them.
+ */
+void zebra_nhg_enable_kernel_nexthops(bool set)
+{
+ g_nexthops_enabled = set;
+}
+
+bool zebra_nhg_kernel_nexthops_enabled(void)
+{
+ return g_nexthops_enabled;
+}