]> bbs.cooldavid.org Git - net-next-2.6.git/blobdiff - drivers/net/tun.c
tun: add ioctl to modify vnet header size
[net-next-2.6.git] / drivers / net / tun.c
index e525a6cf5587bb94b9200187d041866de6a67196..6b150c072a4140a4368530cf37ed1990c6f34ffe 100644 (file)
@@ -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
@@ -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)))
@@ -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) {
@@ -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)) {
@@ -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);
@@ -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);
@@ -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)
@@ -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;