xref: /linux/drivers/net/tun_vnet.h (revision 1a9239bb4253f9076b5b4b2a1a4e8d7defd77a95)
11d41e2faSAkihiko Odaki /* SPDX-License-Identifier: GPL-2.0-or-later */
21d41e2faSAkihiko Odaki #ifndef TUN_VNET_H
31d41e2faSAkihiko Odaki #define TUN_VNET_H
41d41e2faSAkihiko Odaki 
51d41e2faSAkihiko Odaki /* High bits in flags field are unused. */
61d41e2faSAkihiko Odaki #define TUN_VNET_LE     0x80000000
71d41e2faSAkihiko Odaki #define TUN_VNET_BE     0x40000000
81d41e2faSAkihiko Odaki 
tun_vnet_legacy_is_little_endian(unsigned int flags)91d41e2faSAkihiko Odaki static inline bool tun_vnet_legacy_is_little_endian(unsigned int flags)
101d41e2faSAkihiko Odaki {
111d41e2faSAkihiko Odaki 	bool be = IS_ENABLED(CONFIG_TUN_VNET_CROSS_LE) &&
121d41e2faSAkihiko Odaki 		  (flags & TUN_VNET_BE);
131d41e2faSAkihiko Odaki 
141d41e2faSAkihiko Odaki 	return !be && virtio_legacy_is_little_endian();
151d41e2faSAkihiko Odaki }
161d41e2faSAkihiko Odaki 
tun_get_vnet_be(unsigned int flags,int __user * argp)171d41e2faSAkihiko Odaki static inline long tun_get_vnet_be(unsigned int flags, int __user *argp)
181d41e2faSAkihiko Odaki {
191d41e2faSAkihiko Odaki 	int be = !!(flags & TUN_VNET_BE);
201d41e2faSAkihiko Odaki 
211d41e2faSAkihiko Odaki 	if (!IS_ENABLED(CONFIG_TUN_VNET_CROSS_LE))
221d41e2faSAkihiko Odaki 		return -EINVAL;
231d41e2faSAkihiko Odaki 
241d41e2faSAkihiko Odaki 	if (put_user(be, argp))
251d41e2faSAkihiko Odaki 		return -EFAULT;
261d41e2faSAkihiko Odaki 
271d41e2faSAkihiko Odaki 	return 0;
281d41e2faSAkihiko Odaki }
291d41e2faSAkihiko Odaki 
tun_set_vnet_be(unsigned int * flags,int __user * argp)301d41e2faSAkihiko Odaki static inline long tun_set_vnet_be(unsigned int *flags, int __user *argp)
311d41e2faSAkihiko Odaki {
321d41e2faSAkihiko Odaki 	int be;
331d41e2faSAkihiko Odaki 
341d41e2faSAkihiko Odaki 	if (!IS_ENABLED(CONFIG_TUN_VNET_CROSS_LE))
351d41e2faSAkihiko Odaki 		return -EINVAL;
361d41e2faSAkihiko Odaki 
371d41e2faSAkihiko Odaki 	if (get_user(be, argp))
381d41e2faSAkihiko Odaki 		return -EFAULT;
391d41e2faSAkihiko Odaki 
401d41e2faSAkihiko Odaki 	if (be)
411d41e2faSAkihiko Odaki 		*flags |= TUN_VNET_BE;
421d41e2faSAkihiko Odaki 	else
431d41e2faSAkihiko Odaki 		*flags &= ~TUN_VNET_BE;
441d41e2faSAkihiko Odaki 
451d41e2faSAkihiko Odaki 	return 0;
461d41e2faSAkihiko Odaki }
471d41e2faSAkihiko Odaki 
tun_vnet_is_little_endian(unsigned int flags)481d41e2faSAkihiko Odaki static inline bool tun_vnet_is_little_endian(unsigned int flags)
491d41e2faSAkihiko Odaki {
501d41e2faSAkihiko Odaki 	return flags & TUN_VNET_LE || tun_vnet_legacy_is_little_endian(flags);
511d41e2faSAkihiko Odaki }
521d41e2faSAkihiko Odaki 
tun_vnet16_to_cpu(unsigned int flags,__virtio16 val)531d41e2faSAkihiko Odaki static inline u16 tun_vnet16_to_cpu(unsigned int flags, __virtio16 val)
541d41e2faSAkihiko Odaki {
551d41e2faSAkihiko Odaki 	return __virtio16_to_cpu(tun_vnet_is_little_endian(flags), val);
561d41e2faSAkihiko Odaki }
571d41e2faSAkihiko Odaki 
cpu_to_tun_vnet16(unsigned int flags,u16 val)581d41e2faSAkihiko Odaki static inline __virtio16 cpu_to_tun_vnet16(unsigned int flags, u16 val)
591d41e2faSAkihiko Odaki {
601d41e2faSAkihiko Odaki 	return __cpu_to_virtio16(tun_vnet_is_little_endian(flags), val);
611d41e2faSAkihiko Odaki }
621d41e2faSAkihiko Odaki 
tun_vnet_ioctl(int * vnet_hdr_sz,unsigned int * flags,unsigned int cmd,int __user * sp)631d41e2faSAkihiko Odaki static inline long tun_vnet_ioctl(int *vnet_hdr_sz, unsigned int *flags,
641d41e2faSAkihiko Odaki 				  unsigned int cmd, int __user *sp)
651d41e2faSAkihiko Odaki {
661d41e2faSAkihiko Odaki 	int s;
671d41e2faSAkihiko Odaki 
681d41e2faSAkihiko Odaki 	switch (cmd) {
691d41e2faSAkihiko Odaki 	case TUNGETVNETHDRSZ:
701d41e2faSAkihiko Odaki 		s = *vnet_hdr_sz;
711d41e2faSAkihiko Odaki 		if (put_user(s, sp))
721d41e2faSAkihiko Odaki 			return -EFAULT;
731d41e2faSAkihiko Odaki 		return 0;
741d41e2faSAkihiko Odaki 
751d41e2faSAkihiko Odaki 	case TUNSETVNETHDRSZ:
761d41e2faSAkihiko Odaki 		if (get_user(s, sp))
771d41e2faSAkihiko Odaki 			return -EFAULT;
781d41e2faSAkihiko Odaki 		if (s < (int)sizeof(struct virtio_net_hdr))
791d41e2faSAkihiko Odaki 			return -EINVAL;
801d41e2faSAkihiko Odaki 
811d41e2faSAkihiko Odaki 		*vnet_hdr_sz = s;
821d41e2faSAkihiko Odaki 		return 0;
831d41e2faSAkihiko Odaki 
841d41e2faSAkihiko Odaki 	case TUNGETVNETLE:
851d41e2faSAkihiko Odaki 		s = !!(*flags & TUN_VNET_LE);
861d41e2faSAkihiko Odaki 		if (put_user(s, sp))
871d41e2faSAkihiko Odaki 			return -EFAULT;
881d41e2faSAkihiko Odaki 		return 0;
891d41e2faSAkihiko Odaki 
901d41e2faSAkihiko Odaki 	case TUNSETVNETLE:
911d41e2faSAkihiko Odaki 		if (get_user(s, sp))
921d41e2faSAkihiko Odaki 			return -EFAULT;
931d41e2faSAkihiko Odaki 		if (s)
941d41e2faSAkihiko Odaki 			*flags |= TUN_VNET_LE;
951d41e2faSAkihiko Odaki 		else
961d41e2faSAkihiko Odaki 			*flags &= ~TUN_VNET_LE;
971d41e2faSAkihiko Odaki 		return 0;
981d41e2faSAkihiko Odaki 
991d41e2faSAkihiko Odaki 	case TUNGETVNETBE:
1001d41e2faSAkihiko Odaki 		return tun_get_vnet_be(*flags, sp);
1011d41e2faSAkihiko Odaki 
1021d41e2faSAkihiko Odaki 	case TUNSETVNETBE:
1031d41e2faSAkihiko Odaki 		return tun_set_vnet_be(flags, sp);
1041d41e2faSAkihiko Odaki 
1051d41e2faSAkihiko Odaki 	default:
1061d41e2faSAkihiko Odaki 		return -EINVAL;
1071d41e2faSAkihiko Odaki 	}
1081d41e2faSAkihiko Odaki }
1091d41e2faSAkihiko Odaki 
tun_vnet_hdr_get(int sz,unsigned int flags,struct iov_iter * from,struct virtio_net_hdr * hdr)1101d41e2faSAkihiko Odaki static inline int tun_vnet_hdr_get(int sz, unsigned int flags,
1111d41e2faSAkihiko Odaki 				   struct iov_iter *from,
1121d41e2faSAkihiko Odaki 				   struct virtio_net_hdr *hdr)
1131d41e2faSAkihiko Odaki {
1141d41e2faSAkihiko Odaki 	u16 hdr_len;
1151d41e2faSAkihiko Odaki 
1161d41e2faSAkihiko Odaki 	if (iov_iter_count(from) < sz)
1171d41e2faSAkihiko Odaki 		return -EINVAL;
1181d41e2faSAkihiko Odaki 
1191d41e2faSAkihiko Odaki 	if (!copy_from_iter_full(hdr, sizeof(*hdr), from))
1201d41e2faSAkihiko Odaki 		return -EFAULT;
1211d41e2faSAkihiko Odaki 
1221d41e2faSAkihiko Odaki 	hdr_len = tun_vnet16_to_cpu(flags, hdr->hdr_len);
1231d41e2faSAkihiko Odaki 
1241d41e2faSAkihiko Odaki 	if (hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) {
1251d41e2faSAkihiko Odaki 		hdr_len = max(tun_vnet16_to_cpu(flags, hdr->csum_start) + tun_vnet16_to_cpu(flags, hdr->csum_offset) + 2, hdr_len);
1261d41e2faSAkihiko Odaki 		hdr->hdr_len = cpu_to_tun_vnet16(flags, hdr_len);
1271d41e2faSAkihiko Odaki 	}
1281d41e2faSAkihiko Odaki 
1291d41e2faSAkihiko Odaki 	if (hdr_len > iov_iter_count(from))
1301d41e2faSAkihiko Odaki 		return -EINVAL;
1311d41e2faSAkihiko Odaki 
1321d41e2faSAkihiko Odaki 	iov_iter_advance(from, sz - sizeof(*hdr));
1331d41e2faSAkihiko Odaki 
1341d41e2faSAkihiko Odaki 	return hdr_len;
1351d41e2faSAkihiko Odaki }
1361d41e2faSAkihiko Odaki 
tun_vnet_hdr_put(int sz,struct iov_iter * iter,const struct virtio_net_hdr * hdr)1371d41e2faSAkihiko Odaki static inline int tun_vnet_hdr_put(int sz, struct iov_iter *iter,
1381d41e2faSAkihiko Odaki 				   const struct virtio_net_hdr *hdr)
1391d41e2faSAkihiko Odaki {
1401d41e2faSAkihiko Odaki 	if (unlikely(iov_iter_count(iter) < sz))
1411d41e2faSAkihiko Odaki 		return -EINVAL;
1421d41e2faSAkihiko Odaki 
1431d41e2faSAkihiko Odaki 	if (unlikely(copy_to_iter(hdr, sizeof(*hdr), iter) != sizeof(*hdr)))
1441d41e2faSAkihiko Odaki 		return -EFAULT;
1451d41e2faSAkihiko Odaki 
146*4adf7497SAkihiko Odaki 	if (iov_iter_zero(sz - sizeof(*hdr), iter) != sz - sizeof(*hdr))
147*4adf7497SAkihiko Odaki 		return -EFAULT;
1481d41e2faSAkihiko Odaki 
1491d41e2faSAkihiko Odaki 	return 0;
1501d41e2faSAkihiko Odaki }
1511d41e2faSAkihiko Odaki 
tun_vnet_hdr_to_skb(unsigned int flags,struct sk_buff * skb,const struct virtio_net_hdr * hdr)1521d41e2faSAkihiko Odaki static inline int tun_vnet_hdr_to_skb(unsigned int flags, struct sk_buff *skb,
1531d41e2faSAkihiko Odaki 				      const struct virtio_net_hdr *hdr)
1541d41e2faSAkihiko Odaki {
1551d41e2faSAkihiko Odaki 	return virtio_net_hdr_to_skb(skb, hdr, tun_vnet_is_little_endian(flags));
1561d41e2faSAkihiko Odaki }
1571d41e2faSAkihiko Odaki 
tun_vnet_hdr_from_skb(unsigned int flags,const struct net_device * dev,const struct sk_buff * skb,struct virtio_net_hdr * hdr)1581d41e2faSAkihiko Odaki static inline int tun_vnet_hdr_from_skb(unsigned int flags,
1591d41e2faSAkihiko Odaki 					const struct net_device *dev,
1601d41e2faSAkihiko Odaki 					const struct sk_buff *skb,
1611d41e2faSAkihiko Odaki 					struct virtio_net_hdr *hdr)
1621d41e2faSAkihiko Odaki {
1631d41e2faSAkihiko Odaki 	int vlan_hlen = skb_vlan_tag_present(skb) ? VLAN_HLEN : 0;
1641d41e2faSAkihiko Odaki 
1651d41e2faSAkihiko Odaki 	if (virtio_net_hdr_from_skb(skb, hdr,
1661d41e2faSAkihiko Odaki 				    tun_vnet_is_little_endian(flags), true,
1671d41e2faSAkihiko Odaki 				    vlan_hlen)) {
1681d41e2faSAkihiko Odaki 		struct skb_shared_info *sinfo = skb_shinfo(skb);
1691d41e2faSAkihiko Odaki 
1701d41e2faSAkihiko Odaki 		if (net_ratelimit()) {
1711d41e2faSAkihiko Odaki 			netdev_err(dev, "unexpected GSO type: 0x%x, gso_size %d, hdr_len %d\n",
1721d41e2faSAkihiko Odaki 				   sinfo->gso_type, tun_vnet16_to_cpu(flags, hdr->gso_size),
1731d41e2faSAkihiko Odaki 				   tun_vnet16_to_cpu(flags, hdr->hdr_len));
1741d41e2faSAkihiko Odaki 			print_hex_dump(KERN_ERR, "tun: ",
1751d41e2faSAkihiko Odaki 				       DUMP_PREFIX_NONE,
1761d41e2faSAkihiko Odaki 				       16, 1, skb->head,
1771d41e2faSAkihiko Odaki 				       min(tun_vnet16_to_cpu(flags, hdr->hdr_len), 64), true);
1781d41e2faSAkihiko Odaki 		}
1791d41e2faSAkihiko Odaki 		WARN_ON_ONCE(1);
1801d41e2faSAkihiko Odaki 		return -EINVAL;
1811d41e2faSAkihiko Odaki 	}
1821d41e2faSAkihiko Odaki 
1831d41e2faSAkihiko Odaki 	return 0;
1841d41e2faSAkihiko Odaki }
1851d41e2faSAkihiko Odaki 
1861d41e2faSAkihiko Odaki #endif /* TUN_VNET_H */
187