Decouple the vnet handling code so that we can reuse it for tap.

Signed-off-by: Akihiko Odaki <akihiko.od...@daynix.com>
Reviewed-by: Willem de Bruijn <will...@google.com>
---
 drivers/net/tun.c | 237 ++++++++++++++++++++++++++++++++----------------------
 1 file changed, 139 insertions(+), 98 deletions(-)

diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 
8ddd4b352f0307e52cdff75254b5ac1d259d51f8..5bd1c21032ed673ba8e39dd5a488cce11599855b
 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -351,6 +351,127 @@ static inline __virtio16 cpu_to_tun16(unsigned int flags, 
u16 val)
        return __cpu_to_virtio16(tun_is_little_endian(flags), val);
 }
 
+static long tun_vnet_ioctl(int *vnet_hdr_sz, unsigned int *flags,
+                          unsigned int cmd, int __user *sp)
+{
+       int s;
+
+       switch (cmd) {
+       case TUNGETVNETHDRSZ:
+               s = *vnet_hdr_sz;
+               if (put_user(s, sp))
+                       return -EFAULT;
+               return 0;
+
+       case TUNSETVNETHDRSZ:
+               if (get_user(s, sp))
+                       return -EFAULT;
+               if (s < (int)sizeof(struct virtio_net_hdr))
+                       return -EINVAL;
+
+               *vnet_hdr_sz = s;
+               return 0;
+
+       case TUNGETVNETLE:
+               s = !!(*flags & TUN_VNET_LE);
+               if (put_user(s, sp))
+                       return -EFAULT;
+               return 0;
+
+       case TUNSETVNETLE:
+               if (get_user(s, sp))
+                       return -EFAULT;
+               if (s)
+                       *flags |= TUN_VNET_LE;
+               else
+                       *flags &= ~TUN_VNET_LE;
+               return 0;
+
+       case TUNGETVNETBE:
+               return tun_get_vnet_be(*flags, sp);
+
+       case TUNSETVNETBE:
+               return tun_set_vnet_be(flags, sp);
+
+       default:
+               return -EINVAL;
+       }
+}
+
+static int tun_vnet_hdr_get(int sz, unsigned int flags, struct iov_iter *from,
+                           struct virtio_net_hdr *hdr)
+{
+       u16 hdr_len;
+
+       if (iov_iter_count(from) < sz)
+               return -EINVAL;
+
+       if (!copy_from_iter_full(hdr, sizeof(*hdr), from))
+               return -EFAULT;
+
+       hdr_len = tun16_to_cpu(flags, hdr->hdr_len);
+
+       if (hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) {
+               hdr_len = max(tun16_to_cpu(flags, hdr->csum_start) + 
tun16_to_cpu(flags, hdr->csum_offset) + 2, hdr_len);
+               hdr->hdr_len = cpu_to_tun16(flags, hdr_len);
+       }
+
+       if (hdr_len > iov_iter_count(from))
+               return -EINVAL;
+
+       iov_iter_advance(from, sz - sizeof(*hdr));
+
+       return hdr_len;
+}
+
+static int tun_vnet_hdr_put(int sz, struct iov_iter *iter,
+                           const struct virtio_net_hdr *hdr)
+{
+       if (unlikely(iov_iter_count(iter) < sz))
+               return -EINVAL;
+
+       if (unlikely(copy_to_iter(hdr, sizeof(*hdr), iter) != sizeof(*hdr)))
+               return -EFAULT;
+
+       iov_iter_advance(iter, sz - sizeof(*hdr));
+
+       return 0;
+}
+
+static int tun_vnet_hdr_to_skb(unsigned int flags, struct sk_buff *skb,
+                              const struct virtio_net_hdr *hdr)
+{
+       return virtio_net_hdr_to_skb(skb, hdr, tun_is_little_endian(flags));
+}
+
+static int tun_vnet_hdr_from_skb(unsigned int flags,
+                                const struct net_device *dev,
+                                const struct sk_buff *skb,
+                                struct virtio_net_hdr *hdr)
+{
+       int vlan_hlen = skb_vlan_tag_present(skb) ? VLAN_HLEN : 0;
+
+       if (virtio_net_hdr_from_skb(skb, hdr,
+                                   tun_is_little_endian(flags), true,
+                                   vlan_hlen)) {
+               struct skb_shared_info *sinfo = skb_shinfo(skb);
+
+               if (net_ratelimit()) {
+                       netdev_err(dev, "unexpected GSO type: 0x%x, gso_size 
%d, hdr_len %d\n",
+                                  sinfo->gso_type, tun16_to_cpu(flags, 
hdr->gso_size),
+                                  tun16_to_cpu(flags, hdr->hdr_len));
+                       print_hex_dump(KERN_ERR, "tun: ",
+                                      DUMP_PREFIX_NONE,
+                                      16, 1, skb->head,
+                                      min(tun16_to_cpu(flags, hdr->hdr_len), 
64), true);
+               }
+               WARN_ON_ONCE(1);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static inline u32 tun_hashfn(u32 rxhash)
 {
        return rxhash & TUN_MASK_FLOW_ENTRIES;
@@ -1764,25 +1885,12 @@ static ssize_t tun_get_user(struct tun_struct *tun, 
struct tun_file *tfile,
 
        if (tun->flags & IFF_VNET_HDR) {
                int vnet_hdr_sz = READ_ONCE(tun->vnet_hdr_sz);
-               int flags = tun->flags;
-
-               if (len < vnet_hdr_sz)
-                       return -EINVAL;
-               len -= vnet_hdr_sz;
-
-               if (!copy_from_iter_full(&gso, sizeof(gso), from))
-                       return -EFAULT;
-
-               hdr_len = tun16_to_cpu(flags, gso.hdr_len);
 
-               if (gso.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) {
-                       hdr_len = max(tun16_to_cpu(flags, gso.csum_start) + 
tun16_to_cpu(flags, gso.csum_offset) + 2, hdr_len);
-                       gso.hdr_len = cpu_to_tun16(flags, hdr_len);
-               }
+               hdr_len = tun_vnet_hdr_get(vnet_hdr_sz, tun->flags, from, &gso);
+               if (hdr_len < 0)
+                       return hdr_len;
 
-               if (hdr_len > len)
-                       return -EINVAL;
-               iov_iter_advance(from, vnet_hdr_sz - sizeof(gso));
+               len -= vnet_hdr_sz;
        }
 
        if ((tun->flags & TUN_TYPE_MASK) == IFF_TAP) {
@@ -1856,7 +1964,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, 
struct tun_file *tfile,
                }
        }
 
-       if (virtio_net_hdr_to_skb(skb, &gso, tun_is_little_endian(tun->flags))) 
{
+       if (tun_vnet_hdr_to_skb(tun->flags, skb, &gso)) {
                atomic_long_inc(&tun->rx_frame_errors);
                err = -EINVAL;
                goto free_skb;
@@ -2051,18 +2159,15 @@ static ssize_t tun_put_user_xdp(struct tun_struct *tun,
 {
        int vnet_hdr_sz = 0;
        size_t size = xdp_frame->len;
-       size_t ret;
+       ssize_t ret;
 
        if (tun->flags & IFF_VNET_HDR) {
                struct virtio_net_hdr gso = { 0 };
 
                vnet_hdr_sz = READ_ONCE(tun->vnet_hdr_sz);
-               if (unlikely(iov_iter_count(iter) < vnet_hdr_sz))
-                       return -EINVAL;
-               if (unlikely(copy_to_iter(&gso, sizeof(gso), iter) !=
-                            sizeof(gso)))
-                       return -EFAULT;
-               iov_iter_advance(iter, vnet_hdr_sz - sizeof(gso));
+               ret = tun_vnet_hdr_put(vnet_hdr_sz, iter, &gso);
+               if (ret)
+                       return ret;
        }
 
        ret = copy_to_iter(xdp_frame->data, size, iter) + vnet_hdr_sz;
@@ -2085,6 +2190,7 @@ static ssize_t tun_put_user(struct tun_struct *tun,
        int vlan_offset = 0;
        int vlan_hlen = 0;
        int vnet_hdr_sz = 0;
+       int ret;
 
        if (skb_vlan_tag_present(skb))
                vlan_hlen = VLAN_HLEN;
@@ -2110,33 +2216,14 @@ static ssize_t tun_put_user(struct tun_struct *tun,
 
        if (vnet_hdr_sz) {
                struct virtio_net_hdr gso;
-               int flags = tun->flags;
-
-               if (iov_iter_count(iter) < vnet_hdr_sz)
-                       return -EINVAL;
-
-               if (virtio_net_hdr_from_skb(skb, &gso,
-                                           tun_is_little_endian(flags), true,
-                                           vlan_hlen)) {
-                       struct skb_shared_info *sinfo = skb_shinfo(skb);
-
-                       if (net_ratelimit()) {
-                               netdev_err(tun->dev, "unexpected GSO type: 
0x%x, gso_size %d, hdr_len %d\n",
-                                          sinfo->gso_type, tun16_to_cpu(flags, 
gso.gso_size),
-                                          tun16_to_cpu(flags, gso.hdr_len));
-                               print_hex_dump(KERN_ERR, "tun: ",
-                                              DUMP_PREFIX_NONE,
-                                              16, 1, skb->head,
-                                              min((int)tun16_to_cpu(flags, 
gso.hdr_len), 64), true);
-                       }
-                       WARN_ON_ONCE(1);
-                       return -EINVAL;
-               }
 
-               if (copy_to_iter(&gso, sizeof(gso), iter) != sizeof(gso))
-                       return -EFAULT;
+               ret = tun_vnet_hdr_from_skb(tun->flags, tun->dev, skb, &gso);
+               if (ret)
+                       return ret;
 
-               iov_iter_advance(iter, vnet_hdr_sz - sizeof(gso));
+               ret = tun_vnet_hdr_put(vnet_hdr_sz, iter, &gso);
+               if (ret)
+                       return ret;
        }
 
        if (vlan_hlen) {
@@ -2496,7 +2583,7 @@ static int tun_xdp_one(struct tun_struct *tun,
        skb_reserve(skb, xdp->data - xdp->data_hard_start);
        skb_put(skb, xdp->data_end - xdp->data);
 
-       if (virtio_net_hdr_to_skb(skb, gso, tun_is_little_endian(tun->flags))) {
+       if (tun_vnet_hdr_to_skb(tun->flags, skb, gso)) {
                atomic_long_inc(&tun->rx_frame_errors);
                kfree_skb(skb);
                ret = -EINVAL;
@@ -3080,8 +3167,6 @@ static long __tun_chr_ioctl(struct file *file, unsigned 
int cmd,
        kgid_t group;
        int ifindex;
        int sndbuf;
-       int vnet_hdr_sz;
-       int le;
        int ret;
        bool do_notify = false;
 
@@ -3288,50 +3373,6 @@ static long __tun_chr_ioctl(struct file *file, unsigned 
int cmd,
                tun_set_sndbuf(tun);
                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 TUNGETVNETLE:
-               le = !!(tun->flags & TUN_VNET_LE);
-               if (put_user(le, (int __user *)argp))
-                       ret = -EFAULT;
-               break;
-
-       case TUNSETVNETLE:
-               if (get_user(le, (int __user *)argp)) {
-                       ret = -EFAULT;
-                       break;
-               }
-               if (le)
-                       tun->flags |= TUN_VNET_LE;
-               else
-                       tun->flags &= ~TUN_VNET_LE;
-               break;
-
-       case TUNGETVNETBE:
-               ret = tun_get_vnet_be(tun->flags, argp);
-               break;
-
-       case TUNSETVNETBE:
-               ret = tun_set_vnet_be(&tun->flags, argp);
-               break;
-
        case TUNATTACHFILTER:
                /* Can be set only for TAPs */
                ret = -EINVAL;
@@ -3387,7 +3428,7 @@ static long __tun_chr_ioctl(struct file *file, unsigned 
int cmd,
                break;
 
        default:
-               ret = -EINVAL;
+               ret = tun_vnet_ioctl(&tun->vnet_hdr_sz, &tun->flags, cmd, argp);
                break;
        }
 

-- 
2.48.1


Reply via email to