<div dir="ltr">Hi Bruce, Morten,<div><br></div><div>1 follow up question about vlan_filter which is tangentially related to the QinQ/VLAN stripping conversation.</div><div><br></div><div>Dean has a testcase in the QinQ testsuite which is supposed to check the vlan_filter capability correctness when processing a packet with Tag 88a8 + Tag 8100. So, the test steps are like:</div><div><br></div><div>1. Create 2 packets (for context, the Dot1AD just means 88a8)</div><div>        packets = [<br>            Ether(dst="00:11:22:33:44:55", src="66:77:88:99:aa:bb")<br>            / Dot1AD(vlan=100)<br>            / Dot1Q(vlan=200)<br>            / IP(dst="192.0.2.1", src="198.51.100.1")<br>            / UDP(dport=1234, sport=5678)<br>            / Raw(b"xxxxx"),<br>            Ether(dst="00:11:22:33:44:55", src="66:77:88:99:aa:bb")<br>            / Dot1AD(vlan=101)<br>            / Dot1Q(vlan=200)<br>            / IP(dst="192.0.2.1", src="198.51.100.1")<br>            / UDP(dport=1234, sport=5678)<br>            / Raw(b"xxxxx"),<br>        ]<br><br>2. enable vlan filter on testpmd port 0, set port 0 rx_vlan to 100</div><div>3. Transmit the 2 packets above from TG to testpmd on the SUT. The first packet should be accepted, the second should be dropped. </div><div><br></div><div>What he is seeing is that (at least with an ice e810 card) both packets are dropped. So, clearly the vlan_filter capability does not apply to 88a8 + Tag 8100 packets in DPDK (at least right now). We were curious if DPDK was skipping past the Dot1AD and reading the Dot1Q for the vlan_filter operation, but it is not, as even when rx_vlan on testpmd is set to 200, both packets are dropped.</div><div><br></div><div>The vlan_filter capability description from the docs (pasted below) is not very verbose, so we can't tell whether it (should) apply to 88a8 packets or not. In any case, I think we will remove this testcase from the testsuite at least for this DPDK release (that will mean the testsuite just covers QinQ_strip, vlan_strip, and how DPDK reads the tags) which are the parts we are confident we are validating "correctly." But, I wanted to mention this so the question of what is the correct vlan_filter action for these packets is not lost. cheers.</div><div><br>---------<br><br></div><div>2.27. VLAN filter<br>Supports filtering of a VLAN Tag identifier.<br><br>[uses] rte_eth_rxconf,rte_eth_rxmode: offloads:DEV_RX_OFFLOAD_VLAN_FILTER.<br>[implements] eth_dev_ops: vlan_filter_set.<br>[related] API: rte_eth_dev_vlan_filter().</div><div><br></div><div><br></div><div><br></div><div><br></div></div><br><div class="gmail_quote gmail_quote_container"><div dir="ltr" class="gmail_attr">On Fri, Aug 8, 2025 at 12:15 PM Bruce Richardson <<a href="mailto:bruce.richardson@intel.com">bruce.richardson@intel.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">The behaviour of VLAN tag stripping Rx offloads is unclear in DPDK, and<br>
not very well documented. Even the documentation that does exist appears<br>
contradictory.<br>
<br>
For example, the doxygen docs for the mbuf flag<br>
RTE_MBUF_F_RX_QINQ_STRIPPED says:<br>
<br>
  "If RTE_MBUF_F_RX_QINQ_STRIPPED is set and RTE_MBUF_F_RX_VLAN_STRIPPED<br>
  is unset, only the outer VLAN is removed from packet data,..."<br>
<br>
but the docs for RTE_MBUF_F_RX_QINQ says:<br>
<br>
  "If the flag RTE_MBUF_F_RX_QINQ_STRIPPED is also present, both VLANs<br>
  headers have been stripped from mbuf data, ..."<br>
<br>
Without a good definition of what the correct behaviour is, it's not<br>
possible to assess and ensure conformance across drivers. Update the<br>
documentation for NIC features, ethdev and mbuf library to all report<br>
the same information.<br>
<br>
- VLAN strip implies stripping a single/outer tag of type 0x8100<br>
- QinQ strip implies stripping a single/outer tag of type 0x88a8, which<br>
  may be followed by a tag of 0x8100. That inner tag is stripped<br>
  if-and-only-if VLAN stripping is enabled too.<br>
<br>
Signed-off-by: Bruce Richardson <<a href="mailto:bruce.richardson@intel.com" target="_blank">bruce.richardson@intel.com</a>><br>
---<br>
V2: updated and reworded following discussion on RFC patch<br>
---<br>
 doc/guides/nics/features.rst | 31 +++++++++++++++++++++++++++++++<br>
 lib/ethdev/rte_ethdev.h      | 28 ++++++++++++++++++++++++++++<br>
 lib/mbuf/rte_mbuf_core.h     | 32 ++++++++++++++++++--------------<br>
 3 files changed, 77 insertions(+), 14 deletions(-)<br>
<br>
diff --git a/doc/guides/nics/features.rst b/doc/guides/nics/features.rst<br>
index a075c057ec..df0b7edee7 100644<br>
--- a/doc/guides/nics/features.rst<br>
+++ b/doc/guides/nics/features.rst<br>
@@ -483,6 +483,11 @@ VLAN offload<br>
 ------------<br>
<br>
 Supports VLAN offload to hardware.<br>
+This includes both VLAN stripping on Rx and VLAN insertion on Tx.<br>
+<br>
+On Rx, VLAN strip removes one VLAN tag (default ethertype=0x8100) if present.<br>
+If multiple VLAN tags are present, it strips the outer tag only.<br>
+The stripped VLAN TCI is saved in mbuf->vlan_tci.<br>
<br>
 * **[uses]       rte_eth_rxconf,rte_eth_rxmode**: ``offloads:RTE_ETH_RX_OFFLOAD_VLAN_STRIP,RTE_ETH_RX_OFFLOAD_VLAN_FILTER,RTE_ETH_RX_OFFLOAD_VLAN_EXTEND``.<br>
 * **[uses]       rte_eth_txconf,rte_eth_txmode**: ``offloads:RTE_ETH_TX_OFFLOAD_VLAN_INSERT``.<br>
@@ -501,6 +506,32 @@ QinQ offload<br>
 ------------<br>
<br>
 Supports QinQ (queue in queue) offload.<br>
+This includes both QinQ stripping on Rx and QinQ insertion on Tx.<br>
+<br>
+On Rx, QinQ strip removes the outer QinQ tag (default ethertype=0x88a8) if present.<br>
+If multiple QinQ tags are present it only strips the outer tag.<br>
+The tag stripped is saved in mbuf->vlan_tci_outer.<br>
+<br>
+If an outer QinQ tag is stripped and if VLAN stripping is also enabled,<br>
+any inner VLAN tag (default ethertype=0x8100) is also stripped.<br>
+That tag is stored in mbuf->vlan_tci.<br>
+<br>
+Summary of VLAN and QinQ stripping behavior for some possible input traffic options:<br>
+<br>
++----------------------+-----------------------+------------------------------+------------------------------+<br>
+| Input Traffic        | VLAN-strip on         | QinQ strip on                | Both on                      |<br>
++======================+=======================+==============================+==============================+<br>
+| Single Tag 0x8100    | Tag in vlan_tci       |                              | Tag in vlan_tci              |<br>
++----------------------+-----------------------+------------------------------+------------------------------+<br>
+| Single Tag 0x88a8    |                       | Tag in vlan_tci_outer        | Tag in vlan_tci_outer        |<br>
++----------------------+-----------------------+------------------------------+------------------------------+<br>
+| Tag 88a8 + Tag 8100  |                       | Outer tag in vlan_tci_outer  | Outer tag in vlan_tci_outer  |<br>
+|                      |                       |                              |                              |<br>
+|                      |                       |                              | Inner tag in vlan_tci        |<br>
++----------------------+-----------------------+------------------------------+------------------------------+<br>
+| Double Tag 0x8100    | Outer tag in vlan_tci |                              | Outer tag in vlan_tci        |<br>
++----------------------+-----------------------+------------------------------+------------------------------+<br>
+<br>
<br>
 * **[uses]     rte_eth_rxconf,rte_eth_rxmode**: ``offloads:RTE_ETH_RX_OFFLOAD_QINQ_STRIP``.<br>
 * **[uses]     rte_eth_txconf,rte_eth_txmode**: ``offloads:RTE_ETH_TX_OFFLOAD_QINQ_INSERT``.<br>
diff --git a/lib/ethdev/rte_ethdev.h b/lib/ethdev/rte_ethdev.h<br>
index f9fb6ae549..5e226ff1c9 100644<br>
--- a/lib/ethdev/rte_ethdev.h<br>
+++ b/lib/ethdev/rte_ethdev.h<br>
@@ -1552,11 +1552,39 @@ struct rte_eth_conf {<br>
 /**<br>
  * Rx offload capabilities of a device.<br>
  */<br>
+/**<br>
+ * VLAN strip offload.<br>
+ *<br>
+ * When enabled, strips one VLAN tag (ethtype=0x8100) if available.<br>
+ * If multiple VLAN tags are present, it strips the outer tag.<br>
+ * The stripped VLAN TCI is saved in mbuf->vlan_tci<br>
+ * and @ref RTE_MBUF_F_RX_VLAN_STRIPPED flag is set.<br>
+ *<br>
+ * Note: if @ref RTE_ETH_RX_OFFLOAD_QINQ_STRIP is also enabled,<br>
+ * the stripped tag may be an inner tag present after a QinQ tag (ethtype=0x88a8).<br>
+ * In this case, both @ref RTE_MBUF_F_RX_QINQ_STRIPPED and<br>
+ * @ref RTE_MBUF_F_RX_VLAN_STRIPPED flags will be set in the received mbuf.<br>
+ */<br>
 #define RTE_ETH_RX_OFFLOAD_VLAN_STRIP       RTE_BIT64(0)<br>
 #define RTE_ETH_RX_OFFLOAD_IPV4_CKSUM       RTE_BIT64(1)<br>
 #define RTE_ETH_RX_OFFLOAD_UDP_CKSUM        RTE_BIT64(2)<br>
 #define RTE_ETH_RX_OFFLOAD_TCP_CKSUM        RTE_BIT64(3)<br>
 #define RTE_ETH_RX_OFFLOAD_TCP_LRO          RTE_BIT64(4)<br>
+/**<br>
+ * QinQ strip offload.<br>
+ *<br>
+ * When enabled, strips outer QinQ tag (ethtype=0x88a8) if present.<br>
+ * The stripped QinQ tag is saved in mbuf field vlan_tci_outer<br>
+ * and @ref RTE_MBUF_F_RX_QINQ_STRIPPED flag is set.<br>
+ *<br>
+ * If a QinQ tag is stripped and VLAN stripping is also enabled,<br>
+ * any inner VLAN tag (ethtype=0x8100) is also stripped and<br>
+ * stored in mbuf field vlan_tci, with the flag @ref RTE_MBUF_F_RX_VLAN_STRIPPED<br>
+ * being set.<br>
+ *<br>
+ * @see RTE_ETH_RX_OFFLOAD_VLAN_STRIP, @see RTE_MBUF_F_RX_QINQ_STRIPPED,<br>
+ * @see RTE_MBUF_F_RX_VLAN_STRIPPED<br>
+ */<br>
 #define RTE_ETH_RX_OFFLOAD_QINQ_STRIP       RTE_BIT64(5)<br>
 #define RTE_ETH_RX_OFFLOAD_OUTER_IPV4_CKSUM RTE_BIT64(6)<br>
 #define RTE_ETH_RX_OFFLOAD_MACSEC_STRIP     RTE_BIT64(7)<br>
diff --git a/lib/mbuf/rte_mbuf_core.h b/lib/mbuf/rte_mbuf_core.h<br>
index a0df265b5d..824f11d438 100644<br>
--- a/lib/mbuf/rte_mbuf_core.h<br>
+++ b/lib/mbuf/rte_mbuf_core.h<br>
@@ -44,7 +44,7 @@ extern "C" {<br>
 /**<br>
  * The RX packet is a 802.1q VLAN packet, and the tci has been<br>
  * saved in mbuf->vlan_tci.<br>
- * If the flag RTE_MBUF_F_RX_VLAN_STRIPPED is also present, the VLAN<br>
+ * If the flag @ref RTE_MBUF_F_RX_VLAN_STRIPPED is also present, the VLAN<br>
  * header has been stripped from mbuf data, else it is still<br>
  * present.<br>
  */<br>
@@ -66,7 +66,11 @@ extern "C" {<br>
  * A vlan has been stripped by the hardware and its tci is saved in<br>
  * mbuf->vlan_tci. This can only happen if vlan stripping is enabled<br>
  * in the RX configuration of the PMD.<br>
- * When RTE_MBUF_F_RX_VLAN_STRIPPED is set, RTE_MBUF_F_RX_VLAN must also be set.<br>
+ * When RTE_MBUF_F_RX_VLAN_STRIPPED is set, @ref RTE_MBUF_F_RX_VLAN must also be set.<br>
+ *<br>
+ * Note: When @ref RTE_MBUF_F_RX_QINQ_STRIPPED is also set,<br>
+ * the stripped tag is an inner tag, rather than a single or outer tag.<br>
+ * @see RTE_MBUF_F_RX_QINQ_STRIPPED for more information.<br>
  */<br>
 #define RTE_MBUF_F_RX_VLAN_STRIPPED (1ULL << 6)<br>
<br>
@@ -113,19 +117,19 @@ extern "C" {<br>
 #define RTE_MBUF_F_RX_FDIR_FLX      (1ULL << 14)<br>
<br>
 /**<br>
- * The outer VLAN has been stripped by the hardware and its TCI is<br>
+ * The outer QinQ Tag (ethtype=0x88a8) has been stripped by the hardware and its TCI is<br>
  * saved in mbuf->vlan_tci_outer.<br>
- * This can only happen if VLAN stripping is enabled in the Rx<br>
+ * This can only happen if QinQ stripping is enabled in the Rx<br>
  * configuration of the PMD.<br>
- * When RTE_MBUF_F_RX_QINQ_STRIPPED is set, the flags RTE_MBUF_F_RX_VLAN<br>
- * and RTE_MBUF_F_RX_QINQ must also be set.<br>
+ * When RTE_MBUF_F_RX_QINQ_STRIPPED is set, the flag RTE_MBUF_F_RX_QINQ must also be set.<br>
  *<br>
  * - If both RTE_MBUF_F_RX_QINQ_STRIPPED and RTE_MBUF_F_RX_VLAN_STRIPPED are<br>
  *   set, the 2 VLANs have been stripped by the hardware and their TCIs are<br>
  *   saved in mbuf->vlan_tci (inner) and mbuf->vlan_tci_outer (outer).<br>
  * - If RTE_MBUF_F_RX_QINQ_STRIPPED is set and RTE_MBUF_F_RX_VLAN_STRIPPED<br>
- *   is unset, only the outer VLAN is removed from packet data, but both tci<br>
- *   are saved in mbuf->vlan_tci (inner) and mbuf->vlan_tci_outer (outer).<br>
+ *   is unset, only the outer VLAN is removed from packet data and<br>
+ *   stored in mbuf->vlan_tci_outer.<br>
+ *   The contents of mbuf->vlan_tci are undefined in this case.<br>
  */<br>
 #define RTE_MBUF_F_RX_QINQ_STRIPPED (1ULL << 15)<br>
<br>
@@ -149,12 +153,12 @@ extern "C" {<br>
 #define RTE_MBUF_F_RX_SEC_OFFLOAD_FAILED       (1ULL << 19)<br>
<br>
 /**<br>
- * The RX packet is a double VLAN, and the outer tci has been<br>
- * saved in mbuf->vlan_tci_outer. If this flag is set, RTE_MBUF_F_RX_VLAN<br>
- * must also be set and the inner tci is saved in mbuf->vlan_tci.<br>
- * If the flag RTE_MBUF_F_RX_QINQ_STRIPPED is also present, both VLANs<br>
- * headers have been stripped from mbuf data, else they are still<br>
- * present.<br>
+ * The RX packet is a QinQ packet, and the outer tci has been<br>
+ * saved in mbuf->vlan_tci_outer.<br>
+ * If the flag @ref RTE_MBUF_F_RX_QINQ_STRIPPED is also present,<br>
+ * the QinQ tag has been stripped from mbuf data.<br>
+ *<br>
+ * @see RTE_MBUF_F_RX_QINQ_STRIPPED, @see RTE_MBUF_F_RX_VLAN, @see RTE_MBUF_F_RX_VLAN_STRIPPED<br>
  */<br>
 #define RTE_MBUF_F_RX_QINQ          (1ULL << 20)<br>
<br>
--<br>
2.48.1<br>
<br>
</blockquote></div>