[openwrt/openwrt] kernel: fix UDPv6 GSO segmentation with NAT

LEDE Commits lede-commits at lists.infradead.org
Sat Apr 26 10:13:48 PDT 2025


nbd pushed a commit to openwrt/openwrt.git, branch main:
https://git.openwrt.org/5501a502e499e0202bdfd0feebbbedb012d5f82b

commit 5501a502e499e0202bdfd0feebbbedb012d5f82b
Author: Felix Fietkau <nbd at nbd.name>
AuthorDate: Sat Apr 26 19:10:54 2025 +0200

    kernel: fix UDPv6 GSO segmentation with NAT
    
    Fixes issues with rx-gro-list and NAT66
    
    Fixes: https://github.com/openwrt/openwrt/issues/18387
    Fixes: https://github.com/openwrt/openwrt/issues/18516
    Fixes: https://github.com/openwrt/openwrt/issues/18608
    Signed-off-by: Felix Fietkau <nbd at nbd.name>
---
 ...-ipv6-fix-UDPv6-GSO-segmentation-with-NAT.patch | 88 ++++++++++++++++++++++
 1 file changed, 88 insertions(+)

diff --git a/target/linux/generic/pending-6.6/691-net-ipv6-fix-UDPv6-GSO-segmentation-with-NAT.patch b/target/linux/generic/pending-6.6/691-net-ipv6-fix-UDPv6-GSO-segmentation-with-NAT.patch
new file mode 100644
index 0000000000..6bbe2ca880
--- /dev/null
+++ b/target/linux/generic/pending-6.6/691-net-ipv6-fix-UDPv6-GSO-segmentation-with-NAT.patch
@@ -0,0 +1,88 @@
+From: Felix Fietkau <nbd at nbd.name>
+Date: Sat, 26 Apr 2025 17:18:03 +0200
+Subject: [PATCH] net: ipv6: fix UDPv6 GSO segmentation with NAT
+
+If any address or port is changed, update it in all packets and recalculate
+checksum.
+
+Fixes: 9fd1ff5d2ac7 ("udp: Support UDP fraglist GRO/GSO.")
+Signed-off-by: Felix Fietkau <nbd at nbd.name>
+---
+
+--- a/net/ipv4/udp_offload.c
++++ b/net/ipv4/udp_offload.c
+@@ -247,6 +247,62 @@ static struct sk_buff *__udpv4_gso_segme
+ 	return segs;
+ }
+ 
++static void __udpv6_gso_segment_csum(struct sk_buff *seg,
++				     struct in6_addr *oldip,
++				     const struct in6_addr *newip,
++				     __be16 *oldport, __be16 newport)
++{
++	struct udphdr *uh = udp_hdr(seg);
++
++	if (ipv6_addr_equal(oldip, newip) && *oldport == newport)
++		return;
++
++	if (uh->check) {
++		inet_proto_csum_replace16(&uh->check, seg, oldip->s6_addr32,
++					  newip->s6_addr32, true);
++
++		inet_proto_csum_replace2(&uh->check, seg, *oldport, newport,
++					 false);
++		if (!uh->check)
++			uh->check = CSUM_MANGLED_0;
++	}
++
++	*oldip = *newip;
++	*oldport = newport;
++}
++
++static struct sk_buff *__udpv6_gso_segment_list_csum(struct sk_buff *segs)
++{
++	const struct ipv6hdr *iph;
++	const struct udphdr *uh;
++	struct ipv6hdr *iph2;
++	struct sk_buff *seg;
++	struct udphdr *uh2;
++
++	seg = segs;
++	uh = udp_hdr(seg);
++	iph = ipv6_hdr(seg);
++	uh2 = udp_hdr(seg->next);
++	iph2 = ipv6_hdr(seg->next);
++
++	if (!(*(const u32 *)&uh->source ^ *(const u32 *)&uh2->source) &&
++	    ipv6_addr_equal(&iph->saddr, &iph2->saddr) &&
++	    ipv6_addr_equal(&iph->daddr, &iph2->daddr))
++		return segs;
++
++	while ((seg = seg->next)) {
++		uh2 = udp_hdr(seg);
++		iph2 = ipv6_hdr(seg);
++
++		__udpv6_gso_segment_csum(seg, &iph2->saddr, &iph->saddr,
++					 &uh2->source, uh->source);
++		__udpv6_gso_segment_csum(seg, &iph2->daddr, &iph->daddr,
++					 &uh2->dest, uh->dest);
++	}
++
++	return segs;
++}
++
+ static struct sk_buff *__udp_gso_segment_list(struct sk_buff *skb,
+ 					      netdev_features_t features,
+ 					      bool is_ipv6)
+@@ -259,7 +315,10 @@ static struct sk_buff *__udp_gso_segment
+ 
+ 	udp_hdr(skb)->len = htons(sizeof(struct udphdr) + mss);
+ 
+-	return is_ipv6 ? skb : __udpv4_gso_segment_list_csum(skb);
++	if (is_ipv6)
++		return __udpv6_gso_segment_list_csum(skb);
++	else
++		return __udpv4_gso_segment_list_csum(skb);
+ }
+ 
+ struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,




More information about the lede-commits mailing list