aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--share/man/man4/ix.410
-rw-r--r--sys/dev/ixgbe/if_sriov.c154
-rw-r--r--sys/dev/ixgbe/ixgbe.h10
-rw-r--r--sys/dev/ixgbe/ixgbe_sriov.h2
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 *);