Skip to content

Commit 0b392b9

Browse files
committed
kernel: fix UDPv6 GSO segmentation with NAT
Fixes issues with rx-gro-list and NAT66 Fixes: openwrt/openwrt#18387 Fixes: openwrt/openwrt#18516 Fixes: openwrt/openwrt#18608 Signed-off-by: Felix Fietkau <nbd@nbd.name> (cherry picked from commit 5501a50)
1 parent 78d517a commit 0b392b9

File tree

1 file changed

+88
-0
lines changed

1 file changed

+88
-0
lines changed
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
From: Felix Fietkau <nbd@nbd.name>
2+
Date: Sat, 26 Apr 2025 17:18:03 +0200
3+
Subject: [PATCH] net: ipv6: fix UDPv6 GSO segmentation with NAT
4+
5+
If any address or port is changed, update it in all packets and recalculate
6+
checksum.
7+
8+
Fixes: 9fd1ff5d2ac7 ("udp: Support UDP fraglist GRO/GSO.")
9+
Signed-off-by: Felix Fietkau <nbd@nbd.name>
10+
---
11+
12+
--- a/net/ipv4/udp_offload.c
13+
+++ b/net/ipv4/udp_offload.c
14+
@@ -247,6 +247,62 @@ static struct sk_buff *__udpv4_gso_segme
15+
return segs;
16+
}
17+
18+
+static void __udpv6_gso_segment_csum(struct sk_buff *seg,
19+
+ struct in6_addr *oldip,
20+
+ const struct in6_addr *newip,
21+
+ __be16 *oldport, __be16 newport)
22+
+{
23+
+ struct udphdr *uh = udp_hdr(seg);
24+
+
25+
+ if (ipv6_addr_equal(oldip, newip) && *oldport == newport)
26+
+ return;
27+
+
28+
+ if (uh->check) {
29+
+ inet_proto_csum_replace16(&uh->check, seg, oldip->s6_addr32,
30+
+ newip->s6_addr32, true);
31+
+
32+
+ inet_proto_csum_replace2(&uh->check, seg, *oldport, newport,
33+
+ false);
34+
+ if (!uh->check)
35+
+ uh->check = CSUM_MANGLED_0;
36+
+ }
37+
+
38+
+ *oldip = *newip;
39+
+ *oldport = newport;
40+
+}
41+
+
42+
+static struct sk_buff *__udpv6_gso_segment_list_csum(struct sk_buff *segs)
43+
+{
44+
+ const struct ipv6hdr *iph;
45+
+ const struct udphdr *uh;
46+
+ struct ipv6hdr *iph2;
47+
+ struct sk_buff *seg;
48+
+ struct udphdr *uh2;
49+
+
50+
+ seg = segs;
51+
+ uh = udp_hdr(seg);
52+
+ iph = ipv6_hdr(seg);
53+
+ uh2 = udp_hdr(seg->next);
54+
+ iph2 = ipv6_hdr(seg->next);
55+
+
56+
+ if (!(*(const u32 *)&uh->source ^ *(const u32 *)&uh2->source) &&
57+
+ ipv6_addr_equal(&iph->saddr, &iph2->saddr) &&
58+
+ ipv6_addr_equal(&iph->daddr, &iph2->daddr))
59+
+ return segs;
60+
+
61+
+ while ((seg = seg->next)) {
62+
+ uh2 = udp_hdr(seg);
63+
+ iph2 = ipv6_hdr(seg);
64+
+
65+
+ __udpv6_gso_segment_csum(seg, &iph2->saddr, &iph->saddr,
66+
+ &uh2->source, uh->source);
67+
+ __udpv6_gso_segment_csum(seg, &iph2->daddr, &iph->daddr,
68+
+ &uh2->dest, uh->dest);
69+
+ }
70+
+
71+
+ return segs;
72+
+}
73+
+
74+
static struct sk_buff *__udp_gso_segment_list(struct sk_buff *skb,
75+
netdev_features_t features,
76+
bool is_ipv6)
77+
@@ -259,7 +315,10 @@ static struct sk_buff *__udp_gso_segment
78+
79+
udp_hdr(skb)->len = htons(sizeof(struct udphdr) + mss);
80+
81+
- return is_ipv6 ? skb : __udpv4_gso_segment_list_csum(skb);
82+
+ if (is_ipv6)
83+
+ return __udpv6_gso_segment_list_csum(skb);
84+
+ else
85+
+ return __udpv4_gso_segment_list_csum(skb);
86+
}
87+
88+
struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,

0 commit comments

Comments
 (0)