#endif /* DPLANE_DEBUG */
+/*
+ * Nexthop information captured for nexthop/nexthop group updates
+ */
+struct dplane_nexthop_info {
+ uint32_t id;
+ afi_t afi;
+ vrf_id_t vrf_id;
+ int type;
+
+ struct nexthop_group ng;
+ struct nh_grp nh_grp[MULTIPATH_NUM];
+ uint8_t nh_grp_count;
+};
+
/*
* Route information captured for route updates.
*/
uint32_t zd_mtu;
uint32_t zd_nexthop_mtu;
+ /* Nexthop hash entry info */
+ struct dplane_nexthop_info nhe;
+
/* Nexthops */
struct nexthop_group zd_ng;
_Atomic uint32_t dg_route_errors;
_Atomic uint32_t dg_other_errors;
+ _Atomic uint32_t dg_nexthops_in;
+ _Atomic uint32_t dg_nexthop_errors;
+
_Atomic uint32_t dg_lsps_in;
_Atomic uint32_t dg_lsp_errors;
break;
+ case DPLANE_OP_NH_INSTALL:
+ case DPLANE_OP_NH_UPDATE:
+ case DPLANE_OP_NH_DELETE: {
+ if ((*pctx)->u.rinfo.nhe.ng.nexthop) {
+ /* This deals with recursive nexthops too */
+ nexthops_free((*pctx)->u.rinfo.nhe.ng.nexthop);
+
+ (*pctx)->u.rinfo.nhe.ng.nexthop = NULL;
+ }
+ break;
+ }
+
case DPLANE_OP_LSP_INSTALL:
case DPLANE_OP_LSP_UPDATE:
case DPLANE_OP_LSP_DELETE:
ret = "ROUTE_NOTIFY";
break;
+ /* Nexthop update */
+ case DPLANE_OP_NH_INSTALL:
+ ret = "NH_INSTALL";
+ break;
+ case DPLANE_OP_NH_UPDATE:
+ ret = "NH_UPDATE";
+ break;
+ case DPLANE_OP_NH_DELETE:
+ ret = "NH_DELETE";
+ break;
+
case DPLANE_OP_LSP_INSTALL:
ret = "LSP_INSTALL";
break;
return ctx->u.rinfo.zd_old_distance;
}
+/*
+ * Set the nexthops associated with a context: note that processing code
+ * may well expect that nexthops are in canonical (sorted) order, so we
+ * will enforce that here.
+ */
void dplane_ctx_set_nexthops(struct zebra_dplane_ctx *ctx, struct nexthop *nh)
{
DPLANE_CTX_VALID(ctx);
nexthops_free(ctx->u.rinfo.zd_ng.nexthop);
ctx->u.rinfo.zd_ng.nexthop = NULL;
}
- copy_nexthops(&(ctx->u.rinfo.zd_ng.nexthop), nh, NULL);
+ nexthop_group_copy_nh_sorted(&(ctx->u.rinfo.zd_ng), nh);
}
const struct nexthop_group *dplane_ctx_get_ng(
return &(ctx->zd_ns_info);
}
+/* Accessors for nexthop information */
+uint32_t dplane_ctx_get_nhe_id(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+ return ctx->u.rinfo.nhe.id;
+}
+
+afi_t dplane_ctx_get_nhe_afi(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+ return ctx->u.rinfo.nhe.afi;
+}
+
+vrf_id_t dplane_ctx_get_nhe_vrf_id(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+ return ctx->u.rinfo.nhe.vrf_id;
+}
+
+int dplane_ctx_get_nhe_type(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+ return ctx->u.rinfo.nhe.type;
+}
+
+const struct nexthop_group *
+dplane_ctx_get_nhe_ng(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+ return &(ctx->u.rinfo.nhe.ng);
+}
+
+const struct nh_grp *
+dplane_ctx_get_nhe_nh_grp(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+ return ctx->u.rinfo.nhe.nh_grp;
+}
+
+uint8_t dplane_ctx_get_nhe_nh_grp_count(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+ return ctx->u.rinfo.nhe.nh_grp_count;
+}
+
/* Accessors for LSP information */
mpls_label_t dplane_ctx_get_in_label(const struct zebra_dplane_ctx *ctx)
enum nexthop_types_t nh_type,
union g_addr *gate,
ifindex_t ifindex,
- mpls_label_t out_label)
+ uint8_t num_labels,
+ mpls_label_t out_labels[])
{
zebra_nhlfe_t *nhlfe;
nhlfe = zebra_mpls_lsp_add_nhlfe(&(ctx->u.lsp),
lsp_type, nh_type, gate,
- ifindex, out_label);
+ ifindex, num_labels, out_labels);
return nhlfe;
}
ctx->u.rinfo.zd_safi = info->safi;
/* Copy nexthops; recursive info is included too */
- copy_nexthops(&(ctx->u.rinfo.zd_ng.nexthop), re->ng.nexthop, NULL);
+ copy_nexthops(&(ctx->u.rinfo.zd_ng.nexthop),
+ re->nhe->nhg->nexthop, NULL);
/* Ensure that the dplane's nexthops flags are clear. */
for (ALL_NEXTHOPS(ctx->u.rinfo.zd_ng, nexthop))
zns = zvrf->zns;
dplane_ctx_ns_init(ctx, zns, (op == DPLANE_OP_ROUTE_UPDATE));
+#ifdef HAVE_NETLINK
+ if (re->nhe_id) {
+ struct nhg_hash_entry *nhe =
+ zebra_nhg_resolve(zebra_nhg_lookup_id(re->nhe_id));
+
+ ctx->u.rinfo.nhe.id = nhe->id;
+ /*
+ * 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)) {
+ ret = ENOENT;
+ goto done;
+ }
+ }
+#endif /* HAVE_NETLINK */
+
/* Trying out the sequence number idea, so we can try to detect
* when a result is stale.
*/
return ret;
}
+/**
+ * dplane_ctx_nexthop_init() - Initialize a context block for a nexthop update
+ *
+ * @ctx: Dataplane context to init
+ * @op: Operation being performed
+ * @nhe: Nexthop group hash entry
+ *
+ * Return: Result status
+ */
+static int dplane_ctx_nexthop_init(struct zebra_dplane_ctx *ctx,
+ enum dplane_op_e op,
+ struct nhg_hash_entry *nhe)
+{
+ struct zebra_vrf *zvrf = NULL;
+ struct zebra_ns *zns = NULL;
+
+ int ret = EINVAL;
+
+ if (!ctx || !nhe)
+ goto done;
+
+ ctx->zd_op = op;
+ ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS;
+
+ /* Copy over nhe info */
+ ctx->u.rinfo.nhe.id = nhe->id;
+ ctx->u.rinfo.nhe.afi = nhe->afi;
+ ctx->u.rinfo.nhe.vrf_id = nhe->vrf_id;
+ ctx->u.rinfo.nhe.type = nhe->type;
+
+ nexthop_group_copy(&(ctx->u.rinfo.nhe.ng), nhe->nhg);
+
+ /* If its a group, convert it to a grp array of ids */
+ if (!zebra_nhg_depends_is_empty(nhe)
+ && !CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_RECURSIVE))
+ ctx->u.rinfo.nhe.nh_grp_count = zebra_nhg_nhe2grp(
+ ctx->u.rinfo.nhe.nh_grp, nhe, MULTIPATH_NUM);
+
+ zvrf = vrf_info_lookup(nhe->vrf_id);
+
+ /*
+ * Fallback to default namespace if the vrf got ripped out from under
+ * us.
+ */
+ zns = zvrf ? zvrf->zns : zebra_ns_lookup(NS_DEFAULT);
+
+ /*
+ * TODO: Might not need to mark this as an update, since
+ * it probably won't require two messages
+ */
+ dplane_ctx_ns_init(ctx, zns, (op == DPLANE_OP_NH_UPDATE));
+
+ ret = AOK;
+
+done:
+ return ret;
+}
+
/*
* Capture information for an LSP update in a dplane context.
*/
nhlfe->nexthop->type,
&(nhlfe->nexthop->gate),
nhlfe->nexthop->ifindex,
- nhlfe->nexthop->nh_label->label[0]);
+ nhlfe->nexthop->nh_label->num_labels,
+ nhlfe->nexthop->nh_label->label);
if (new_nhlfe == NULL || new_nhlfe->nexthop == NULL) {
ret = ENOMEM;
if (re)
copy_nexthops(&(ctx->u.pw.nhg.nexthop),
- re->ng.nexthop, NULL);
+ re->nhe->nhg->nexthop, NULL);
route_unlock_node(rn);
}
* We'll need these to do per-nexthop deletes.
*/
copy_nexthops(&(ctx->u.rinfo.zd_old_ng.nexthop),
- old_re->ng.nexthop, NULL);
+ old_re->nhe->nhg->nexthop, NULL);
#endif /* !HAVE_NETLINK */
}
if (ret == AOK)
result = ZEBRA_DPLANE_REQUEST_QUEUED;
else {
- atomic_fetch_add_explicit(&zdplane_info.dg_route_errors, 1,
+ if (ret == ENOENT)
+ result = ZEBRA_DPLANE_REQUEST_SUCCESS;
+ else
+ atomic_fetch_add_explicit(&zdplane_info.dg_route_errors,
+ 1, memory_order_relaxed);
+ if (ctx)
+ dplane_ctx_free(&ctx);
+ }
+
+ return result;
+}
+
+/**
+ * dplane_nexthop_update_internal() - Helper for enqueuing nexthop changes
+ *
+ * @nhe: Nexthop group hash entry where the change occured
+ * @op: The operation to be enqued
+ *
+ * Return: Result of the change
+ */
+static enum zebra_dplane_result
+dplane_nexthop_update_internal(struct nhg_hash_entry *nhe, 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_nexthop_init(ctx, op, nhe);
+ if (ret == AOK)
+ ret = dplane_update_enqueue(ctx);
+
+done:
+ /* Update counter */
+ atomic_fetch_add_explicit(&zdplane_info.dg_nexthops_in, 1,
+ memory_order_relaxed);
+
+ if (ret == AOK)
+ result = ZEBRA_DPLANE_REQUEST_QUEUED;
+ else {
+ atomic_fetch_add_explicit(&zdplane_info.dg_nexthop_errors, 1,
memory_order_relaxed);
if (ctx)
dplane_ctx_free(&ctx);
return ret;
}
+/*
+ * Enqueue a nexthop add for the dataplane.
+ */
+enum zebra_dplane_result dplane_nexthop_add(struct nhg_hash_entry *nhe)
+{
+ enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE;
+
+ if (nhe)
+ ret = dplane_nexthop_update_internal(nhe, DPLANE_OP_NH_INSTALL);
+ return ret;
+}
+
+/*
+ * Enqueue a nexthop update for the dataplane.
+ *
+ * Might not need this func since zebra's nexthop objects should be immutable?
+ */
+enum zebra_dplane_result dplane_nexthop_update(struct nhg_hash_entry *nhe)
+{
+ enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE;
+
+ if (nhe)
+ ret = dplane_nexthop_update_internal(nhe, DPLANE_OP_NH_UPDATE);
+ return ret;
+}
+
+/*
+ * Enqueue a nexthop removal for the dataplane.
+ */
+enum zebra_dplane_result dplane_nexthop_delete(struct nhg_hash_entry *nhe)
+{
+ enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE;
+
+ if (nhe)
+ ret = dplane_nexthop_update_internal(nhe, DPLANE_OP_NH_DELETE);
+
+ return ret;
+}
+
/*
* Enqueue LSP add for the dataplane.
*/
enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE;
result = neigh_update_internal(DPLANE_OP_NEIGH_INSTALL,
- ifp, mac, ip, flags, 0);
+ ifp, mac, ip, flags, DPLANE_NUD_NOARP);
return result;
}
TAILQ_INIT(&(p->dp_ctx_in_q));
TAILQ_INIT(&(p->dp_ctx_out_q));
+ p->dp_flags = flags;
p->dp_priority = prio;
p->dp_fp = fp;
p->dp_start = start_fp;
return res;
}
+/**
+ * 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)
+{
+ enum zebra_dplane_result res;
+
+ if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
+ 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)));
+ }
+
+ res = kernel_nexthop_update(ctx);
+
+ if (res != ZEBRA_DPLANE_REQUEST_SUCCESS)
+ atomic_fetch_add_explicit(&zdplane_info.dg_nexthop_errors, 1,
+ memory_order_relaxed);
+
+ return res;
+}
+
/*
* Handler for kernel-facing EVPN MAC address updates
*/
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:
zdplane_info.dg_run = false;
- THREAD_OFF(zdplane_info.dg_t_update);
+ if (zdplane_info.dg_t_update)
+ thread_cancel_async(zdplane_info.dg_t_update->master,
+ &zdplane_info.dg_t_update, NULL);
frr_pthread_stop(zdplane_info.dg_pthread, NULL);