aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorMichael S. Tsirkin <mst@redhat.com>2010-03-17 11:45:01 -0400
committerMichael S. Tsirkin <mst@redhat.com>2010-05-03 05:33:13 -0400
commitd9d52b5178af586d679c1052fb161ee05ea2e83f (patch)
tree6148fda40e5aa28f3e8ceb438ffc51c5bb08dd86 /drivers/net
parent7ef527377b88ff05fb122a47619ea506c631c914 (diff)
tun: add ioctl to modify vnet header size
virtio added mergeable buffers mode where 2 bytes of extra info is put after vnet header but before actual data (tun does not need this data). In hindsight, it would have been better to add the new info *before* the packet: as it is, users need a lot of tricky code to skip the extra 2 bytes in the middle of the iovec, and in fact applications seem to get it wrong, and only work with specific iovec layout. The fact we might need to split iovec also means we might in theory overflow iovec max size. This patch adds a simpler way for applications to handle this, and future proofs the interface against further extensions, by making the size of the virtio net header configurable from userspace. As a result, tun driver will simply skip the extra 2 bytes on both input and output. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> Acked-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/tun.c32
1 files changed, 28 insertions, 4 deletions
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index e525a6cf5587..6b150c072a41 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -110,6 +110,9 @@ struct tun_struct {
110 struct tap_filter txflt; 110 struct tap_filter txflt;
111 struct socket socket; 111 struct socket socket;
112 struct socket_wq wq; 112 struct socket_wq wq;
113
114 int vnet_hdr_sz;
115
113#ifdef TUN_DEBUG 116#ifdef TUN_DEBUG
114 int debug; 117 int debug;
115#endif 118#endif
@@ -563,7 +566,7 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun,
563 } 566 }
564 567
565 if (tun->flags & TUN_VNET_HDR) { 568 if (tun->flags & TUN_VNET_HDR) {
566 if ((len -= sizeof(gso)) > count) 569 if ((len -= tun->vnet_hdr_sz) > count)
567 return -EINVAL; 570 return -EINVAL;
568 571
569 if (memcpy_fromiovecend((void *)&gso, iv, offset, sizeof(gso))) 572 if (memcpy_fromiovecend((void *)&gso, iv, offset, sizeof(gso)))
@@ -575,7 +578,7 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun,
575 578
576 if (gso.hdr_len > len) 579 if (gso.hdr_len > len)
577 return -EINVAL; 580 return -EINVAL;
578 offset += sizeof(gso); 581 offset += tun->vnet_hdr_sz;
579 } 582 }
580 583
581 if ((tun->flags & TUN_TYPE_MASK) == TUN_TAP_DEV) { 584 if ((tun->flags & TUN_TYPE_MASK) == TUN_TAP_DEV) {
@@ -718,7 +721,7 @@ static __inline__ ssize_t tun_put_user(struct tun_struct *tun,
718 721
719 if (tun->flags & TUN_VNET_HDR) { 722 if (tun->flags & TUN_VNET_HDR) {
720 struct virtio_net_hdr gso = { 0 }; /* no info leak */ 723 struct virtio_net_hdr gso = { 0 }; /* no info leak */
721 if ((len -= sizeof(gso)) < 0) 724 if ((len -= tun->vnet_hdr_sz) < 0)
722 return -EINVAL; 725 return -EINVAL;
723 726
724 if (skb_is_gso(skb)) { 727 if (skb_is_gso(skb)) {
@@ -749,7 +752,7 @@ static __inline__ ssize_t tun_put_user(struct tun_struct *tun,
749 if (unlikely(memcpy_toiovecend(iv, (void *)&gso, total, 752 if (unlikely(memcpy_toiovecend(iv, (void *)&gso, total,
750 sizeof(gso)))) 753 sizeof(gso))))
751 return -EFAULT; 754 return -EFAULT;
752 total += sizeof(gso); 755 total += tun->vnet_hdr_sz;
753 } 756 }
754 757
755 len = min_t(int, skb->len, len); 758 len = min_t(int, skb->len, len);
@@ -1035,6 +1038,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
1035 tun->dev = dev; 1038 tun->dev = dev;
1036 tun->flags = flags; 1039 tun->flags = flags;
1037 tun->txflt.count = 0; 1040 tun->txflt.count = 0;
1041 tun->vnet_hdr_sz = sizeof(struct virtio_net_hdr);
1038 1042
1039 err = -ENOMEM; 1043 err = -ENOMEM;
1040 sk = sk_alloc(net, AF_UNSPEC, GFP_KERNEL, &tun_proto); 1044 sk = sk_alloc(net, AF_UNSPEC, GFP_KERNEL, &tun_proto);
@@ -1177,6 +1181,7 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
1177 struct sock_fprog fprog; 1181 struct sock_fprog fprog;
1178 struct ifreq ifr; 1182 struct ifreq ifr;
1179 int sndbuf; 1183 int sndbuf;
1184 int vnet_hdr_sz;
1180 int ret; 1185 int ret;
1181 1186
1182 if (cmd == TUNSETIFF || _IOC_TYPE(cmd) == 0x89) 1187 if (cmd == TUNSETIFF || _IOC_TYPE(cmd) == 0x89)
@@ -1322,6 +1327,25 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
1322 tun->socket.sk->sk_sndbuf = sndbuf; 1327 tun->socket.sk->sk_sndbuf = sndbuf;
1323 break; 1328 break;
1324 1329
1330 case TUNGETVNETHDRSZ:
1331 vnet_hdr_sz = tun->vnet_hdr_sz;
1332 if (copy_to_user(argp, &vnet_hdr_sz, sizeof(vnet_hdr_sz)))
1333 ret = -EFAULT;
1334 break;
1335
1336 case TUNSETVNETHDRSZ:
1337 if (copy_from_user(&vnet_hdr_sz, argp, sizeof(vnet_hdr_sz))) {
1338 ret = -EFAULT;
1339 break;
1340 }
1341 if (vnet_hdr_sz < (int)sizeof(struct virtio_net_hdr)) {
1342 ret = -EINVAL;
1343 break;
1344 }
1345
1346 tun->vnet_hdr_sz = vnet_hdr_sz;
1347 break;
1348
1325 case TUNATTACHFILTER: 1349 case TUNATTACHFILTER:
1326 /* Can be set only for TAPs */ 1350 /* Can be set only for TAPs */
1327 ret = -EINVAL; 1351 ret = -EINVAL;