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

From: Kevin Bowling <kevin.bowling_at_kev009.com>
Date: Mon, 10 Aug 2026 20:15:55 UTC
On Mon, Aug 10, 2026 at 12:52 PM Pouria Mousavizadeh Tehrani
<pouria@freebsd.org> wrote:
>
> On 8/10/26 22:56, Kevin Bowling wrote:
> > On Mon, Aug 10, 2026 at 12:18 PM Pouria Mousavizadeh Tehrani
> > <pouria@freebsd.org> 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 <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 ***
> >>>
> >>
> >> 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!

I will ponder the links.  I'm not sure if the anchor I sent works as
intended on Phabricator, it was supposed to jump near "/* Render
scalar extension fields without coupling ifconfig to a driver. */"

The idea is to let the driver itself own whatever custom ontology
(which could theoretically live in ports or some external package like
Intel provides).


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