git: 660ea2c4dafe - main - ixgbe: enforce VF promiscuity and multicast policy

From: Kevin Bowling <kbowling_at_FreeBSD.org>
Date: Fri, 07 Aug 2026 01:47:46 UTC
The branch main has been updated by kbowling:

URL: https://cgit.FreeBSD.org/src/commit/?id=660ea2c4dafe9c2206c95fe57ecd8972d6395952

commit 660ea2c4dafe9c2206c95fe57ecd8972d6395952
Author:     Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-07-31 12:53:13 +0000
Commit:     Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-08-07 01:40:58 +0000

    ixgbe: enforce VF promiscuity and multicast policy
    
    The allow-promisc IOV property is advertised but ignored, and the PF
    rejects the xcast request used by modern VFs. Negotiate mailbox APIs
    1.2 and 1.3, implement pool-scoped xcast modes, and require
    allow-promisc for requested all-multicast or unicast-promiscuous modes.
    
    The VF mailbox can carry only 30 multicast hashes. When ixv has a
    larger list, request the API 1.2 all-multicast xcast mode instead of
    extending the legacy SET_MULTICAST message. The PF grants that fallback
    only to VFs configured with allow-promisc; otherwise ixv reports that
    only the first 30 addresses are active.
    
    Reset xcast state with the VF and have ixv replay the mode implied by
    its interface flags after multicast updates.
    
    Follow DPDK's ixgbe API 1.2/1.3 xcast contract, with allow-promisc
    policy adapted from igb(4) in a2ed165f0049.
    
    MFC after:      1 week
    Relnotes:       yes
---
 sys/dev/ixgbe/if_ixv.c      |  49 ++++++++++++++++++-
 sys/dev/ixgbe/if_sriov.c    | 116 ++++++++++++++++++++++++++++++++++----------
 sys/dev/ixgbe/ixgbe.h       |   2 +
 sys/dev/ixgbe/ixgbe_sriov.h |   3 ++
 sys/dev/ixgbe/ixgbe_vf.c    |   2 +-
 5 files changed, 144 insertions(+), 28 deletions(-)

diff --git a/sys/dev/ixgbe/if_ixv.c b/sys/dev/ixgbe/if_ixv.c
index f5af6a53da49..788dd506669d 100644
--- a/sys/dev/ixgbe/if_ixv.c
+++ b/sys/dev/ixgbe/if_ixv.c
@@ -114,6 +114,7 @@ static void     ixv_configure_ivars(struct ixgbe_softc *);
 static void     ixv_if_enable_intr(if_ctx_t);
 static void     ixv_if_disable_intr(if_ctx_t);
 static void     ixv_if_multi_set(if_ctx_t);
+static int      ixv_if_promisc_set(if_ctx_t, int);
 
 static void     ixv_if_register_vlan(if_ctx_t, u16);
 static void     ixv_if_unregister_vlan(if_ctx_t, u16);
@@ -174,6 +175,7 @@ static device_method_t ixv_if_methods[] = {
 	DEVMETHOD(ifdi_queues_free, ixv_if_queues_free),
 	DEVMETHOD(ifdi_update_admin_status, ixv_if_update_admin_status),
 	DEVMETHOD(ifdi_multi_set, ixv_if_multi_set),
+	DEVMETHOD(ifdi_promisc_set, ixv_if_promisc_set),
 	DEVMETHOD(ifdi_mtu_set, ixv_if_mtu_set),
 	DEVMETHOD(ifdi_media_status, ixv_if_media_status),
 	DEVMETHOD(ifdi_media_change, ixv_if_media_change),
@@ -847,6 +849,7 @@ ixv_negotiate_api(struct ixgbe_softc *sc)
 {
 	struct ixgbe_hw *hw = &sc->hw;
 	int mbx_api[] = {
+		ixgbe_mbox_api_13,
 		ixgbe_mbox_api_12,
 		ixgbe_mbox_api_11,
 		ixgbe_mbox_api_10,
@@ -863,6 +866,40 @@ ixv_negotiate_api(struct ixgbe_softc *sc)
 	return (EINVAL);
 } /* ixv_negotiate_api */
 
+static int
+ixv_update_xcast_mode(struct ixgbe_softc *sc, int flags)
+{
+	if_t ifp;
+	int mode;
+
+	ifp = iflib_get_ifp(sc->ctx);
+	if (flags & IFF_PROMISC)
+		mode = IXGBEVF_XCAST_MODE_PROMISC;
+	else if ((flags & IFF_ALLMULTI) != 0 ||
+	    if_llmaddr_count(ifp) > IXGBE_MAX_VF_MC)
+		mode = IXGBEVF_XCAST_MODE_ALLMULTI;
+	else if (if_llmaddr_count(ifp) != 0)
+		mode = IXGBEVF_XCAST_MODE_MULTI;
+	else
+		mode = IXGBEVF_XCAST_MODE_NONE;
+	return (ixgbevf_update_xcast_mode(&sc->hw, mode));
+}
+
+static int
+ixv_if_promisc_set(if_ctx_t ctx, int flags)
+{
+	struct ixgbe_softc *sc;
+	if_t ifp;
+
+	sc = iflib_get_softc(ctx);
+	ifp = iflib_get_ifp(ctx);
+	if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0)
+		return (0);
+	if (ixv_update_xcast_mode(sc, flags) != IXGBE_SUCCESS)
+		return (EOPNOTSUPP);
+	return (0);
+} /* ixv_if_promisc_set */
+
 
 static u_int
 ixv_if_multi_set_cb(void *cb_arg, struct sockaddr_dl *addr, u_int cnt)
@@ -889,7 +926,7 @@ ixv_if_multi_set(if_ctx_t ctx)
 	struct ixgbe_softc *sc = iflib_get_softc(ctx);
 	u8 *update_ptr;
 	if_t ifp = iflib_get_ifp(ctx);
-	int mcnt = 0;
+	int error, mcnt = 0;
 
 	IOCTL_DEBUGOUT("ixv_if_multi_set: begin");
 
@@ -899,6 +936,16 @@ ixv_if_multi_set(if_ctx_t ctx)
 
 	sc->hw.mac.ops.update_mc_addr_list(&sc->hw, update_ptr, mcnt,
 	    ixv_mc_array_itr, true);
+	error = ixv_update_xcast_mode(sc, if_getflags(ifp));
+	if (mcnt > IXGBE_MAX_VF_MC && error != IXGBE_SUCCESS) {
+		if (!sc->vf_mcast_overflow_warned)
+			device_printf(sc->dev,
+			    "PF rejected all-multicast fallback; only %d "
+			    "multicast addresses are active\n",
+			    IXGBE_MAX_VF_MC);
+		sc->vf_mcast_overflow_warned = true;
+	} else if (mcnt <= IXGBE_MAX_VF_MC)
+		sc->vf_mcast_overflow_warned = false;
 } /* ixv_if_multi_set */
 
 /************************************************************************
diff --git a/sys/dev/ixgbe/if_sriov.c b/sys/dev/ixgbe/if_sriov.c
index 2a5fd6a864ee..903ed72d6e2f 100644
--- a/sys/dev/ixgbe/if_sriov.c
+++ b/sys/dev/ixgbe/if_sriov.c
@@ -342,11 +342,11 @@ ixgbe_vf_vlan_record(struct ixgbe_vf *vf, uint16_t tag, bool enable)
 }
 
 static void
-ixgbe_vf_configure_default_vlan(struct ixgbe_softc *sc,
-    struct ixgbe_vf *vf)
+ixgbe_vf_configure_vmolr(struct ixgbe_softc *sc, struct ixgbe_vf *vf)
 {
 	struct ixgbe_hw *hw;
 	uint32_t vmolr, vmvir;
+	uint8_t xcast_mode;
 	uint16_t tag;
 
 	hw = &sc->hw;
@@ -354,11 +354,9 @@ ixgbe_vf_configure_default_vlan(struct ixgbe_softc *sc,
 
 	vmolr = IXGBE_READ_REG(hw, IXGBE_VMOLR(vf->pool));
 
-	/* Do not receive packets that pass inexact filters. */
-	vmolr &= ~(IXGBE_VMOLR_ROMPE | IXGBE_VMOLR_ROPE);
-
-	/* Disable Multicast Promicuous Mode. */
-	vmolr &= ~IXGBE_VMOLR_MPE;
+	vmolr &= ~(IXGBE_VMOLR_UPE | IXGBE_VMOLR_ROMPE |
+	    IXGBE_VMOLR_ROPE | IXGBE_VMOLR_MPE | IXGBE_VMOLR_VPE |
+	    IXGBE_VMOLR_AUPE);
 
 	/* Accept broadcasts. */
 	vmolr |= IXGBE_VMOLR_BAM;
@@ -376,9 +374,28 @@ ixgbe_vf_configure_default_vlan(struct ixgbe_softc *sc,
 		/* Tag all traffic with provided vlan tag. */
 		vmvir = (tag | IXGBE_VMVIR_VLANA_DEFAULT);
 	}
+
+	xcast_mode = vf->xcast_mode;
+	if ((vf->api_ver == IXGBE_API_VER_UNKNOWN ||
+	    vf->api_ver < IXGBE_API_VER_1_2) && vf->num_mc_hashes != 0)
+		xcast_mode = IXGBEVF_XCAST_MODE_MULTI;
+	switch (xcast_mode) {
+	case IXGBEVF_XCAST_MODE_PROMISC:
+		vmolr |= IXGBE_VMOLR_UPE;
+		/* FALLTHROUGH */
+	case IXGBEVF_XCAST_MODE_ALLMULTI:
+		vmolr |= IXGBE_VMOLR_MPE;
+		/* FALLTHROUGH */
+	case IXGBEVF_XCAST_MODE_MULTI:
+		vmolr |= IXGBE_VMOLR_ROMPE;
+		break;
+	case IXGBEVF_XCAST_MODE_NONE:
+	default:
+		break;
+	}
 	IXGBE_WRITE_REG(hw, IXGBE_VMOLR(vf->pool), vmolr);
 	IXGBE_WRITE_REG(hw, IXGBE_VMVIR(vf->pool), vmvir);
-} /* ixgbe_vf_configure_default_vlan */
+} /* ixgbe_vf_configure_vmolr */
 
 static void
 ixgbe_vf_clear_vlans(struct ixgbe_softc *sc, struct ixgbe_vf *vf,
@@ -416,7 +433,7 @@ ixgbe_vf_reset_vlan(struct ixgbe_softc *sc, struct ixgbe_vf *vf,
 		if (error == IXGBE_SUCCESS)
 			ixgbe_vf_vlan_record(vf, vf->default_vlan, true);
 	}
-	ixgbe_vf_configure_default_vlan(sc, vf);
+	ixgbe_vf_configure_vmolr(sc, vf);
 	return (error);
 }
 
@@ -472,15 +489,15 @@ ixgbe_process_vf_reset(struct ixgbe_softc *sc, struct ixgbe_vf *vf)
 	bool rebuild_mta;
 	s32 error;
 
+	rebuild_mta = vf->num_mc_hashes != 0;
+	vf->xcast_mode = IXGBEVF_XCAST_MODE_NONE;
+	vf->num_mc_hashes = 0;
+	bzero(vf->mc_hash, sizeof(vf->mc_hash));
 	error = ixgbe_vf_reset_vlan(sc, vf, true);
 	if (error != IXGBE_SUCCESS)
 		device_printf(sc->dev,
 		    "VF %u default VLAN restore failed: %d\n",
 		    vf->pool, error);
-
-	rebuild_mta = vf->num_mc_hashes != 0;
-	vf->num_mc_hashes = 0;
-	bzero(vf->mc_hash, sizeof(vf->mc_hash));
 	if (rebuild_mta)
 		ixgbe_iov_rebuild_mta(sc);
 
@@ -609,24 +626,21 @@ ixgbe_vf_set_mc_addr(struct ixgbe_softc *sc, struct ixgbe_vf *vf, u32 *msg)
 {
 	u16	*list = (u16*)&msg[1];
 	int	entries;
-	u32	vmolr;
 
-	entries = (msg[0] & IXGBE_VT_MSGINFO_MASK) >> IXGBE_VT_MSGINFO_SHIFT;
+	entries = (msg[0] & IXGBE_VT_MSGINFO_MASK) >>
+	    IXGBE_VT_MSGINFO_SHIFT;
 	entries = min(entries, IXGBE_MAX_VF_MC);
 
-	vmolr = IXGBE_READ_REG(&sc->hw, IXGBE_VMOLR(vf->pool));
-	vmolr &= ~IXGBE_VMOLR_ROMPE;
-	IXGBE_WRITE_REG(&sc->hw, IXGBE_VMOLR(vf->pool), vmolr);
-
 	bzero(vf->mc_hash, sizeof(vf->mc_hash));
 	bcopy(list, vf->mc_hash, entries * sizeof(*list));
 	vf->num_mc_hashes = entries;
+	if (entries != 0 && vf->xcast_mode == IXGBEVF_XCAST_MODE_NONE)
+		vf->xcast_mode = IXGBEVF_XCAST_MODE_MULTI;
+	else if (entries == 0 &&
+	    vf->xcast_mode == IXGBEVF_XCAST_MODE_MULTI)
+		vf->xcast_mode = IXGBEVF_XCAST_MODE_NONE;
 	ixgbe_iov_rebuild_mta(sc);
-
-	if (entries != 0) {
-		vmolr |= IXGBE_VMOLR_ROMPE;
-		IXGBE_WRITE_REG(&sc->hw, IXGBE_VMOLR(vf->pool), vmolr);
-	}
+	ixgbe_vf_configure_vmolr(sc, vf);
 	ixgbe_send_vf_success(sc, vf, msg[0]);
 } /* ixgbe_vf_set_mc_addr */
 
@@ -730,6 +744,8 @@ ixgbe_vf_api_negotiate(struct ixgbe_softc *sc, struct ixgbe_vf *vf,
 	switch (msg[1]) {
 	case IXGBE_API_VER_1_0:
 	case IXGBE_API_VER_1_1:
+	case IXGBE_API_VER_1_2:
+	case IXGBE_API_VER_1_3:
 		vf->api_ver = msg[1];
 		ixgbe_send_vf_success(sc, vf, msg[0]);
 		break;
@@ -740,6 +756,47 @@ ixgbe_vf_api_negotiate(struct ixgbe_softc *sc, struct ixgbe_vf *vf,
 	}
 } /* ixgbe_vf_api_negotiate */
 
+static void
+ixgbe_vf_update_xcast_mode(struct ixgbe_softc *sc, struct ixgbe_vf *vf,
+    uint32_t *msg)
+{
+	struct ixgbe_hw *hw;
+	uint32_t mode;
+
+	hw = &sc->hw;
+	mode = msg[1];
+	switch (vf->api_ver) {
+	case IXGBE_API_VER_1_2:
+		if (mode == IXGBEVF_XCAST_MODE_PROMISC)
+			goto failure;
+		break;
+	case IXGBE_API_VER_1_3:
+		break;
+	default:
+		goto failure;
+	}
+	if (mode > IXGBEVF_XCAST_MODE_PROMISC)
+		goto failure;
+	if (mode > IXGBEVF_XCAST_MODE_MULTI &&
+	    !(vf->flags & IXGBE_VF_ALLOW_PROMISC))
+		goto failure;
+	if (mode == IXGBEVF_XCAST_MODE_PROMISC &&
+	    (hw->mac.type <= ixgbe_mac_82599EB ||
+	    !(IXGBE_READ_REG(hw, IXGBE_FCTRL) & IXGBE_FCTRL_UPE)))
+		goto failure;
+
+	vf->xcast_mode = mode;
+	ixgbe_vf_configure_vmolr(sc, vf);
+	msg[0] &= IXGBE_VT_MSG_MASK;
+	msg[0] |= IXGBE_VT_MSGTYPE_SUCCESS | IXGBE_VT_MSGTYPE_CTS;
+	msg[1] = mode;
+	ixgbe_write_mbx(hw, msg, 2, vf->pool);
+	return;
+
+failure:
+	ixgbe_send_vf_failure(sc, vf, msg[0]);
+} /* ixgbe_vf_update_xcast_mode */
+
 
 static void
 ixgbe_vf_get_queues(struct ixgbe_softc *sc, struct ixgbe_vf *vf,
@@ -824,6 +881,9 @@ ixgbe_process_vf_msg(if_ctx_t ctx, struct ixgbe_vf *vf)
 	case IXGBE_VF_GET_QUEUES:
 		ixgbe_vf_get_queues(sc, vf, msg);
 		break;
+	case IXGBE_VF_UPDATE_XCAST_MODE:
+		ixgbe_vf_update_xcast_mode(sc, vf, msg);
+		break;
 	default:
 		ixgbe_send_vf_failure(sc, vf, msg[0]);
 	}
@@ -982,11 +1042,13 @@ ixgbe_init_vf(struct ixgbe_softc *sc, struct ixgbe_vf *vf)
 	pfmbimr |= IXGBE_VF_BIT(vf->pool);
 	IXGBE_WRITE_REG(hw, IXGBE_PFMBIMR(vf_index), pfmbimr);
 
+	vf->xcast_mode = IXGBEVF_XCAST_MODE_NONE;
+	vf->api_ver = IXGBE_API_VER_UNKNOWN;
+	vf->num_mc_hashes = 0;
+	bzero(vf->mc_hash, sizeof(vf->mc_hash));
 	error = ixgbe_vf_reset_vlan(sc, vf, false);
 	if (error != IXGBE_SUCCESS)
 		return (error);
-	vf->num_mc_hashes = 0;
-	bzero(vf->mc_hash, sizeof(vf->mc_hash));
 
 	if (ixgbe_validate_mac_addr(vf->ether_addr) == 0) {
 		ixgbe_set_rar(&sc->hw, vf->rar_index,
@@ -1114,6 +1176,8 @@ ixgbe_if_iov_vf_add(if_ctx_t ctx, u16 vfnum, const nvlist_t *config)
 	ixgbe_update_max_frame(sc, vf->maximum_frame_size);
 	if (nvlist_get_bool(config, "mac-anti-spoof"))
 		vf->flags |= IXGBE_VF_ANTI_SPOOF;
+	if (nvlist_get_bool(config, "allow-promisc"))
+		vf->flags |= IXGBE_VF_ALLOW_PROMISC;
 
 	if (nvlist_exists_binary(config, "mac-addr")) {
 		mac = nvlist_get_binary(config, "mac-addr", NULL);
diff --git a/sys/dev/ixgbe/ixgbe.h b/sys/dev/ixgbe/ixgbe.h
index 26513a0ba965..330d1a8a2cb3 100644
--- a/sys/dev/ixgbe/ixgbe.h
+++ b/sys/dev/ixgbe/ixgbe.h
@@ -357,6 +357,7 @@ struct ixgbe_vf {
 	uint16_t	num_vlans;
 	uint16_t	default_vlan;
 	uint16_t	api_ver;
+	uint8_t		xcast_mode;
 };
 
 /* Our softc structure */
@@ -397,6 +398,7 @@ struct ixgbe_softc {
 	 * to repopulate it.
 	 */
 	u32			shadow_vfta[IXGBE_VFTA_SIZE];
+	bool			vf_mcast_overflow_warned;
 
 	/* Info about the interface */
 	int			advertise;	/* link speeds */
diff --git a/sys/dev/ixgbe/ixgbe_sriov.h b/sys/dev/ixgbe/ixgbe_sriov.h
index bae3f25f7d71..491a708b6f41 100644
--- a/sys/dev/ixgbe/ixgbe_sriov.h
+++ b/sys/dev/ixgbe/ixgbe_sriov.h
@@ -48,6 +48,7 @@
 #define IXGBE_VF_CAP_VLAN       (1 << 2) /* VF is permitted to join vlans. */
 #define IXGBE_VF_ACTIVE         (1 << 3) /* VF is active. */
 #define IXGBE_VF_ANTI_SPOOF     (1 << 4) /* Enforce source identity. */
+#define IXGBE_VF_ALLOW_PROMISC  (1 << 5) /* VF may request promiscuity. */
 #define IXGBE_VF_INDEX(vmdq)    ((vmdq) / 32)
 #define IXGBE_VF_BIT(vmdq)      (1 << ((vmdq) % 32))
 
@@ -61,6 +62,8 @@
 #define IXGBE_API_VER_1_0	0
 #define IXGBE_API_VER_2_0	1	/* Solaris API.  Not supported. */
 #define IXGBE_API_VER_1_1	2
+#define IXGBE_API_VER_1_2	3
+#define IXGBE_API_VER_1_3	4
 #define IXGBE_API_VER_UNKNOWN	UINT16_MAX
 
 #define IXGBE_NO_VM             0
diff --git a/sys/dev/ixgbe/ixgbe_vf.c b/sys/dev/ixgbe/ixgbe_vf.c
index 89bf5929260d..5f65ee038753 100644
--- a/sys/dev/ixgbe/ixgbe_vf.c
+++ b/sys/dev/ixgbe/ixgbe_vf.c
@@ -411,7 +411,7 @@ s32 ixgbe_update_mc_addr_list_vf(struct ixgbe_hw *hw, u8 *mc_addr_list,
 
 	DEBUGOUT1("MC Addr Count = %d\n", mc_addr_count);
 
-	cnt = (mc_addr_count > 30) ? 30 : mc_addr_count;
+	cnt = min(mc_addr_count, IXGBE_MAX_VF_MC);
 	msgbuf[0] = IXGBE_VF_SET_MULTICAST;
 	msgbuf[0] |= cnt << IXGBE_VT_MSGINFO_SHIFT;