diff options
Diffstat (limited to 'drivers/net/tun.c')
-rw-r--r-- | drivers/net/tun.c | 28 |
1 files changed, 17 insertions, 11 deletions
diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 7302398f0b1f..9dd3746994a4 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c | |||
@@ -1235,12 +1235,20 @@ 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 | int vnet_hdr_sz = 0; | ||
1240 | |||
1241 | if (vlan_tx_tag_present(skb)) | ||
1242 | vlan_hlen = VLAN_HLEN; | ||
1243 | |||
1244 | if (tun->flags & TUN_VNET_HDR) | ||
1245 | vnet_hdr_sz = tun->vnet_hdr_sz; | ||
1238 | 1246 | ||
1239 | if (!(tun->flags & TUN_NO_PI)) { | 1247 | if (!(tun->flags & TUN_NO_PI)) { |
1240 | if ((len -= sizeof(pi)) < 0) | 1248 | if ((len -= sizeof(pi)) < 0) |
1241 | return -EINVAL; | 1249 | return -EINVAL; |
1242 | 1250 | ||
1243 | if (len < skb->len) { | 1251 | if (len < skb->len + vlan_hlen + vnet_hdr_sz) { |
1244 | /* Packet will be striped */ | 1252 | /* Packet will be striped */ |
1245 | pi.flags |= TUN_PKT_STRIP; | 1253 | pi.flags |= TUN_PKT_STRIP; |
1246 | } | 1254 | } |
@@ -1250,9 +1258,9 @@ static ssize_t tun_put_user(struct tun_struct *tun, | |||
1250 | total += sizeof(pi); | 1258 | total += sizeof(pi); |
1251 | } | 1259 | } |
1252 | 1260 | ||
1253 | if (tun->flags & TUN_VNET_HDR) { | 1261 | if (vnet_hdr_sz) { |
1254 | struct virtio_net_hdr gso = { 0 }; /* no info leak */ | 1262 | struct virtio_net_hdr gso = { 0 }; /* no info leak */ |
1255 | if ((len -= tun->vnet_hdr_sz) < 0) | 1263 | if ((len -= vnet_hdr_sz) < 0) |
1256 | return -EINVAL; | 1264 | return -EINVAL; |
1257 | 1265 | ||
1258 | if (skb_is_gso(skb)) { | 1266 | if (skb_is_gso(skb)) { |
@@ -1284,7 +1292,8 @@ static ssize_t tun_put_user(struct tun_struct *tun, | |||
1284 | 1292 | ||
1285 | if (skb->ip_summed == CHECKSUM_PARTIAL) { | 1293 | if (skb->ip_summed == CHECKSUM_PARTIAL) { |
1286 | gso.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; | 1294 | gso.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; |
1287 | gso.csum_start = skb_checksum_start_offset(skb); | 1295 | gso.csum_start = skb_checksum_start_offset(skb) + |
1296 | vlan_hlen; | ||
1288 | gso.csum_offset = skb->csum_offset; | 1297 | gso.csum_offset = skb->csum_offset; |
1289 | } else if (skb->ip_summed == CHECKSUM_UNNECESSARY) { | 1298 | } else if (skb->ip_summed == CHECKSUM_UNNECESSARY) { |
1290 | gso.flags = VIRTIO_NET_HDR_F_DATA_VALID; | 1299 | gso.flags = VIRTIO_NET_HDR_F_DATA_VALID; |
@@ -1293,14 +1302,13 @@ static ssize_t tun_put_user(struct tun_struct *tun, | |||
1293 | if (unlikely(memcpy_toiovecend(iv, (void *)&gso, total, | 1302 | if (unlikely(memcpy_toiovecend(iv, (void *)&gso, total, |
1294 | sizeof(gso)))) | 1303 | sizeof(gso)))) |
1295 | return -EFAULT; | 1304 | return -EFAULT; |
1296 | total += tun->vnet_hdr_sz; | 1305 | total += vnet_hdr_sz; |
1297 | } | 1306 | } |
1298 | 1307 | ||
1299 | copied = total; | 1308 | copied = total; |
1300 | total += skb->len; | 1309 | len = min_t(int, skb->len + vlan_hlen, len); |
1301 | if (!vlan_tx_tag_present(skb)) { | 1310 | total += skb->len + vlan_hlen; |
1302 | len = min_t(int, skb->len, len); | 1311 | if (vlan_hlen) { |
1303 | } else { | ||
1304 | int copy, ret; | 1312 | int copy, ret; |
1305 | struct { | 1313 | struct { |
1306 | __be16 h_vlan_proto; | 1314 | __be16 h_vlan_proto; |
@@ -1311,8 +1319,6 @@ static ssize_t tun_put_user(struct tun_struct *tun, | |||
1311 | veth.h_vlan_TCI = htons(vlan_tx_tag_get(skb)); | 1319 | veth.h_vlan_TCI = htons(vlan_tx_tag_get(skb)); |
1312 | 1320 | ||
1313 | vlan_offset = offsetof(struct vlan_ethhdr, h_vlan_proto); | 1321 | vlan_offset = offsetof(struct vlan_ethhdr, h_vlan_proto); |
1314 | len = min_t(int, skb->len + VLAN_HLEN, len); | ||
1315 | total += VLAN_HLEN; | ||
1316 | 1322 | ||
1317 | copy = min_t(int, vlan_offset, len); | 1323 | copy = min_t(int, vlan_offset, len); |
1318 | ret = skb_copy_datagram_const_iovec(skb, 0, iv, copied, copy); | 1324 | ret = skb_copy_datagram_const_iovec(skb, 0, iv, copied, copy); |