#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")
*/
struct dplane_nexthop_info {
uint32_t id;
+ uint32_t old_id;
afi_t afi;
vrf_id_t vrf_id;
int type;
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
*/
};
/*
- * EVPN neighbor info for the dataplane
+ * Neighbor info for the dataplane
*/
struct dplane_neigh_info {
struct ipaddr ip_addr;
uint8_t dsfield;
struct prefix src_ip;
struct prefix dst_ip;
+ char ifname[INTERFACE_NAMSIZ + 1];
};
struct dplane_rule_info {
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;
_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;
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;
}
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;
case DPLANE_OP_RULE_UPDATE:
ret = "RULE_UPDATE";
break;
+
+ case DPLANE_OP_NEIGH_DISCOVER:
+ ret = "NEIGH_DISCOVER";
+ break;
}
return ret;
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);
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);
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
*/
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.
* 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;
}
* 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;
/* 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));
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);
}
/**
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;
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.
#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);
}
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);
}
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.
*/
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();
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.
*/
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;
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;
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,
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)));
}
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;
}
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();
case DPLANE_OP_ROUTE_INSTALL:
case DPLANE_OP_ROUTE_UPDATE:
case DPLANE_OP_ROUTE_DELETE:
- prefix2str(dplane_ctx_get_dest(ctx), buf, sizeof(buf));
-
- zlog_debug("%u:%s Dplane route update ctx %p op %s",
- dplane_ctx_get_vrf(ctx), buf, ctx,
- dplane_op2str(dplane_ctx_get_op(ctx)));
+ 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)));
break;
case DPLANE_OP_NH_INSTALL:
case DPLANE_OP_ADDR_INSTALL:
case DPLANE_OP_ADDR_UNINSTALL:
- prefix2str(dplane_ctx_get_intf_addr(ctx), buf, sizeof(buf));
-
- zlog_debug("Dplane intf %s, idx %u, addr %s",
+ zlog_debug("Dplane intf %s, idx %u, addr %pFX",
dplane_op2str(dplane_ctx_get_op(ctx)),
- dplane_ctx_get_ifindex(ctx), buf);
+ dplane_ctx_get_ifindex(ctx),
+ dplane_ctx_get_intf_addr(ctx));
break;
case DPLANE_OP_MAC_INSTALL:
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));
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;
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);
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:
}
}
-static void kernel_dplane_dispatch_updates(struct dplane_ctx_q *ctx_list)
-{
- enum zebra_dplane_result res;
- struct zebra_dplane_ctx *ctx;
-
- TAILQ_FOREACH (ctx, ctx_list, zd_q_entries) {
- /*
- * 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_route_update(ctx);
- break;
-
- case DPLANE_OP_NH_INSTALL:
- case DPLANE_OP_NH_UPDATE:
- case DPLANE_OP_NH_DELETE:
- res = kernel_nexthop_update(ctx);
- break;
-
- case DPLANE_OP_LSP_INSTALL:
- case DPLANE_OP_LSP_UPDATE:
- case DPLANE_OP_LSP_DELETE:
- res = kernel_lsp_update(ctx);
- break;
-
- case DPLANE_OP_PW_INSTALL:
- case DPLANE_OP_PW_UNINSTALL:
- res = kernel_pw_update(ctx);
- break;
-
- case DPLANE_OP_ADDR_INSTALL:
- case DPLANE_OP_ADDR_UNINSTALL:
- res = kernel_address_update_ctx(ctx);
- break;
-
- case DPLANE_OP_MAC_INSTALL:
- case DPLANE_OP_MAC_DELETE:
- res = kernel_mac_update_ctx(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_neigh_update_ctx(ctx);
- break;
-
- case DPLANE_OP_RULE_ADD:
- case DPLANE_OP_RULE_DELETE:
- case DPLANE_OP_RULE_UPDATE:
- res = kernel_pbr_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:
- res = ZEBRA_DPLANE_REQUEST_FAILURE;
- break;
- }
-
- skip_one:
- dplane_ctx_set_status(ctx, res);
- }
-}
-
/*
* Kernel provider callback
*/
TAILQ_INSERT_TAIL(&work_list, ctx, zd_q_entries);
}
- if (kernel_supports_batch())
- kernel_update_multi(&work_list);
- else
- kernel_dplane_dispatch_updates(&work_list);
+ kernel_update_multi(&work_list);
TAILQ_FOREACH_SAFE (ctx, &work_list, zd_q_entries, tctx) {
kernel_dplane_handle_result(ctx);