Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 09 Aug 2026 06:47:48 +0000
From:      Kevin Bowling <kbowling@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: d15f2551b25f - main - rtnetlink: Report SR-IOV VF status
Message-ID:  <6a782294.3e4e1.77fae9fb@gitrepo.freebsd.org>

index | next in thread | raw e-mail

The branch main has been updated by kbowling:

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

commit d15f2551b25f79ddcbe289faa95e655100b952da
Author:     Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-08-09 02:04:11 +0000
Commit:     Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-08-09 06:46:57 +0000

    rtnetlink: Report SR-IOV VF status
    
    Honor RTEXT_FILTER_VF on RTM_GETLINK requests and expose the versioned
    SR-IOV VF status through typed nested FreeBSD attributes.  Report
    IFLA_NUM_VF with a successful requested query and preserve per-provider
    errors in the status container.
    
    Map the common nvlist schema to native integer, boolean, string, and
    binary attributes.  Carry namespaced driver extensions as packed
    versioned nvlists so adding a driver-specific field does not expand the
    common netlink ABI.
    
    Add SNL parsers, parser verification, a constructed nested-status test,
    and an RTM_GETLINK test for an interface without SR-IOV support.
    Document the query contract and every attribute.
---
 share/man/man4/rtnetlink.4                    | 118 ++++++++++-
 sys/netlink/netlink_snl_route_parsers.h       | 112 ++++++++++
 sys/netlink/route/iface.c                     | 284 +++++++++++++++++++++++++-
 sys/netlink/route/interface.h                 |  82 +++++++-
 sys/netlink/route/route_var.h                 |   1 +
 tests/atf_python/sys/netlink/attrs.py         |  28 +++
 tests/atf_python/sys/netlink/netlink_route.py | 117 +++++++++++
 tests/sys/netlink/test_rtnl_iface.py          |  13 +-
 tests/sys/netlink/test_snl.c                  |  89 ++++++++
 9 files changed, 835 insertions(+), 9 deletions(-)

diff --git a/share/man/man4/rtnetlink.4 b/share/man/man4/rtnetlink.4
index 3d76c66c1917..5d5e69cdbbf0 100644
--- 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 8, 2026
 .Dt RTNETLINK 4
 .Os
 .Sh NAME
@@ -325,7 +325,17 @@ The following filters are recognised by the kernel:
 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,112 @@ IFLA_ALT_IFNAME	interface name
 (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_VERSION	(uint32_t) VF status schema version
+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_LIST		(nested) repeated IFLAF_VF_LIST_ENTRY
+.Ed
+.Pp
+On a successful query,
+.Dv IFLAF_VFS_VERSION
+and
+.Dv IFLAF_VFS_LIST
+are present.
+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_VF_LIST_ENTRY
+may contain the following attributes:
+.Bd -literal -offset indent -compact
+IFLAF_VF_INDEX			(uint32_t) PF-local VF index
+IFLAF_VF_CONFIGURED		(uint8_t) PF accepted configuration
+IFLAF_VF_INITIALIZED		(uint8_t) 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		(uint8_t) administrative permission
+IFLAF_VF_ALLOW_SET_VLAN		(uint8_t) administrative permission
+IFLAF_VF_MAC_ANTI_SPOOF		(uint8_t) MAC anti-spoofing enabled
+IFLAF_VF_ALLOW_PROMISC		(uint8_t) administrative permission
+IFLAF_VF_TRAFFIC_ENABLED	(uint8_t) VF traffic enabled
+IFLAF_VF_MDD_BLOCKED		(uint8_t) malicious-driver block active
+IFLAF_VF_QUARANTINED		(uint8_t) persistent quarantine active
+IFLAF_VF_API_VERSION		(string) negotiated mailbox API
+IFLAF_VF_LINK_STATE_POLICY	(uint8_t) IFLAF_VF_LINK_*
+IFLAF_VF_EXTENSIONS		(nested) driver-specific namespaces
+.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 zero or one.
+.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_EXTENSIONS
+as repeated
+.Dv IFLAF_VF_EXT_LIST_ENTRY
+objects.
+Each object contains a stable namespace string in
+.Dv IFLAF_VF_EXT_NAME
+and its packed, versioned
+.Xr nvlist 9
+in
+.Dv IFLAF_VF_EXT_DATA .
+Each namespace contains a version number.
+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/netlink/netlink_snl_route_parsers.h b/sys/netlink/netlink_snl_route_parsers.h
index f0849884ec97..9f699ef528a3 100644
--- a/sys/netlink/netlink_snl_route_parsers.h
+++ b/sys/netlink/netlink_snl_route_parsers.h
@@ -178,6 +178,113 @@ SNL_DECLARE_PARSER_EXT(snl_rtm_route_parser, sizeof(struct rtmsg),
 		_cb_p_route);
 
 /* RTM_<NEW|DEL|GET>LINK message parser */
+struct snl_parsed_vf_extension {
+	char			*name;
+	struct nlattr		*data;
+};
+
+#define	_OUT(_field)	offsetof(struct snl_parsed_vf_extension, _field)
+static const struct snl_attr_parser _nla_p_vf_extension[] = {
+	{ .type = IFLAF_VF_EXT_NAME, .off = _OUT(name),
+	    .cb = snl_attr_dup_string },
+	{ .type = IFLAF_VF_EXT_DATA, .off = _OUT(data),
+	    .cb = snl_attr_dup_nla },
+};
+#undef _OUT
+SNL_DECLARE_ATTR_PARSER_EXT(_vf_extension_parser,
+	    sizeof(struct snl_parsed_vf_extension), _nla_p_vf_extension, NULL);
+
+struct snl_parsed_vf {
+	uint32_t		index;
+	uint32_t		vlan_count;
+	uint32_t		vlan_limit;
+	uint32_t		num_queues;
+	uint16_t		vlan;
+	uint8_t			configured;
+	uint8_t			initialized;
+	uint8_t			vlan_mode;
+	uint8_t			allow_set_mac;
+	uint8_t			allow_set_vlan;
+	uint8_t			mac_anti_spoof;
+	uint8_t			allow_promisc;
+	uint8_t			traffic_enabled;
+	uint8_t			mdd_blocked;
+	uint8_t			quarantined;
+	uint8_t			link_state_policy;
+	char			*api_version;
+	struct nlattr		*mac;
+	struct snl_parray	extensions;
+};
+
+#define	_OUT(_field)	offsetof(struct snl_parsed_vf, _field)
+static const struct snl_attr_parser _nla_p_vf[] = {
+	{ .type = IFLAF_VF_INDEX, .off = _OUT(index),
+	    .cb = snl_attr_get_uint32 },
+	{ .type = IFLAF_VF_CONFIGURED, .off = _OUT(configured),
+	    .cb = snl_attr_get_uint8 },
+	{ .type = IFLAF_VF_INITIALIZED, .off = _OUT(initialized),
+	    .cb = snl_attr_get_uint8 },
+	{ .type = IFLAF_VF_MAC, .off = _OUT(mac), .cb = snl_attr_dup_nla },
+	{ .type = IFLAF_VF_VLAN_MODE, .off = _OUT(vlan_mode),
+	    .cb = snl_attr_get_uint8 },
+	{ .type = IFLAF_VF_VLAN, .off = _OUT(vlan),
+	    .cb = snl_attr_get_uint16 },
+	{ .type = IFLAF_VF_VLAN_COUNT, .off = _OUT(vlan_count),
+	    .cb = snl_attr_get_uint32 },
+	{ .type = IFLAF_VF_VLAN_LIMIT, .off = _OUT(vlan_limit),
+	    .cb = snl_attr_get_uint32 },
+	{ .type = IFLAF_VF_NUM_QUEUES, .off = _OUT(num_queues),
+	    .cb = snl_attr_get_uint32 },
+	{ .type = IFLAF_VF_ALLOW_SET_MAC, .off = _OUT(allow_set_mac),
+	    .cb = snl_attr_get_uint8 },
+	{ .type = IFLAF_VF_ALLOW_SET_VLAN, .off = _OUT(allow_set_vlan),
+	    .cb = snl_attr_get_uint8 },
+	{ .type = IFLAF_VF_MAC_ANTI_SPOOF, .off = _OUT(mac_anti_spoof),
+	    .cb = snl_attr_get_uint8 },
+	{ .type = IFLAF_VF_ALLOW_PROMISC, .off = _OUT(allow_promisc),
+	    .cb = snl_attr_get_uint8 },
+	{ .type = IFLAF_VF_TRAFFIC_ENABLED, .off = _OUT(traffic_enabled),
+	    .cb = snl_attr_get_uint8 },
+	{ .type = IFLAF_VF_MDD_BLOCKED, .off = _OUT(mdd_blocked),
+	    .cb = snl_attr_get_uint8 },
+	{ .type = IFLAF_VF_QUARANTINED, .off = _OUT(quarantined),
+	    .cb = snl_attr_get_uint8 },
+	{ .type = IFLAF_VF_API_VERSION, .off = _OUT(api_version),
+	    .cb = snl_attr_dup_string },
+	{ .type = IFLAF_VF_LINK_STATE_POLICY,
+	    .off = _OUT(link_state_policy), .cb = snl_attr_get_uint8 },
+	{ .type = IFLAF_VF_EXTENSIONS, .off = _OUT(extensions),
+	    .arg = &_vf_extension_parser, .cb = snl_attr_get_parray },
+};
+#undef _OUT
+SNL_DECLARE_ATTR_PARSER_EXT(_vf_parser, sizeof(struct snl_parsed_vf),
+	    _nla_p_vf, NULL);
+
+struct snl_parsed_vf_status {
+	uint64_t		pf_link_speed;
+	uint32_t		version;
+	uint32_t		error;
+	uint8_t			pf_link_state;
+	struct snl_parray	vfs;
+};
+
+#define	_OUT(_field)	offsetof(struct snl_parsed_vf_status, _field)
+static const struct snl_attr_parser _nla_p_vf_status[] = {
+	{ .type = IFLAF_VFS_VERSION, .off = _OUT(version),
+	    .cb = snl_attr_get_uint32 },
+	{ .type = IFLAF_VFS_ERROR, .off = _OUT(error),
+	    .cb = snl_attr_get_uint32 },
+	{ .type = IFLAF_VFS_PF_LINK_STATE, .off = _OUT(pf_link_state),
+	    .cb = snl_attr_get_uint8 },
+	{ .type = IFLAF_VFS_PF_LINK_SPEED, .off = _OUT(pf_link_speed),
+	    .cb = snl_attr_get_uint64 },
+	{ .type = IFLAF_VFS_LIST, .off = _OUT(vfs),
+	    .arg = &_vf_parser, .cb = snl_attr_get_parray },
+};
+#undef _OUT
+SNL_DECLARE_ATTR_PARSER_EXT(_vf_status_parser,
+	    sizeof(struct snl_parsed_vf_status), _nla_p_vf_status, NULL);
+
 struct snl_parsed_link {
 	uint32_t			ifi_index;
 	uint32_t			ifi_flags;
@@ -191,9 +298,11 @@ struct snl_parsed_link {
 	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)
@@ -201,6 +310,8 @@ struct snl_parsed_link {
 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),
+	    .arg = &_vf_status_parser, .cb = snl_attr_get_nested },
 };
 SNL_DECLARE_ATTR_PARSER(_link_fbsd_parser, _nla_p_link_fbsd);
 
@@ -211,6 +322,7 @@ static const struct snl_attr_parser _nla_p_link[] = {
 	{ .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
index 5b6e58a598aa..bb04c2a94c1e 100644
--- a/sys/netlink/route/iface.c
+++ b/sys/netlink/route/iface.c
@@ -33,6 +33,7 @@
 #include <sys/kernel.h>
 #include <sys/jail.h>
 #include <sys/malloc.h>
+#include <sys/nv.h>
 #include <sys/socket.h>
 #include <sys/sockio.h>
 #include <sys/syslog.h>
@@ -65,6 +66,7 @@ struct netlink_walkargs {
 	struct nlpcb *so;
 	struct ucred *cred;
 	uint32_t fibnum;
+	uint32_t ext_mask;
 	int family;
 	int error;
 	int count;
@@ -279,6 +281,245 @@ dump_iface_caps(struct nl_writer *nw, struct ifnet *ifp)
 	return (true);
 }
 
+static uint8_t
+vf_link_state(const char *state)
+{
+
+	if (strcmp(state, IFVF_LINK_STATE_DOWN) == 0)
+		return (IFLAF_VF_LINK_DOWN);
+	if (strcmp(state, IFVF_LINK_STATE_UP) == 0)
+		return (IFLAF_VF_LINK_UP);
+	if (strcmp(state, IFVF_LINK_STATE_AUTO) == 0)
+		return (IFLAF_VF_LINK_AUTO);
+	return (IFLAF_VF_LINK_UNKNOWN);
+}
+
+static bool
+dump_vf_bool(struct nl_writer *nw, const nvlist_t *vf, const char *name,
+    uint16_t attr)
+{
+
+	if (!nvlist_exists_bool(vf, name))
+		return (true);
+	return (nlattr_add_u8(nw, attr, nvlist_get_bool(vf, name) ? 1 : 0));
+}
+
+static bool
+dump_vf_u16(struct nl_writer *nw, const nvlist_t *vf, const char *name,
+    uint16_t attr)
+{
+	uint64_t value;
+
+	if (!nvlist_exists_number(vf, name))
+		return (true);
+	value = nvlist_get_number(vf, name);
+	if (value > UINT16_MAX)
+		return (false);
+	return (nlattr_add_u16(nw, attr, (uint16_t)value));
+}
+
+static bool
+dump_vf_u32(struct nl_writer *nw, const nvlist_t *vf, const char *name,
+    uint16_t attr)
+{
+	uint64_t value;
+
+	if (!nvlist_exists_number(vf, name))
+		return (true);
+	value = nvlist_get_number(vf, name);
+	if (value > UINT32_MAX)
+		return (false);
+	return (nlattr_add_u32(nw, attr, (uint32_t)value));
+}
+
+static bool
+dump_vf_string(struct nl_writer *nw, const nvlist_t *vf, const char *name,
+    uint16_t attr)
+{
+
+	if (!nvlist_exists_string(vf, name))
+		return (true);
+	return (nlattr_add_string(nw, attr, nvlist_get_string(vf, name)));
+}
+
+static bool
+dump_vf_extensions(struct nl_writer *nw, const nvlist_t *vf)
+{
+	const nvlist_t *extension, *extensions;
+	const char *name;
+	void *buf, *cookie;
+	size_t buflen;
+	int entry_off, extensions_off, type;
+	bool ok;
+
+	if (!nvlist_exists(vf, IFVF_STATUS_EXTENSIONS))
+		return (true);
+	if (!nvlist_exists_nvlist(vf, IFVF_STATUS_EXTENSIONS))
+		return (false);
+
+	extensions = nvlist_get_nvlist(vf, IFVF_STATUS_EXTENSIONS);
+	extensions_off = nlattr_add_nested(nw, IFLAF_VF_EXTENSIONS);
+	if (extensions_off == 0)
+		return (false);
+	cookie = NULL;
+	while ((name = nvlist_next(extensions, &type, &cookie)) != NULL) {
+		if (type != NV_TYPE_NVLIST)
+			return (false);
+		extension = nvlist_get_nvlist(extensions, name);
+		if (!nvlist_exists_number(extension,
+		    IFVF_STATUS_EXT_VERSION))
+			return (false);
+		buf = nvlist_pack(extension, &buflen);
+		if (buf == NULL)
+			return (false);
+		if (buflen > UINT16_MAX - sizeof(struct nlattr)) {
+			free(buf, M_NVLIST);
+			return (false);
+		}
+		entry_off = nlattr_add_nested(nw,
+		    IFLAF_VF_EXT_LIST_ENTRY);
+		if (entry_off == 0) {
+			free(buf, M_NVLIST);
+			return (false);
+		}
+		ok = nlattr_add_string(nw, IFLAF_VF_EXT_NAME, name) &&
+		    nlattr_add(nw, IFLAF_VF_EXT_DATA, (uint16_t)buflen, buf);
+		free(buf, M_NVLIST);
+		if (!ok)
+			return (false);
+		nlattr_set_len(nw, entry_off);
+	}
+	nlattr_set_len(nw, extensions_off);
+	return (true);
+}
+
+static bool
+dump_vf_entry(struct nl_writer *nw, const nvlist_t *vf)
+{
+	const void *mac;
+	const char *mode, *policy;
+	size_t maclen;
+	uint8_t vlan_mode;
+	int off;
+	bool ok;
+
+	off = nlattr_add_nested(nw, IFLAF_VF_LIST_ENTRY);
+	if (off == 0)
+		return (false);
+	ok = dump_vf_u32(nw, vf, IFVF_STATUS_INDEX, IFLAF_VF_INDEX) &&
+	    dump_vf_bool(nw, vf, IFVF_STATUS_CONFIGURED,
+	    IFLAF_VF_CONFIGURED) &&
+	    dump_vf_bool(nw, vf, IFVF_STATUS_INITIALIZED,
+	    IFLAF_VF_INITIALIZED);
+	if (!ok)
+		return (false);
+
+	if (nvlist_exists_binary(vf, IFVF_STATUS_MAC)) {
+		mac = nvlist_get_binary(vf, IFVF_STATUS_MAC, &maclen);
+		if (maclen > UINT16_MAX - sizeof(struct nlattr) ||
+		    !nlattr_add(nw, IFLAF_VF_MAC, (uint16_t)maclen, mac))
+			return (false);
+	}
+	if (nvlist_exists_string(vf, IFVF_STATUS_VLAN_MODE)) {
+		mode = nvlist_get_string(vf, IFVF_STATUS_VLAN_MODE);
+		if (strcmp(mode, IFVF_VLAN_MODE_ACCESS) == 0)
+			vlan_mode = IFLAF_VF_VLAN_ACCESS;
+		else if (strcmp(mode, IFVF_VLAN_MODE_TRUNK) == 0)
+			vlan_mode = IFLAF_VF_VLAN_TRUNK;
+		else
+			vlan_mode = IFLAF_VF_VLAN_UNKNOWN;
+		if (!nlattr_add_u8(nw, IFLAF_VF_VLAN_MODE, vlan_mode))
+			return (false);
+	}
+	ok = dump_vf_u16(nw, vf, IFVF_STATUS_VLAN, IFLAF_VF_VLAN) &&
+	    dump_vf_u32(nw, vf, IFVF_STATUS_VLAN_COUNT,
+	    IFLAF_VF_VLAN_COUNT) &&
+	    dump_vf_u32(nw, vf, IFVF_STATUS_VLAN_LIMIT,
+	    IFLAF_VF_VLAN_LIMIT) &&
+	    dump_vf_u32(nw, vf, IFVF_STATUS_NUM_QUEUES,
+	    IFLAF_VF_NUM_QUEUES) &&
+	    dump_vf_bool(nw, vf, IFVF_STATUS_ALLOW_SET_MAC,
+	    IFLAF_VF_ALLOW_SET_MAC) &&
+	    dump_vf_bool(nw, vf, IFVF_STATUS_ALLOW_SET_VLAN,
+	    IFLAF_VF_ALLOW_SET_VLAN) &&
+	    dump_vf_bool(nw, vf, IFVF_STATUS_MAC_ANTI_SPOOF,
+	    IFLAF_VF_MAC_ANTI_SPOOF) &&
+	    dump_vf_bool(nw, vf, IFVF_STATUS_ALLOW_PROMISC,
+	    IFLAF_VF_ALLOW_PROMISC) &&
+	    dump_vf_bool(nw, vf, IFVF_STATUS_TRAFFIC_ENABLED,
+	    IFLAF_VF_TRAFFIC_ENABLED) &&
+	    dump_vf_bool(nw, vf, IFVF_STATUS_MDD_BLOCKED,
+	    IFLAF_VF_MDD_BLOCKED) &&
+	    dump_vf_bool(nw, vf, IFVF_STATUS_QUARANTINED,
+	    IFLAF_VF_QUARANTINED) &&
+	    dump_vf_string(nw, vf, IFVF_STATUS_API_VERSION,
+	    IFLAF_VF_API_VERSION);
+	if (!ok)
+		return (false);
+	if (nvlist_exists_string(vf, IFVF_STATUS_LINK_STATE_POLICY)) {
+		policy = nvlist_get_string(vf,
+		    IFVF_STATUS_LINK_STATE_POLICY);
+		if (!nlattr_add_u8(nw, IFLAF_VF_LINK_STATE_POLICY,
+		    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, const nvlist_t *status, int error)
+{
+	const nvlist_t * const *vfs;
+	const char *state;
+	size_t i, num_vfs;
+	uint64_t version;
+	int list_off, 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);
+	}
+
+	if (!nvlist_exists_number(status, IFVF_STATUS_VERSION_KEY))
+		return (false);
+	version = nvlist_get_number(status, IFVF_STATUS_VERSION_KEY);
+	if (version > UINT32_MAX ||
+	    !nlattr_add_u32(nw, IFLAF_VFS_VERSION, (uint32_t)version))
+		return (false);
+	if (nvlist_exists_string(status, IFVF_STATUS_PF_LINK_STATE)) {
+		state = nvlist_get_string(status, IFVF_STATUS_PF_LINK_STATE);
+		if (!nlattr_add_u8(nw, IFLAF_VFS_PF_LINK_STATE,
+		    vf_link_state(state)))
+			return (false);
+	}
+	if (nvlist_exists_number(status, IFVF_STATUS_PF_LINK_SPEED) &&
+	    !nlattr_add_u64(nw, IFLAF_VFS_PF_LINK_SPEED,
+	    nvlist_get_number(status, IFVF_STATUS_PF_LINK_SPEED)))
+		return (false);
+	if (!nvlist_exists_nvlist_array(status, IFVF_STATUS_VFS))
+		return (false);
+
+	vfs = nvlist_get_nvlist_array(status, IFVF_STATUS_VFS, &num_vfs);
+	list_off = nlattr_add_nested(nw, IFLAF_VFS_LIST);
+	if (list_off == 0)
+		return (false);
+	for (i = 0; i < num_vfs; i++) {
+		if (!dump_vf_entry(nw, vfs[i]))
+			return (false);
+	}
+	nlattr_set_len(nw, list_off);
+	nlattr_set_len(nw, off);
+	return (true);
+}
+
 /*
  * Dumps interface state, properties and metrics.
  * @nw: message writer
@@ -290,12 +531,26 @@ dump_iface_caps(struct nl_writer *nw, struct ifnet *ifp)
  */
 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;
+	nvlist_t *vf_status;
+	size_t num_vfs;
         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 +596,14 @@ dump_iface(struct nl_writer *nw, if_t ifp, const struct nlmsghdr *hdr,
         }
 
         nlattr_add_u32(nw, IFLA_MTU, if_getmtu(ifp));
+	if (vf_status != NULL &&
+	    nvlist_exists_nvlist_array(vf_status, IFVF_STATUS_VFS)) {
+		(void)nvlist_get_nvlist_array(vf_status, IFVF_STATUS_VFS,
+		    &num_vfs);
+		if (num_vfs > UINT32_MAX ||
+		    !nlattr_add_u32(nw, IFLA_NUM_VF, (uint32_t)num_vfs))
+			goto enomem;
+	}
 /*
         nlattr_add_u32(nw, IFLA_MIN_MTU, 60);
         nlattr_add_u32(nw, IFLA_MAX_MTU, 9000);
@@ -355,6 +618,9 @@ dump_iface(struct nl_writer *nw, if_t ifp, const struct nlmsghdr *hdr,
 	if (off != 0) {
 		get_hwaddr(nw, ifp);
 		dump_iface_caps(nw, ifp);
+		if (include_vf_status &&
+		    !dump_vf_status(nw, vf_status, vf_error))
+			goto enomem;
 
 		nlattr_set_len(nw, off);
 	}
@@ -368,11 +634,14 @@ dump_iface(struct nl_writer *nw, if_t ifp, const struct nlmsghdr *hdr,
 
 	nw->ifp = ifp;
 
-        if (nlmsg_end(nw))
+	if (nlmsg_end(nw)) {
+		nvlist_destroy(vf_status);
 		return (true);
+	}
 
 enomem:
-        NL_LOG(LOG_DEBUG, "unable to dump interface %s state (ENOMEM)", if_name(ifp));
+	nvlist_destroy(vf_status);
+	NL_LOG(LOG_DEBUG, "unable to dump interface %s state", if_name(ifp));
         nlmsg_abort(nw);
         return (false);
 }
@@ -415,6 +684,7 @@ static const struct nlattr_parser nla_p_if[] = {
 	{ .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 +711,7 @@ static int
 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 +743,7 @@ rtnl_handle_getlink(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_pstate *n
 		.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 +762,8 @@ rtnl_handle_getlink(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_pstate *n
 
 		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 +1690,7 @@ rtnl_handle_ifevent(if_t ifp, int nlmsg_type, int if_flags_mask,
 		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
index c018a0adb20d..66bab24067df 100644
--- a/sys/netlink/route/interface.h
+++ b/sys/netlink/route/interface.h
@@ -94,7 +94,7 @@ enum {
 #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,95 @@ enum {
 };
 #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_VERSION	= 1,	/* u32: IFVF_STATUS_VERSION */
+	IFLAF_VFS_ERROR		= 2,	/* u32: errno from requested query */
+	IFLAF_VFS_PF_LINK_STATE	= 3,	/* u8: IFLAF_VF_LINK_* */
+	IFLAF_VFS_PF_LINK_SPEED	= 4,	/* u64: bits per second */
+	IFLAF_VFS_LIST		= 5,	/* nested, IFLAF_VF_LIST_* */
+	__IFLAF_VFS_MAX
+};
+#define	IFLAF_VFS_MAX	(__IFLAF_VFS_MAX - 1)
+
+/* IFLAF_VFS_LIST attributes. */
+enum {
+	IFLAF_VF_LIST_UNSPEC	= 0,
+	IFLAF_VF_LIST_ENTRY	= 1,	/* nested, IFLAF_VF_*; repeated */
+	__IFLAF_VF_LIST_MAX
+};
+#define	IFLAF_VF_LIST_MAX	(__IFLAF_VF_LIST_MAX - 1)
+
+/* IFLAF_VF_LIST_ENTRY attributes. */
+enum {
+	IFLAF_VF_UNSPEC			= 0,
+	IFLAF_VF_INDEX			= 1,	/* u32 */
+	IFLAF_VF_CONFIGURED		= 2,	/* u8 boolean */
+	IFLAF_VF_INITIALIZED		= 3,	/* u8 boolean */
+	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,	/* u8 boolean */
+	IFLAF_VF_ALLOW_SET_VLAN		= 11,	/* u8 boolean */
+	IFLAF_VF_MAC_ANTI_SPOOF		= 12,	/* u8 boolean */
+	IFLAF_VF_ALLOW_PROMISC		= 13,	/* u8 boolean */
+	IFLAF_VF_TRAFFIC_ENABLED	= 14,	/* u8 boolean */
+	IFLAF_VF_MDD_BLOCKED		= 15,	/* u8 boolean */
+	IFLAF_VF_QUARANTINED		= 16,	/* u8 boolean */
+	IFLAF_VF_API_VERSION		= 17,	/* string */
+	IFLAF_VF_LINK_STATE_POLICY	= 18,	/* u8: IFLAF_VF_LINK_* */
+	IFLAF_VF_EXTENSIONS		= 19,	/* nested, IFLAF_VF_EXT_LIST_* */
+	__IFLAF_VF_MAX
+};
+#define	IFLAF_VF_MAX	(__IFLAF_VF_MAX - 1)
+
+/* IFLAF_VF_EXTENSIONS attributes. */
+enum {
+	IFLAF_VF_EXT_LIST_UNSPEC	= 0,
+	IFLAF_VF_EXT_LIST_ENTRY		= 1,	/* nested; repeated */
+	__IFLAF_VF_EXT_LIST_MAX
+};
+#define	IFLAF_VF_EXT_LIST_MAX	(__IFLAF_VF_EXT_LIST_MAX - 1)
+
+/* IFLAF_VF_EXT_LIST_ENTRY attributes. */
+enum {
+	IFLAF_VF_EXT_UNSPEC	= 0,
+	IFLAF_VF_EXT_NAME	= 1,	/* string: stable namespace */
+	IFLAF_VF_EXT_DATA	= 2,	/* binary: packed namespace nvlist */
+	__IFLAF_VF_EXT_MAX
+};
+#define	IFLAF_VF_EXT_MAX	(__IFLAF_VF_EXT_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
index 012dc5ad0594..23adf012733f 100644
--- a/sys/netlink/route/route_var.h
+++ b/sys/netlink/route/route_var.h
@@ -74,6 +74,7 @@ struct nl_parsed_link {
 	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
index 36dd8191df1c..b0e03e50258e 100644
--- a/tests/atf_python/sys/netlink/attrs.py
+++ b/tests/atf_python/sys/netlink/attrs.py
@@ -137,6 +137,34 @@ class NlAttrU32(NlAttr):
         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
index 2cfeb57da13f..d6edc6ad875d 100644
--- 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 NlAttr
 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,56 @@ class IflafAttrType(Enum):
     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_VERSION = 1
+    IFLAF_VFS_ERROR = 2
+    IFLAF_VFS_PF_LINK_STATE = 3
+    IFLAF_VFS_PF_LINK_SPEED = 4
+    IFLAF_VFS_LIST = 5
+
+
+class IflafVfListType(Enum):
+    IFLAF_VF_LIST_UNSPEC = 0
+    IFLAF_VF_LIST_ENTRY = 1
+
+
+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_EXTENSIONS = 19
+
+
+class IflafVfExtListType(Enum):
+    IFLAF_VF_EXT_LIST_UNSPEC = 0
+    IFLAF_VF_EXT_LIST_ENTRY = 1
+
+
+class IflafVfExtType(Enum):
+    IFLAF_VF_EXT_UNSPEC = 0
+    IFLAF_VF_EXT_NAME = 1
+    IFLAF_VF_EXT_DATA = 2
 
 
 class IflinkInfo(Enum):
@@ -621,6 +673,65 @@ rtnl_route_attrs = prepare_attrs_map(
     ]
 )
 
+rtnl_iflaf_vf_ext_attrs = [
+    AttrDescr(IflafVfExtType.IFLAF_VF_EXT_NAME, NlAttrStr),
+    AttrDescr(IflafVfExtType.IFLAF_VF_EXT_DATA, NlAttr),
+]
+
+rtnl_iflaf_vf_ext_list_attrs = [
+    AttrDescr(
+        IflafVfExtListType.IFLAF_VF_EXT_LIST_ENTRY,
+        NlAttrNested,
+        rtnl_iflaf_vf_ext_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_EXTENSIONS,
+        NlAttrNested,
+        rtnl_iflaf_vf_ext_list_attrs,
+    ),
+]
+
+rtnl_iflaf_vf_list_attrs = [
+    AttrDescr(
+        IflafVfListType.IFLAF_VF_LIST_ENTRY,
+        NlAttrNested,
+        rtnl_iflaf_vf_attrs,
*** 195 LINES SKIPPED ***


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a782294.3e4e1.77fae9fb>