* [PATCH] app/testpmd: fix VXLAN-GPE and GENEVE custom port parsing
@ 2026-11-05 7:31 Maayan Kashani
2026-11-09 18:36 ` Stephen Hemminger
2026-11-10 7:41 ` [PATCH v2] " Maayan Kashani
0 siblings, 2 replies; 3+ messages in thread
From: Maayan Kashani @ 2026-11-05 7:31 UTC (permalink / raw)
To: dev; +Cc: aman.deep.singh, haijie1, rasland, Maayan Kashani, stable
The csum forwarding engine parses packets with rte_net_get_ptype(),
which recognizes VXLAN-GPE and GENEVE tunnels only on their default UDP
ports. Since the switch to that API, --vxlan-gpe-port and
--geneve-parsed-port no longer take effect: a packet whose outer UDP
destination port matches a configured non-default port is not detected
as a tunnel, so tunnel Tx flags, outer checksum handling and tunnel TSO
are skipped.
When parse-tunnel is enabled and the outer UDP port matches one of those
options, temporarily present the corresponding default port to
rte_net_get_ptype() and restore the original port afterwards. This reuses
the existing parser without a new library API. Plain VXLAN is unchanged:
the csum engine has always recognized it only on RTE_VXLAN_DEFAULT_PORT,
and rx_vxlan_port configures the device filter rather than this parser.
Only process inner checksums when the parser identified an inner IP
header and all declared inner headers fit in the packet. This preserves
tunnel forwarding for valid non-IP payloads and avoids accessing
truncated inner headers on both default and configured tunnel ports.
Fixes: 76730c7b9b5a ("app/testpmd: use packet type parsing API")
Cc: stable@dpdk.org
Signed-off-by: Maayan Kashani <mkashani@nvidia.com>
---
app/test-pmd/csumonly.c | 84 ++++++++++++++++++++++++++++++++++++++---
1 file changed, 79 insertions(+), 5 deletions(-)
diff --git a/app/test-pmd/csumonly.c b/app/test-pmd/csumonly.c
index c841651756d..e0ce91fed80 100644
--- a/app/test-pmd/csumonly.c
+++ b/app/test-pmd/csumonly.c
@@ -573,6 +573,69 @@ parse_l4_proto(const struct rte_mbuf *m, uint32_t off, uint32_t ptype, bool in_t
return 0;
}
+/*
+ * rte_net_get_ptype() recognizes VXLAN-GPE and GENEVE only on their default
+ * UDP ports. --vxlan-gpe-port and --geneve-parsed-port select other ports.
+ * When one of those matches, present the default port to the existing parser
+ * and restore the original port afterwards. Plain VXLAN stays on
+ * RTE_VXLAN_DEFAULT_PORT: the csum engine has no equivalent option for it.
+ * If a configured port overlaps a tunnel port already recognized by
+ * rte_net_get_ptype(), keep the library's classification.
+ */
+static uint32_t
+parse_tunnel_udp_custom_port(struct rte_mbuf *m, uint32_t ptype,
+ struct rte_net_hdr_lens *hdr_lens)
+{
+ uint32_t off = hdr_lens->l2_len + hdr_lens->l3_len;
+ struct rte_net_hdr_lens parsed = {0};
+ struct rte_udp_hdr *uh;
+ uint16_t default_port;
+ rte_be16_t saved_port;
+ uint16_t dst_port;
+ uint32_t new_ptype;
+
+ if (rte_pktmbuf_data_len(m) < off + sizeof(*uh))
+ return ptype;
+
+ uh = rte_pktmbuf_mtod_offset(m, struct rte_udp_hdr *, off);
+ dst_port = rte_be_to_cpu_16(uh->dst_port);
+ if (vxlan_gpe_udp_port != RTE_VXLAN_GPE_DEFAULT_PORT &&
+ dst_port == vxlan_gpe_udp_port)
+ default_port = RTE_VXLAN_GPE_DEFAULT_PORT;
+ else if (geneve_udp_port != RTE_GENEVE_DEFAULT_PORT &&
+ dst_port == geneve_udp_port)
+ default_port = RTE_GENEVE_DEFAULT_PORT;
+ else
+ return ptype;
+
+ saved_port = uh->dst_port;
+ uh->dst_port = rte_cpu_to_be_16(default_port);
+ new_ptype = rte_net_get_ptype(m, &parsed, RTE_PTYPE_ALL_MASK);
+ uh->dst_port = saved_port;
+
+ if (RTE_ETH_IS_TUNNEL_PKT(new_ptype) == 0)
+ return ptype;
+
+ *hdr_lens = parsed;
+ return new_ptype;
+}
+
+static bool
+can_process_inner_headers(const struct rte_mbuf *m, uint32_t ptype,
+ const struct rte_net_hdr_lens *hdr_lens)
+{
+ uint32_t headers_len;
+
+ if ((ptype & RTE_PTYPE_INNER_L3_MASK) == 0)
+ return false;
+
+ headers_len = hdr_lens->l2_len + hdr_lens->l3_len +
+ hdr_lens->inner_l2_len + hdr_lens->inner_l3_len +
+ hdr_lens->inner_l4_len;
+
+ return rte_pktmbuf_pkt_len(m) >= headers_len;
+}
+
/*
* Receive a burst of packets, and for each packet:
* - parse packet, and try to recognize a supported packet type (1)
@@ -669,6 +732,7 @@ pkt_burst_checksum_forward(struct fwd_stream *fs)
for (i = 0; i < nb_rx; i++) {
struct rte_net_hdr_lens hdr_lens = {0};
+ bool process_inner = false;
if (likely(i < nb_rx - 1))
rte_prefetch0(rte_pktmbuf_mtod(pkts_burst[i + 1],
@@ -703,6 +767,9 @@ pkt_burst_checksum_forward(struct fwd_stream *fs)
}
ptype = rte_net_get_ptype(m, &hdr_lens, RTE_PTYPE_ALL_MASK);
+ if (txp->parse_tunnel && RTE_ETH_IS_TUNNEL_PKT(ptype) == 0 &&
+ (ptype & RTE_PTYPE_L4_MASK) == RTE_PTYPE_L4_UDP)
+ ptype = parse_tunnel_udp_custom_port(m, ptype, &hdr_lens);
info.l2_len = hdr_lens.l2_len;
info.l3_len = hdr_lens.l3_len;
info.l4_len = hdr_lens.l4_len;
@@ -718,10 +785,16 @@ pkt_burst_checksum_forward(struct fwd_stream *fs)
info.l2_len = hdr_lens.inner_l2_len;
info.l3_len = hdr_lens.inner_l3_len;
info.l4_len = hdr_lens.inner_l4_len;
- eth_hdr = (struct rte_ether_hdr *)((char *)l3_hdr +
- hdr_lens.l3_len + hdr_lens.l4_len + hdr_lens.tunnel_len);
- info.ethertype = get_ethertype_by_ptype(eth_hdr,
- ptype & RTE_PTYPE_INNER_L3_MASK);
+ process_inner = can_process_inner_headers(m, ptype, &hdr_lens);
+ if (process_inner) {
+ eth_hdr = (struct rte_ether_hdr *)((char *)l3_hdr +
+ hdr_lens.l3_len + hdr_lens.l4_len +
+ hdr_lens.tunnel_len);
+ info.ethertype = get_ethertype_by_ptype(eth_hdr,
+ ptype & RTE_PTYPE_INNER_L3_MASK);
+ } else {
+ info.ethertype = 0;
+ }
tx_ol_flags |= get_tunnel_ol_flags_by_ptype(ptype);
}
/* update l3_hdr and outer_l3_hdr if a tunnel was parsed */
@@ -730,7 +803,8 @@ pkt_burst_checksum_forward(struct fwd_stream *fs)
outer_l3_hdr = l3_hdr;
l3_hdr = (char *)l3_hdr + info.outer_l3_len + info.l2_len;
- info.l4_proto = parse_l4_proto(m, l3_off, ptype, true);
+ if (process_inner)
+ info.l4_proto = parse_l4_proto(m, l3_off, ptype, true);
}
/* step 2: depending on user command line configuration,
* recompute checksum either in software or flag the
--
2.25.1
^ permalink raw reply related [flat|nested] 3+ messages in thread* Re: [PATCH] app/testpmd: fix VXLAN-GPE and GENEVE custom port parsing
2026-11-05 7:31 [PATCH] app/testpmd: fix VXLAN-GPE and GENEVE custom port parsing Maayan Kashani
@ 2026-11-09 18:36 ` Stephen Hemminger
2026-11-10 7:41 ` [PATCH v2] " Maayan Kashani
1 sibling, 0 replies; 3+ messages in thread
From: Stephen Hemminger @ 2026-11-09 18:36 UTC (permalink / raw)
To: Maayan Kashani; +Cc: dev, aman.deep.singh, haijie1, rasland, stable
On Thu, 5 Nov 2026 09:31:10 +0200
Maayan Kashani <mkashani@nvidia.com> wrote:
> The csum forwarding engine parses packets with rte_net_get_ptype(),
> which recognizes VXLAN-GPE and GENEVE tunnels only on their default UDP
> ports. Since the switch to that API, --vxlan-gpe-port and
> --geneve-parsed-port no longer take effect: a packet whose outer UDP
> destination port matches a configured non-default port is not detected
> as a tunnel, so tunnel Tx flags, outer checksum handling and tunnel TSO
> are skipped.
>
> When parse-tunnel is enabled and the outer UDP port matches one of those
> options, temporarily present the corresponding default port to
> rte_net_get_ptype() and restore the original port afterwards. This reuses
> the existing parser without a new library API. Plain VXLAN is unchanged:
> the csum engine has always recognized it only on RTE_VXLAN_DEFAULT_PORT,
> and rx_vxlan_port configures the device filter rather than this parser.
>
> Only process inner checksums when the parser identified an inner IP
> header and all declared inner headers fit in the packet. This preserves
> tunnel forwarding for valid non-IP payloads and avoids accessing
> truncated inner headers on both default and configured tunnel ports.
>
> Fixes: 76730c7b9b5a ("app/testpmd: use packet type parsing API")
> Cc: stable@dpdk.org
>
> Signed-off-by: Maayan Kashani <mkashani@nvidia.com>
> ---
AI review (run manually with claude) sees a problem here.
Subject: Re: [PATCH] app/testpmd: fix VXLAN-GPE and GENEVE custom port parsing
Applied to main (v26.11-rc2), testpmd builds with -Dwerror=true.
Fixes: tag checked, 76730c7b9b5a is in v25.03, so Cc stable is right.
Warning
can_process_inner_headers() checks the wrong length:
return rte_pktmbuf_pkt_len(m) >= headers_len;
Everything after this check reaches the inner headers through a pointer
into the first segment. l3_hdr is built from eth_hdr (mtod), and
process_inner_cksums() then reads and writes ipv4_hdr->hdr_checksum and
udp_hdr->dgram_cksum through it. For a multi-segment mbuf (scatter Rx)
whose first segment ends inside the inner headers, pkt_len passes and
the access still runs past the segment's data. The commit message says
this avoids accessing truncated inner headers. That is only true if
the check matches how the headers are accessed:
return rte_pktmbuf_data_len(m) >= headers_len;
Info
parse_tunnel_udp_custom_port() runs for every non-tunnel UDP packet
when parse-tunnel is on, even when both ports are at their defaults.
It only returns after it reads the UDP header. Bail out first:
if (vxlan_gpe_udp_port == RTE_VXLAN_GPE_DEFAULT_PORT &&
geneve_udp_port == RTE_GENEVE_DEFAULT_PORT)
return ptype;
Before 76730c7b9b5a, --geneve-parsed-port replaced 6081 (old
parse_geneve() compared against geneve_udp_port only), and run_app.rst
still says it "sets" the port. With this patch, 6081 is still parsed
as GENEVE in addition to the configured port. The same applies to
--vxlan-gpe-port and 4790. If additive is intended, update the
run_app.rst text to say so.
Review-Result: WARNING
^ permalink raw reply [flat|nested] 3+ messages in thread* [PATCH v2] app/testpmd: fix VXLAN-GPE and GENEVE custom port parsing
2026-11-05 7:31 [PATCH] app/testpmd: fix VXLAN-GPE and GENEVE custom port parsing Maayan Kashani
2026-11-09 18:36 ` Stephen Hemminger
@ 2026-11-10 7:41 ` Maayan Kashani
1 sibling, 0 replies; 3+ messages in thread
From: Maayan Kashani @ 2026-11-10 7:41 UTC (permalink / raw)
To: dev; +Cc: aman.deep.singh, haijie1, rasland, Maayan Kashani, stable
The csum forwarding engine parses packets with rte_net_get_ptype(),
which recognizes VXLAN-GPE and GENEVE tunnels only on their default UDP
ports. Since the switch to that API, --vxlan-gpe-port and
--geneve-parsed-port no longer take effect: a packet whose outer UDP
destination port matches a configured non-default port is not detected
as a tunnel, so tunnel Tx flags, outer checksum handling and tunnel TSO
are skipped.
When parse-tunnel is enabled, temporarily map a configured port to the
corresponding default while calling rte_net_get_ptype(), then restore the
packet. If an option replaces a default port, hide that default from the
parser to preserve the options' previous behavior. Plain VXLAN is
unchanged: the csum engine has always recognized it only on
RTE_VXLAN_DEFAULT_PORT, and rx_vxlan_port configures the device filter
rather than this parser.
Only process inner checksums when the parser identified an inner IP
header and all declared inner headers fit in the first segment. This
preserves tunnel forwarding for valid non-IP payloads and avoids
accessing truncated or non-contiguous inner headers on both default and
configured tunnel ports.
Fixes: 76730c7b9b5a ("app/testpmd: use packet type parsing API")
Cc: stable@dpdk.org
Signed-off-by: Maayan Kashani <mkashani@nvidia.com>
---
v2:
- Require inner headers to fit in the first mbuf segment.
- Skip custom-port parsing when both ports use their defaults.
- Preserve replacement semantics for configured tunnel ports.
app/test-pmd/csumonly.c | 90 ++++++++++++++++++++++++++++++++++++++---
1 file changed, 85 insertions(+), 5 deletions(-)
diff --git a/app/test-pmd/csumonly.c b/app/test-pmd/csumonly.c
index c841651756d..52ced5b15cc 100644
--- a/app/test-pmd/csumonly.c
+++ b/app/test-pmd/csumonly.c
@@ -573,6 +573,75 @@ parse_l4_proto(const struct rte_mbuf *m, uint32_t off, uint32_t ptype, bool in_t
return 0;
}
+/*
+ * rte_net_get_ptype() recognizes VXLAN-GPE and GENEVE only on their default
+ * UDP ports. Present the configured port as the corresponding default port,
+ * or hide a default port that has been replaced, while calling the parser.
+ * Plain VXLAN stays on RTE_VXLAN_DEFAULT_PORT: the csum engine has no
+ * equivalent option for it.
+ */
+static uint32_t
+parse_tunnel_udp_custom_port(struct rte_mbuf *m, uint32_t ptype,
+ struct rte_net_hdr_lens *hdr_lens)
+{
+ uint32_t off = hdr_lens->l2_len + hdr_lens->l3_len;
+ struct rte_net_hdr_lens parsed = {0};
+ struct rte_udp_hdr *uh;
+ uint32_t tunnel_type;
+ uint16_t default_port;
+ rte_be16_t saved_port;
+ uint16_t dst_port;
+ uint32_t new_ptype;
+
+ if (vxlan_gpe_udp_port == RTE_VXLAN_GPE_DEFAULT_PORT &&
+ geneve_udp_port == RTE_GENEVE_DEFAULT_PORT)
+ return ptype;
+
+ if (rte_pktmbuf_data_len(m) < off + sizeof(*uh))
+ return ptype;
+
+ uh = rte_pktmbuf_mtod_offset(m, struct rte_udp_hdr *, off);
+ dst_port = rte_be_to_cpu_16(uh->dst_port);
+ tunnel_type = ptype & RTE_PTYPE_TUNNEL_MASK;
+ if (dst_port == vxlan_gpe_udp_port)
+ default_port = RTE_VXLAN_GPE_DEFAULT_PORT;
+ else if (dst_port == geneve_udp_port)
+ default_port = RTE_GENEVE_DEFAULT_PORT;
+ else if ((tunnel_type == RTE_PTYPE_TUNNEL_VXLAN_GPE &&
+ vxlan_gpe_udp_port != RTE_VXLAN_GPE_DEFAULT_PORT) ||
+ (tunnel_type == RTE_PTYPE_TUNNEL_GENEVE &&
+ geneve_udp_port != RTE_GENEVE_DEFAULT_PORT))
+ default_port = 0;
+ else
+ return ptype;
+
+ saved_port = uh->dst_port;
+ if (saved_port == rte_cpu_to_be_16(default_port))
+ return ptype;
+ uh->dst_port = rte_cpu_to_be_16(default_port);
+ new_ptype = rte_net_get_ptype(m, &parsed, RTE_PTYPE_ALL_MASK);
+ uh->dst_port = saved_port;
+
+ *hdr_lens = parsed;
+ return new_ptype;
+}
+
+static bool
+can_process_inner_headers(const struct rte_mbuf *m, uint32_t ptype,
+ const struct rte_net_hdr_lens *hdr_lens)
+{
+ uint32_t headers_len;
+
+ if ((ptype & RTE_PTYPE_INNER_L3_MASK) == 0)
+ return false;
+
+ headers_len = hdr_lens->l2_len + hdr_lens->l3_len +
+ hdr_lens->inner_l2_len + hdr_lens->inner_l3_len +
+ hdr_lens->inner_l4_len;
+
+ return rte_pktmbuf_data_len(m) >= headers_len;
+}
+
/*
* Receive a burst of packets, and for each packet:
* - parse packet, and try to recognize a supported packet type (1)
@@ -669,6 +738,7 @@ pkt_burst_checksum_forward(struct fwd_stream *fs)
for (i = 0; i < nb_rx; i++) {
struct rte_net_hdr_lens hdr_lens = {0};
+ bool process_inner = false;
if (likely(i < nb_rx - 1))
rte_prefetch0(rte_pktmbuf_mtod(pkts_burst[i + 1],
@@ -703,6 +773,9 @@ pkt_burst_checksum_forward(struct fwd_stream *fs)
}
ptype = rte_net_get_ptype(m, &hdr_lens, RTE_PTYPE_ALL_MASK);
+ if (txp->parse_tunnel &&
+ (ptype & RTE_PTYPE_L4_MASK) == RTE_PTYPE_L4_UDP)
+ ptype = parse_tunnel_udp_custom_port(m, ptype, &hdr_lens);
info.l2_len = hdr_lens.l2_len;
info.l3_len = hdr_lens.l3_len;
info.l4_len = hdr_lens.l4_len;
@@ -718,10 +791,16 @@ pkt_burst_checksum_forward(struct fwd_stream *fs)
info.l2_len = hdr_lens.inner_l2_len;
info.l3_len = hdr_lens.inner_l3_len;
info.l4_len = hdr_lens.inner_l4_len;
- eth_hdr = (struct rte_ether_hdr *)((char *)l3_hdr +
- hdr_lens.l3_len + hdr_lens.l4_len + hdr_lens.tunnel_len);
- info.ethertype = get_ethertype_by_ptype(eth_hdr,
- ptype & RTE_PTYPE_INNER_L3_MASK);
+ process_inner = can_process_inner_headers(m, ptype, &hdr_lens);
+ if (process_inner) {
+ eth_hdr = (struct rte_ether_hdr *)((char *)l3_hdr +
+ hdr_lens.l3_len + hdr_lens.l4_len +
+ hdr_lens.tunnel_len);
+ info.ethertype = get_ethertype_by_ptype(eth_hdr,
+ ptype & RTE_PTYPE_INNER_L3_MASK);
+ } else {
+ info.ethertype = 0;
+ }
tx_ol_flags |= get_tunnel_ol_flags_by_ptype(ptype);
}
/* update l3_hdr and outer_l3_hdr if a tunnel was parsed */
@@ -730,7 +809,8 @@ pkt_burst_checksum_forward(struct fwd_stream *fs)
outer_l3_hdr = l3_hdr;
l3_hdr = (char *)l3_hdr + info.outer_l3_len + info.l2_len;
- info.l4_proto = parse_l4_proto(m, l3_off, ptype, true);
+ if (process_inner)
+ info.l4_proto = parse_l4_proto(m, l3_off, ptype, true);
}
/* step 2: depending on user command line configuration,
* recompute checksum either in software or flag the
--
2.25.1
^ permalink raw reply related [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-11-10 7:42 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-05 7:31 [PATCH] app/testpmd: fix VXLAN-GPE and GENEVE custom port parsing Maayan Kashani
2026-11-09 18:36 ` Stephen Hemminger
2026-11-10 7:41 ` [PATCH v2] " Maayan Kashani
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.