#include "zebra/zebra_dplane.h"
#include "zebra/zebra_nhg.h"
+DEFINE_MTYPE_STATIC(ZEBRA, RIB_UPDATE_CTX, "Rib update context object");
+
/*
* Event, list, and mutex for delivery of dataplane results
*/
/* Add nexthop to the end of a rib node's nexthop list */
void route_entry_nexthop_add(struct route_entry *re, struct nexthop *nexthop)
{
- _nexthop_group_add_sorted(&re->ng, nexthop);
- re->nexthop_num++;
+ _nexthop_group_add_sorted(re->ng, nexthop);
}
*/
void route_entry_copy_nexthops(struct route_entry *re, struct nexthop *nh)
{
- assert(!re->ng.nexthop);
- copy_nexthops(&re->ng.nexthop, nh, NULL);
- for (struct nexthop *nexthop = nh; nexthop; nexthop = nexthop->next)
- re->nexthop_num++;
+ assert(!re->ng->nexthop);
+ copy_nexthops(&re->ng->nexthop, nh, NULL);
}
/* Delete specified nexthop from the list. */
if (nexthop->prev)
nexthop->prev->next = nexthop->next;
else
- re->ng.nexthop = nexthop->next;
- re->nexthop_num--;
+ re->ng->nexthop = nexthop->next;
}
if (re->type != ZEBRA_ROUTE_BGP)
return 0;
- for (ALL_NEXTHOPS(re->ng, nexthop))
+ for (ALL_NEXTHOPS_PTR(re->ng, nexthop))
if (!nexthop->nh_label || !nexthop->nh_label->num_labels)
return 0;
srcdest_rnode_prefixes(rn, &p, &src_p);
if (info->safi != SAFI_UNICAST) {
- for (ALL_NEXTHOPS(re->ng, nexthop))
+ for (ALL_NEXTHOPS_PTR(re->ng, nexthop))
SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
return;
} else {
struct nexthop *prev;
- for (ALL_NEXTHOPS(re->ng, nexthop)) {
+ for (ALL_NEXTHOPS_PTR(re->ng, nexthop)) {
UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_DUPLICATE);
- for (ALL_NEXTHOPS(re->ng, prev)) {
+ for (ALL_NEXTHOPS_PTR(re->ng, prev)) {
if (prev == nexthop)
break;
if (nexthop_same_firsthop(nexthop, prev)) {
if (!RIB_SYSTEM_ROUTE(old)) {
/* Clear old route's FIB flags */
- for (ALL_NEXTHOPS(old->ng, nexthop)) {
+ for (ALL_NEXTHOPS_PTR(old->ng, nexthop)) {
UNSET_FLAG(nexthop->flags,
NEXTHOP_FLAG_FIB);
}
if (info->safi != SAFI_UNICAST) {
UNSET_FLAG(re->status, ROUTE_ENTRY_INSTALLED);
- for (ALL_NEXTHOPS(re->ng, nexthop))
+ for (ALL_NEXTHOPS_PTR(re->ng, nexthop))
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
return;
}
re->fib_ng.nexthop = NULL;
}
- for (ALL_NEXTHOPS(re->ng, nexthop))
+ for (ALL_NEXTHOPS_PTR(re->ng, nexthop))
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
}
srcdest_rnode_prefixes(rn, &p, &src_p);
- redistribute_delete(p, src_p, re);
+ redistribute_delete(p, src_p, re, NULL);
UNSET_FLAG(re->flags, ZEBRA_FLAG_SELECTED);
}
}
/* Update real nexthop. This may actually determine if nexthop is active
* or not. */
- if (!nexthop_group_active_nexthop_num(&new->ng)) {
+ if (!nexthop_group_active_nexthop_num(new->ng)) {
UNSET_FLAG(new->status, ROUTE_ENTRY_CHANGED);
return;
}
/* Update the nexthop; we could determine here that nexthop is
* inactive. */
- if (nexthop_group_active_nexthop_num(&new->ng))
+ if (nexthop_group_active_nexthop_num(new->ng))
nh_active = 1;
/* If nexthop is active, install the selected route, if
/* both are connected. are either loop or vrf? */
struct nexthop *nexthop = NULL;
- for (ALL_NEXTHOPS(alternate->ng, nexthop)) {
- if (if_is_loopback_or_vrf(if_lookup_by_index(
- nexthop->ifindex, alternate->vrf_id)))
+ for (ALL_NEXTHOPS_PTR(alternate->ng, nexthop)) {
+ struct interface *ifp = if_lookup_by_index(
+ nexthop->ifindex, alternate->vrf_id);
+
+ if (ifp && if_is_loopback_or_vrf(ifp))
return alternate;
}
- for (ALL_NEXTHOPS(current->ng, nexthop)) {
- if (if_is_loopback_or_vrf(if_lookup_by_index(
- nexthop->ifindex, current->vrf_id)))
+ for (ALL_NEXTHOPS_PTR(current->ng, nexthop)) {
+ struct interface *ifp = if_lookup_by_index(
+ nexthop->ifindex, current->vrf_id);
+
+ if (ifp && if_is_loopback_or_vrf(ifp))
return current;
}
SET_FLAG(new_selected->flags, ZEBRA_FLAG_SELECTED);
if (old_selected) {
- if (!new_selected)
- redistribute_delete(p, src_p, old_selected);
+ /*
+ * If we're removing the old entry, we should tell
+ * redist subscribers about that *if* they aren't
+ * going to see a redist for the new entry.
+ */
+ if (!new_selected || CHECK_FLAG(old_selected->status,
+ ROUTE_ENTRY_REMOVED))
+ redistribute_delete(p, src_p,
+ old_selected,
+ new_selected);
+
if (old_selected != new_selected)
UNSET_FLAG(old_selected->flags,
ZEBRA_FLAG_SELECTED);
SET_FLAG(re->status, ROUTE_ENTRY_INSTALLED);
UNSET_FLAG(re->status, ROUTE_ENTRY_QUEUED);
- for (ALL_NEXTHOPS(re->ng, nhop)) {
+ for (ALL_NEXTHOPS_PTR(re->ng, nhop)) {
if (CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_RECURSIVE))
continue;
* that is actually installed.
*/
matched = true;
- for (ALL_NEXTHOPS(re->ng, nexthop)) {
+ for (ALL_NEXTHOPS_PTR(re->ng, nexthop)) {
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
continue;
changed_p = true;
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
- break;
+
+ /* Keep checking nexthops */
+ continue;
}
if (CHECK_FLAG(ctx_nexthop->flags, NEXTHOP_FLAG_FIB)) {
* not-installed; or not-installed to installed.
*/
if (start_count > 0 && end_count > 0) {
+ if (debug_p)
+ zlog_debug("%u:%s applied nexthop changes from dplane notification",
+ dplane_ctx_get_vrf(ctx), dest_str);
/* Changed nexthops - update kernel/others */
dplane_route_notif_update(rn, re,
dplane_route_notif_update(rn, re, DPLANE_OP_ROUTE_DELETE, ctx);
/* Redistribute, lsp, and nht update */
- redistribute_delete(dest_pfx, src_pfx, re);
+ redistribute_delete(dest_pfx, src_pfx, re, NULL);
zebra_rib_evaluate_rn_nexthops(
rn, zebra_router_get_next_sequence());
return 1;
}
-
-/*
- * Perform next-hop tracking processing after RIB updates.
- */
-static void do_nht_processing(void)
-{
-}
-
/* Dispatch the meta queue by picking, processing and unlocking the next RN from
* a non-empty sub-queue with lowest priority. wq is equal to zebra->ribq and
* data
void rib_unlink(struct route_node *rn, struct route_entry *re)
{
rib_dest_t *dest;
+ struct nhg_hash_entry *nhe = NULL;
assert(rn && re);
if (dest->selected_fib == re)
dest->selected_fib = NULL;
- nexthops_free(re->ng.nexthop);
+ nhe = zebra_nhg_lookup_id(re->nhe_id);
+ if (nhe)
+ zebra_nhg_decrement_ref(nhe);
+
nexthops_free(re->fib_ng.nexthop);
XFREE(MTYPE_RE, re);
"%s: metric == %u, mtu == %u, distance == %u, flags == %u, status == %u",
straddr, re->metric, re->mtu, re->distance, re->flags, re->status);
zlog_debug("%s: nexthop_num == %u, nexthop_active_num == %u", straddr,
- re->nexthop_num, re->nexthop_active_num);
+ nexthop_group_nexthop_num(re->ng),
+ nexthop_group_active_nexthop_num(re->ng));
- for (ALL_NEXTHOPS(re->ng, nexthop)) {
+ for (ALL_NEXTHOPS_PTR(re->ng, nexthop)) {
struct interface *ifp;
struct vrf *vrf = vrf_lookup_by_id(nexthop->vrf_id);
struct route_table *table;
struct route_node *rn;
struct route_entry *same = NULL;
+ struct nhg_hash_entry *nhe = NULL;
+ struct list *nhg_depends = NULL;
+ /* Default to route afi */
+ afi_t nhg_afi = afi;
int ret = 0;
if (!re)
/* Lookup table. */
table = zebra_vrf_table_with_table_id(afi, safi, re->vrf_id, re->table);
if (!table) {
+ zebra_nhg_free_group_depends(re->ng, nhg_depends);
XFREE(MTYPE_RE, re);
return 0;
}
if (src_p)
apply_mask_ipv6(src_p);
+ /* If its a group, create a dependency list */
+ if (re->ng && re->ng->nexthop->next) {
+ struct nexthop *nh = NULL;
+ struct nexthop lookup = {0};
+ struct nhg_hash_entry *depend = NULL;
+
+ nhg_depends = nhg_depend_new_list();
+
+ for (ALL_NEXTHOPS_PTR(re->ng, 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(&lookup, afi);
+ nhg_depend_add(nhg_depends, depend);
+ }
+
+ /* change the afi for group */
+ if (listcount(nhg_depends))
+ nhg_afi = AFI_UNSPEC;
+ }
+
+ nhe = zebra_nhg_find(re->ng, re->vrf_id, nhg_afi, re->nhe_id,
+ nhg_depends, false);
+
+ if (nhe) {
+ // TODO: Add interface pointer
+ zebra_nhg_free_group_depends(re->ng, nhg_depends);
+ re->ng = nhe->nhg;
+ re->nhe_id = nhe->id;
+ zebra_nhg_increment_ref(nhe);
+ } else {
+ flog_err(
+ EC_ZEBRA_TABLE_LOOKUP_FAILED,
+ "Zebra failed to find or create a nexthop hash entry for id=%u in a route entry",
+ re->nhe_id);
+ }
+
+
/* Set default distance by route type. */
if (re->distance == 0)
re->distance = route_distance(re->type);
if (re->type == ZEBRA_ROUTE_KERNEL && re->metric != metric)
continue;
- if (re->type == ZEBRA_ROUTE_CONNECT && (rtnh = re->ng.nexthop)
+ if (re->type == ZEBRA_ROUTE_CONNECT && (rtnh = re->ng->nexthop)
&& rtnh->type == NEXTHOP_TYPE_IFINDEX && nh) {
if (rtnh->ifindex != nh->ifindex)
continue;
same = re;
break;
}
- for (ALL_NEXTHOPS(re->ng, rtnh))
+ for (ALL_NEXTHOPS_PTR(re->ng, rtnh))
/*
* No guarantee all kernel send nh with labels
* on delete.
if (allow_delete) {
UNSET_FLAG(fib->status, ROUTE_ENTRY_INSTALLED);
/* Unset flags. */
- for (rtnh = fib->ng.nexthop; rtnh;
+ for (rtnh = fib->ng->nexthop; rtnh;
rtnh = rtnh->next)
UNSET_FLAG(rtnh->flags,
NEXTHOP_FLAG_FIB);
if (CHECK_FLAG(flags, ZEBRA_FLAG_EVPN_ROUTE)) {
struct nexthop *tmp_nh;
- for (ALL_NEXTHOPS(re->ng, tmp_nh)) {
+ for (ALL_NEXTHOPS_PTR(re->ng, tmp_nh)) {
struct ipaddr vtep_ip;
memset(&vtep_ip, 0, sizeof(struct ipaddr));
int rib_add(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
unsigned short instance, int flags, struct prefix *p,
struct prefix_ipv6 *src_p, const struct nexthop *nh,
- uint32_t table_id, uint32_t metric, uint32_t mtu, uint8_t distance,
- route_tag_t tag)
+ uint32_t nhe_id, uint32_t table_id, uint32_t metric, uint32_t mtu,
+ uint8_t distance, route_tag_t tag)
{
- struct route_entry *re;
- struct nexthop *nexthop;
+ struct route_entry *re = NULL;
+ struct nexthop *nexthop = NULL;
/* Allocate new route_entry structure. */
re = XCALLOC(MTYPE_RE, sizeof(struct route_entry));
re->mtu = mtu;
re->table = table_id;
re->vrf_id = vrf_id;
- re->nexthop_num = 0;
re->uptime = monotime(NULL);
re->tag = tag;
+ re->nhe_id = nhe_id;
+
+ re->ng = nexthop_group_new();
/* Add nexthop. */
nexthop = nexthop_new();
return rib_add_multipath(afi, safi, p, src_p, re);
}
+static const char *rib_update_event2str(rib_update_event_t event)
+{
+ const char *ret = "UNKNOWN";
+
+ switch (event) {
+ case RIB_UPDATE_KERNEL:
+ ret = "RIB_UPDATE_KERNEL";
+ break;
+ case RIB_UPDATE_RMAP_CHANGE:
+ ret = "RIB_UPDATE_RMAP_CHANGE";
+ break;
+ case RIB_UPDATE_OTHER:
+ ret = "RIB_UPDATE_OTHER";
+ break;
+ case RIB_UPDATE_MAX:
+ break;
+ }
+
+ return ret;
+}
+
+
+/* Schedule route nodes to be processed if they match the type */
+static void rib_update_route_node(struct route_node *rn, int type)
+{
+ struct route_entry *re, *next;
+ bool re_changed = false;
+
+ RNODE_FOREACH_RE_SAFE (rn, re, next) {
+ if (type == ZEBRA_ROUTE_ALL || type == re->type) {
+ SET_FLAG(re->status, ROUTE_ENTRY_CHANGED);
+ re_changed = true;
+ }
+ }
+
+ if (re_changed)
+ rib_queue_add(rn);
+}
+
/* Schedule routes of a particular table (address-family) based on event. */
void rib_update_table(struct route_table *table, rib_update_event_t event)
{
struct route_node *rn;
- struct route_entry *re, *next;
+
+ if (IS_ZEBRA_DEBUG_EVENT) {
+ struct zebra_vrf *zvrf;
+ struct vrf *vrf;
+
+ zvrf = table->info ? ((rib_table_info_t *)table->info)->zvrf
+ : NULL;
+ vrf = zvrf ? zvrf->vrf : NULL;
+
+ zlog_debug("%s: %s VRF %s Table %u event %s", __func__,
+ table->info ? afi2str(
+ ((rib_table_info_t *)table->info)->afi)
+ : "Unknown",
+ vrf ? vrf->name : "Unknown",
+ zvrf ? zvrf->table_id : 0,
+ rib_update_event2str(event));
+ }
/* Walk all routes and queue for processing, if appropriate for
* the trigger event.
* has already been queued we don't
* need to queue it up again
*/
- if (rn->info && CHECK_FLAG(rib_dest_from_rnode(rn)->flags,
- RIB_ROUTE_ANY_QUEUED))
+ if (rn->info
+ && CHECK_FLAG(rib_dest_from_rnode(rn)->flags,
+ RIB_ROUTE_ANY_QUEUED))
continue;
+
switch (event) {
+ case RIB_UPDATE_KERNEL:
+ rib_update_route_node(rn, ZEBRA_ROUTE_KERNEL);
+ break;
case RIB_UPDATE_RMAP_CHANGE:
case RIB_UPDATE_OTHER:
- /* Right now, examine all routes. Can restrict to a
- * protocol in
- * some cases (TODO).
- */
- if (rnode_to_ribs(rn)) {
- RNODE_FOREACH_RE_SAFE (rn, re, next)
- SET_FLAG(re->status,
- ROUTE_ENTRY_CHANGED);
- rib_queue_add(rn);
- }
+ rib_update_route_node(rn, ZEBRA_ROUTE_ALL);
break;
-
default:
break;
}
}
}
-/* RIB update function. */
-void rib_update(vrf_id_t vrf_id, rib_update_event_t event)
+static void rib_update_handle_vrf(vrf_id_t vrf_id, rib_update_event_t event)
{
struct route_table *table;
+ if (IS_ZEBRA_DEBUG_EVENT)
+ zlog_debug("%s: Handling VRF %s event %s", __func__,
+ vrf_id_to_name(vrf_id), rib_update_event2str(event));
+
/* Process routes of interested address-families. */
table = zebra_vrf_table(AFI_IP, SAFI_UNICAST, vrf_id);
- if (table) {
- if (IS_ZEBRA_DEBUG_EVENT)
- zlog_debug("%s : AFI_IP event %d", __func__, event);
+ if (table)
rib_update_table(table, event);
- }
table = zebra_vrf_table(AFI_IP6, SAFI_UNICAST, vrf_id);
- if (table) {
- if (IS_ZEBRA_DEBUG_EVENT)
- zlog_debug("%s : AFI_IP6 event %d", __func__, event);
+ if (table)
rib_update_table(table, event);
- }
+}
+
+static void rib_update_handle_vrf_all(rib_update_event_t event)
+{
+ struct zebra_router_table *zrt;
+
+ if (IS_ZEBRA_DEBUG_EVENT)
+ zlog_debug("%s: Handling VRF (ALL) event %s", __func__,
+ rib_update_event2str(event));
+
+ /* Just iterate over all the route tables, rather than vrf lookups */
+ RB_FOREACH (zrt, zebra_router_table_head, &zrouter.tables)
+ rib_update_table(zrt->table, event);
+}
+
+struct rib_update_ctx {
+ rib_update_event_t event;
+ bool vrf_all;
+ vrf_id_t vrf_id;
+};
+
+static struct rib_update_ctx *rib_update_ctx_init(vrf_id_t vrf_id,
+ rib_update_event_t event)
+{
+ struct rib_update_ctx *ctx;
+
+ ctx = XCALLOC(MTYPE_RIB_UPDATE_CTX, sizeof(struct rib_update_ctx));
+
+ ctx->event = event;
+ ctx->vrf_id = vrf_id;
+
+ return ctx;
+}
+
+static void rib_update_ctx_fini(struct rib_update_ctx **ctx)
+{
+ XFREE(MTYPE_RIB_UPDATE_CTX, *ctx);
+
+ *ctx = NULL;
+}
+
+static int rib_update_handler(struct thread *thread)
+{
+ struct rib_update_ctx *ctx;
+
+ ctx = THREAD_ARG(thread);
+
+ if (ctx->vrf_all)
+ rib_update_handle_vrf_all(ctx->event);
+ else
+ rib_update_handle_vrf(ctx->vrf_id, ctx->event);
+
+ rib_update_ctx_fini(&ctx);
+
+ return 0;
+}
+
+/*
+ * Thread list to ensure we don't schedule a ton of events
+ * if interfaces are flapping for instance.
+ */
+static struct thread *t_rib_update_threads[RIB_UPDATE_MAX];
+
+/* Schedule a RIB update event for specific vrf */
+void rib_update_vrf(vrf_id_t vrf_id, rib_update_event_t event)
+{
+ struct rib_update_ctx *ctx;
+
+ ctx = rib_update_ctx_init(vrf_id, event);
+
+ /* Don't worry about making sure multiple rib updates for specific vrf
+ * are scheduled at once for now. If it becomes a problem, we can use a
+ * lookup of some sort to keep track of running threads via t_vrf_id
+ * like how we are doing it in t_rib_update_threads[].
+ */
+ thread_add_event(zrouter.master, rib_update_handler, ctx, 0, NULL);
+
+ if (IS_ZEBRA_DEBUG_EVENT)
+ zlog_debug("%s: Scheduled VRF %s, event %s", __func__,
+ vrf_id_to_name(ctx->vrf_id),
+ rib_update_event2str(event));
+}
+
+/* Schedule a RIB update event for all vrfs */
+void rib_update(rib_update_event_t event)
+{
+ struct rib_update_ctx *ctx;
+
+ ctx = rib_update_ctx_init(0, event);
+
+ ctx->vrf_all = true;
+
+ if (!thread_add_event(zrouter.master, rib_update_handler, ctx, 0,
+ &t_rib_update_threads[event]))
+ rib_update_ctx_fini(&ctx); /* Already scheduled */
+ else if (IS_ZEBRA_DEBUG_EVENT)
+ zlog_debug("%s: Schedued VRF (ALL), event %s", __func__,
+ rib_update_event2str(event));
}
/* Delete self installed routes after zebra is relaunched. */
* this decision needs to be revisited
*/
SET_FLAG(re->status, ROUTE_ENTRY_INSTALLED);
- for (ALL_NEXTHOPS(re->ng, nexthop))
+ for (ALL_NEXTHOPS_PTR(re->ng, nexthop))
SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
rib_uninstall_kernel(rn, re);
{
struct zebra_dplane_ctx *ctx;
struct dplane_ctx_q ctxlist;
+ bool shut_p = false;
/* Dequeue a list of completed updates with one lock/unlock cycle */
if (ctx == NULL)
break;
+ /* If zebra is shutting down, avoid processing results,
+ * just drain the results queue.
+ */
+ shut_p = atomic_load_explicit(&zrouter.in_shutdown,
+ memory_order_relaxed);
+ if (shut_p) {
+ while (ctx) {
+ dplane_ctx_fini(&ctx);
+
+ ctx = dplane_ctx_dequeue(&ctxlist);
+ }
+
+ continue;
+ }
+
while (ctx) {
switch (dplane_ctx_get_op(ctx)) {
case DPLANE_OP_ROUTE_INSTALL:
rib_process_dplane_notify(ctx);
break;
+ case DPLANE_OP_NH_INSTALL:
+ case DPLANE_OP_NH_UPDATE:
+ case DPLANE_OP_NH_DELETE:
+ zebra_nhg_dplane_result(ctx);
+ break;
+
case DPLANE_OP_LSP_INSTALL:
case DPLANE_OP_LSP_UPDATE:
case DPLANE_OP_LSP_DELETE:
zebra_vxlan_handle_result(ctx);
break;
- default:
+ /* Some op codes not handled here */
+ case DPLANE_OP_ADDR_INSTALL:
+ case DPLANE_OP_ADDR_UNINSTALL:
+ case DPLANE_OP_NEIGH_INSTALL:
+ case DPLANE_OP_NEIGH_UPDATE:
+ case DPLANE_OP_NEIGH_DELETE:
+ case DPLANE_OP_VTEP_ADD:
+ case DPLANE_OP_VTEP_DELETE:
+ case DPLANE_OP_NONE:
/* Don't expect this: just return the struct? */
dplane_ctx_fini(&ctx);
break;
+
} /* Dispatch by op code */
ctx = dplane_ctx_dequeue(&ctxlist);
} while (1);
- /* Check for nexthop tracking processing after finishing with results */
- do_nht_processing();
-
return 0;
}