rtnetlink: Add native SR-IOV VF status

Add a transport neutral kernel snapshot for NIC-specific SR-IOV VF
status and an optional iflib provider method.  Providers gather state
under driver defined synchronization.

Honor RTEXT_FILTER_VF on RTM_GETLINK requests and encode the status as
native typed route Netlink attributes.  Represent VFs, driver
namespaces, and namespace fields as directly repeated nested attributes.
Presence masks in consumers can distinguish omission from false or zero.

Drivers may add custom status under stable, versioned namespaces.  The
named, typed representation lets generic transports and consumers carry
or display fields without knowing their driver-specific schemas, while
the driver retains ownership of their names and meanings.

Document the ABI and add parser and RTM_GETLINK coverage.

Reviewed by:	melifaro, iflib (gallatin), kgalazka (previous version)
Sponsored by:	BBOX.io
Differential Revision:	https://reviews.freebsd.org/D58776
This commit is contained in:
Kevin Bowling committed 2026-09-06 06:51:52 -07:00
1 parent 7e34176e03
commit 2019f3d86f
17 files changed
+2175 -23

No files matched your search

+187 -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 September 6, 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,182 @@ 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 VF records returned.
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_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
.Ed
.Pp
If the PF status provider does not implement this query, the status block is
omitted.
If an implemented PF status query fails,
.Dv IFLAF_VFS_ERROR
contains the errno value and the other status attributes are omitted.
An empty status snapshot reports
.Dv IFLA_NUM_VF
as zero and contains no
.Dv IFLA_FREEBSD_VF
attributes.
.Pp
Invalid or oversized data returned by a provider is reported through
.Dv IFLAF_VFS_ERROR .
Allocation failures while encoding the reply terminate the requested
.Dv RTM_GETLINK
operation with an error.
For a multipart dump, valid interface records may precede the terminating
error.
.It Dv IFLA_FREEBSD_VF
(nested) (readonly) One VF record containing
.Dv IFLAF_VF_*
attributes.
The attribute is repeated once for every VF in a successful status snapshot.
Keeping each VF in an independent attribute permits the complete Netlink
message to exceed the 16-bit length limit of an individual attribute.
Each
.Dv IFLA_FREEBSD_VF
may contain the following attributes:
.Bd -literal -offset indent -compact
IFLAF_VF_INDEX (uint32_t) PF-local VF index
IFLAF_VF_CONFIGURED (bool) PF accepted configuration
IFLAF_VF_INITIALIZED (bool) 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_PCP (uint8_t) access VLAN priority code point
IFLAF_VF_VLAN_PROTO (uint16_t) host-order access VLAN EtherType
IFLAF_VF_VLAN_COUNT (uint32_t) explicit VLAN filters
IFLAF_VF_VLAN_LIMIT (uint32_t) explicit VLAN-filter limit
IFLAF_VF_NUM_TX_QUEUES (uint16_t) allocated transmit queues
IFLAF_VF_NUM_RX_QUEUES (uint16_t) allocated receive queues
IFLAF_VF_MIN_TX_RATE (uint64_t) minimum transmit bits per second
IFLAF_VF_MAX_TX_RATE (uint64_t) maximum transmit bits per second
IFLAF_VF_ALLOW_SET_MAC (bool) administrative permission
IFLAF_VF_ALLOW_SET_VLAN (bool) administrative permission
IFLAF_VF_MAC_ANTI_SPOOF (bool) MAC anti-spoofing enabled
IFLAF_VF_ALLOW_PROMISC (bool) administrative permission
IFLAF_VF_TRAFFIC_ALLOWED (bool) PF permits VF data traffic
IFLAF_VF_FAULT_BLOCKED (bool) fault-containment block active
IFLAF_VF_QUARANTINED (bool) persistent quarantine active
IFLAF_VF_API_VERSION (string) negotiated mailbox API
IFLAF_VF_LINK_STATE_POLICY (uint8_t) IFLAF_VF_LINK_*
IFLAF_VF_DRIVER (nested) driver namespace; repeated
.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 a native Netlink boolean value.
.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_TRAFFIC_ALLOWED
means that the PF currently permits the VF to send and receive data traffic.
It does not imply that the VF completed initialization, enabled queues, has
link, or is actively passing packets.
.Dv IFLAF_VF_FAULT_BLOCKED
means that the PF isolated the VF because of a detected fault or abusive
behavior.
It does not include ordinary unconfigured, reset, or link-down states.
The driver namespace describes the particular cause and recovery policy.
When both fields are present, a fault block implies that traffic is not
allowed.
.Dv IFLAF_VF_QUARANTINED
describes a persistent form of containment and, when both fields are present,
implies that a fault block is active.
.Pp
.Dv IFLAF_VF_NUM_TX_QUEUES
and
.Dv IFLAF_VF_NUM_RX_QUEUES
are the numbers of transmit and receive queues allocated to the VF, not
necessarily the numbers currently used by its driver.
The transmit-rate attributes describe configured aggregate VF policy.
A present zero minimum means no guaranteed allocation; a present zero maximum
means no rate limit.
When both are present and the maximum is nonzero, the minimum does not exceed
the maximum.
Access VLAN mode means that the PF imposes a single port VLAN policy.
The optional
.Dv IFLAF_VF_VLAN ,
.Dv IFLAF_VF_VLAN_PCP ,
and
.Dv IFLAF_VF_VLAN_PROTO
attributes describe its VLAN identifier, IEEE 802.1p priority code point, and
host-order tag EtherType, respectively.
A present zero VLAN identifier can represent a priority-only access tag, and a
present zero priority is distinct from an omitted priority.
These attributes do not describe the VF's trunk filters.
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_DRIVER ,
which may be repeated directly in a VF record.
Each driver object contains:
.Bd -literal -offset indent -compact
IFLAF_VFD_NAME (string) stable namespace name
IFLAF_VFD_VERSION (uint32_t) namespace schema version
IFLAF_VFD_FIELD (nested) namespace field; repeated
.Ed
.Pp
Each field contains its namespace-local name in
.Dv IFLAF_VFDF_NAME
and exactly one of
.Dv IFLAF_VFDF_BOOL ,
.Dv IFLAF_VFDF_NUMBER ,
.Dv IFLAF_VFDF_STRING ,
or
.Dv IFLAF_VFDF_BINARY .
The namespace defines the types and meanings of its fields.
The named, typed form permits generic consumers to carry or display an
extension without knowing its driver-specific schema.
Consumers that interpret extensions must ignore unknown namespaces and
fields.
Each attribute, including each containing nested attribute, is limited to
65535 bytes by the Netlink encoding.
If an individual VF record, driver extension, or field cannot be encoded
within that limit,
.Dv IFLAF_VFS_ERROR
reports
.Er EMSGSIZE .
The enclosing
.Dv RTM_NEWLINK
message may exceed 65535 bytes.
.It Dv IFLA_LINKINFO
(nested) Interface type-specific attributes:
.Bd -literal -offset indent -compact
+165
View File
@@ -81,6 +81,7 @@
#include <net/if_strings.h>
#include <net/if_types.h>
#include <net/if_var.h>
#include <net/if_vf_status.h>
#include <net/if_media.h>
#include <net/if_mib.h>
#include <net/if_private.h>
@@ -2360,6 +2361,164 @@ if_capint_to_capnv(nvlist_t *nv, const struct ifcap_nv_bit_name *nn,
}
}
struct if_vf_status *
if_vf_status_alloc(uint32_t num_vfs)
{
struct if_vf_status *status;
size_t size;
KASSERT(num_vfs <= IFVF_MAX_VFS,
("invalid VF count %u", num_vfs));
size = sizeof(struct if_vf_status) +
num_vfs * sizeof(struct if_vf_info);
status = malloc(size, M_IFNET, M_WAITOK | M_ZERO);
status->num_vfs = num_vfs;
return (status);
}
void
if_vf_status_free(struct if_vf_status *status)
{
struct if_vf_ext_field *field;
struct if_vf_extension *extension;
uint32_t i, j;
KASSERT(status != NULL, ("NULL VF status"));
for (i = 0; i < status->num_vfs; i++) {
for (j = 0; j < status->vfs[i].num_extensions; j++) {
extension = &status->vfs[i].extensions[j];
for (uint32_t k = 0; k < extension->num_fields; k++) {
field = &extension->fields[k];
if (field->type == IFVF_EXT_STRING)
free(field->value.string, M_IFNET);
else if (field->type == IFVF_EXT_BINARY)
free(field->value.binary.data, M_IFNET);
}
free(extension->fields, M_IFNET);
}
free(status->vfs[i].extensions, M_IFNET);
}
free(status, M_IFNET);
}
struct if_vf_extension *
if_vf_status_add_extension(struct if_vf_info *vf, const char *name,
uint32_t version, uint32_t num_fields)
{
struct if_vf_extension *extensions, *extension;
uint32_t count;
KASSERT(vf != NULL, ("NULL VF information"));
KASSERT(name != NULL, ("VF extension without a name"));
KASSERT(vf->num_extensions < IFVF_MAX_EXTENSIONS,
("too many VF extensions"));
KASSERT(num_fields > 0 && num_fields <= IFVF_MAX_EXTENSION_FIELDS,
("invalid VF extension field count %u", num_fields));
count = vf->num_extensions + 1;
extensions = mallocarray(count, sizeof(*extensions), M_IFNET,
M_WAITOK | M_ZERO);
if (vf->num_extensions != 0) {
memcpy(extensions, vf->extensions,
vf->num_extensions * sizeof(*extensions));
free(vf->extensions, M_IFNET);
}
vf->extensions = extensions;
vf->num_extensions = count;
extension = &extensions[count - 1];
extension->name = name;
extension->version = version;
extension->num_fields = num_fields;
extension->fields = mallocarray(num_fields, sizeof(*extension->fields),
M_IFNET, M_WAITOK | M_ZERO);
return (extension);
}
static struct if_vf_ext_field *
if_vf_extension_field(struct if_vf_extension *extension, uint32_t index,
const char *name, enum if_vf_ext_type type)
{
struct if_vf_ext_field *field;
KASSERT(extension != NULL && index < extension->num_fields,
("invalid VF extension field"));
KASSERT(name != NULL, ("VF extension field without a name"));
field = &extension->fields[index];
KASSERT(field->type == 0, ("VF extension field initialized twice"));
field->name = name;
field->type = type;
return (field);
}
void
if_vf_extension_set_bool(struct if_vf_extension *extension, uint32_t index,
const char *name, bool value)
{
struct if_vf_ext_field *field;
field = if_vf_extension_field(extension, index, name, IFVF_EXT_BOOL);
field->value.boolean = value;
}
void
if_vf_extension_set_number(struct if_vf_extension *extension, uint32_t index,
const char *name, uint64_t value)
{
struct if_vf_ext_field *field;
field = if_vf_extension_field(extension, index, name, IFVF_EXT_NUMBER);
field->value.number = value;
}
void
if_vf_extension_set_string(struct if_vf_extension *extension, uint32_t index,
const char *name, const char *value)
{
struct if_vf_ext_field *field;
KASSERT(value != NULL, ("NULL VF extension string"));
field = if_vf_extension_field(extension, index, name, IFVF_EXT_STRING);
field->value.string = strdup(value, M_IFNET);
}
void
if_vf_extension_set_binary(struct if_vf_extension *extension, uint32_t index,
const char *name, const void *value, uint32_t length)
{
struct if_vf_ext_field *field;
KASSERT(value != NULL && length != 0, ("empty VF extension binary"));
field = if_vf_extension_field(extension, index, name, IFVF_EXT_BINARY);
field->value.binary.data = malloc(length, M_IFNET, M_WAITOK);
memcpy(field->value.binary.data, value, length);
field->value.binary.length = length;
}
int
if_get_vf_status(if_t ifp, struct if_vf_status **statusp)
{
struct if_vf_status *status;
int error;
KASSERT(statusp != NULL, ("NULL VF status output"));
if (ifp->if_vf_status == NULL)
return (EOPNOTSUPP);
status = NULL;
error = ifp->if_vf_status(ifp, &status);
KASSERT((error == 0) == (status != NULL),
("VF status provider returned error %d with status %p", error,
status));
if (error != 0) {
if (status != NULL)
if_vf_status_free(status);
return (error);
}
if (status == NULL)
return (EBADMSG);
*statusp = status;
return (0);
}
/*
* Hardware specific interface ioctls.
*/
@@ -4886,6 +5045,12 @@ if_setioctlfn(if_t ifp, if_ioctl_fn_t ioctl_fn)
ifp->if_ioctl = ioctl_fn;
}
void
if_setvfstatusfn(if_t ifp, if_vf_status_fn_t vf_status_fn)
{
ifp->if_vf_status = vf_status_fn;
}
void
if_setoutputfn(if_t ifp, if_output_fn_t output_fn)
{
+8
View File
@@ -107,6 +107,13 @@ ifdead_snd_tag_alloc(struct ifnet *ifp, union if_snd_tag_alloc_params *params,
return (EOPNOTSUPP);
}
static int
ifdead_vf_status(struct ifnet *ifp __unused,
struct if_vf_status **statusp __unused)
{
return (EOPNOTSUPP);
}
static void
ifdead_ratelimit_query(struct ifnet *ifp __unused,
struct if_ratelimit_query_results *q)
@@ -138,4 +145,5 @@ if_dead(struct ifnet *ifp)
ifp->if_get_counter = ifdead_get_counter;
ifp->if_snd_tag_alloc = ifdead_snd_tag_alloc;
ifp->if_ratelimit_query = ifdead_ratelimit_query;
ifp->if_vf_status = ifdead_vf_status;
}
+1
View File
@@ -127,6 +127,7 @@ struct ifnet {
void (*if_bridge_linkstate)(struct ifnet *ifp);
if_start_fn_t if_start; /* initiate output routine */
if_ioctl_fn_t if_ioctl; /* ioctl routine */
if_vf_status_fn_t if_vf_status; /* snapshot SR-IOV VF state */
if_init_fn_t if_init; /* Init routine */
int (*if_resolvemulti) /* validate/resolve multicast */
(struct ifnet *, struct sockaddr **, struct sockaddr *);
+4
View File
@@ -131,6 +131,8 @@ typedef void (*if_qflush_fn_t)(if_t);
typedef int (*if_transmit_fn_t)(if_t, struct mbuf *);
typedef uint64_t (*if_get_counter_t)(if_t, ift_counter);
typedef void (*if_reassign_fn_t)(if_t, struct vnet *, char *);
struct if_vf_status;
typedef int (*if_vf_status_fn_t)(if_t, struct if_vf_status **);
typedef int (*if_spdadd_fn_t)(if_t ifp, void *sp, void *inp, void **priv);
typedef int (*if_spddel_fn_t)(if_t ifp, void *sp, void *priv);
typedef int (*if_sa_newkey_fn_t)(if_t ifp, void *sav, u_int drv_spi,
@@ -729,6 +731,7 @@ void if_setinitfn(if_t ifp, if_init_fn_t);
void if_setinputfn(if_t ifp, if_input_fn_t);
if_input_fn_t if_getinputfn(if_t ifp);
void if_setioctlfn(if_t ifp, if_ioctl_fn_t);
void if_setvfstatusfn(if_t ifp, if_vf_status_fn_t);
void if_setoutputfn(if_t ifp, if_output_fn_t);
void if_setstartfn(if_t ifp, if_start_fn_t);
if_start_fn_t if_getstartfn(if_t ifp);
@@ -765,6 +768,7 @@ void *ifr_buffer_get_buffer(void *data);
size_t ifr_buffer_get_length(void *data);
int ifhwioctl(u_long, if_t, caddr_t, struct thread *);
int if_get_vf_status(if_t, struct if_vf_status **);
#ifdef DEVICE_POLLING
enum poll_cmd { POLL_ONLY, POLL_AND_CHECK_STATUS };
+157
View File
@@ -0,0 +1,157 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2026 Kevin Bowling <kbowling@FreeBSD.org>
*/
#ifndef _NET_IF_VF_STATUS_H_
#define _NET_IF_VF_STATUS_H_
#include <sys/types.h>
#ifndef _KERNEL
#include <stdbool.h>
#endif
#include <net/ethernet.h>
/*
* Kernel-only snapshot of SR-IOV VF state. The fields member is a bit mask
* identifying which other members contain valid data. This distinguishes a
* missing value from a value of false or zero. Drivers set bits only for
* information they can report, and each transport converts the snapshot to
* its own ABI. VLAN PCP and protocol describe the PF-administered access
* VLAN, not the VF's trunk filters. Transmit rates describe an aggregate VF
* policy. When both are present and the maximum is nonzero, the minimum must
* not exceed it. A successful snapshot may contain zero VFs, allowing a
* provider to report that SR-IOV is available but is not currently configured.
*/
#define IFVF_MAX_VFS UINT16_MAX
#define IFVF_MAX_EXTENSIONS 16
#define IFVF_MAX_EXTENSION_FIELDS 64
enum if_vf_field {
IFVF_F_CONFIGURED = 1ULL << 0,
IFVF_F_INITIALIZED = 1ULL << 1,
IFVF_F_MAC = 1ULL << 2,
IFVF_F_VLAN_MODE = 1ULL << 3,
IFVF_F_VLAN = 1ULL << 4,
IFVF_F_VLAN_PCP = 1ULL << 5,
IFVF_F_VLAN_PROTO = 1ULL << 6,
IFVF_F_VLAN_COUNT = 1ULL << 7,
IFVF_F_VLAN_LIMIT = 1ULL << 8,
IFVF_F_NUM_TX_QUEUES = 1ULL << 9,
IFVF_F_NUM_RX_QUEUES = 1ULL << 10,
IFVF_F_MIN_TX_RATE = 1ULL << 11,
IFVF_F_MAX_TX_RATE = 1ULL << 12,
IFVF_F_ALLOW_SET_MAC = 1ULL << 13,
IFVF_F_ALLOW_SET_VLAN = 1ULL << 14,
IFVF_F_MAC_ANTI_SPOOF = 1ULL << 15,
IFVF_F_ALLOW_PROMISC = 1ULL << 16,
IFVF_F_TRAFFIC_ALLOWED = 1ULL << 17,
IFVF_F_FAULT_BLOCKED = 1ULL << 18,
IFVF_F_QUARANTINED = 1ULL << 19,
IFVF_F_API_VERSION = 1ULL << 20,
IFVF_F_LINK_STATE_POLICY = 1ULL << 21,
};
enum if_vf_vlan_mode {
IFVF_VLAN_UNKNOWN = 0,
IFVF_VLAN_ACCESS,
IFVF_VLAN_TRUNK,
};
enum if_vf_link_state {
IFVF_LINK_UNKNOWN = 0,
IFVF_LINK_DOWN,
IFVF_LINK_UP,
IFVF_LINK_AUTO,
};
/*
* Drivers may add fields under a stable, versioned namespace. The named,
* typed representation lets transports and generic consumers carry and
* display fields whose driver-specific schema they do not know. The driver
* owns the field names and meanings; common code does not interpret them.
* Namespace and field names must remain valid until the snapshot is freed.
*/
enum if_vf_ext_type {
IFVF_EXT_BOOL = 1,
IFVF_EXT_NUMBER,
IFVF_EXT_STRING,
IFVF_EXT_BINARY,
};
struct if_vf_ext_field {
const char *name;
enum if_vf_ext_type type;
union {
bool boolean;
uint64_t number;
char *string;
struct {
void *data;
uint32_t length;
} binary;
} value;
};
struct if_vf_extension {
const char *name;
uint32_t version;
uint32_t num_fields;
struct if_vf_ext_field *fields;
};
#define IFVF_API_VERSION_MAX 16
struct if_vf_info {
uint64_t fields;
uint64_t min_tx_rate_bps; /* Zero means no guaranteed allocation. */
uint64_t max_tx_rate_bps; /* Zero means unlimited. */
uint32_t index;
uint32_t vlan_count;
uint32_t vlan_limit;
uint16_t tx_queue_count;
uint16_t rx_queue_count;
uint16_t vlan;
uint16_t vlan_proto; /* Host-order Ethernet type. */
uint8_t vlan_pcp;
uint8_t mac[ETHER_ADDR_LEN];
enum if_vf_vlan_mode vlan_mode;
enum if_vf_link_state link_state_policy;
bool configured:1;
bool initialized:1;
bool allow_set_mac:1;
bool allow_set_vlan:1;
bool mac_anti_spoof:1;
bool allow_promisc:1;
bool traffic_allowed:1;
bool fault_blocked:1;
bool quarantined:1;
/* Negotiated PF/VF mailbox API, not a version of this structure. */
char api_version[IFVF_API_VERSION_MAX];
uint32_t num_extensions;
struct if_vf_extension *extensions;
};
struct if_vf_status {
uint32_t num_vfs;
struct if_vf_info vfs[];
};
#ifdef _KERNEL
struct if_vf_status *if_vf_status_alloc(uint32_t);
void if_vf_status_free(struct if_vf_status *);
struct if_vf_extension *if_vf_status_add_extension(struct if_vf_info *,
const char *, uint32_t, uint32_t);
void if_vf_extension_set_bool(struct if_vf_extension *, uint32_t,
const char *, bool);
void if_vf_extension_set_number(struct if_vf_extension *, uint32_t,
const char *, uint64_t);
void if_vf_extension_set_string(struct if_vf_extension *, uint32_t,
const char *, const char *);
void if_vf_extension_set_binary(struct if_vf_extension *, uint32_t,
const char *, const void *, uint32_t);
#endif
#endif /* _NET_IF_VF_STATUS_H_ */
+13
View File
@@ -36,6 +36,7 @@
#include <net/ethernet.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_vf_status.h>
#include <net/if_media.h>
#include <net/iflib.h>
#include <net/if_private.h>
@@ -118,6 +119,13 @@ CODE {
return (ENOTSUP);
}
static int
null_vf_status(if_ctx_t _ctx __unused,
struct if_vf_status **_status __unused)
{
return (ENOTSUP);
}
static bool
null_needs_restart(if_ctx_t _ctx __unused, enum iflib_restart_event _event __unused)
{
@@ -385,3 +393,8 @@ METHOD int get_downreason {
if_ctx_t _ctx;
struct ifdownreason *_ifdr;
} DEFAULT null_get_downreason;
METHOD int vf_status {
if_ctx_t _ctx;
struct if_vf_status **_status;
} DEFAULT null_vf_status;
+17
View File
@@ -88,6 +88,7 @@
#include <dev/pci/pci_private.h>
#include <net/iflib.h>
#include <net/if_vf_status.h>
#include "ifdi_if.h"
@@ -4741,6 +4742,19 @@ iflib_if_ioctl(if_t ifp, u_long command, caddr_t data)
return (err);
}
static int
iflib_if_vf_status(if_t ifp, struct if_vf_status **statusp)
{
if_ctx_t ctx;
int error;
ctx = if_getsoftc(ifp);
CTX_LOCK(ctx);
error = IFDI_VF_STATUS(ctx, statusp);
CTX_UNLOCK(ctx);
return (error);
}
static uint64_t
iflib_if_get_counter(if_t ifp, ift_counter cnt)
{
@@ -6020,6 +6034,9 @@ iflib_register(if_ctx_t ctx)
if_setdev(ifp, dev);
if_setinitfn(ifp, iflib_if_init);
if_setioctlfn(ifp, iflib_if_ioctl);
/* VF status describes children of an SR-IOV PF. */
if (!CTX_IS_VF(ctx))
if_setvfstatusfn(ifp, iflib_if_vf_status);
#ifdef ALTQ
if_setstartfn(ifp, iflib_altq_if_start);
if_settransmitfn(ifp, iflib_altq_if_transmit);
+26
View File
@@ -325,6 +325,32 @@ snl_send_message(struct snl_state *ss, struct nlmsghdr *hdr)
return (send(ss->fd, hdr, sz, 0) == sz);
}
/* Ensure the receive buffer can hold the next complete Netlink message. */
static inline int
snl_grow_rxbuf_to_next_message(struct snl_state *ss)
{
char *buf;
ssize_t len;
if (ss->off != ss->datalen)
return (0);
do {
len = recv(ss->fd, NULL, 0, MSG_PEEK | MSG_TRUNC);
} while (len < 0 && errno == EINTR);
if (len < 0)
return (errno);
if (len == 0)
return (EIO);
if ((size_t)len <= ss->bufsize)
return (0);
buf = realloc(ss->buf, (size_t)len);
if (buf == NULL)
return (ENOMEM);
ss->buf = buf;
ss->bufsize = (size_t)len;
return (0);
}
static inline uint32_t
snl_get_seq(struct snl_state *ss)
{
+355
View File
@@ -180,6 +180,352 @@ static const struct snl_attr_parser _nla_p_ifgroups[] = {
SNL_DECLARE_ATTR_PARSER_EXT(_ifgroups_parser, sizeof(char[IFNAMSIZ]), _nla_p_ifgroups, NULL);
/* RTM_<NEW|DEL|GET>LINK message parser */
_Static_assert(IFLAF_VF_MAX < 64,
"VF attribute numbers must fit the presence mask");
enum snl_vf_driver_field_type {
SNL_VFDF_NONE = 0,
SNL_VFDF_BOOL,
SNL_VFDF_NUMBER,
SNL_VFDF_STRING,
SNL_VFDF_BINARY,
};
struct snl_parsed_vf_driver_field {
char *name;
enum snl_vf_driver_field_type type;
bool boolean;
uint64_t number;
char *string;
struct nlattr *binary;
};
struct snl_parsed_vf_driver {
char *name;
uint32_t version;
bool version_present;
struct snl_parray fields;
};
struct snl_parsed_vf {
uint64_t attrs; /* Bits indexed by IFLAF_VF_*. */
uint64_t min_tx_rate_bps;
uint64_t max_tx_rate_bps;
uint32_t index;
bool index_present;
uint32_t vlan_count;
uint32_t vlan_limit;
uint16_t tx_queue_count;
uint16_t rx_queue_count;
uint16_t vlan;
uint16_t vlan_proto;
uint8_t vlan_pcp;
bool configured;
bool initialized;
uint8_t vlan_mode;
bool allow_set_mac;
bool allow_set_vlan;
bool mac_anti_spoof;
bool allow_promisc;
bool traffic_allowed;
bool fault_blocked;
bool quarantined;
uint8_t link_state_policy;
char *api_version;
struct nlattr *mac;
struct snl_parray drivers;
};
static inline bool
_snl_vf_get_index(struct snl_state *ss, struct nlattr *nla,
const void *arg __unused, void *target)
{
struct snl_parsed_vf *vf = target;
if (!snl_attr_get_uint32(ss, nla, NULL, &vf->index))
return (false);
vf->index_present = true;
return (true);
}
static inline bool
_cb_p_vf(struct snl_state *ss __unused, void *target)
{
struct snl_parsed_vf *vf = target;
return (vf->index_present);
}
#define _SNL_VF_ARG(_attr, _field) ((const void *)(uintptr_t)( \
((uint32_t)(_attr) << 16) | offsetof(struct snl_parsed_vf, _field)))
#define _SNL_VF_ATTR(_arg) ((uintptr_t)(_arg) >> 16)
#define _SNL_VF_OFF(_arg) ((uintptr_t)(_arg) & 0xffff)
#define _SNL_VF_GETTER(_name, _getter) \
static inline bool \
_name(struct snl_state *ss, struct nlattr *nla, const void *arg, \
void *target) \
{ \
struct snl_parsed_vf *vf = target; \
void *value = (char *)target + _SNL_VF_OFF(arg); \
\
if (!_getter(ss, nla, NULL, value)) \
return (false); \
vf->attrs |= 1ULL << _SNL_VF_ATTR(arg); \
return (true); \
}
_SNL_VF_GETTER(_snl_vf_get_bool, snl_attr_get_bool)
_SNL_VF_GETTER(_snl_vf_get_u8, snl_attr_get_uint8)
_SNL_VF_GETTER(_snl_vf_get_u16, snl_attr_get_uint16)
_SNL_VF_GETTER(_snl_vf_get_u32, snl_attr_get_uint32)
_SNL_VF_GETTER(_snl_vf_get_u64, snl_attr_get_uint64)
_SNL_VF_GETTER(_snl_vf_get_string, snl_attr_dup_string)
_SNL_VF_GETTER(_snl_vf_get_nla, snl_attr_dup_nla)
#undef _SNL_VF_GETTER
#define _SNL_VFDF_GETTER(_name, _getter, _type, _field) \
static inline bool \
_name(struct snl_state *ss, struct nlattr *nla, const void *arg, \
void *target) \
{ \
struct snl_parsed_vf_driver_field *field = target; \
\
if (field->type != SNL_VFDF_NONE || \
!_getter(ss, nla, arg, &field->_field)) \
return (false); \
field->type = _type; \
return (true); \
}
_SNL_VFDF_GETTER(_snl_vfdf_get_bool, snl_attr_get_bool, SNL_VFDF_BOOL,
boolean)
_SNL_VFDF_GETTER(_snl_vfdf_get_number, snl_attr_get_uint64,
SNL_VFDF_NUMBER, number)
_SNL_VFDF_GETTER(_snl_vfdf_get_string, snl_attr_dup_string,
SNL_VFDF_STRING, string)
_SNL_VFDF_GETTER(_snl_vfdf_get_binary, snl_attr_dup_nla,
SNL_VFDF_BINARY, binary)
#undef _SNL_VFDF_GETTER
static inline bool
_cb_p_vf_driver_field(struct snl_state *ss __unused, void *target)
{
struct snl_parsed_vf_driver_field *field = target;
return (field->name != NULL);
}
#define _OUT(_field) offsetof(struct snl_parsed_vf_driver_field, _field)
static const struct snl_attr_parser _nla_p_vf_driver_field[] = {
{ .type = IFLAF_VFDF_NAME, .off = _OUT(name),
.cb = snl_attr_dup_string },
{ .type = IFLAF_VFDF_BOOL, .off = 0, .cb = _snl_vfdf_get_bool },
{ .type = IFLAF_VFDF_NUMBER, .off = 0,
.cb = _snl_vfdf_get_number },
{ .type = IFLAF_VFDF_STRING, .off = 0,
.cb = _snl_vfdf_get_string },
{ .type = IFLAF_VFDF_BINARY, .off = 0,
.cb = _snl_vfdf_get_binary },
};
#undef _OUT
SNL_DECLARE_ATTR_PARSER_EXT(_vf_driver_field_parser,
sizeof(struct snl_parsed_vf_driver_field), _nla_p_vf_driver_field,
_cb_p_vf_driver_field);
static inline bool
_snl_vfd_get_field_multi(struct snl_state *ss, struct nlattr *nla,
const void *arg __unused, void *target)
{
struct snl_parsed_vf_driver_field *field;
struct snl_parray *fields = target;
struct nlattr *field_nla;
bool unknown_value;
field = snl_allocz(ss, sizeof(*field));
if (field == NULL ||
!snl_parse_header(ss, NLA_DATA(nla), NLA_DATA_LEN(nla),
&_vf_driver_field_parser, field))
return (false);
if (field->type != SNL_VFDF_NONE)
return (snl_parray_append(ss, fields, field, 8));
/* Ignore a field whose value type was added by a newer kernel. */
unknown_value = false;
NLA_FOREACH(field_nla, NLA_DATA(nla), NLA_DATA_LEN(nla)) {
if (NLA_TYPE(field_nla) > IFLAF_VFDF_MAX) {
unknown_value = true;
break;
}
}
return (unknown_value);
}
static inline bool
_snl_vfd_get_version(struct snl_state *ss, struct nlattr *nla,
const void *arg __unused, void *target)
{
struct snl_parsed_vf_driver *driver = target;
if (!snl_attr_get_uint32(ss, nla, NULL, &driver->version))
return (false);
driver->version_present = true;
return (true);
}
static inline bool
_cb_p_vf_driver(struct snl_state *ss __unused, void *target)
{
struct snl_parsed_vf_driver *driver = target;
return (driver->name != NULL && driver->version_present);
}
#define _OUT(_field) offsetof(struct snl_parsed_vf_driver, _field)
static const struct snl_attr_parser _nla_p_vf_driver[] = {
{ .type = IFLAF_VFD_NAME, .off = _OUT(name),
.cb = snl_attr_dup_string },
{ .type = IFLAF_VFD_VERSION, .off = 0,
.cb = _snl_vfd_get_version },
{ .type = IFLAF_VFD_FIELD, .off = _OUT(fields),
.cb = _snl_vfd_get_field_multi },
};
#undef _OUT
SNL_DECLARE_ATTR_PARSER_EXT(_vf_driver_parser,
sizeof(struct snl_parsed_vf_driver), _nla_p_vf_driver,
_cb_p_vf_driver);
#define _OUT(_field) offsetof(struct snl_parsed_vf, _field)
static const struct snl_attr_parser _nla_p_vf[] = {
{ .type = IFLAF_VF_INDEX, .off = 0, .cb = _snl_vf_get_index },
{ .type = IFLAF_VF_CONFIGURED, .off = 0,
.arg = _SNL_VF_ARG(IFLAF_VF_CONFIGURED, configured),
.cb = _snl_vf_get_bool },
{ .type = IFLAF_VF_INITIALIZED, .off = 0,
.arg = _SNL_VF_ARG(IFLAF_VF_INITIALIZED, initialized),
.cb = _snl_vf_get_bool },
{ .type = IFLAF_VF_MAC, .off = 0,
.arg = _SNL_VF_ARG(IFLAF_VF_MAC, mac), .cb = _snl_vf_get_nla },
{ .type = IFLAF_VF_VLAN_MODE, .off = 0,
.arg = _SNL_VF_ARG(IFLAF_VF_VLAN_MODE, vlan_mode),
.cb = _snl_vf_get_u8 },
{ .type = IFLAF_VF_VLAN, .off = 0,
.arg = _SNL_VF_ARG(IFLAF_VF_VLAN, vlan), .cb = _snl_vf_get_u16 },
{ .type = IFLAF_VF_VLAN_PCP, .off = 0,
.arg = _SNL_VF_ARG(IFLAF_VF_VLAN_PCP, vlan_pcp),
.cb = _snl_vf_get_u8 },
{ .type = IFLAF_VF_VLAN_PROTO, .off = 0,
.arg = _SNL_VF_ARG(IFLAF_VF_VLAN_PROTO, vlan_proto),
.cb = _snl_vf_get_u16 },
{ .type = IFLAF_VF_VLAN_COUNT, .off = 0,
.arg = _SNL_VF_ARG(IFLAF_VF_VLAN_COUNT, vlan_count),
.cb = _snl_vf_get_u32 },
{ .type = IFLAF_VF_VLAN_LIMIT, .off = 0,
.arg = _SNL_VF_ARG(IFLAF_VF_VLAN_LIMIT, vlan_limit),
.cb = _snl_vf_get_u32 },
{ .type = IFLAF_VF_NUM_TX_QUEUES, .off = 0,
.arg = _SNL_VF_ARG(IFLAF_VF_NUM_TX_QUEUES, tx_queue_count),
.cb = _snl_vf_get_u16 },
{ .type = IFLAF_VF_NUM_RX_QUEUES, .off = 0,
.arg = _SNL_VF_ARG(IFLAF_VF_NUM_RX_QUEUES, rx_queue_count),
.cb = _snl_vf_get_u16 },
{ .type = IFLAF_VF_MIN_TX_RATE, .off = 0,
.arg = _SNL_VF_ARG(IFLAF_VF_MIN_TX_RATE, min_tx_rate_bps),
.cb = _snl_vf_get_u64 },
{ .type = IFLAF_VF_MAX_TX_RATE, .off = 0,
.arg = _SNL_VF_ARG(IFLAF_VF_MAX_TX_RATE, max_tx_rate_bps),
.cb = _snl_vf_get_u64 },
{ .type = IFLAF_VF_ALLOW_SET_MAC, .off = 0,
.arg = _SNL_VF_ARG(IFLAF_VF_ALLOW_SET_MAC, allow_set_mac),
.cb = _snl_vf_get_bool },
{ .type = IFLAF_VF_ALLOW_SET_VLAN, .off = 0,
.arg = _SNL_VF_ARG(IFLAF_VF_ALLOW_SET_VLAN, allow_set_vlan),
.cb = _snl_vf_get_bool },
{ .type = IFLAF_VF_MAC_ANTI_SPOOF, .off = 0,
.arg = _SNL_VF_ARG(IFLAF_VF_MAC_ANTI_SPOOF, mac_anti_spoof),
.cb = _snl_vf_get_bool },
{ .type = IFLAF_VF_ALLOW_PROMISC, .off = 0,
.arg = _SNL_VF_ARG(IFLAF_VF_ALLOW_PROMISC, allow_promisc),
.cb = _snl_vf_get_bool },
{ .type = IFLAF_VF_TRAFFIC_ALLOWED, .off = 0,
.arg = _SNL_VF_ARG(IFLAF_VF_TRAFFIC_ALLOWED, traffic_allowed),
.cb = _snl_vf_get_bool },
{ .type = IFLAF_VF_FAULT_BLOCKED, .off = 0,
.arg = _SNL_VF_ARG(IFLAF_VF_FAULT_BLOCKED, fault_blocked),
.cb = _snl_vf_get_bool },
{ .type = IFLAF_VF_QUARANTINED, .off = 0,
.arg = _SNL_VF_ARG(IFLAF_VF_QUARANTINED, quarantined),
.cb = _snl_vf_get_bool },
{ .type = IFLAF_VF_API_VERSION, .off = 0,
.arg = _SNL_VF_ARG(IFLAF_VF_API_VERSION, api_version),
.cb = _snl_vf_get_string },
{ .type = IFLAF_VF_LINK_STATE_POLICY, .off = 0,
.arg = _SNL_VF_ARG(IFLAF_VF_LINK_STATE_POLICY, link_state_policy),
.cb = _snl_vf_get_u8 },
{ .type = IFLAF_VF_DRIVER, .off = _OUT(drivers),
.arg = &_vf_driver_parser, .cb = snl_attr_get_multi },
};
#undef _OUT
SNL_DECLARE_ATTR_PARSER_EXT(_vf_parser, sizeof(struct snl_parsed_vf),
_nla_p_vf, _cb_p_vf);
struct snl_parsed_vf_status {
uint64_t pf_link_speed;
uint32_t error;
uint8_t pf_link_state;
bool present;
bool pf_link_state_present;
struct snl_parray vfs;
};
static inline bool
_snl_vfs_get_link_state(struct snl_state *ss, struct nlattr *nla,
const void *arg __unused, void *target)
{
struct snl_parsed_vf_status *status = target;
if (!snl_attr_get_uint8(ss, nla, NULL, &status->pf_link_state))
return (false);
status->pf_link_state_present = true;
return (true);
}
static inline bool
_cb_p_vf_status(struct snl_state *ss __unused, void *target)
{
struct snl_parsed_vf_status *status = target;
return (status->error != 0 || status->pf_link_state_present);
}
#define _OUT(_field) offsetof(struct snl_parsed_vf_status, _field)
static const struct snl_attr_parser _nla_p_vf_status[] = {
{ .type = IFLAF_VFS_ERROR, .off = _OUT(error),
.cb = snl_attr_get_uint32 },
{ .type = IFLAF_VFS_PF_LINK_STATE, .off = 0,
.cb = _snl_vfs_get_link_state },
{ .type = IFLAF_VFS_PF_LINK_SPEED, .off = _OUT(pf_link_speed),
.cb = snl_attr_get_uint64 },
};
#undef _OUT
SNL_DECLARE_ATTR_PARSER_EXT(_vf_status_parser,
sizeof(struct snl_parsed_vf_status), _nla_p_vf_status,
_cb_p_vf_status);
static inline bool
_snl_get_vf_status(struct snl_state *ss, struct nlattr *nla,
const void *arg __unused, void *target)
{
struct snl_parsed_vf_status *status = target;
if (!snl_attr_get_nested(ss, nla, &_vf_status_parser, status))
return (false);
status->present = true;
return (true);
}
struct snl_parsed_link {
uint32_t ifi_index;
uint32_t ifi_flags;
@@ -193,10 +539,12 @@ 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_parray iflaf_groups;
struct snl_parsed_vf_status iflaf_vf_status;
};
#define _IN(_field) offsetof(struct ifinfomsg, _field)
@@ -206,6 +554,8 @@ static const struct snl_attr_parser _nla_p_link_fbsd[] = {
{ .type = IFLAF_CAPS, .off = _OUT(iflaf_caps), .cb = snl_attr_get_bitset_c },
{ .type = IFLAF_GROUP, .off = _OUT(iflaf_groups), .cb = snl_attr_get_parray,
.arg = &_ifgroups_parser },
{ .type = IFLAF_VF_STATUS, .off = _OUT(iflaf_vf_status),
.cb = _snl_get_vf_status },
};
SNL_DECLARE_ATTR_PARSER(_link_fbsd_parser, _nla_p_link_fbsd);
@@ -216,10 +566,15 @@ 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 },
{ .type = IFLA_FREEBSD, .arg = &_link_fbsd_parser, .cb = snl_attr_get_nested },
{ .type = IFLA_FREEBSD_VF,
.off = _OUT(iflaf_vf_status) +
offsetof(struct snl_parsed_vf_status, vfs),
.arg = &_vf_parser, .cb = snl_attr_get_multi },
};
static const struct snl_field_parser _fp_p_link[] = {
{.off_in = _IN(ifi_index), .off_out = _OUT(ifi_index), .cb = snl_field_get_uint32 },
+584 -20
View File
@@ -42,6 +42,7 @@
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/if_var.h>
#include <net/if_vf_status.h>
#include <net/if_clone.h>
#include <net/route.h>
#include <net/route/nhop.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;
@@ -311,6 +313,529 @@ dump_iface_caps(struct nl_writer *nw, struct ifnet *ifp)
return (true);
}
static bool
dump_vf_bool(struct nl_writer *nw, const struct if_vf_info *vf,
uint64_t field, uint16_t attr, bool value)
{
if ((vf->fields & field) == 0)
return (true);
return (nlattr_add_bool(nw, attr, value));
}
static bool
dump_vf_u8(struct nl_writer *nw, const struct if_vf_info *vf,
uint64_t field, uint16_t attr, uint8_t value)
{
if ((vf->fields & field) == 0)
return (true);
return (nlattr_add_u8(nw, attr, value));
}
static bool
dump_vf_u16(struct nl_writer *nw, const struct if_vf_info *vf,
uint64_t field, uint16_t attr, uint16_t value)
{
if ((vf->fields & field) == 0)
return (true);
return (nlattr_add_u16(nw, attr, value));
}
static bool
dump_vf_u32(struct nl_writer *nw, const struct if_vf_info *vf,
uint64_t field, uint16_t attr, uint32_t value)
{
if ((vf->fields & field) == 0)
return (true);
return (nlattr_add_u32(nw, attr, value));
}
static bool
dump_vf_u64(struct nl_writer *nw, const struct if_vf_info *vf,
uint64_t field, uint16_t attr, uint64_t value)
{
if ((vf->fields & field) == 0)
return (true);
return (nlattr_add_u64(nw, attr, value));
}
static int
dump_vf_string(struct nl_writer *nw, uint16_t attr, const char *value)
{
size_t length;
if (value == NULL)
return (EINVAL);
length = strnlen(value, UINT16_MAX - sizeof(struct nlattr));
if (length == UINT16_MAX - sizeof(struct nlattr))
return (EMSGSIZE);
if (!nlattr_add(nw, attr, (uint16_t)length + 1, value))
return (ENOMEM);
return (0);
}
static int
nlattr_set_len_checked(struct nl_writer *nw, int off)
{
u_int end;
end = nlattr_save_offset(nw);
MPASS(off >= 0 && (u_int)off <= end);
if (end - (u_int)off > UINT16_MAX)
return (EMSGSIZE);
nlattr_set_len(nw, off);
return (0);
}
static int
vf_add_encoded_size(size_t *size, size_t encoded_size)
{
if (encoded_size > UINT16_MAX || *size > UINT16_MAX - encoded_size)
return (EMSGSIZE);
*size += encoded_size;
return (0);
}
static int
vf_add_attr_size(size_t *size, size_t payload_size)
{
if (payload_size > UINT16_MAX - sizeof(struct nlattr))
return (EMSGSIZE);
return (vf_add_encoded_size(size,
NLA_ALIGN(sizeof(struct nlattr) + payload_size)));
}
static int
vf_add_string_size(size_t *size, const char *value)
{
size_t length;
if (value == NULL)
return (EINVAL);
length = strnlen(value, UINT16_MAX - sizeof(struct nlattr));
if (length == UINT16_MAX - sizeof(struct nlattr))
return (EMSGSIZE);
return (vf_add_attr_size(size, length + 1));
}
static int
validate_vf_extension_field(const struct if_vf_ext_field *field,
size_t *encoded_size)
{
size_t size;
int error;
size = NLA_ALIGN(sizeof(struct nlattr));
error = vf_add_string_size(&size, field->name);
if (error != 0)
return (error);
switch (field->type) {
case IFVF_EXT_BOOL:
error = vf_add_attr_size(&size, sizeof(bool));
break;
case IFVF_EXT_NUMBER:
error = vf_add_attr_size(&size, sizeof(uint64_t));
break;
case IFVF_EXT_STRING:
error = vf_add_string_size(&size, field->value.string);
break;
case IFVF_EXT_BINARY:
if (field->value.binary.data == NULL ||
field->value.binary.length == 0)
return (EINVAL);
error = vf_add_attr_size(&size, field->value.binary.length);
break;
default:
return (EINVAL);
}
if (error != 0)
return (error);
*encoded_size = size;
return (0);
}
static int
validate_vf_extension(const struct if_vf_extension *extension,
size_t *encoded_size)
{
size_t field_size, size;
uint32_t i;
int error;
if (extension->name == NULL || extension->num_fields == 0 ||
extension->num_fields > IFVF_MAX_EXTENSION_FIELDS ||
extension->fields == NULL)
return (EINVAL);
size = NLA_ALIGN(sizeof(struct nlattr));
error = vf_add_string_size(&size, extension->name);
if (error == 0)
error = vf_add_attr_size(&size, sizeof(uint32_t));
for (i = 0; error == 0 && i < extension->num_fields; i++) {
error = validate_vf_extension_field(&extension->fields[i],
&field_size);
if (error == 0)
error = vf_add_encoded_size(&size, field_size);
}
if (error != 0)
return (error);
*encoded_size = size;
return (0);
}
static int
validate_vf_entry(const struct if_vf_info *vf, size_t *encoded_size)
{
const uint64_t access_fields = IFVF_F_VLAN | IFVF_F_VLAN_PCP |
IFVF_F_VLAN_PROTO;
const uint64_t rate_fields = IFVF_F_MIN_TX_RATE |
IFVF_F_MAX_TX_RATE;
const uint64_t bool_fields = IFVF_F_CONFIGURED | IFVF_F_INITIALIZED |
IFVF_F_ALLOW_SET_MAC | IFVF_F_ALLOW_SET_VLAN |
IFVF_F_MAC_ANTI_SPOOF | IFVF_F_ALLOW_PROMISC |
IFVF_F_TRAFFIC_ALLOWED | IFVF_F_FAULT_BLOCKED |
IFVF_F_QUARANTINED;
size_t extension_size, size;
uint32_t i;
int error;
if ((vf->fields & IFVF_F_VLAN) != 0 && vf->vlan > EVL_VLID_MASK)
return (EINVAL);
if ((vf->fields & IFVF_F_VLAN_PCP) != 0 && vf->vlan_pcp > 7)
return (EINVAL);
if ((vf->fields & IFVF_F_VLAN_PROTO) != 0 && vf->vlan_proto == 0)
return (EINVAL);
if ((vf->fields & access_fields) != 0 &&
((vf->fields & IFVF_F_VLAN_MODE) == 0 ||
vf->vlan_mode != IFVF_VLAN_ACCESS))
return (EINVAL);
if ((vf->fields & rate_fields) == rate_fields &&
vf->max_tx_rate_bps != 0 &&
vf->min_tx_rate_bps > vf->max_tx_rate_bps)
return (EINVAL);
if ((vf->fields & (IFVF_F_TRAFFIC_ALLOWED | IFVF_F_FAULT_BLOCKED)) ==
(IFVF_F_TRAFFIC_ALLOWED | IFVF_F_FAULT_BLOCKED) &&
vf->traffic_allowed && vf->fault_blocked)
return (EINVAL);
if ((vf->fields & (IFVF_F_FAULT_BLOCKED | IFVF_F_QUARANTINED)) ==
(IFVF_F_FAULT_BLOCKED | IFVF_F_QUARANTINED) &&
vf->quarantined && !vf->fault_blocked)
return (EINVAL);
if ((vf->fields & IFVF_F_VLAN_MODE) != 0 &&
vf->vlan_mode > IFVF_VLAN_TRUNK)
return (EINVAL);
if ((vf->fields & IFVF_F_LINK_STATE_POLICY) != 0 &&
vf->link_state_policy > IFVF_LINK_AUTO)
return (EINVAL);
if ((vf->fields & IFVF_F_API_VERSION) != 0 &&
strnlen(vf->api_version, sizeof(vf->api_version)) ==
sizeof(vf->api_version))
return (EINVAL);
if (vf->num_extensions > IFVF_MAX_EXTENSIONS ||
(vf->num_extensions != 0 && vf->extensions == NULL))
return (EINVAL);
size = NLA_ALIGN(sizeof(struct nlattr));
error = vf_add_attr_size(&size, sizeof(uint32_t));
for (uint64_t field = 1; error == 0 && field <= IFVF_F_QUARANTINED;
field <<= 1) {
if ((bool_fields & field) != 0 && (vf->fields & field) != 0)
error = vf_add_attr_size(&size, sizeof(bool));
}
if (error == 0 && (vf->fields & IFVF_F_MAC) != 0)
error = vf_add_attr_size(&size, sizeof(vf->mac));
if (error == 0 && (vf->fields & IFVF_F_VLAN_MODE) != 0)
error = vf_add_attr_size(&size, sizeof(uint8_t));
if (error == 0 && (vf->fields & IFVF_F_VLAN) != 0)
error = vf_add_attr_size(&size, sizeof(uint16_t));
if (error == 0 && (vf->fields & IFVF_F_VLAN_PCP) != 0)
error = vf_add_attr_size(&size, sizeof(uint8_t));
if (error == 0 && (vf->fields & IFVF_F_VLAN_PROTO) != 0)
error = vf_add_attr_size(&size, sizeof(uint16_t));
if (error == 0 && (vf->fields & IFVF_F_VLAN_COUNT) != 0)
error = vf_add_attr_size(&size, sizeof(uint32_t));
if (error == 0 && (vf->fields & IFVF_F_VLAN_LIMIT) != 0)
error = vf_add_attr_size(&size, sizeof(uint32_t));
if (error == 0 && (vf->fields & IFVF_F_NUM_TX_QUEUES) != 0)
error = vf_add_attr_size(&size, sizeof(uint16_t));
if (error == 0 && (vf->fields & IFVF_F_NUM_RX_QUEUES) != 0)
error = vf_add_attr_size(&size, sizeof(uint16_t));
if (error == 0 && (vf->fields & IFVF_F_MIN_TX_RATE) != 0)
error = vf_add_attr_size(&size, sizeof(uint64_t));
if (error == 0 && (vf->fields & IFVF_F_MAX_TX_RATE) != 0)
error = vf_add_attr_size(&size, sizeof(uint64_t));
if (error == 0 && (vf->fields & IFVF_F_API_VERSION) != 0)
error = vf_add_string_size(&size, vf->api_version);
if (error == 0 && (vf->fields & IFVF_F_LINK_STATE_POLICY) != 0)
error = vf_add_attr_size(&size, sizeof(uint8_t));
for (i = 0; error == 0 && i < vf->num_extensions; i++) {
error = validate_vf_extension(&vf->extensions[i],
&extension_size);
if (error == 0)
error = vf_add_encoded_size(&size, extension_size);
}
if (error != 0)
return (error);
*encoded_size = size;
return (0);
}
static int
validate_vf_status(const struct if_vf_status *status)
{
size_t encoded_size;
uint32_t i;
int error;
if (status == NULL || status->num_vfs > IFVF_MAX_VFS)
return (EINVAL);
for (i = 0; i < status->num_vfs; i++) {
error = validate_vf_entry(&status->vfs[i], &encoded_size);
if (error != 0)
return (error);
}
return (0);
}
static int
dump_vf_extension_field(struct nl_writer *nw,
const struct if_vf_ext_field *field)
{
int error, off;
if (field->name == NULL)
return (EINVAL);
off = nlattr_add_nested(nw, IFLAF_VFD_FIELD);
if (off == 0)
return (ENOMEM);
error = dump_vf_string(nw, IFLAF_VFDF_NAME, field->name);
if (error != 0)
return (error);
switch (field->type) {
case IFVF_EXT_BOOL:
error = nlattr_add_bool(nw, IFLAF_VFDF_BOOL,
field->value.boolean) ? 0 : ENOMEM;
break;
case IFVF_EXT_NUMBER:
error = nlattr_add_u64(nw, IFLAF_VFDF_NUMBER,
field->value.number) ? 0 : ENOMEM;
break;
case IFVF_EXT_STRING:
error = dump_vf_string(nw, IFLAF_VFDF_STRING,
field->value.string);
break;
case IFVF_EXT_BINARY:
if (field->value.binary.data == NULL ||
field->value.binary.length == 0) {
error = EINVAL;
break;
}
if (field->value.binary.length >
UINT16_MAX - sizeof(struct nlattr)) {
error = EMSGSIZE;
break;
}
error = nlattr_add(nw, IFLAF_VFDF_BINARY,
(uint16_t)field->value.binary.length,
field->value.binary.data) ? 0 : ENOMEM;
break;
default:
error = EINVAL;
break;
}
if (error != 0)
return (error);
return (nlattr_set_len_checked(nw, off));
}
static int
dump_vf_extensions(struct nl_writer *nw, const struct if_vf_info *vf)
{
const struct if_vf_extension *extension;
uint32_t i, j;
int error, off;
if (vf->num_extensions > IFVF_MAX_EXTENSIONS ||
(vf->num_extensions != 0 && vf->extensions == NULL))
return (EINVAL);
for (i = 0; i < vf->num_extensions; i++) {
extension = &vf->extensions[i];
if (extension->name == NULL || extension->num_fields == 0 ||
extension->num_fields > IFVF_MAX_EXTENSION_FIELDS ||
extension->fields == NULL)
return (EINVAL);
off = nlattr_add_nested(nw, IFLAF_VF_DRIVER);
if (off == 0)
return (ENOMEM);
error = dump_vf_string(nw, IFLAF_VFD_NAME, extension->name);
if (error != 0)
return (error);
if (!nlattr_add_u32(nw, IFLAF_VFD_VERSION,
extension->version))
return (ENOMEM);
for (j = 0; j < extension->num_fields; j++) {
error = dump_vf_extension_field(nw,
&extension->fields[j]);
if (error != 0)
return (error);
}
error = nlattr_set_len_checked(nw, off);
if (error != 0)
return (error);
}
return (0);
}
static int
dump_vf_entry(struct nl_writer *nw, const struct if_vf_info *vf)
{
const uint64_t access_fields = IFVF_F_VLAN | IFVF_F_VLAN_PCP |
IFVF_F_VLAN_PROTO;
const uint64_t rate_fields = IFVF_F_MIN_TX_RATE |
IFVF_F_MAX_TX_RATE;
int error, off;
bool ok;
if ((vf->fields & IFVF_F_VLAN) != 0 && vf->vlan > EVL_VLID_MASK)
return (EINVAL);
if ((vf->fields & IFVF_F_VLAN_PCP) != 0 && vf->vlan_pcp > 7)
return (EINVAL);
if ((vf->fields & IFVF_F_VLAN_PROTO) != 0 && vf->vlan_proto == 0)
return (EINVAL);
if ((vf->fields & access_fields) != 0 &&
((vf->fields & IFVF_F_VLAN_MODE) == 0 ||
vf->vlan_mode != IFVF_VLAN_ACCESS))
return (EINVAL);
if ((vf->fields & rate_fields) == rate_fields &&
vf->max_tx_rate_bps != 0 &&
vf->min_tx_rate_bps > vf->max_tx_rate_bps)
return (EINVAL);
if ((vf->fields & (IFVF_F_TRAFFIC_ALLOWED | IFVF_F_FAULT_BLOCKED)) ==
(IFVF_F_TRAFFIC_ALLOWED | IFVF_F_FAULT_BLOCKED) &&
vf->traffic_allowed && vf->fault_blocked)
return (EINVAL);
if ((vf->fields & (IFVF_F_FAULT_BLOCKED | IFVF_F_QUARANTINED)) ==
(IFVF_F_FAULT_BLOCKED | IFVF_F_QUARANTINED) &&
vf->quarantined && !vf->fault_blocked)
return (EINVAL);
off = nlattr_add_nested(nw, IFLA_FREEBSD_VF);
if (off == 0)
return (ENOMEM);
ok = nlattr_add_u32(nw, IFLAF_VF_INDEX, vf->index) &&
dump_vf_bool(nw, vf, IFVF_F_CONFIGURED, IFLAF_VF_CONFIGURED,
vf->configured) &&
dump_vf_bool(nw, vf, IFVF_F_INITIALIZED, IFLAF_VF_INITIALIZED,
vf->initialized);
if (!ok)
return (ENOMEM);
if ((vf->fields & IFVF_F_MAC) != 0 &&
!nlattr_add(nw, IFLAF_VF_MAC, sizeof(vf->mac), vf->mac))
return (ENOMEM);
if ((vf->fields & IFVF_F_VLAN_MODE) != 0) {
if (vf->vlan_mode > IFVF_VLAN_TRUNK)
return (EINVAL);
if (!nlattr_add_u8(nw, IFLAF_VF_VLAN_MODE,
(uint8_t)vf->vlan_mode))
return (ENOMEM);
}
ok = dump_vf_u16(nw, vf, IFVF_F_VLAN, IFLAF_VF_VLAN, vf->vlan) &&
dump_vf_u8(nw, vf, IFVF_F_VLAN_PCP, IFLAF_VF_VLAN_PCP,
vf->vlan_pcp) &&
dump_vf_u16(nw, vf, IFVF_F_VLAN_PROTO, IFLAF_VF_VLAN_PROTO,
vf->vlan_proto) &&
dump_vf_u32(nw, vf, IFVF_F_VLAN_COUNT, IFLAF_VF_VLAN_COUNT,
vf->vlan_count) &&
dump_vf_u32(nw, vf, IFVF_F_VLAN_LIMIT, IFLAF_VF_VLAN_LIMIT,
vf->vlan_limit) &&
dump_vf_u16(nw, vf, IFVF_F_NUM_TX_QUEUES,
IFLAF_VF_NUM_TX_QUEUES, vf->tx_queue_count) &&
dump_vf_u16(nw, vf, IFVF_F_NUM_RX_QUEUES,
IFLAF_VF_NUM_RX_QUEUES, vf->rx_queue_count) &&
dump_vf_u64(nw, vf, IFVF_F_MIN_TX_RATE, IFLAF_VF_MIN_TX_RATE,
vf->min_tx_rate_bps) &&
dump_vf_u64(nw, vf, IFVF_F_MAX_TX_RATE, IFLAF_VF_MAX_TX_RATE,
vf->max_tx_rate_bps) &&
dump_vf_bool(nw, vf, IFVF_F_ALLOW_SET_MAC, IFLAF_VF_ALLOW_SET_MAC,
vf->allow_set_mac) &&
dump_vf_bool(nw, vf, IFVF_F_ALLOW_SET_VLAN,
IFLAF_VF_ALLOW_SET_VLAN, vf->allow_set_vlan) &&
dump_vf_bool(nw, vf, IFVF_F_MAC_ANTI_SPOOF,
IFLAF_VF_MAC_ANTI_SPOOF, vf->mac_anti_spoof) &&
dump_vf_bool(nw, vf, IFVF_F_ALLOW_PROMISC,
IFLAF_VF_ALLOW_PROMISC, vf->allow_promisc) &&
dump_vf_bool(nw, vf, IFVF_F_TRAFFIC_ALLOWED,
IFLAF_VF_TRAFFIC_ALLOWED, vf->traffic_allowed) &&
dump_vf_bool(nw, vf, IFVF_F_FAULT_BLOCKED, IFLAF_VF_FAULT_BLOCKED,
vf->fault_blocked) &&
dump_vf_bool(nw, vf, IFVF_F_QUARANTINED, IFLAF_VF_QUARANTINED,
vf->quarantined);
if (!ok)
return (ENOMEM);
if ((vf->fields & IFVF_F_API_VERSION) != 0) {
if (strnlen(vf->api_version, sizeof(vf->api_version)) ==
sizeof(vf->api_version))
return (EINVAL);
error = dump_vf_string(nw, IFLAF_VF_API_VERSION,
vf->api_version);
if (error != 0)
return (error);
}
if ((vf->fields & IFVF_F_LINK_STATE_POLICY) != 0) {
if (vf->link_state_policy > IFVF_LINK_AUTO)
return (EINVAL);
if (!nlattr_add_u8(nw, IFLAF_VF_LINK_STATE_POLICY,
(uint8_t)vf->link_state_policy))
return (ENOMEM);
}
error = dump_vf_extensions(nw, vf);
if (error != 0)
return (error);
return (nlattr_set_len_checked(nw, off));
}
static int
dump_vf_status(struct nl_writer *nw, if_t ifp,
int error)
{
uint64_t baudrate;
uint8_t link_state;
int off;
off = nlattr_add_nested(nw, IFLAF_VF_STATUS);
if (off == 0)
return (ENOMEM);
if (error != 0) {
if (!nlattr_add_u32(nw, IFLAF_VFS_ERROR, (uint32_t)error))
return (ENOMEM);
return (nlattr_set_len_checked(nw, off));
}
switch (if_getlinkstate(ifp)) {
case LINK_STATE_DOWN:
link_state = IFLAF_VF_LINK_DOWN;
break;
case LINK_STATE_UP:
link_state = IFLAF_VF_LINK_UP;
break;
default:
link_state = IFLAF_VF_LINK_UNKNOWN;
break;
}
if (!nlattr_add_u8(nw, IFLAF_VFS_PF_LINK_STATE, link_state))
return (ENOMEM);
baudrate = if_getbaudrate(ifp);
if (baudrate != 0 &&
!nlattr_add_u64(nw, IFLAF_VFS_PF_LINK_SPEED, baudrate))
return (ENOMEM);
return (nlattr_set_len_checked(nw, off));
}
/*
* Dumps interface state, properties and metrics.
* @nw: message writer
@@ -320,14 +845,27 @@ dump_iface_caps(struct nl_writer *nw, struct ifnet *ifp)
*
* This function is called without epoch and MAY sleep.
*/
static bool
static int
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;
struct if_vf_status *vf_status;
struct ifinfomsg *ifinfo;
bool include_vf_status;
int error, 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 == EOPNOTSUPP)
include_vf_status = false;
else if (vf_error == 0)
vf_error = validate_vf_status(vf_status);
}
if (!nlmsg_reply(nw, hdr, sizeof(struct ifinfomsg)))
goto enomem;
@@ -373,6 +911,10 @@ dump_iface(struct nl_writer *nw, if_t ifp, const struct nlmsghdr *hdr,
}
nlattr_add_u32(nw, IFLA_MTU, if_getmtu(ifp));
if (vf_error == 0 && vf_status != NULL) {
if (!nlattr_add_u32(nw, IFLA_NUM_VF, vf_status->num_vfs))
goto enomem;
}
/*
nlattr_add_u32(nw, IFLA_MIN_MTU, 60);
nlattr_add_u32(nw, IFLA_MAX_MTU, 9000);
@@ -383,13 +925,26 @@ dump_iface(struct nl_writer *nw, if_t ifp, const struct nlmsghdr *hdr,
/* Store FreeBSD-specific attributes */
int off = nlattr_add_nested(nw, IFLA_FREEBSD);
if (off != 0) {
get_hwaddr(nw, ifp);
dump_iface_caps(nw, ifp);
if (dump_group(nw, ifp) != 0)
goto enomem;
nlattr_set_len(nw, off);
if (off == 0)
goto enomem;
get_hwaddr(nw, ifp);
dump_iface_caps(nw, ifp);
if (dump_group(nw, ifp) != 0)
goto enomem;
if (include_vf_status) {
error = dump_vf_status(nw, ifp, vf_error);
if (error != 0)
goto fail;
}
error = nlattr_set_len_checked(nw, off);
if (error != 0)
goto fail;
if (vf_error == 0 && vf_status != NULL) {
for (uint32_t i = 0; i < vf_status->num_vfs; i++) {
error = dump_vf_entry(nw, &vf_status->vfs[i]);
if (error != 0)
goto fail;
}
}
get_stats(nw, ifp);
@@ -401,13 +956,21 @@ dump_iface(struct nl_writer *nw, if_t ifp, const struct nlmsghdr *hdr,
nw->ifp = ifp;
if (nlmsg_end(nw))
return (true);
if (nlmsg_end(nw)) {
if (vf_status != NULL)
if_vf_status_free(vf_status);
return (0);
}
enomem:
NL_LOG(LOG_DEBUG, "unable to dump interface %s state (ENOMEM)", if_name(ifp));
error = ENOMEM;
fail:
if (vf_status != NULL)
if_vf_status_free(vf_status);
NL_LOG(LOG_DEBUG, "unable to dump interface %s state: %d",
if_name(ifp), error);
nlmsg_abort(nw);
return (false);
return (error);
}
static bool
@@ -448,6 +1011,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
@@ -474,9 +1038,8 @@ 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))
return (ENOMEM);
return (0);
return (dump_iface(wa->nw, ifp, &wa->hdr, 0, NULL, wa->ext_mask));
}
/*
@@ -506,6 +1069,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 */
@@ -524,8 +1088,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))
error = ENOMEM;
error = dump_iface(wa.nw, ifp, &wa.hdr, 0,
NULL, wa.ext_mask);
} else
error = ENODEV;
if_rele(ifp);
@@ -544,7 +1108,7 @@ rtnl_handle_getlink(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_pstate *n
*/
NL_LOG(LOG_DEBUG2, "Start dump");
if_foreach_sleep(match_iface, &attrs, dump_cb, &wa);
error = if_foreach_sleep(match_iface, &attrs, dump_cb, &wa);
NL_LOG(LOG_DEBUG2, "End dump, iterated %d dumped %d", wa.count, wa.dumped);
if (!nlmsg_end_dump(wa.nw, error, &wa.hdr)) {
@@ -1451,7 +2015,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);
}
+82 -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: VF records, when requested */
#define IFLA_NUM_VF IFLA_NUM_VF
IFLA_VFINFO_LIST= 22, /* not supported */
#define IFLA_VFINFO_LIST IFLA_VFINFO_LIST
@@ -143,20 +143,101 @@ enum {
IFLA_GSO_IPV4_MAX_SIZE,
IFLA_GRO_IPV4_MAX_SIZE,
IFLA_FREEBSD,
IFLA_FREEBSD_VF, /* nested, IFLAF_VF_*; repeated */
__IFLA_MAX
};
#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_GROUP = 4, /* string, interface group name (multi-attr) */
IFLAF_VF_STATUS = 5, /* nested, IFLAF_VFS_* */
__IFLAF_MAX
};
#define IFLAF_MAX (__IFLAF_MAX - 1)
/* IFLAF_VF_STATUS attributes. */
enum {
IFLAF_VFS_UNSPEC = 0,
IFLAF_VFS_ERROR = 1, /* u32: errno from requested query */
IFLAF_VFS_PF_LINK_STATE = 2, /* u8: IFLAF_VF_LINK_* */
IFLAF_VFS_PF_LINK_SPEED = 3, /* u64: bits per second */
__IFLAF_VFS_MAX
};
#define IFLAF_VFS_MAX (__IFLAF_VFS_MAX - 1)
/* IFLA_FREEBSD_VF attributes. */
enum {
IFLAF_VF_UNSPEC = 0,
IFLAF_VF_INDEX = 1, /* u32 */
IFLAF_VF_CONFIGURED = 2, /* bool */
IFLAF_VF_INITIALIZED = 3, /* bool */
IFLAF_VF_MAC = 4, /* binary */
IFLAF_VF_VLAN_MODE = 5, /* u8: IFLAF_VF_VLAN_* */
IFLAF_VF_VLAN = 6, /* u16 */
IFLAF_VF_VLAN_PCP = 7, /* u8: IEEE 802.1p PCP */
IFLAF_VF_VLAN_PROTO = 8, /* u16: host-order EtherType */
IFLAF_VF_VLAN_COUNT = 9, /* u32 */
IFLAF_VF_VLAN_LIMIT = 10, /* u32 */
IFLAF_VF_NUM_TX_QUEUES = 11, /* u16 */
IFLAF_VF_NUM_RX_QUEUES = 12, /* u16 */
IFLAF_VF_MIN_TX_RATE = 13, /* u64: minimum bits per second */
IFLAF_VF_MAX_TX_RATE = 14, /* u64: maximum bits per second */
IFLAF_VF_ALLOW_SET_MAC = 15, /* bool */
IFLAF_VF_ALLOW_SET_VLAN = 16, /* bool */
IFLAF_VF_MAC_ANTI_SPOOF = 17, /* bool */
IFLAF_VF_ALLOW_PROMISC = 18, /* bool */
IFLAF_VF_TRAFFIC_ALLOWED = 19, /* bool */
IFLAF_VF_FAULT_BLOCKED = 20, /* bool */
IFLAF_VF_QUARANTINED = 21, /* bool */
IFLAF_VF_API_VERSION = 22, /* string */
IFLAF_VF_LINK_STATE_POLICY = 23, /* u8: IFLAF_VF_LINK_* */
IFLAF_VF_DRIVER = 24, /* nested; repeated */
__IFLAF_VF_MAX
};
#define IFLAF_VF_MAX (__IFLAF_VF_MAX - 1)
/* IFLAF_VF_DRIVER attributes. */
enum {
IFLAF_VFD_UNSPEC = 0,
IFLAF_VFD_NAME = 1, /* string: stable namespace */
IFLAF_VFD_VERSION = 2, /* u32: namespace version */
IFLAF_VFD_FIELD = 3, /* nested, IFLAF_VFDF_*; repeated */
__IFLAF_VFD_MAX
};
#define IFLAF_VFD_MAX (__IFLAF_VFD_MAX - 1)
/* IFLAF_VFD_FIELD attributes; exactly one value attribute is present. */
enum {
IFLAF_VFDF_UNSPEC = 0,
IFLAF_VFDF_NAME = 1, /* string: namespace-local name */
IFLAF_VFDF_BOOL = 2, /* bool */
IFLAF_VFDF_NUMBER = 3, /* u64 */
IFLAF_VFDF_STRING = 4, /* string */
IFLAF_VFDF_BINARY = 5, /* binary */
__IFLAF_VFDF_MAX
};
#define IFLAF_VFDF_MAX (__IFLAF_VFDF_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
@@ -355,12 +357,66 @@ class IflattrType(Enum):
IFLA_GSO_IPV4_MAX_SIZE = 62
IFLA_GRO_IPV4_MAX_SIZE = 63
IFLA_FREEBSD = 64
IFLA_FREEBSD_VF = 65
class IflafAttrType(Enum):
IFLAF_UNSPEC = 0
IFLAF_ORIG_IFNAME = 1
IFLAF_ORIG_HWADDR = 2
IFLAF_CAPS = 3
IFLAF_VF_STATUS = 5
class IflafVfStatusType(Enum):
IFLAF_VFS_UNSPEC = 0
IFLAF_VFS_ERROR = 1
IFLAF_VFS_PF_LINK_STATE = 2
IFLAF_VFS_PF_LINK_SPEED = 3
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_PCP = 7
IFLAF_VF_VLAN_PROTO = 8
IFLAF_VF_VLAN_COUNT = 9
IFLAF_VF_VLAN_LIMIT = 10
IFLAF_VF_NUM_TX_QUEUES = 11
IFLAF_VF_NUM_RX_QUEUES = 12
IFLAF_VF_MIN_TX_RATE = 13
IFLAF_VF_MAX_TX_RATE = 14
IFLAF_VF_ALLOW_SET_MAC = 15
IFLAF_VF_ALLOW_SET_VLAN = 16
IFLAF_VF_MAC_ANTI_SPOOF = 17
IFLAF_VF_ALLOW_PROMISC = 18
IFLAF_VF_TRAFFIC_ALLOWED = 19
IFLAF_VF_FAULT_BLOCKED = 20
IFLAF_VF_QUARANTINED = 21
IFLAF_VF_API_VERSION = 22
IFLAF_VF_LINK_STATE_POLICY = 23
IFLAF_VF_DRIVER = 24
class IflafVfDriverType(Enum):
IFLAF_VFD_UNSPEC = 0
IFLAF_VFD_NAME = 1
IFLAF_VFD_VERSION = 2
IFLAF_VFD_FIELD = 3
class IflafVfDriverFieldType(Enum):
IFLAF_VFDF_UNSPEC = 0
IFLAF_VFDF_NAME = 1
IFLAF_VFDF_BOOL = 2
IFLAF_VFDF_NUMBER = 3
IFLAF_VFDF_STRING = 4
IFLAF_VFDF_BINARY = 5
class IflinkInfo(Enum):
@@ -621,6 +677,61 @@ rtnl_route_attrs = prepare_attrs_map(
]
)
rtnl_iflaf_vf_driver_field_attrs = [
AttrDescr(IflafVfDriverFieldType.IFLAF_VFDF_NAME, NlAttrStr),
AttrDescr(IflafVfDriverFieldType.IFLAF_VFDF_BOOL, NlAttrU8),
AttrDescr(IflafVfDriverFieldType.IFLAF_VFDF_NUMBER, NlAttrU64),
AttrDescr(IflafVfDriverFieldType.IFLAF_VFDF_STRING, NlAttrStr),
AttrDescr(IflafVfDriverFieldType.IFLAF_VFDF_BINARY, NlAttr),
]
rtnl_iflaf_vf_driver_attrs = [
AttrDescr(IflafVfDriverType.IFLAF_VFD_NAME, NlAttrStr),
AttrDescr(IflafVfDriverType.IFLAF_VFD_VERSION, NlAttrU32),
AttrDescr(
IflafVfDriverType.IFLAF_VFD_FIELD,
NlAttrNested,
rtnl_iflaf_vf_driver_field_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_PCP, NlAttrU8),
AttrDescr(IflafVfType.IFLAF_VF_VLAN_PROTO, NlAttrU16),
AttrDescr(IflafVfType.IFLAF_VF_VLAN_COUNT, NlAttrU32),
AttrDescr(IflafVfType.IFLAF_VF_VLAN_LIMIT, NlAttrU32),
AttrDescr(IflafVfType.IFLAF_VF_NUM_TX_QUEUES, NlAttrU16),
AttrDescr(IflafVfType.IFLAF_VF_NUM_RX_QUEUES, NlAttrU16),
AttrDescr(IflafVfType.IFLAF_VF_MIN_TX_RATE, NlAttrU64),
AttrDescr(IflafVfType.IFLAF_VF_MAX_TX_RATE, NlAttrU64),
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_ALLOWED, NlAttrU8),
AttrDescr(IflafVfType.IFLAF_VF_FAULT_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_DRIVER,
NlAttrNested,
rtnl_iflaf_vf_driver_attrs,
),
]
rtnl_iflaf_vf_status_attrs = [
AttrDescr(IflafVfStatusType.IFLAF_VFS_ERROR, NlAttrU32),
AttrDescr(IflafVfStatusType.IFLAF_VFS_PF_LINK_STATE, NlAttrU8),
AttrDescr(IflafVfStatusType.IFLAF_VFS_PF_LINK_SPEED, NlAttrU64),
]
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,8 +759,18 @@ rtnl_ifla_attrs = prepare_attrs_map(
NlAttrNested,
[
AttrDescr(IflafAttrType.IFLAF_ORIG_HWADDR, NlAttrMac),
AttrDescr(
IflafAttrType.IFLAF_VF_STATUS,
NlAttrNested,
rtnl_iflaf_vf_status_attrs,
),
],
),
AttrDescr(
IflattrType.IFLA_FREEBSD_VF,
NlAttrNested,
rtnl_iflaf_vf_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"""
+413
View File
@@ -5,6 +5,8 @@
#include <sys/param.h>
#include <sys/module.h>
#include <net/ethernet.h>
#include <netlink/netlink.h>
#include <netlink/netlink_route.h>
#include "netlink/netlink_snl.h"
@@ -20,6 +22,8 @@ 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_driver_field_parser, &_vf_driver_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,
@@ -101,6 +105,411 @@ ATF_TC_BODY(snl_parse_bitset_array, tc)
ATF_CHECK_STREQ(bit->bit_name, "bit-8");
ATF_CHECK_EQ(bit->bit_value, 1);
}
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_driver_field *field;
struct snl_parsed_vf_driver *driver;
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;
uint32_t i;
int driver_off, entry_off, fbsd_off, field_off, 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, 9));
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_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));
snl_end_attr_nested(&nw, status_off);
snl_end_attr_nested(&nw, fbsd_off);
entry_off = snl_add_msg_attr_nested(&nw, IFLA_FREEBSD_VF);
ATF_REQUIRE(entry_off != 0);
ATF_REQUIRE(snl_add_msg_attr_u32(&nw, IFLAF_VF_INDEX, 7));
ATF_REQUIRE(snl_add_msg_attr_bool(&nw, IFLAF_VF_CONFIGURED, true));
ATF_REQUIRE(snl_add_msg_attr_bool(&nw, IFLAF_VF_TRAFFIC_ALLOWED,
true));
ATF_REQUIRE(snl_add_msg_attr_bool(&nw, IFLAF_VF_FAULT_BLOCKED,
false));
ATF_REQUIRE(snl_add_msg_attr_u16(&nw, IFLAF_VF_NUM_TX_QUEUES, 4));
ATF_REQUIRE(snl_add_msg_attr_u16(&nw, IFLAF_VF_NUM_RX_QUEUES, 8));
ATF_REQUIRE(snl_add_msg_attr_u64(&nw, IFLAF_VF_MIN_TX_RATE,
25000000000ULL));
ATF_REQUIRE(snl_add_msg_attr_u64(&nw, IFLAF_VF_MAX_TX_RATE,
100000000000ULL));
ATF_REQUIRE(snl_add_msg_attr_u8(&nw, IFLAF_VF_VLAN_MODE,
IFLAF_VF_VLAN_ACCESS));
ATF_REQUIRE(snl_add_msg_attr_u16(&nw, IFLAF_VF_VLAN, 0));
ATF_REQUIRE(snl_add_msg_attr_u8(&nw, IFLAF_VF_VLAN_PCP, 5));
ATF_REQUIRE(snl_add_msg_attr_u16(&nw, IFLAF_VF_VLAN_PROTO,
ETHERTYPE_VLAN));
ATF_REQUIRE(snl_add_msg_attr(&nw, IFLAF_VF_MAC, sizeof(mac), mac));
driver_off = snl_add_msg_attr_nested(&nw, IFLAF_VF_DRIVER);
ATF_REQUIRE(driver_off != 0);
ATF_REQUIRE(snl_add_msg_attr_string(&nw, IFLAF_VFD_NAME,
"driver.test"));
ATF_REQUIRE(snl_add_msg_attr_u32(&nw, IFLAF_VFD_VERSION, 3));
field_off = snl_add_msg_attr_nested(&nw, IFLAF_VFD_FIELD);
ATF_REQUIRE(field_off != 0);
ATF_REQUIRE(snl_add_msg_attr_string(&nw, IFLAF_VFDF_NAME,
"ready"));
ATF_REQUIRE(snl_add_msg_attr_bool(&nw, IFLAF_VFDF_BOOL, false));
snl_end_attr_nested(&nw, field_off);
field_off = snl_add_msg_attr_nested(&nw, IFLAF_VFD_FIELD);
ATF_REQUIRE(field_off != 0);
ATF_REQUIRE(snl_add_msg_attr_string(&nw, IFLAF_VFDF_NAME,
"opaque"));
ATF_REQUIRE(snl_add_msg_attr(&nw, IFLAF_VFDF_BINARY,
sizeof(extension_data), extension_data));
snl_end_attr_nested(&nw, field_off);
field_off = snl_add_msg_attr_nested(&nw, IFLAF_VFD_FIELD);
ATF_REQUIRE(field_off != 0);
ATF_REQUIRE(snl_add_msg_attr_string(&nw, IFLAF_VFDF_NAME,
"sequence"));
ATF_REQUIRE(snl_add_msg_attr_u64(&nw, IFLAF_VFDF_NUMBER,
0x100000002ULL));
snl_end_attr_nested(&nw, field_off);
field_off = snl_add_msg_attr_nested(&nw, IFLAF_VFD_FIELD);
ATF_REQUIRE(field_off != 0);
ATF_REQUIRE(snl_add_msg_attr_string(&nw, IFLAF_VFDF_NAME,
"mode"));
ATF_REQUIRE(snl_add_msg_attr_string(&nw, IFLAF_VFDF_STRING,
"diagnostic"));
snl_end_attr_nested(&nw, field_off);
/* A newer field value type must not hide fields known to this parser. */
field_off = snl_add_msg_attr_nested(&nw, IFLAF_VFD_FIELD);
ATF_REQUIRE(field_off != 0);
ATF_REQUIRE(snl_add_msg_attr_string(&nw, IFLAF_VFDF_NAME,
"future-value"));
ATF_REQUIRE(snl_add_msg_attr_u32(&nw, IFLAF_VFDF_MAX + 1, 1));
snl_end_attr_nested(&nw, field_off);
snl_end_attr_nested(&nw, driver_off);
snl_end_attr_nested(&nw, entry_off);
/* A second attribute of the same type is the second array member. */
entry_off = snl_add_msg_attr_nested(&nw, IFLA_FREEBSD_VF);
ATF_REQUIRE(entry_off != 0);
ATF_REQUIRE(snl_add_msg_attr_u32(&nw, IFLAF_VF_INDEX, 8));
ATF_REQUIRE(snl_add_msg_attr_bool(&nw, IFLAF_VF_CONFIGURED, false));
ATF_REQUIRE(snl_add_msg_attr_u64(&nw, IFLAF_VF_MIN_TX_RATE, 0));
ATF_REQUIRE(snl_add_msg_attr_u64(&nw, IFLAF_VF_MAX_TX_RATE, 0));
snl_end_attr_nested(&nw, entry_off);
/* Cross the parser array's initial capacity to exercise growth. */
for (i = 9; i <= 15; i++) {
entry_off = snl_add_msg_attr_nested(&nw, IFLA_FREEBSD_VF);
ATF_REQUIRE(entry_off != 0);
ATF_REQUIRE(snl_add_msg_attr_u32(&nw, IFLAF_VF_INDEX, i));
snl_end_attr_nested(&nw, entry_off);
}
hdr = snl_finalize_msg(&nw);
ATF_REQUIRE(hdr != NULL);
ATF_REQUIRE(snl_parse_nlmsg(&ss, hdr, &snl_rtm_link_parser, &link));
ATF_CHECK_EQ(link.ifla_num_vf, 9);
status = &link.iflaf_vf_status;
ATF_CHECK(status->present);
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, 9);
vf = status->vfs.items[0];
ATF_CHECK_EQ(vf->index, 7);
ATF_CHECK_EQ(vf->configured, 1);
ATF_CHECK((vf->attrs & (1ULL << IFLAF_VF_TRAFFIC_ALLOWED)) != 0);
ATF_CHECK_EQ(vf->traffic_allowed, true);
ATF_CHECK((vf->attrs & (1ULL << IFLAF_VF_FAULT_BLOCKED)) != 0);
ATF_CHECK_EQ(vf->fault_blocked, false);
ATF_CHECK((vf->attrs & (1ULL << IFLAF_VF_NUM_TX_QUEUES)) != 0);
ATF_CHECK_EQ(vf->tx_queue_count, 4);
ATF_CHECK((vf->attrs & (1ULL << IFLAF_VF_NUM_RX_QUEUES)) != 0);
ATF_CHECK_EQ(vf->rx_queue_count, 8);
ATF_CHECK((vf->attrs & (1ULL << IFLAF_VF_MIN_TX_RATE)) != 0);
ATF_CHECK_EQ(vf->min_tx_rate_bps, 25000000000ULL);
ATF_CHECK((vf->attrs & (1ULL << IFLAF_VF_MAX_TX_RATE)) != 0);
ATF_CHECK_EQ(vf->max_tx_rate_bps, 100000000000ULL);
ATF_CHECK((vf->attrs & (1ULL << IFLAF_VF_VLAN_MODE)) != 0);
ATF_CHECK_EQ(vf->vlan_mode, IFLAF_VF_VLAN_ACCESS);
ATF_CHECK((vf->attrs & (1ULL << IFLAF_VF_VLAN)) != 0);
ATF_CHECK_EQ(vf->vlan, 0);
ATF_CHECK((vf->attrs & (1ULL << IFLAF_VF_VLAN_PCP)) != 0);
ATF_CHECK_EQ(vf->vlan_pcp, 5);
ATF_CHECK((vf->attrs & (1ULL << IFLAF_VF_VLAN_PROTO)) != 0);
ATF_CHECK_EQ(vf->vlan_proto, ETHERTYPE_VLAN);
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->drivers.count, 1);
driver = vf->drivers.items[0];
ATF_CHECK_STREQ(driver->name, "driver.test");
ATF_CHECK_EQ(driver->version, 3);
ATF_REQUIRE_EQ(driver->fields.count, 4);
field = driver->fields.items[0];
ATF_CHECK_STREQ(field->name, "ready");
ATF_CHECK_EQ(field->type, SNL_VFDF_BOOL);
ATF_CHECK_EQ(field->boolean, false);
field = driver->fields.items[1];
ATF_CHECK_STREQ(field->name, "opaque");
ATF_CHECK_EQ(field->type, SNL_VFDF_BINARY);
ATF_REQUIRE(field->binary != NULL);
ATF_CHECK_EQ(NLA_DATA_LEN(field->binary), sizeof(extension_data));
ATF_CHECK(memcmp(NLA_DATA(field->binary), extension_data,
sizeof(extension_data)) == 0);
field = driver->fields.items[2];
ATF_CHECK_STREQ(field->name, "sequence");
ATF_CHECK_EQ(field->type, SNL_VFDF_NUMBER);
ATF_CHECK_EQ(field->number, 0x100000002ULL);
field = driver->fields.items[3];
ATF_CHECK_STREQ(field->name, "mode");
ATF_CHECK_EQ(field->type, SNL_VFDF_STRING);
ATF_CHECK_STREQ(field->string, "diagnostic");
vf = status->vfs.items[1];
ATF_CHECK_EQ(vf->index, 8);
ATF_CHECK_EQ(vf->configured, false);
ATF_CHECK((vf->attrs & (1ULL << IFLAF_VF_MIN_TX_RATE)) != 0);
ATF_CHECK_EQ(vf->min_tx_rate_bps, 0);
ATF_CHECK((vf->attrs & (1ULL << IFLAF_VF_MAX_TX_RATE)) != 0);
ATF_CHECK_EQ(vf->max_tx_rate_bps, 0);
vf = status->vfs.items[8];
ATF_CHECK_EQ(vf->index, 15);
}
ATF_TC(snl_parse_large_vf_status);
ATF_TC_HEAD(snl_parse_large_vf_status, tc)
{
atf_tc_set_md_var(tc, "descr",
"Tests snl(3) parsing more than 256 full VF records");
atf_tc_set_md_var(tc, "require.kmods", "netlink");
}
ATF_TC_BODY(snl_parse_large_vf_status, tc)
{
static const char *field_names[] = {
"mirror-configured",
"mirror-ingress-active",
"mirror-egress-active",
};
static const uint32_t vf_count = 512;
struct snl_parsed_vf_driver *driver;
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;
uint8_t mac[ETHER_ADDR_LEN] = { 0x02, 0, 0, 0, 0, 0 };
uint32_t i, j;
int driver_off, entry_off, fbsd_off, field_off, 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, vf_count));
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_u8(&nw, IFLAF_VFS_PF_LINK_STATE,
IFLAF_VF_LINK_UP));
ATF_REQUIRE(snl_add_msg_attr_u64(&nw, IFLAF_VFS_PF_LINK_SPEED,
100000000000ULL));
snl_end_attr_nested(&nw, status_off);
snl_end_attr_nested(&nw, fbsd_off);
for (i = 0; i < vf_count; i++) {
mac[4] = i >> 8;
mac[5] = i;
entry_off = snl_add_msg_attr_nested(&nw, IFLA_FREEBSD_VF);
ATF_REQUIRE(entry_off != 0);
ATF_REQUIRE(snl_add_msg_attr_u32(&nw, IFLAF_VF_INDEX, i));
ATF_REQUIRE(snl_add_msg_attr_bool(&nw, IFLAF_VF_CONFIGURED,
true));
ATF_REQUIRE(snl_add_msg_attr_bool(&nw, IFLAF_VF_INITIALIZED,
true));
ATF_REQUIRE(snl_add_msg_attr(&nw, IFLAF_VF_MAC, sizeof(mac),
mac));
ATF_REQUIRE(snl_add_msg_attr_u8(&nw, IFLAF_VF_VLAN_MODE,
IFLAF_VF_VLAN_ACCESS));
ATF_REQUIRE(snl_add_msg_attr_u16(&nw, IFLAF_VF_VLAN,
i & EVL_VLID_MASK));
ATF_REQUIRE(snl_add_msg_attr_u8(&nw, IFLAF_VF_VLAN_PCP,
i & 7));
ATF_REQUIRE(snl_add_msg_attr_u16(&nw, IFLAF_VF_VLAN_PROTO,
ETHERTYPE_VLAN));
ATF_REQUIRE(snl_add_msg_attr_u32(&nw, IFLAF_VF_VLAN_COUNT,
1));
ATF_REQUIRE(snl_add_msg_attr_u32(&nw, IFLAF_VF_VLAN_LIMIT,
16));
ATF_REQUIRE(snl_add_msg_attr_u16(&nw,
IFLAF_VF_NUM_TX_QUEUES, 4));
ATF_REQUIRE(snl_add_msg_attr_u16(&nw,
IFLAF_VF_NUM_RX_QUEUES, 4));
ATF_REQUIRE(snl_add_msg_attr_u64(&nw, IFLAF_VF_MIN_TX_RATE,
1000000000ULL));
ATF_REQUIRE(snl_add_msg_attr_u64(&nw, IFLAF_VF_MAX_TX_RATE,
10000000000ULL));
ATF_REQUIRE(snl_add_msg_attr_bool(&nw, IFLAF_VF_ALLOW_SET_MAC,
true));
ATF_REQUIRE(snl_add_msg_attr_bool(&nw,
IFLAF_VF_ALLOW_SET_VLAN, true));
ATF_REQUIRE(snl_add_msg_attr_bool(&nw,
IFLAF_VF_MAC_ANTI_SPOOF, true));
ATF_REQUIRE(snl_add_msg_attr_bool(&nw, IFLAF_VF_ALLOW_PROMISC,
false));
ATF_REQUIRE(snl_add_msg_attr_bool(&nw,
IFLAF_VF_TRAFFIC_ALLOWED, true));
ATF_REQUIRE(snl_add_msg_attr_bool(&nw,
IFLAF_VF_FAULT_BLOCKED, false));
ATF_REQUIRE(snl_add_msg_attr_bool(&nw, IFLAF_VF_QUARANTINED,
false));
ATF_REQUIRE(snl_add_msg_attr_string(&nw,
IFLAF_VF_API_VERSION, "1.1"));
ATF_REQUIRE(snl_add_msg_attr_u8(&nw,
IFLAF_VF_LINK_STATE_POLICY, IFLAF_VF_LINK_AUTO));
driver_off = snl_add_msg_attr_nested(&nw, IFLAF_VF_DRIVER);
ATF_REQUIRE(driver_off != 0);
ATF_REQUIRE(snl_add_msg_attr_string(&nw, IFLAF_VFD_NAME,
"driver.ice"));
ATF_REQUIRE(snl_add_msg_attr_u32(&nw, IFLAF_VFD_VERSION, 1));
for (j = 0; j < nitems(field_names); j++) {
field_off = snl_add_msg_attr_nested(&nw,
IFLAF_VFD_FIELD);
ATF_REQUIRE(field_off != 0);
ATF_REQUIRE(snl_add_msg_attr_string(&nw,
IFLAF_VFDF_NAME, field_names[j]));
ATF_REQUIRE(snl_add_msg_attr_bool(&nw,
IFLAF_VFDF_BOOL, false));
snl_end_attr_nested(&nw, field_off);
}
snl_end_attr_nested(&nw, driver_off);
snl_end_attr_nested(&nw, entry_off);
}
hdr = snl_finalize_msg(&nw);
ATF_REQUIRE(hdr != NULL);
ATF_REQUIRE_MSG(hdr->nlmsg_len > UINT16_MAX,
"test message is only %u bytes", hdr->nlmsg_len);
ATF_REQUIRE(snl_parse_nlmsg(&ss, hdr, &snl_rtm_link_parser, &link));
ATF_CHECK_EQ(link.ifla_num_vf, vf_count);
status = &link.iflaf_vf_status;
ATF_CHECK(status->present);
ATF_REQUIRE_EQ(status->vfs.count, vf_count);
vf = status->vfs.items[0];
ATF_CHECK_EQ(vf->index, 0);
vf = status->vfs.items[vf_count - 1];
ATF_CHECK_EQ(vf->index, vf_count - 1);
ATF_CHECK_EQ(vf->tx_queue_count, 4);
ATF_CHECK_EQ(vf->rx_queue_count, 4);
ATF_REQUIRE_EQ(vf->drivers.count, 1);
driver = vf->drivers.items[0];
ATF_CHECK_STREQ(driver->name, "driver.ice");
ATF_CHECK_EQ(driver->fields.count, nitems(field_names));
}
ATF_TC(snl_parse_empty_vf_status);
ATF_TC_HEAD(snl_parse_empty_vf_status, tc)
{
atf_tc_set_md_var(tc, "descr",
"Tests snl(3) parsing an empty VF status snapshot");
atf_tc_set_md_var(tc, "require.kmods", "netlink");
}
ATF_TC_BODY(snl_parse_empty_vf_status, tc)
{
struct snl_parsed_link link = {};
struct snl_state ss;
struct snl_writer nw;
struct nlmsghdr *hdr;
int fbsd_off, 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, 0));
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_u8(&nw, IFLAF_VFS_PF_LINK_STATE,
IFLAF_VF_LINK_DOWN));
snl_end_attr_nested(&nw, status_off);
snl_end_attr_nested(&nw, fbsd_off);
hdr = snl_finalize_msg(&nw);
ATF_REQUIRE(hdr != NULL);
ATF_REQUIRE(snl_parse_nlmsg(&ss, hdr, &snl_rtm_link_parser, &link));
ATF_CHECK_EQ(link.ifla_num_vf, 0);
ATF_CHECK(link.iflaf_vf_status.present);
ATF_CHECK_EQ(link.iflaf_vf_status.pf_link_state,
IFLAF_VF_LINK_DOWN);
ATF_CHECK_EQ(link.iflaf_vf_status.vfs.count, 0);
}
ATF_TC(snl_parse_vf_status_error);
ATF_TC_HEAD(snl_parse_vf_status_error, tc)
{
atf_tc_set_md_var(tc, "descr",
"Tests snl(3) parsing a VF status provider error");
atf_tc_set_md_var(tc, "require.kmods", "netlink");
}
ATF_TC_BODY(snl_parse_vf_status_error, tc)
{
struct snl_parsed_link link = {};
struct snl_state ss;
struct snl_writer nw;
struct nlmsghdr *hdr;
int fbsd_off, 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);
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_ERROR, EMSGSIZE));
snl_end_attr_nested(&nw, status_off);
snl_end_attr_nested(&nw, fbsd_off);
hdr = snl_finalize_msg(&nw);
ATF_REQUIRE(hdr != NULL);
ATF_REQUIRE(snl_parse_nlmsg(&ss, hdr, &snl_rtm_link_parser, &link));
ATF_CHECK(link.iflaf_vf_status.present);
ATF_CHECK_EQ(link.iflaf_vf_status.error, EMSGSIZE);
ATF_CHECK_EQ(link.iflaf_vf_status.vfs.count, 0);
}
ATF_TC(snl_verify_route_parsers);
ATF_TC_HEAD(snl_verify_route_parsers, tc)
{
@@ -296,6 +705,10 @@ ATF_TP_ADD_TCS(tp)
ATF_TP_ADD_TC(tp, snl_verify_core_parsers);
ATF_TP_ADD_TC(tp, snl_parse_bitset_array);
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_large_vf_status);
ATF_TP_ADD_TC(tp, snl_parse_empty_vf_status);
ATF_TP_ADD_TC(tp, snl_parse_vf_status_error);
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);