]> git.proxmox.com Git - mirror_frr.git/blobdiff - zebra/zebra_dplane.c
Merge pull request #7352 from mjstapp/fix_rt_netlink_indent
[mirror_frr.git] / zebra / zebra_dplane.c
index 5dcf76db152cb349d316b85d2e5e51f1584ffe5c..d3bcffe960e158bf4a0ff40247a5e23d3c5b8331 100644 (file)
@@ -36,6 +36,7 @@
 #include "zebra/rt.h"
 #include "zebra/debug.h"
 #include "zebra/zebra_pbr.h"
+#include "printfrr.h"
 
 /* Memory type for context blocks */
 DEFINE_MTYPE_STATIC(ZEBRA, DP_CTX, "Zebra DPlane Ctx")
@@ -73,6 +74,7 @@ const uint32_t DPLANE_DEFAULT_NEW_WORK = 100;
  */
 struct dplane_nexthop_info {
        uint32_t id;
+       uint32_t old_id;
        afi_t afi;
        vrf_id_t vrf_id;
        int type;
@@ -147,6 +149,17 @@ struct dplane_pw_info {
        union pw_protocol_fields fields;
 };
 
+/*
+ * Bridge port info for the dataplane
+ */
+struct dplane_br_port_info {
+       uint32_t sph_filter_cnt;
+       struct in_addr sph_filters[ES_VTEP_MAX_CNT];
+       /* DPLANE_BR_PORT_XXX - see zebra_dplane.h*/
+       uint32_t flags;
+       uint32_t backup_nhg_id;
+};
+
 /*
  * Interface/prefix info for the dataplane
  */
@@ -185,7 +198,7 @@ struct dplane_mac_info {
 };
 
 /*
- * EVPN neighbor info for the dataplane
+ * Neighbor info for the dataplane
  */
 struct dplane_neigh_info {
        struct ipaddr ip_addr;
@@ -210,6 +223,7 @@ struct dplane_ctx_rule {
        uint8_t dsfield;
        struct prefix src_ip;
        struct prefix dst_ip;
+       char ifname[INTERFACE_NAMSIZ + 1];
 };
 
 struct dplane_rule_info {
@@ -270,6 +284,7 @@ struct zebra_dplane_ctx {
                struct dplane_route_info rinfo;
                zebra_lsp_t lsp;
                struct dplane_pw_info pw;
+               struct dplane_br_port_info br_port;
                struct dplane_intf_info intf;
                struct dplane_mac_info macinfo;
                struct dplane_neigh_info neigh;
@@ -388,6 +403,9 @@ static struct zebra_dplane_globals {
        _Atomic uint32_t dg_pws_in;
        _Atomic uint32_t dg_pw_errors;
 
+       _Atomic uint32_t dg_br_port_in;
+       _Atomic uint32_t dg_br_port_errors;
+
        _Atomic uint32_t dg_intf_addrs_in;
        _Atomic uint32_t dg_intf_addr_errors;
 
@@ -607,6 +625,8 @@ static void dplane_ctx_free_internal(struct zebra_dplane_ctx *ctx)
        case DPLANE_OP_RULE_ADD:
        case DPLANE_OP_RULE_DELETE:
        case DPLANE_OP_RULE_UPDATE:
+       case DPLANE_OP_NEIGH_DISCOVER:
+       case DPLANE_OP_BR_PORT_UPDATE:
        case DPLANE_OP_NONE:
                break;
        }
@@ -800,6 +820,10 @@ const char *dplane_op2str(enum dplane_op_e op)
                ret = "SYS_ROUTE_DEL";
                break;
 
+       case DPLANE_OP_BR_PORT_UPDATE:
+               ret = "BR_PORT_UPDATE";
+               break;
+
        case DPLANE_OP_ADDR_INSTALL:
                ret = "ADDR_INSTALL";
                break;
@@ -839,6 +863,10 @@ const char *dplane_op2str(enum dplane_op_e op)
        case DPLANE_OP_RULE_UPDATE:
                ret = "RULE_UPDATE";
                break;
+
+       case DPLANE_OP_NEIGH_DISCOVER:
+               ret = "NEIGH_DISCOVER";
+               break;
        }
 
        return ret;
@@ -858,8 +886,6 @@ const char *dplane_res2str(enum zebra_dplane_result res)
        case ZEBRA_DPLANE_REQUEST_SUCCESS:
                ret = "SUCCESS";
                break;
-       case ZEBRA_DPLANE_REQUEST_PENDING:
-               ret = "PENDING";
        }
 
        return ret;
@@ -1238,6 +1264,12 @@ uint32_t dplane_ctx_get_nhe_id(const struct zebra_dplane_ctx *ctx)
        return ctx->u.rinfo.nhe.id;
 }
 
+uint32_t dplane_ctx_get_old_nhe_id(const struct zebra_dplane_ctx *ctx)
+{
+       DPLANE_CTX_VALID(ctx);
+       return ctx->u.rinfo.nhe.old_id;
+}
+
 afi_t dplane_ctx_get_nhe_afi(const struct zebra_dplane_ctx *ctx)
 {
        DPLANE_CTX_VALID(ctx);
@@ -1629,6 +1661,13 @@ int dplane_ctx_rule_get_sock(const struct zebra_dplane_ctx *ctx)
        return ctx->u.rule.sock;
 }
 
+const char *dplane_ctx_rule_get_ifname(const struct zebra_dplane_ctx *ctx)
+{
+       DPLANE_CTX_VALID(ctx);
+
+       return ctx->u.rule.new.ifname;
+}
+
 int dplane_ctx_rule_get_unique(const struct zebra_dplane_ctx *ctx)
 {
        DPLANE_CTX_VALID(ctx);
@@ -1745,6 +1784,37 @@ dplane_ctx_rule_get_old_dst_ip(const struct zebra_dplane_ctx *ctx)
        return &(ctx->u.rule.old.dst_ip);
 }
 
+uint32_t dplane_ctx_get_br_port_flags(const struct zebra_dplane_ctx *ctx)
+{
+       DPLANE_CTX_VALID(ctx);
+
+       return ctx->u.br_port.flags;
+}
+
+uint32_t
+dplane_ctx_get_br_port_sph_filter_cnt(const struct zebra_dplane_ctx *ctx)
+{
+       DPLANE_CTX_VALID(ctx);
+
+       return ctx->u.br_port.sph_filter_cnt;
+}
+
+const struct in_addr *
+dplane_ctx_get_br_port_sph_filters(const struct zebra_dplane_ctx *ctx)
+{
+       DPLANE_CTX_VALID(ctx);
+
+       return ctx->u.br_port.sph_filters;
+}
+
+uint32_t
+dplane_ctx_get_br_port_backup_nhg_id(const struct zebra_dplane_ctx *ctx)
+{
+       DPLANE_CTX_VALID(ctx);
+
+       return ctx->u.br_port.backup_nhg_id;
+}
+
 /*
  * End of dplane context accessors
  */
@@ -1901,6 +1971,7 @@ int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,
                struct nhg_hash_entry *nhe = zebra_nhg_resolve(re->nhe);
 
                ctx->u.rinfo.nhe.id = nhe->id;
+               ctx->u.rinfo.nhe.old_id = 0;
                /*
                 * Check if the nhe is installed/queued before doing anything
                 * with this route.
@@ -1908,10 +1979,11 @@ int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,
                 * If its a delete we only use the prefix anyway, so this only
                 * matters for INSTALL/UPDATE.
                 */
-               if (((op == DPLANE_OP_ROUTE_INSTALL)
-                    || (op == DPLANE_OP_ROUTE_UPDATE))
-                   && !CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED)
-                   && !CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_QUEUED)) {
+               if (zebra_nhg_kernel_nexthops_enabled()
+                   && (((op == DPLANE_OP_ROUTE_INSTALL)
+                        || (op == DPLANE_OP_ROUTE_UPDATE))
+                       && !CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED)
+                       && !CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_QUEUED))) {
                        ret = ENOENT;
                        goto done;
                }
@@ -1979,6 +2051,7 @@ int dplane_ctx_nexthop_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,
         * it probably won't require two messages
         */
        dplane_ctx_ns_init(ctx, zns, (op == DPLANE_OP_NH_UPDATE));
+       ctx->zd_is_update = (op == DPLANE_OP_NH_UPDATE);
 
        ret = AOK;
 
@@ -2001,6 +2074,7 @@ int dplane_ctx_lsp_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,
        /* Capture namespace info */
        dplane_ctx_ns_init(ctx, zebra_ns_lookup(NS_DEFAULT),
                           (op == DPLANE_OP_LSP_UPDATE));
+       ctx->zd_is_update = (op == DPLANE_OP_LSP_UPDATE);
 
        memset(&ctx->u.lsp, 0, sizeof(ctx->u.lsp));
 
@@ -2185,6 +2259,7 @@ static void dplane_ctx_rule_init_single(struct dplane_ctx_rule *dplane_rule,
        dplane_rule->dsfield = rule->rule.filter.dsfield;
        prefix_copy(&(dplane_rule->dst_ip), &rule->rule.filter.dst_ip);
        prefix_copy(&(dplane_rule->src_ip), &rule->rule.filter.src_ip);
+       strlcpy(dplane_rule->ifname, rule->ifname, INTERFACE_NAMSIZ);
 }
 
 /**
@@ -2201,31 +2276,24 @@ static int dplane_ctx_rule_init(struct zebra_dplane_ctx *ctx,
                                struct zebra_pbr_rule *new_rule,
                                struct zebra_pbr_rule *old_rule)
 {
-       if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
-               char buf1[PREFIX_STRLEN];
-               char buf2[PREFIX_STRLEN];
-
+       if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
                zlog_debug(
-                       "init dplane ctx %s: IF %s(%u) Prio %u Fwmark %u Src %s Dst %s Table %u",
+                       "init dplane ctx %s: IF %s Prio %u Fwmark %u Src %pFX Dst %pFX Table %u",
                        dplane_op2str(op), new_rule->ifname,
-                       new_rule->rule.ifindex, new_rule->rule.priority,
-                       new_rule->rule.filter.fwmark,
-                       prefix2str(&new_rule->rule.filter.src_ip, buf1,
-                                  sizeof(buf1)),
-                       prefix2str(&new_rule->rule.filter.dst_ip, buf2,
-                                  sizeof(buf2)),
+                       new_rule->rule.priority, new_rule->rule.filter.fwmark,
+                       &new_rule->rule.filter.src_ip,
+                       &new_rule->rule.filter.dst_ip,
                        new_rule->rule.action.table);
-       }
 
        ctx->zd_op = op;
        ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS;
 
        dplane_ctx_ns_init(ctx, zebra_ns_lookup(NS_DEFAULT),
                           op == DPLANE_OP_RULE_UPDATE);
+       ctx->zd_is_update = (op == DPLANE_OP_RULE_UPDATE);
 
        ctx->zd_vrf_id = new_rule->vrf_id;
        memcpy(ctx->zd_ifname, new_rule->ifname, sizeof(new_rule->ifname));
-       ctx->zd_ifindex = new_rule->rule.ifindex;
 
        ctx->u.rule.sock = new_rule->sock;
        ctx->u.rule.unique = new_rule->rule.unique;
@@ -2314,6 +2382,7 @@ dplane_route_update_internal(struct route_node *rn,
                        ctx->u.rinfo.zd_old_instance = old_re->instance;
                        ctx->u.rinfo.zd_old_distance = old_re->distance;
                        ctx->u.rinfo.zd_old_metric = old_re->metric;
+                       ctx->u.rinfo.nhe.old_id = old_re->nhe->id;
 
 #ifndef HAVE_NETLINK
                        /* For bsd, capture previous re's nexthops too, sigh.
@@ -2335,6 +2404,40 @@ dplane_route_update_internal(struct route_node *rn,
 #endif /* !HAVE_NETLINK */
                }
 
+               /*
+                * If the old and new context type, and nexthop group id
+                * are the same there is no need to send down a route replace
+                * as that we know we have sent a nexthop group replace
+                * or an upper level protocol has sent us the exact
+                * same route again.
+                */
+               if ((dplane_ctx_get_type(ctx) == dplane_ctx_get_old_type(ctx))
+                   && (dplane_ctx_get_nhe_id(ctx)
+                       == dplane_ctx_get_old_nhe_id(ctx))
+                   && (dplane_ctx_get_nhe_id(ctx) >= ZEBRA_NHG_PROTO_LOWER)) {
+                       struct nexthop *nexthop;
+
+                       if (IS_ZEBRA_DEBUG_DPLANE)
+                               zlog_debug(
+                                       "%s: Ignoring Route exactly the same",
+                                       __func__);
+
+                       for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx),
+                                             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);
+                       }
+
+                       dplane_ctx_free(&ctx);
+                       return ZEBRA_DPLANE_REQUEST_SUCCESS;
+               }
+
                /* Enqueue context for processing */
                ret = dplane_update_enqueue(ctx);
        }
@@ -2346,11 +2449,8 @@ dplane_route_update_internal(struct route_node *rn,
        if (ret == AOK)
                result = ZEBRA_DPLANE_REQUEST_QUEUED;
        else {
-               if (ret == ENOENT)
-                       result = ZEBRA_DPLANE_REQUEST_SUCCESS;
-               else
-                       atomic_fetch_add_explicit(&zdplane_info.dg_route_errors,
-                                                 1, memory_order_relaxed);
+               atomic_fetch_add_explicit(&zdplane_info.dg_route_errors, 1,
+                                         memory_order_relaxed);
                if (ctx)
                        dplane_ctx_free(&ctx);
        }
@@ -2790,6 +2890,80 @@ done:
        return result;
 }
 
+/*
+ * Enqueue access br_port update.
+ */
+enum zebra_dplane_result
+dplane_br_port_update(const struct interface *ifp, bool non_df,
+                     uint32_t sph_filter_cnt,
+                     const struct in_addr *sph_filters, uint32_t backup_nhg_id)
+{
+       enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE;
+       uint32_t flags = 0;
+       int ret;
+       struct zebra_dplane_ctx *ctx = NULL;
+       struct zebra_ns *zns;
+       enum dplane_op_e op = DPLANE_OP_BR_PORT_UPDATE;
+
+       if (non_df)
+               flags |= DPLANE_BR_PORT_NON_DF;
+
+       if (IS_ZEBRA_DEBUG_DPLANE_DETAIL || IS_ZEBRA_DEBUG_EVPN_MH_ES) {
+               uint32_t i;
+               char vtep_str[ES_VTEP_LIST_STR_SZ];
+
+               vtep_str[0] = '\0';
+               for (i = 0; i < sph_filter_cnt; ++i) {
+                       snprintfrr(vtep_str + strlen(vtep_str),
+                                  sizeof(vtep_str) - strlen(vtep_str), "%pI4 ",
+                                  &sph_filters[i]);
+               }
+               zlog_debug(
+                       "init br_port ctx %s: ifp %s, flags 0x%x backup_nhg 0x%x sph %s",
+                       dplane_op2str(op), ifp->name, flags, backup_nhg_id,
+                       vtep_str);
+       }
+
+       ctx = dplane_ctx_alloc();
+
+       ctx->zd_op = op;
+       ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS;
+       ctx->zd_vrf_id = ifp->vrf_id;
+
+       zns = zebra_ns_lookup(ifp->vrf_id);
+       dplane_ctx_ns_init(ctx, zns, false);
+
+       ctx->zd_ifindex = ifp->ifindex;
+       strlcpy(ctx->zd_ifname, ifp->name, sizeof(ctx->zd_ifname));
+
+       /* Init the br-port-specific data area */
+       memset(&ctx->u.br_port, 0, sizeof(ctx->u.br_port));
+
+       ctx->u.br_port.flags = flags;
+       ctx->u.br_port.backup_nhg_id = backup_nhg_id;
+       ctx->u.br_port.sph_filter_cnt = sph_filter_cnt;
+       memcpy(ctx->u.br_port.sph_filters, sph_filters,
+              sizeof(ctx->u.br_port.sph_filters[0]) * sph_filter_cnt);
+
+       /* Enqueue for processing on the dplane pthread */
+       ret = dplane_update_enqueue(ctx);
+
+       /* Increment counter */
+       atomic_fetch_add_explicit(&zdplane_info.dg_br_port_in, 1,
+                                 memory_order_relaxed);
+
+       if (ret == AOK) {
+               result = ZEBRA_DPLANE_REQUEST_QUEUED;
+       } else {
+               /* Error counter */
+               atomic_fetch_add_explicit(&zdplane_info.dg_br_port_errors, 1,
+                                         memory_order_relaxed);
+               dplane_ctx_free(&ctx);
+       }
+
+       return result;
+}
+
 /*
  * Enqueue interface address add for the dataplane.
  */
@@ -2841,15 +3015,10 @@ static enum zebra_dplane_result intf_addr_update_internal(
        struct zebra_dplane_ctx *ctx = NULL;
        struct zebra_ns *zns;
 
-       if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
-               char addr_str[PREFIX_STRLEN];
-
-               prefix2str(ifc->address, addr_str, sizeof(addr_str));
-
-               zlog_debug("init intf ctx %s: idx %d, addr %u:%s",
+       if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
+               zlog_debug("init intf ctx %s: idx %d, addr %u:%pFX",
                           dplane_op2str(op), ifp->ifindex, ifp->vrf_id,
-                          addr_str);
-       }
+                          ifc->address);
 
        ctx = dplane_ctx_alloc();
 
@@ -3137,25 +3306,6 @@ enum zebra_dplane_result dplane_local_neigh_add(const struct interface *ifp,
        return result;
 }
 
-/*
- * Enqueue evpn neighbor update for the dataplane.
- */
-enum zebra_dplane_result dplane_rem_neigh_update(const struct interface *ifp,
-                                            const struct ipaddr *ip,
-                                            const struct ethaddr *mac)
-{
-       enum zebra_dplane_result result;
-       uint32_t update_flags = 0;
-
-       update_flags |= DPLANE_NEIGH_REMOTE;
-
-       result = neigh_update_internal(DPLANE_OP_NEIGH_UPDATE,
-                                      ifp, mac, ip, 0, DPLANE_NUD_PROBE,
-                                      update_flags);
-
-       return result;
-}
-
 /*
  * Enqueue evpn neighbor delete for the dataplane.
  */
@@ -3185,8 +3335,8 @@ enum zebra_dplane_result dplane_vtep_add(const struct interface *ifp,
        struct ipaddr addr;
 
        if (IS_ZEBRA_DEBUG_VXLAN)
-               zlog_debug("Install %s into flood list for VNI %u intf %s(%u)",
-                          inet_ntoa(*ip), vni, ifp->name, ifp->ifindex);
+               zlog_debug("Install %pI4 into flood list for VNI %u intf %s(%u)",
+                          ip, vni, ifp->name, ifp->ifindex);
 
        SET_IPADDR_V4(&addr);
        addr.ipaddr_v4 = *ip;
@@ -3210,8 +3360,8 @@ enum zebra_dplane_result dplane_vtep_delete(const struct interface *ifp,
 
        if (IS_ZEBRA_DEBUG_VXLAN)
                zlog_debug(
-                       "Uninstall %s from flood list for VNI %u intf %s(%u)",
-                       inet_ntoa(*ip), vni, ifp->name, ifp->ifindex);
+                       "Uninstall %pI4 from flood list for VNI %u intf %s(%u)",
+                       ip, vni, ifp->name, ifp->ifindex);
 
        SET_IPADDR_V4(&addr);
        addr.ipaddr_v4 = *ip;
@@ -3222,8 +3372,19 @@ enum zebra_dplane_result dplane_vtep_delete(const struct interface *ifp,
        return result;
 }
 
+enum zebra_dplane_result dplane_neigh_discover(const struct interface *ifp,
+                                              const struct ipaddr *ip)
+{
+       enum zebra_dplane_result result;
+
+       result = neigh_update_internal(DPLANE_OP_NEIGH_DISCOVER, ifp, NULL, ip,
+                                      DPLANE_NTF_USE, DPLANE_NUD_INCOMPLETE, 0);
+
+       return result;
+}
+
 /*
- * Common helper api for evpn neighbor updates
+ * Common helper api for neighbor updates
  */
 static enum zebra_dplane_result
 neigh_update_internal(enum dplane_op_e op,
@@ -3242,9 +3403,8 @@ neigh_update_internal(enum dplane_op_e op,
                char buf1[ETHER_ADDR_STRLEN], buf2[PREFIX_STRLEN];
 
                zlog_debug("init neigh ctx %s: ifp %s, mac %s, ip %s",
-                          dplane_op2str(op),
+                          dplane_op2str(op), ifp->name,
                           prefix_mac2str(mac, buf1, sizeof(buf1)),
-                          ifp->name,
                           ipaddr2str(ip, buf2, sizeof(buf2)));
        }
 
@@ -3416,6 +3576,13 @@ int dplane_show_helper(struct vty *vty, bool detailed)
        vty_out(vty, "Rule updates:             %" PRIu64 "\n", incoming);
        vty_out(vty, "Rule errors:              %" PRIu64 "\n", errs);
 
+       incoming = atomic_load_explicit(&zdplane_info.dg_br_port_in,
+                                       memory_order_relaxed);
+       errs = atomic_load_explicit(&zdplane_info.dg_br_port_errors,
+                                   memory_order_relaxed);
+       vty_out(vty, "Bridge port updates:      %" PRIu64 "\n", incoming);
+       vty_out(vty, "Bridge port errors:       %" PRIu64 "\n", errs);
+
        return CMD_SUCCESS;
 }
 
@@ -3445,7 +3612,9 @@ int dplane_show_provs_helper(struct vty *vty, bool detailed)
                out_max = atomic_load_explicit(&prov->dp_out_max,
                                               memory_order_relaxed);
 
-               vty_out(vty, "%s (%u): in: %"PRIu64", q_max: %"PRIu64", out: %"PRIu64", q_max: %"PRIu64"\n",
+               vty_out(vty,
+                       "%s (%u): in: %" PRIu64 ", q_max: %" PRIu64
+                       ", out: %" PRIu64 ", q_max: %" PRIu64 "\n",
                        prov->dp_name, prov->dp_id, in, in_max, out, out_max);
 
                DPLANE_LOCK();
@@ -3717,149 +3886,98 @@ void dplane_provider_enqueue_to_zebra(struct zebra_dplane_ctx *ctx)
  * Kernel dataplane provider
  */
 
-/*
- * Handler for kernel LSP updates
- */
-static enum zebra_dplane_result
-kernel_dplane_lsp_update(struct zebra_dplane_ctx *ctx)
-{
-       return kernel_lsp_update(ctx);
-}
-
-/*
- * Handler for kernel pseudowire updates
- */
-static enum zebra_dplane_result
-kernel_dplane_pw_update(struct zebra_dplane_ctx *ctx)
+static void kernel_dplane_log_detail(struct zebra_dplane_ctx *ctx)
 {
-       if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
-               zlog_debug("Dplane pw %s: op %s af %d loc: %u rem: %u",
-                          dplane_ctx_get_ifname(ctx),
-                          dplane_op2str(ctx->zd_op),
-                          dplane_ctx_get_pw_af(ctx),
-                          dplane_ctx_get_pw_local_label(ctx),
-                          dplane_ctx_get_pw_remote_label(ctx));
+       char buf[PREFIX_STRLEN];
 
-       return kernel_pw_update(ctx);
-}
-
-/*
- * Handler for kernel route updates
- */
-static enum zebra_dplane_result
-kernel_dplane_route_update(struct zebra_dplane_ctx *ctx)
-{
-       if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
-               char dest_str[PREFIX_STRLEN];
-
-               prefix2str(dplane_ctx_get_dest(ctx),
-                          dest_str, sizeof(dest_str));
+       switch (dplane_ctx_get_op(ctx)) {
 
-               zlog_debug("%u:%s Dplane route update ctx %p op %s",
-                          dplane_ctx_get_vrf(ctx), dest_str,
+       case DPLANE_OP_ROUTE_INSTALL:
+       case DPLANE_OP_ROUTE_UPDATE:
+       case DPLANE_OP_ROUTE_DELETE:
+               zlog_debug("%u:%pFX Dplane route update ctx %p op %s",
+                          dplane_ctx_get_vrf(ctx), dplane_ctx_get_dest(ctx),
                           ctx, dplane_op2str(dplane_ctx_get_op(ctx)));
-       }
-
-       return kernel_route_update(ctx);
-}
-
-/*
- * Handler for kernel-facing interface address updates
- */
-static enum zebra_dplane_result
-kernel_dplane_address_update(struct zebra_dplane_ctx *ctx)
-{
-       if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
-               char dest_str[PREFIX_STRLEN];
-
-               prefix2str(dplane_ctx_get_intf_addr(ctx), dest_str,
-                          sizeof(dest_str));
-
-               zlog_debug("Dplane intf %s, idx %u, addr %s",
-                          dplane_op2str(dplane_ctx_get_op(ctx)),
-                          dplane_ctx_get_ifindex(ctx), dest_str);
-       }
-
-       return kernel_address_update_ctx(ctx);
-}
+               break;
 
-/**
- * kernel_dplane_nexthop_update() - Handler for kernel nexthop updates
- *
- * @ctx:       Dataplane context
- *
- * Return:     Dataplane result flag
- */
-static enum zebra_dplane_result
-kernel_dplane_nexthop_update(struct zebra_dplane_ctx *ctx)
-{
-       if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
+       case DPLANE_OP_NH_INSTALL:
+       case DPLANE_OP_NH_UPDATE:
+       case DPLANE_OP_NH_DELETE:
                zlog_debug("ID (%u) Dplane nexthop update ctx %p op %s",
                           dplane_ctx_get_nhe_id(ctx), ctx,
                           dplane_op2str(dplane_ctx_get_op(ctx)));
-       }
+               break;
 
-       return kernel_nexthop_update(ctx);
-}
+       case DPLANE_OP_LSP_INSTALL:
+       case DPLANE_OP_LSP_UPDATE:
+       case DPLANE_OP_LSP_DELETE:
+               break;
 
-/*
- * Handler for kernel-facing EVPN MAC address updates
- */
-static enum zebra_dplane_result
-kernel_dplane_mac_update(struct zebra_dplane_ctx *ctx)
-{
-       if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
-               char buf[ETHER_ADDR_STRLEN];
+       case DPLANE_OP_PW_INSTALL:
+       case DPLANE_OP_PW_UNINSTALL:
+               zlog_debug("Dplane pw %s: op %s af %d loc: %u rem: %u",
+                          dplane_ctx_get_ifname(ctx),
+                          dplane_op2str(ctx->zd_op), dplane_ctx_get_pw_af(ctx),
+                          dplane_ctx_get_pw_local_label(ctx),
+                          dplane_ctx_get_pw_remote_label(ctx));
+               break;
 
+       case DPLANE_OP_ADDR_INSTALL:
+       case DPLANE_OP_ADDR_UNINSTALL:
+               zlog_debug("Dplane intf %s, idx %u, addr %pFX",
+                          dplane_op2str(dplane_ctx_get_op(ctx)),
+                          dplane_ctx_get_ifindex(ctx),
+                          dplane_ctx_get_intf_addr(ctx));
+               break;
+
+       case DPLANE_OP_MAC_INSTALL:
+       case DPLANE_OP_MAC_DELETE:
                prefix_mac2str(dplane_ctx_mac_get_addr(ctx), buf,
                               sizeof(buf));
 
                zlog_debug("Dplane %s, mac %s, ifindex %u",
                           dplane_op2str(dplane_ctx_get_op(ctx)),
                           buf, dplane_ctx_get_ifindex(ctx));
-       }
-
-       return kernel_mac_update_ctx(ctx);
-}
-
-/*
- * Handler for kernel-facing EVPN neighbor updates
- */
-static enum zebra_dplane_result
-kernel_dplane_neigh_update(struct zebra_dplane_ctx *ctx)
-{
-       if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
-               char buf[PREFIX_STRLEN];
+               break;
 
+       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_NEIGH_DISCOVER:
                ipaddr2str(dplane_ctx_neigh_get_ipaddr(ctx), buf,
                           sizeof(buf));
 
                zlog_debug("Dplane %s, ip %s, ifindex %u",
                           dplane_op2str(dplane_ctx_get_op(ctx)),
                           buf, dplane_ctx_get_ifindex(ctx));
-       }
-
-       return kernel_neigh_update_ctx(ctx);
-}
+               break;
 
-/*
- *  Handler for kernel PBR rule updates
- */
-static enum zebra_dplane_result
-kernel_dplane_rule_update(struct zebra_dplane_ctx *ctx)
-{
-       if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
+       case DPLANE_OP_RULE_ADD:
+       case DPLANE_OP_RULE_DELETE:
+       case DPLANE_OP_RULE_UPDATE:
                zlog_debug("Dplane rule update op %s, if %s(%u), ctx %p",
                           dplane_op2str(dplane_ctx_get_op(ctx)),
                           dplane_ctx_get_ifname(ctx),
                           dplane_ctx_get_ifindex(ctx), ctx);
+               break;
 
-       return kernel_pbr_rule_update(ctx);
+       case DPLANE_OP_SYS_ROUTE_ADD:
+       case DPLANE_OP_SYS_ROUTE_DELETE:
+       case DPLANE_OP_ROUTE_NOTIFY:
+       case DPLANE_OP_LSP_NOTIFY:
+       case DPLANE_OP_BR_PORT_UPDATE:
+
+       case DPLANE_OP_NONE:
+               break;
+       }
 }
 
-static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx,
-                                       enum zebra_dplane_result res)
+static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx)
 {
+       enum zebra_dplane_result res = dplane_ctx_get_status(ctx);
+
        switch (dplane_ctx_get_op(ctx)) {
 
        case DPLANE_OP_ROUTE_INSTALL:
@@ -3868,6 +3986,27 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx,
                if (res != ZEBRA_DPLANE_REQUEST_SUCCESS)
                        atomic_fetch_add_explicit(&zdplane_info.dg_route_errors,
                                                  1, memory_order_relaxed);
+
+               if ((dplane_ctx_get_op(ctx) != DPLANE_OP_ROUTE_DELETE)
+                   && (res == ZEBRA_DPLANE_REQUEST_SUCCESS)) {
+                       struct nexthop *nexthop;
+
+                       /* Update installed nexthops to signal which have been
+                        * installed.
+                        */
+                       for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx),
+                                             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);
+                               }
+                       }
+               }
                break;
 
        case DPLANE_OP_NH_INSTALL:
@@ -3914,6 +4053,7 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx,
        case DPLANE_OP_NEIGH_DELETE:
        case DPLANE_OP_VTEP_ADD:
        case DPLANE_OP_VTEP_DELETE:
+       case DPLANE_OP_NEIGH_DISCOVER:
                if (res != ZEBRA_DPLANE_REQUEST_SUCCESS)
                        atomic_fetch_add_explicit(&zdplane_info.dg_neigh_errors,
                                                  1, memory_order_relaxed);
@@ -3932,11 +4072,15 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx,
        case DPLANE_OP_SYS_ROUTE_DELETE:
        case DPLANE_OP_ROUTE_NOTIFY:
        case DPLANE_OP_LSP_NOTIFY:
+       case DPLANE_OP_BR_PORT_UPDATE:
+               break;
+
        case DPLANE_OP_NONE:
+               if (res != ZEBRA_DPLANE_REQUEST_SUCCESS)
+                       atomic_fetch_add_explicit(&zdplane_info.dg_other_errors,
+                                                 1, memory_order_relaxed);
                break;
        }
-
-       dplane_ctx_set_status(ctx, res);
 }
 
 /*
@@ -3944,7 +4088,6 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx,
  */
 static int kernel_dplane_process_func(struct zebra_dplane_provider *prov)
 {
-       enum zebra_dplane_result res;
        struct zebra_dplane_ctx *ctx, *tctx;
        struct dplane_ctx_q work_list;
        int counter, limit;
@@ -3958,97 +4101,21 @@ static int kernel_dplane_process_func(struct zebra_dplane_provider *prov)
                           dplane_provider_get_name(prov));
 
        for (counter = 0; counter < limit; counter++) {
-
                ctx = dplane_provider_dequeue_in_ctx(prov);
                if (ctx == NULL)
                        break;
 
-               /* A previous provider plugin may have asked to skip the
-                * kernel update.
-                */
-               if (dplane_ctx_is_skip_kernel(ctx)) {
-                       res = ZEBRA_DPLANE_REQUEST_SUCCESS;
-                       goto skip_one;
-               }
-
-               /* Dispatch to appropriate kernel-facing apis */
-               switch (dplane_ctx_get_op(ctx)) {
-
-               case DPLANE_OP_ROUTE_INSTALL:
-               case DPLANE_OP_ROUTE_UPDATE:
-               case DPLANE_OP_ROUTE_DELETE:
-                       res = kernel_dplane_route_update(ctx);
-                       break;
-
-               case DPLANE_OP_NH_INSTALL:
-               case DPLANE_OP_NH_UPDATE:
-               case DPLANE_OP_NH_DELETE:
-                       res = kernel_dplane_nexthop_update(ctx);
-                       break;
-
-               case DPLANE_OP_LSP_INSTALL:
-               case DPLANE_OP_LSP_UPDATE:
-               case DPLANE_OP_LSP_DELETE:
-                       res = kernel_dplane_lsp_update(ctx);
-                       break;
-
-               case DPLANE_OP_PW_INSTALL:
-               case DPLANE_OP_PW_UNINSTALL:
-                       res = kernel_dplane_pw_update(ctx);
-                       break;
-
-               case DPLANE_OP_ADDR_INSTALL:
-               case DPLANE_OP_ADDR_UNINSTALL:
-                       res = kernel_dplane_address_update(ctx);
-                       break;
-
-               case DPLANE_OP_MAC_INSTALL:
-               case DPLANE_OP_MAC_DELETE:
-                       res = kernel_dplane_mac_update(ctx);
-                       break;
-
-               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:
-                       res = kernel_dplane_neigh_update(ctx);
-                       break;
-
-               case DPLANE_OP_RULE_ADD:
-               case DPLANE_OP_RULE_DELETE:
-               case DPLANE_OP_RULE_UPDATE:
-                       res = kernel_dplane_rule_update(ctx);
-                       break;
-
-               /* Ignore 'notifications' - no-op */
-               case DPLANE_OP_SYS_ROUTE_ADD:
-               case DPLANE_OP_SYS_ROUTE_DELETE:
-               case DPLANE_OP_ROUTE_NOTIFY:
-               case DPLANE_OP_LSP_NOTIFY:
-                       res = ZEBRA_DPLANE_REQUEST_SUCCESS;
-                       break;
-
-               default:
-                       atomic_fetch_add_explicit(
-                               &zdplane_info.dg_other_errors, 1,
-                               memory_order_relaxed);
-
-                       res = ZEBRA_DPLANE_REQUEST_FAILURE;
-                       break;
-               }
-
-       skip_one:
-               /* If the request isn't pending, we can handle the result right
-                * away.
-                */
-               if (res != ZEBRA_DPLANE_REQUEST_PENDING)
-                       kernel_dplane_handle_result(ctx, res);
+               if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
+                       kernel_dplane_log_detail(ctx);
 
                TAILQ_INSERT_TAIL(&work_list, ctx, zd_q_entries);
        }
 
+       kernel_update_multi(&work_list);
+
        TAILQ_FOREACH_SAFE (ctx, &work_list, zd_q_entries, tctx) {
+               kernel_dplane_handle_result(ctx);
+
                TAILQ_REMOVE(&work_list, ctx, zd_q_entries);
                dplane_provider_enqueue_out_ctx(prov, ctx);
        }
@@ -4093,7 +4160,6 @@ static int test_dplane_process_func(struct zebra_dplane_provider *prov)
        limit = dplane_provider_get_work_limit(prov);
 
        for (counter = 0; counter < limit; counter++) {
-
                ctx = dplane_provider_dequeue_in_ctx(prov);
                if (ctx == NULL)
                        break;