aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Dumazet <edumazet@google.com>2013-01-25 15:34:37 -0500
committerDavid S. Miller <davem@davemloft.net>2013-01-28 00:27:15 -0500
commitcef401de7be8c4e155c6746bfccf721a4fa5fab9 (patch)
treeaf082329bf0613ed9de5b9575e3f94f3f03b77ec
parent61550022b9586972082904b80de26a464c558437 (diff)
net: fix possible wrong checksum generation
Pravin Shelar mentioned that GSO could potentially generate wrong TX checksum if skb has fragments that are overwritten by the user between the checksum computation and transmit. He suggested to linearize skbs but this extra copy can be avoided for normal tcp skbs cooked by tcp_sendmsg(). This patch introduces a new SKB_GSO_SHARED_FRAG flag, set in skb_shinfo(skb)->gso_type if at least one frag can be modified by the user. Typical sources of such possible overwrites are {vm}splice(), sendfile(), and macvtap/tun/virtio_net drivers. Tested: $ netperf -H 7.7.8.84 MIGRATED TCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 7.7.8.84 () port 0 AF_INET Recv Send Send Socket Socket Message Elapsed Size Size Size Time Throughput bytes bytes bytes secs. 10^6bits/sec 87380 16384 16384 10.00 3959.52 $ netperf -H 7.7.8.84 -t TCP_SENDFILE TCP SENDFILE TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to 7.7.8.84 () port 0 AF_INET Recv Send Send Socket Socket Message Elapsed Size Size Size Time Throughput bytes bytes bytes secs. 10^6bits/sec 87380 16384 16384 10.00 3216.80 Performance of the SENDFILE is impacted by the extra allocation and copy, and because we use order-0 pages, while the TCP_STREAM uses bigger pages. Reported-by: Pravin Shelar <pshelar@nicira.com> Signed-off-by: Eric Dumazet <edumazet@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/macvtap.c3
-rw-r--r--drivers/net/tun.c12
-rw-r--r--drivers/net/virtio_net.c12
-rw-r--r--include/linux/skbuff.h19
-rw-r--r--net/core/dev.c9
-rw-r--r--net/core/skbuff.c4
-rw-r--r--net/ipv4/af_inet.c1
-rw-r--r--net/ipv4/ip_gre.c4
-rw-r--r--net/ipv4/ipip.c4
-rw-r--r--net/ipv4/tcp.c3
-rw-r--r--net/ipv4/tcp_input.c4
-rw-r--r--net/ipv4/tcp_output.c4
-rw-r--r--net/ipv6/ip6_offload.c1
13 files changed, 65 insertions, 15 deletions
diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c
index 0f0f9ce3a776..b181dfb3d6d6 100644
--- a/drivers/net/macvtap.c
+++ b/drivers/net/macvtap.c
@@ -543,6 +543,7 @@ static int zerocopy_sg_from_iovec(struct sk_buff *skb, const struct iovec *from,
543 skb->data_len += len; 543 skb->data_len += len;
544 skb->len += len; 544 skb->len += len;
545 skb->truesize += truesize; 545 skb->truesize += truesize;
546 skb_shinfo(skb)->gso_type |= SKB_GSO_SHARED_FRAG;
546 atomic_add(truesize, &skb->sk->sk_wmem_alloc); 547 atomic_add(truesize, &skb->sk->sk_wmem_alloc);
547 while (len) { 548 while (len) {
548 int off = base & ~PAGE_MASK; 549 int off = base & ~PAGE_MASK;
@@ -598,7 +599,7 @@ static int macvtap_skb_from_vnet_hdr(struct sk_buff *skb,
598 599
599 if (vnet_hdr->gso_type != VIRTIO_NET_HDR_GSO_NONE) { 600 if (vnet_hdr->gso_type != VIRTIO_NET_HDR_GSO_NONE) {
600 skb_shinfo(skb)->gso_size = vnet_hdr->gso_size; 601 skb_shinfo(skb)->gso_size = vnet_hdr->gso_size;
601 skb_shinfo(skb)->gso_type = gso_type; 602 skb_shinfo(skb)->gso_type |= gso_type;
602 603
603 /* Header must be checked, and gso_segs computed. */ 604 /* Header must be checked, and gso_segs computed. */
604 skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY; 605 skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY;
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index c81680dc10eb..293ce8dfc9e6 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -1005,6 +1005,7 @@ static int zerocopy_sg_from_iovec(struct sk_buff *skb, const struct iovec *from,
1005 skb->data_len += len; 1005 skb->data_len += len;
1006 skb->len += len; 1006 skb->len += len;
1007 skb->truesize += truesize; 1007 skb->truesize += truesize;
1008 skb_shinfo(skb)->gso_type |= SKB_GSO_SHARED_FRAG;
1008 atomic_add(truesize, &skb->sk->sk_wmem_alloc); 1009 atomic_add(truesize, &skb->sk->sk_wmem_alloc);
1009 while (len) { 1010 while (len) {
1010 int off = base & ~PAGE_MASK; 1011 int off = base & ~PAGE_MASK;
@@ -1150,16 +1151,18 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
1150 } 1151 }
1151 1152
1152 if (gso.gso_type != VIRTIO_NET_HDR_GSO_NONE) { 1153 if (gso.gso_type != VIRTIO_NET_HDR_GSO_NONE) {
1154 unsigned short gso_type = 0;
1155
1153 pr_debug("GSO!\n"); 1156 pr_debug("GSO!\n");
1154 switch (gso.gso_type & ~VIRTIO_NET_HDR_GSO_ECN) { 1157 switch (gso.gso_type & ~VIRTIO_NET_HDR_GSO_ECN) {
1155 case VIRTIO_NET_HDR_GSO_TCPV4: 1158 case VIRTIO_NET_HDR_GSO_TCPV4:
1156 skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4; 1159 gso_type = SKB_GSO_TCPV4;
1157 break; 1160 break;
1158 case VIRTIO_NET_HDR_GSO_TCPV6: 1161 case VIRTIO_NET_HDR_GSO_TCPV6:
1159 skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6; 1162 gso_type = SKB_GSO_TCPV6;
1160 break; 1163 break;
1161 case VIRTIO_NET_HDR_GSO_UDP: 1164 case VIRTIO_NET_HDR_GSO_UDP:
1162 skb_shinfo(skb)->gso_type = SKB_GSO_UDP; 1165 gso_type = SKB_GSO_UDP;
1163 break; 1166 break;
1164 default: 1167 default:
1165 tun->dev->stats.rx_frame_errors++; 1168 tun->dev->stats.rx_frame_errors++;
@@ -1168,9 +1171,10 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,
1168 } 1171 }
1169 1172
1170 if (gso.gso_type & VIRTIO_NET_HDR_GSO_ECN) 1173 if (gso.gso_type & VIRTIO_NET_HDR_GSO_ECN)
1171 skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ECN; 1174 gso_type |= SKB_GSO_TCP_ECN;
1172 1175
1173 skb_shinfo(skb)->gso_size = gso.gso_size; 1176 skb_shinfo(skb)->gso_size = gso.gso_size;
1177 skb_shinfo(skb)->gso_type |= gso_type;
1174 if (skb_shinfo(skb)->gso_size == 0) { 1178 if (skb_shinfo(skb)->gso_size == 0) {
1175 tun->dev->stats.rx_frame_errors++; 1179 tun->dev->stats.rx_frame_errors++;
1176 kfree_skb(skb); 1180 kfree_skb(skb);
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 701408a1ded6..58914c8ea68f 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -220,6 +220,7 @@ static void set_skb_frag(struct sk_buff *skb, struct page *page,
220 skb->len += size; 220 skb->len += size;
221 skb->truesize += PAGE_SIZE; 221 skb->truesize += PAGE_SIZE;
222 skb_shinfo(skb)->nr_frags++; 222 skb_shinfo(skb)->nr_frags++;
223 skb_shinfo(skb)->gso_type |= SKB_GSO_SHARED_FRAG;
223 *len -= size; 224 *len -= size;
224} 225}
225 226
@@ -379,16 +380,18 @@ static void receive_buf(struct receive_queue *rq, void *buf, unsigned int len)
379 ntohs(skb->protocol), skb->len, skb->pkt_type); 380 ntohs(skb->protocol), skb->len, skb->pkt_type);
380 381
381 if (hdr->hdr.gso_type != VIRTIO_NET_HDR_GSO_NONE) { 382 if (hdr->hdr.gso_type != VIRTIO_NET_HDR_GSO_NONE) {
383 unsigned short gso_type = 0;
384
382 pr_debug("GSO!\n"); 385 pr_debug("GSO!\n");
383 switch (hdr->hdr.gso_type & ~VIRTIO_NET_HDR_GSO_ECN) { 386 switch (hdr->hdr.gso_type & ~VIRTIO_NET_HDR_GSO_ECN) {
384 case VIRTIO_NET_HDR_GSO_TCPV4: 387 case VIRTIO_NET_HDR_GSO_TCPV4:
385 skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4; 388 gso_type = SKB_GSO_TCPV4;
386 break; 389 break;
387 case VIRTIO_NET_HDR_GSO_UDP: 390 case VIRTIO_NET_HDR_GSO_UDP:
388 skb_shinfo(skb)->gso_type = SKB_GSO_UDP; 391 gso_type = SKB_GSO_UDP;
389 break; 392 break;
390 case VIRTIO_NET_HDR_GSO_TCPV6: 393 case VIRTIO_NET_HDR_GSO_TCPV6:
391 skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6; 394 gso_type = SKB_GSO_TCPV6;
392 break; 395 break;
393 default: 396 default:
394 net_warn_ratelimited("%s: bad gso type %u.\n", 397 net_warn_ratelimited("%s: bad gso type %u.\n",
@@ -397,7 +400,7 @@ static void receive_buf(struct receive_queue *rq, void *buf, unsigned int len)
397 } 400 }
398 401
399 if (hdr->hdr.gso_type & VIRTIO_NET_HDR_GSO_ECN) 402 if (hdr->hdr.gso_type & VIRTIO_NET_HDR_GSO_ECN)
400 skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ECN; 403 gso_type |= SKB_GSO_TCP_ECN;
401 404
402 skb_shinfo(skb)->gso_size = hdr->hdr.gso_size; 405 skb_shinfo(skb)->gso_size = hdr->hdr.gso_size;
403 if (skb_shinfo(skb)->gso_size == 0) { 406 if (skb_shinfo(skb)->gso_size == 0) {
@@ -405,6 +408,7 @@ static void receive_buf(struct receive_queue *rq, void *buf, unsigned int len)
405 goto frame_err; 408 goto frame_err;
406 } 409 }
407 410
411 skb_shinfo(skb)->gso_type |= gso_type;
408 /* Header must be checked, and gso_segs computed. */ 412 /* Header must be checked, and gso_segs computed. */
409 skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY; 413 skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY;
410 skb_shinfo(skb)->gso_segs = 0; 414 skb_shinfo(skb)->gso_segs = 0;
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 8b2256e880e0..0259b719bebf 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -307,6 +307,13 @@ enum {
307 SKB_GSO_TCPV6 = 1 << 4, 307 SKB_GSO_TCPV6 = 1 << 4,
308 308
309 SKB_GSO_FCOE = 1 << 5, 309 SKB_GSO_FCOE = 1 << 5,
310
311 /* This indicates at least one fragment might be overwritten
312 * (as in vmsplice(), sendfile() ...)
313 * If we need to compute a TX checksum, we'll need to copy
314 * all frags to avoid possible bad checksum
315 */
316 SKB_GSO_SHARED_FRAG = 1 << 6,
310}; 317};
311 318
312#if BITS_PER_LONG > 32 319#if BITS_PER_LONG > 32
@@ -2201,6 +2208,18 @@ static inline int skb_linearize(struct sk_buff *skb)
2201} 2208}
2202 2209
2203/** 2210/**
2211 * skb_has_shared_frag - can any frag be overwritten
2212 * @skb: buffer to test
2213 *
2214 * Return true if the skb has at least one frag that might be modified
2215 * by an external entity (as in vmsplice()/sendfile())
2216 */
2217static inline bool skb_has_shared_frag(const struct sk_buff *skb)
2218{
2219 return skb_shinfo(skb)->gso_type & SKB_GSO_SHARED_FRAG;
2220}
2221
2222/**
2204 * skb_linearize_cow - make sure skb is linear and writable 2223 * skb_linearize_cow - make sure skb is linear and writable
2205 * @skb: buffer to process 2224 * @skb: buffer to process
2206 * 2225 *
diff --git a/net/core/dev.c b/net/core/dev.c
index c69cd8721b28..a83375d3af72 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -2271,6 +2271,15 @@ int skb_checksum_help(struct sk_buff *skb)
2271 return -EINVAL; 2271 return -EINVAL;
2272 } 2272 }
2273 2273
2274 /* Before computing a checksum, we should make sure no frag could
2275 * be modified by an external entity : checksum could be wrong.
2276 */
2277 if (skb_has_shared_frag(skb)) {
2278 ret = __skb_linearize(skb);
2279 if (ret)
2280 goto out;
2281 }
2282
2274 offset = skb_checksum_start_offset(skb); 2283 offset = skb_checksum_start_offset(skb);
2275 BUG_ON(offset >= skb_headlen(skb)); 2284 BUG_ON(offset >= skb_headlen(skb));
2276 csum = skb_checksum(skb, offset, skb->len - offset, 0); 2285 csum = skb_checksum(skb, offset, skb->len - offset, 0);
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 2568c449fe36..bddc1dd2e7f2 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -2340,6 +2340,8 @@ void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len)
2340{ 2340{
2341 int pos = skb_headlen(skb); 2341 int pos = skb_headlen(skb);
2342 2342
2343 skb_shinfo(skb1)->gso_type = skb_shinfo(skb)->gso_type;
2344
2343 if (len < pos) /* Split line is inside header. */ 2345 if (len < pos) /* Split line is inside header. */
2344 skb_split_inside_header(skb, skb1, len, pos); 2346 skb_split_inside_header(skb, skb1, len, pos);
2345 else /* Second chunk has no header, nothing to copy. */ 2347 else /* Second chunk has no header, nothing to copy. */
@@ -2845,6 +2847,8 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features)
2845 skb_copy_from_linear_data_offset(skb, offset, 2847 skb_copy_from_linear_data_offset(skb, offset,
2846 skb_put(nskb, hsize), hsize); 2848 skb_put(nskb, hsize), hsize);
2847 2849
2850 skb_shinfo(nskb)->gso_type = skb_shinfo(skb)->gso_type;
2851
2848 while (pos < offset + len && i < nfrags) { 2852 while (pos < offset + len && i < nfrags) {
2849 *frag = skb_shinfo(skb)->frags[i]; 2853 *frag = skb_shinfo(skb)->frags[i];
2850 __skb_frag_ref(frag); 2854 __skb_frag_ref(frag);
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index 4b7053919976..49ddca31c4da 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -1306,6 +1306,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb,
1306 SKB_GSO_UDP | 1306 SKB_GSO_UDP |
1307 SKB_GSO_DODGY | 1307 SKB_GSO_DODGY |
1308 SKB_GSO_TCP_ECN | 1308 SKB_GSO_TCP_ECN |
1309 SKB_GSO_SHARED_FRAG |
1309 0))) 1310 0)))
1310 goto out; 1311 goto out;
1311 1312
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index 303012adf9e6..af6be70821c4 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -738,7 +738,7 @@ drop:
738static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) 738static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
739{ 739{
740 struct ip_tunnel *tunnel = netdev_priv(dev); 740 struct ip_tunnel *tunnel = netdev_priv(dev);
741 const struct iphdr *old_iph = ip_hdr(skb); 741 const struct iphdr *old_iph;
742 const struct iphdr *tiph; 742 const struct iphdr *tiph;
743 struct flowi4 fl4; 743 struct flowi4 fl4;
744 u8 tos; 744 u8 tos;
@@ -756,6 +756,8 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev
756 skb_checksum_help(skb)) 756 skb_checksum_help(skb))
757 goto tx_error; 757 goto tx_error;
758 758
759 old_iph = ip_hdr(skb);
760
759 if (dev->type == ARPHRD_ETHER) 761 if (dev->type == ARPHRD_ETHER)
760 IPCB(skb)->flags = 0; 762 IPCB(skb)->flags = 0;
761 763
diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c
index 191fc24a745a..8f024d41eefa 100644
--- a/net/ipv4/ipip.c
+++ b/net/ipv4/ipip.c
@@ -472,7 +472,7 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
472 __be16 df = tiph->frag_off; 472 __be16 df = tiph->frag_off;
473 struct rtable *rt; /* Route to the other host */ 473 struct rtable *rt; /* Route to the other host */
474 struct net_device *tdev; /* Device to other host */ 474 struct net_device *tdev; /* Device to other host */
475 const struct iphdr *old_iph = ip_hdr(skb); 475 const struct iphdr *old_iph;
476 struct iphdr *iph; /* Our new IP header */ 476 struct iphdr *iph; /* Our new IP header */
477 unsigned int max_headroom; /* The extra header space needed */ 477 unsigned int max_headroom; /* The extra header space needed */
478 __be32 dst = tiph->daddr; 478 __be32 dst = tiph->daddr;
@@ -486,6 +486,8 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
486 skb_checksum_help(skb)) 486 skb_checksum_help(skb))
487 goto tx_error; 487 goto tx_error;
488 488
489 old_iph = ip_hdr(skb);
490
489 if (tos & 1) 491 if (tos & 1)
490 tos = old_iph->tos; 492 tos = old_iph->tos;
491 493
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 52271947a471..3ec1f69c5ceb 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -896,6 +896,8 @@ new_segment:
896 skb_fill_page_desc(skb, i, page, offset, copy); 896 skb_fill_page_desc(skb, i, page, offset, copy);
897 } 897 }
898 898
899 skb_shinfo(skb)->gso_type |= SKB_GSO_SHARED_FRAG;
900
899 skb->len += copy; 901 skb->len += copy;
900 skb->data_len += copy; 902 skb->data_len += copy;
901 skb->truesize += copy; 903 skb->truesize += copy;
@@ -3032,6 +3034,7 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb,
3032 SKB_GSO_DODGY | 3034 SKB_GSO_DODGY |
3033 SKB_GSO_TCP_ECN | 3035 SKB_GSO_TCP_ECN |
3034 SKB_GSO_TCPV6 | 3036 SKB_GSO_TCPV6 |
3037 SKB_GSO_SHARED_FRAG |
3035 0) || 3038 0) ||
3036 !(type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)))) 3039 !(type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))))
3037 goto out; 3040 goto out;
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 0905997e5873..492c7cfe1453 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -1240,13 +1240,13 @@ static bool tcp_shifted_skb(struct sock *sk, struct sk_buff *skb,
1240 */ 1240 */
1241 if (!skb_shinfo(prev)->gso_size) { 1241 if (!skb_shinfo(prev)->gso_size) {
1242 skb_shinfo(prev)->gso_size = mss; 1242 skb_shinfo(prev)->gso_size = mss;
1243 skb_shinfo(prev)->gso_type = sk->sk_gso_type; 1243 skb_shinfo(prev)->gso_type |= sk->sk_gso_type;
1244 } 1244 }
1245 1245
1246 /* CHECKME: To clear or not to clear? Mimics normal skb currently */ 1246 /* CHECKME: To clear or not to clear? Mimics normal skb currently */
1247 if (skb_shinfo(skb)->gso_segs <= 1) { 1247 if (skb_shinfo(skb)->gso_segs <= 1) {
1248 skb_shinfo(skb)->gso_size = 0; 1248 skb_shinfo(skb)->gso_size = 0;
1249 skb_shinfo(skb)->gso_type = 0; 1249 skb_shinfo(skb)->gso_type &= SKB_GSO_SHARED_FRAG;
1250 } 1250 }
1251 1251
1252 /* Difference in this won't matter, both ACKed by the same cumul. ACK */ 1252 /* Difference in this won't matter, both ACKed by the same cumul. ACK */
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 667a6adfccf8..367e2ec01da1 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -1133,6 +1133,7 @@ static void tcp_queue_skb(struct sock *sk, struct sk_buff *skb)
1133static void tcp_set_skb_tso_segs(const struct sock *sk, struct sk_buff *skb, 1133static void tcp_set_skb_tso_segs(const struct sock *sk, struct sk_buff *skb,
1134 unsigned int mss_now) 1134 unsigned int mss_now)
1135{ 1135{
1136 skb_shinfo(skb)->gso_type &= SKB_GSO_SHARED_FRAG;
1136 if (skb->len <= mss_now || !sk_can_gso(sk) || 1137 if (skb->len <= mss_now || !sk_can_gso(sk) ||
1137 skb->ip_summed == CHECKSUM_NONE) { 1138 skb->ip_summed == CHECKSUM_NONE) {
1138 /* Avoid the costly divide in the normal 1139 /* Avoid the costly divide in the normal
@@ -1140,11 +1141,10 @@ static void tcp_set_skb_tso_segs(const struct sock *sk, struct sk_buff *skb,
1140 */ 1141 */
1141 skb_shinfo(skb)->gso_segs = 1; 1142 skb_shinfo(skb)->gso_segs = 1;
1142 skb_shinfo(skb)->gso_size = 0; 1143 skb_shinfo(skb)->gso_size = 0;
1143 skb_shinfo(skb)->gso_type = 0;
1144 } else { 1144 } else {
1145 skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss_now); 1145 skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss_now);
1146 skb_shinfo(skb)->gso_size = mss_now; 1146 skb_shinfo(skb)->gso_size = mss_now;
1147 skb_shinfo(skb)->gso_type = sk->sk_gso_type; 1147 skb_shinfo(skb)->gso_type |= sk->sk_gso_type;
1148 } 1148 }
1149} 1149}
1150 1150
diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c
index f26f0da7f095..d141fc32a2ea 100644
--- a/net/ipv6/ip6_offload.c
+++ b/net/ipv6/ip6_offload.c
@@ -100,6 +100,7 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb,
100 SKB_GSO_DODGY | 100 SKB_GSO_DODGY |
101 SKB_GSO_TCP_ECN | 101 SKB_GSO_TCP_ECN |
102 SKB_GSO_TCPV6 | 102 SKB_GSO_TCPV6 |
103 SKB_GSO_SHARED_FRAG |
103 0))) 104 0)))
104 goto out; 105 goto out;
105 106