| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 |
- /* SPDX-License-Identifier: GPL-2.0-or-later */
- #ifndef TUN_VNET_H
- #define TUN_VNET_H
- /* High bits in flags field are unused. */
- #define TUN_VNET_LE 0x80000000
- #define TUN_VNET_BE 0x40000000
- #define TUN_VNET_TNL_SIZE sizeof(struct virtio_net_hdr_v1_hash_tunnel)
- static inline bool tun_vnet_legacy_is_little_endian(unsigned int flags)
- {
- bool be = IS_ENABLED(CONFIG_TUN_VNET_CROSS_LE) &&
- (flags & TUN_VNET_BE);
- return !be && virtio_legacy_is_little_endian();
- }
- static inline long tun_get_vnet_be(unsigned int flags, int __user *argp)
- {
- int be = !!(flags & TUN_VNET_BE);
- if (!IS_ENABLED(CONFIG_TUN_VNET_CROSS_LE))
- return -EINVAL;
- if (put_user(be, argp))
- return -EFAULT;
- return 0;
- }
- static inline long tun_set_vnet_be(unsigned int *flags, int __user *argp)
- {
- int be;
- if (!IS_ENABLED(CONFIG_TUN_VNET_CROSS_LE))
- return -EINVAL;
- if (get_user(be, argp))
- return -EFAULT;
- if (be)
- *flags |= TUN_VNET_BE;
- else
- *flags &= ~TUN_VNET_BE;
- return 0;
- }
- static inline bool tun_vnet_is_little_endian(unsigned int flags)
- {
- return flags & TUN_VNET_LE || tun_vnet_legacy_is_little_endian(flags);
- }
- static inline u16 tun_vnet16_to_cpu(unsigned int flags, __virtio16 val)
- {
- return __virtio16_to_cpu(tun_vnet_is_little_endian(flags), val);
- }
- static inline __virtio16 cpu_to_tun_vnet16(unsigned int flags, u16 val)
- {
- return __cpu_to_virtio16(tun_vnet_is_little_endian(flags), val);
- }
- static inline long tun_vnet_ioctl(int *vnet_hdr_sz, unsigned int *flags,
- unsigned int cmd, int __user *sp)
- {
- int s;
- switch (cmd) {
- case TUNGETVNETHDRSZ:
- s = *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;
- *vnet_hdr_sz = s;
- return 0;
- case TUNGETVNETLE:
- s = !!(*flags & TUN_VNET_LE);
- if (put_user(s, sp))
- return -EFAULT;
- return 0;
- case TUNSETVNETLE:
- if (get_user(s, sp))
- return -EFAULT;
- if (s)
- *flags |= TUN_VNET_LE;
- else
- *flags &= ~TUN_VNET_LE;
- return 0;
- case TUNGETVNETBE:
- return tun_get_vnet_be(*flags, sp);
- case TUNSETVNETBE:
- return tun_set_vnet_be(flags, sp);
- default:
- return -EINVAL;
- }
- }
- static inline unsigned int tun_vnet_parse_size(netdev_features_t features)
- {
- if (!(features & NETIF_F_GSO_UDP_TUNNEL))
- return sizeof(struct virtio_net_hdr);
- return TUN_VNET_TNL_SIZE;
- }
- static inline int __tun_vnet_hdr_get(int sz, unsigned int flags,
- netdev_features_t features,
- struct iov_iter *from,
- struct virtio_net_hdr *hdr)
- {
- unsigned int parsed_size = tun_vnet_parse_size(features);
- u16 hdr_len;
- if (iov_iter_count(from) < sz)
- return -EINVAL;
- if (!copy_from_iter_full(hdr, parsed_size, from))
- return -EFAULT;
- hdr_len = tun_vnet16_to_cpu(flags, hdr->hdr_len);
- if (hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) {
- hdr_len = max(tun_vnet16_to_cpu(flags, hdr->csum_start) + tun_vnet16_to_cpu(flags, hdr->csum_offset) + 2, hdr_len);
- hdr->hdr_len = cpu_to_tun_vnet16(flags, hdr_len);
- }
- if (hdr_len > iov_iter_count(from))
- return -EINVAL;
- iov_iter_advance(from, sz - parsed_size);
- return hdr_len;
- }
- static inline int tun_vnet_hdr_get(int sz, unsigned int flags,
- struct iov_iter *from,
- struct virtio_net_hdr *hdr)
- {
- return __tun_vnet_hdr_get(sz, flags, 0, from, hdr);
- }
- static inline int __tun_vnet_hdr_put(int sz, netdev_features_t features,
- struct iov_iter *iter,
- const struct virtio_net_hdr *hdr)
- {
- unsigned int parsed_size = tun_vnet_parse_size(features);
- if (unlikely(iov_iter_count(iter) < sz))
- return -EINVAL;
- if (unlikely(copy_to_iter(hdr, parsed_size, iter) != parsed_size))
- return -EFAULT;
- if (iov_iter_zero(sz - parsed_size, iter) != sz - parsed_size)
- return -EFAULT;
- return 0;
- }
- static inline int tun_vnet_hdr_put(int sz, struct iov_iter *iter,
- const struct virtio_net_hdr *hdr)
- {
- return __tun_vnet_hdr_put(sz, 0, iter, hdr);
- }
- static inline int tun_vnet_hdr_to_skb(unsigned int flags, struct sk_buff *skb,
- const struct virtio_net_hdr *hdr)
- {
- return virtio_net_hdr_to_skb(skb, hdr, tun_vnet_is_little_endian(flags));
- }
- /*
- * Tun is not aware of the negotiated guest features, guess them from the
- * virtio net hdr size
- */
- static inline netdev_features_t tun_vnet_hdr_guest_features(int vnet_hdr_sz)
- {
- if (vnet_hdr_sz >= TUN_VNET_TNL_SIZE)
- return NETIF_F_GSO_UDP_TUNNEL | NETIF_F_GSO_UDP_TUNNEL_CSUM;
- return 0;
- }
- static inline int
- tun_vnet_hdr_tnl_to_skb(unsigned int flags, netdev_features_t features,
- struct sk_buff *skb,
- const struct virtio_net_hdr_v1_hash_tunnel *hdr)
- {
- return virtio_net_hdr_tnl_to_skb(skb, hdr,
- features & NETIF_F_GSO_UDP_TUNNEL,
- features & NETIF_F_GSO_UDP_TUNNEL_CSUM,
- tun_vnet_is_little_endian(flags));
- }
- static inline int tun_vnet_hdr_from_skb(unsigned int flags,
- const struct net_device *dev,
- const struct sk_buff *skb,
- struct virtio_net_hdr *hdr)
- {
- int vlan_hlen = skb_vlan_tag_present(skb) ? VLAN_HLEN : 0;
- if (virtio_net_hdr_from_skb(skb, hdr,
- tun_vnet_is_little_endian(flags), true,
- vlan_hlen)) {
- struct skb_shared_info *sinfo = skb_shinfo(skb);
- if (net_ratelimit()) {
- netdev_err(dev, "unexpected GSO type: 0x%x, gso_size %d, hdr_len %d\n",
- sinfo->gso_type, tun_vnet16_to_cpu(flags, hdr->gso_size),
- tun_vnet16_to_cpu(flags, hdr->hdr_len));
- print_hex_dump(KERN_ERR, "tun: ",
- DUMP_PREFIX_NONE,
- 16, 1, skb->head,
- min(tun_vnet16_to_cpu(flags, hdr->hdr_len), 64), true);
- }
- WARN_ON_ONCE(1);
- return -EINVAL;
- }
- return 0;
- }
- static inline int
- tun_vnet_hdr_tnl_from_skb(unsigned int flags,
- const struct net_device *dev,
- const struct sk_buff *skb,
- struct virtio_net_hdr_v1_hash_tunnel *tnl_hdr)
- {
- bool has_tnl_offload = !!(dev->features & NETIF_F_GSO_UDP_TUNNEL);
- int vlan_hlen = skb_vlan_tag_present(skb) ? VLAN_HLEN : 0;
- if (virtio_net_hdr_tnl_from_skb(skb, tnl_hdr, has_tnl_offload,
- tun_vnet_is_little_endian(flags),
- vlan_hlen, true, false)) {
- struct virtio_net_hdr_v1 *hdr = &tnl_hdr->hash_hdr.hdr;
- struct skb_shared_info *sinfo = skb_shinfo(skb);
- if (net_ratelimit()) {
- int hdr_len = tun_vnet16_to_cpu(flags, hdr->hdr_len);
- netdev_err(dev, "unexpected GSO type: 0x%x, gso_size %d, hdr_len %d\n",
- sinfo->gso_type,
- tun_vnet16_to_cpu(flags, hdr->gso_size),
- tun_vnet16_to_cpu(flags, hdr->hdr_len));
- print_hex_dump(KERN_ERR, "tun: ", DUMP_PREFIX_NONE,
- 16, 1, skb->head, min(hdr_len, 64),
- true);
- }
- WARN_ON_ONCE(1);
- return -EINVAL;
- }
- return 0;
- }
- #endif /* TUN_VNET_H */
|