]> git.proxmox.com Git - mirror_frr.git/blobdiff - zebra/zebra_rib.c
zebra: Releasing/uninstalling re-work with groups
[mirror_frr.git] / zebra / zebra_rib.c
index 157c67fa62dcb6d6b4f658748af577bf49f77396..3155570df5e3d2f937833629b99d712456ac82d6 100644 (file)
@@ -58,6 +58,8 @@
 #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
  */
@@ -194,8 +196,7 @@ int zebra_check_addr(const struct prefix *p)
 /* 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);
 }
 
 
@@ -204,10 +205,8 @@ void route_entry_nexthop_add(struct route_entry *re, struct nexthop *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. */
@@ -218,8 +217,7 @@ void route_entry_nexthop_delete(struct route_entry *re, struct nexthop *nexthop)
        if (nexthop->prev)
                nexthop->prev->next = nexthop->next;
        else
-               re->ng.nexthop = nexthop->next;
-       re->nexthop_num--;
+               re->ng->nexthop = nexthop->next;
 }
 
 
@@ -503,7 +501,7 @@ int zebra_rib_labeled_unicast(struct route_entry *re)
        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;
 
@@ -527,15 +525,15 @@ void rib_install_kernel(struct route_node *rn, struct route_entry *re,
        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)) {
@@ -584,7 +582,7 @@ void rib_install_kernel(struct route_node *rn, struct route_entry *re,
 
                        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);
                                }
@@ -622,7 +620,7 @@ void rib_uninstall_kernel(struct route_node *rn, struct route_entry *re)
 
        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;
        }
@@ -682,7 +680,7 @@ static void rib_uninstall(struct route_node *rn, struct route_entry *re)
                        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);
        }
 
@@ -691,7 +689,7 @@ static void rib_uninstall(struct route_node *rn, struct route_entry *re)
 
                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);
        }
 }
@@ -858,7 +856,7 @@ static void rib_process_add_fib(struct zebra_vrf *zvrf, struct route_node *rn,
 
        /* 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;
        }
@@ -927,7 +925,7 @@ static void rib_process_update_fib(struct zebra_vrf *zvrf,
 
                /* 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
@@ -1045,15 +1043,19 @@ static struct route_entry *rib_choose_best(struct route_entry *current,
                /* 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;
                }
 
@@ -1248,8 +1250,17 @@ static void rib_process(struct route_node *rn)
                        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);
@@ -1365,7 +1376,7 @@ static void zebra_rib_fixup_system(struct route_node *rn)
                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;
 
@@ -1517,7 +1528,7 @@ static bool rib_update_re_from_ctx(struct route_entry *re,
         * 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;
@@ -1547,7 +1558,9 @@ static bool rib_update_re_from_ctx(struct route_entry *re,
                                changed_p = true;
 
                        UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
-                       break;
+
+                       /* Keep checking nexthops */
+                       continue;
                }
 
                if (CHECK_FLAG(ctx_nexthop->flags, NEXTHOP_FLAG_FIB)) {
@@ -1983,6 +1996,9 @@ static void rib_process_dplane_notify(struct zebra_dplane_ctx *ctx)
         * 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,
@@ -2026,7 +2042,7 @@ static void rib_process_dplane_notify(struct zebra_dplane_ctx *ctx)
                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());
@@ -2088,14 +2104,6 @@ static unsigned int process_subq(struct list *subq, uint8_t qindex)
        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
@@ -2388,6 +2396,7 @@ static void rib_addnode(struct route_node *rn,
 void rib_unlink(struct route_node *rn, struct route_entry *re)
 {
        rib_dest_t *dest;
+       struct nhg_hash_entry *nhe = NULL;
 
        assert(rn && re);
 
@@ -2402,7 +2411,10 @@ void rib_unlink(struct route_node *rn, struct route_entry *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);
@@ -2468,9 +2480,10 @@ void _route_entry_dump(const char *func, union prefixconstptr pp,
                "%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);
 
@@ -2624,6 +2637,10 @@ int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p,
        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)
@@ -2634,6 +2651,7 @@ int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p,
        /* 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;
        }
@@ -2643,6 +2661,45 @@ int rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p,
        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);
@@ -2778,7 +2835,7 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int 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;
@@ -2791,7 +2848,7 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
                                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.
@@ -2832,7 +2889,7 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
                        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);
@@ -2888,7 +2945,7 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
                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));
@@ -2923,11 +2980,11 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
 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));
@@ -2939,9 +2996,11 @@ int rib_add(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
        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();
@@ -2951,11 +3010,66 @@ int rib_add(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
        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.
@@ -2966,49 +3080,139 @@ void rib_update_table(struct route_table *table, rib_update_event_t 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.  */
@@ -3061,7 +3265,7 @@ void rib_sweep_table(struct route_table *table)
                         * 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);
@@ -3198,6 +3402,7 @@ static int rib_process_dplane_results(struct thread *thread)
 {
        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 */
 
@@ -3217,6 +3422,21 @@ static int rib_process_dplane_results(struct thread *thread)
                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:
@@ -3239,6 +3459,12 @@ static int rib_process_dplane_results(struct thread *thread)
                                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:
@@ -3294,9 +3520,6 @@ static int rib_process_dplane_results(struct thread *thread)
 
        } while (1);
 
-       /* Check for nexthop tracking processing after finishing with results */
-       do_nht_processing();
-
        return 0;
 }