Skip to content

Commit

Permalink
macvtap/tun: cross-endian support for little-endian hosts
Browse files Browse the repository at this point in the history
The VNET_LE flag was introduced to fix accesses to virtio 1.0 headers
that are always little-endian. It can also be used to handle the special
case of a legacy little-endian device implemented by a big-endian host.

Let's add a flag and ioctls for big-endian devices as well. If both flags
are set, little-endian wins.

Since this is isn't a common usecase, the feature is controlled by a kernel
config option (not set by default).

Both macvtap and tun are covered by this patch since they share the same
API with userland.

Signed-off-by: Greg Kurz <gkurz@linux.vnet.ibm.com>

Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
  • Loading branch information
Greg Kurz authored and Michael S. Tsirkin committed Jun 1, 2015
1 parent 2751c98 commit 8b8e658
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 2 deletions.
14 changes: 14 additions & 0 deletions drivers/net/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,20 @@ config TUN

If you don't know what to use this for, you don't need it.

config TUN_VNET_CROSS_LE
bool "Support for cross-endian vnet headers on little-endian kernels"
default n
---help---
This option allows TUN/TAP and MACVTAP device drivers in a
little-endian kernel to parse vnet headers that come from a
big-endian legacy virtio device.

Userspace programs can control the feature using the TUNSETVNETBE
and TUNGETVNETBE ioctls.

Unless you have a little-endian system hosting a big-endian virtual
machine with a legacy virtio NIC, you should say N.

config VETH
tristate "Virtual ethernet pair device"
---help---
Expand Down
57 changes: 56 additions & 1 deletion drivers/net/macvtap.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,60 @@ struct macvtap_queue {
#define MACVTAP_FEATURES (IFF_VNET_HDR | IFF_MULTI_QUEUE)

#define MACVTAP_VNET_LE 0x80000000
#define MACVTAP_VNET_BE 0x40000000

#ifdef CONFIG_TUN_VNET_CROSS_LE
static inline bool macvtap_legacy_is_little_endian(struct macvtap_queue *q)
{
return q->flags & MACVTAP_VNET_BE ? false :
virtio_legacy_is_little_endian();
}

static long macvtap_get_vnet_be(struct macvtap_queue *q, int __user *sp)
{
int s = !!(q->flags & MACVTAP_VNET_BE);

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

return 0;
}

static long macvtap_set_vnet_be(struct macvtap_queue *q, int __user *sp)
{
int s;

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

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

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

static long macvtap_get_vnet_be(struct macvtap_queue *q, int __user *argp)
{
return -EINVAL;
}

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

static inline bool macvtap_is_little_endian(struct macvtap_queue *q)
{
return q->flags & MACVTAP_VNET_LE ||
virtio_legacy_is_little_endian();
macvtap_legacy_is_little_endian(q);
}

static inline u16 macvtap16_to_cpu(struct macvtap_queue *q, __virtio16 val)
Expand Down Expand Up @@ -1096,6 +1145,12 @@ static long macvtap_ioctl(struct file *file, unsigned int cmd,
q->flags &= ~MACVTAP_VNET_LE;
return 0;

case TUNGETVNETBE:
return macvtap_get_vnet_be(q, sp);

case TUNSETVNETBE:
return macvtap_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 |
Expand Down
59 changes: 58 additions & 1 deletion drivers/net/tun.c
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ do { \
#define TUN_FASYNC IFF_ATTACH_QUEUE
/* High bits in flags field are unused. */
#define TUN_VNET_LE 0x80000000
#define TUN_VNET_BE 0x40000000

#define TUN_FEATURES (IFF_NO_PI | IFF_ONE_QUEUE | IFF_VNET_HDR | \
IFF_MULTI_QUEUE)
Expand Down Expand Up @@ -206,10 +207,58 @@ struct tun_struct {
u32 flow_count;
};

#ifdef CONFIG_TUN_VNET_CROSS_LE
static inline bool tun_legacy_is_little_endian(struct tun_struct *tun)
{
return tun->flags & TUN_VNET_BE ? false :
virtio_legacy_is_little_endian();
}

static long tun_get_vnet_be(struct tun_struct *tun, int __user *argp)
{
int be = !!(tun->flags & TUN_VNET_BE);

if (put_user(be, argp))
return -EFAULT;

return 0;
}

static long tun_set_vnet_be(struct tun_struct *tun, int __user *argp)
{
int be;

if (get_user(be, argp))
return -EFAULT;

if (be)
tun->flags |= TUN_VNET_BE;
else
tun->flags &= ~TUN_VNET_BE;

return 0;
}
#else
static inline bool tun_legacy_is_little_endian(struct tun_struct *tun)
{
return virtio_legacy_is_little_endian();
}

static long tun_get_vnet_be(struct tun_struct *tun, int __user *argp)
{
return -EINVAL;
}

static long tun_set_vnet_be(struct tun_struct *tun, int __user *argp)
{
return -EINVAL;
}
#endif /* CONFIG_TUN_VNET_CROSS_LE */

static inline bool tun_is_little_endian(struct tun_struct *tun)
{
return tun->flags & TUN_VNET_LE ||
virtio_legacy_is_little_endian();
tun_legacy_is_little_endian(tun);
}

static inline u16 tun16_to_cpu(struct tun_struct *tun, __virtio16 val)
Expand Down Expand Up @@ -2062,6 +2111,14 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
tun->flags &= ~TUN_VNET_LE;
break;

case TUNGETVNETBE:
ret = tun_get_vnet_be(tun, argp);
break;

case TUNSETVNETBE:
ret = tun_set_vnet_be(tun, argp);
break;

case TUNATTACHFILTER:
/* Can be set only for TAPs */
ret = -EINVAL;
Expand Down
6 changes: 6 additions & 0 deletions include/uapi/linux/if_tun.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@
#define TUNGETFILTER _IOR('T', 219, struct sock_fprog)
#define TUNSETVNETLE _IOW('T', 220, int)
#define TUNGETVNETLE _IOR('T', 221, int)
/* The TUNSETVNETBE and TUNGETVNETBE ioctls are for cross-endian support on
* little-endian hosts. Not all kernel configurations support them, but all
* configurations that support SET also support GET.
*/
#define TUNSETVNETBE _IOW('T', 222, int)
#define TUNGETVNETBE _IOR('T', 223, int)

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

0 comments on commit 8b8e658

Please sign in to comment.