aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/tun.c
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 /drivers/net/tun.c
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>
Diffstat (limited to 'drivers/net/tun.c')
-rw-r--r--drivers/net/tun.c12
1 files changed, 8 insertions, 4 deletions
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);