Skip to content

Commit

Permalink
tun: add ioctl to modify vnet header size
Browse files Browse the repository at this point in the history
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 <[email protected]>
Acked-by: David S. Miller <[email protected]>
  • Loading branch information
mstsirkin committed May 3, 2010
1 parent 7ef5273 commit d9d52b5
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 4 deletions.
32 changes: 28 additions & 4 deletions drivers/net/tun.c
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ struct tun_struct {
struct tap_filter txflt;
struct socket socket;
struct socket_wq wq;

int vnet_hdr_sz;

#ifdef TUN_DEBUG
int debug;
#endif
Expand Down Expand Up @@ -563,7 +566,7 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun,
}

if (tun->flags & TUN_VNET_HDR) {
if ((len -= sizeof(gso)) > count)
if ((len -= tun->vnet_hdr_sz) > count)
return -EINVAL;

if (memcpy_fromiovecend((void *)&gso, iv, offset, sizeof(gso)))
Expand All @@ -575,7 +578,7 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun,

if (gso.hdr_len > len)
return -EINVAL;
offset += sizeof(gso);
offset += tun->vnet_hdr_sz;
}

if ((tun->flags & TUN_TYPE_MASK) == TUN_TAP_DEV) {
Expand Down Expand Up @@ -718,7 +721,7 @@ static __inline__ ssize_t tun_put_user(struct tun_struct *tun,

if (tun->flags & TUN_VNET_HDR) {
struct virtio_net_hdr gso = { 0 }; /* no info leak */
if ((len -= sizeof(gso)) < 0)
if ((len -= tun->vnet_hdr_sz) < 0)
return -EINVAL;

if (skb_is_gso(skb)) {
Expand Down Expand Up @@ -749,7 +752,7 @@ static __inline__ ssize_t tun_put_user(struct tun_struct *tun,
if (unlikely(memcpy_toiovecend(iv, (void *)&gso, total,
sizeof(gso))))
return -EFAULT;
total += sizeof(gso);
total += tun->vnet_hdr_sz;
}

len = min_t(int, skb->len, len);
Expand Down Expand Up @@ -1035,6 +1038,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
tun->dev = dev;
tun->flags = flags;
tun->txflt.count = 0;
tun->vnet_hdr_sz = sizeof(struct virtio_net_hdr);

err = -ENOMEM;
sk = sk_alloc(net, AF_UNSPEC, GFP_KERNEL, &tun_proto);
Expand Down Expand Up @@ -1177,6 +1181,7 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
struct sock_fprog fprog;
struct ifreq ifr;
int sndbuf;
int vnet_hdr_sz;
int ret;

if (cmd == TUNSETIFF || _IOC_TYPE(cmd) == 0x89)
Expand Down Expand Up @@ -1322,6 +1327,25 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
tun->socket.sk->sk_sndbuf = sndbuf;
break;

case TUNGETVNETHDRSZ:
vnet_hdr_sz = tun->vnet_hdr_sz;
if (copy_to_user(argp, &vnet_hdr_sz, sizeof(vnet_hdr_sz)))
ret = -EFAULT;
break;

case TUNSETVNETHDRSZ:
if (copy_from_user(&vnet_hdr_sz, argp, sizeof(vnet_hdr_sz))) {
ret = -EFAULT;
break;
}
if (vnet_hdr_sz < (int)sizeof(struct virtio_net_hdr)) {
ret = -EINVAL;
break;
}

tun->vnet_hdr_sz = vnet_hdr_sz;
break;

case TUNATTACHFILTER:
/* Can be set only for TAPs */
ret = -EINVAL;
Expand Down
2 changes: 2 additions & 0 deletions include/linux/if_tun.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@
#define TUNSETSNDBUF _IOW('T', 212, int)
#define TUNATTACHFILTER _IOW('T', 213, struct sock_fprog)
#define TUNDETACHFILTER _IOW('T', 214, struct sock_fprog)
#define TUNGETVNETHDRSZ _IOR('T', 215, int)
#define TUNSETVNETHDRSZ _IOW('T', 216, int)

/* TUNSETIFF ifr flags */
#define IFF_TUN 0x0001
Expand Down

0 comments on commit d9d52b5

Please sign in to comment.