@@ -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+
250306static 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
265324struct sk_buff * __udp_gso_segment (struct sk_buff * gso_skb ,
@@ -273,6 +332,7 @@ struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
273332 bool copy_dtor ;
274333 __sum16 check ;
275334 __be16 newlen ;
335+ int ret = 0 ;
276336
277337 mss = skb_shinfo (gso_skb )-> gso_size ;
278338 if (gso_skb -> len <= sizeof (* uh ) + mss )
@@ -301,6 +361,10 @@ struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
301361 if (skb_pagelen (gso_skb ) - sizeof (* uh ) == skb_shinfo (gso_skb )-> gso_size )
302362 return __udp_gso_segment_list (gso_skb , features , is_ipv6 );
303363
364+ ret = __skb_linearize (gso_skb );
365+ if (ret )
366+ return ERR_PTR (ret );
367+
304368 /* Setup csum, as fraglist skips this in udp4_gro_receive. */
305369 gso_skb -> csum_start = skb_transport_header (gso_skb ) - gso_skb -> head ;
306370 gso_skb -> csum_offset = offsetof(struct udphdr , check );
0 commit comments