* [PATCH dpdk] net: fix L2 ptype assignment in VLAN loop
@ 2026-04-22 10:28 Robin Jarry
2026-04-22 10:35 ` Robin Jarry
` (3 more replies)
0 siblings, 4 replies; 19+ messages in thread
From: Robin Jarry @ 2026-04-22 10:28 UTC (permalink / raw)
To: dev, Gregory Etelson; +Cc: stable
Since commit 1f250674085a ("net: fix packet type for stacked VLAN"),
rte_net_get_ptype() uses |= to set the L2 ptype inside the VLAN
parsing loop. Since pkt_type is already initialized with
RTE_PTYPE_L2_ETHER (0x1), or-ing it with RTE_PTYPE_L2_ETHER_VLAN
(0x6) results in RTE_PTYPE_L2_ETHER_QINQ (0x7). This causes single
VLAN frames to be misidentified as QinQ.
This was detected while testing DPDK 25.11.1 in grout. The net/tap
driver calls rte_net_get_ptype() in tap_verify_csum() to determine
the L2 header length. With the wrong ptype, l2_len is set to 22
(ether + QinQ = 14 + 8) instead of 18 (ether + VLAN = 14 + 4),
shifting the IP header pointer by 4 bytes. The checksum is then
computed on garbage data, causing valid packets to be dropped.
Use a simple assignment to replace the L2 ptype instead. Add VLAN
and QinQ test packets in cksum_autotest to prevent regressions.
Fixes: 1f250674085a ("net: fix packet type for stacked VLAN")
Cc: stable@dpdk.org
Signed-off-by: Robin Jarry <rjarry@redhat.com>
---
app/test/test_cksum.c | 29 +++++++++++++++++++++++++++++
lib/net/rte_net.c | 2 +-
2 files changed, 30 insertions(+), 1 deletion(-)
diff --git a/app/test/test_cksum.c b/app/test/test_cksum.c
index ea443382a128..3cc42eccedd9 100644
--- a/app/test/test_cksum.c
+++ b/app/test/test_cksum.c
@@ -75,6 +75,27 @@ static const char test_cksum_ipv6_udp[] = {
0x00, 0x35, 0x00, 0x09, 0x87, 0x70, 0x78,
};
+/* generated in scapy with Ether()/Dot1Q(vlan=42)/IP()/UDP()/Raw('x') */
+static const char test_cksum_vlan_ipv4_udp[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x2a,
+ 0x08, 0x00, 0x45, 0x00, 0x00, 0x1d, 0x00, 0x01,
+ 0x00, 0x00, 0x40, 0x11, 0x7c, 0xcd, 0x7f, 0x00,
+ 0x00, 0x01, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x35,
+ 0x00, 0x35, 0x00, 0x09, 0x89, 0x6f, 0x78,
+};
+
+/* generated in scapy with Ether()/Dot1AD(vlan=42)/Dot1Q(vlan=43)/IP()/UDP()/Raw('x') */
+static const char test_cksum_qinq_ipv4_udp[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x88, 0xa8, 0x00, 0x2a,
+ 0x81, 0x00, 0x00, 0x2b, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x1d, 0x00, 0x01, 0x00, 0x00, 0x40, 0x11,
+ 0x7c, 0xcd, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00,
+ 0x00, 0x01, 0x00, 0x35, 0x00, 0x35, 0x00, 0x09,
+ 0x89, 0x6f, 0x78,
+};
+
/* generated in scapy with Ether()/IP(options='\x00')/UDP()/Raw('x')) */
static const char test_cksum_ipv4_opts_udp[] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
@@ -252,6 +273,14 @@ test_cksum(void)
sizeof(test_cksum_ipv6_udp)) < 0)
GOTO_FAIL("checksum error on ipv6_udp");
+ if (test_l4_cksum(pktmbuf_pool, test_cksum_vlan_ipv4_udp,
+ sizeof(test_cksum_vlan_ipv4_udp)) < 0)
+ GOTO_FAIL("checksum error on vlan_ipv4_udp");
+
+ if (test_l4_cksum(pktmbuf_pool, test_cksum_qinq_ipv4_udp,
+ sizeof(test_cksum_qinq_ipv4_udp)) < 0)
+ GOTO_FAIL("checksum error on qinq_ipv4_udp");
+
if (test_l4_cksum(pktmbuf_pool, test_cksum_ipv4_opts_udp,
sizeof(test_cksum_ipv4_opts_udp)) < 0)
GOTO_FAIL("checksum error on ipv4_opts_udp");
diff --git a/lib/net/rte_net.c b/lib/net/rte_net.c
index 458b4814a9c9..ea5ba7019089 100644
--- a/lib/net/rte_net.c
+++ b/lib/net/rte_net.c
@@ -359,7 +359,7 @@ uint32_t rte_net_get_ptype(const struct rte_mbuf *m,
if (++vlan_depth > RTE_NET_VLAN_MAX_DEPTH)
return 0;
- pkt_type |=
+ pkt_type =
proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN) ?
RTE_PTYPE_L2_ETHER_VLAN :
RTE_PTYPE_L2_ETHER_QINQ;
--
2.53.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH dpdk] net: fix L2 ptype assignment in VLAN loop
2026-04-22 10:28 [PATCH dpdk] net: fix L2 ptype assignment in VLAN loop Robin Jarry
@ 2026-04-22 10:35 ` Robin Jarry
2026-04-22 10:38 ` [PATCH dpdk v2] " Robin Jarry
` (2 subsequent siblings)
3 siblings, 0 replies; 19+ messages in thread
From: Robin Jarry @ 2026-04-22 10:35 UTC (permalink / raw)
To: dev, Gregory Etelson; +Cc: stable
Robin Jarry, Apr 22, 2026 at 12:28:
> Since commit 1f250674085a ("net: fix packet type for stacked VLAN"),
> rte_net_get_ptype() uses |= to set the L2 ptype inside the VLAN
> parsing loop. Since pkt_type is already initialized with
> RTE_PTYPE_L2_ETHER (0x1), or-ing it with RTE_PTYPE_L2_ETHER_VLAN
> (0x6) results in RTE_PTYPE_L2_ETHER_QINQ (0x7). This causes single
> VLAN frames to be misidentified as QinQ.
>
> This was detected while testing DPDK 25.11.1 in grout. The net/tap
> driver calls rte_net_get_ptype() in tap_verify_csum() to determine
> the L2 header length. With the wrong ptype, l2_len is set to 22
> (ether + QinQ = 14 + 8) instead of 18 (ether + VLAN = 14 + 4),
> shifting the IP header pointer by 4 bytes. The checksum is then
> computed on garbage data, causing valid packets to be dropped.
>
> Use a simple assignment to replace the L2 ptype instead. Add VLAN
> and QinQ test packets in cksum_autotest to prevent regressions.
>
> Fixes: 1f250674085a ("net: fix packet type for stacked VLAN")
> Cc: stable@dpdk.org
>
> Signed-off-by: Robin Jarry <rjarry@redhat.com>
> ---
> app/test/test_cksum.c | 29 +++++++++++++++++++++++++++++
> lib/net/rte_net.c | 2 +-
> 2 files changed, 30 insertions(+), 1 deletion(-)
>
> diff --git a/app/test/test_cksum.c b/app/test/test_cksum.c
> index ea443382a128..3cc42eccedd9 100644
> --- a/app/test/test_cksum.c
> +++ b/app/test/test_cksum.c
> @@ -75,6 +75,27 @@ static const char test_cksum_ipv6_udp[] = {
> 0x00, 0x35, 0x00, 0x09, 0x87, 0x70, 0x78,
> };
>
> +/* generated in scapy with Ether()/Dot1Q(vlan=42)/IP()/UDP()/Raw('x') */
> +static const char test_cksum_vlan_ipv4_udp[] = {
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x2a,
> + 0x08, 0x00, 0x45, 0x00, 0x00, 0x1d, 0x00, 0x01,
> + 0x00, 0x00, 0x40, 0x11, 0x7c, 0xcd, 0x7f, 0x00,
> + 0x00, 0x01, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x35,
> + 0x00, 0x35, 0x00, 0x09, 0x89, 0x6f, 0x78,
> +};
> +
> +/* generated in scapy with Ether()/Dot1AD(vlan=42)/Dot1Q(vlan=43)/IP()/UDP()/Raw('x') */
> +static const char test_cksum_qinq_ipv4_udp[] = {
> + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x88, 0xa8, 0x00, 0x2a,
> + 0x81, 0x00, 0x00, 0x2b, 0x08, 0x00, 0x45, 0x00,
> + 0x00, 0x1d, 0x00, 0x01, 0x00, 0x00, 0x40, 0x11,
> + 0x7c, 0xcd, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00,
> + 0x00, 0x01, 0x00, 0x35, 0x00, 0x35, 0x00, 0x09,
> + 0x89, 0x6f, 0x78,
> +};
> +
> /* generated in scapy with Ether()/IP(options='\x00')/UDP()/Raw('x')) */
> static const char test_cksum_ipv4_opts_udp[] = {
> 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
> @@ -252,6 +273,14 @@ test_cksum(void)
> sizeof(test_cksum_ipv6_udp)) < 0)
> GOTO_FAIL("checksum error on ipv6_udp");
>
> + if (test_l4_cksum(pktmbuf_pool, test_cksum_vlan_ipv4_udp,
> + sizeof(test_cksum_vlan_ipv4_udp)) < 0)
> + GOTO_FAIL("checksum error on vlan_ipv4_udp");
> +
> + if (test_l4_cksum(pktmbuf_pool, test_cksum_qinq_ipv4_udp,
> + sizeof(test_cksum_qinq_ipv4_udp)) < 0)
> + GOTO_FAIL("checksum error on qinq_ipv4_udp");
The tests are useless, they don't check that the L2 ptype is correct.
I'll send a v2 with proper ptype tests.
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH dpdk v2] net: fix L2 ptype assignment in VLAN loop
2026-04-22 10:28 [PATCH dpdk] net: fix L2 ptype assignment in VLAN loop Robin Jarry
2026-04-22 10:35 ` Robin Jarry
@ 2026-04-22 10:38 ` Robin Jarry
2026-04-22 13:16 ` Thomas Monjalon
2026-04-22 13:23 ` David Marchand
2026-04-22 13:32 ` [PATCH dpdk v3] net: fix VLAN packet type Robin Jarry
2026-04-23 11:24 ` [PATCH dpdk v4] " Robin Jarry
3 siblings, 2 replies; 19+ messages in thread
From: Robin Jarry @ 2026-04-22 10:38 UTC (permalink / raw)
To: dev, Gregory Etelson; +Cc: stable
Since commit 1f250674085a ("net: fix packet type for stacked VLAN"),
rte_net_get_ptype() uses |= to set the L2 ptype inside the VLAN
parsing loop. Since pkt_type is already initialized with
RTE_PTYPE_L2_ETHER (0x1), or-ing it with RTE_PTYPE_L2_ETHER_VLAN
(0x6) results in RTE_PTYPE_L2_ETHER_QINQ (0x7). This causes single
VLAN frames to be misidentified as QinQ.
This was detected while testing DPDK 25.11.1 in grout. The net/tap
driver calls rte_net_get_ptype() in tap_verify_csum() to determine
the L2 header length. With the wrong ptype, l2_len is set to 22
(ether + QinQ = 14 + 8) instead of 18 (ether + VLAN = 14 + 4),
shifting the IP header pointer by 4 bytes. The checksum is then
computed on garbage data, causing valid packets to be dropped.
Use a simple assignment to replace the L2 ptype instead. Add a ptype
unit test covering plain Ethernet, VLAN and QinQ L2 detection to
prevent regressions.
Fixes: 1f250674085a ("net: fix packet type for stacked VLAN")
Cc: stable@dpdk.org
Signed-off-by: Robin Jarry <rjarry@redhat.com>
---
Notes:
v2: added new ptype tests
app/test/meson.build | 1 +
app/test/test_net_ptype.c | 117 ++++++++++++++++++++++++++++++++++++++
lib/net/rte_net.c | 2 +-
3 files changed, 119 insertions(+), 1 deletion(-)
create mode 100644 app/test/test_net_ptype.c
diff --git a/app/test/meson.build b/app/test/meson.build
index 7d458f9c079a..9f4afb040a46 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -135,6 +135,7 @@ source_file_deps = {
'test_mp_secondary.c': ['hash'],
'test_net_ether.c': ['net'],
'test_net_ip6.c': ['net'],
+ 'test_net_ptype.c': ['net'],
'test_pcapng.c': ['net_null', 'net', 'ethdev', 'pcapng', 'bus_vdev'],
'test_pdcp.c': ['eventdev', 'pdcp', 'net', 'timer', 'security'],
'test_pdump.c': ['pdump'] + sample_packet_forward_deps,
diff --git a/app/test/test_net_ptype.c b/app/test/test_net_ptype.c
new file mode 100644
index 000000000000..9619c1d54a8e
--- /dev/null
+++ b/app/test/test_net_ptype.c
@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2026 Red Hat, Inc.
+ */
+
+#include <string.h>
+
+#include <rte_mbuf.h>
+#include <rte_net.h>
+
+#include <rte_test.h>
+#include "test.h"
+
+#define MEMPOOL_CACHE_SIZE 0
+#define MBUF_DATA_SIZE 256
+#define NB_MBUF 128
+
+/* Ether()/IP()/UDP()/Raw('x') */
+static const char pkt_ether_ipv4_udp[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x1d, 0x00, 0x01, 0x00, 0x00, 0x40, 0x11,
+ 0x7c, 0xcd, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00,
+ 0x00, 0x01, 0x00, 0x35, 0x00, 0x35, 0x00, 0x09,
+ 0x89, 0x6f, 0x78,
+};
+
+/* Ether()/Dot1Q(vlan=42)/IP()/UDP()/Raw('x') */
+static const char pkt_vlan_ipv4_udp[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x2a,
+ 0x08, 0x00, 0x45, 0x00, 0x00, 0x1d, 0x00, 0x01,
+ 0x00, 0x00, 0x40, 0x11, 0x7c, 0xcd, 0x7f, 0x00,
+ 0x00, 0x01, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x35,
+ 0x00, 0x35, 0x00, 0x09, 0x89, 0x6f, 0x78,
+};
+
+/* Ether()/Dot1AD(vlan=42)/Dot1Q(vlan=43)/IP()/UDP()/Raw('x') */
+static const char pkt_qinq_ipv4_udp[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x88, 0xa8, 0x00, 0x2a,
+ 0x81, 0x00, 0x00, 0x2b, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x1d, 0x00, 0x01, 0x00, 0x00, 0x40, 0x11,
+ 0x7c, 0xcd, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00,
+ 0x00, 0x01, 0x00, 0x35, 0x00, 0x35, 0x00, 0x09,
+ 0x89, 0x6f, 0x78,
+};
+
+static int
+test_get_ptype(struct rte_mempool *pool, const char *pktdata, size_t len,
+ uint32_t expected_l2, uint8_t expected_l2_len)
+{
+ struct rte_net_hdr_lens hdr_lens;
+ struct rte_mbuf *m;
+ uint32_t ptype;
+ uint32_t l2;
+ char *data;
+
+ m = rte_pktmbuf_alloc(pool);
+ RTE_TEST_ASSERT_NOT_NULL(m, "cannot allocate mbuf");
+
+ data = rte_pktmbuf_append(m, len);
+ if (data == NULL) {
+ rte_pktmbuf_free(m);
+ RTE_TEST_ASSERT_NOT_NULL(data, "cannot append data");
+ }
+
+ memcpy(data, pktdata, len);
+
+ ptype = rte_net_get_ptype(m, &hdr_lens, RTE_PTYPE_ALL_MASK);
+ l2 = ptype & RTE_PTYPE_L2_MASK;
+
+ rte_pktmbuf_free(m);
+
+ RTE_TEST_ASSERT_EQUAL(l2, expected_l2,
+ "unexpected L2 ptype: got 0x%x, expected 0x%x",
+ l2, expected_l2);
+ RTE_TEST_ASSERT_EQUAL(hdr_lens.l2_len, expected_l2_len,
+ "unexpected l2_len: got %u, expected %u",
+ hdr_lens.l2_len, expected_l2_len);
+
+ return 0;
+}
+
+static int
+test_net_ptype(void)
+{
+ struct rte_mempool *pool;
+
+ pool = rte_pktmbuf_pool_create("test_ptype_mbuf_pool",
+ NB_MBUF, MEMPOOL_CACHE_SIZE, 0, MBUF_DATA_SIZE,
+ SOCKET_ID_ANY);
+ RTE_TEST_ASSERT_NOT_NULL(pool, "cannot allocate mbuf pool");
+
+ if (test_get_ptype(pool, pkt_ether_ipv4_udp,
+ sizeof(pkt_ether_ipv4_udp),
+ RTE_PTYPE_L2_ETHER, 14))
+ goto fail;
+
+ if (test_get_ptype(pool, pkt_vlan_ipv4_udp,
+ sizeof(pkt_vlan_ipv4_udp),
+ RTE_PTYPE_L2_ETHER_VLAN, 18))
+ goto fail;
+
+ if (test_get_ptype(pool, pkt_qinq_ipv4_udp,
+ sizeof(pkt_qinq_ipv4_udp),
+ RTE_PTYPE_L2_ETHER_QINQ, 22))
+ goto fail;
+
+ rte_mempool_free(pool);
+ return 0;
+
+fail:
+ rte_mempool_free(pool);
+ return -1;
+}
+
+REGISTER_FAST_TEST(net_ptype_autotest, NOHUGE_OK, ASAN_OK, test_net_ptype);
diff --git a/lib/net/rte_net.c b/lib/net/rte_net.c
index 458b4814a9c9..ea5ba7019089 100644
--- a/lib/net/rte_net.c
+++ b/lib/net/rte_net.c
@@ -359,7 +359,7 @@ uint32_t rte_net_get_ptype(const struct rte_mbuf *m,
if (++vlan_depth > RTE_NET_VLAN_MAX_DEPTH)
return 0;
- pkt_type |=
+ pkt_type =
proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN) ?
RTE_PTYPE_L2_ETHER_VLAN :
RTE_PTYPE_L2_ETHER_QINQ;
--
2.53.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH dpdk v2] net: fix L2 ptype assignment in VLAN loop
2026-04-22 10:38 ` [PATCH dpdk v2] " Robin Jarry
@ 2026-04-22 13:16 ` Thomas Monjalon
2026-04-22 13:18 ` Robin Jarry
2026-04-22 13:23 ` David Marchand
1 sibling, 1 reply; 19+ messages in thread
From: Thomas Monjalon @ 2026-04-22 13:16 UTC (permalink / raw)
To: Robin Jarry; +Cc: dev, Gregory Etelson, stable
22/04/2026 12:38, Robin Jarry:
> Since commit 1f250674085a ("net: fix packet type for stacked VLAN"),
> rte_net_get_ptype() uses |= to set the L2 ptype inside the VLAN
> parsing loop. Since pkt_type is already initialized with
> RTE_PTYPE_L2_ETHER (0x1), or-ing it with RTE_PTYPE_L2_ETHER_VLAN
> (0x6) results in RTE_PTYPE_L2_ETHER_QINQ (0x7). This causes single
> VLAN frames to be misidentified as QinQ.
Thanks for finding this major regression.
Minor nit about the title (for whoever will merge it):
we don't really care the problem is in a loop.
I think this title is easier to read:
net: fix VLAN packet type
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH dpdk v2] net: fix L2 ptype assignment in VLAN loop
2026-04-22 13:16 ` Thomas Monjalon
@ 2026-04-22 13:18 ` Robin Jarry
0 siblings, 0 replies; 19+ messages in thread
From: Robin Jarry @ 2026-04-22 13:18 UTC (permalink / raw)
To: Thomas Monjalon; +Cc: dev, Gregory Etelson, stable
Thomas Monjalon, Apr 22, 2026 at 15:16:
> 22/04/2026 12:38, Robin Jarry:
>> Since commit 1f250674085a ("net: fix packet type for stacked VLAN"),
>> rte_net_get_ptype() uses |= to set the L2 ptype inside the VLAN
>> parsing loop. Since pkt_type is already initialized with
>> RTE_PTYPE_L2_ETHER (0x1), or-ing it with RTE_PTYPE_L2_ETHER_VLAN
>> (0x6) results in RTE_PTYPE_L2_ETHER_QINQ (0x7). This causes single
>> VLAN frames to be misidentified as QinQ.
>
> Thanks for finding this major regression.
>
> Minor nit about the title (for whoever will merge it):
> we don't really care the problem is in a loop.
> I think this title is easier to read:
> net: fix VLAN packet type
I will send a v3 with a different approach. Currently my fix breaks the
detection of QinQ if it is followed by a plain vlan header.
I'll adjust the title.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH dpdk v2] net: fix L2 ptype assignment in VLAN loop
2026-04-22 10:38 ` [PATCH dpdk v2] " Robin Jarry
2026-04-22 13:16 ` Thomas Monjalon
@ 2026-04-22 13:23 ` David Marchand
1 sibling, 0 replies; 19+ messages in thread
From: David Marchand @ 2026-04-22 13:23 UTC (permalink / raw)
To: Robin Jarry; +Cc: dev, Gregory Etelson, stable
On Wed, 22 Apr 2026 at 12:39, Robin Jarry <rjarry@redhat.com> wrote:
> +static int
> +test_get_ptype(struct rte_mempool *pool, const char *pktdata, size_t len,
> + uint32_t expected_l2, uint8_t expected_l2_len)
> +{
> + struct rte_net_hdr_lens hdr_lens;
> + struct rte_mbuf *m;
> + uint32_t ptype;
> + uint32_t l2;
> + char *data;
> +
> + m = rte_pktmbuf_alloc(pool);
> + RTE_TEST_ASSERT_NOT_NULL(m, "cannot allocate mbuf");
> +
> + data = rte_pktmbuf_append(m, len);
> + if (data == NULL) {
> + rte_pktmbuf_free(m);
> + RTE_TEST_ASSERT_NOT_NULL(data, "cannot append data");
> + }
> +
> + memcpy(data, pktdata, len);
> +
> + ptype = rte_net_get_ptype(m, &hdr_lens, RTE_PTYPE_ALL_MASK);
> + l2 = ptype & RTE_PTYPE_L2_MASK;
> +
> + rte_pktmbuf_free(m);
> +
> + RTE_TEST_ASSERT_EQUAL(l2, expected_l2,
> + "unexpected L2 ptype: got 0x%x, expected 0x%x",
> + l2, expected_l2);
> + RTE_TEST_ASSERT_EQUAL(hdr_lens.l2_len, expected_l2_len,
> + "unexpected l2_len: got %u, expected %u",
> + hdr_lens.l2_len, expected_l2_len);
Since this test asks for RTE_PTYPE_ALL_MASK, let's validate the full
returned ptype, and the returned offsets.
> +
> + return 0;
--
David Marchand
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH dpdk v3] net: fix VLAN packet type
2026-04-22 10:28 [PATCH dpdk] net: fix L2 ptype assignment in VLAN loop Robin Jarry
2026-04-22 10:35 ` Robin Jarry
2026-04-22 10:38 ` [PATCH dpdk v2] " Robin Jarry
@ 2026-04-22 13:32 ` Robin Jarry
2026-04-23 9:19 ` Kevin Traynor
2026-04-23 11:24 ` [PATCH dpdk v4] " Robin Jarry
3 siblings, 1 reply; 19+ messages in thread
From: Robin Jarry @ 2026-04-22 13:32 UTC (permalink / raw)
To: dev, Gregory Etelson; +Cc: stable
Since commit 1f250674085a ("net: fix packet type for stacked VLAN"),
rte_net_get_ptype() uses |= to set the L2 ptype inside the VLAN
parsing loop. Since pkt_type is already initialized with
RTE_PTYPE_L2_ETHER (0x1), or-ing it with RTE_PTYPE_L2_ETHER_VLAN
(0x6) results in RTE_PTYPE_L2_ETHER_QINQ (0x7). This causes single
VLAN frames to be misidentified as QinQ.
This was detected while testing DPDK 25.11.1 in grout. The net/tap
driver calls rte_net_get_ptype() in tap_verify_csum() to determine the
L2 header length. With the wrong ptype, l2_len is set to 22 (ether
+ QinQ = 14 + 8) instead of 18 (ether + VLAN = 14 + 4), shifting the IP
header pointer by 4 bytes. The checksum is then computed on garbage
data, causing valid packets to be dropped.
Initialize pkt_type to 0 and defer the RTE_PTYPE_L2_ETHER assignment to
the l3 label, only if no VLAN/QinQ type was set in the loop. This avoids
the bitwise-or conflict between the L2 ptype constants entirely.
Add a new net_ptype_autotest unit test that verifies the ptype and
header lengths (l2_len, l3_len, l4_len) returned by rte_net_get_ptype()
for plain Ethernet, single VLAN, stacked VLAN (two 802.1Q tags), and
QinQ (802.1ad + 802.1Q) frames, with both IPv4/IPv6 and UDP/TCP
combinations.
Fixes: 1f250674085a ("net: fix packet type for stacked VLAN")
Cc: stable@dpdk.org
Signed-off-by: Robin Jarry <rjarry@redhat.com>
---
Notes:
v3:
* changed the approach: initialize pkt_type=0 and only set it to
RTE_PTYPE_L2_ETHER if neither of VLAN nor QINQ matched.
* extended the unit tests to check for header lengths and added ipv6 / tcp
cases.
v2: added new ptype tests
app/test/meson.build | 1 +
app/test/test_net_ptype.c | 231 ++++++++++++++++++++++++++++++++++++++
lib/net/rte_net.c | 4 +-
3 files changed, 235 insertions(+), 1 deletion(-)
create mode 100644 app/test/test_net_ptype.c
diff --git a/app/test/meson.build b/app/test/meson.build
index 7d458f9c079a..9f4afb040a46 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -135,6 +135,7 @@ source_file_deps = {
'test_mp_secondary.c': ['hash'],
'test_net_ether.c': ['net'],
'test_net_ip6.c': ['net'],
+ 'test_net_ptype.c': ['net'],
'test_pcapng.c': ['net_null', 'net', 'ethdev', 'pcapng', 'bus_vdev'],
'test_pdcp.c': ['eventdev', 'pdcp', 'net', 'timer', 'security'],
'test_pdump.c': ['pdump'] + sample_packet_forward_deps,
diff --git a/app/test/test_net_ptype.c b/app/test/test_net_ptype.c
new file mode 100644
index 000000000000..bfe85da13543
--- /dev/null
+++ b/app/test/test_net_ptype.c
@@ -0,0 +1,231 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2026 Red Hat, Inc.
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include <rte_mbuf.h>
+#include <rte_net.h>
+
+#include <rte_test.h>
+#include "test.h"
+
+#define MEMPOOL_CACHE_SIZE 0
+#define MBUF_DATA_SIZE 256
+#define NB_MBUF 128
+
+/* Ether()/IP()/UDP()/Raw('x') */
+static const uint8_t pkt_ether_ipv4_udp[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x1d, 0x00, 0x01, 0x00, 0x00, 0x40, 0x11,
+ 0x7c, 0xcd, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00,
+ 0x00, 0x01, 0x00, 0x35, 0x00, 0x35, 0x00, 0x09,
+ 0x89, 0x6f, 0x78,
+};
+
+/* Ether()/IP()/TCP() */
+static const uint8_t pkt_ether_ipv4_tcp[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x28, 0x00, 0x01, 0x00, 0x00, 0x40, 0x06,
+ 0x7c, 0xcd, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00,
+ 0x00, 0x01, 0x00, 0x14, 0x00, 0x50, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x02,
+ 0x20, 0x00, 0x91, 0x7c, 0x00, 0x00,
+};
+
+/* Ether()/IPv6()/UDP()/Raw('x') */
+static const uint8_t pkt_ether_ipv6_udp[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x86, 0xdd, 0x60, 0x00,
+ 0x00, 0x00, 0x00, 0x09, 0x11, 0x40, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x35,
+ 0x00, 0x35, 0x00, 0x09, 0x87, 0x70, 0x78,
+};
+
+/* Ether()/IPv6()/TCP() */
+static const uint8_t pkt_ether_ipv6_tcp[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x86, 0xdd, 0x60, 0x00,
+ 0x00, 0x00, 0x00, 0x14, 0x06, 0x40, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x14,
+ 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x50, 0x02, 0x20, 0x00, 0x8f, 0x7d,
+ 0x00, 0x00,
+};
+
+/* Ether()/IP(options='\x00')/UDP()/Raw('x') -- ihl=6 */
+static const uint8_t pkt_ether_ipv4_opts_udp[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x46, 0x00,
+ 0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x40, 0x11,
+ 0x7b, 0xc9, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35,
+ 0x00, 0x35, 0x00, 0x09, 0x89, 0x6f, 0x78,
+};
+
+/* Ether()/Dot1Q(vlan=42)/IP()/UDP()/Raw('x') */
+static const uint8_t pkt_vlan_ipv4_udp[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x2a,
+ 0x08, 0x00, 0x45, 0x00, 0x00, 0x1d, 0x00, 0x01,
+ 0x00, 0x00, 0x40, 0x11, 0x7c, 0xcd, 0x7f, 0x00,
+ 0x00, 0x01, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x35,
+ 0x00, 0x35, 0x00, 0x09, 0x89, 0x6f, 0x78,
+};
+
+/* Ether()/Dot1Q(vlan=42)/IPv6()/TCP() */
+static const uint8_t pkt_vlan_ipv6_tcp[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x2a,
+ 0x86, 0xdd, 0x60, 0x00, 0x00, 0x00, 0x00, 0x14,
+ 0x06, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x14, 0x00, 0x50, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x02,
+ 0x20, 0x00, 0x8f, 0x7d, 0x00, 0x00,
+};
+
+/* Ether()/Dot1AD(vlan=42)/Dot1Q(vlan=43)/IP()/UDP()/Raw('x') */
+static const uint8_t pkt_qinq_ipv4_udp[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x88, 0xa8, 0x00, 0x2a,
+ 0x81, 0x00, 0x00, 0x2b, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x1d, 0x00, 0x01, 0x00, 0x00, 0x40, 0x11,
+ 0x7c, 0xcd, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00,
+ 0x00, 0x01, 0x00, 0x35, 0x00, 0x35, 0x00, 0x09,
+ 0x89, 0x6f, 0x78,
+};
+
+/* Ether()/Dot1Q(vlan=42)/Dot1Q(vlan=43)/IP()/UDP()/Raw('x') */
+static const uint8_t pkt_vlan_vlan_ipv4_udp[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x2a,
+ 0x81, 0x00, 0x00, 0x2b, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x1d, 0x00, 0x01, 0x00, 0x00, 0x40, 0x11,
+ 0x7c, 0xcd, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00,
+ 0x00, 0x01, 0x00, 0x35, 0x00, 0x35, 0x00, 0x09,
+ 0x89, 0x6f, 0x78,
+};
+
+/* Ether()/Dot1AD(vlan=42)/Dot1Q(vlan=43)/IPv6()/TCP() */
+static const uint8_t pkt_qinq_ipv6_tcp[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x88, 0xa8, 0x00, 0x2a,
+ 0x81, 0x00, 0x00, 0x2b, 0x86, 0xdd, 0x60, 0x00,
+ 0x00, 0x00, 0x00, 0x14, 0x06, 0x40, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x14,
+ 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x50, 0x02, 0x20, 0x00, 0x8f, 0x7d,
+ 0x00, 0x00,
+};
+
+static int
+test_get_ptype(struct rte_mempool *pool, const char *name,
+ const uint8_t *pktdata, size_t len, uint32_t expected_ptype,
+ uint8_t expected_l2_len, uint16_t expected_l3_len,
+ uint8_t expected_l4_len)
+{
+ struct rte_net_hdr_lens hdr_lens;
+ struct rte_mbuf *m;
+ uint32_t ptype;
+ char *data;
+
+ RTE_LOG(INFO, EAL, "%s: %s\n", __func__, name);
+
+ m = rte_pktmbuf_alloc(pool);
+ RTE_TEST_ASSERT_NOT_NULL(m, "cannot allocate mbuf");
+
+ data = rte_pktmbuf_append(m, len);
+ if (data == NULL) {
+ rte_pktmbuf_free(m);
+ RTE_TEST_ASSERT_NOT_NULL(data, "cannot append data");
+ }
+
+ memcpy(data, pktdata, len);
+
+ memset(&hdr_lens, 0, sizeof(hdr_lens));
+ ptype = rte_net_get_ptype(m, &hdr_lens, RTE_PTYPE_ALL_MASK);
+
+ rte_pktmbuf_free(m);
+
+ RTE_TEST_ASSERT_EQUAL(ptype, expected_ptype,
+ "unexpected ptype: got 0x%x, expected 0x%x",
+ ptype, expected_ptype);
+ RTE_TEST_ASSERT_EQUAL(hdr_lens.l2_len, expected_l2_len,
+ "unexpected l2_len: got %u, expected %u",
+ hdr_lens.l2_len, expected_l2_len);
+ RTE_TEST_ASSERT_EQUAL(hdr_lens.l3_len, expected_l3_len,
+ "unexpected l3_len: got %u, expected %u",
+ hdr_lens.l3_len, expected_l3_len);
+ RTE_TEST_ASSERT_EQUAL(hdr_lens.l4_len, expected_l4_len,
+ "unexpected l4_len: got %u, expected %u",
+ hdr_lens.l4_len, expected_l4_len);
+
+ return 0;
+}
+
+#define test_case(pool, pkt, expected_ptype, l2, l3, l4) \
+ test_get_ptype(pool, #pkt, pkt, sizeof(pkt), expected_ptype, l2, l3, l4)
+
+static int
+test_net_ptype(void)
+{
+ struct rte_mempool *pool;
+ int ret = 0;
+
+ pool = rte_pktmbuf_pool_create("test_ptype_mbuf_pool",
+ NB_MBUF, MEMPOOL_CACHE_SIZE, 0, MBUF_DATA_SIZE,
+ SOCKET_ID_ANY);
+ RTE_TEST_ASSERT_NOT_NULL(pool, "cannot allocate mbuf pool");
+
+ ret |= test_case(pool, pkt_ether_ipv4_udp,
+ RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4 | RTE_PTYPE_L4_UDP,
+ 14, 20, 8);
+ ret |= test_case(pool, pkt_ether_ipv4_tcp,
+ RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4 | RTE_PTYPE_L4_TCP,
+ 14, 20, 20);
+ ret |= test_case(pool, pkt_ether_ipv6_udp,
+ RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6 | RTE_PTYPE_L4_UDP,
+ 14, 40, 8);
+ ret |= test_case(pool, pkt_ether_ipv6_tcp,
+ RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6 | RTE_PTYPE_L4_TCP,
+ 14, 40, 20);
+ ret |= test_case(pool, pkt_ether_ipv4_opts_udp,
+ RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT | RTE_PTYPE_L4_UDP,
+ 14, 24, 8);
+ ret |= test_case(pool, pkt_vlan_ipv4_udp,
+ RTE_PTYPE_L2_ETHER_VLAN | RTE_PTYPE_L3_IPV4 | RTE_PTYPE_L4_UDP,
+ 18, 20, 8);
+ ret |= test_case(pool, pkt_vlan_ipv6_tcp,
+ RTE_PTYPE_L2_ETHER_VLAN | RTE_PTYPE_L3_IPV6 | RTE_PTYPE_L4_TCP,
+ 18, 40, 20);
+ ret |= test_case(pool, pkt_qinq_ipv4_udp,
+ RTE_PTYPE_L2_ETHER_QINQ | RTE_PTYPE_L3_IPV4 | RTE_PTYPE_L4_UDP,
+ 22, 20, 8);
+ ret |= test_case(pool, pkt_vlan_vlan_ipv4_udp,
+ RTE_PTYPE_L2_ETHER_VLAN | RTE_PTYPE_L3_IPV4 | RTE_PTYPE_L4_UDP,
+ 22, 20, 8);
+ ret |= test_case(pool, pkt_qinq_ipv6_tcp,
+ RTE_PTYPE_L2_ETHER_QINQ | RTE_PTYPE_L3_IPV6 | RTE_PTYPE_L4_TCP,
+ 22, 40, 20);
+
+ rte_mempool_free(pool);
+
+ return ret;
+}
+
+REGISTER_FAST_TEST(net_ptype_autotest, NOHUGE_OK, ASAN_OK, test_net_ptype);
diff --git a/lib/net/rte_net.c b/lib/net/rte_net.c
index 458b4814a9c9..0228f1eb2f18 100644
--- a/lib/net/rte_net.c
+++ b/lib/net/rte_net.c
@@ -331,8 +331,8 @@ uint32_t rte_net_get_ptype(const struct rte_mbuf *m,
struct rte_net_hdr_lens local_hdr_lens;
const struct rte_ether_hdr *eh;
struct rte_ether_hdr eh_copy;
- uint32_t pkt_type = RTE_PTYPE_L2_ETHER;
uint32_t off = 0, vlan_depth = 0;
+ uint32_t pkt_type = 0;
uint16_t proto;
int ret;
@@ -392,6 +392,8 @@ uint32_t rte_net_get_ptype(const struct rte_mbuf *m,
}
l3:
+ if (pkt_type == 0)
+ pkt_type = RTE_PTYPE_L2_ETHER;
if ((layers & RTE_PTYPE_L3_MASK) == 0)
return pkt_type;
--
2.53.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH dpdk v3] net: fix VLAN packet type
2026-04-22 13:32 ` [PATCH dpdk v3] net: fix VLAN packet type Robin Jarry
@ 2026-04-23 9:19 ` Kevin Traynor
2026-04-23 9:49 ` Robin Jarry
0 siblings, 1 reply; 19+ messages in thread
From: Kevin Traynor @ 2026-04-23 9:19 UTC (permalink / raw)
To: Robin Jarry, dev, Gregory Etelson; +Cc: stable
On 4/22/26 2:32 PM, Robin Jarry wrote:
> Since commit 1f250674085a ("net: fix packet type for stacked VLAN"),
> rte_net_get_ptype() uses |= to set the L2 ptype inside the VLAN
> parsing loop. Since pkt_type is already initialized with
> RTE_PTYPE_L2_ETHER (0x1), or-ing it with RTE_PTYPE_L2_ETHER_VLAN
> (0x6) results in RTE_PTYPE_L2_ETHER_QINQ (0x7). This causes single
> VLAN frames to be misidentified as QinQ.
>
> This was detected while testing DPDK 25.11.1 in grout. The net/tap
> driver calls rte_net_get_ptype() in tap_verify_csum() to determine the
> L2 header length. With the wrong ptype, l2_len is set to 22 (ether
> + QinQ = 14 + 8) instead of 18 (ether + VLAN = 14 + 4), shifting the IP
> header pointer by 4 bytes. The checksum is then computed on garbage
> data, causing valid packets to be dropped.
>
> Initialize pkt_type to 0 and defer the RTE_PTYPE_L2_ETHER assignment to
> the l3 label, only if no VLAN/QinQ type was set in the loop. This avoids
> the bitwise-or conflict between the L2 ptype constants entirely.
>
> Add a new net_ptype_autotest unit test that verifies the ptype and
> header lengths (l2_len, l3_len, l4_len) returned by rte_net_get_ptype()
> for plain Ethernet, single VLAN, stacked VLAN (two 802.1Q tags), and
> QinQ (802.1ad + 802.1Q) frames, with both IPv4/IPv6 and UDP/TCP
> combinations.
>
> Fixes: 1f250674085a ("net: fix packet type for stacked VLAN")
> Cc: stable@dpdk.org
>
> Signed-off-by: Robin Jarry <rjarry@redhat.com>
Hi Robin,
Thanks for reporting this.
> ---
>
> Notes:
> v3:
>
> * changed the approach: initialize pkt_type=0 and only set it to
> RTE_PTYPE_L2_ETHER if neither of VLAN nor QINQ matched.
> * extended the unit tests to check for header lengths and added ipv6 / tcp
> cases.
>
> v2: added new ptype tests
>
> app/test/meson.build | 1 +
> app/test/test_net_ptype.c | 231 ++++++++++++++++++++++++++++++++++++++
> lib/net/rte_net.c | 4 +-
> 3 files changed, 235 insertions(+), 1 deletion(-)
> create mode 100644 app/test/test_net_ptype.c
>
<snip>
> diff --git a/lib/net/rte_net.c b/lib/net/rte_net.c
> index 458b4814a9c9..0228f1eb2f18 100644
> --- a/lib/net/rte_net.c
> +++ b/lib/net/rte_net.c
> @@ -331,8 +331,8 @@ uint32_t rte_net_get_ptype(const struct rte_mbuf *m,
> struct rte_net_hdr_lens local_hdr_lens;
> const struct rte_ether_hdr *eh;
> struct rte_ether_hdr eh_copy;
> - uint32_t pkt_type = RTE_PTYPE_L2_ETHER;
> uint32_t off = 0, vlan_depth = 0;
> + uint32_t pkt_type = 0;
> uint16_t proto;
> int ret;
>
> @@ -392,6 +392,8 @@ uint32_t rte_net_get_ptype(const struct rte_mbuf *m,
> }
>
Not shown in diff, but for:
pkt_type |=
proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN) ?
RTE_PTYPE_L2_ETHER_VLAN :
RTE_PTYPE_L2_ETHER_QINQ;
It seems to be produce the right result, but I'm not sure we should be
treating the ptype L2 defines as bitmasks. Maybe I'm wrong and it was
planned, but it looks like a coincidence it works now because QINQ (0x7)
happens to be a superset of VLAN (0x6) for the OR.
Perhaps you could check vlan_depth and assign VLAN or QINQ based on that?
thanks,
Kevin.
> l3:
> + if (pkt_type == 0)
> + pkt_type = RTE_PTYPE_L2_ETHER;
> if ((layers & RTE_PTYPE_L3_MASK) == 0)
> return pkt_type;
>
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH dpdk v3] net: fix VLAN packet type
2026-04-23 9:19 ` Kevin Traynor
@ 2026-04-23 9:49 ` Robin Jarry
2026-04-23 10:59 ` Kevin Traynor
0 siblings, 1 reply; 19+ messages in thread
From: Robin Jarry @ 2026-04-23 9:49 UTC (permalink / raw)
To: Kevin Traynor, dev, Gregory Etelson; +Cc: stable
Kevin Traynor, Apr 23, 2026 at 11:19:
> Not shown in diff, but for:
>
> pkt_type |=
> proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN) ?
> RTE_PTYPE_L2_ETHER_VLAN :
> RTE_PTYPE_L2_ETHER_QINQ;
>
> It seems to be produce the right result, but I'm not sure we should be
> treating the ptype L2 defines as bitmasks. Maybe I'm wrong and it was
> planned, but it looks like a coincidence it works now because QINQ (0x7)
> happens to be a superset of VLAN (0x6) for the OR.
>
> Perhaps you could check vlan_depth and assign VLAN or QINQ based on that?
I hadn't considered this. It would make sense to have something like:
l3:
switch (vlan_depth) {
case 0:
pkt_type = RTE_PTYPE_L2_ETHER;
break;
case 1:
pkt_type = RTE_PTYPE_L2_ETHER_VLAN;
break;
default:
pkt_type = RTE_PTYPE_L2_ETHER_QINQ;
break;
}
Mind that this will report RTE_PTYPE_L2_ETHER_QINQ even if we have
double stacked 802.1Q tags (ether type 0x8100) and no 802.1ad S-tag
(QinQ 0x88A8). I don't think this is an issue, but I'll let you judge.
I can send a v4 with this if that suits you.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH dpdk v3] net: fix VLAN packet type
2026-04-23 9:49 ` Robin Jarry
@ 2026-04-23 10:59 ` Kevin Traynor
2026-04-23 11:11 ` Robin Jarry
0 siblings, 1 reply; 19+ messages in thread
From: Kevin Traynor @ 2026-04-23 10:59 UTC (permalink / raw)
To: Robin Jarry, dev, Gregory Etelson; +Cc: stable
On 4/23/26 10:49 AM, Robin Jarry wrote:
> Kevin Traynor, Apr 23, 2026 at 11:19:
>> Not shown in diff, but for:
>>
>> pkt_type |=
>> proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN) ?
>> RTE_PTYPE_L2_ETHER_VLAN :
>> RTE_PTYPE_L2_ETHER_QINQ;
>>
>> It seems to be produce the right result, but I'm not sure we should be
>> treating the ptype L2 defines as bitmasks. Maybe I'm wrong and it was
>> planned, but it looks like a coincidence it works now because QINQ (0x7)
>> happens to be a superset of VLAN (0x6) for the OR.
>>
>> Perhaps you could check vlan_depth and assign VLAN or QINQ based on that?
>
> I hadn't considered this. It would make sense to have something like:
>
> l3:
> switch (vlan_depth) {
> case 0:
> pkt_type = RTE_PTYPE_L2_ETHER;
> break;
> case 1:
> pkt_type = RTE_PTYPE_L2_ETHER_VLAN;
> break;
> default:
> pkt_type = RTE_PTYPE_L2_ETHER_QINQ;
> break;
> }
>
> Mind that this will report RTE_PTYPE_L2_ETHER_QINQ even if we have
> double stacked 802.1Q tags (ether type 0x8100) and no 802.1ad S-tag
> (QinQ 0x88A8). I don't think this is an issue, but I'll let you judge.
>
I'm not sure about this.
> I can send a v4 with this if that suits you.
>
Setting pkt_type later has some implications for early returns (now i
notice that's present in v3 too)
Would this work ?
@@ -358,10 +358,12 @@ uint32_t rte_net_get_ptype(const struct rte_mbuf *m,
struct rte_vlan_hdr vh_copy;
+ if (vlan_depth == 0) {
+ pkt_type =
+ proto ==
rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN) ?
+ RTE_PTYPE_L2_ETHER_VLAN :
+ RTE_PTYPE_L2_ETHER_QINQ;
+ }
if (++vlan_depth > RTE_NET_VLAN_MAX_DEPTH)
return 0;
- pkt_type |=
- proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN) ?
- RTE_PTYPE_L2_ETHER_VLAN :
- RTE_PTYPE_L2_ETHER_QINQ;
vh = rte_pktmbuf_read(m, off, sizeof(*vh), &vh_copy);
if (unlikely(vh == NULL))
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH dpdk v3] net: fix VLAN packet type
2026-04-23 10:59 ` Kevin Traynor
@ 2026-04-23 11:11 ` Robin Jarry
0 siblings, 0 replies; 19+ messages in thread
From: Robin Jarry @ 2026-04-23 11:11 UTC (permalink / raw)
To: Kevin Traynor, dev, Gregory Etelson; +Cc: stable
Kevin Traynor, Apr 23, 2026 at 12:59:
> Setting pkt_type later has some implications for early returns (now i
> notice that's present in v3 too)
>
> Would this work ?
>
> @@ -358,10 +358,12 @@ uint32_t rte_net_get_ptype(const struct rte_mbuf *m,
> struct rte_vlan_hdr vh_copy;
>
> + if (vlan_depth == 0) {
> + pkt_type =
> + proto ==
> rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN) ?
> + RTE_PTYPE_L2_ETHER_VLAN :
> + RTE_PTYPE_L2_ETHER_QINQ;
> + }
> if (++vlan_depth > RTE_NET_VLAN_MAX_DEPTH)
> return 0;
> - pkt_type |=
> - proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN) ?
> - RTE_PTYPE_L2_ETHER_VLAN :
> - RTE_PTYPE_L2_ETHER_QINQ;
> vh = rte_pktmbuf_read(m, off, sizeof(*vh), &vh_copy);
> if (unlikely(vh == NULL))
Yes, that works. I'll send a v4.
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH dpdk v4] net: fix VLAN packet type
2026-04-22 10:28 [PATCH dpdk] net: fix L2 ptype assignment in VLAN loop Robin Jarry
` (2 preceding siblings ...)
2026-04-22 13:32 ` [PATCH dpdk v3] net: fix VLAN packet type Robin Jarry
@ 2026-04-23 11:24 ` Robin Jarry
2026-04-24 16:18 ` Kevin Traynor
2026-04-25 8:40 ` David Marchand
3 siblings, 2 replies; 19+ messages in thread
From: Robin Jarry @ 2026-04-23 11:24 UTC (permalink / raw)
To: dev, Gregory Etelson; +Cc: stable
Since commit 1f250674085a ("net: fix packet type for stacked VLAN"),
rte_net_get_ptype() uses |= to set the L2 ptype inside the VLAN
parsing loop. Since pkt_type is already initialized with
RTE_PTYPE_L2_ETHER (0x1), or-ing it with RTE_PTYPE_L2_ETHER_VLAN
(0x6) results in RTE_PTYPE_L2_ETHER_QINQ (0x7). This causes single
VLAN frames to be misidentified as QinQ.
This was detected while testing DPDK 25.11.1 in grout. The net/tap
driver calls rte_net_get_ptype() in tap_verify_csum() to determine the
L2 header length. With the wrong ptype, l2_len is set to 22 (ether
+ QinQ = 14 + 8) instead of 18 (ether + VLAN = 14 + 4), shifting the IP
header pointer by 4 bytes. The checksum is then computed on garbage
data, causing valid packets to be dropped.
Set pkt_type to RTE_PTYPE_L2_ETHER_VLAN or RTE_PTYPE_L2_ETHER_QINQ only
for the first level of "VLAN" tag seen. This avoids the bitwise-or
conflict between the L2 ptype constants entirely.
Add a new net_ptype_autotest unit test that verifies the ptype and
header lengths (l2_len, l3_len, l4_len) returned by rte_net_get_ptype()
for plain Ethernet, single VLAN, stacked VLAN (two 802.1Q tags), and
QinQ (802.1ad + 802.1Q) frames, with both IPv4/IPv6 and UDP/TCP
combinations.
Fixes: 1f250674085a ("net: fix packet type for stacked VLAN")
Cc: stable@dpdk.org
Signed-off-by: Robin Jarry <rjarry@redhat.com>
---
Notes:
v4:
* changed the approach again. Only set VLAN or QINQ ptype for the first
encountered vlan/qinq ether type.
* unit tests not changed
v3:
* changed the approach: initialize pkt_type=0 and only set it to
RTE_PTYPE_L2_ETHER if neither of VLAN nor QINQ matched.
* extended the unit tests to check for header lengths and added ipv6 / tcp
cases.
v2: added new ptype tests
v3:
* changed the approach: initialize pkt_type=0 and only set it to
RTE_PTYPE_L2_ETHER if neither of VLAN nor QINQ matched.
* extended the unit tests to check for header lengths and added ipv6 / tcp
cases.
v2: added new ptype tests
app/test/meson.build | 1 +
app/test/test_net_ptype.c | 231 ++++++++++++++++++++++++++++++++++++++
lib/net/rte_net.c | 10 +-
3 files changed, 238 insertions(+), 4 deletions(-)
create mode 100644 app/test/test_net_ptype.c
diff --git a/app/test/meson.build b/app/test/meson.build
index 7d458f9c079a..9f4afb040a46 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -135,6 +135,7 @@ source_file_deps = {
'test_mp_secondary.c': ['hash'],
'test_net_ether.c': ['net'],
'test_net_ip6.c': ['net'],
+ 'test_net_ptype.c': ['net'],
'test_pcapng.c': ['net_null', 'net', 'ethdev', 'pcapng', 'bus_vdev'],
'test_pdcp.c': ['eventdev', 'pdcp', 'net', 'timer', 'security'],
'test_pdump.c': ['pdump'] + sample_packet_forward_deps,
diff --git a/app/test/test_net_ptype.c b/app/test/test_net_ptype.c
new file mode 100644
index 000000000000..bfe85da13543
--- /dev/null
+++ b/app/test/test_net_ptype.c
@@ -0,0 +1,231 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2026 Red Hat, Inc.
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include <rte_mbuf.h>
+#include <rte_net.h>
+
+#include <rte_test.h>
+#include "test.h"
+
+#define MEMPOOL_CACHE_SIZE 0
+#define MBUF_DATA_SIZE 256
+#define NB_MBUF 128
+
+/* Ether()/IP()/UDP()/Raw('x') */
+static const uint8_t pkt_ether_ipv4_udp[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x1d, 0x00, 0x01, 0x00, 0x00, 0x40, 0x11,
+ 0x7c, 0xcd, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00,
+ 0x00, 0x01, 0x00, 0x35, 0x00, 0x35, 0x00, 0x09,
+ 0x89, 0x6f, 0x78,
+};
+
+/* Ether()/IP()/TCP() */
+static const uint8_t pkt_ether_ipv4_tcp[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x28, 0x00, 0x01, 0x00, 0x00, 0x40, 0x06,
+ 0x7c, 0xcd, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00,
+ 0x00, 0x01, 0x00, 0x14, 0x00, 0x50, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x02,
+ 0x20, 0x00, 0x91, 0x7c, 0x00, 0x00,
+};
+
+/* Ether()/IPv6()/UDP()/Raw('x') */
+static const uint8_t pkt_ether_ipv6_udp[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x86, 0xdd, 0x60, 0x00,
+ 0x00, 0x00, 0x00, 0x09, 0x11, 0x40, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x35,
+ 0x00, 0x35, 0x00, 0x09, 0x87, 0x70, 0x78,
+};
+
+/* Ether()/IPv6()/TCP() */
+static const uint8_t pkt_ether_ipv6_tcp[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x86, 0xdd, 0x60, 0x00,
+ 0x00, 0x00, 0x00, 0x14, 0x06, 0x40, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x14,
+ 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x50, 0x02, 0x20, 0x00, 0x8f, 0x7d,
+ 0x00, 0x00,
+};
+
+/* Ether()/IP(options='\x00')/UDP()/Raw('x') -- ihl=6 */
+static const uint8_t pkt_ether_ipv4_opts_udp[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x46, 0x00,
+ 0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x40, 0x11,
+ 0x7b, 0xc9, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35,
+ 0x00, 0x35, 0x00, 0x09, 0x89, 0x6f, 0x78,
+};
+
+/* Ether()/Dot1Q(vlan=42)/IP()/UDP()/Raw('x') */
+static const uint8_t pkt_vlan_ipv4_udp[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x2a,
+ 0x08, 0x00, 0x45, 0x00, 0x00, 0x1d, 0x00, 0x01,
+ 0x00, 0x00, 0x40, 0x11, 0x7c, 0xcd, 0x7f, 0x00,
+ 0x00, 0x01, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x35,
+ 0x00, 0x35, 0x00, 0x09, 0x89, 0x6f, 0x78,
+};
+
+/* Ether()/Dot1Q(vlan=42)/IPv6()/TCP() */
+static const uint8_t pkt_vlan_ipv6_tcp[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x2a,
+ 0x86, 0xdd, 0x60, 0x00, 0x00, 0x00, 0x00, 0x14,
+ 0x06, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x14, 0x00, 0x50, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x02,
+ 0x20, 0x00, 0x8f, 0x7d, 0x00, 0x00,
+};
+
+/* Ether()/Dot1AD(vlan=42)/Dot1Q(vlan=43)/IP()/UDP()/Raw('x') */
+static const uint8_t pkt_qinq_ipv4_udp[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x88, 0xa8, 0x00, 0x2a,
+ 0x81, 0x00, 0x00, 0x2b, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x1d, 0x00, 0x01, 0x00, 0x00, 0x40, 0x11,
+ 0x7c, 0xcd, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00,
+ 0x00, 0x01, 0x00, 0x35, 0x00, 0x35, 0x00, 0x09,
+ 0x89, 0x6f, 0x78,
+};
+
+/* Ether()/Dot1Q(vlan=42)/Dot1Q(vlan=43)/IP()/UDP()/Raw('x') */
+static const uint8_t pkt_vlan_vlan_ipv4_udp[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x2a,
+ 0x81, 0x00, 0x00, 0x2b, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x1d, 0x00, 0x01, 0x00, 0x00, 0x40, 0x11,
+ 0x7c, 0xcd, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00,
+ 0x00, 0x01, 0x00, 0x35, 0x00, 0x35, 0x00, 0x09,
+ 0x89, 0x6f, 0x78,
+};
+
+/* Ether()/Dot1AD(vlan=42)/Dot1Q(vlan=43)/IPv6()/TCP() */
+static const uint8_t pkt_qinq_ipv6_tcp[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x88, 0xa8, 0x00, 0x2a,
+ 0x81, 0x00, 0x00, 0x2b, 0x86, 0xdd, 0x60, 0x00,
+ 0x00, 0x00, 0x00, 0x14, 0x06, 0x40, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x14,
+ 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x50, 0x02, 0x20, 0x00, 0x8f, 0x7d,
+ 0x00, 0x00,
+};
+
+static int
+test_get_ptype(struct rte_mempool *pool, const char *name,
+ const uint8_t *pktdata, size_t len, uint32_t expected_ptype,
+ uint8_t expected_l2_len, uint16_t expected_l3_len,
+ uint8_t expected_l4_len)
+{
+ struct rte_net_hdr_lens hdr_lens;
+ struct rte_mbuf *m;
+ uint32_t ptype;
+ char *data;
+
+ RTE_LOG(INFO, EAL, "%s: %s\n", __func__, name);
+
+ m = rte_pktmbuf_alloc(pool);
+ RTE_TEST_ASSERT_NOT_NULL(m, "cannot allocate mbuf");
+
+ data = rte_pktmbuf_append(m, len);
+ if (data == NULL) {
+ rte_pktmbuf_free(m);
+ RTE_TEST_ASSERT_NOT_NULL(data, "cannot append data");
+ }
+
+ memcpy(data, pktdata, len);
+
+ memset(&hdr_lens, 0, sizeof(hdr_lens));
+ ptype = rte_net_get_ptype(m, &hdr_lens, RTE_PTYPE_ALL_MASK);
+
+ rte_pktmbuf_free(m);
+
+ RTE_TEST_ASSERT_EQUAL(ptype, expected_ptype,
+ "unexpected ptype: got 0x%x, expected 0x%x",
+ ptype, expected_ptype);
+ RTE_TEST_ASSERT_EQUAL(hdr_lens.l2_len, expected_l2_len,
+ "unexpected l2_len: got %u, expected %u",
+ hdr_lens.l2_len, expected_l2_len);
+ RTE_TEST_ASSERT_EQUAL(hdr_lens.l3_len, expected_l3_len,
+ "unexpected l3_len: got %u, expected %u",
+ hdr_lens.l3_len, expected_l3_len);
+ RTE_TEST_ASSERT_EQUAL(hdr_lens.l4_len, expected_l4_len,
+ "unexpected l4_len: got %u, expected %u",
+ hdr_lens.l4_len, expected_l4_len);
+
+ return 0;
+}
+
+#define test_case(pool, pkt, expected_ptype, l2, l3, l4) \
+ test_get_ptype(pool, #pkt, pkt, sizeof(pkt), expected_ptype, l2, l3, l4)
+
+static int
+test_net_ptype(void)
+{
+ struct rte_mempool *pool;
+ int ret = 0;
+
+ pool = rte_pktmbuf_pool_create("test_ptype_mbuf_pool",
+ NB_MBUF, MEMPOOL_CACHE_SIZE, 0, MBUF_DATA_SIZE,
+ SOCKET_ID_ANY);
+ RTE_TEST_ASSERT_NOT_NULL(pool, "cannot allocate mbuf pool");
+
+ ret |= test_case(pool, pkt_ether_ipv4_udp,
+ RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4 | RTE_PTYPE_L4_UDP,
+ 14, 20, 8);
+ ret |= test_case(pool, pkt_ether_ipv4_tcp,
+ RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4 | RTE_PTYPE_L4_TCP,
+ 14, 20, 20);
+ ret |= test_case(pool, pkt_ether_ipv6_udp,
+ RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6 | RTE_PTYPE_L4_UDP,
+ 14, 40, 8);
+ ret |= test_case(pool, pkt_ether_ipv6_tcp,
+ RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6 | RTE_PTYPE_L4_TCP,
+ 14, 40, 20);
+ ret |= test_case(pool, pkt_ether_ipv4_opts_udp,
+ RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT | RTE_PTYPE_L4_UDP,
+ 14, 24, 8);
+ ret |= test_case(pool, pkt_vlan_ipv4_udp,
+ RTE_PTYPE_L2_ETHER_VLAN | RTE_PTYPE_L3_IPV4 | RTE_PTYPE_L4_UDP,
+ 18, 20, 8);
+ ret |= test_case(pool, pkt_vlan_ipv6_tcp,
+ RTE_PTYPE_L2_ETHER_VLAN | RTE_PTYPE_L3_IPV6 | RTE_PTYPE_L4_TCP,
+ 18, 40, 20);
+ ret |= test_case(pool, pkt_qinq_ipv4_udp,
+ RTE_PTYPE_L2_ETHER_QINQ | RTE_PTYPE_L3_IPV4 | RTE_PTYPE_L4_UDP,
+ 22, 20, 8);
+ ret |= test_case(pool, pkt_vlan_vlan_ipv4_udp,
+ RTE_PTYPE_L2_ETHER_VLAN | RTE_PTYPE_L3_IPV4 | RTE_PTYPE_L4_UDP,
+ 22, 20, 8);
+ ret |= test_case(pool, pkt_qinq_ipv6_tcp,
+ RTE_PTYPE_L2_ETHER_QINQ | RTE_PTYPE_L3_IPV6 | RTE_PTYPE_L4_TCP,
+ 22, 40, 20);
+
+ rte_mempool_free(pool);
+
+ return ret;
+}
+
+REGISTER_FAST_TEST(net_ptype_autotest, NOHUGE_OK, ASAN_OK, test_net_ptype);
diff --git a/lib/net/rte_net.c b/lib/net/rte_net.c
index 458b4814a9c9..a871318b21c2 100644
--- a/lib/net/rte_net.c
+++ b/lib/net/rte_net.c
@@ -357,12 +357,14 @@ uint32_t rte_net_get_ptype(const struct rte_mbuf *m,
const struct rte_vlan_hdr *vh;
struct rte_vlan_hdr vh_copy;
+ if (vlan_depth == 0) {
+ pkt_type =
+ proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN) ?
+ RTE_PTYPE_L2_ETHER_VLAN :
+ RTE_PTYPE_L2_ETHER_QINQ;
+ }
if (++vlan_depth > RTE_NET_VLAN_MAX_DEPTH)
return 0;
- pkt_type |=
- proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN) ?
- RTE_PTYPE_L2_ETHER_VLAN :
- RTE_PTYPE_L2_ETHER_QINQ;
vh = rte_pktmbuf_read(m, off, sizeof(*vh), &vh_copy);
if (unlikely(vh == NULL))
return pkt_type;
--
2.53.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH dpdk v4] net: fix VLAN packet type
2026-04-23 11:24 ` [PATCH dpdk v4] " Robin Jarry
@ 2026-04-24 16:18 ` Kevin Traynor
2026-04-25 8:40 ` David Marchand
1 sibling, 0 replies; 19+ messages in thread
From: Kevin Traynor @ 2026-04-24 16:18 UTC (permalink / raw)
To: Robin Jarry, dev, Gregory Etelson
Cc: stable, Thomas Monjalon, David Marchand, luca.boccassi@gmail.com,
Shani Peretz
On 4/23/26 12:24 PM, Robin Jarry wrote:
> Since commit 1f250674085a ("net: fix packet type for stacked VLAN"),
> rte_net_get_ptype() uses |= to set the L2 ptype inside the VLAN
> parsing loop. Since pkt_type is already initialized with
> RTE_PTYPE_L2_ETHER (0x1), or-ing it with RTE_PTYPE_L2_ETHER_VLAN
> (0x6) results in RTE_PTYPE_L2_ETHER_QINQ (0x7). This causes single
> VLAN frames to be misidentified as QinQ.
>
> This was detected while testing DPDK 25.11.1 in grout. The net/tap
> driver calls rte_net_get_ptype() in tap_verify_csum() to determine the
> L2 header length. With the wrong ptype, l2_len is set to 22 (ether
> + QinQ = 14 + 8) instead of 18 (ether + VLAN = 14 + 4), shifting the IP
> header pointer by 4 bytes. The checksum is then computed on garbage
> data, causing valid packets to be dropped.
>
> Set pkt_type to RTE_PTYPE_L2_ETHER_VLAN or RTE_PTYPE_L2_ETHER_QINQ only
> for the first level of "VLAN" tag seen. This avoids the bitwise-or
> conflict between the L2 ptype constants entirely.
>
> Add a new net_ptype_autotest unit test that verifies the ptype and
> header lengths (l2_len, l3_len, l4_len) returned by rte_net_get_ptype()
> for plain Ethernet, single VLAN, stacked VLAN (two 802.1Q tags), and
> QinQ (802.1ad + 802.1Q) frames, with both IPv4/IPv6 and UDP/TCP
> combinations.
>
> Fixes: 1f250674085a ("net: fix packet type for stacked VLAN")
> Cc: stable@dpdk.org
>
> Signed-off-by: Robin Jarry <rjarry@redhat.com>
> ---
>
Thanks Robin, LGTM.
Acked-by: Kevin Traynor <ktraynor@redhat.com>
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH dpdk v4] net: fix VLAN packet type
2026-04-23 11:24 ` [PATCH dpdk v4] " Robin Jarry
2026-04-24 16:18 ` Kevin Traynor
@ 2026-04-25 8:40 ` David Marchand
2026-04-27 10:47 ` Robin Jarry
2026-05-15 11:17 ` Kevin Traynor
1 sibling, 2 replies; 19+ messages in thread
From: David Marchand @ 2026-04-25 8:40 UTC (permalink / raw)
To: Robin Jarry
Cc: dev, Gregory Etelson, stable, Thomas Monjalon, Kevin Traynor,
Luca Boccassi
On Thu, 23 Apr 2026 at 13:25, Robin Jarry <rjarry@redhat.com> wrote:
> diff --git a/lib/net/rte_net.c b/lib/net/rte_net.c
> index 458b4814a9c9..a871318b21c2 100644
> --- a/lib/net/rte_net.c
> +++ b/lib/net/rte_net.c
> @@ -357,12 +357,14 @@ uint32_t rte_net_get_ptype(const struct rte_mbuf *m,
> const struct rte_vlan_hdr *vh;
> struct rte_vlan_hdr vh_copy;
>
> + if (vlan_depth == 0) {
> + pkt_type =
> + proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN) ?
> + RTE_PTYPE_L2_ETHER_VLAN :
> + RTE_PTYPE_L2_ETHER_QINQ;
> + }
This code is becoming too complex.
The original usecase with more than 2 stacked vlan is a bit strange,
but the max depth limit seems just arbitrary (why 8?).
We have clear boundaries, with the size of the packet (see below,
check on vh == NULL).
The offending commit also allows stacking mpls on top of vlan, without
mentioning it.
I think we want this behavior, but still was it intended?
In the end, reverting the previous fix then just advancing off and
breaking once proto is not a vlan/qinq type gives a much simpler fix
when compared to v25.11.
(ignoring indent changes with -w)
$ git diff -w v25.11 lib/net/rte_net.c
diff --git a/lib/net/rte_net.c b/lib/net/rte_net.c
index c70b57fdc0..f58d699c83 100644
--- a/lib/net/rte_net.c
+++ b/lib/net/rte_net.c
@@ -349,30 +349,28 @@ uint32_t rte_net_get_ptype(const struct rte_mbuf *m,
if (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4))
goto l3; /* fast path if packet is IPv4 */
- if (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN)) {
+ if (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN) ||
+ proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_QINQ)) {
const struct rte_vlan_hdr *vh;
struct rte_vlan_hdr vh_copy;
+ if (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN))
pkt_type = RTE_PTYPE_L2_ETHER_VLAN;
+ else
+ pkt_type = RTE_PTYPE_L2_ETHER_QINQ;
+
+ do {
vh = rte_pktmbuf_read(m, off, sizeof(*vh), &vh_copy);
if (unlikely(vh == NULL))
return pkt_type;
off += sizeof(*vh);
hdr_lens->l2_len += sizeof(*vh);
proto = vh->eth_proto;
- } else if (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_QINQ)) {
- const struct rte_vlan_hdr *vh;
- struct rte_vlan_hdr vh_copy;
+ } while (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN) ||
+ proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_QINQ));
+ }
- pkt_type = RTE_PTYPE_L2_ETHER_QINQ;
- vh = rte_pktmbuf_read(m, off + sizeof(*vh), sizeof(*vh),
- &vh_copy);
- if (unlikely(vh == NULL))
- return pkt_type;
- off += 2 * sizeof(*vh);
- hdr_lens->l2_len += 2 * sizeof(*vh);
- proto = vh->eth_proto;
- } else if ((proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_MPLS)) ||
+ if ((proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_MPLS)) ||
(proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_MPLSM))) {
unsigned int i;
const struct rte_mpls_hdr *mh;
This is untested, but what do you think?
--
David Marchand
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH dpdk v4] net: fix VLAN packet type
2026-04-25 8:40 ` David Marchand
@ 2026-04-27 10:47 ` Robin Jarry
2026-04-27 15:53 ` Thomas Monjalon
2026-05-15 11:17 ` Kevin Traynor
1 sibling, 1 reply; 19+ messages in thread
From: Robin Jarry @ 2026-04-27 10:47 UTC (permalink / raw)
To: David Marchand
Cc: dev, Gregory Etelson, stable, Thomas Monjalon, Kevin Traynor,
Luca Boccassi
David Marchand, Apr 25, 2026 at 10:40:
> On Thu, 23 Apr 2026 at 13:25, Robin Jarry <rjarry@redhat.com> wrote:
>> diff --git a/lib/net/rte_net.c b/lib/net/rte_net.c
>> index 458b4814a9c9..a871318b21c2 100644
>> --- a/lib/net/rte_net.c
>> +++ b/lib/net/rte_net.c
>> @@ -357,12 +357,14 @@ uint32_t rte_net_get_ptype(const struct rte_mbuf *m,
>> const struct rte_vlan_hdr *vh;
>> struct rte_vlan_hdr vh_copy;
>>
>> + if (vlan_depth == 0) {
>> + pkt_type =
>> + proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN) ?
>> + RTE_PTYPE_L2_ETHER_VLAN :
>> + RTE_PTYPE_L2_ETHER_QINQ;
>> + }
>
> This code is becoming too complex.
> The original usecase with more than 2 stacked vlan is a bit strange,
> but the max depth limit seems just arbitrary (why 8?).
> We have clear boundaries, with the size of the packet (see below,
> check on vh == NULL).
>
> The offending commit also allows stacking mpls on top of vlan, without
> mentioning it.
> I think we want this behavior, but still was it intended?
>
> In the end, reverting the previous fix then just advancing off and
> breaking once proto is not a vlan/qinq type gives a much simpler fix
> when compared to v25.11.
>
> (ignoring indent changes with -w)
>
> $ git diff -w v25.11 lib/net/rte_net.c
> diff --git a/lib/net/rte_net.c b/lib/net/rte_net.c
> index c70b57fdc0..f58d699c83 100644
> --- a/lib/net/rte_net.c
> +++ b/lib/net/rte_net.c
> @@ -349,30 +349,28 @@ uint32_t rte_net_get_ptype(const struct rte_mbuf *m,
> if (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4))
> goto l3; /* fast path if packet is IPv4 */
>
> - if (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN)) {
> + if (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN) ||
> + proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_QINQ)) {
> const struct rte_vlan_hdr *vh;
> struct rte_vlan_hdr vh_copy;
>
> + if (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN))
> pkt_type = RTE_PTYPE_L2_ETHER_VLAN;
> + else
> + pkt_type = RTE_PTYPE_L2_ETHER_QINQ;
> +
> + do {
> vh = rte_pktmbuf_read(m, off, sizeof(*vh), &vh_copy);
> if (unlikely(vh == NULL))
> return pkt_type;
> off += sizeof(*vh);
> hdr_lens->l2_len += sizeof(*vh);
> proto = vh->eth_proto;
> - } else if (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_QINQ)) {
> - const struct rte_vlan_hdr *vh;
> - struct rte_vlan_hdr vh_copy;
> + } while (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN) ||
> + proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_QINQ));
> + }
>
> - pkt_type = RTE_PTYPE_L2_ETHER_QINQ;
> - vh = rte_pktmbuf_read(m, off + sizeof(*vh), sizeof(*vh),
> - &vh_copy);
> - if (unlikely(vh == NULL))
> - return pkt_type;
> - off += 2 * sizeof(*vh);
> - hdr_lens->l2_len += 2 * sizeof(*vh);
> - proto = vh->eth_proto;
> - } else if ((proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_MPLS)) ||
> + if ((proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_MPLS)) ||
> (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_MPLSM))) {
> unsigned int i;
> const struct rte_mpls_hdr *mh;
>
>
> This is untested, but what do you think?
That looks correct. But you don't impose a limit in the number of VLANs?
I don't see any good reason to support more than 2 stacked tags.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH dpdk v4] net: fix VLAN packet type
2026-04-27 10:47 ` Robin Jarry
@ 2026-04-27 15:53 ` Thomas Monjalon
2026-04-30 10:12 ` David Marchand
0 siblings, 1 reply; 19+ messages in thread
From: Thomas Monjalon @ 2026-04-27 15:53 UTC (permalink / raw)
To: David Marchand, Robin Jarry
Cc: dev, Gregory Etelson, stable, Kevin Traynor, Luca Boccassi
27/04/2026 12:47, Robin Jarry:
> David Marchand, Apr 25, 2026 at 10:40:
> > On Thu, 23 Apr 2026 at 13:25, Robin Jarry <rjarry@redhat.com> wrote:
> > This is untested, but what do you think?
>
> That looks correct. But you don't impose a limit in the number of VLANs?
>
> I don't see any good reason to support more than 2 stacked tags.
Look in the mailing list.
I remember it was to limit a risk of infinite loop.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH dpdk v4] net: fix VLAN packet type
2026-04-27 15:53 ` Thomas Monjalon
@ 2026-04-30 10:12 ` David Marchand
2026-04-30 11:06 ` Morten Brørup
0 siblings, 1 reply; 19+ messages in thread
From: David Marchand @ 2026-04-30 10:12 UTC (permalink / raw)
To: Thomas Monjalon
Cc: Robin Jarry, dev, Gregory Etelson, stable, Kevin Traynor,
Luca Boccassi
On Mon, 27 Apr 2026 at 17:53, Thomas Monjalon <thomas@monjalon.net> wrote:
>
> 27/04/2026 12:47, Robin Jarry:
> > David Marchand, Apr 25, 2026 at 10:40:
> > > On Thu, 23 Apr 2026 at 13:25, Robin Jarry <rjarry@redhat.com> wrote:
> > > This is untested, but what do you think?
> >
> > That looks correct. But you don't impose a limit in the number of VLANs?
> >
> > I don't see any good reason to support more than 2 stacked tags.
>
> Look in the mailing list.
> I remember it was to limit a risk of infinite loop.
Defensive programming (iiuc why it was introduced) seems a bad idea.
It imposes a restriction for no good reason.
The same can be achieved by checking the offset vs packet length
(rte_pktmbuf_read), and no arbitrary limit needed.
I am for simply reverting the patch in LTS releases.
We can come up with a new change in main for Gregory usecase, but it
does not seem like something we want to backport in LTS.
--
David Marchand
^ permalink raw reply [flat|nested] 19+ messages in thread
* RE: [PATCH dpdk v4] net: fix VLAN packet type
2026-04-30 10:12 ` David Marchand
@ 2026-04-30 11:06 ` Morten Brørup
0 siblings, 0 replies; 19+ messages in thread
From: Morten Brørup @ 2026-04-30 11:06 UTC (permalink / raw)
To: dev
Cc: Robin Jarry, Gregory Etelson, stable, Kevin Traynor,
Luca Boccassi, David Marchand, Thomas Monjalon
> From: David Marchand [mailto:david.marchand@redhat.com]
> Sent: Thursday, 30 April 2026 12.12
>
> On Mon, 27 Apr 2026 at 17:53, Thomas Monjalon <thomas@monjalon.net>
> wrote:
> >
> > 27/04/2026 12:47, Robin Jarry:
> > > David Marchand, Apr 25, 2026 at 10:40:
> > > > On Thu, 23 Apr 2026 at 13:25, Robin Jarry <rjarry@redhat.com>
> wrote:
> > > > This is untested, but what do you think?
> > >
> > > That looks correct. But you don't impose a limit in the number of
> VLANs?
> > >
> > > I don't see any good reason to support more than 2 stacked tags.
> >
> > Look in the mailing list.
> > I remember it was to limit a risk of infinite loop.
>
> Defensive programming (iiuc why it was introduced) seems a bad idea.
> It imposes a restriction for no good reason.
> The same can be achieved by checking the offset vs packet length
> (rte_pktmbuf_read), and no arbitrary limit needed.
>
> I am for simply reverting the patch in LTS releases.
> We can come up with a new change in main for Gregory usecase, but it
> does not seem like something we want to backport in LTS.
>
I just took a look at rte_net_get_ptype().
The MPLS parser is also broken.
It simply skips 5 MPLS labels without checking if they are MPLS labels at all.
And there's no PPPoE parser.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH dpdk v4] net: fix VLAN packet type
2026-04-25 8:40 ` David Marchand
2026-04-27 10:47 ` Robin Jarry
@ 2026-05-15 11:17 ` Kevin Traynor
1 sibling, 0 replies; 19+ messages in thread
From: Kevin Traynor @ 2026-05-15 11:17 UTC (permalink / raw)
To: David Marchand, Robin Jarry
Cc: dev, Gregory Etelson, stable, Thomas Monjalon, Luca Boccassi
On 4/25/26 10:40 AM, David Marchand wrote:
> On Thu, 23 Apr 2026 at 13:25, Robin Jarry <rjarry@redhat.com> wrote:
>> diff --git a/lib/net/rte_net.c b/lib/net/rte_net.c
>> index 458b4814a9c9..a871318b21c2 100644
>> --- a/lib/net/rte_net.c
>> +++ b/lib/net/rte_net.c
>> @@ -357,12 +357,14 @@ uint32_t rte_net_get_ptype(const struct rte_mbuf *m,
>> const struct rte_vlan_hdr *vh;
>> struct rte_vlan_hdr vh_copy;
>>
>> + if (vlan_depth == 0) {
>> + pkt_type =
>> + proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN) ?
>> + RTE_PTYPE_L2_ETHER_VLAN :
>> + RTE_PTYPE_L2_ETHER_QINQ;
>> + }
>
> This code is becoming too complex.
> The original usecase with more than 2 stacked vlan is a bit strange,
> but the max depth limit seems just arbitrary (why 8?).
> We have clear boundaries, with the size of the packet (see below,
> check on vh == NULL).
>
> The offending commit also allows stacking mpls on top of vlan, without
> mentioning it.
> I think we want this behavior, but still was it intended?
>
> In the end, reverting the previous fix then just advancing off and
> breaking once proto is not a vlan/qinq type gives a much simpler fix
> when compared to v25.11.
>
> (ignoring indent changes with -w)
>
> $ git diff -w v25.11 lib/net/rte_net.c
> diff --git a/lib/net/rte_net.c b/lib/net/rte_net.c
> index c70b57fdc0..f58d699c83 100644
> --- a/lib/net/rte_net.c
> +++ b/lib/net/rte_net.c
> @@ -349,30 +349,28 @@ uint32_t rte_net_get_ptype(const struct rte_mbuf *m,
> if (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4))
> goto l3; /* fast path if packet is IPv4 */
>
> - if (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN)) {
> + if (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN) ||
> + proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_QINQ)) {
> const struct rte_vlan_hdr *vh;
> struct rte_vlan_hdr vh_copy;
>
> + if (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN))
> pkt_type = RTE_PTYPE_L2_ETHER_VLAN;
> + else
> + pkt_type = RTE_PTYPE_L2_ETHER_QINQ;
> +
> + do {
> vh = rte_pktmbuf_read(m, off, sizeof(*vh), &vh_copy);
> if (unlikely(vh == NULL))
> return pkt_type;
> off += sizeof(*vh);
> hdr_lens->l2_len += sizeof(*vh);
> proto = vh->eth_proto;
> - } else if (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_QINQ)) {
> - const struct rte_vlan_hdr *vh;
> - struct rte_vlan_hdr vh_copy;
> + } while (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN) ||
> + proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_QINQ));
> + }
>
> - pkt_type = RTE_PTYPE_L2_ETHER_QINQ;
> - vh = rte_pktmbuf_read(m, off + sizeof(*vh), sizeof(*vh),
> - &vh_copy);
> - if (unlikely(vh == NULL))
> - return pkt_type;
> - off += 2 * sizeof(*vh);
> - hdr_lens->l2_len += 2 * sizeof(*vh);
> - proto = vh->eth_proto;
> - } else if ((proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_MPLS)) ||
> + if ((proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_MPLS)) ||
> (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_MPLSM))) {
> unsigned int i;
> const struct rte_mpls_hdr *mh;
>
>
> This is untested, but what do you think?
>
>
This version LGTM. Maybe we should consider returning NULL on invalid
packets
^ permalink raw reply [flat|nested] 19+ messages in thread
end of thread, other threads:[~2026-05-15 11:17 UTC | newest]
Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-22 10:28 [PATCH dpdk] net: fix L2 ptype assignment in VLAN loop Robin Jarry
2026-04-22 10:35 ` Robin Jarry
2026-04-22 10:38 ` [PATCH dpdk v2] " Robin Jarry
2026-04-22 13:16 ` Thomas Monjalon
2026-04-22 13:18 ` Robin Jarry
2026-04-22 13:23 ` David Marchand
2026-04-22 13:32 ` [PATCH dpdk v3] net: fix VLAN packet type Robin Jarry
2026-04-23 9:19 ` Kevin Traynor
2026-04-23 9:49 ` Robin Jarry
2026-04-23 10:59 ` Kevin Traynor
2026-04-23 11:11 ` Robin Jarry
2026-04-23 11:24 ` [PATCH dpdk v4] " Robin Jarry
2026-04-24 16:18 ` Kevin Traynor
2026-04-25 8:40 ` David Marchand
2026-04-27 10:47 ` Robin Jarry
2026-04-27 15:53 ` Thomas Monjalon
2026-04-30 10:12 ` David Marchand
2026-04-30 11:06 ` Morten Brørup
2026-05-15 11:17 ` Kevin Traynor
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox