|SUCCESS| [v5, 5/5] net: add truncated packet tests for rte_net_get_ptype
dpdklab at iol.unh.edu
dpdklab at iol.unh.edu
Mon May 18 16:58:57 CEST 2026
Test-Label: ai-code-review
Test-Status: SUCCESS
https://dpdk.org/patch/164083
_AI Code Review_
=== Patch Review: series.patch (via Claude) ===
Review date: 2026-05-18
# Review of DPDK Patch Series v5
git sha: 1f250674085aeb4ffd15ac2519a68efc04faf7ac (reverted)
Author: Robin Jarry <rjarry at redhat.com>
Subject: [PATCH dpdk v5 1/5] Revert "net: fix packet type for stacked VLAN"
This series addresses a regression in rte_net_get_ptype() where single
VLAN frames are misidentified as QinQ due to incorrect use of bitwise OR
with packet type flags.
## Patch 1/5: Revert "net: fix packet type for stacked VLAN"
> diff --git a/lib/net/rte_net.c b/lib/net/rte_net.c
> index 458b4814a9c9..c70b57fdc0f8 100644
> --- a/lib/net/rte_net.c
> +++ b/lib/net/rte_net.c
> @@ -320,9 +320,6 @@ rte_net_skip_ip6_ext(uint16_t proto, const struct rte_mbuf *m, uint32_t *off,
> return -1;
> }
>
> -/* limit number of supported VLAN headers */
> -#define RTE_NET_VLAN_MAX_DEPTH 8
> -
Does removing this limit create a potential for excessive iterations in
the VLAN parsing loop?
[ ... ]
> @@ -352,26 +349,30 @@ 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 */
>
> - while (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN) ||
> - proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_QINQ)) {
> + if (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN)) {
> const struct rte_vlan_hdr *vh;
> struct rte_vlan_hdr vh_copy;
>
> - 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;
> + pkt_type = RTE_PTYPE_L2_ETHER_VLAN;
> 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;
>
> - if ((proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_MPLS)) ||
> + pkt_type = RTE_PTYPE_L2_ETHER_QINQ;
> + vh = rte_pktmbuf_read(m, off + sizeof(*vh), sizeof(*vh),
> + &vh_copy);
Does this code handle the first VLAN header correctly? The offset skips
past the first tag without processing it, but we're reading the second
tag and using its eth_proto.
> + 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)) ||
> (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_MPLSM))) {
## Patch 2/5: net: support multiple stacked VLAN tags
> diff --git a/lib/net/rte_net.c b/lib/net/rte_net.c
> index c70b57fdc0f8..d3cded961fb5 100644
> --- a/lib/net/rte_net.c
> +++ b/lib/net/rte_net.c
> @@ -349,29 +349,26 @@ 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))) {
Inconsistent indentation: the continuation line should align with the
opening parenthesis of the outer condition or be indented with an
additional tab.
> const struct rte_vlan_hdr *vh;
> struct rte_vlan_hdr vh_copy;
>
> - pkt_type = RTE_PTYPE_L2_ETHER_VLAN;
> - 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;
> + 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;
> + } while (proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_VLAN) ||
> + proto == rte_cpu_to_be_16(RTE_ETHER_TYPE_QINQ));
Does this loop need bounds checking? Without a maximum depth limit, can
a malformed packet cause excessive iterations?
## Patch 4/5: net: parse L3 protocol after MPLS labels
> diff --git a/lib/net/rte_net.c b/lib/net/rte_net.c
> index d3cded961fb5..3bb5fbc16d43 100644
> --- a/lib/net/rte_net.c
> +++ b/lib/net/rte_net.c
> @@ -371,22 +371,36 @@ uint32_t rte_net_get_ptype(const struct rte_mbuf *m,
>
> } else 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;
> struct rte_mpls_hdr mh_copy;
> + const uint8_t *nimble;
> + uint8_t nimble_copy;
Should this variable be named "nibble" rather than "nimble"?
>
> -#define MAX_MPLS_HDR 5
> - for (i = 0; i < MAX_MPLS_HDR; i++) {
> - mh = rte_pktmbuf_read(m, off + (i * sizeof(*mh)),
> - sizeof(*mh), &mh_copy);
> + pkt_type = RTE_PTYPE_L2_ETHER_MPLS;
> +
> + /* consume all labels until bottom of stack is reached */
> + do {
> + mh = rte_pktmbuf_read(m, off, sizeof(*mh), &mh_copy);
> if (unlikely(mh == NULL))
> return pkt_type;
> - }
> - if (i == MAX_MPLS_HDR)
> + off += sizeof(*mh);
> + hdr_lens->l2_len += sizeof(*mh);
> + } while (!mh->bs);
Does this loop require bounds checking? Can a packet with no bottom of
stack bit set cause infinite iteration?
> +
> + /* try to guess what is the payload based on the first 4 bits */
> + nimble = rte_pktmbuf_read(m, off, sizeof(*nimble), &nimble_copy);
> + if (nimble == NULL)
Implicit comparison: should be "if (nimble == NULL)" is correct per
coding style, but you could also write "if (unlikely(nimble == NULL))".
> return pkt_type;
> - pkt_type = RTE_PTYPE_L2_ETHER_MPLS;
> - hdr_lens->l2_len += (sizeof(*mh) * i);
> - return pkt_type;
> + switch (*nimble & 0xf0) {
> + case 0x40:
> + proto = RTE_BE16(RTE_ETHER_TYPE_IPV4);
> + break;
> + case 0x60:
> + proto = RTE_BE16(RTE_ETHER_TYPE_IPV6);
> + break;
> + default:
> + return pkt_type;
> + }
> }
## Patch 3/5 and 5/5: Unit tests
The test additions look reasonable and follow DPDK test patterns
correctly.
More information about the test-report
mailing list