Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 07 Aug 2026 01:47:47 +0000
From:      Kevin Bowling <kbowling@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: 6404ef10d629 - main - ixgbe: implement VF secondary MAC filters
Message-ID:  <6a753943.3c60b.174df26@gitrepo.freebsd.org>

index | next in thread | raw e-mail

The branch main has been updated by kbowling:

URL: https://cgit.FreeBSD.org/src/commit/?id=6404ef10d62999d6ac16b0fb25bbdcb463b866a2

commit 6404ef10d62999d6ac16b0fb25bbdcb463b866a2
Author:     Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-07-31 12:58:01 +0000
Commit:     Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-08-07 01:42:14 +0000

    ixgbe: implement VF secondary MAC filters
    
    The PF advertises the legacy SET_MACVLAN mailbox request but always
    rejects it. The request installs secondary unicast addresses.
    
    Allocate an owned RAR pool for VF secondary addresses, reserve low
    entries for PF filters, and place VF-primary addresses at the top of
    the usable RAR range. Reject address collisions and cap each VF at
    three secondary filters so one guest cannot exhaust the shared table.
    
    Clear secondary filters on VF or PF reset and on SR-IOV teardown. This
    hardware can anti-spoof only the VF primary source address. Reject
    secondary filters while MAC anti-spoofing is configured, so installing
    them requires an explicit administrative policy choice. Report optional
    filter-table allocation failure without disabling SR-IOV.
    
    Adapt the owned-RAR allocation and reset-cleanup model from igb(4) in
    a2ed165f0049 to DPDK's ixgbe SET_MACVLAN mailbox semantics.
    
    MFC after:      1 week
    Relnotes:       yes
---
 share/man/man4/ix.4         |  10 +++
 sys/dev/ixgbe/if_sriov.c    | 154 ++++++++++++++++++++++++++++++++++++++++++--
 sys/dev/ixgbe/ixgbe.h       |  10 +++
 sys/dev/ixgbe/ixgbe_sriov.h |   2 +
 4 files changed, 171 insertions(+), 5 deletions(-)

diff --git a/share/man/man4/ix.4 b/share/man/man4/ix.4
index fb1d1032c0f1..62fe4e1f9287 100644
--- a/share/man/man4/ix.4
+++ b/share/man/man4/ix.4
@@ -91,6 +91,16 @@ registered by the VF are presented normally to its network stack.
 VLAN filtering remains enabled in the hardware while SR-IOV is active so that
 the PF can enforce VLAN ownership for each VF pool, including when hardware
 VLAN filtering has been disabled on the PF interface.
+.Pp
+Secondary unicast addresses requested by a VF are accepted only when the VF
+is permitted to change its MAC address and
+.Va mac-anti-spoof
+is disabled in its
+.Xr iovctl.conf 5
+configuration.
+The hardware MAC anti-spoofing control can validate only the VF's primary
+source address, so the driver does not silently weaken an enabled policy to
+install secondary addresses.
 .Sh HARDWARE
 The
 .Nm
diff --git a/sys/dev/ixgbe/if_sriov.c b/sys/dev/ixgbe/if_sriov.c
index 903ed72d6e2f..31db2cbb0407 100644
--- a/sys/dev/ixgbe/if_sriov.c
+++ b/sys/dev/ixgbe/if_sriov.c
@@ -167,6 +167,31 @@ ixgbe_vf_mac_changed(struct ixgbe_vf *vf, const uint8_t *mac)
 	return (bcmp(mac, vf->ether_addr, ETHER_ADDR_LEN) != 0);
 }
 
+static bool
+ixgbe_rar_mac_in_use(struct ixgbe_softc *sc, const uint8_t *mac,
+    int excluded_rar)
+{
+	struct ixgbe_hw *hw;
+	uint32_t addr_high, addr_low, rah;
+	int i;
+
+	hw = &sc->hw;
+	addr_low = (uint32_t)mac[0] | ((uint32_t)mac[1] << 8) |
+	    ((uint32_t)mac[2] << 16) | ((uint32_t)mac[3] << 24);
+	addr_high = (uint32_t)mac[4] | ((uint32_t)mac[5] << 8);
+	for (i = 0; i < hw->mac.num_rar_entries; i++) {
+		if (i == excluded_rar)
+			continue;
+		rah = IXGBE_READ_REG(hw, IXGBE_RAH(i));
+		if ((rah & IXGBE_RAH_AV) != 0 &&
+		    (rah & 0xffff) == addr_high &&
+		    IXGBE_READ_REG(hw, IXGBE_RAL(i)) == addr_low)
+			return (true);
+	}
+	return (ixgbe_validate_mac_addr(hw->mac.san_addr) == IXGBE_SUCCESS &&
+	    bcmp(hw->mac.san_addr, mac, ETHER_ADDR_LEN) == 0);
+}
+
 static inline int
 ixgbe_vf_queues(int mode)
 {
@@ -437,6 +462,25 @@ ixgbe_vf_reset_vlan(struct ixgbe_softc *sc, struct ixgbe_vf *vf,
 	return (error);
 }
 
+static void
+ixgbe_vf_clear_mac_filters(struct ixgbe_softc *sc, struct ixgbe_vf *vf,
+    bool clear_hw)
+{
+	struct ixgbe_vf_mac_filter *filter;
+	int i;
+
+	for (i = 0; i < sc->num_vf_mac_filters; i++) {
+		filter = &sc->vf_mac_filters[i];
+		if (!filter->active || filter->pool != vf->pool)
+			continue;
+		if (clear_hw)
+			(void)ixgbe_clear_rar(&sc->hw, filter->rar_index);
+		filter->active = false;
+		bzero(filter->mac, sizeof(filter->mac));
+	}
+	vf->num_mac_filters = 0;
+}
+
 static boolean_t
 ixgbe_vf_frame_size_compatible(struct ixgbe_softc *sc, struct ixgbe_vf *vf)
 {
@@ -501,6 +545,7 @@ ixgbe_process_vf_reset(struct ixgbe_softc *sc, struct ixgbe_vf *vf)
 	if (rebuild_mta)
 		ixgbe_iov_rebuild_mta(sc);
 
+	ixgbe_vf_clear_mac_filters(sc, vf, true);
 	ixgbe_clear_rar(&sc->hw, vf->rar_index);
 	ixgbe_vf_set_anti_spoof(sc, vf);
 	ixgbe_toggle_txdctl(&sc->hw, vf->pool);
@@ -607,6 +652,11 @@ ixgbe_vf_set_mac(struct ixgbe_softc *sc, struct ixgbe_vf *vf, uint32_t *msg)
 		ixgbe_send_vf_failure(sc, vf, msg[0]);
 		return;
 	}
+	if (ixgbe_vf_mac_changed(vf, mac) &&
+	    ixgbe_rar_mac_in_use(sc, mac, vf->rar_index)) {
+		ixgbe_send_vf_failure(sc, vf, msg[0]);
+		return;
+	}
 
 	bcopy(mac, vf->ether_addr, ETHER_ADDR_LEN);
 
@@ -732,7 +782,73 @@ static void
 ixgbe_vf_set_macvlan(struct ixgbe_softc *sc, struct ixgbe_vf *vf,
     uint32_t *msg)
 {
-	//XXX implement this
+	struct ixgbe_vf_mac_filter *filter, *free_filter;
+	struct ixgbe_hw *hw;
+	uint8_t *mac;
+	int i, index;
+
+	hw = &sc->hw;
+	index = IXGBE_VT_MSGINFO(msg[0]);
+	if (index == 0) {
+		ixgbe_vf_clear_mac_filters(sc, vf, true);
+		ixgbe_vf_set_anti_spoof(sc, vf);
+		ixgbe_send_vf_success(sc, vf, msg[0]);
+		return;
+	}
+	if (!(vf->flags & IXGBE_VF_CAP_MAC))
+		goto failure;
+	/* This hardware can anti-spoof only the VF's primary source MAC. */
+	if ((vf->flags & IXGBE_VF_ANTI_SPOOF) != 0)
+		goto failure;
+
+	mac = (uint8_t *)&msg[1];
+	if (ixgbe_validate_mac_addr(mac) != IXGBE_SUCCESS)
+		goto failure;
+	if (index == 1)
+		ixgbe_vf_clear_mac_filters(sc, vf, true);
+	if (bcmp(mac, vf->ether_addr, ETHER_ADDR_LEN) == 0) {
+		ixgbe_vf_set_anti_spoof(sc, vf);
+		ixgbe_send_vf_success(sc, vf, msg[0]);
+		return;
+	}
+
+	free_filter = NULL;
+	for (i = 0; i < sc->num_vf_mac_filters; i++) {
+		filter = &sc->vf_mac_filters[i];
+		if (!filter->active) {
+			if (free_filter == NULL)
+				free_filter = filter;
+			continue;
+		}
+		if (bcmp(filter->mac, mac, ETHER_ADDR_LEN) != 0)
+			continue;
+		if (filter->pool != vf->pool)
+			goto failure;
+		ixgbe_send_vf_success(sc, vf, msg[0]);
+		return;
+	}
+	if (vf->num_mac_filters >= IXGBE_MAX_VF_MAC_FILTERS ||
+	    free_filter == NULL)
+		goto failure;
+
+	/* Reject collisions with PF, VF-primary, or other reserved RARs. */
+	if (ixgbe_rar_mac_in_use(sc, mac, -1))
+		goto failure;
+
+	if (ixgbe_set_rar(hw, free_filter->rar_index, mac, vf->pool,
+	    true) != IXGBE_SUCCESS)
+		goto failure;
+	free_filter->active = true;
+	free_filter->pool = vf->pool;
+	bcopy(mac, free_filter->mac, ETHER_ADDR_LEN);
+	vf->num_mac_filters++;
+	/* MAC anti-spoofing accepts only the primary address on this family. */
+	ixgbe_vf_set_anti_spoof(sc, vf);
+	ixgbe_send_vf_success(sc, vf, msg[0]);
+	return;
+
+failure:
+	ixgbe_vf_set_anti_spoof(sc, vf);
 	ixgbe_send_vf_failure(sc, vf, msg[0]);
 } /* ixgbe_vf_set_macvlan */
 
@@ -924,7 +1040,7 @@ int
 ixgbe_if_iov_init(if_ctx_t ctx, u16 num_vfs, const nvlist_t *config)
 {
 	struct ixgbe_softc *sc;
-	int retval = 0;
+	int i, num_filters, retval = 0;
 
 	sc = iflib_get_softc(ctx);
 	sc->iov_mode = IXGBE_NO_VM;
@@ -961,6 +1077,22 @@ ixgbe_if_iov_init(if_ctx_t ctx, u16 num_vfs, const nvlist_t *config)
 		retval = ENOMEM;
 		goto err_init_iov;
 	}
+	num_filters = sc->hw.mac.num_rar_entries - num_vfs - 1 -
+	    IXGBE_MAX_PF_MAC_FILTERS;
+	if (num_filters > 0) {
+		sc->vf_mac_filters = mallocarray(num_filters,
+		    sizeof(*sc->vf_mac_filters), M_IXGBE_SRIOV,
+		    M_NOWAIT | M_ZERO);
+		if (sc->vf_mac_filters != NULL) {
+			sc->num_vf_mac_filters = num_filters;
+			for (i = 0; i < num_filters; i++)
+				sc->vf_mac_filters[i].rar_index =
+				    IXGBE_MAX_PF_MAC_FILTERS + 1 + i;
+		} else
+			device_printf(sc->dev,
+			    "could not allocate VF secondary MAC filters; "
+			    "SET_MACVLAN will be unavailable\n");
+	}
 
 	sc->num_vfs = num_vfs;
 	ixgbe_init_mbx_params_pf(&sc->hw);
@@ -1004,6 +1136,7 @@ ixgbe_if_iov_uninit(if_ctx_t ctx)
 	for (i = 0; i < sc->num_vfs; i++) {
 		if (!(sc->vfs[i].flags & IXGBE_VF_ACTIVE))
 			continue;
+		ixgbe_vf_clear_mac_filters(sc, &sc->vfs[i], true);
 		sc->vfs[i].flags &= ~IXGBE_VF_ANTI_SPOOF;
 		ixgbe_vf_set_anti_spoof(sc, &sc->vfs[i]);
 	}
@@ -1016,6 +1149,9 @@ ixgbe_if_iov_uninit(if_ctx_t ctx)
 
 	sc->num_vfs = 0;
 	ixgbe_iov_rebuild_mta(sc);
+	free(sc->vf_mac_filters, M_IXGBE_SRIOV);
+	sc->vf_mac_filters = NULL;
+	sc->num_vf_mac_filters = 0;
 	free(sc->vfs, M_IXGBE_SRIOV);
 	sc->vfs = NULL;
 	sc->feat_en &= ~IXGBE_FEATURE_SRIOV;
@@ -1046,6 +1182,7 @@ ixgbe_init_vf(struct ixgbe_softc *sc, struct ixgbe_vf *vf)
 	vf->api_ver = IXGBE_API_VER_UNKNOWN;
 	vf->num_mc_hashes = 0;
 	bzero(vf->mc_hash, sizeof(vf->mc_hash));
+	ixgbe_vf_clear_mac_filters(sc, vf, false);
 	error = ixgbe_vf_reset_vlan(sc, vf, false);
 	if (error != IXGBE_SUCCESS)
 		return (error);
@@ -1145,6 +1282,7 @@ ixgbe_if_iov_vf_add(if_ctx_t ctx, u16 vfnum, const nvlist_t *config)
 	struct ixgbe_softc *sc;
 	struct ixgbe_vf *vf;
 	const void *mac;
+	uint8_t mac_addr[ETHER_ADDR_LEN];
 	uint64_t configured_vlan;
 	uint16_t vlan;
 	s32 error;
@@ -1157,6 +1295,7 @@ ixgbe_if_iov_vf_add(if_ctx_t ctx, u16 vfnum, const nvlist_t *config)
 	vf = &sc->vfs[vfnum];
 	if (vf->flags & IXGBE_VF_ACTIVE)
 		return (EBUSY);
+	bzero(vf, sizeof(*vf));
 
 	configured_vlan = nvlist_get_number(config, "vlan");
 	if (configured_vlan > VF_VLAN_TRUNK)
@@ -1169,8 +1308,8 @@ ixgbe_if_iov_vf_add(if_ctx_t ctx, u16 vfnum, const nvlist_t *config)
 
 	vf->pool = vfnum;
 
-	/* RAR[0] is used by the PF so use vfnum + 1 for VF RAR. */
-	vf->rar_index = vfnum + 1;
+	/* Allocate VF-primary RARs from the top, away from PF filters. */
+	vf->rar_index = sc->hw.mac.num_rar_entries - (vfnum + 1);
 	vf->default_vlan = vlan;
 	vf->maximum_frame_size = ETHER_MAX_LEN;
 	ixgbe_update_max_frame(sc, vf->maximum_frame_size);
@@ -1181,7 +1320,12 @@ ixgbe_if_iov_vf_add(if_ctx_t ctx, u16 vfnum, const nvlist_t *config)
 
 	if (nvlist_exists_binary(config, "mac-addr")) {
 		mac = nvlist_get_binary(config, "mac-addr", NULL);
-		bcopy(mac, vf->ether_addr, ETHER_ADDR_LEN);
+		bcopy(mac, mac_addr, sizeof(mac_addr));
+		if (ixgbe_validate_mac_addr(mac_addr) != IXGBE_SUCCESS)
+			return (EINVAL);
+		if (ixgbe_rar_mac_in_use(sc, mac_addr, vf->rar_index))
+			return (EADDRINUSE);
+		bcopy(mac_addr, vf->ether_addr, ETHER_ADDR_LEN);
 		if (nvlist_get_bool(config, "allow-set-mac"))
 			vf->flags |= IXGBE_VF_CAP_MAC;
 	} else
diff --git a/sys/dev/ixgbe/ixgbe.h b/sys/dev/ixgbe/ixgbe.h
index 330d1a8a2cb3..4db140249006 100644
--- a/sys/dev/ixgbe/ixgbe.h
+++ b/sys/dev/ixgbe/ixgbe.h
@@ -345,6 +345,13 @@ struct ix_tx_queue {
 
 #define IXGBE_MAX_VF_MC	30	/* Max number of multicast entries */
 
+struct ixgbe_vf_mac_filter {
+	uint16_t	rar_index;
+	uint16_t	pool;
+	bool		active;
+	uint8_t		mac[ETHER_ADDR_LEN];
+};
+
 struct ixgbe_vf {
 	u_int		pool;
 	u_int		rar_index;
@@ -354,6 +361,7 @@ struct ixgbe_vf {
 	uint16_t	mc_hash[IXGBE_MAX_VF_MC];
 	uint32_t	vlans[IXGBE_VFTA_SIZE];
 	uint16_t	num_mc_hashes;
+	uint16_t	num_mac_filters;
 	uint16_t	num_vlans;
 	uint16_t	default_vlan;
 	uint16_t	api_ver;
@@ -444,6 +452,8 @@ struct ixgbe_softc {
 	int			num_vfs;
 	int			pool;
 	struct ixgbe_vf		*vfs;
+	struct ixgbe_vf_mac_filter *vf_mac_filters;
+	int			num_vf_mac_filters;
 	bool			iov_mta_valid;
 	bool			iov_vfta_valid;
 	bool			iov_vlan_promisc;
diff --git a/sys/dev/ixgbe/ixgbe_sriov.h b/sys/dev/ixgbe/ixgbe_sriov.h
index 491a708b6f41..84b5e25a8c3c 100644
--- a/sys/dev/ixgbe/ixgbe_sriov.h
+++ b/sys/dev/ixgbe/ixgbe_sriov.h
@@ -69,6 +69,8 @@
 #define IXGBE_NO_VM             0
 #define IXGBE_32_VM             32
 #define IXGBE_64_VM             64
+#define IXGBE_MAX_PF_MAC_FILTERS 15
+#define IXGBE_MAX_VF_MAC_FILTERS 3
 
 int  ixgbe_if_iov_vf_add(if_ctx_t, u16, const nvlist_t *);
 int  ixgbe_if_iov_init(if_ctx_t, u16, const nvlist_t *);


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a753943.3c60b.174df26>