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>
