rtnetlink: Allow VF priority reporting in trunk mode

PF-administered priority need not impose an access VLAN.  Permit the
existing VLAN PCP attribute in either access or trunk mode, while
keeping VLAN identifier and protocol access only.

Display trunk PCP in ifconfig and document the broader meaning in the
kernel snapshot, libifconfig, and rtnetlink contracts.  This changes no
attribute numbers, wire encoding, or structure layout.

Reported by:	kib
Sponsored by:	BBOX.io
This commit is contained in:
Kevin Bowling committed 2026-09-10 00:15:27 -07:00
1 parent b2a270da9f
commit 429fbc7c35
7 files changed
+102 -23

No files matched your search

+4 -2
View File
@@ -214,8 +214,10 @@ int ifconfig_get_ifstatus(ifconfig_handle_t *h, const char *name,
* the returned status object and is released by ifconfig_free_vf_status().
* VF records are returned through a pointer vector so append-only growth of
* struct ifconfig_vf_info does not change the array stride seen by existing
* consumers. VLAN PCP and protocol describe the PF-administered access VLAN;
* they do not describe trunk-filter entries.
* consumers. VLAN identifier and protocol describe the PF-administered
* access VLAN. VLAN PCP describes PF-administered priority in access or
* trunk mode, including priority-only tagging with VID 0. These fields do
* not describe individual trunk filters or VF-selected priorities.
*/
enum ifconfig_vf_vlan_mode {
+6 -2
View File
@@ -28,7 +28,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd September 6, 2026
.Dd September 10, 2026
.Dt IFCONFIG 8
.Os
.Sh NAME
@@ -359,8 +359,12 @@ A zero minimum is displayed as
.It Cm vlan
An access VLAN is imposed by the PF; trunk mode means that no access VLAN is
imposed and does not promise unlimited filter capacity.
Access mode may also report the priority code point and VLAN protocol.
Access mode may also report the VLAN protocol.
The VLAN identifier can be zero when the PF imposes priority tagging only.
The PF-administered priority code point may be reported in access or trunk
mode.
An omitted priority is distinct from
.Cm pcp=0 .
The filter count includes explicit filters recorded by the PF and excludes
implicit untagged and priority-tag membership.
.It Cm policy
+2 -1
View File
@@ -241,7 +241,8 @@ vf_status(if_ctx *ctx)
if (vf->vlan_mode == IFCONFIG_VF_VLAN_ACCESS &&
(vf->fields & (1ULL << IFLAF_VF_VLAN)) != 0)
printf(" vid=%u", vf->vlan);
if (vf->vlan_mode == IFCONFIG_VF_VLAN_ACCESS &&
if ((vf->vlan_mode == IFCONFIG_VF_VLAN_ACCESS ||
vf->vlan_mode == IFCONFIG_VF_VLAN_TRUNK) &&
(vf->fields & (1ULL << IFLAF_VF_VLAN_PCP)) != 0)
printf(" pcp=%u", vf->vlan_pcp);
if (vf->vlan_mode == IFCONFIG_VF_VLAN_ACCESS &&
+13 -9
View File
@@ -22,7 +22,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd September 6, 2026
.Dd September 9, 2026
.Dt RTNETLINK 4
.Os
.Sh NAME
@@ -401,7 +401,7 @@ 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_PCP (uint8_t) PF-administered 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
@@ -460,17 +460,21 @@ 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 ,
.Dv IFLAF_VF_VLAN
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.
attributes describe its VLAN identifier and host-order tag EtherType,
respectively, and are present only in access mode.
A present zero VLAN identifier can represent a priority-only access tag.
Trunk mode means that no access VLAN is imposed and does not promise unlimited
filter capacity.
.Dv IFLAF_VF_VLAN_PCP
describes the PF-administered IEEE 802.1p priority code point (0 through 7).
It may be present in access or trunk mode, including priority-only tagging
with VID 0.
A present zero priority is distinct from an omitted priority.
These attributes do not describe individual trunk filters or priorities
selected by the VF.
.Dv IFLAF_VF_VLAN_COUNT
counts explicit filters recorded by the PF.
It excludes untagged or priority-tag membership installed implicitly by the
+8 -5
View File
@@ -19,11 +19,14 @@
* 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.
* its own ABI. VLAN identifier and protocol describe the PF-administered
* access VLAN. VLAN PCP describes PF-administered priority in access or
* trunk mode, including priority-only tagging with VID 0. These fields do
* not describe individual trunk filters or VF-selected priorities.
* 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
+12 -4
View File
@@ -491,8 +491,7 @@ validate_vf_extension(const struct if_vf_extension *extension,
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 access_fields = IFVF_F_VLAN | 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 |
@@ -514,6 +513,11 @@ validate_vf_entry(const struct if_vf_info *vf, size_t *encoded_size)
((vf->fields & IFVF_F_VLAN_MODE) == 0 ||
vf->vlan_mode != IFVF_VLAN_ACCESS))
return (EINVAL);
if ((vf->fields & IFVF_F_VLAN_PCP) != 0 &&
((vf->fields & IFVF_F_VLAN_MODE) == 0 ||
(vf->vlan_mode != IFVF_VLAN_ACCESS &&
vf->vlan_mode != IFVF_VLAN_TRUNK)))
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)
@@ -695,8 +699,7 @@ dump_vf_extensions(struct nl_writer *nw, const struct if_vf_info *vf)
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 access_fields = IFVF_F_VLAN | IFVF_F_VLAN_PROTO;
const uint64_t rate_fields = IFVF_F_MIN_TX_RATE |
IFVF_F_MAX_TX_RATE;
int error, off;
@@ -712,6 +715,11 @@ dump_vf_entry(struct nl_writer *nw, const struct if_vf_info *vf)
((vf->fields & IFVF_F_VLAN_MODE) == 0 ||
vf->vlan_mode != IFVF_VLAN_ACCESS))
return (EINVAL);
if ((vf->fields & IFVF_F_VLAN_PCP) != 0 &&
((vf->fields & IFVF_F_VLAN_MODE) == 0 ||
(vf->vlan_mode != IFVF_VLAN_ACCESS &&
vf->vlan_mode != IFVF_VLAN_TRUNK)))
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)
+57
View File
@@ -297,6 +297,62 @@ ATF_TC_BODY(snl_parse_vf_status, tc)
ATF_CHECK_EQ(vf->index, 15);
}
ATF_TC(snl_parse_vf_trunk_pcp);
ATF_TC_HEAD(snl_parse_vf_trunk_pcp, tc)
{
atf_tc_set_md_var(tc, "descr",
"Tests snl(3) parsing trunk PCP values and omitted priority");
atf_tc_set_md_var(tc, "require.kmods", "netlink");
}
ATF_TC_BODY(snl_parse_vf_trunk_pcp, tc)
{
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 entry_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));
for (i = 0; i < 9; 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_u8(&nw, IFLAF_VF_VLAN_MODE,
IFLAF_VF_VLAN_TRUNK));
/* Cover PCP 0..7 and absence, without an imposed VLAN. */
if (i < 8)
ATF_REQUIRE(snl_add_msg_attr_u8(&nw, IFLAF_VF_VLAN_PCP,
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);
ATF_REQUIRE_EQ(link.iflaf_vf_status.vfs.count, 9);
for (i = 0; i < 9; i++) {
vf = link.iflaf_vf_status.vfs.items[i];
ATF_CHECK_EQ(vf->index, i);
ATF_CHECK((vf->attrs & (1ULL << IFLAF_VF_VLAN_MODE)) != 0);
ATF_CHECK_EQ(vf->vlan_mode, IFLAF_VF_VLAN_TRUNK);
ATF_CHECK((vf->attrs & ((1ULL << IFLAF_VF_VLAN) |
(1ULL << IFLAF_VF_VLAN_PROTO))) == 0);
ATF_CHECK_EQ((vf->attrs & (1ULL << IFLAF_VF_VLAN_PCP)) != 0,
i < 8);
if (i < 8)
ATF_CHECK_EQ(vf->vlan_pcp, i);
}
snl_free(&ss);
}
ATF_TC(snl_parse_large_vf_status);
ATF_TC_HEAD(snl_parse_large_vf_status, tc)
{
@@ -706,6 +762,7 @@ ATF_TP_ADD_TCS(tp)
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_vf_trunk_pcp);
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);