Re: git: d15f2551b25f - main - rtnetlink: Report SR-IOV VF status

Pouria Mousavizadeh Tehrani <[email protected]>
Newsgroups gmane.os.freebsd.devel.cvs.src
Message-ID <41b8cda3-9e4f-49f1-af9b-18c04e215ab1__30208.9529739291$1786389549$gmane$org@FreeBSD.org>
Hi

On 8/9/26 10:17, Kevin Bowling wrote:
> The branch main has been updated by kbowling:
> 
> URL: https://cgit.FreeBSD.org/src/commit/?id=d15f2551b25f79ddcbe289faa95e655100b952da
> 
> commit d15f2551b25f79ddcbe289faa95e655100b952da
> Author:     Kevin Bowling <[email protected]>
> AuthorDate: 2026-08-09 02:04:11 +0000
> Commit:     Kevin Bowling <[email protected]>
> 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 ***
> 

I think we should minimize our use of nvlist(9) in the new code, 
especially under netlink.
IMO, Using nvlist under netlink defeats the purpose.

looI don't think snl_attr_get_parray is appropriate for parser-only 
attributes.
We should use multi-attr instead to avoid too many nested netlink 
attributes. (e.g. /* nested, IFLAF_VF_*; repeated */)
https://docs.kernel.org/userspace-api/netlink/genetlink-legacy.html

We might also want to use snl_attr_get_bool or snl_attr_get_flag for 
many of the data structures above.

I wish there were a phabricator review for this change.

-- 
Pouria
OpenPGP_signature.asc (application/pgp-signature, 265 B)
-----BEGIN PGP SIGNATURE-----

iJEEARYKADkWIQSqt7cppfvJ816gj0lUwVnUeMwagAUCanojxRsUgAAAAAAEAA5t
YW51MiwyLjUrMS4xMiwyLDMACgkQVMFZ1HjMGoD4aAD+MkE/zLJHVZC2D5/yZlvE
SQy0UbDOfqmXA/x4+ifqghAA/RKGUUMwB3+Vc6e7q002q7mZynmAetvL4crKwhDA
q3oO
=5P+p
-----END PGP SIGNATURE-----
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.