diff --git a/drivers/common/sfc_efx/base/efx.h b/drivers/common/sfc_efx/base/efx.h
index 8d88834c30..6f63a6ecd0 100644
--- a/drivers/common/sfc_efx/base/efx.h
+++ b/drivers/common/sfc_efx/base/efx.h
@@ -4094,11 +4094,14 @@ typedef struct efx_mport_sel_s {
 	uint32_t sel;
 } efx_mport_sel_t;
 
+#define	EFX_MPORT_NULL			(0U)
+
 /*
  * Get MPORT selector of a physical port.
  *
  * The resulting MPORT selector is opaque to the caller and can be
- * passed as an argument to efx_mae_match_spec_mport_set().
+ * passed as an argument to efx_mae_match_spec_mport_set()
+ * and efx_mae_action_set_populate_deliver().
  */
 LIBEFX_API
 extern	__checkReturn			efx_rc_t
@@ -4163,6 +4166,12 @@ efx_mae_action_set_spec_fini(
 	__in				efx_nic_t *enp,
 	__in				efx_mae_actions_t *spec);
 
+LIBEFX_API
+extern	__checkReturn			efx_rc_t
+efx_mae_action_set_populate_deliver(
+	__in				efx_mae_actions_t *spec,
+	__in				const efx_mport_sel_t *mportp);
+
 LIBEFX_API
 extern	__checkReturn			boolean_t
 efx_mae_action_set_specs_equal(
diff --git a/drivers/common/sfc_efx/base/efx_impl.h b/drivers/common/sfc_efx/base/efx_impl.h
index 86ef8e1b92..927324c85c 100644
--- a/drivers/common/sfc_efx/base/efx_impl.h
+++ b/drivers/common/sfc_efx/base/efx_impl.h
@@ -1699,7 +1699,18 @@ struct efx_mae_match_spec_s {
 	} emms_mask_value_pairs;
 };
 
+typedef enum efx_mae_action_e {
+	/* DELIVER is always the last action. */
+	EFX_MAE_ACTION_DELIVER,
+
+	EFX_MAE_NACTIONS
+} efx_mae_action_t;
+
 typedef struct efx_mae_actions_s {
+	/* Bitmap of actions in spec, indexed by action type */
+	uint32_t			emass_actions;
+
+	efx_mport_sel_t			emass_deliver_mport;
 } efx_mae_actions_t;
 
 #endif /* EFSYS_OPT_MAE */
diff --git a/drivers/common/sfc_efx/base/efx_mae.c b/drivers/common/sfc_efx/base/efx_mae.c
index 01b2e311a5..47611c4397 100644
--- a/drivers/common/sfc_efx/base/efx_mae.c
+++ b/drivers/common/sfc_efx/base/efx_mae.c
@@ -641,6 +641,147 @@ efx_mae_action_set_spec_fini(
 	EFSYS_KMEM_FREE(enp->en_esip, sizeof (*spec), spec);
 }
 
+static	__checkReturn			efx_rc_t
+efx_mae_action_set_add_deliver(
+	__in				efx_mae_actions_t *spec,
+	__in				size_t arg_size,
+	__in_bcount(arg_size)		const uint8_t *arg)
+{
+	efx_rc_t rc;
+
+	if (arg_size != sizeof (spec->emass_deliver_mport)) {
+		rc = EINVAL;
+		goto fail1;
+	}
+
+	if (arg == NULL) {
+		rc = EINVAL;
+		goto fail2;
+	}
+
+	memcpy(&spec->emass_deliver_mport, arg, arg_size);
+
+	return (0);
+
+fail2:
+	EFSYS_PROBE(fail2);
+fail1:
+	EFSYS_PROBE1(fail1, efx_rc_t, rc);
+	return (rc);
+}
+
+typedef struct efx_mae_action_desc_s {
+	/* Action specific handler */
+	efx_rc_t	(*emad_add)(efx_mae_actions_t *,
+				    size_t, const uint8_t *);
+} efx_mae_action_desc_t;
+
+static const efx_mae_action_desc_t efx_mae_actions[EFX_MAE_NACTIONS] = {
+	[EFX_MAE_ACTION_DELIVER] = {
+		.emad_add = efx_mae_action_set_add_deliver
+	}
+};
+
+static const uint32_t efx_mae_action_ordered_map =
+	(1U << EFX_MAE_ACTION_DELIVER);
+
+static const uint32_t efx_mae_action_repeat_map = 0;
+
+/*
+ * Add an action to an action set.
+ *
+ * This has to be invoked in the desired action order.
+ * An out-of-order action request will be turned down.
+ */
+static	__checkReturn			efx_rc_t
+efx_mae_action_set_spec_populate(
+	__in				efx_mae_actions_t *spec,
+	__in				efx_mae_action_t type,
+	__in				size_t arg_size,
+	__in_bcount(arg_size)		const uint8_t *arg)
+{
+	uint32_t action_mask;
+	efx_rc_t rc;
+
+	EFX_STATIC_ASSERT(EFX_MAE_NACTIONS <=
+	    (sizeof (efx_mae_action_ordered_map) * 8));
+	EFX_STATIC_ASSERT(EFX_MAE_NACTIONS <=
+	    (sizeof (efx_mae_action_repeat_map) * 8));
+
+	EFX_STATIC_ASSERT(EFX_MAE_ACTION_DELIVER + 1 == EFX_MAE_NACTIONS);
+
+	if (type >= EFX_ARRAY_SIZE(efx_mae_actions)) {
+		rc = EINVAL;
+		goto fail1;
+	}
+
+	action_mask = (1U << type);
+
+	if ((spec->emass_actions & action_mask) != 0) {
+		/* The action set already contains this action. */
+		if ((efx_mae_action_repeat_map & action_mask) == 0) {
+			/* Cannot add another non-repeatable action. */
+			rc = ENOTSUP;
+			goto fail2;
+		}
+	}
+
+	if ((efx_mae_action_ordered_map & action_mask) != 0) {
+		uint32_t later_actions_mask =
+			efx_mae_action_ordered_map &
+			~(action_mask | (action_mask - 1));
+
+		if ((spec->emass_actions & later_actions_mask) != 0) {
+			/* Cannot add an action after later ordered actions. */
+			rc = ENOTSUP;
+			goto fail3;
+		}
+	}
+
+	if (efx_mae_actions[type].emad_add != NULL) {
+		rc = efx_mae_actions[type].emad_add(spec, arg_size, arg);
+		if (rc != 0)
+			goto fail4;
+	}
+
+	spec->emass_actions |= action_mask;
+
+	return (0);
+
+fail4:
+	EFSYS_PROBE(fail4);
+fail3:
+	EFSYS_PROBE(fail3);
+fail2:
+	EFSYS_PROBE(fail2);
+fail1:
+	EFSYS_PROBE1(fail1, efx_rc_t, rc);
+	return (rc);
+}
+
+	__checkReturn			efx_rc_t
+efx_mae_action_set_populate_deliver(
+	__in				efx_mae_actions_t *spec,
+	__in				const efx_mport_sel_t *mportp)
+{
+	const uint8_t *arg;
+	efx_rc_t rc;
+
+	if (mportp == NULL) {
+		rc = EINVAL;
+		goto fail1;
+	}
+
+	arg = (const uint8_t *)&mportp->sel;
+
+	return (efx_mae_action_set_spec_populate(spec,
+	    EFX_MAE_ACTION_DELIVER, sizeof (mportp->sel), arg));
+
+fail1:
+	EFSYS_PROBE1(fail1, efx_rc_t, rc);
+	return (rc);
+}
+
 	__checkReturn			boolean_t
 efx_mae_action_set_specs_equal(
 	__in				const efx_mae_actions_t *left,
diff --git a/drivers/common/sfc_efx/rte_common_sfc_efx_version.map b/drivers/common/sfc_efx/rte_common_sfc_efx_version.map
index 86ed437e8d..d2a5d58ae8 100644
--- a/drivers/common/sfc_efx/rte_common_sfc_efx_version.map
+++ b/drivers/common/sfc_efx/rte_common_sfc_efx_version.map
@@ -85,6 +85,7 @@ INTERNAL {
 	efx_mac_stats_upload;
 	efx_mac_up;
 
+	efx_mae_action_set_populate_deliver;
 	efx_mae_action_set_spec_fini;
 	efx_mae_action_set_spec_init;
 	efx_mae_action_set_specs_equal;
