diff options
author | Herbert Xu <herbert@gondor.apana.org.au> | 2014-11-02 15:30:14 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-11-03 14:27:06 -0500 |
commit | 2eb783c43e7cf807a45899c10ed556b6dc116625 (patch) | |
tree | 15b3ea5b89ea7f75965d3ce1c4eba43d69c182c6 /drivers/net/tun.c | |
parent | a8f9bfdf982e2b1fb9f094e4de9ab08c57f3d2fd (diff) |
tun: Fix TUN_PKT_STRIP setting
We set the flag TUN_PKT_STRIP if the user buffer provided is too
small to contain the entire packet plus meta-data. However, this
has been broken ever since we added GSO meta-data. VLAN acceleration
also has the same problem.
This patch fixes this by taking both into account when setting the
TUN_PKT_STRIP flag.
The fact that this has been broken for six years without anyone
realising means that nobody actually uses this flag.
Fixes: f43798c27684 ("tun: Allow GSO using virtio_net_hdr")
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.c | 12 |
1 files changed, 8 insertions, 4 deletions
diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 57e6bf75a632..9dd3746994a4 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c | |||
@@ -1236,15 +1236,19 @@ static ssize_t tun_put_user(struct tun_struct *tun, | |||
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; | 1238 | int vlan_hlen = 0; |
1239 | int vnet_hdr_sz = 0; | ||
1239 | 1240 | ||
1240 | if (vlan_tx_tag_present(skb)) | 1241 | if (vlan_tx_tag_present(skb)) |
1241 | vlan_hlen = VLAN_HLEN; | 1242 | vlan_hlen = VLAN_HLEN; |
1242 | 1243 | ||
1244 | if (tun->flags & TUN_VNET_HDR) | ||
1245 | vnet_hdr_sz = tun->vnet_hdr_sz; | ||
1246 | |||
1243 | if (!(tun->flags & TUN_NO_PI)) { | 1247 | if (!(tun->flags & TUN_NO_PI)) { |
1244 | if ((len -= sizeof(pi)) < 0) | 1248 | if ((len -= sizeof(pi)) < 0) |
1245 | return -EINVAL; | 1249 | return -EINVAL; |
1246 | 1250 | ||
1247 | if (len < skb->len) { | 1251 | if (len < skb->len + vlan_hlen + vnet_hdr_sz) { |
1248 | /* Packet will be striped */ | 1252 | /* Packet will be striped */ |
1249 | pi.flags |= TUN_PKT_STRIP; | 1253 | pi.flags |= TUN_PKT_STRIP; |
1250 | } | 1254 | } |
@@ -1254,9 +1258,9 @@ static ssize_t tun_put_user(struct tun_struct *tun, | |||
1254 | total += sizeof(pi); | 1258 | total += sizeof(pi); |
1255 | } | 1259 | } |
1256 | 1260 | ||
1257 | if (tun->flags & TUN_VNET_HDR) { | 1261 | if (vnet_hdr_sz) { |
1258 | struct virtio_net_hdr gso = { 0 }; /* no info leak */ | 1262 | struct virtio_net_hdr gso = { 0 }; /* no info leak */ |
1259 | if ((len -= tun->vnet_hdr_sz) < 0) | 1263 | if ((len -= vnet_hdr_sz) < 0) |
1260 | return -EINVAL; | 1264 | return -EINVAL; |
1261 | 1265 | ||
1262 | if (skb_is_gso(skb)) { | 1266 | if (skb_is_gso(skb)) { |
@@ -1298,7 +1302,7 @@ static ssize_t tun_put_user(struct tun_struct *tun, | |||
1298 | if (unlikely(memcpy_toiovecend(iv, (void *)&gso, total, | 1302 | if (unlikely(memcpy_toiovecend(iv, (void *)&gso, total, |
1299 | sizeof(gso)))) | 1303 | sizeof(gso)))) |
1300 | return -EFAULT; | 1304 | return -EFAULT; |
1301 | total += tun->vnet_hdr_sz; | 1305 | total += vnet_hdr_sz; |
1302 | } | 1306 | } |
1303 | 1307 | ||
1304 | copied = total; | 1308 | copied = total; |