aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/tun.c
diff options
context:
space:
mode:
authorGreg Kurz <gkurz@linux.vnet.ibm.com>2015-04-24 08:50:36 -0400
committerMichael S. Tsirkin <mst@redhat.com>2015-06-01 09:48:56 -0400
commit8b8e658b16336f0f50aba733f51db636ef121f50 (patch)
tree07e2259234babcd1c2f535ff43e1bede107f74d2 /drivers/net/tun.c
parent2751c9882b947292fcfb084c4f604e01724af804 (diff)
macvtap/tun: cross-endian support for little-endian hosts
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>
Diffstat (limited to 'drivers/net/tun.c')
-rw-r--r--drivers/net/tun.c59
1 files changed, 58 insertions, 1 deletions
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index b210139bef8f..cb376b2d548a 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -111,6 +111,7 @@ do { \
111#define TUN_FASYNC IFF_ATTACH_QUEUE 111#define TUN_FASYNC IFF_ATTACH_QUEUE
112/* High bits in flags field are unused. */ 112/* High bits in flags field are unused. */
113#define TUN_VNET_LE 0x80000000 113#define TUN_VNET_LE 0x80000000
114#define TUN_VNET_BE 0x40000000
114 115
115#define TUN_FEATURES (IFF_NO_PI | IFF_ONE_QUEUE | IFF_VNET_HDR | \ 116#define TUN_FEATURES (IFF_NO_PI | IFF_ONE_QUEUE | IFF_VNET_HDR | \
116 IFF_MULTI_QUEUE) 117 IFF_MULTI_QUEUE)
@@ -206,10 +207,58 @@ struct tun_struct {
206 u32 flow_count; 207 u32 flow_count;
207}; 208};
208 209
210#ifdef CONFIG_TUN_VNET_CROSS_LE
211static inline bool tun_legacy_is_little_endian(struct tun_struct *tun)
212{
213 return tun->flags & TUN_VNET_BE ? false :
214 virtio_legacy_is_little_endian();
215}
216
217static long tun_get_vnet_be(struct tun_struct *tun, int __user *argp)
218{
219 int be = !!(tun->flags & TUN_VNET_BE);
220
221 if (put_user(be, argp))
222 return -EFAULT;
223
224 return 0;
225}
226
227static long tun_set_vnet_be(struct tun_struct *tun, int __user *argp)
228{
229 int be;
230
231 if (get_user(be, argp))
232 return -EFAULT;
233
234 if (be)
235 tun->flags |= TUN_VNET_BE;
236 else
237 tun->flags &= ~TUN_VNET_BE;
238
239 return 0;
240}
241#else
242static inline bool tun_legacy_is_little_endian(struct tun_struct *tun)
243{
244 return virtio_legacy_is_little_endian();
245}
246
247static long tun_get_vnet_be(struct tun_struct *tun, int __user *argp)
248{
249 return -EINVAL;
250}
251
252static long tun_set_vnet_be(struct tun_struct *tun, int __user *argp)
253{
254 return -EINVAL;
255}
256#endif /* CONFIG_TUN_VNET_CROSS_LE */
257
209static inline bool tun_is_little_endian(struct tun_struct *tun) 258static inline bool tun_is_little_endian(struct tun_struct *tun)
210{ 259{
211 return tun->flags & TUN_VNET_LE || 260 return tun->flags & TUN_VNET_LE ||
212 virtio_legacy_is_little_endian(); 261 tun_legacy_is_little_endian(tun);
213} 262}
214 263
215static inline u16 tun16_to_cpu(struct tun_struct *tun, __virtio16 val) 264static inline u16 tun16_to_cpu(struct tun_struct *tun, __virtio16 val)
@@ -2062,6 +2111,14 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
2062 tun->flags &= ~TUN_VNET_LE; 2111 tun->flags &= ~TUN_VNET_LE;
2063 break; 2112 break;
2064 2113
2114 case TUNGETVNETBE:
2115 ret = tun_get_vnet_be(tun, argp);
2116 break;
2117
2118 case TUNSETVNETBE:
2119 ret = tun_set_vnet_be(tun, argp);
2120 break;
2121
2065 case TUNATTACHFILTER: 2122 case TUNATTACHFILTER:
2066 /* Can be set only for TAPs */ 2123 /* Can be set only for TAPs */
2067 ret = -EINVAL; 2124 ret = -EINVAL;