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.
This commit is contained in:
Kevin Bowling committed 2026-08-08 23:46:57 -07:00
1 parent 25de742864
commit d15f2551b2
9 files changed
+835 -9

No files matched your search

+117 -1
View File
@@ -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
+112
View File
@@ -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 },
+278 -6
View File
@@ -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);
}
+81 -1
View File
@@ -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
+1
View File
@@ -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;
};
+28
View File
@@ -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)
@@ -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,
),
]
rtnl_iflaf_vf_status_attrs = [
AttrDescr(IflafVfStatusType.IFLAF_VFS_VERSION, NlAttrU32),
AttrDescr(IflafVfStatusType.IFLAF_VFS_ERROR, NlAttrU32),
AttrDescr(IflafVfStatusType.IFLAF_VFS_PF_LINK_STATE, NlAttrU8),
AttrDescr(IflafVfStatusType.IFLAF_VFS_PF_LINK_SPEED, NlAttrU64),
AttrDescr(
IflafVfStatusType.IFLAF_VFS_LIST,
NlAttrNested,
rtnl_iflaf_vf_list_attrs,
),
]
rtnl_ifla_attrs = prepare_attrs_map(
[
AttrDescr(IflattrType.IFLA_ADDRESS, NlAttrMac),
@@ -632,6 +743,7 @@ rtnl_ifla_attrs = prepare_attrs_map(
AttrDescr(IflattrType.IFLA_OPERSTATE, NlAttrU8),
AttrDescr(IflattrType.IFLA_CARRIER, NlAttrU8),
AttrDescr(IflattrType.IFLA_IFALIAS, NlAttrStr),
AttrDescr(IflattrType.IFLA_NUM_VF, NlAttrU32),
AttrDescr(IflattrType.IFLA_STATS64, NlAttrIfStats),
AttrDescr(IflattrType.IFLA_NEW_IFINDEX, NlAttrU32),
AttrDescr(
@@ -647,6 +759,11 @@ rtnl_ifla_attrs = prepare_attrs_map(
NlAttrNested,
[
AttrDescr(IflafAttrType.IFLAF_ORIG_HWADDR, NlAttrMac),
AttrDescr(
IflafAttrType.IFLAF_VF_STATUS,
NlAttrNested,
rtnl_iflaf_vf_status_attrs,
),
],
),
]
+12 -1
View File
@@ -3,6 +3,7 @@ import socket
import pytest
from atf_python.sys.netlink.netlink_route import IflattrType
from atf_python.sys.netlink.netlink_route import IflafAttrType
from atf_python.sys.netlink.netlink_route import IflinkInfo
from atf_python.sys.netlink.netlink_route import IfLinkInfoDataVlan
from atf_python.sys.netlink.netlink_route import NetlinkIflaMessage
@@ -27,12 +28,14 @@ class TestRtNlIface(NetlinkTestTemplate, SingleVnetTestTemplate):
super().setup_method(method)
self.setup_netlink(NlConst.NETLINK_ROUTE)
def get_interface_byname(self, ifname):
def get_interface_byname(self, ifname, ext_mask=0):
msg = NetlinkIflaMessage(self.helper, NlRtMsgType.RTM_GETLINK.value)
msg.nl_hdr.nlmsg_flags = (
NlmBaseFlags.NLM_F_ACK.value | NlmBaseFlags.NLM_F_REQUEST.value
)
msg.add_nla(NlAttrStr(IflattrType.IFLA_IFNAME, ifname))
if ext_mask != 0:
msg.add_nla(NlAttrU32(IflattrType.IFLA_EXT_MASK, ext_mask))
self.write_message(msg)
while True:
rx_msg = self.read_message()
@@ -69,6 +72,14 @@ class TestRtNlIface(NetlinkTestTemplate, SingleVnetTestTemplate):
assert rx_msg.is_type(NlMsgType.NLMSG_ERROR)
assert rx_msg.error_code == errno.ENODEV
def test_get_iface_vf_filter_unsupported(self):
"""Tests that the VF filter is accepted on a non-SR-IOV interface"""
rx_msg = self.get_interface_byname("lo0", 1)
assert rx_msg.get_nla(IflattrType.IFLA_NUM_VF) is None
fbsd = rx_msg.get_nla(IflattrType.IFLA_FREEBSD)
assert fbsd is not None
assert fbsd.get_nla(IflafAttrType.IFLAF_VF_STATUS) is None
@pytest.mark.require_user("root")
def test_create_iface_plain(self):
"""Tests loopback creation w/o any parameters"""
+89
View File
@@ -20,6 +20,7 @@ static const struct snl_hdr_parser *snl_all_core_parsers[] = {
static const struct snl_hdr_parser *snl_all_route_parsers[] = {
&_metrics_mp_nh_parser, &_mpath_nh_parser, &_metrics_parser, &snl_rtm_route_parser,
&_vf_extension_parser, &_vf_parser, &_vf_status_parser,
&_link_fbsd_parser, &snl_rtm_link_parser, &snl_rtm_link_parser_simple,
&_neigh_fbsd_parser, &snl_rtm_neigh_parser,
&_addr_fbsd_parser, &snl_rtm_addr_parser, &_nh_fbsd_parser, &snl_nhmsg_parser,
@@ -37,6 +38,93 @@ ATF_TC_BODY(snl_verify_core_parsers, tc)
}
ATF_TC(snl_parse_vf_status);
ATF_TC_HEAD(snl_parse_vf_status, tc)
{
atf_tc_set_md_var(tc, "descr",
"Tests snl(3) parsing nested VF status");
atf_tc_set_md_var(tc, "require.kmods", "netlink");
}
ATF_TC_BODY(snl_parse_vf_status, tc)
{
static const uint8_t mac[] = { 0x02, 0, 0, 0, 0, 1 };
static const uint8_t extension_data[] = { 1, 2, 3, 4 };
struct snl_parsed_vf_extension *extension;
struct snl_parsed_vf_status *status;
struct snl_parsed_link link = {};
struct snl_parsed_vf *vf;
struct snl_state ss;
struct snl_writer nw;
struct nlmsghdr *hdr;
int entry_off, extension_off, extensions_off, fbsd_off, list_off;
int status_off;
ATF_REQUIRE(snl_init(&ss, NETLINK_ROUTE));
snl_init_writer(&ss, &nw);
hdr = snl_create_msg_request(&nw, RTM_NEWLINK);
ATF_REQUIRE(hdr != NULL);
ATF_REQUIRE(snl_reserve_msg_object(&nw, struct ifinfomsg) != NULL);
ATF_REQUIRE(snl_add_msg_attr_u32(&nw, IFLA_NUM_VF, 1));
fbsd_off = snl_add_msg_attr_nested(&nw, IFLA_FREEBSD);
ATF_REQUIRE(fbsd_off != 0);
status_off = snl_add_msg_attr_nested(&nw, IFLAF_VF_STATUS);
ATF_REQUIRE(status_off != 0);
ATF_REQUIRE(snl_add_msg_attr_u32(&nw, IFLAF_VFS_VERSION, 1));
ATF_REQUIRE(snl_add_msg_attr_u8(&nw, IFLAF_VFS_PF_LINK_STATE,
IFLAF_VF_LINK_UP));
ATF_REQUIRE(snl_add_msg_attr_u64(&nw, IFLAF_VFS_PF_LINK_SPEED,
10000000000ULL));
list_off = snl_add_msg_attr_nested(&nw, IFLAF_VFS_LIST);
ATF_REQUIRE(list_off != 0);
entry_off = snl_add_msg_attr_nested(&nw, IFLAF_VF_LIST_ENTRY);
ATF_REQUIRE(entry_off != 0);
ATF_REQUIRE(snl_add_msg_attr_u32(&nw, IFLAF_VF_INDEX, 7));
ATF_REQUIRE(snl_add_msg_attr_u8(&nw, IFLAF_VF_CONFIGURED, 1));
ATF_REQUIRE(snl_add_msg_attr(&nw, IFLAF_VF_MAC, sizeof(mac), mac));
extensions_off = snl_add_msg_attr_nested(&nw,
IFLAF_VF_EXTENSIONS);
ATF_REQUIRE(extensions_off != 0);
extension_off = snl_add_msg_attr_nested(&nw,
IFLAF_VF_EXT_LIST_ENTRY);
ATF_REQUIRE(extension_off != 0);
ATF_REQUIRE(snl_add_msg_attr_string(&nw, IFLAF_VF_EXT_NAME,
"driver.test"));
ATF_REQUIRE(snl_add_msg_attr(&nw, IFLAF_VF_EXT_DATA,
sizeof(extension_data), extension_data));
snl_end_attr_nested(&nw, extension_off);
snl_end_attr_nested(&nw, extensions_off);
snl_end_attr_nested(&nw, entry_off);
snl_end_attr_nested(&nw, list_off);
snl_end_attr_nested(&nw, status_off);
snl_end_attr_nested(&nw, fbsd_off);
ATF_REQUIRE(snl_finalize_msg(&nw) != NULL);
ATF_REQUIRE(snl_parse_nlmsg(&ss, hdr, &snl_rtm_link_parser, &link));
ATF_CHECK_EQ(link.ifla_num_vf, 1);
status = &link.iflaf_vf_status;
ATF_CHECK_EQ(status->version, 1);
ATF_CHECK_EQ(status->pf_link_state, IFLAF_VF_LINK_UP);
ATF_CHECK_EQ(status->pf_link_speed, 10000000000ULL);
ATF_REQUIRE_EQ(status->vfs.count, 1);
vf = status->vfs.items[0];
ATF_CHECK_EQ(vf->index, 7);
ATF_CHECK_EQ(vf->configured, 1);
ATF_REQUIRE(vf->mac != NULL);
ATF_CHECK_EQ(NLA_DATA_LEN(vf->mac), sizeof(mac));
ATF_CHECK(memcmp(NLA_DATA(vf->mac), mac, sizeof(mac)) == 0);
ATF_REQUIRE_EQ(vf->extensions.count, 1);
extension = vf->extensions.items[0];
ATF_CHECK_STREQ(extension->name, "driver.test");
ATF_REQUIRE(extension->data != NULL);
ATF_CHECK_EQ(NLA_DATA_LEN(extension->data), sizeof(extension_data));
ATF_CHECK(memcmp(NLA_DATA(extension->data), extension_data,
sizeof(extension_data)) == 0);
}
ATF_TC(snl_verify_route_parsers);
ATF_TC_HEAD(snl_verify_route_parsers, tc)
{
@@ -231,6 +319,7 @@ ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, snl_verify_core_parsers);
ATF_TP_ADD_TC(tp, snl_verify_route_parsers);
ATF_TP_ADD_TC(tp, snl_parse_vf_status);
ATF_TP_ADD_TC(tp, snl_parse_errmsg_capped);
ATF_TP_ADD_TC(tp, snl_parse_errmsg_capped_extack);
ATF_TP_ADD_TC(tp, snl_parse_errmsg_uncapped_extack);