#include "lib/memory.h"
#include "lib/queue.h"
#include "lib/zebra.h"
+#include "zebra/netconf_netlink.h"
#include "zebra/zebra_router.h"
#include "zebra/zebra_dplane.h"
#include "zebra/zebra_vxlan_private.h"
uint32_t metric;
uint32_t flags;
+ bool protodown;
+ bool pd_reason_val;
+
#define DPLANE_INTF_CONNECTED (1 << 0) /* Connected peer, p2p */
#define DPLANE_INTF_SECONDARY (1 << 1)
#define DPLANE_INTF_BROADCAST (1 << 2)
unsigned int mtu;
struct zebra_l2info_gre info;
};
+
+
+/*
+ * Network interface configuration info - aligned with netlink's NETCONF
+ * info. The flags values are public, in the dplane.h file...
+ */
+struct dplane_netconf_info {
+ enum dplane_netconf_status_e mpls_val;
+ enum dplane_netconf_status_e mcast_val;
+ enum dplane_netconf_status_e linkdown_val;
+};
+
/*
* The context block used to exchange info about route updates across
* the boundary between the zebra main context (and pthread) and the
} ipset_entry;
struct dplane_neigh_table neightable;
struct dplane_gre_ctx gre;
+ struct dplane_netconf_info netconf;
} u;
/* Namespace info, used especially for netlink kernel communication */
struct dplane_zns_info {
struct zebra_dplane_info info;
+ /* Request data from the OS */
+ struct thread *t_request;
+
/* Read event */
struct thread *t_read;
_Atomic uint32_t dg_gre_set_in;
_Atomic uint32_t dg_gre_set_errors;
+ _Atomic uint32_t dg_intfs_in;
+ _Atomic uint32_t dg_intf_errors;
+
/* Dataplane pthread */
struct frr_pthread *dg_pthread;
#define DPLANE_PROV_UNLOCK(p) pthread_mutex_unlock(&((p)->dp_mutex))
/* Prototypes */
-static int dplane_thread_loop(struct thread *event);
-static void dplane_info_from_zns(struct zebra_dplane_info *ns_info,
- struct zebra_ns *zns);
+static void dplane_thread_loop(struct thread *event);
static enum zebra_dplane_result lsp_update_internal(struct zebra_lsp *lsp,
enum dplane_op_e op);
static enum zebra_dplane_result pw_update_internal(struct zebra_pw *pw,
/* Maybe free label string, if allocated */
if (ctx->u.intf.label != NULL &&
ctx->u.intf.label != ctx->u.intf.label_buf) {
- free(ctx->u.intf.label);
+ XFREE(MTYPE_DP_CTX, ctx->u.intf.label);
ctx->u.intf.label = NULL;
}
break;
case DPLANE_OP_NONE:
case DPLANE_OP_IPSET_ADD:
case DPLANE_OP_IPSET_DELETE:
+ case DPLANE_OP_INTF_INSTALL:
+ case DPLANE_OP_INTF_UPDATE:
+ case DPLANE_OP_INTF_DELETE:
break;
case DPLANE_OP_IPSET_ENTRY_ADD:
}
break;
case DPLANE_OP_GRE_SET:
+ case DPLANE_OP_INTF_NETCONFIG:
break;
}
}
case DPLANE_OP_INTF_ADDR_DEL:
return "INTF_ADDR_DEL";
+
+ case DPLANE_OP_INTF_NETCONFIG:
+ return "INTF_NETCONFIG";
+
+ case DPLANE_OP_INTF_INSTALL:
+ ret = "INTF_INSTALL";
+ break;
+ case DPLANE_OP_INTF_UPDATE:
+ ret = "INTF_UPDATE";
+ break;
+ case DPLANE_OP_INTF_DELETE:
+ ret = "INTF_DELETE";
+ break;
}
return ret;
ctx->u.intf.metric = metric;
}
+uint32_t dplane_ctx_get_intf_pd_reason_val(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.intf.pd_reason_val;
+}
+
+void dplane_ctx_set_intf_pd_reason_val(struct zebra_dplane_ctx *ctx, bool val)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->u.intf.pd_reason_val = val;
+}
+
+bool dplane_ctx_intf_is_protodown(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.intf.protodown;
+}
+
/* Is interface addr p2p? */
bool dplane_ctx_intf_is_connected(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
if (ctx->u.intf.label && ctx->u.intf.label != ctx->u.intf.label_buf)
- free(ctx->u.intf.label);
+ XFREE(MTYPE_DP_CTX, ctx->u.intf.label);
ctx->u.intf.label = NULL;
sizeof(ctx->u.intf.label_buf));
ctx->u.intf.label = ctx->u.intf.label_buf;
} else {
- ctx->u.intf.label = strdup(label);
+ ctx->u.intf.label = XSTRDUP(MTYPE_DP_CTX, label);
}
} else {
ctx->u.intf.flags &= ~DPLANE_INTF_HAS_LABEL;
return ptr->status;
}
+/*
+ * End of interface extra info accessors
+ */
+
uint8_t dplane_ctx_neightable_get_family(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
return ctx->u.neightable.mcast_probes;
}
-/*
- * End of interface extra info accessors
- */
+enum dplane_netconf_status_e
+dplane_ctx_get_netconf_mpls(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.netconf.mpls_val;
+}
+
+enum dplane_netconf_status_e
+dplane_ctx_get_netconf_mcast(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.netconf.mcast_val;
+}
+
+enum dplane_netconf_status_e
+dplane_ctx_get_netconf_linkdown(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.netconf.linkdown_val;
+}
+
+void dplane_ctx_set_netconf_mpls(struct zebra_dplane_ctx *ctx,
+ enum dplane_netconf_status_e val)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->u.netconf.mpls_val = val;
+}
+
+void dplane_ctx_set_netconf_mcast(struct zebra_dplane_ctx *ctx,
+ enum dplane_netconf_status_e val)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->u.netconf.mcast_val = val;
+}
+
+void dplane_ctx_set_netconf_linkdown(struct zebra_dplane_ctx *ctx,
+ enum dplane_netconf_status_e val)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->u.netconf.linkdown_val = val;
+}
+
/*
* Retrieve the limit on the number of pending, unprocessed updates.
memory_order_seq_cst);
}
+/*
+ * Internal helper that copies information from a zebra ns object; this is
+ * called in the zebra main pthread context as part of dplane ctx init.
+ */
+static void ctx_info_from_zns(struct zebra_dplane_info *ns_info,
+ struct zebra_ns *zns)
+{
+ ns_info->ns_id = zns->ns_id;
+
+#if defined(HAVE_NETLINK)
+ ns_info->is_cmd = true;
+ ns_info->sock = zns->netlink_dplane_out.sock;
+ ns_info->seq = zns->netlink_dplane_out.seq;
+#endif /* NETLINK */
+}
+
/*
* Common dataplane context init with zebra namespace info.
*/
struct zebra_ns *zns,
bool is_update)
{
- dplane_info_from_zns(&(ctx->zd_ns_info), zns);
+ ctx_info_from_zns(&(ctx->zd_ns_info), zns); /* */
ctx->zd_is_update = is_update;
}
/* Check for available evpn encapsulations. */
- if (!CHECK_FLAG(re->flags, ZEBRA_FLAG_EVPN_ROUTE))
+ if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_EVPN))
continue;
zl3vni = zl3vni_from_vrf(nexthop->vrf_id);
return ret;
}
+/**
+ * dplane_ctx_intf_init() - Initialize a context block for a interface update
+ *
+ * @ctx: Dataplane context to init
+ * @op: Operation being performed
+ * @ifp: Interface
+ *
+ * Return: Result status
+ */
+int dplane_ctx_intf_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,
+ const struct interface *ifp)
+{
+ struct zebra_ns *zns;
+ struct zebra_if *zif;
+ int ret = EINVAL;
+ bool set_pdown, unset_pdown;
+
+ if (!ctx || !ifp)
+ goto done;
+
+ ctx->zd_op = op;
+ ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS;
+ ctx->zd_vrf_id = ifp->vrf->vrf_id;
+
+ strlcpy(ctx->zd_ifname, ifp->name, sizeof(ctx->zd_ifname));
+ ctx->zd_ifindex = ifp->ifindex;
+
+ zns = zebra_ns_lookup(ifp->vrf->vrf_id);
+ dplane_ctx_ns_init(ctx, zns, false);
+
+
+ /* Copy over ifp info */
+ ctx->u.intf.metric = ifp->metric;
+ ctx->u.intf.flags = ifp->flags;
+
+ /* Copy over extra zebra info, if available */
+ zif = (struct zebra_if *)ifp->info;
+
+ if (zif) {
+ set_pdown = !!(zif->flags & ZIF_FLAG_SET_PROTODOWN);
+ unset_pdown = !!(zif->flags & ZIF_FLAG_UNSET_PROTODOWN);
+
+ if (zif->protodown_rc &&
+ ZEBRA_IF_IS_PROTODOWN_ONLY_EXTERNAL(zif) == false)
+ ctx->u.intf.pd_reason_val = true;
+
+ /*
+ * See if we have new protodown state to set, otherwise keep
+ * current state
+ */
+ if (set_pdown)
+ ctx->u.intf.protodown = true;
+ else if (unset_pdown)
+ ctx->u.intf.protodown = false;
+ else
+ ctx->u.intf.protodown = !!ZEBRA_IF_IS_PROTODOWN(zif);
+ }
+
+ dplane_ctx_ns_init(ctx, zns, (op == DPLANE_OP_INTF_UPDATE));
+ ctx->zd_is_update = (op == DPLANE_OP_INTF_UPDATE);
+
+ ret = AOK;
+
+done:
+ return ret;
+}
+
/*
* Capture information for an LSP update in a dplane context.
*/
sizeof(ctx->u.intf.label_buf));
ctx->u.intf.label = ctx->u.intf.label_buf;
} else {
- ctx->u.intf.label = strdup(ifc->label);
+ ctx->u.intf.label = XSTRDUP(MTYPE_DP_CTX, ifc->label);
}
}
return result;
}
+/**
+ * dplane_intf_update_internal() - Helper for enqueuing interface changes
+ *
+ * @ifp: Interface where the change occured
+ * @op: The operation to be enqued
+ *
+ * Return: Result of the change
+ */
+static enum zebra_dplane_result
+dplane_intf_update_internal(const struct interface *ifp, enum dplane_op_e op)
+{
+ enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE;
+ int ret = EINVAL;
+ struct zebra_dplane_ctx *ctx = NULL;
+
+ /* Obtain context block */
+ ctx = dplane_ctx_alloc();
+ if (!ctx) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = dplane_ctx_intf_init(ctx, op, ifp);
+ if (ret == AOK)
+ ret = dplane_update_enqueue(ctx);
+
+done:
+ /* Update counter */
+ atomic_fetch_add_explicit(&zdplane_info.dg_intfs_in, 1,
+ memory_order_relaxed);
+
+ if (ret == AOK)
+ result = ZEBRA_DPLANE_REQUEST_QUEUED;
+ else {
+ atomic_fetch_add_explicit(&zdplane_info.dg_intf_errors, 1,
+ memory_order_relaxed);
+ if (ctx)
+ dplane_ctx_free(&ctx);
+ }
+
+ return result;
+}
+
+/*
+ * Enqueue a interface add for the dataplane.
+ */
+enum zebra_dplane_result dplane_intf_add(const struct interface *ifp)
+{
+ enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE;
+
+ if (ifp)
+ ret = dplane_intf_update_internal(ifp, DPLANE_OP_INTF_INSTALL);
+ return ret;
+}
+
+/*
+ * Enqueue a interface update for the dataplane.
+ */
+enum zebra_dplane_result dplane_intf_update(const struct interface *ifp)
+{
+ enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE;
+
+ if (ifp)
+ ret = dplane_intf_update_internal(ifp, DPLANE_OP_INTF_UPDATE);
+ return ret;
+}
+
+/*
+ * Enqueue a interface delete for the dataplane.
+ */
+enum zebra_dplane_result dplane_intf_delete(const struct interface *ifp)
+{
+ enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE;
+
+ if (ifp)
+ ret = dplane_intf_update_internal(ifp, DPLANE_OP_INTF_DELETE);
+ return ret;
+}
+
/*
* Enqueue vxlan/evpn mac add (or update).
*/
struct zebra_dplane_ctx *ctx;
int ret;
+ if ((op == DPLANE_OP_IPTABLE_ADD &&
+ CHECK_FLAG(iptable->internal_flags, IPTABLE_INSTALL_QUEUED)) ||
+ (op == DPLANE_OP_IPTABLE_DELETE &&
+ CHECK_FLAG(iptable->internal_flags, IPTABLE_UNINSTALL_QUEUED))) {
+ if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
+ zlog_debug(
+ "update dplane ctx %s: iptable %s already in progress",
+ dplane_op2str(op), iptable->ipset_name);
+ return result;
+ }
+
ctx = dplane_ctx_alloc();
ret = dplane_ctx_iptable_init(ctx, op, iptable);
atomic_fetch_add_explicit(&zdplane_info.dg_iptable_in, 1,
memory_order_relaxed);
- if (ret == AOK)
+ if (ret == AOK) {
result = ZEBRA_DPLANE_REQUEST_QUEUED;
- else {
+ if (op == DPLANE_OP_IPTABLE_ADD)
+ SET_FLAG(iptable->internal_flags,
+ IPTABLE_INSTALL_QUEUED);
+ else
+ SET_FLAG(iptable->internal_flags,
+ IPTABLE_UNINSTALL_QUEUED);
+ } else {
atomic_fetch_add_explicit(&zdplane_info.dg_iptable_errors, 1,
memory_order_relaxed);
dplane_ctx_free(&ctx);
}
-
return result;
}
return (prov->dp_flags & DPLANE_PROV_FLAG_THREADED);
}
-/*
- * Internal helper that copies information from a zebra ns object; this is
- * called in the zebra main pthread context as part of dplane ctx init.
- */
-static void dplane_info_from_zns(struct zebra_dplane_info *ns_info,
- struct zebra_ns *zns)
-{
- ns_info->ns_id = zns->ns_id;
-
-#if defined(HAVE_NETLINK)
- ns_info->is_cmd = true;
- ns_info->sock = zns->netlink_dplane_out.sock;
-#endif /* NETLINK */
-}
-
#ifdef HAVE_NETLINK
/*
* Callback when an OS (netlink) incoming event read is ready. This runs
* in the dplane pthread.
*/
-static int dplane_incoming_read(struct thread *event)
+static void dplane_incoming_read(struct thread *event)
{
struct dplane_zns_info *zi = THREAD_ARG(event);
/* Re-start read task */
thread_add_read(zdplane_info.dg_master, dplane_incoming_read, zi,
zi->info.sock, &zi->t_read);
+}
- return 0;
+/*
+ * Callback in the dataplane pthread that requests info from the OS and
+ * initiates netlink reads.
+ */
+static void dplane_incoming_request(struct thread *event)
+{
+ struct dplane_zns_info *zi = THREAD_ARG(event);
+
+ /* Start read task */
+ thread_add_read(zdplane_info.dg_master, dplane_incoming_read, zi,
+ zi->info.sock, &zi->t_read);
+
+ /* Send requests */
+ netlink_request_netconf(zi->info.sock);
}
+
+/*
+ * Initiate requests for existing info from the OS. This is called by the
+ * main pthread, but we want all activity on the dplane netlink socket to
+ * take place on the dplane pthread, so we schedule an event to accomplish
+ * that.
+ */
+static void dplane_kernel_info_request(struct dplane_zns_info *zi)
+{
+ /* If we happen to encounter an enabled zns before the dplane
+ * pthread is running, we'll initiate this later on.
+ */
+ if (zdplane_info.dg_master)
+ thread_add_event(zdplane_info.dg_master,
+ dplane_incoming_request, zi, 0,
+ &zi->t_request);
+}
+
#endif /* HAVE_NETLINK */
/*
zi->info.is_cmd = false;
zi->info.sock = zns->netlink_dplane_in.sock;
- /* Start read task for the dplane pthread. */
- if (zdplane_info.dg_master)
- thread_add_read(zdplane_info.dg_master,
- dplane_incoming_read, zi, zi->info.sock,
- &zi->t_read);
+ /* Initiate requests for existing info from the OS, and
+ * begin reading from the netlink socket.
+ */
+ dplane_kernel_info_request(zi);
#endif
} else if (zi) {
if (IS_ZEBRA_DEBUG_DPLANE)
/* Stop reading, free memory */
zns_info_list_del(&zdplane_info.dg_zns_list, zi);
- if (zdplane_info.dg_master)
+ /* Stop any outstanding tasks */
+ if (zdplane_info.dg_master) {
+ thread_cancel_async(zdplane_info.dg_master,
+ &zi->t_request, NULL);
+
thread_cancel_async(zdplane_info.dg_master, &zi->t_read,
NULL);
+ }
XFREE(MTYPE_DP_NS, zi);
}
dplane_ctx_get_ifname(ctx),
dplane_ctx_get_intf_addr(ctx));
break;
+
+ case DPLANE_OP_INTF_NETCONFIG:
+ zlog_debug("%s: ifindex %d, mpls %d, mcast %d",
+ dplane_op2str(dplane_ctx_get_op(ctx)),
+ dplane_ctx_get_ifindex(ctx),
+ dplane_ctx_get_netconf_mpls(ctx),
+ dplane_ctx_get_netconf_mcast(ctx));
+ break;
+
+ case DPLANE_OP_INTF_INSTALL:
+ case DPLANE_OP_INTF_UPDATE:
+ case DPLANE_OP_INTF_DELETE:
+ zlog_debug("Dplane intf %s, idx %u, protodown %d",
+ dplane_op2str(dplane_ctx_get_op(ctx)),
+ dplane_ctx_get_ifindex(ctx),
+ dplane_ctx_intf_is_protodown(ctx));
+ break;
}
}
&zdplane_info.dg_gre_set_errors, 1,
memory_order_relaxed);
break;
+
+ case DPLANE_OP_INTF_INSTALL:
+ case DPLANE_OP_INTF_UPDATE:
+ case DPLANE_OP_INTF_DELETE:
+ if (res != ZEBRA_DPLANE_REQUEST_SUCCESS)
+ atomic_fetch_add_explicit(&zdplane_info.dg_intf_errors,
+ 1, memory_order_relaxed);
+ break;
+
/* Ignore 'notifications' - no-op */
case DPLANE_OP_SYS_ROUTE_ADD:
case DPLANE_OP_SYS_ROUTE_DELETE:
/* TODO -- error counters for incoming events? */
case DPLANE_OP_INTF_ADDR_ADD:
case DPLANE_OP_INTF_ADDR_DEL:
+ case DPLANE_OP_INTF_NETCONFIG:
break;
case DPLANE_OP_NONE:
* final zebra shutdown.
* This runs in the dplane pthread context.
*/
-static int dplane_check_shutdown_status(struct thread *event)
+static void dplane_check_shutdown_status(struct thread *event)
{
struct dplane_zns_info *zi;
frr_each_safe (zns_info_list, &zdplane_info.dg_zns_list, zi) {
zns_info_list_del(&zdplane_info.dg_zns_list, zi);
- if (zdplane_info.dg_master)
+ if (zdplane_info.dg_master) {
thread_cancel(&zi->t_read);
+ thread_cancel(&zi->t_request);
+ }
XFREE(MTYPE_DP_NS, zi);
}
*/
thread_add_event(zrouter.master, zebra_finalize, NULL, 0, NULL);
}
-
- return 0;
}
/*
* pthread can look for other pending work - such as i/o work on behalf of
* providers.
*/
-static int dplane_thread_loop(struct thread *event)
+static void dplane_thread_loop(struct thread *event)
{
struct dplane_ctx_q work_list;
struct dplane_ctx_q error_list;
/* Check for zebra shutdown */
if (!zdplane_info.dg_run)
- goto done;
+ return;
/* Dequeue some incoming work from zebra (if any) onto the temporary
* working list.
(zdplane_info.dg_results_cb)(&work_list);
TAILQ_INIT(&work_list);
-
-done:
- return 0;
}
/*
thread_add_event(zdplane_info.dg_master, dplane_thread_loop, NULL, 0,
&zdplane_info.dg_t_update);
- /* Enqueue reads if necessary */
+ /* Enqueue requests and reads if necessary */
frr_each (zns_info_list, &zdplane_info.dg_zns_list, zi) {
#if defined(HAVE_NETLINK)
thread_add_read(zdplane_info.dg_master, dplane_incoming_read,
zi, zi->info.sock, &zi->t_read);
+ dplane_kernel_info_request(zi);
#endif
}