Skip to content

Commit 15cb7ea

Browse files
nbd168gregkh
authored andcommitted
net: ipv6: fix UDPv6 GSO segmentation with NAT
[ Upstream commit b936a9b ] If any address or port is changed, update it in all packets and recalculate checksum. Fixes: 9fd1ff5 ("udp: Support UDP fraglist GRO/GSO.") Signed-off-by: Felix Fietkau <[email protected]> Reviewed-by: Willem de Bruijn <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]> Signed-off-by: Sasha Levin <[email protected]>
1 parent 6b73232 commit 15cb7ea

File tree

1 file changed

+60
-1
lines changed

1 file changed

+60
-1
lines changed

net/ipv4/udp_offload.c

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,62 @@ static struct sk_buff *__udpv4_gso_segment_list_csum(struct sk_buff *segs)
245245
return segs;
246246
}
247247

248+
static void __udpv6_gso_segment_csum(struct sk_buff *seg,
249+
struct in6_addr *oldip,
250+
const struct in6_addr *newip,
251+
__be16 *oldport, __be16 newport)
252+
{
253+
struct udphdr *uh = udp_hdr(seg);
254+
255+
if (ipv6_addr_equal(oldip, newip) && *oldport == newport)
256+
return;
257+
258+
if (uh->check) {
259+
inet_proto_csum_replace16(&uh->check, seg, oldip->s6_addr32,
260+
newip->s6_addr32, true);
261+
262+
inet_proto_csum_replace2(&uh->check, seg, *oldport, newport,
263+
false);
264+
if (!uh->check)
265+
uh->check = CSUM_MANGLED_0;
266+
}
267+
268+
*oldip = *newip;
269+
*oldport = newport;
270+
}
271+
272+
static struct sk_buff *__udpv6_gso_segment_list_csum(struct sk_buff *segs)
273+
{
274+
const struct ipv6hdr *iph;
275+
const struct udphdr *uh;
276+
struct ipv6hdr *iph2;
277+
struct sk_buff *seg;
278+
struct udphdr *uh2;
279+
280+
seg = segs;
281+
uh = udp_hdr(seg);
282+
iph = ipv6_hdr(seg);
283+
uh2 = udp_hdr(seg->next);
284+
iph2 = ipv6_hdr(seg->next);
285+
286+
if (!(*(const u32 *)&uh->source ^ *(const u32 *)&uh2->source) &&
287+
ipv6_addr_equal(&iph->saddr, &iph2->saddr) &&
288+
ipv6_addr_equal(&iph->daddr, &iph2->daddr))
289+
return segs;
290+
291+
while ((seg = seg->next)) {
292+
uh2 = udp_hdr(seg);
293+
iph2 = ipv6_hdr(seg);
294+
295+
__udpv6_gso_segment_csum(seg, &iph2->saddr, &iph->saddr,
296+
&uh2->source, uh->source);
297+
__udpv6_gso_segment_csum(seg, &iph2->daddr, &iph->daddr,
298+
&uh2->dest, uh->dest);
299+
}
300+
301+
return segs;
302+
}
303+
248304
static struct sk_buff *__udp_gso_segment_list(struct sk_buff *skb,
249305
netdev_features_t features,
250306
bool is_ipv6)
@@ -257,7 +313,10 @@ static struct sk_buff *__udp_gso_segment_list(struct sk_buff *skb,
257313

258314
udp_hdr(skb)->len = htons(sizeof(struct udphdr) + mss);
259315

260-
return is_ipv6 ? skb : __udpv4_gso_segment_list_csum(skb);
316+
if (is_ipv6)
317+
return __udpv6_gso_segment_list_csum(skb);
318+
else
319+
return __udpv4_gso_segment_list_csum(skb);
261320
}
262321

263322
struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,

0 commit comments

Comments
 (0)