]> git.proxmox.com Git - mirror_frr.git/blobdiff - zebra/zebra_rib.c
zebra: delay default vrf name after vrf initialization
[mirror_frr.git] / zebra / zebra_rib.c
index 2192907b216798d8ac2346c22e5e99b170638bd9..828539252732166570af5970fd31135c58a703db 100644 (file)
@@ -38,6 +38,7 @@
 #include "vrf.h"
 #include "workqueue.h"
 
+#include "zebra/zebra_router.h"
 #include "zebra/connected.h"
 #include "zebra/debug.h"
 #include "zebra/interface.h"
 #include "zebra/zebra_routemap.h"
 #include "zebra/zebra_vrf.h"
 #include "zebra/zebra_vxlan.h"
+#include "zebra/zapi_msg.h"
+#include "zebra/zebra_dplane.h"
+
+/*
+ * Event, list, and mutex for delivery of dataplane results
+ */
+static pthread_mutex_t dplane_mutex;
+static struct thread *t_dplane;
+static struct dplane_ctx_q rib_dplane_q;
 
 DEFINE_HOOK(rib_update, (struct route_node * rn, const char *reason),
            (rn, reason))
@@ -348,8 +358,6 @@ static void nexthop_set_resolved(afi_t afi, const struct nexthop *newhop,
                if (newhop->ifindex) {
                        resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
                        resolved_hop->ifindex = newhop->ifindex;
-                       if (newhop->flags & NEXTHOP_FLAG_ONLINK)
-                               resolved_hop->flags |= NEXTHOP_FLAG_ONLINK;
                }
                break;
        case NEXTHOP_TYPE_IPV6:
@@ -388,6 +396,9 @@ static void nexthop_set_resolved(afi_t afi, const struct nexthop *newhop,
                break;
        }
 
+       if (newhop->flags & NEXTHOP_FLAG_ONLINK)
+               resolved_hop->flags |= NEXTHOP_FLAG_ONLINK;
+
        /* Copy labels of the resolved route */
        if (newhop->nh_label)
                nexthop_add_labels(resolved_hop, newhop->nh_label_type,
@@ -601,9 +612,20 @@ static int nexthop_active(afi_t afi, struct route_entry *re,
                                        __PRETTY_FUNCTION__);
                        return resolved;
                } else {
+                       if (IS_ZEBRA_DEBUG_RIB_DETAILED) {
+                               zlog_debug("\t%s: Route Type %s has not turned on recursion",
+                                          __PRETTY_FUNCTION__,
+                                          zebra_route_string(re->type));
+                               if (re->type == ZEBRA_ROUTE_BGP &&
+                                   !CHECK_FLAG(re->flags, ZEBRA_FLAG_IBGP))
+                                       zlog_debug("\tEBGP: see \"disable-ebgp-connected-route-check\" or \"disable-connected-check\"");
+                       }
                        return 0;
                }
        }
+       if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+               zlog_debug("\t%s: Nexthop did not lookup in table",
+                          __PRETTY_FUNCTION__);
        return 0;
 }
 
@@ -1072,75 +1094,6 @@ int zebra_rib_labeled_unicast(struct route_entry *re)
        return 1;
 }
 
-void kernel_route_rib_pass_fail(struct route_node *rn, const struct prefix *p,
-                               struct route_entry *re,
-                               enum zebra_dplane_status res)
-{
-       struct nexthop *nexthop;
-       char buf[PREFIX_STRLEN];
-       rib_dest_t *dest;
-
-       dest = rib_dest_from_rnode(rn);
-
-       switch (res) {
-       case ZEBRA_DPLANE_INSTALL_SUCCESS:
-               dest->selected_fib = re;
-               for (ALL_NEXTHOPS(re->ng, nexthop)) {
-                       if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
-                               continue;
-
-                       if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
-                               SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
-                       else
-                               UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
-               }
-               zsend_route_notify_owner(re, p, ZAPI_ROUTE_INSTALLED);
-               break;
-       case ZEBRA_DPLANE_INSTALL_FAILURE:
-               /*
-                * I am not sure this is the right thing to do here
-                * but the code always set selected_fib before
-                * this assignment was moved here.
-                */
-               dest->selected_fib = re;
-
-               zsend_route_notify_owner(re, p, ZAPI_ROUTE_FAIL_INSTALL);
-               flog_err(EC_ZEBRA_DP_INSTALL_FAIL,
-                        "%u:%s: Route install failed", re->vrf_id,
-                        prefix2str(p, buf, sizeof(buf)));
-               break;
-       case ZEBRA_DPLANE_DELETE_SUCCESS:
-               /*
-                * The case where selected_fib is not re is
-                * when we have received a system route
-                * that is overriding our installed route
-                * as such we should leave the selected_fib
-                * pointer alone
-                */
-               if (dest->selected_fib == re)
-                       dest->selected_fib = NULL;
-               for (ALL_NEXTHOPS(re->ng, nexthop))
-                       UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
-
-               zsend_route_notify_owner(re, p, ZAPI_ROUTE_REMOVED);
-               break;
-       case ZEBRA_DPLANE_DELETE_FAILURE:
-               /*
-                * Should we set this to NULL if the
-                * delete fails?
-                */
-               dest->selected_fib = NULL;
-               flog_err(EC_ZEBRA_DP_DELETE_FAIL,
-                        "%u:%s: Route Deletion failure", re->vrf_id,
-                        prefix2str(p, buf, sizeof(buf)));
-
-               zsend_route_notify_owner(re, p, ZAPI_ROUTE_REMOVE_FAIL);
-               break;
-       case ZEBRA_DPLANE_STATUS_NONE:
-               break;
-       }
-}
-
 /* Update flag indicates whether this is a "replace" or not. Currently, this
  * is only used for IPv4.
  */
@@ -1149,8 +1102,11 @@ void rib_install_kernel(struct route_node *rn, struct route_entry *re,
 {
        struct nexthop *nexthop;
        rib_table_info_t *info = srcdest_rnode_table_info(rn);
-       const struct prefix *p, *src_p;
        struct zebra_vrf *zvrf = vrf_info_lookup(re->vrf_id);
+       const struct prefix *p, *src_p;
+       enum zebra_dplane_result ret;
+
+       rib_dest_t *dest = rib_dest_from_rnode(rn);
 
        srcdest_rnode_prefixes(rn, &p, &src_p);
 
@@ -1182,24 +1138,39 @@ void rib_install_kernel(struct route_node *rn, struct route_entry *re,
        if (old && (old != re) && (old->type != re->type))
                zsend_route_notify_owner(old, p, ZAPI_ROUTE_BETTER_ADMIN_WON);
 
+       /* Update fib selection */
+       dest->selected_fib = re;
+
        /*
         * Make sure we update the FPM any time we send new information to
         * the kernel.
         */
        hook_call(rib_update, rn, "installing in kernel");
-       switch (kernel_route_rib(rn, p, src_p, old, re)) {
+
+       /* Send add or update */
+       if (old && (old != re))
+               ret = dplane_route_update(rn, re, old);
+       else
+               ret = dplane_route_add(rn, re);
+
+       switch (ret) {
        case ZEBRA_DPLANE_REQUEST_QUEUED:
-               flog_err(
-                       EC_ZEBRA_DP_INVALID_RC,
-                       "No current known DataPlane interfaces can return this, please fix");
+               if (zvrf)
+                       zvrf->installs_queued++;
                break;
        case ZEBRA_DPLANE_REQUEST_FAILURE:
-               flog_err(
-                       EC_ZEBRA_DP_INSTALL_FAIL,
-                       "No current known Rib Install Failure cases, please fix");
+       {
+               char str[SRCDEST2STR_BUFFER];
+
+               srcdest_rnode2str(rn, str, sizeof(str));
+               flog_err(EC_ZEBRA_DP_INSTALL_FAIL,
+                        "%u:%s: Failed to enqueue dataplane install",
+                        re->vrf_id, str);
                break;
+       }
        case ZEBRA_DPLANE_REQUEST_SUCCESS:
-               zvrf->installs++;
+               if (zvrf)
+                       zvrf->installs++;
                break;
        }
 
@@ -1211,11 +1182,8 @@ void rib_uninstall_kernel(struct route_node *rn, struct route_entry *re)
 {
        struct nexthop *nexthop;
        rib_table_info_t *info = srcdest_rnode_table_info(rn);
-       const struct prefix *p, *src_p;
        struct zebra_vrf *zvrf = vrf_info_lookup(re->vrf_id);
 
-       srcdest_rnode_prefixes(rn, &p, &src_p);
-
        if (info->safi != SAFI_UNICAST) {
                for (ALL_NEXTHOPS(re->ng, nexthop))
                        UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
@@ -1224,20 +1192,25 @@ void rib_uninstall_kernel(struct route_node *rn, struct route_entry *re)
 
        /*
         * Make sure we update the FPM any time we send new information to
-        * the kernel.
+        * the dataplane.
         */
        hook_call(rib_update, rn, "uninstalling from kernel");
-       switch (kernel_route_rib(rn, p, src_p, re, NULL)) {
+
+       switch (dplane_route_delete(rn, re)) {
        case ZEBRA_DPLANE_REQUEST_QUEUED:
-               flog_err(
-                       EC_ZEBRA_DP_INVALID_RC,
-                       "No current known DataPlane interfaces can return this, please fix");
+               if (zvrf)
+                       zvrf->removals_queued++;
                break;
        case ZEBRA_DPLANE_REQUEST_FAILURE:
-               flog_err(
-                       EC_ZEBRA_DP_INSTALL_FAIL,
-                       "No current known RIB Install Failure cases, please fix");
+       {
+               char str[SRCDEST2STR_BUFFER];
+
+               srcdest_rnode2str(rn, str, sizeof(str));
+               flog_err(EC_ZEBRA_DP_INSTALL_FAIL,
+                        "%u:%s: Failed to enqueue dataplane uninstall",
+                        re->vrf_id, str);
                break;
+       }
        case ZEBRA_DPLANE_REQUEST_SUCCESS:
                if (zvrf)
                        zvrf->removals++;
@@ -1252,17 +1225,23 @@ static void rib_uninstall(struct route_node *rn, struct route_entry *re)
 {
        rib_table_info_t *info = srcdest_rnode_table_info(rn);
        rib_dest_t *dest = rib_dest_from_rnode(rn);
+       struct nexthop *nexthop;
 
        if (dest && dest->selected_fib == re) {
                if (info->safi == SAFI_UNICAST)
                        hook_call(rib_update, rn, "rib_uninstall");
 
-               if (!RIB_SYSTEM_ROUTE(re))
-                       rib_uninstall_kernel(rn, re);
-
                /* If labeled-unicast route, uninstall transit LSP. */
                if (zebra_rib_labeled_unicast(re))
                        zebra_mpls_lsp_uninstall(info->zvrf, rn, re);
+
+               if (!RIB_SYSTEM_ROUTE(re))
+                       rib_uninstall_kernel(rn, re);
+
+               dest->selected_fib = NULL;
+
+               for (ALL_NEXTHOPS(re->ng, nexthop))
+                       UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
        }
 
        if (CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED)) {
@@ -1791,28 +1770,26 @@ static void rib_process(struct route_node *rn)
        else if (old_fib)
                rib_process_del_fib(zvrf, rn, old_fib);
 
-       /* Redistribute SELECTED entry */
+       /* Update SELECTED entry */
        if (old_selected != new_selected || selected_changed) {
-               struct nexthop *nexthop = NULL;
-
-               /* Check if we have a FIB route for the destination, otherwise,
-                * don't redistribute it */
-               if (new_fib) {
-                       for (ALL_NEXTHOPS(new_fib->ng, nexthop)) {
-                               if (CHECK_FLAG(nexthop->flags,
-                                              NEXTHOP_FLAG_FIB)) {
-                                       break;
-                               }
-                       }
-               }
-               if (!nexthop)
-                       new_selected = NULL;
 
                if (new_selected && new_selected != new_fib) {
                        nexthop_active_update(rn, new_selected, 1);
                        UNSET_FLAG(new_selected->status, ROUTE_ENTRY_CHANGED);
                }
 
+               if (new_selected) {
+                       SET_FLAG(new_selected->flags, ZEBRA_FLAG_SELECTED);
+
+                       /* Special case: new route is system route, so
+                        * dataplane update will not be done - ensure we
+                        * redistribute the route.
+                        */
+                       if (RIB_SYSTEM_ROUTE(new_selected))
+                               redistribute_update(p, src_p, new_selected,
+                                                   old_selected);
+               }
+
                if (old_selected) {
                        if (!new_selected)
                                redistribute_delete(p, src_p, old_selected);
@@ -1820,14 +1797,6 @@ static void rib_process(struct route_node *rn)
                                UNSET_FLAG(old_selected->flags,
                                           ZEBRA_FLAG_SELECTED);
                }
-
-               if (new_selected) {
-                       /* Install new or replace existing redistributed entry
-                        */
-                       SET_FLAG(new_selected->flags, ZEBRA_FLAG_SELECTED);
-                       redistribute_update(p, src_p, new_selected,
-                                           old_selected);
-               }
        }
 
        /* Remove all RE entries queued for removal */
@@ -1847,6 +1816,271 @@ static void rib_process(struct route_node *rn)
        rib_gc_dest(rn);
 }
 
+/*
+ * Utility to match route with dplane context data
+ */
+static bool rib_route_match_ctx(const struct route_entry *re,
+                               const struct zebra_dplane_ctx *ctx,
+                               bool is_update)
+{
+       bool result = false;
+
+       if (is_update) {
+               /*
+                * In 'update' case, we test info about the 'previous' or
+                * 'old' route
+                */
+               if ((re->type == dplane_ctx_get_old_type(ctx)) &&
+                   (re->instance == dplane_ctx_get_old_instance(ctx))) {
+                       result = true;
+
+                       /* TODO -- we're using this extra test, but it's not
+                        * exactly clear why.
+                        */
+                       if (re->type == ZEBRA_ROUTE_STATIC &&
+                           (re->distance != dplane_ctx_get_old_distance(ctx) ||
+                            re->tag != dplane_ctx_get_old_tag(ctx))) {
+                               result = false;
+                       }
+               }
+
+       } else {
+               /*
+                * Ordinary, single-route case using primary context info
+                */
+               if ((dplane_ctx_get_op(ctx) != DPLANE_OP_ROUTE_DELETE) &&
+                   CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED)) {
+                       /* Skip route that's been deleted */
+                       goto done;
+               }
+
+               if ((re->type == dplane_ctx_get_type(ctx)) &&
+                   (re->instance == dplane_ctx_get_instance(ctx))) {
+                       result = true;
+
+                       /* TODO -- we're using this extra test, but it's not
+                        * exactly clear why.
+                        */
+                       if (re->type == ZEBRA_ROUTE_STATIC &&
+                           (re->distance != dplane_ctx_get_distance(ctx) ||
+                            re->tag != dplane_ctx_get_tag(ctx))) {
+                               result = false;
+                       }
+               }
+       }
+
+done:
+
+       return (result);
+}
+
+/*
+ * Route-update results processing after async dataplane update.
+ */
+static void rib_process_after(struct zebra_dplane_ctx *ctx)
+{
+       struct route_table *table = NULL;
+       struct zebra_vrf *zvrf = NULL;
+       struct route_node *rn = NULL;
+       struct route_entry *re = NULL, *old_re = NULL, *rib;
+       bool is_update = false;
+       struct nexthop *nexthop, *ctx_nexthop;
+       char dest_str[PREFIX_STRLEN] = "";
+       enum dplane_op_e op;
+       enum zebra_dplane_result status;
+       const struct prefix *dest_pfx, *src_pfx;
+
+       /* Locate rn and re(s) from ctx */
+
+       table = zebra_vrf_table_with_table_id(dplane_ctx_get_afi(ctx),
+                                             dplane_ctx_get_safi(ctx),
+                                             dplane_ctx_get_vrf(ctx),
+                                             dplane_ctx_get_table(ctx));
+       if (table == NULL) {
+               if (IS_ZEBRA_DEBUG_DPLANE) {
+                       zlog_debug("Failed to process dplane results: no table for afi %d, safi %d, vrf %u",
+                                  dplane_ctx_get_afi(ctx),
+                                  dplane_ctx_get_safi(ctx),
+                                  dplane_ctx_get_vrf(ctx));
+               }
+               goto done;
+       }
+
+       zvrf = vrf_info_lookup(dplane_ctx_get_vrf(ctx));
+
+       dest_pfx = dplane_ctx_get_dest(ctx);
+
+       /* Note well: only capturing the prefix string if debug is enabled here;
+        * unconditional log messages will have to generate the string.
+        */
+       if (IS_ZEBRA_DEBUG_DPLANE)
+               prefix2str(dest_pfx, dest_str, sizeof(dest_str));
+
+       src_pfx = dplane_ctx_get_src(ctx);
+       rn = srcdest_rnode_get(table, dplane_ctx_get_dest(ctx),
+                              src_pfx ? (struct prefix_ipv6 *)src_pfx : NULL);
+       if (rn == NULL) {
+               if (IS_ZEBRA_DEBUG_DPLANE) {
+                       zlog_debug("Failed to process dplane results: no route for %u:%s",
+                                  dplane_ctx_get_vrf(ctx), dest_str);
+               }
+               goto done;
+       }
+
+       srcdest_rnode_prefixes(rn, &dest_pfx, &src_pfx);
+
+       op = dplane_ctx_get_op(ctx);
+       status = dplane_ctx_get_status(ctx);
+
+       if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
+               zlog_debug("%u:%s Processing dplane ctx %p, op %s result %s",
+                          dplane_ctx_get_vrf(ctx), dest_str, ctx,
+                          dplane_op2str(op), dplane_res2str(status));
+       }
+
+       if (op == DPLANE_OP_ROUTE_DELETE) {
+               /*
+                * In the delete case, the zebra core datastructs were
+                * updated (or removed) at the time the delete was issued,
+                * so we're just notifying the route owner.
+                */
+               if (status == ZEBRA_DPLANE_REQUEST_SUCCESS) {
+                       zsend_route_notify_owner_ctx(ctx, ZAPI_ROUTE_REMOVED);
+
+                       if (zvrf)
+                               zvrf->removals++;
+               } else {
+                       zsend_route_notify_owner_ctx(ctx,
+                                                    ZAPI_ROUTE_FAIL_INSTALL);
+
+                       zlog_warn("%u:%s: Route Deletion failure",
+                                 dplane_ctx_get_vrf(ctx),
+                                 prefix2str(dest_pfx,
+                                            dest_str, sizeof(dest_str)));
+               }
+
+               /* Nothing more to do in delete case */
+               goto done;
+       }
+
+       /*
+        * Update is a bit of a special case, where we may have both old and new
+        * routes to post-process.
+        */
+       is_update = dplane_ctx_is_update(ctx);
+
+       /*
+        * Take a pass through the routes, look for matches with the context
+        * info.
+        */
+       RNODE_FOREACH_RE(rn, rib) {
+
+               if (re == NULL) {
+                       if (rib_route_match_ctx(rib, ctx, false))
+                               re = rib;
+               }
+
+               /* Check for old route match */
+               if (is_update && (old_re == NULL)) {
+                       if (rib_route_match_ctx(rib, ctx, true /*is_update*/))
+                               old_re = rib;
+               }
+
+               /* Have we found the routes we need to work on? */
+               if (re && ((!is_update || old_re)))
+                       break;
+       }
+
+       /*
+        * Check sequence number(s) to detect stale results before continuing
+        */
+       if (re && (re->dplane_sequence != dplane_ctx_get_seq(ctx))) {
+               if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
+                       zlog_debug("%u:%s Stale dplane result for re %p",
+                                  dplane_ctx_get_vrf(ctx), dest_str, re);
+               }
+               re = NULL;
+       }
+
+       if (old_re &&
+           (old_re->dplane_sequence != dplane_ctx_get_old_seq(ctx))) {
+               if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
+                       zlog_debug("%u:%s Stale dplane result for old_re %p",
+                                  dplane_ctx_get_vrf(ctx), dest_str, old_re);
+               }
+               old_re = NULL;
+       }
+
+       /*
+        * Here's sort of a tough one: the route update result is stale.
+        * Is it better to use the context block info to generate
+        * redist and owner notification, or is it better to wait
+        * for the up-to-date result to arrive?
+        */
+       if (re == NULL) {
+               /* TODO -- for now, only expose up-to-date results */
+               goto done;
+       }
+
+       if (status == ZEBRA_DPLANE_REQUEST_SUCCESS) {
+               /* Update zebra nexthop FIB flag for each
+                * nexthop that was installed.
+                */
+               for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), ctx_nexthop)) {
+
+                       for (ALL_NEXTHOPS(re->ng, nexthop)) {
+                               if (nexthop_same(ctx_nexthop, nexthop))
+                                       break;
+                       }
+
+                       if (nexthop == NULL)
+                               continue;
+
+                       if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
+                               continue;
+
+                       if (CHECK_FLAG(ctx_nexthop->flags,
+                                      NEXTHOP_FLAG_FIB))
+                               SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
+                       else
+                               UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
+               }
+
+               if (zvrf) {
+                       zvrf->installs++;
+                       /* Set flag for nexthop tracking processing */
+                       zvrf->flags |= ZEBRA_VRF_RIB_SCHEDULED;
+               }
+
+               /* Redistribute */
+               /* TODO -- still calling the redist api using the route_entries,
+                * and there's a corner-case here: if there's no client
+                * for the 'new' route, a redist deleting the 'old' route
+                * will be sent. But if the 'old' context info was stale,
+                * 'old_re' will be NULL here and that delete will not be sent.
+                */
+               redistribute_update(dest_pfx, src_pfx, re, old_re);
+
+               /* Notify route owner */
+               zsend_route_notify_owner(re,
+                                        dest_pfx, ZAPI_ROUTE_INSTALLED);
+
+       } else {
+               zsend_route_notify_owner(re, dest_pfx,
+                                        ZAPI_ROUTE_FAIL_INSTALL);
+
+               zlog_warn("%u:%s: Route install failed",
+                         dplane_ctx_get_vrf(ctx),
+                         prefix2str(dest_pfx,
+                                    dest_str, sizeof(dest_str)));
+       }
+
+done:
+
+       /* Return context to dataplane module */
+       dplane_ctx_fini(&ctx);
+}
+
 /* Take a list of route_node structs and return 1, if there was a record
  * picked from it and processed by rib_process(). Don't process more,
  * than one RN record; operate only in the specified sub-queue.
@@ -1893,9 +2127,9 @@ static unsigned int process_subq(struct list *subq, uint8_t qindex)
 }
 
 /*
- * All meta queues have been processed. Trigger next-hop evaluation.
+ * Perform next-hop tracking processing after RIB updates.
  */
-static void meta_queue_process_complete(struct work_queue *dummy)
+static void do_nht_processing(void)
 {
        struct vrf *vrf;
        struct zebra_vrf *zvrf;
@@ -1910,6 +2144,10 @@ static void meta_queue_process_complete(struct work_queue *dummy)
                if (zvrf == NULL || !(zvrf->flags & ZEBRA_VRF_RIB_SCHEDULED))
                        continue;
 
+               if (IS_ZEBRA_DEBUG_RIB_DETAILED || IS_ZEBRA_DEBUG_NHT)
+                       zlog_debug("NHT processing check for zvrf %s",
+                                  zvrf_name(zvrf));
+
                zvrf->flags &= ~ZEBRA_VRF_RIB_SCHEDULED;
                zebra_evaluate_rnh(zvrf, AF_INET, 0, RNH_NEXTHOP_TYPE, NULL);
                zebra_evaluate_rnh(zvrf, AF_INET, 0, RNH_IMPORT_CHECK_TYPE,
@@ -1931,6 +2169,14 @@ static void meta_queue_process_complete(struct work_queue *dummy)
        }
 }
 
+/*
+ * All meta queues have been processed. Trigger next-hop evaluation.
+ */
+static void meta_queue_process_complete(struct work_queue *dummy)
+{
+       do_nht_processing();
+}
+
 /* 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
@@ -1940,6 +2186,22 @@ static wq_item_status meta_queue_process(struct work_queue *dummy, void *data)
 {
        struct meta_queue *mq = data;
        unsigned i;
+       uint32_t queue_len, queue_limit;
+
+       /* Ensure there's room for more dataplane updates */
+       queue_limit = dplane_get_in_queue_limit();
+       queue_len = dplane_get_in_queue_len();
+       if (queue_len > queue_limit) {
+               if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+                       zlog_debug("rib queue: dplane queue len %u, limit %u, retrying",
+                                  queue_len, queue_limit);
+
+               /* Ensure that the meta-queue is actually enqueued */
+               if (work_queue_empty(zebrad.ribq))
+                       work_queue_add(zebrad.ribq, zebrad.mq);
+
+               return WQ_QUEUE_BLOCKED;
+       }
 
        for (i = 0; i < MQ_SIZE; i++)
                if (process_subq(mq->subq[i], i)) {
@@ -2919,7 +3181,7 @@ void rib_sweep_route(void)
                rib_sweep_table(zvrf->table[AFI_IP6][SAFI_UNICAST]);
        }
 
-       zebra_ns_sweep_route();
+       zebra_router_sweep_route();
 }
 
 /* Remove specific by protocol routes from 'table'. */
@@ -2961,7 +3223,7 @@ unsigned long rib_score_proto(uint8_t proto, unsigned short instance)
                                         proto, instance,
                                         zvrf->table[AFI_IP6][SAFI_UNICAST]);
 
-       cnt += zebra_ns_score_proto(proto, instance);
+       cnt += zebra_router_score_proto(proto, instance);
 
        return cnt;
 }
@@ -2985,16 +3247,75 @@ void rib_close_table(struct route_table *table)
                        if (info->safi == SAFI_UNICAST)
                                hook_call(rib_update, rn, NULL);
 
-                       if (!RIB_SYSTEM_ROUTE(dest->selected_fib))
+                       if (!RIB_SYSTEM_ROUTE(dest->selected_fib)) {
                                rib_uninstall_kernel(rn, dest->selected_fib);
+                               dest->selected_fib = NULL;
+                       }
                }
        }
 }
 
+/*
+ *
+ */
+static int rib_process_dplane_results(struct thread *thread)
+{
+       struct zebra_dplane_ctx *ctx;
+
+       do {
+               /* Take lock controlling queue of results */
+               pthread_mutex_lock(&dplane_mutex);
+               {
+                       /* Dequeue context block */
+                       dplane_ctx_dequeue(&rib_dplane_q, &ctx);
+               }
+               pthread_mutex_unlock(&dplane_mutex);
+
+               if (ctx)
+                       rib_process_after(ctx);
+               else
+                       break;
+
+       } while (1);
+
+       /* Check for nexthop tracking processing after finishing with results */
+       do_nht_processing();
+
+       return 0;
+}
+
+/*
+ * Results are returned from the dataplane subsystem, in the context of
+ * the dataplane pthread. We enqueue the results here for processing by
+ * the main thread later.
+ */
+static int rib_dplane_results(const struct zebra_dplane_ctx *ctx)
+{
+       /* Take lock controlling queue of results */
+       pthread_mutex_lock(&dplane_mutex);
+       {
+               /* Enqueue context block */
+               dplane_ctx_enqueue_tail(&rib_dplane_q, ctx);
+       }
+       pthread_mutex_unlock(&dplane_mutex);
+
+       /* Ensure event is signalled to zebra main thread */
+       thread_add_event(zebrad.master, rib_process_dplane_results, NULL, 0,
+                        &t_dplane);
+
+       return 0;
+}
+
 /* Routing information base initialize. */
 void rib_init(void)
 {
        rib_queue_init(&zebrad);
+
+       /* Init dataplane, and register for results */
+       pthread_mutex_init(&dplane_mutex, NULL);
+       TAILQ_INIT(&rib_dplane_q);
+       zebra_dplane_init();
+       dplane_results_register(rib_dplane_results);
 }
 
 /*