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 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 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 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 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 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 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 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 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 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 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 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