mirror of
https://github.com/freebsd/freebsd-src.git
synced 2026-11-07 04:36:52 -10:00
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:
1 parent
25de742864
commit
d15f2551b2
9 files changed
+835
-9
No files matched your search
+117
-1
@@ -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
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
||||
@@ -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"""
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user