Skip to content

Commit b936a9b

Browse files
nbd168kuba-moo
authored andcommitted
net: ipv6: fix UDPv6 GSO segmentation with NAT
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]>
1 parent 1e0bff3 commit b936a9b

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
@@ -247,6 +247,62 @@ static struct sk_buff *__udpv4_gso_segment_list_csum(struct sk_buff *segs)
247247
return segs;
248248
}
249249

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

260316
udp_hdr(skb)->len = htons(sizeof(struct udphdr) + mss);
261317

262-
return is_ipv6 ? skb : __udpv4_gso_segment_list_csum(skb);
318+
if (is_ipv6)
319+
return __udpv6_gso_segment_list_csum(skb);
320+
else
321+
return __udpv4_gso_segment_list_csum(skb);
263322
}
264323

265324
struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,

0 commit comments

Comments
 (0)