Page MenuHomeFreeBSD

D58776.id183852.diff
No OneTemporary

D58776.id183852.diff

diff --git a/share/man/man4/rtnetlink.4 b/share/man/man4/rtnetlink.4
--- a/share/man/man4/rtnetlink.4
+++ b/share/man/man4/rtnetlink.4
@@ -22,7 +22,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd July 28, 2026
+.Dd August 10, 2026
.Dt RTNETLINK 4
.Os
.Sh NAME
@@ -325,7 +325,17 @@
ifi_index interface index
IFLA_IFNAME interface name
IFLA_ALT_IFNAME interface name
+IFLA_EXT_MASK extended information selection bitmap
.Ed
+.Pp
+Setting the
+.Dv RTEXT_FILTER_VF
+bit in
+.Dv IFLA_EXT_MASK
+requests SR-IOV VF status.
+The query is opt-in because obtaining status can require entering the PF
+driver.
+VF status is not included in unsolicited link notifications.
.Ss TLVs
.Bl -tag -width indent
.It Dv IFLA_ADDRESS
@@ -340,6 +350,113 @@
(uint32_t) (readonly) Interface index.
.It Dv IFLA_MASTER
(uint32_t) Parent interface index.
+.It Dv IFLA_NUM_VF
+(uint32_t) (readonly) Number of active VFs.
+This attribute is returned with a successful
+.Dv RTEXT_FILTER_VF
+query.
+.It Dv IFLA_FREEBSD
+(nested) Local interface attributes.
+When requested,
+.Dv IFLAF_VF_STATUS
+contains the following nested attributes:
+.Bd -literal -offset indent -compact
+IFLAF_VFS_ERROR (uint32_t) errno if the PF query failed
+IFLAF_VFS_PF_LINK_STATE (uint8_t) IFLAF_VF_LINK_*
+IFLAF_VFS_PF_LINK_SPEED (uint64_t) bits per second
+IFLAF_VFS_VF (nested) IFLAF_VF_*; repeated
+.Ed
+.Pp
+If the PF status query fails,
+.Dv IFLAF_VFS_ERROR
+contains the errno value and the other status attributes are omitted.
+.Pp
+Each
+.Dv IFLAF_VFS_VF
+may contain the following attributes:
+.Bd -literal -offset indent -compact
+IFLAF_VF_INDEX (uint32_t) PF-local VF index
+IFLAF_VF_CONFIGURED (bool) PF accepted configuration
+IFLAF_VF_INITIALIZED (bool) VF handshake completed
+IFLAF_VF_MAC (binary) PF-known primary MAC address
+IFLAF_VF_VLAN_MODE (uint8_t) IFLAF_VF_VLAN_*
+IFLAF_VF_VLAN (uint16_t) access VLAN identifier
+IFLAF_VF_VLAN_COUNT (uint32_t) explicit VLAN filters
+IFLAF_VF_VLAN_LIMIT (uint32_t) explicit VLAN-filter limit
+IFLAF_VF_NUM_QUEUES (uint32_t) allocated RX/TX queue pairs
+IFLAF_VF_ALLOW_SET_MAC (bool) administrative permission
+IFLAF_VF_ALLOW_SET_VLAN (bool) administrative permission
+IFLAF_VF_MAC_ANTI_SPOOF (bool) MAC anti-spoofing enabled
+IFLAF_VF_ALLOW_PROMISC (bool) administrative permission
+IFLAF_VF_TRAFFIC_ENABLED (bool) VF traffic enabled
+IFLAF_VF_MDD_BLOCKED (bool) malicious-driver block active
+IFLAF_VF_QUARANTINED (bool) persistent quarantine active
+IFLAF_VF_API_VERSION (string) negotiated mailbox API
+IFLAF_VF_LINK_STATE_POLICY (uint8_t) IFLAF_VF_LINK_*
+IFLAF_VF_DRIVER (nested) driver namespace; repeated
+.Ed
+.Pp
+.Dv IFLAF_VF_INDEX
+is required in every list entry.
+Other fields are optional and are omitted when the PF driver cannot observe
+them; omission does not mean false or zero.
+Boolean attributes contain a native Netlink boolean value.
+.Dv IFLAF_VF_CONFIGURED
+means that the PF accepted the VF configuration, while
+.Dv IFLAF_VF_INITIALIZED
+means that the VF completed its driver or mailbox handshake since its last
+reset.
+.Pp
+.Dv IFLAF_VF_NUM_QUEUES
+is the number of allocated symmetric RX/TX queue pairs, not necessarily the
+number currently used by the VF driver.
+Access VLAN mode means that the PF imposes
+.Dv IFLAF_VF_VLAN ;
+trunk mode means that no access VLAN is imposed and does not promise unlimited
+filter capacity.
+.Dv IFLAF_VF_VLAN_COUNT
+counts explicit filters recorded by the PF and excludes implicit untagged and
+priority-tag membership.
+The permission attributes describe requests the VF may make, not requests it
+has made.
+The top-level PF link state and speed are values normally advertised to VFs,
+not evidence that a VF driver is operational.
+.Pp
+Link-state values are:
+.Bd -literal -offset indent -compact
+IFLAF_VF_LINK_UNKNOWN state is unavailable
+IFLAF_VF_LINK_DOWN link is forced or observed down
+IFLAF_VF_LINK_UP link is forced or observed up
+IFLAF_VF_LINK_AUTO VF follows PF link state
+.Ed
+.Pp
+VLAN-mode values are:
+.Bd -literal -offset indent -compact
+IFLAF_VF_VLAN_UNKNOWN mode is unavailable
+IFLAF_VF_VLAN_ACCESS PF imposes an access VLAN
+IFLAF_VF_VLAN_TRUNK no access VLAN is imposed
+.Ed
+.Pp
+Driver-specific data is returned under
+.Dv IFLAF_VF_DRIVER ,
+which may be repeated directly in a VF record.
+Each driver object contains:
+.Bd -literal -offset indent -compact
+IFLAF_VFD_NAME (string) stable namespace name
+IFLAF_VFD_VERSION (uint32_t) namespace schema version
+IFLAF_VFD_FIELD (nested) namespace field; repeated
+.Ed
+.Pp
+Each field contains its namespace-local name in
+.Dv IFLAF_VFDF_NAME
+and exactly one of
+.Dv IFLAF_VFDF_BOOL ,
+.Dv IFLAF_VFDF_NUMBER ,
+.Dv IFLAF_VFDF_STRING ,
+or
+.Dv IFLAF_VFDF_BINARY .
+The namespace defines the types and meanings of its fields.
+Consumers must ignore unknown namespaces and fields.
.It Dv IFLA_LINKINFO
(nested) Interface type-specific attributes:
.Bd -literal -offset indent -compact
diff --git a/sys/net/if.c b/sys/net/if.c
--- a/sys/net/if.c
+++ b/sys/net/if.c
@@ -81,6 +81,7 @@
#include <net/if_strings.h>
#include <net/if_types.h>
#include <net/if_var.h>
+#include <net/if_vf_status.h>
#include <net/if_media.h>
#include <net/if_mib.h>
#include <net/if_private.h>
@@ -124,6 +125,12 @@
uint32_t buffer; /* (void *) */
};
+struct ifreq_nv_req32 {
+ u_int buf_length;
+ u_int length;
+ uint32_t buffer; /* (void *) */
+};
+
/*
* Interface request structure used for socket
* ioctl's. All interface ioctl's must have parameter
@@ -148,6 +155,7 @@
int ifru_cap[2];
u_int ifru_fib;
u_char ifru_vlan_pcp;
+ struct ifreq_nv_req32 ifru_nv;
} ifr_ifru;
};
CTASSERT(sizeof(struct ifreq) == sizeof(struct ifreq32));
@@ -2237,6 +2245,74 @@
ifrup->ifr.ifr_ifru.ifru_buffer.length = len;
}
+static void *
+ifr_nv_get_buffer(void *data)
+{
+ union ifreq_union *ifrup;
+
+ ifrup = data;
+#ifdef COMPAT_FREEBSD32
+ if (SV_CURPROC_FLAG(SV_ILP32))
+ return ((void *)(uintptr_t)
+ ifrup->ifr32.ifr_ifru.ifru_nv.buffer);
+#endif
+ return (ifrup->ifr.ifr_ifru.ifru_nv.buffer);
+}
+
+static void
+ifr_nv_set_buffer_null(void *data)
+{
+ union ifreq_union *ifrup;
+
+ ifrup = data;
+#ifdef COMPAT_FREEBSD32
+ if (SV_CURPROC_FLAG(SV_ILP32))
+ ifrup->ifr32.ifr_ifru.ifru_nv.buffer = 0;
+ else
+#endif
+ ifrup->ifr.ifr_ifru.ifru_nv.buffer = NULL;
+}
+
+static u_int
+ifr_nv_get_buf_length(void *data)
+{
+ union ifreq_union *ifrup;
+
+ ifrup = data;
+#ifdef COMPAT_FREEBSD32
+ if (SV_CURPROC_FLAG(SV_ILP32))
+ return (ifrup->ifr32.ifr_ifru.ifru_nv.buf_length);
+#endif
+ return (ifrup->ifr.ifr_ifru.ifru_nv.buf_length);
+}
+
+static u_int
+ifr_nv_get_length(void *data)
+{
+ union ifreq_union *ifrup;
+
+ ifrup = data;
+#ifdef COMPAT_FREEBSD32
+ if (SV_CURPROC_FLAG(SV_ILP32))
+ return (ifrup->ifr32.ifr_ifru.ifru_nv.length);
+#endif
+ return (ifrup->ifr.ifr_ifru.ifru_nv.length);
+}
+
+static void
+ifr_nv_set_length(void *data, u_int len)
+{
+ union ifreq_union *ifrup;
+
+ ifrup = data;
+#ifdef COMPAT_FREEBSD32
+ if (SV_CURPROC_FLAG(SV_ILP32))
+ ifrup->ifr32.ifr_ifru.ifru_nv.length = len;
+ else
+#endif
+ ifrup->ifr.ifr_ifru.ifru_nv.length = len;
+}
+
void *
ifr_data_get_ptr(void *ifrp)
{
@@ -2336,6 +2412,161 @@
}
}
+struct if_vf_status *
+if_vf_status_alloc(uint32_t num_vfs)
+{
+ struct if_vf_status *status;
+ size_t size;
+
+ if (num_vfs > (SIZE_MAX - sizeof(struct if_vf_status)) /
+ sizeof(struct if_vf_info))
+ return (NULL);
+ size = sizeof(struct if_vf_status) +
+ num_vfs * sizeof(struct if_vf_info);
+ status = malloc(size, M_IFNET, M_WAITOK | M_ZERO);
+ status->num_vfs = num_vfs;
+ return (status);
+}
+
+void
+if_vf_status_free(struct if_vf_status *status)
+{
+ struct if_vf_ext_field *field;
+ struct if_vf_extension *extension;
+ uint32_t i, j;
+
+ if (status == NULL)
+ return;
+ for (i = 0; i < status->num_vfs; i++) {
+ for (j = 0; j < status->vfs[i].num_extensions; j++) {
+ extension = &status->vfs[i].extensions[j];
+ for (uint32_t k = 0; k < extension->num_fields; k++) {
+ field = &extension->fields[k];
+ if (field->type == IFVF_EXT_STRING)
+ free(field->value.string, M_IFNET);
+ else if (field->type == IFVF_EXT_BINARY)
+ free(field->value.binary.data, M_IFNET);
+ }
+ free(extension->fields, M_IFNET);
+ }
+ free(status->vfs[i].extensions, M_IFNET);
+ }
+
+ free(status, M_IFNET);
+}
+
+struct if_vf_extension *
+if_vf_status_add_extension(struct if_vf_info *vf, const char *name,
+ uint32_t version, uint32_t num_fields)
+{
+ struct if_vf_extension *extensions, *extension;
+ uint32_t count;
+
+ if (name == NULL || vf->num_extensions == UINT32_MAX ||
+ (size_t)num_fields > SIZE_MAX / sizeof(*extension->fields) ||
+ (size_t)vf->num_extensions + 1 >
+ SIZE_MAX / sizeof(*extensions))
+ return (NULL);
+ count = vf->num_extensions + 1;
+ extensions = mallocarray(count, sizeof(*extensions), M_IFNET,
+ M_WAITOK | M_ZERO);
+ if (vf->num_extensions != 0) {
+ memcpy(extensions, vf->extensions,
+ vf->num_extensions * sizeof(*extensions));
+ free(vf->extensions, M_IFNET);
+ }
+ vf->extensions = extensions;
+ vf->num_extensions = count;
+ extension = &extensions[count - 1];
+ extension->name = name;
+ extension->version = version;
+ extension->num_fields = num_fields;
+ if (num_fields != 0) {
+ extension->fields = mallocarray(num_fields,
+ sizeof(*extension->fields), M_IFNET, M_WAITOK | M_ZERO);
+ }
+ return (extension);
+}
+
+static struct if_vf_ext_field *
+if_vf_extension_field(struct if_vf_extension *extension, uint32_t index,
+ const char *name, enum if_vf_ext_type type)
+{
+ struct if_vf_ext_field *field;
+
+ KASSERT(extension != NULL && index < extension->num_fields,
+ ("invalid VF extension field"));
+ KASSERT(name != NULL, ("VF extension field without a name"));
+ field = &extension->fields[index];
+ KASSERT(field->type == 0, ("VF extension field initialized twice"));
+ field->name = name;
+ field->type = type;
+ return (field);
+}
+
+void
+if_vf_extension_set_bool(struct if_vf_extension *extension, uint32_t index,
+ const char *name, bool value)
+{
+ struct if_vf_ext_field *field;
+
+ field = if_vf_extension_field(extension, index, name, IFVF_EXT_BOOL);
+ field->value.boolean = value;
+}
+
+void
+if_vf_extension_set_number(struct if_vf_extension *extension, uint32_t index,
+ const char *name, uint64_t value)
+{
+ struct if_vf_ext_field *field;
+
+ field = if_vf_extension_field(extension, index, name, IFVF_EXT_NUMBER);
+ field->value.number = value;
+}
+
+void
+if_vf_extension_set_string(struct if_vf_extension *extension, uint32_t index,
+ const char *name, const char *value)
+{
+ struct if_vf_ext_field *field;
+
+ KASSERT(value != NULL, ("NULL VF extension string"));
+ field = if_vf_extension_field(extension, index, name, IFVF_EXT_STRING);
+ field->value.string = strdup(value, M_IFNET);
+}
+
+void
+if_vf_extension_set_binary(struct if_vf_extension *extension, uint32_t index,
+ const char *name, const void *value, uint32_t length)
+{
+ struct if_vf_ext_field *field;
+
+ KASSERT(value != NULL && length != 0, ("empty VF extension binary"));
+ field = if_vf_extension_field(extension, index, name, IFVF_EXT_BINARY);
+ field->value.binary.data = malloc(length, M_IFNET, M_WAITOK);
+ memcpy(field->value.binary.data, value, length);
+ field->value.binary.length = length;
+}
+
+int
+if_get_vf_status(if_t ifp, struct if_vf_status **statusp)
+{
+ int error;
+
+ *statusp = NULL;
+ if (ifp->if_vf_status == NULL)
+ return (EOPNOTSUPP);
+ error = ifp->if_vf_status(ifp, statusp);
+ if (error != 0) {
+ if_vf_status_free(*statusp);
+ *statusp = NULL;
+ return (error);
+ }
+ if (*statusp == NULL)
+ return (EBADMSG);
+ return (0);
+}
+
/*
* Hardware specific interface ioctls.
*/
@@ -2396,14 +2627,14 @@
error = EDOOFUS;
break;
}
- if (nvbuflen > ifr->ifr_cap_nv.buf_length) {
- ifr->ifr_cap_nv.length = nvbuflen;
- ifr->ifr_cap_nv.buffer = NULL;
+ if (nvbuflen > ifr_nv_get_buf_length(data)) {
+ ifr_nv_set_length(data, nvbuflen);
+ ifr_nv_set_buffer_null(data);
error = EFBIG;
break;
}
- ifr->ifr_cap_nv.length = nvbuflen;
- error = copyout(buf, ifr->ifr_cap_nv.buffer, nvbuflen);
+ ifr_nv_set_length(data, nvbuflen);
+ error = copyout(buf, ifr_nv_get_buffer(data), nvbuflen);
break;
}
free(buf, M_NVLIST);
@@ -2563,16 +2794,16 @@
return (EOPNOTSUPP);
if ((ifp->if_capabilities & IFCAP_NV) == 0)
return (EINVAL);
- if (ifr->ifr_cap_nv.length > IFR_CAP_NV_MAXBUFSIZE)
+ if (ifr_nv_get_length(data) > IFR_CAP_NV_MAXBUFSIZE)
return (EINVAL);
nvcap = NULL;
- buf = malloc(ifr->ifr_cap_nv.length, M_TEMP, M_WAITOK);
+ buf = malloc(ifr_nv_get_length(data), M_TEMP, M_WAITOK);
for (;;) {
- error = copyin(ifr->ifr_cap_nv.buffer, buf,
- ifr->ifr_cap_nv.length);
+ error = copyin(ifr_nv_get_buffer(data), buf,
+ ifr_nv_get_length(data));
if (error != 0)
break;
- nvcap = nvlist_unpack(buf, ifr->ifr_cap_nv.length, 0);
+ nvcap = nvlist_unpack(buf, ifr_nv_get_length(data), 0);
if (nvcap == NULL) {
error = EINVAL;
break;
@@ -4862,6 +5093,12 @@
ifp->if_ioctl = ioctl_fn;
}
+void
+if_setvfstatusfn(if_t ifp, if_vf_status_fn_t vf_status_fn)
+{
+ ifp->if_vf_status = vf_status_fn;
+}
+
void
if_setoutputfn(if_t ifp, if_output_fn_t output_fn)
{
diff --git a/sys/net/if_private.h b/sys/net/if_private.h
--- a/sys/net/if_private.h
+++ b/sys/net/if_private.h
@@ -127,6 +127,7 @@
void (*if_bridge_linkstate)(struct ifnet *ifp);
if_start_fn_t if_start; /* initiate output routine */
if_ioctl_fn_t if_ioctl; /* ioctl routine */
+ if_vf_status_fn_t if_vf_status; /* snapshot SR-IOV VF state */
if_init_fn_t if_init; /* Init routine */
int (*if_resolvemulti) /* validate/resolve multicast */
(struct ifnet *, struct sockaddr **, struct sockaddr *);
diff --git a/sys/net/if_var.h b/sys/net/if_var.h
--- a/sys/net/if_var.h
+++ b/sys/net/if_var.h
@@ -131,6 +131,8 @@
typedef int (*if_transmit_fn_t)(if_t, struct mbuf *);
typedef uint64_t (*if_get_counter_t)(if_t, ift_counter);
typedef void (*if_reassign_fn_t)(if_t, struct vnet *, char *);
+struct if_vf_status;
+typedef int (*if_vf_status_fn_t)(if_t, struct if_vf_status **);
typedef int (*if_spdadd_fn_t)(if_t ifp, void *sp, void *inp, void **priv);
typedef int (*if_spddel_fn_t)(if_t ifp, void *sp, void *priv);
typedef int (*if_sa_newkey_fn_t)(if_t ifp, void *sav, u_int drv_spi,
@@ -727,6 +729,7 @@
void if_setinputfn(if_t ifp, if_input_fn_t);
if_input_fn_t if_getinputfn(if_t ifp);
void if_setioctlfn(if_t ifp, if_ioctl_fn_t);
+void if_setvfstatusfn(if_t ifp, if_vf_status_fn_t);
void if_setoutputfn(if_t ifp, if_output_fn_t);
void if_setstartfn(if_t ifp, if_start_fn_t);
if_start_fn_t if_getstartfn(if_t ifp);
@@ -763,6 +766,7 @@
size_t ifr_buffer_get_length(void *data);
int ifhwioctl(u_long, if_t, caddr_t, struct thread *);
+int if_get_vf_status(if_t, struct if_vf_status **);
#ifdef DEVICE_POLLING
enum poll_cmd { POLL_ONLY, POLL_AND_CHECK_STATUS };
diff --git a/sys/net/if_vf_status.h b/sys/net/if_vf_status.h
new file mode 100644
--- /dev/null
+++ b/sys/net/if_vf_status.h
@@ -0,0 +1,134 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Kevin Bowling <kbowling@FreeBSD.org>
+ */
+
+#ifndef _NET_IF_VF_STATUS_H_
+#define _NET_IF_VF_STATUS_H_
+
+#include <sys/types.h>
+#ifndef _KERNEL
+#include <stdbool.h>
+#endif
+
+#include <net/ethernet.h>
+
+/*
+ * Kernel-internal representation of SR-IOV VF state. The field mask makes
+ * absence distinct from false or zero; providers omit facts they cannot
+ * observe. This is a transport-neutral snapshot, not a userspace ABI.
+ */
+#define IFVF_F_CONFIGURED (1ULL << 0)
+#define IFVF_F_INITIALIZED (1ULL << 1)
+#define IFVF_F_MAC (1ULL << 2)
+#define IFVF_F_VLAN_MODE (1ULL << 3)
+#define IFVF_F_VLAN (1ULL << 4)
+#define IFVF_F_VLAN_COUNT (1ULL << 5)
+#define IFVF_F_VLAN_LIMIT (1ULL << 6)
+#define IFVF_F_NUM_QUEUES (1ULL << 7)
+#define IFVF_F_ALLOW_SET_MAC (1ULL << 8)
+#define IFVF_F_ALLOW_SET_VLAN (1ULL << 9)
+#define IFVF_F_MAC_ANTI_SPOOF (1ULL << 10)
+#define IFVF_F_ALLOW_PROMISC (1ULL << 11)
+#define IFVF_F_TRAFFIC_ENABLED (1ULL << 12)
+#define IFVF_F_MDD_BLOCKED (1ULL << 13)
+#define IFVF_F_QUARANTINED (1ULL << 14)
+#define IFVF_F_API_VERSION (1ULL << 15)
+#define IFVF_F_LINK_STATE_POLICY (1ULL << 16)
+
+enum if_vf_vlan_mode {
+ IFVF_VLAN_UNKNOWN = 0,
+ IFVF_VLAN_ACCESS,
+ IFVF_VLAN_TRUNK,
+};
+
+enum if_vf_link_state {
+ IFVF_LINK_UNKNOWN = 0,
+ IFVF_LINK_DOWN,
+ IFVF_LINK_UP,
+ IFVF_LINK_AUTO,
+};
+
+/*
+ * Driver-specific status retains driver ownership of its ontology. A
+ * stable namespace defines the names, types, and meanings of its fields.
+ * The common layer only transports scalar values and ignores namespaces or
+ * fields it does not understand. Namespace and field names must remain
+ * valid for the lifetime of the snapshot.
+ */
+enum if_vf_ext_type {
+ IFVF_EXT_BOOL = 1,
+ IFVF_EXT_NUMBER,
+ IFVF_EXT_STRING,
+ IFVF_EXT_BINARY,
+};
+
+struct if_vf_ext_field {
+ const char *name;
+ enum if_vf_ext_type type;
+ union {
+ bool boolean;
+ uint64_t number;
+ char *string;
+ struct {
+ void *data;
+ uint32_t length;
+ } binary;
+ } value;
+};
+
+struct if_vf_extension {
+ const char *name;
+ uint32_t version;
+ uint32_t num_fields;
+ struct if_vf_ext_field *fields;
+};
+
+#define IFVF_API_VERSION_MAX 16
+
+struct if_vf_info {
+ uint64_t fields;
+ uint32_t index;
+ uint32_t vlan_count;
+ uint32_t vlan_limit;
+ uint32_t num_queues;
+ uint16_t vlan;
+ uint8_t mac[ETHER_ADDR_LEN];
+ enum if_vf_vlan_mode vlan_mode;
+ enum if_vf_link_state link_state_policy;
+ bool configured;
+ bool initialized;
+ bool allow_set_mac;
+ bool allow_set_vlan;
+ bool mac_anti_spoof;
+ bool allow_promisc;
+ bool traffic_enabled;
+ bool mdd_blocked;
+ bool quarantined;
+ char api_version[IFVF_API_VERSION_MAX];
+ uint32_t num_extensions;
+ struct if_vf_extension *extensions;
+};
+
+struct if_vf_status {
+ uint32_t num_vfs;
+ struct if_vf_info vfs[];
+};
+
+#ifdef _KERNEL
+struct if_vf_status *if_vf_status_alloc(uint32_t);
+void if_vf_status_free(struct if_vf_status *);
+struct if_vf_extension *if_vf_status_add_extension(struct if_vf_info *,
+ const char *, uint32_t, uint32_t);
+void if_vf_extension_set_bool(struct if_vf_extension *, uint32_t,
+ const char *, bool);
+void if_vf_extension_set_number(struct if_vf_extension *, uint32_t,
+ const char *, uint64_t);
+void if_vf_extension_set_string(struct if_vf_extension *, uint32_t,
+ const char *, const char *);
+void if_vf_extension_set_binary(struct if_vf_extension *, uint32_t,
+ const char *, const void *, uint32_t);
+#endif
+
+#endif /* _NET_IF_VF_STATUS_H_ */
diff --git a/sys/net/ifdi_if.m b/sys/net/ifdi_if.m
--- a/sys/net/ifdi_if.m
+++ b/sys/net/ifdi_if.m
@@ -36,6 +36,7 @@
#include <net/ethernet.h>
#include <net/if.h>
#include <net/if_var.h>
+#include <net/if_vf_status.h>
#include <net/if_media.h>
#include <net/iflib.h>
#include <net/if_private.h>
@@ -112,6 +113,13 @@
return (ENOTSUP);
}
+ static int
+ null_vf_status(if_ctx_t _ctx __unused,
+ struct if_vf_status **_status __unused)
+ {
+ return (ENOTSUP);
+ }
+
static bool
null_needs_restart(if_ctx_t _ctx __unused, enum iflib_restart_event _event __unused)
{
@@ -375,3 +383,8 @@
if_ctx_t _ctx;
struct ifdownreason *_ifdr;
} DEFAULT null_get_downreason;
+
+METHOD int vf_status {
+ if_ctx_t _ctx;
+ struct if_vf_status **_status;
+} DEFAULT null_vf_status;
diff --git a/sys/net/iflib.c b/sys/net/iflib.c
--- a/sys/net/iflib.c
+++ b/sys/net/iflib.c
@@ -87,6 +87,7 @@
#include <dev/pci/pci_private.h>
#include <net/iflib.h>
+#include <net/if_vf_status.h>
#include "ifdi_if.h"
@@ -4684,6 +4685,29 @@
return (err);
}
+static int
+iflib_if_vf_status(if_t ifp, struct if_vf_status **statusp)
+{
+ if_ctx_t ctx;
+ kobjop_desc_t kobj_desc;
+ kobj_method_t *kobj_method;
+ int error;
+
+ ctx = if_getsoftc(ifp);
+ /* VF status describes children of an SR-IOV PF. */
+ if (CTX_IS_VF(ctx))
+ return (ENOTSUP);
+ kobj_desc = &ifdi_vf_status_desc;
+ kobj_method = kobj_lookup_method(((kobj_t)ctx)->ops->cls, NULL,
+ kobj_desc);
+ if (kobj_method == &kobj_desc->deflt)
+ return (ENOTSUP);
+ CTX_LOCK(ctx);
+ error = IFDI_VF_STATUS(ctx, statusp);
+ CTX_UNLOCK(ctx);
+ return (error);
+}
+
static uint64_t
iflib_if_get_counter(if_t ifp, ift_counter cnt)
{
@@ -5823,6 +5847,7 @@
if_setdev(ifp, dev);
if_setinitfn(ifp, iflib_if_init);
if_setioctlfn(ifp, iflib_if_ioctl);
+ if_setvfstatusfn(ifp, iflib_if_vf_status);
#ifdef ALTQ
if_setstartfn(ifp, iflib_altq_if_start);
if_settransmitfn(ifp, iflib_altq_if_transmit);
diff --git a/sys/netlink/netlink_snl_route_parsers.h b/sys/netlink/netlink_snl_route_parsers.h
--- a/sys/netlink/netlink_snl_route_parsers.h
+++ b/sys/netlink/netlink_snl_route_parsers.h
@@ -175,6 +175,304 @@
_cb_p_route);
/* RTM_<NEW|DEL|GET>LINK message parser */
+#define SNL_VF_F_CONFIGURED (1ULL << 0)
+#define SNL_VF_F_INITIALIZED (1ULL << 1)
+#define SNL_VF_F_MAC (1ULL << 2)
+#define SNL_VF_F_VLAN_MODE (1ULL << 3)
+#define SNL_VF_F_VLAN (1ULL << 4)
+#define SNL_VF_F_VLAN_COUNT (1ULL << 5)
+#define SNL_VF_F_VLAN_LIMIT (1ULL << 6)
+#define SNL_VF_F_NUM_QUEUES (1ULL << 7)
+#define SNL_VF_F_ALLOW_SET_MAC (1ULL << 8)
+#define SNL_VF_F_ALLOW_SET_VLAN (1ULL << 9)
+#define SNL_VF_F_MAC_ANTI_SPOOF (1ULL << 10)
+#define SNL_VF_F_ALLOW_PROMISC (1ULL << 11)
+#define SNL_VF_F_TRAFFIC_ENABLED (1ULL << 12)
+#define SNL_VF_F_MDD_BLOCKED (1ULL << 13)
+#define SNL_VF_F_QUARANTINED (1ULL << 14)
+#define SNL_VF_F_API_VERSION (1ULL << 15)
+#define SNL_VF_F_LINK_STATE_POLICY (1ULL << 16)
+enum snl_vf_driver_field_type {
+ SNL_VFDF_NONE = 0,
+ SNL_VFDF_BOOL,
+ SNL_VFDF_NUMBER,
+ SNL_VFDF_STRING,
+ SNL_VFDF_BINARY,
+};
+
+struct snl_parsed_vf_driver_field {
+ char *name;
+ enum snl_vf_driver_field_type type;
+ bool boolean;
+ uint64_t number;
+ char *string;
+ struct nlattr *binary;
+};
+
+struct snl_parsed_vf_driver {
+ char *name;
+ uint32_t version;
+ bool version_present;
+ struct snl_parray fields;
+};
+
+struct snl_parsed_vf {
+ uint64_t attrs;
+ uint32_t index;
+ bool index_present;
+ uint32_t vlan_count;
+ uint32_t vlan_limit;
+ uint32_t num_queues;
+ uint16_t vlan;
+ bool configured;
+ bool initialized;
+ uint8_t vlan_mode;
+ bool allow_set_mac;
+ bool allow_set_vlan;
+ bool mac_anti_spoof;
+ bool allow_promisc;
+ bool traffic_enabled;
+ bool mdd_blocked;
+ bool quarantined;
+ uint8_t link_state_policy;
+ char *api_version;
+ struct nlattr *mac;
+ struct snl_parray drivers;
+};
+
+static inline bool
+_snl_vf_get_index(struct snl_state *ss, struct nlattr *nla,
+ const void *arg __unused, void *target)
+{
+ struct snl_parsed_vf *vf = target;
+
+ if (!snl_attr_get_uint32(ss, nla, NULL, &vf->index))
+ return (false);
+ vf->index_present = true;
+ return (true);
+}
+
+static inline bool
+_cb_p_vf(struct snl_state *ss __unused, void *target)
+{
+ struct snl_parsed_vf *vf = target;
+
+ return (vf->index_present);
+}
+
+#define _SNL_VF_ARG(_bit, _field) ((const void *)(uintptr_t)( \
+ ((uint32_t)(_bit) << 16) | offsetof(struct snl_parsed_vf, _field)))
+#define _SNL_VF_BIT(_arg) ((uintptr_t)(_arg) >> 16)
+#define _SNL_VF_OFF(_arg) ((uintptr_t)(_arg) & 0xffff)
+
+#define _SNL_VF_GETTER(_name, _getter) \
+static inline bool \
+_name(struct snl_state *ss, struct nlattr *nla, const void *arg, \
+ void *target) \
+{ \
+ struct snl_parsed_vf *vf = target; \
+ void *value = (char *)target + _SNL_VF_OFF(arg); \
+ \
+ if (!_getter(ss, nla, NULL, value)) \
+ return (false); \
+ vf->attrs |= 1ULL << _SNL_VF_BIT(arg); \
+ return (true); \
+}
+
+_SNL_VF_GETTER(_snl_vf_get_bool, snl_attr_get_bool)
+_SNL_VF_GETTER(_snl_vf_get_u8, snl_attr_get_uint8)
+_SNL_VF_GETTER(_snl_vf_get_u16, snl_attr_get_uint16)
+_SNL_VF_GETTER(_snl_vf_get_u32, snl_attr_get_uint32)
+_SNL_VF_GETTER(_snl_vf_get_string, snl_attr_dup_string)
+_SNL_VF_GETTER(_snl_vf_get_nla, snl_attr_dup_nla)
+
+#undef _SNL_VF_GETTER
+
+#define _SNL_VFDF_GETTER(_name, _getter, _type, _field) \
+static inline bool \
+_name(struct snl_state *ss, struct nlattr *nla, const void *arg, \
+ void *target) \
+{ \
+ struct snl_parsed_vf_driver_field *field = target; \
+ \
+ if (field->type != SNL_VFDF_NONE || \
+ !_getter(ss, nla, arg, &field->_field)) \
+ return (false); \
+ field->type = _type; \
+ return (true); \
+}
+
+_SNL_VFDF_GETTER(_snl_vfdf_get_bool, snl_attr_get_bool, SNL_VFDF_BOOL,
+ boolean)
+_SNL_VFDF_GETTER(_snl_vfdf_get_number, snl_attr_get_uint64,
+ SNL_VFDF_NUMBER, number)
+_SNL_VFDF_GETTER(_snl_vfdf_get_string, snl_attr_dup_string,
+ SNL_VFDF_STRING, string)
+_SNL_VFDF_GETTER(_snl_vfdf_get_binary, snl_attr_dup_nla,
+ SNL_VFDF_BINARY, binary)
+
+#undef _SNL_VFDF_GETTER
+
+static inline bool
+_cb_p_vf_driver_field(struct snl_state *ss __unused, void *target)
+{
+ struct snl_parsed_vf_driver_field *field = target;
+
+ return (field->name != NULL && field->type != SNL_VFDF_NONE);
+}
+
+#define _OUT(_field) offsetof(struct snl_parsed_vf_driver_field, _field)
+static const struct snl_attr_parser _nla_p_vf_driver_field[] = {
+ { .type = IFLAF_VFDF_NAME, .off = _OUT(name),
+ .cb = snl_attr_dup_string },
+ { .type = IFLAF_VFDF_BOOL, .off = 0, .cb = _snl_vfdf_get_bool },
+ { .type = IFLAF_VFDF_NUMBER, .off = 0,
+ .cb = _snl_vfdf_get_number },
+ { .type = IFLAF_VFDF_STRING, .off = 0,
+ .cb = _snl_vfdf_get_string },
+ { .type = IFLAF_VFDF_BINARY, .off = 0,
+ .cb = _snl_vfdf_get_binary },
+};
+#undef _OUT
+SNL_DECLARE_ATTR_PARSER_EXT(_vf_driver_field_parser,
+ sizeof(struct snl_parsed_vf_driver_field), _nla_p_vf_driver_field,
+ _cb_p_vf_driver_field);
+
+static inline bool
+_snl_vfd_get_version(struct snl_state *ss, struct nlattr *nla,
+ const void *arg __unused, void *target)
+{
+ struct snl_parsed_vf_driver *driver = target;
+
+ if (!snl_attr_get_uint32(ss, nla, NULL, &driver->version))
+ return (false);
+ driver->version_present = true;
+ return (true);
+}
+
+static inline bool
+_cb_p_vf_driver(struct snl_state *ss __unused, void *target)
+{
+ struct snl_parsed_vf_driver *driver = target;
+
+ return (driver->name != NULL && driver->version_present);
+}
+
+#define _OUT(_field) offsetof(struct snl_parsed_vf_driver, _field)
+static const struct snl_attr_parser _nla_p_vf_driver[] = {
+ { .type = IFLAF_VFD_NAME, .off = _OUT(name),
+ .cb = snl_attr_dup_string },
+ { .type = IFLAF_VFD_VERSION, .off = 0,
+ .cb = _snl_vfd_get_version },
+ { .type = IFLAF_VFD_FIELD, .off = _OUT(fields),
+ .arg = &_vf_driver_field_parser, .cb = snl_attr_get_multi },
+};
+#undef _OUT
+SNL_DECLARE_ATTR_PARSER_EXT(_vf_driver_parser,
+ sizeof(struct snl_parsed_vf_driver), _nla_p_vf_driver,
+ _cb_p_vf_driver);
+
+#define _OUT(_field) offsetof(struct snl_parsed_vf, _field)
+static const struct snl_attr_parser _nla_p_vf[] = {
+ { .type = IFLAF_VF_INDEX, .off = 0, .cb = _snl_vf_get_index },
+ { .type = IFLAF_VF_CONFIGURED, .off = 0,
+ .arg = _SNL_VF_ARG(0, configured), .cb = _snl_vf_get_bool },
+ { .type = IFLAF_VF_INITIALIZED, .off = 0,
+ .arg = _SNL_VF_ARG(1, initialized), .cb = _snl_vf_get_bool },
+ { .type = IFLAF_VF_MAC, .off = 0,
+ .arg = _SNL_VF_ARG(2, mac), .cb = _snl_vf_get_nla },
+ { .type = IFLAF_VF_VLAN_MODE, .off = 0,
+ .arg = _SNL_VF_ARG(3, vlan_mode), .cb = _snl_vf_get_u8 },
+ { .type = IFLAF_VF_VLAN, .off = 0,
+ .arg = _SNL_VF_ARG(4, vlan), .cb = _snl_vf_get_u16 },
+ { .type = IFLAF_VF_VLAN_COUNT, .off = 0,
+ .arg = _SNL_VF_ARG(5, vlan_count), .cb = _snl_vf_get_u32 },
+ { .type = IFLAF_VF_VLAN_LIMIT, .off = 0,
+ .arg = _SNL_VF_ARG(6, vlan_limit), .cb = _snl_vf_get_u32 },
+ { .type = IFLAF_VF_NUM_QUEUES, .off = 0,
+ .arg = _SNL_VF_ARG(7, num_queues), .cb = _snl_vf_get_u32 },
+ { .type = IFLAF_VF_ALLOW_SET_MAC, .off = 0,
+ .arg = _SNL_VF_ARG(8, allow_set_mac), .cb = _snl_vf_get_bool },
+ { .type = IFLAF_VF_ALLOW_SET_VLAN, .off = 0,
+ .arg = _SNL_VF_ARG(9, allow_set_vlan), .cb = _snl_vf_get_bool },
+ { .type = IFLAF_VF_MAC_ANTI_SPOOF, .off = 0,
+ .arg = _SNL_VF_ARG(10, mac_anti_spoof), .cb = _snl_vf_get_bool },
+ { .type = IFLAF_VF_ALLOW_PROMISC, .off = 0,
+ .arg = _SNL_VF_ARG(11, allow_promisc), .cb = _snl_vf_get_bool },
+ { .type = IFLAF_VF_TRAFFIC_ENABLED, .off = 0,
+ .arg = _SNL_VF_ARG(12, traffic_enabled), .cb = _snl_vf_get_bool },
+ { .type = IFLAF_VF_MDD_BLOCKED, .off = 0,
+ .arg = _SNL_VF_ARG(13, mdd_blocked), .cb = _snl_vf_get_bool },
+ { .type = IFLAF_VF_QUARANTINED, .off = 0,
+ .arg = _SNL_VF_ARG(14, quarantined), .cb = _snl_vf_get_bool },
+ { .type = IFLAF_VF_API_VERSION, .off = 0,
+ .arg = _SNL_VF_ARG(15, api_version), .cb = _snl_vf_get_string },
+ { .type = IFLAF_VF_LINK_STATE_POLICY, .off = 0,
+ .arg = _SNL_VF_ARG(16, link_state_policy), .cb = _snl_vf_get_u8 },
+ { .type = IFLAF_VF_DRIVER, .off = _OUT(drivers),
+ .arg = &_vf_driver_parser, .cb = snl_attr_get_multi },
+};
+#undef _OUT
+SNL_DECLARE_ATTR_PARSER_EXT(_vf_parser, sizeof(struct snl_parsed_vf),
+ _nla_p_vf, _cb_p_vf);
+
+struct snl_parsed_vf_status {
+ uint64_t pf_link_speed;
+ uint32_t error;
+ uint8_t pf_link_state;
+ bool present;
+ bool pf_link_state_present;
+ struct snl_parray vfs;
+};
+
+static inline bool
+_snl_vfs_get_link_state(struct snl_state *ss, struct nlattr *nla,
+ const void *arg __unused, void *target)
+{
+ struct snl_parsed_vf_status *status = target;
+
+ if (!snl_attr_get_uint8(ss, nla, NULL, &status->pf_link_state))
+ return (false);
+ status->pf_link_state_present = true;
+ return (true);
+}
+
+static inline bool
+_cb_p_vf_status(struct snl_state *ss __unused, void *target)
+{
+ struct snl_parsed_vf_status *status = target;
+
+ return (status->error != 0 || status->pf_link_state_present);
+}
+
+#define _OUT(_field) offsetof(struct snl_parsed_vf_status, _field)
+static const struct snl_attr_parser _nla_p_vf_status[] = {
+ { .type = IFLAF_VFS_ERROR, .off = _OUT(error),
+ .cb = snl_attr_get_uint32 },
+ { .type = IFLAF_VFS_PF_LINK_STATE, .off = 0,
+ .cb = _snl_vfs_get_link_state },
+ { .type = IFLAF_VFS_PF_LINK_SPEED, .off = _OUT(pf_link_speed),
+ .cb = snl_attr_get_uint64 },
+ { .type = IFLAF_VFS_VF, .off = _OUT(vfs),
+ .arg = &_vf_parser, .cb = snl_attr_get_multi },
+};
+#undef _OUT
+SNL_DECLARE_ATTR_PARSER_EXT(_vf_status_parser,
+ sizeof(struct snl_parsed_vf_status), _nla_p_vf_status,
+ _cb_p_vf_status);
+
+static inline bool
+_snl_get_vf_status(struct snl_state *ss, struct nlattr *nla,
+ const void *arg __unused, void *target)
+{
+ struct snl_parsed_vf_status *status = target;
+
+ if (!snl_attr_get_nested(ss, nla, &_vf_status_parser, status))
+ return (false);
+ status->present = true;
+ return (true);
+}
+
struct snl_parsed_link {
uint32_t ifi_index;
uint32_t ifi_flags;
@@ -188,9 +486,11 @@
struct nlattr *ifla_broadcast;
char *ifla_ifalias;
uint32_t ifla_promiscuity;
+ uint32_t ifla_num_vf;
struct rtnl_link_stats64 *ifla_stats64;
struct nlattr *iflaf_orig_hwaddr;
struct snl_attr_bitset iflaf_caps;
+ struct snl_parsed_vf_status iflaf_vf_status;
};
#define _IN(_field) offsetof(struct ifinfomsg, _field)
@@ -198,6 +498,8 @@
static const struct snl_attr_parser _nla_p_link_fbsd[] = {
{ .type = IFLAF_ORIG_HWADDR, .off = _OUT(iflaf_orig_hwaddr), .cb = snl_attr_dup_nla },
{ .type = IFLAF_CAPS, .off = _OUT(iflaf_caps), .cb = snl_attr_get_bitset_c },
+ { .type = IFLAF_VF_STATUS, .off = _OUT(iflaf_vf_status),
+ .cb = _snl_get_vf_status },
};
SNL_DECLARE_ATTR_PARSER(_link_fbsd_parser, _nla_p_link_fbsd);
@@ -208,6 +510,7 @@
{ .type = IFLA_MTU, .off = _OUT(ifla_mtu), .cb = snl_attr_get_uint32 },
{ .type = IFLA_OPERSTATE, .off = _OUT(ifla_operstate), .cb = snl_attr_get_uint8 },
{ .type = IFLA_IFALIAS, .off = _OUT(ifla_ifalias), .cb = snl_attr_dup_string },
+ { .type = IFLA_NUM_VF, .off = _OUT(ifla_num_vf), .cb = snl_attr_get_uint32 },
{ .type = IFLA_STATS64, .off = _OUT(ifla_stats64), .cb = snl_attr_dup_struct },
{ .type = IFLA_PROMISCUITY, .off = _OUT(ifla_promiscuity), .cb = snl_attr_get_uint32 },
{ .type = IFLA_CARRIER, .off = _OUT(ifla_carrier), .cb = snl_attr_get_uint8 },
diff --git a/sys/netlink/route/iface.c b/sys/netlink/route/iface.c
--- a/sys/netlink/route/iface.c
+++ b/sys/netlink/route/iface.c
@@ -42,6 +42,7 @@
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/if_var.h>
+#include <net/if_vf_status.h>
#include <net/if_clone.h>
#include <net/route.h>
#include <net/route/nhop.h>
@@ -65,6 +66,7 @@
struct nlpcb *so;
struct ucred *cred;
uint32_t fibnum;
+ uint32_t ext_mask;
int family;
int error;
int count;
@@ -279,6 +281,212 @@
return (true);
}
+static bool
+dump_vf_bool(struct nl_writer *nw, const struct if_vf_info *vf,
+ uint64_t field, uint16_t attr, bool value)
+{
+
+ if ((vf->fields & field) == 0)
+ return (true);
+ return (nlattr_add_bool(nw, attr, value));
+}
+
+static bool
+dump_vf_u16(struct nl_writer *nw, const struct if_vf_info *vf,
+ uint64_t field, uint16_t attr, uint16_t value)
+{
+
+ if ((vf->fields & field) == 0)
+ return (true);
+ return (nlattr_add_u16(nw, attr, value));
+}
+
+static bool
+dump_vf_u32(struct nl_writer *nw, const struct if_vf_info *vf,
+ uint64_t field, uint16_t attr, uint32_t value)
+{
+
+ if ((vf->fields & field) == 0)
+ return (true);
+ return (nlattr_add_u32(nw, attr, value));
+}
+
+static bool
+dump_vf_extension_field(struct nl_writer *nw,
+ const struct if_vf_ext_field *field)
+{
+ int off;
+ bool ok;
+
+ if (field->name == NULL)
+ return (false);
+ off = nlattr_add_nested(nw, IFLAF_VFD_FIELD);
+ if (off == 0)
+ return (false);
+ if (!nlattr_add_string(nw, IFLAF_VFDF_NAME, field->name))
+ return (false);
+ switch (field->type) {
+ case IFVF_EXT_BOOL:
+ ok = nlattr_add_bool(nw, IFLAF_VFDF_BOOL,
+ field->value.boolean);
+ break;
+ case IFVF_EXT_NUMBER:
+ ok = nlattr_add_u64(nw, IFLAF_VFDF_NUMBER,
+ field->value.number);
+ break;
+ case IFVF_EXT_STRING:
+ ok = field->value.string != NULL && nlattr_add_string(nw,
+ IFLAF_VFDF_STRING, field->value.string);
+ break;
+ case IFVF_EXT_BINARY:
+ ok = field->value.binary.data != NULL &&
+ field->value.binary.length != 0 && nlattr_add(nw,
+ IFLAF_VFDF_BINARY, field->value.binary.length,
+ field->value.binary.data);
+ break;
+ default:
+ ok = false;
+ break;
+ }
+ if (!ok)
+ return (false);
+ nlattr_set_len(nw, off);
+ return (true);
+}
+
+static bool
+dump_vf_extensions(struct nl_writer *nw, const struct if_vf_info *vf)
+{
+ const struct if_vf_extension *extension;
+ uint32_t i, j;
+ int off;
+
+ for (i = 0; i < vf->num_extensions; i++) {
+ extension = &vf->extensions[i];
+ if (extension->name == NULL)
+ return (false);
+ off = nlattr_add_nested(nw, IFLAF_VF_DRIVER);
+ if (off == 0 ||
+ !nlattr_add_string(nw, IFLAF_VFD_NAME, extension->name) ||
+ !nlattr_add_u32(nw, IFLAF_VFD_VERSION, extension->version))
+ return (false);
+ for (j = 0; j < extension->num_fields; j++) {
+ if (!dump_vf_extension_field(nw,
+ &extension->fields[j]))
+ return (false);
+ }
+ nlattr_set_len(nw, off);
+ }
+ return (true);
+}
+
+static bool
+dump_vf_entry(struct nl_writer *nw, const struct if_vf_info *vf)
+{
+ int off;
+ bool ok;
+
+ off = nlattr_add_nested(nw, IFLAF_VFS_VF);
+ if (off == 0)
+ return (false);
+ ok = nlattr_add_u32(nw, IFLAF_VF_INDEX, vf->index) &&
+ dump_vf_bool(nw, vf, IFVF_F_CONFIGURED, IFLAF_VF_CONFIGURED,
+ vf->configured) &&
+ dump_vf_bool(nw, vf, IFVF_F_INITIALIZED, IFLAF_VF_INITIALIZED,
+ vf->initialized);
+ if (!ok)
+ return (false);
+ if ((vf->fields & IFVF_F_MAC) != 0 &&
+ !nlattr_add(nw, IFLAF_VF_MAC, sizeof(vf->mac), vf->mac))
+ return (false);
+ if ((vf->fields & IFVF_F_VLAN_MODE) != 0) {
+ if (vf->vlan_mode > IFVF_VLAN_TRUNK ||
+ !nlattr_add_u8(nw, IFLAF_VF_VLAN_MODE,
+ (uint8_t)vf->vlan_mode))
+ return (false);
+ }
+ ok = dump_vf_u16(nw, vf, IFVF_F_VLAN, IFLAF_VF_VLAN, vf->vlan) &&
+ dump_vf_u32(nw, vf, IFVF_F_VLAN_COUNT, IFLAF_VF_VLAN_COUNT,
+ vf->vlan_count) &&
+ dump_vf_u32(nw, vf, IFVF_F_VLAN_LIMIT, IFLAF_VF_VLAN_LIMIT,
+ vf->vlan_limit) &&
+ dump_vf_u32(nw, vf, IFVF_F_NUM_QUEUES, IFLAF_VF_NUM_QUEUES,
+ vf->num_queues) &&
+ dump_vf_bool(nw, vf, IFVF_F_ALLOW_SET_MAC, IFLAF_VF_ALLOW_SET_MAC,
+ vf->allow_set_mac) &&
+ dump_vf_bool(nw, vf, IFVF_F_ALLOW_SET_VLAN,
+ IFLAF_VF_ALLOW_SET_VLAN, vf->allow_set_vlan) &&
+ dump_vf_bool(nw, vf, IFVF_F_MAC_ANTI_SPOOF,
+ IFLAF_VF_MAC_ANTI_SPOOF, vf->mac_anti_spoof) &&
+ dump_vf_bool(nw, vf, IFVF_F_ALLOW_PROMISC,
+ IFLAF_VF_ALLOW_PROMISC, vf->allow_promisc) &&
+ dump_vf_bool(nw, vf, IFVF_F_TRAFFIC_ENABLED,
+ IFLAF_VF_TRAFFIC_ENABLED, vf->traffic_enabled) &&
+ dump_vf_bool(nw, vf, IFVF_F_MDD_BLOCKED, IFLAF_VF_MDD_BLOCKED,
+ vf->mdd_blocked) &&
+ dump_vf_bool(nw, vf, IFVF_F_QUARANTINED, IFLAF_VF_QUARANTINED,
+ vf->quarantined);
+ if (!ok)
+ return (false);
+ if ((vf->fields & IFVF_F_API_VERSION) != 0 &&
+ !nlattr_add_string(nw, IFLAF_VF_API_VERSION, vf->api_version))
+ return (false);
+ if ((vf->fields & IFVF_F_LINK_STATE_POLICY) != 0) {
+ if (vf->link_state_policy > IFVF_LINK_AUTO ||
+ !nlattr_add_u8(nw, IFLAF_VF_LINK_STATE_POLICY,
+ (uint8_t)vf->link_state_policy))
+ return (false);
+ }
+ if (!dump_vf_extensions(nw, vf))
+ return (false);
+ nlattr_set_len(nw, off);
+ return (true);
+}
+
+static bool
+dump_vf_status(struct nl_writer *nw, if_t ifp,
+ const struct if_vf_status *status, int error)
+{
+ uint64_t baudrate;
+ uint32_t i;
+ uint8_t link_state;
+ int off;
+
+ off = nlattr_add_nested(nw, IFLAF_VF_STATUS);
+ if (off == 0)
+ return (false);
+ if (error != 0) {
+ if (!nlattr_add_u32(nw, IFLAF_VFS_ERROR, (uint32_t)error))
+ return (false);
+ nlattr_set_len(nw, off);
+ return (true);
+ }
+
+ switch (if_getlinkstate(ifp)) {
+ case LINK_STATE_DOWN:
+ link_state = IFLAF_VF_LINK_DOWN;
+ break;
+ case LINK_STATE_UP:
+ link_state = IFLAF_VF_LINK_UP;
+ break;
+ default:
+ link_state = IFLAF_VF_LINK_UNKNOWN;
+ break;
+ }
+ if (!nlattr_add_u8(nw, IFLAF_VFS_PF_LINK_STATE, link_state))
+ return (false);
+ baudrate = if_getbaudrate(ifp);
+ if (baudrate != 0 &&
+ !nlattr_add_u64(nw, IFLAF_VFS_PF_LINK_SPEED, baudrate))
+ return (false);
+ for (i = 0; i < status->num_vfs; i++) {
+ if (!dump_vf_entry(nw, &status->vfs[i]))
+ return (false);
+ }
+ nlattr_set_len(nw, off);
+ return (true);
+}
+
/*
* Dumps interface state, properties and metrics.
* @nw: message writer
@@ -290,12 +498,25 @@
*/
static bool
dump_iface(struct nl_writer *nw, if_t ifp, const struct nlmsghdr *hdr,
- int if_flags_mask, const char *ifname)
+ int if_flags_mask, const char *ifname, uint32_t ext_mask)
{
struct epoch_tracker et;
+ struct if_vf_status *vf_status;
struct ifinfomsg *ifinfo;
+ bool include_vf_status;
+ int vf_error;
NL_LOG(LOG_DEBUG3, "dumping interface %s data", if_name(ifp));
+ vf_status = NULL;
+ vf_error = 0;
+ include_vf_status = (ext_mask & RTEXT_FILTER_VF) != 0;
+ if (include_vf_status) {
+ vf_error = if_get_vf_status(ifp, &vf_status);
+ if (vf_error == EINVAL || vf_error == ENOTTY ||
+ vf_error == ENXIO || vf_error == ENOTSUP ||
+ vf_error == EOPNOTSUPP)
+ include_vf_status = false;
+ }
if (!nlmsg_reply(nw, hdr, sizeof(struct ifinfomsg)))
goto enomem;
@@ -341,6 +562,10 @@
}
nlattr_add_u32(nw, IFLA_MTU, if_getmtu(ifp));
+ if (vf_status != NULL) {
+ if (!nlattr_add_u32(nw, IFLA_NUM_VF, vf_status->num_vfs))
+ goto enomem;
+ }
/*
nlattr_add_u32(nw, IFLA_MIN_MTU, 60);
nlattr_add_u32(nw, IFLA_MAX_MTU, 9000);
@@ -355,6 +580,9 @@
if (off != 0) {
get_hwaddr(nw, ifp);
dump_iface_caps(nw, ifp);
+ if (include_vf_status &&
+ !dump_vf_status(nw, ifp, vf_status, vf_error))
+ goto enomem;
nlattr_set_len(nw, off);
}
@@ -368,11 +596,14 @@
nw->ifp = ifp;
- if (nlmsg_end(nw))
+ if (nlmsg_end(nw)) {
+ if_vf_status_free(vf_status);
return (true);
+ }
enomem:
- NL_LOG(LOG_DEBUG, "unable to dump interface %s state (ENOMEM)", if_name(ifp));
+ if_vf_status_free(vf_status);
+ NL_LOG(LOG_DEBUG, "unable to dump interface %s state", if_name(ifp));
nlmsg_abort(nw);
return (false);
}
@@ -415,6 +646,7 @@
{ .type = IFLA_LINKINFO, .arg = &linfo_parser, .cb = nlattr_get_nested },
{ .type = IFLA_IFALIAS, .off = _OUT(ifla_ifalias), .cb = nlattr_get_string },
{ .type = IFLA_GROUP, .off = _OUT(ifla_group), .cb = nlattr_get_string },
+ { .type = IFLA_EXT_MASK, .off = _OUT(ifla_ext_mask), .cb = nlattr_get_uint32 },
{ .type = IFLA_ALT_IFNAME, .off = _OUT(ifla_ifname), .cb = nlattr_get_string },
};
#undef _IN
@@ -441,7 +673,7 @@
dump_cb(if_t ifp, void *_arg)
{
struct netlink_walkargs *wa = (struct netlink_walkargs *)_arg;
- if (!dump_iface(wa->nw, ifp, &wa->hdr, 0, NULL))
+ if (!dump_iface(wa->nw, ifp, &wa->hdr, 0, NULL, wa->ext_mask))
return (ENOMEM);
return (0);
}
@@ -473,6 +705,7 @@
.hdr.nlmsg_seq = hdr->nlmsg_seq,
.hdr.nlmsg_flags = hdr->nlmsg_flags,
.hdr.nlmsg_type = NL_RTM_NEWLINK,
+ .ext_mask = attrs.ifla_ext_mask,
};
/* Fast track for an interface w/ explicit name or index match */
@@ -491,7 +724,8 @@
if (ifp != NULL) {
if (match_iface(ifp, &attrs)) {
- if (!dump_iface(wa.nw, ifp, &wa.hdr, 0, NULL))
+ if (!dump_iface(wa.nw, ifp, &wa.hdr, 0, NULL,
+ wa.ext_mask))
error = ENOMEM;
} else
error = ENODEV;
@@ -1418,7 +1652,7 @@
NL_LOG(LOG_DEBUG, "error allocating group writer");
return;
}
- dump_iface(&nw, ifp, &hdr, if_flags_mask, ifname);
+ dump_iface(&nw, ifp, &hdr, if_flags_mask, ifname, 0);
nlmsg_flush(&nw);
}
diff --git a/sys/netlink/route/interface.h b/sys/netlink/route/interface.h
--- a/sys/netlink/route/interface.h
+++ b/sys/netlink/route/interface.h
@@ -94,7 +94,7 @@
#define IFLA_NET_NS_PID IFLA_NET_NS_PID
IFLA_IFALIAS = 20, /* string: interface description */
#define IFLA_IFALIAS IFLA_IFALIAS
- IFLA_NUM_VF = 21, /* not supported */
+ IFLA_NUM_VF = 21, /* u32: active VFs, when requested */
#define IFLA_NUM_VF IFLA_NUM_VF
IFLA_VFINFO_LIST= 22, /* not supported */
#define IFLA_VFINFO_LIST IFLA_VFINFO_LIST
@@ -147,15 +147,91 @@
};
#define IFLA_MAX (__IFLA_MAX - 1)
+/* IFLA_EXT_MASK values. */
+#define RTEXT_FILTER_VF (1U << 0)
+
enum {
IFLAF_UNSPEC = 0,
IFLAF_ORIG_IFNAME = 1, /* string, original interface name at creation */
IFLAF_ORIG_HWADDR = 2, /* binary, original hardware address */
IFLAF_CAPS = 3, /* bitset, interface capabilities */
+ IFLAF_VF_STATUS = 4, /* nested, IFLAF_VFS_* */
__IFLAF_MAX
};
#define IFLAF_MAX (__IFLAF_MAX - 1)
+/* IFLAF_VF_STATUS attributes. */
+enum {
+ IFLAF_VFS_UNSPEC = 0,
+ IFLAF_VFS_ERROR = 1, /* u32: errno from requested query */
+ IFLAF_VFS_PF_LINK_STATE = 2, /* u8: IFLAF_VF_LINK_* */
+ IFLAF_VFS_PF_LINK_SPEED = 3, /* u64: bits per second */
+ IFLAF_VFS_VF = 4, /* nested, IFLAF_VF_*; repeated */
+ __IFLAF_VFS_MAX
+};
+#define IFLAF_VFS_MAX (__IFLAF_VFS_MAX - 1)
+
+/* IFLAF_VFS_VF attributes. */
+enum {
+ IFLAF_VF_UNSPEC = 0,
+ IFLAF_VF_INDEX = 1, /* u32 */
+ IFLAF_VF_CONFIGURED = 2, /* bool */
+ IFLAF_VF_INITIALIZED = 3, /* bool */
+ IFLAF_VF_MAC = 4, /* binary */
+ IFLAF_VF_VLAN_MODE = 5, /* u8: IFLAF_VF_VLAN_* */
+ IFLAF_VF_VLAN = 6, /* u16 */
+ IFLAF_VF_VLAN_COUNT = 7, /* u32 */
+ IFLAF_VF_VLAN_LIMIT = 8, /* u32 */
+ IFLAF_VF_NUM_QUEUES = 9, /* u32 */
+ IFLAF_VF_ALLOW_SET_MAC = 10, /* bool */
+ IFLAF_VF_ALLOW_SET_VLAN = 11, /* bool */
+ IFLAF_VF_MAC_ANTI_SPOOF = 12, /* bool */
+ IFLAF_VF_ALLOW_PROMISC = 13, /* bool */
+ IFLAF_VF_TRAFFIC_ENABLED = 14, /* bool */
+ IFLAF_VF_MDD_BLOCKED = 15, /* bool */
+ IFLAF_VF_QUARANTINED = 16, /* bool */
+ IFLAF_VF_API_VERSION = 17, /* string */
+ IFLAF_VF_LINK_STATE_POLICY = 18, /* u8: IFLAF_VF_LINK_* */
+ IFLAF_VF_DRIVER = 19, /* nested; repeated */
+ __IFLAF_VF_MAX
+};
+#define IFLAF_VF_MAX (__IFLAF_VF_MAX - 1)
+
+/* IFLAF_VF_DRIVER attributes. */
+enum {
+ IFLAF_VFD_UNSPEC = 0,
+ IFLAF_VFD_NAME = 1, /* string: stable namespace */
+ IFLAF_VFD_VERSION = 2, /* u32: namespace version */
+ IFLAF_VFD_FIELD = 3, /* nested, IFLAF_VFDF_*; repeated */
+ __IFLAF_VFD_MAX
+};
+#define IFLAF_VFD_MAX (__IFLAF_VFD_MAX - 1)
+
+/* IFLAF_VFD_FIELD attributes; exactly one value attribute is present. */
+enum {
+ IFLAF_VFDF_UNSPEC = 0,
+ IFLAF_VFDF_NAME = 1, /* string: namespace-local name */
+ IFLAF_VFDF_BOOL = 2, /* bool */
+ IFLAF_VFDF_NUMBER = 3, /* u64 */
+ IFLAF_VFDF_STRING = 4, /* string */
+ IFLAF_VFDF_BINARY = 5, /* binary */
+ __IFLAF_VFDF_MAX
+};
+#define IFLAF_VFDF_MAX (__IFLAF_VFDF_MAX - 1)
+
+enum {
+ IFLAF_VF_LINK_UNKNOWN = 0,
+ IFLAF_VF_LINK_DOWN = 1,
+ IFLAF_VF_LINK_UP = 2,
+ IFLAF_VF_LINK_AUTO = 3,
+};
+
+enum {
+ IFLAF_VF_VLAN_UNKNOWN = 0,
+ IFLAF_VF_VLAN_ACCESS = 1,
+ IFLAF_VF_VLAN_TRUNK = 2,
+};
+
/*
* Attributes that can be used as filters:
* IFLA_IFNAME, IFLA_GROUP, IFLA_ALT_IFNAME
diff --git a/sys/netlink/route/route_var.h b/sys/netlink/route/route_var.h
--- a/sys/netlink/route/route_var.h
+++ b/sys/netlink/route/route_var.h
@@ -74,6 +74,7 @@
int ifi_index;
uint32_t ifla_link;
uint32_t ifla_mtu;
+ uint32_t ifla_ext_mask;
uint32_t ifi_flags;
uint32_t ifi_change;
};
diff --git a/tests/atf_python/sys/netlink/attrs.py b/tests/atf_python/sys/netlink/attrs.py
--- a/tests/atf_python/sys/netlink/attrs.py
+++ b/tests/atf_python/sys/netlink/attrs.py
@@ -137,6 +137,34 @@
return self._to_bytes(struct.pack("@I", self.u32))
+class NlAttrU64(NlAttr):
+ def __init__(self, nla_type, val):
+ self.u64 = enum_or_int(val)
+ super().__init__(nla_type, b"")
+
+ @property
+ def nla_len(self):
+ return 12
+
+ def _print_attr_value(self):
+ return " val={}".format(self.u64)
+
+ @staticmethod
+ def _validate(data):
+ assert len(data) == 12
+ nla_len, nla_type = struct.unpack("@HH", data[:4])
+ assert nla_len == 12
+
+ @classmethod
+ def _parse(cls, data):
+ nla_len, nla_type = struct.unpack("@HH", data[:4])
+ val = struct.unpack("@Q", data[4:])[0]
+ return cls(nla_type, val)
+
+ def __bytes__(self):
+ return self._to_bytes(struct.pack("@Q", self.u64))
+
+
class NlAttrS32(NlAttr):
def __init__(self, nla_type, val):
self.s32 = enum_or_int(val)
diff --git a/tests/atf_python/sys/netlink/netlink_route.py b/tests/atf_python/sys/netlink/netlink_route.py
--- a/tests/atf_python/sys/netlink/netlink_route.py
+++ b/tests/atf_python/sys/netlink/netlink_route.py
@@ -13,7 +13,9 @@
from atf_python.sys.netlink.attrs import NlAttrIp
from atf_python.sys.netlink.attrs import NlAttrNested
from atf_python.sys.netlink.attrs import NlAttrStr
+from atf_python.sys.netlink.attrs import NlAttrU16
from atf_python.sys.netlink.attrs import NlAttrU32
+from atf_python.sys.netlink.attrs import NlAttrU64
from atf_python.sys.netlink.attrs import NlAttrU8
from atf_python.sys.netlink.message import StdNetlinkMessage
from atf_python.sys.netlink.message import NlMsgProps
@@ -361,6 +363,55 @@
IFLAF_UNSPEC = 0
IFLAF_ORIG_IFNAME = 1
IFLAF_ORIG_HWADDR = 2
+ IFLAF_CAPS = 3
+ IFLAF_VF_STATUS = 4
+
+
+class IflafVfStatusType(Enum):
+ IFLAF_VFS_UNSPEC = 0
+ IFLAF_VFS_ERROR = 1
+ IFLAF_VFS_PF_LINK_STATE = 2
+ IFLAF_VFS_PF_LINK_SPEED = 3
+ IFLAF_VFS_VF = 4
+
+
+class IflafVfType(Enum):
+ IFLAF_VF_UNSPEC = 0
+ IFLAF_VF_INDEX = 1
+ IFLAF_VF_CONFIGURED = 2
+ IFLAF_VF_INITIALIZED = 3
+ IFLAF_VF_MAC = 4
+ IFLAF_VF_VLAN_MODE = 5
+ IFLAF_VF_VLAN = 6
+ IFLAF_VF_VLAN_COUNT = 7
+ IFLAF_VF_VLAN_LIMIT = 8
+ IFLAF_VF_NUM_QUEUES = 9
+ IFLAF_VF_ALLOW_SET_MAC = 10
+ IFLAF_VF_ALLOW_SET_VLAN = 11
+ IFLAF_VF_MAC_ANTI_SPOOF = 12
+ IFLAF_VF_ALLOW_PROMISC = 13
+ IFLAF_VF_TRAFFIC_ENABLED = 14
+ IFLAF_VF_MDD_BLOCKED = 15
+ IFLAF_VF_QUARANTINED = 16
+ IFLAF_VF_API_VERSION = 17
+ IFLAF_VF_LINK_STATE_POLICY = 18
+ IFLAF_VF_DRIVER = 19
+
+
+class IflafVfDriverType(Enum):
+ IFLAF_VFD_UNSPEC = 0
+ IFLAF_VFD_NAME = 1
+ IFLAF_VFD_VERSION = 2
+ IFLAF_VFD_FIELD = 3
+
+
+class IflafVfDriverFieldType(Enum):
+ IFLAF_VFDF_UNSPEC = 0
+ IFLAF_VFDF_NAME = 1
+ IFLAF_VFDF_BOOL = 2
+ IFLAF_VFDF_NUMBER = 3
+ IFLAF_VFDF_STRING = 4
+ IFLAF_VFDF_BINARY = 5
class IflinkInfo(Enum):
@@ -621,6 +672,61 @@
]
)
+rtnl_iflaf_vf_driver_field_attrs = [
+ AttrDescr(IflafVfDriverFieldType.IFLAF_VFDF_NAME, NlAttrStr),
+ AttrDescr(IflafVfDriverFieldType.IFLAF_VFDF_BOOL, NlAttrU8),
+ AttrDescr(IflafVfDriverFieldType.IFLAF_VFDF_NUMBER, NlAttrU64),
+ AttrDescr(IflafVfDriverFieldType.IFLAF_VFDF_STRING, NlAttrStr),
+ AttrDescr(IflafVfDriverFieldType.IFLAF_VFDF_BINARY, NlAttr),
+]
+
+rtnl_iflaf_vf_driver_attrs = [
+ AttrDescr(IflafVfDriverType.IFLAF_VFD_NAME, NlAttrStr),
+ AttrDescr(IflafVfDriverType.IFLAF_VFD_VERSION, NlAttrU32),
+ AttrDescr(
+ IflafVfDriverType.IFLAF_VFD_FIELD,
+ NlAttrNested,
+ rtnl_iflaf_vf_driver_field_attrs,
+ ),
+]
+
+rtnl_iflaf_vf_attrs = [
+ AttrDescr(IflafVfType.IFLAF_VF_INDEX, NlAttrU32),
+ AttrDescr(IflafVfType.IFLAF_VF_CONFIGURED, NlAttrU8),
+ AttrDescr(IflafVfType.IFLAF_VF_INITIALIZED, NlAttrU8),
+ AttrDescr(IflafVfType.IFLAF_VF_MAC, NlAttrMac),
+ AttrDescr(IflafVfType.IFLAF_VF_VLAN_MODE, NlAttrU8),
+ AttrDescr(IflafVfType.IFLAF_VF_VLAN, NlAttrU16),
+ AttrDescr(IflafVfType.IFLAF_VF_VLAN_COUNT, NlAttrU32),
+ AttrDescr(IflafVfType.IFLAF_VF_VLAN_LIMIT, NlAttrU32),
+ AttrDescr(IflafVfType.IFLAF_VF_NUM_QUEUES, NlAttrU32),
+ AttrDescr(IflafVfType.IFLAF_VF_ALLOW_SET_MAC, NlAttrU8),
+ AttrDescr(IflafVfType.IFLAF_VF_ALLOW_SET_VLAN, NlAttrU8),
+ AttrDescr(IflafVfType.IFLAF_VF_MAC_ANTI_SPOOF, NlAttrU8),
+ AttrDescr(IflafVfType.IFLAF_VF_ALLOW_PROMISC, NlAttrU8),
+ AttrDescr(IflafVfType.IFLAF_VF_TRAFFIC_ENABLED, NlAttrU8),
+ AttrDescr(IflafVfType.IFLAF_VF_MDD_BLOCKED, NlAttrU8),
+ AttrDescr(IflafVfType.IFLAF_VF_QUARANTINED, NlAttrU8),
+ AttrDescr(IflafVfType.IFLAF_VF_API_VERSION, NlAttrStr),
+ AttrDescr(IflafVfType.IFLAF_VF_LINK_STATE_POLICY, NlAttrU8),
+ AttrDescr(
+ IflafVfType.IFLAF_VF_DRIVER,
+ NlAttrNested,
+ rtnl_iflaf_vf_driver_attrs,
+ ),
+]
+
+rtnl_iflaf_vf_status_attrs = [
+ AttrDescr(IflafVfStatusType.IFLAF_VFS_ERROR, NlAttrU32),
+ AttrDescr(IflafVfStatusType.IFLAF_VFS_PF_LINK_STATE, NlAttrU8),
+ AttrDescr(IflafVfStatusType.IFLAF_VFS_PF_LINK_SPEED, NlAttrU64),
+ AttrDescr(
+ IflafVfStatusType.IFLAF_VFS_VF,
+ NlAttrNested,
+ rtnl_iflaf_vf_attrs,
+ ),
+]
+
rtnl_ifla_attrs = prepare_attrs_map(
[
AttrDescr(IflattrType.IFLA_ADDRESS, NlAttrMac),
@@ -632,6 +738,7 @@
AttrDescr(IflattrType.IFLA_OPERSTATE, NlAttrU8),
AttrDescr(IflattrType.IFLA_CARRIER, NlAttrU8),
AttrDescr(IflattrType.IFLA_IFALIAS, NlAttrStr),
+ AttrDescr(IflattrType.IFLA_NUM_VF, NlAttrU32),
AttrDescr(IflattrType.IFLA_STATS64, NlAttrIfStats),
AttrDescr(IflattrType.IFLA_NEW_IFINDEX, NlAttrU32),
AttrDescr(
@@ -647,6 +754,11 @@
NlAttrNested,
[
AttrDescr(IflafAttrType.IFLAF_ORIG_HWADDR, NlAttrMac),
+ AttrDescr(
+ IflafAttrType.IFLAF_VF_STATUS,
+ NlAttrNested,
+ rtnl_iflaf_vf_status_attrs,
+ ),
],
),
]
diff --git a/tests/sys/netlink/test_rtnl_iface.py b/tests/sys/netlink/test_rtnl_iface.py
--- a/tests/sys/netlink/test_rtnl_iface.py
+++ b/tests/sys/netlink/test_rtnl_iface.py
@@ -3,6 +3,7 @@
import pytest
from atf_python.sys.netlink.netlink_route import IflattrType
+from atf_python.sys.netlink.netlink_route import IflafAttrType
from atf_python.sys.netlink.netlink_route import IflinkInfo
from atf_python.sys.netlink.netlink_route import IfLinkInfoDataVlan
from atf_python.sys.netlink.netlink_route import NetlinkIflaMessage
@@ -27,12 +28,14 @@
super().setup_method(method)
self.setup_netlink(NlConst.NETLINK_ROUTE)
- def get_interface_byname(self, ifname):
+ def get_interface_byname(self, ifname, ext_mask=0):
msg = NetlinkIflaMessage(self.helper, NlRtMsgType.RTM_GETLINK.value)
msg.nl_hdr.nlmsg_flags = (
NlmBaseFlags.NLM_F_ACK.value | NlmBaseFlags.NLM_F_REQUEST.value
)
msg.add_nla(NlAttrStr(IflattrType.IFLA_IFNAME, ifname))
+ if ext_mask != 0:
+ msg.add_nla(NlAttrU32(IflattrType.IFLA_EXT_MASK, ext_mask))
self.write_message(msg)
while True:
rx_msg = self.read_message()
@@ -69,6 +72,14 @@
assert rx_msg.is_type(NlMsgType.NLMSG_ERROR)
assert rx_msg.error_code == errno.ENODEV
+ def test_get_iface_vf_filter_unsupported(self):
+ """Tests that the VF filter is accepted on a non-SR-IOV interface"""
+ rx_msg = self.get_interface_byname("lo0", 1)
+ assert rx_msg.get_nla(IflattrType.IFLA_NUM_VF) is None
+ fbsd = rx_msg.get_nla(IflattrType.IFLA_FREEBSD)
+ assert fbsd is not None
+ assert fbsd.get_nla(IflafAttrType.IFLAF_VF_STATUS) is None
+
@pytest.mark.require_user("root")
def test_create_iface_plain(self):
"""Tests loopback creation w/o any parameters"""
diff --git a/tests/sys/netlink/test_snl.c b/tests/sys/netlink/test_snl.c
--- a/tests/sys/netlink/test_snl.c
+++ b/tests/sys/netlink/test_snl.c
@@ -20,6 +20,8 @@
static const struct snl_hdr_parser *snl_all_route_parsers[] = {
&_metrics_mp_nh_parser, &_mpath_nh_parser, &_metrics_parser, &snl_rtm_route_parser,
+ &_vf_driver_field_parser, &_vf_driver_parser, &_vf_parser,
+ &_vf_status_parser,
&_link_fbsd_parser, &snl_rtm_link_parser, &snl_rtm_link_parser_simple,
&_neigh_fbsd_parser, &snl_rtm_neigh_parser,
&_addr_fbsd_parser, &snl_rtm_addr_parser, &_nh_fbsd_parser, &snl_nhmsg_parser,
@@ -101,6 +103,127 @@
ATF_CHECK_STREQ(bit->bit_name, "bit-8");
ATF_CHECK_EQ(bit->bit_value, 1);
}
+
+ATF_TC(snl_parse_vf_status);
+ATF_TC_HEAD(snl_parse_vf_status, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Tests snl(3) parsing nested VF status");
+ atf_tc_set_md_var(tc, "require.kmods", "netlink");
+}
+
+ATF_TC_BODY(snl_parse_vf_status, tc)
+{
+ static const uint8_t mac[] = { 0x02, 0, 0, 0, 0, 1 };
+ static const uint8_t extension_data[] = { 1, 2, 3, 4 };
+ struct snl_parsed_vf_driver_field *field;
+ struct snl_parsed_vf_driver *driver;
+ struct snl_parsed_vf_status *status;
+ struct snl_parsed_link link = {};
+ struct snl_parsed_vf *vf;
+ struct snl_state ss;
+ struct snl_writer nw;
+ struct nlmsghdr *hdr;
+ uint32_t i;
+ int driver_off, entry_off, fbsd_off, field_off, status_off;
+
+ ATF_REQUIRE(snl_init(&ss, NETLINK_ROUTE));
+ snl_init_writer(&ss, &nw);
+ hdr = snl_create_msg_request(&nw, RTM_NEWLINK);
+ ATF_REQUIRE(hdr != NULL);
+ ATF_REQUIRE(snl_reserve_msg_object(&nw, struct ifinfomsg) != NULL);
+ ATF_REQUIRE(snl_add_msg_attr_u32(&nw, IFLA_NUM_VF, 9));
+
+ fbsd_off = snl_add_msg_attr_nested(&nw, IFLA_FREEBSD);
+ ATF_REQUIRE(fbsd_off != 0);
+ status_off = snl_add_msg_attr_nested(&nw, IFLAF_VF_STATUS);
+ ATF_REQUIRE(status_off != 0);
+ ATF_REQUIRE(snl_add_msg_attr_u8(&nw, IFLAF_VFS_PF_LINK_STATE,
+ IFLAF_VF_LINK_UP));
+ ATF_REQUIRE(snl_add_msg_attr_u64(&nw, IFLAF_VFS_PF_LINK_SPEED,
+ 10000000000ULL));
+
+ entry_off = snl_add_msg_attr_nested(&nw, IFLAF_VFS_VF);
+ ATF_REQUIRE(entry_off != 0);
+ ATF_REQUIRE(snl_add_msg_attr_u32(&nw, IFLAF_VF_INDEX, 7));
+ ATF_REQUIRE(snl_add_msg_attr_bool(&nw, IFLAF_VF_CONFIGURED, true));
+ ATF_REQUIRE(snl_add_msg_attr(&nw, IFLAF_VF_MAC, sizeof(mac), mac));
+
+ driver_off = snl_add_msg_attr_nested(&nw, IFLAF_VF_DRIVER);
+ ATF_REQUIRE(driver_off != 0);
+ ATF_REQUIRE(snl_add_msg_attr_string(&nw, IFLAF_VFD_NAME,
+ "driver.test"));
+ ATF_REQUIRE(snl_add_msg_attr_u32(&nw, IFLAF_VFD_VERSION, 3));
+ field_off = snl_add_msg_attr_nested(&nw, IFLAF_VFD_FIELD);
+ ATF_REQUIRE(field_off != 0);
+ ATF_REQUIRE(snl_add_msg_attr_string(&nw, IFLAF_VFDF_NAME,
+ "ready"));
+ ATF_REQUIRE(snl_add_msg_attr_bool(&nw, IFLAF_VFDF_BOOL, false));
+ snl_end_attr_nested(&nw, field_off);
+ field_off = snl_add_msg_attr_nested(&nw, IFLAF_VFD_FIELD);
+ ATF_REQUIRE(field_off != 0);
+ ATF_REQUIRE(snl_add_msg_attr_string(&nw, IFLAF_VFDF_NAME,
+ "opaque"));
+ ATF_REQUIRE(snl_add_msg_attr(&nw, IFLAF_VFDF_BINARY,
+ sizeof(extension_data), extension_data));
+ snl_end_attr_nested(&nw, field_off);
+ snl_end_attr_nested(&nw, driver_off);
+ snl_end_attr_nested(&nw, entry_off);
+
+ /* A second attribute of the same type is the second array member. */
+ entry_off = snl_add_msg_attr_nested(&nw, IFLAF_VFS_VF);
+ ATF_REQUIRE(entry_off != 0);
+ ATF_REQUIRE(snl_add_msg_attr_u32(&nw, IFLAF_VF_INDEX, 8));
+ ATF_REQUIRE(snl_add_msg_attr_bool(&nw, IFLAF_VF_CONFIGURED, false));
+ snl_end_attr_nested(&nw, entry_off);
+ /* Cross the parser array's initial capacity to exercise growth. */
+ for (i = 9; i <= 15; i++) {
+ entry_off = snl_add_msg_attr_nested(&nw, IFLAF_VFS_VF);
+ ATF_REQUIRE(entry_off != 0);
+ ATF_REQUIRE(snl_add_msg_attr_u32(&nw, IFLAF_VF_INDEX, i));
+ snl_end_attr_nested(&nw, entry_off);
+ }
+ snl_end_attr_nested(&nw, status_off);
+ snl_end_attr_nested(&nw, fbsd_off);
+ hdr = snl_finalize_msg(&nw);
+ ATF_REQUIRE(hdr != NULL);
+
+ ATF_REQUIRE(snl_parse_nlmsg(&ss, hdr, &snl_rtm_link_parser, &link));
+ ATF_CHECK_EQ(link.ifla_num_vf, 9);
+ status = &link.iflaf_vf_status;
+ ATF_CHECK(status->present);
+ ATF_CHECK_EQ(status->pf_link_state, IFLAF_VF_LINK_UP);
+ ATF_CHECK_EQ(status->pf_link_speed, 10000000000ULL);
+ ATF_REQUIRE_EQ(status->vfs.count, 9);
+ vf = status->vfs.items[0];
+ ATF_CHECK_EQ(vf->index, 7);
+ ATF_CHECK_EQ(vf->configured, 1);
+ ATF_REQUIRE(vf->mac != NULL);
+ ATF_CHECK_EQ(NLA_DATA_LEN(vf->mac), sizeof(mac));
+ ATF_CHECK(memcmp(NLA_DATA(vf->mac), mac, sizeof(mac)) == 0);
+ ATF_REQUIRE_EQ(vf->drivers.count, 1);
+ driver = vf->drivers.items[0];
+ ATF_CHECK_STREQ(driver->name, "driver.test");
+ ATF_CHECK_EQ(driver->version, 3);
+ ATF_REQUIRE_EQ(driver->fields.count, 2);
+ field = driver->fields.items[0];
+ ATF_CHECK_STREQ(field->name, "ready");
+ ATF_CHECK_EQ(field->type, SNL_VFDF_BOOL);
+ ATF_CHECK_EQ(field->boolean, false);
+ field = driver->fields.items[1];
+ ATF_CHECK_STREQ(field->name, "opaque");
+ ATF_CHECK_EQ(field->type, SNL_VFDF_BINARY);
+ ATF_REQUIRE(field->binary != NULL);
+ ATF_CHECK_EQ(NLA_DATA_LEN(field->binary), sizeof(extension_data));
+ ATF_CHECK(memcmp(NLA_DATA(field->binary), extension_data,
+ sizeof(extension_data)) == 0);
+ vf = status->vfs.items[1];
+ ATF_CHECK_EQ(vf->index, 8);
+ ATF_CHECK_EQ(vf->configured, false);
+ vf = status->vfs.items[8];
+ ATF_CHECK_EQ(vf->index, 15);
+}
+
ATF_TC(snl_verify_route_parsers);
ATF_TC_HEAD(snl_verify_route_parsers, tc)
{
@@ -296,6 +419,7 @@
ATF_TP_ADD_TC(tp, snl_verify_core_parsers);
ATF_TP_ADD_TC(tp, snl_parse_bitset_array);
ATF_TP_ADD_TC(tp, snl_verify_route_parsers);
+ ATF_TP_ADD_TC(tp, snl_parse_vf_status);
ATF_TP_ADD_TC(tp, snl_parse_errmsg_capped);
ATF_TP_ADD_TC(tp, snl_parse_errmsg_capped_extack);
ATF_TP_ADD_TC(tp, snl_parse_errmsg_uncapped_extack);

File Metadata

Mime Type
text/plain
Expires
Sat, Oct 10, 10:48 AM (19 h, 15 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
40541139
Default Alt Text
D58776.id183852.diff (58 KB)

Event Timeline