aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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