aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/tun.c
diff options
context:
space:
mode:
authorJason Wang <jasowang@redhat.com>2013-12-09 05:25:16 -0500
committerDavid S. Miller <davem@davemloft.net>2013-12-10 22:06:49 -0500
commit923347bb83c67c3a572b04decb5875c3adb0d306 (patch)
tree90c725a751b4f1a964685cf73d486d53ae88d7e4 /drivers/net/tun.c
parentfffc15a5012e9052d3b236efc56840841a125416 (diff)
tun: unbreak truncated packet signalling
Commit 6680ec68eff47d36f67b4351bc9836fd6cba9532 (tuntap: hardware vlan tx support) breaks the truncated packet signal by never return a length greater than iov length in tun_put_user(). This patch fixes this by always return the length of packet plus possible vlan header. Caller can detect the truncated packet by comparing the return value and the size of iov length. Reported-by: Vlad Yasevich <vyasevich@gmail.com> Cc: Vlad Yasevich <vyasevich@gmail.com> Cc: Zhi Yong Wu <wuzhy@linux.vnet.ibm.com> Signed-off-by: Jason Wang <jasowang@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/tun.c')
-rw-r--r--drivers/net/tun.c23
1 files changed, 12 insertions, 11 deletions
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index e26cbea1ce68..dd1bd7aedc3c 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -1183,7 +1183,11 @@ static ssize_t tun_put_user(struct tun_struct *tun,
1183 const struct iovec *iv, int len) 1183 const struct iovec *iv, int len)
1184{ 1184{
1185 struct tun_pi pi = { 0, skb->protocol }; 1185 struct tun_pi pi = { 0, skb->protocol };
1186 ssize_t total = 0; 1186 struct {
1187 __be16 h_vlan_proto;
1188 __be16 h_vlan_TCI;
1189 } veth;
1190 ssize_t total = 0, off = 0;
1187 int vlan_offset = 0; 1191 int vlan_offset = 0;
1188 1192
1189 if (!(tun->flags & TUN_NO_PI)) { 1193 if (!(tun->flags & TUN_NO_PI)) {
@@ -1248,14 +1252,11 @@ static ssize_t tun_put_user(struct tun_struct *tun,
1248 total += tun->vnet_hdr_sz; 1252 total += tun->vnet_hdr_sz;
1249 } 1253 }
1250 1254
1255 off = total;
1251 if (!vlan_tx_tag_present(skb)) { 1256 if (!vlan_tx_tag_present(skb)) {
1252 len = min_t(int, skb->len, len); 1257 len = min_t(int, skb->len, len);
1253 } else { 1258 } else {
1254 int copy, ret; 1259 int copy, ret;
1255 struct {
1256 __be16 h_vlan_proto;
1257 __be16 h_vlan_TCI;
1258 } veth;
1259 1260
1260 veth.h_vlan_proto = skb->vlan_proto; 1261 veth.h_vlan_proto = skb->vlan_proto;
1261 veth.h_vlan_TCI = htons(vlan_tx_tag_get(skb)); 1262 veth.h_vlan_TCI = htons(vlan_tx_tag_get(skb));
@@ -1264,22 +1265,22 @@ static ssize_t tun_put_user(struct tun_struct *tun,
1264 len = min_t(int, skb->len + VLAN_HLEN, len); 1265 len = min_t(int, skb->len + VLAN_HLEN, len);
1265 1266
1266 copy = min_t(int, vlan_offset, len); 1267 copy = min_t(int, vlan_offset, len);
1267 ret = skb_copy_datagram_const_iovec(skb, 0, iv, total, copy); 1268 ret = skb_copy_datagram_const_iovec(skb, 0, iv, off, copy);
1268 len -= copy; 1269 len -= copy;
1269 total += copy; 1270 off += copy;
1270 if (ret || !len) 1271 if (ret || !len)
1271 goto done; 1272 goto done;
1272 1273
1273 copy = min_t(int, sizeof(veth), len); 1274 copy = min_t(int, sizeof(veth), len);
1274 ret = memcpy_toiovecend(iv, (void *)&veth, total, copy); 1275 ret = memcpy_toiovecend(iv, (void *)&veth, off, copy);
1275 len -= copy; 1276 len -= copy;
1276 total += copy; 1277 off += copy;
1277 if (ret || !len) 1278 if (ret || !len)
1278 goto done; 1279 goto done;
1279 } 1280 }
1280 1281
1281 skb_copy_datagram_const_iovec(skb, vlan_offset, iv, total, len); 1282 skb_copy_datagram_const_iovec(skb, vlan_offset, iv, off, len);
1282 total += len; 1283 total += skb->len + (vlan_offset ? sizeof(veth) : 0);
1283 1284
1284done: 1285done:
1285 tun->dev->stats.tx_packets++; 1286 tun->dev->stats.tx_packets++;