Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F175251503
D58776.id183852.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
58 KB
Referenced Files
None
Subscribers
None
D58776.id183852.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D58776: rtnetlink: Add native SR-IOV VF status
Attached
Detach File
Event Timeline
Log In to Comment