Skip to content

Commit c3df39a

Browse files
DongseokYikuba-moo
authored andcommitted
udp: ipv4: manipulate network header of NATed UDP GRO fraglist
UDP/IP header of UDP GROed frag_skbs are not updated even after NAT forwarding. Only the header of head_skb from ip_finish_output_gso -> skb_gso_segment is updated but following frag_skbs are not updated. A call path skb_mac_gso_segment -> inet_gso_segment -> udp4_ufo_fragment -> __udp_gso_segment -> __udp_gso_segment_list does not try to update UDP/IP header of the segment list but copy only the MAC header. Update port, addr and check of each skb of the segment list in __udp_gso_segment_list. It covers both SNAT and DNAT. Fixes: 9fd1ff5 (udp: Support UDP fraglist GRO/GSO.) Signed-off-by: Dongseok Yi <dseok.yi@samsung.com> Acked-by: Steffen Klassert <steffen.klassert@secunet.com> Link: https://lore.kernel.org/r/1611962007-80092-1-git-send-email-dseok.yi@samsung.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
1 parent 28e104d commit c3df39a

3 files changed

Lines changed: 66 additions & 7 deletions

File tree

include/net/udp.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ struct sk_buff *udp_gro_receive(struct list_head *head, struct sk_buff *skb,
178178
int udp_gro_complete(struct sk_buff *skb, int nhoff, udp_lookup_t lookup);
179179

180180
struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
181-
netdev_features_t features);
181+
netdev_features_t features, bool is_ipv6);
182182

183183
static inline struct udphdr *udp_gro_udphdr(struct sk_buff *skb)
184184
{

net/ipv4/udp_offload.c

Lines changed: 64 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -187,8 +187,67 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
187187
}
188188
EXPORT_SYMBOL(skb_udp_tunnel_segment);
189189

190+
static void __udpv4_gso_segment_csum(struct sk_buff *seg,
191+
__be32 *oldip, __be32 *newip,
192+
__be16 *oldport, __be16 *newport)
193+
{
194+
struct udphdr *uh;
195+
struct iphdr *iph;
196+
197+
if (*oldip == *newip && *oldport == *newport)
198+
return;
199+
200+
uh = udp_hdr(seg);
201+
iph = ip_hdr(seg);
202+
203+
if (uh->check) {
204+
inet_proto_csum_replace4(&uh->check, seg, *oldip, *newip,
205+
true);
206+
inet_proto_csum_replace2(&uh->check, seg, *oldport, *newport,
207+
false);
208+
if (!uh->check)
209+
uh->check = CSUM_MANGLED_0;
210+
}
211+
*oldport = *newport;
212+
213+
csum_replace4(&iph->check, *oldip, *newip);
214+
*oldip = *newip;
215+
}
216+
217+
static struct sk_buff *__udpv4_gso_segment_list_csum(struct sk_buff *segs)
218+
{
219+
struct sk_buff *seg;
220+
struct udphdr *uh, *uh2;
221+
struct iphdr *iph, *iph2;
222+
223+
seg = segs;
224+
uh = udp_hdr(seg);
225+
iph = ip_hdr(seg);
226+
227+
if ((udp_hdr(seg)->dest == udp_hdr(seg->next)->dest) &&
228+
(udp_hdr(seg)->source == udp_hdr(seg->next)->source) &&
229+
(ip_hdr(seg)->daddr == ip_hdr(seg->next)->daddr) &&
230+
(ip_hdr(seg)->saddr == ip_hdr(seg->next)->saddr))
231+
return segs;
232+
233+
while ((seg = seg->next)) {
234+
uh2 = udp_hdr(seg);
235+
iph2 = ip_hdr(seg);
236+
237+
__udpv4_gso_segment_csum(seg,
238+
&iph2->saddr, &iph->saddr,
239+
&uh2->source, &uh->source);
240+
__udpv4_gso_segment_csum(seg,
241+
&iph2->daddr, &iph->daddr,
242+
&uh2->dest, &uh->dest);
243+
}
244+
245+
return segs;
246+
}
247+
190248
static struct sk_buff *__udp_gso_segment_list(struct sk_buff *skb,
191-
netdev_features_t features)
249+
netdev_features_t features,
250+
bool is_ipv6)
192251
{
193252
unsigned int mss = skb_shinfo(skb)->gso_size;
194253

@@ -198,11 +257,11 @@ static struct sk_buff *__udp_gso_segment_list(struct sk_buff *skb,
198257

199258
udp_hdr(skb)->len = htons(sizeof(struct udphdr) + mss);
200259

201-
return skb;
260+
return is_ipv6 ? skb : __udpv4_gso_segment_list_csum(skb);
202261
}
203262

204263
struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
205-
netdev_features_t features)
264+
netdev_features_t features, bool is_ipv6)
206265
{
207266
struct sock *sk = gso_skb->sk;
208267
unsigned int sum_truesize = 0;
@@ -214,7 +273,7 @@ struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
214273
__be16 newlen;
215274

216275
if (skb_shinfo(gso_skb)->gso_type & SKB_GSO_FRAGLIST)
217-
return __udp_gso_segment_list(gso_skb, features);
276+
return __udp_gso_segment_list(gso_skb, features, is_ipv6);
218277

219278
mss = skb_shinfo(gso_skb)->gso_size;
220279
if (gso_skb->len <= sizeof(*uh) + mss)
@@ -328,7 +387,7 @@ static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
328387
goto out;
329388

330389
if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4)
331-
return __udp_gso_segment(skb, features);
390+
return __udp_gso_segment(skb, features, false);
332391

333392
mss = skb_shinfo(skb)->gso_size;
334393
if (unlikely(skb->len <= mss))

net/ipv6/udp_offload.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
4242
goto out;
4343

4444
if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4)
45-
return __udp_gso_segment(skb, features);
45+
return __udp_gso_segment(skb, features, true);
4646

4747
mss = skb_shinfo(skb)->gso_size;
4848
if (unlikely(skb->len <= mss))

0 commit comments

Comments
 (0)