aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/tun.c
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2014-11-02 15:30:13 -0500
committerDavid S. Miller <davem@davemloft.net>2014-11-03 14:27:06 -0500
commita8f9bfdf982e2b1fb9f094e4de9ab08c57f3d2fd (patch)
treef95d7271223a6fb762b1786a4c15f9ecf95c7aed /drivers/net/tun.c
parent7071cf7fc435ab84df872613f613a9a055964fc1 (diff)
tun: Fix csum_start with VLAN acceleration
When VLAN acceleration is in use on the xmit path, we end up setting csum_start to the wrong place. The result is that the whoever ends up doing the checksum setting will corrupt the packet instead of writing the checksum to the expected location, usually this means writing the checksum with an offset of -4. This patch fixes this by adjusting csum_start when VLAN acceleration is detected. Fixes: 6680ec68eff4 ("tuntap: hardware vlan tx support") Cc: stable@vger.kernel.org Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/tun.c')
-rw-r--r--drivers/net/tun.c16
1 files changed, 9 insertions, 7 deletions
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 7302398f0b1f..57e6bf75a632 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -1235,6 +1235,10 @@ static ssize_t tun_put_user(struct tun_struct *tun,
1235 struct tun_pi pi = { 0, skb->protocol }; 1235 struct tun_pi pi = { 0, skb->protocol };
1236 ssize_t total = 0; 1236 ssize_t total = 0;
1237 int vlan_offset = 0, copied; 1237 int vlan_offset = 0, copied;
1238 int vlan_hlen = 0;
1239
1240 if (vlan_tx_tag_present(skb))
1241 vlan_hlen = VLAN_HLEN;
1238 1242
1239 if (!(tun->flags & TUN_NO_PI)) { 1243 if (!(tun->flags & TUN_NO_PI)) {
1240 if ((len -= sizeof(pi)) < 0) 1244 if ((len -= sizeof(pi)) < 0)
@@ -1284,7 +1288,8 @@ static ssize_t tun_put_user(struct tun_struct *tun,
1284 1288
1285 if (skb->ip_summed == CHECKSUM_PARTIAL) { 1289 if (skb->ip_summed == CHECKSUM_PARTIAL) {
1286 gso.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; 1290 gso.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
1287 gso.csum_start = skb_checksum_start_offset(skb); 1291 gso.csum_start = skb_checksum_start_offset(skb) +
1292 vlan_hlen;
1288 gso.csum_offset = skb->csum_offset; 1293 gso.csum_offset = skb->csum_offset;
1289 } else if (skb->ip_summed == CHECKSUM_UNNECESSARY) { 1294 } else if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
1290 gso.flags = VIRTIO_NET_HDR_F_DATA_VALID; 1295 gso.flags = VIRTIO_NET_HDR_F_DATA_VALID;
@@ -1297,10 +1302,9 @@ static ssize_t tun_put_user(struct tun_struct *tun,
1297 } 1302 }
1298 1303
1299 copied = total; 1304 copied = total;
1300 total += skb->len; 1305 len = min_t(int, skb->len + vlan_hlen, len);
1301 if (!vlan_tx_tag_present(skb)) { 1306 total += skb->len + vlan_hlen;
1302 len = min_t(int, skb->len, len); 1307 if (vlan_hlen) {
1303 } else {
1304 int copy, ret; 1308 int copy, ret;
1305 struct { 1309 struct {
1306 __be16 h_vlan_proto; 1310 __be16 h_vlan_proto;
@@ -1311,8 +1315,6 @@ static ssize_t tun_put_user(struct tun_struct *tun,
1311 veth.h_vlan_TCI = htons(vlan_tx_tag_get(skb)); 1315 veth.h_vlan_TCI = htons(vlan_tx_tag_get(skb));
1312 1316
1313 vlan_offset = offsetof(struct vlan_ethhdr, h_vlan_proto); 1317 vlan_offset = offsetof(struct vlan_ethhdr, h_vlan_proto);
1314 len = min_t(int, skb->len + VLAN_HLEN, len);
1315 total += VLAN_HLEN;
1316 1318
1317 copy = min_t(int, vlan_offset, len); 1319 copy = min_t(int, vlan_offset, len);
1318 ret = skb_copy_datagram_const_iovec(skb, 0, iv, copied, copy); 1320 ret = skb_copy_datagram_const_iovec(skb, 0, iv, copied, copy);