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

Pouria Mousavizadeh Tehrani <[email protected]>
Newsgroups gmane.os.freebsd.devel.cvs.src,gmane.os.freebsd.current.scm
Message-ID <[email protected]>
On 8/10/26 22:56, Kevin Bowling wrote:
> On Mon, Aug 10, 2026 at 12:18 PM Pouria Mousavizadeh Tehrani
> <[email protected]> wrote:
>>
>> 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.
> 
> Hi,
> 
> Is there a better way to do something like this
> https://reviews.freebsd.org/D58739#change-0sX8k3Z9eTKs?

I see, I had the same problem on geneve implementation.
I asked on the mailing list and kp@ gave me some advice on it.
See this thread:
https://mail-archive.freebsd.org/cgi/getmsg.cgi?fetch=270856+0+archive/2025/freebsd-net/20251020.freebsd-net

Somehow, I managed to implement it directly with netlink:
https://reviews.freebsd.org/D54172?vs=174551&id=175142#toc

Hope it helps!

>> 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
>>
> 

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

iJEEARYKADkWIQSqt7cppfvJ816gj0lUwVnUeMwagAUCanorlBsUgAAAAAAEAA5t
YW51MiwyLjUrMS4xMiwyLDMACgkQVMFZ1HjMGoCcswEA8n2zwbOVDkOe+TFz5Xan
TAr3nlE7eJtdoWppieRxuCUA/A7hafMsZcsOEyadW3Qcb6tUSeGE4+Slu2odSctX
qJsJ
=Uqyy
-----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.