git: 429fbc7c35a6 - main - rtnetlink: Allow VF priority reporting in trunk mode

From: Kevin Bowling <kbowling_at_FreeBSD.org>
Date: Thu, 10 Sep 2026 07:17:10 UTC
The branch main has been updated by kbowling:

URL: https://cgit.FreeBSD.org/src/commit/?id=429fbc7c35a6d860462db3109608b7f800f0e50b

commit 429fbc7c35a6d860462db3109608b7f800f0e50b
Author:     Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-09-10 06:54:50 +0000
Commit:     Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-09-10 07:15:27 +0000

    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
---
 lib/libifconfig/libifconfig.h |  6 +++--
 sbin/ifconfig/ifconfig.8      |  8 ++++--
 sbin/ifconfig/ifvfstatus.c    |  3 ++-
 share/man/man4/rtnetlink.4    | 22 ++++++++++-------
 sys/net/if_vf_status.h        | 13 ++++++----
 sys/netlink/route/iface.c     | 16 +++++++++---
 tests/sys/netlink/test_snl.c  | 57 +++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 102 insertions(+), 23 deletions(-)

diff --git a/lib/libifconfig/libifconfig.h b/lib/libifconfig/libifconfig.h
index dd2fd16dba92..bf9039f80c8d 100644
--- a/lib/libifconfig/libifconfig.h
+++ b/lib/libifconfig/libifconfig.h
@@ -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 {
diff --git a/sbin/ifconfig/ifconfig.8 b/sbin/ifconfig/ifconfig.8
index c1d560fb4d12..5474655a4ab1 100644
--- a/sbin/ifconfig/ifconfig.8
+++ b/sbin/ifconfig/ifconfig.8
@@ -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
diff --git a/sbin/ifconfig/ifvfstatus.c b/sbin/ifconfig/ifvfstatus.c
index 82cf0c5400d7..70cac1d1e8cc 100644
--- a/sbin/ifconfig/ifvfstatus.c
+++ b/sbin/ifconfig/ifvfstatus.c
@@ -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 &&
diff --git a/share/man/man4/rtnetlink.4 b/share/man/man4/rtnetlink.4
index e217b1190b4d..eacb9c5db54a 100644
--- a/share/man/man4/rtnetlink.4
+++ b/share/man/man4/rtnetlink.4
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd 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
diff --git a/sys/net/if_vf_status.h b/sys/net/if_vf_status.h
index 57c625ff530c..4c24a4a8b615 100644
--- a/sys/net/if_vf_status.h
+++ b/sys/net/if_vf_status.h
@@ -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
diff --git a/sys/netlink/route/iface.c b/sys/netlink/route/iface.c
index 328314d25ebb..59101ee60e77 100644
--- a/sys/netlink/route/iface.c
+++ b/sys/netlink/route/iface.c
@@ -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)
diff --git a/tests/sys/netlink/test_snl.c b/tests/sys/netlink/test_snl.c
index a1c5b8455b3b..fc115116261d 100644
--- a/tests/sys/netlink/test_snl.c
+++ b/tests/sys/netlink/test_snl.c
@@ -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);