Commit 6a53fc5a authored by Akihiko Odaki's avatar Akihiko Odaki Committed by Jakub Kicinski
Browse files

tap: Use tun's vnet-related code



tun and tap implements the same vnet-related features so reuse the code.

Signed-off-by: default avatarAkihiko Odaki <akihiko.odaki@daynix.com>
Reviewed-by: default avatarWillem de Bruijn <willemb@google.com>
Link: https://patch.msgid.link/20250207-tun-v6-7-fb49cf8b103e@daynix.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 74212f20
Loading
Loading
Loading
Loading
+16 −136
Original line number Diff line number Diff line
@@ -26,74 +26,9 @@
#include <linux/virtio_net.h>
#include <linux/skb_array.h>

#define TAP_IFFEATURES (IFF_VNET_HDR | IFF_MULTI_QUEUE)

#define TAP_VNET_LE 0x80000000
#define TAP_VNET_BE 0x40000000

#ifdef CONFIG_TUN_VNET_CROSS_LE
static inline bool tap_legacy_is_little_endian(struct tap_queue *q)
{
	return q->flags & TAP_VNET_BE ? false :
		virtio_legacy_is_little_endian();
}

static long tap_get_vnet_be(struct tap_queue *q, int __user *sp)
{
	int s = !!(q->flags & TAP_VNET_BE);

	if (put_user(s, sp))
		return -EFAULT;

	return 0;
}

static long tap_set_vnet_be(struct tap_queue *q, int __user *sp)
{
	int s;

	if (get_user(s, sp))
		return -EFAULT;

	if (s)
		q->flags |= TAP_VNET_BE;
	else
		q->flags &= ~TAP_VNET_BE;

	return 0;
}
#else
static inline bool tap_legacy_is_little_endian(struct tap_queue *q)
{
	return virtio_legacy_is_little_endian();
}

static long tap_get_vnet_be(struct tap_queue *q, int __user *argp)
{
	return -EINVAL;
}
#include "tun_vnet.h"

static long tap_set_vnet_be(struct tap_queue *q, int __user *argp)
{
	return -EINVAL;
}
#endif /* CONFIG_TUN_VNET_CROSS_LE */

static inline bool tap_is_little_endian(struct tap_queue *q)
{
	return q->flags & TAP_VNET_LE ||
		tap_legacy_is_little_endian(q);
}

static inline u16 tap16_to_cpu(struct tap_queue *q, __virtio16 val)
{
	return __virtio16_to_cpu(tap_is_little_endian(q), val);
}

static inline __virtio16 cpu_to_tap16(struct tap_queue *q, u16 val)
{
	return __cpu_to_virtio16(tap_is_little_endian(q), val);
}
#define TAP_IFFEATURES (IFF_VNET_HDR | IFF_MULTI_QUEUE)

static struct proto tap_proto = {
	.name = "tap",
@@ -655,25 +590,13 @@ static ssize_t tap_get_user(struct tap_queue *q, void *msg_control,
	if (q->flags & IFF_VNET_HDR) {
		vnet_hdr_len = READ_ONCE(q->vnet_hdr_sz);

		err = -EINVAL;
		if (len < vnet_hdr_len)
			goto err;
		len -= vnet_hdr_len;

		err = -EFAULT;
		if (!copy_from_iter_full(&vnet_hdr, sizeof(vnet_hdr), from))
		hdr_len = tun_vnet_hdr_get(vnet_hdr_len, q->flags, from, &vnet_hdr);
		if (hdr_len < 0) {
			err = hdr_len;
			goto err;
		iov_iter_advance(from, vnet_hdr_len - sizeof(vnet_hdr));
		hdr_len = tap16_to_cpu(q, vnet_hdr.hdr_len);
		if (vnet_hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) {
			hdr_len = max(tap16_to_cpu(q, vnet_hdr.csum_start) +
				      tap16_to_cpu(q, vnet_hdr.csum_offset) + 2,
				      hdr_len);
			vnet_hdr.hdr_len = cpu_to_tap16(q, hdr_len);
		}
		err = -EINVAL;
		if (tap16_to_cpu(q, vnet_hdr.hdr_len) > len)
			goto err;

		len -= vnet_hdr_len;
	}

	err = -EINVAL;
@@ -725,8 +648,7 @@ static ssize_t tap_get_user(struct tap_queue *q, void *msg_control,
	skb->dev = tap->dev;

	if (vnet_hdr_len) {
		err = virtio_net_hdr_to_skb(skb, &vnet_hdr,
					    tap_is_little_endian(q));
		err = tun_vnet_hdr_to_skb(q->flags, skb, &vnet_hdr);
		if (err) {
			rcu_read_unlock();
			drop_reason = SKB_DROP_REASON_DEV_HDR;
@@ -789,23 +711,17 @@ static ssize_t tap_put_user(struct tap_queue *q,
	int total;

	if (q->flags & IFF_VNET_HDR) {
		int vlan_hlen = skb_vlan_tag_present(skb) ? VLAN_HLEN : 0;
		struct virtio_net_hdr vnet_hdr;

		vnet_hdr_len = READ_ONCE(q->vnet_hdr_sz);
		if (iov_iter_count(iter) < vnet_hdr_len)
			return -EINVAL;

		if (virtio_net_hdr_from_skb(skb, &vnet_hdr,
					    tap_is_little_endian(q), true,
					    vlan_hlen))
			BUG();

		if (copy_to_iter(&vnet_hdr, sizeof(vnet_hdr), iter) !=
		    sizeof(vnet_hdr))
			return -EFAULT;
		ret = tun_vnet_hdr_from_skb(q->flags, NULL, skb, &vnet_hdr);
		if (ret)
			return ret;

		iov_iter_advance(iter, vnet_hdr_len - sizeof(vnet_hdr));
		ret = tun_vnet_hdr_put(vnet_hdr_len, iter, &vnet_hdr);
		if (ret)
			return ret;
	}
	total = vnet_hdr_len;
	total += skb->len;
@@ -1064,42 +980,6 @@ static long tap_ioctl(struct file *file, unsigned int cmd,
		q->sk.sk_sndbuf = s;
		return 0;

	case TUNGETVNETHDRSZ:
		s = q->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;

		q->vnet_hdr_sz = s;
		return 0;

	case TUNGETVNETLE:
		s = !!(q->flags & TAP_VNET_LE);
		if (put_user(s, sp))
			return -EFAULT;
		return 0;

	case TUNSETVNETLE:
		if (get_user(s, sp))
			return -EFAULT;
		if (s)
			q->flags |= TAP_VNET_LE;
		else
			q->flags &= ~TAP_VNET_LE;
		return 0;

	case TUNGETVNETBE:
		return tap_get_vnet_be(q, sp);

	case TUNSETVNETBE:
		return tap_set_vnet_be(q, sp);

	case TUNSETOFFLOAD:
		/* let the user check for future flags */
		if (arg & ~(TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 |
@@ -1143,7 +1023,7 @@ static long tap_ioctl(struct file *file, unsigned int cmd,
		return ret;

	default:
		return -EINVAL;
		return tun_vnet_ioctl(&q->vnet_hdr_sz, &q->flags, cmd, sp);
	}
}

@@ -1190,7 +1070,7 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp)
	skb->protocol = eth_hdr(skb)->h_proto;

	if (vnet_hdr_len) {
		err = virtio_net_hdr_to_skb(skb, gso, tap_is_little_endian(q));
		err = tun_vnet_hdr_to_skb(q->flags, skb, gso);
		if (err)
			goto err_kfree;
	}