tun_vnet.h 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. /* SPDX-License-Identifier: GPL-2.0-or-later */
  2. #ifndef TUN_VNET_H
  3. #define TUN_VNET_H
  4. /* High bits in flags field are unused. */
  5. #define TUN_VNET_LE 0x80000000
  6. #define TUN_VNET_BE 0x40000000
  7. #define TUN_VNET_TNL_SIZE sizeof(struct virtio_net_hdr_v1_hash_tunnel)
  8. static inline bool tun_vnet_legacy_is_little_endian(unsigned int flags)
  9. {
  10. bool be = IS_ENABLED(CONFIG_TUN_VNET_CROSS_LE) &&
  11. (flags & TUN_VNET_BE);
  12. return !be && virtio_legacy_is_little_endian();
  13. }
  14. static inline long tun_get_vnet_be(unsigned int flags, int __user *argp)
  15. {
  16. int be = !!(flags & TUN_VNET_BE);
  17. if (!IS_ENABLED(CONFIG_TUN_VNET_CROSS_LE))
  18. return -EINVAL;
  19. if (put_user(be, argp))
  20. return -EFAULT;
  21. return 0;
  22. }
  23. static inline long tun_set_vnet_be(unsigned int *flags, int __user *argp)
  24. {
  25. int be;
  26. if (!IS_ENABLED(CONFIG_TUN_VNET_CROSS_LE))
  27. return -EINVAL;
  28. if (get_user(be, argp))
  29. return -EFAULT;
  30. if (be)
  31. *flags |= TUN_VNET_BE;
  32. else
  33. *flags &= ~TUN_VNET_BE;
  34. return 0;
  35. }
  36. static inline bool tun_vnet_is_little_endian(unsigned int flags)
  37. {
  38. return flags & TUN_VNET_LE || tun_vnet_legacy_is_little_endian(flags);
  39. }
  40. static inline u16 tun_vnet16_to_cpu(unsigned int flags, __virtio16 val)
  41. {
  42. return __virtio16_to_cpu(tun_vnet_is_little_endian(flags), val);
  43. }
  44. static inline __virtio16 cpu_to_tun_vnet16(unsigned int flags, u16 val)
  45. {
  46. return __cpu_to_virtio16(tun_vnet_is_little_endian(flags), val);
  47. }
  48. static inline long tun_vnet_ioctl(int *vnet_hdr_sz, unsigned int *flags,
  49. unsigned int cmd, int __user *sp)
  50. {
  51. int s;
  52. switch (cmd) {
  53. case TUNGETVNETHDRSZ:
  54. s = *vnet_hdr_sz;
  55. if (put_user(s, sp))
  56. return -EFAULT;
  57. return 0;
  58. case TUNSETVNETHDRSZ:
  59. if (get_user(s, sp))
  60. return -EFAULT;
  61. if (s < (int)sizeof(struct virtio_net_hdr))
  62. return -EINVAL;
  63. *vnet_hdr_sz = s;
  64. return 0;
  65. case TUNGETVNETLE:
  66. s = !!(*flags & TUN_VNET_LE);
  67. if (put_user(s, sp))
  68. return -EFAULT;
  69. return 0;
  70. case TUNSETVNETLE:
  71. if (get_user(s, sp))
  72. return -EFAULT;
  73. if (s)
  74. *flags |= TUN_VNET_LE;
  75. else
  76. *flags &= ~TUN_VNET_LE;
  77. return 0;
  78. case TUNGETVNETBE:
  79. return tun_get_vnet_be(*flags, sp);
  80. case TUNSETVNETBE:
  81. return tun_set_vnet_be(flags, sp);
  82. default:
  83. return -EINVAL;
  84. }
  85. }
  86. static inline unsigned int tun_vnet_parse_size(netdev_features_t features)
  87. {
  88. if (!(features & NETIF_F_GSO_UDP_TUNNEL))
  89. return sizeof(struct virtio_net_hdr);
  90. return TUN_VNET_TNL_SIZE;
  91. }
  92. static inline int __tun_vnet_hdr_get(int sz, unsigned int flags,
  93. netdev_features_t features,
  94. struct iov_iter *from,
  95. struct virtio_net_hdr *hdr)
  96. {
  97. unsigned int parsed_size = tun_vnet_parse_size(features);
  98. u16 hdr_len;
  99. if (iov_iter_count(from) < sz)
  100. return -EINVAL;
  101. if (!copy_from_iter_full(hdr, parsed_size, from))
  102. return -EFAULT;
  103. hdr_len = tun_vnet16_to_cpu(flags, hdr->hdr_len);
  104. if (hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) {
  105. hdr_len = max(tun_vnet16_to_cpu(flags, hdr->csum_start) + tun_vnet16_to_cpu(flags, hdr->csum_offset) + 2, hdr_len);
  106. hdr->hdr_len = cpu_to_tun_vnet16(flags, hdr_len);
  107. }
  108. if (hdr_len > iov_iter_count(from))
  109. return -EINVAL;
  110. iov_iter_advance(from, sz - parsed_size);
  111. return hdr_len;
  112. }
  113. static inline int tun_vnet_hdr_get(int sz, unsigned int flags,
  114. struct iov_iter *from,
  115. struct virtio_net_hdr *hdr)
  116. {
  117. return __tun_vnet_hdr_get(sz, flags, 0, from, hdr);
  118. }
  119. static inline int __tun_vnet_hdr_put(int sz, netdev_features_t features,
  120. struct iov_iter *iter,
  121. const struct virtio_net_hdr *hdr)
  122. {
  123. unsigned int parsed_size = tun_vnet_parse_size(features);
  124. if (unlikely(iov_iter_count(iter) < sz))
  125. return -EINVAL;
  126. if (unlikely(copy_to_iter(hdr, parsed_size, iter) != parsed_size))
  127. return -EFAULT;
  128. if (iov_iter_zero(sz - parsed_size, iter) != sz - parsed_size)
  129. return -EFAULT;
  130. return 0;
  131. }
  132. static inline int tun_vnet_hdr_put(int sz, struct iov_iter *iter,
  133. const struct virtio_net_hdr *hdr)
  134. {
  135. return __tun_vnet_hdr_put(sz, 0, iter, hdr);
  136. }
  137. static inline int tun_vnet_hdr_to_skb(unsigned int flags, struct sk_buff *skb,
  138. const struct virtio_net_hdr *hdr)
  139. {
  140. return virtio_net_hdr_to_skb(skb, hdr, tun_vnet_is_little_endian(flags));
  141. }
  142. /*
  143. * Tun is not aware of the negotiated guest features, guess them from the
  144. * virtio net hdr size
  145. */
  146. static inline netdev_features_t tun_vnet_hdr_guest_features(int vnet_hdr_sz)
  147. {
  148. if (vnet_hdr_sz >= TUN_VNET_TNL_SIZE)
  149. return NETIF_F_GSO_UDP_TUNNEL | NETIF_F_GSO_UDP_TUNNEL_CSUM;
  150. return 0;
  151. }
  152. static inline int
  153. tun_vnet_hdr_tnl_to_skb(unsigned int flags, netdev_features_t features,
  154. struct sk_buff *skb,
  155. const struct virtio_net_hdr_v1_hash_tunnel *hdr)
  156. {
  157. return virtio_net_hdr_tnl_to_skb(skb, hdr,
  158. features & NETIF_F_GSO_UDP_TUNNEL,
  159. features & NETIF_F_GSO_UDP_TUNNEL_CSUM,
  160. tun_vnet_is_little_endian(flags));
  161. }
  162. static inline int tun_vnet_hdr_from_skb(unsigned int flags,
  163. const struct net_device *dev,
  164. const struct sk_buff *skb,
  165. struct virtio_net_hdr *hdr)
  166. {
  167. int vlan_hlen = skb_vlan_tag_present(skb) ? VLAN_HLEN : 0;
  168. if (virtio_net_hdr_from_skb(skb, hdr,
  169. tun_vnet_is_little_endian(flags), true,
  170. vlan_hlen)) {
  171. struct skb_shared_info *sinfo = skb_shinfo(skb);
  172. if (net_ratelimit()) {
  173. netdev_err(dev, "unexpected GSO type: 0x%x, gso_size %d, hdr_len %d\n",
  174. sinfo->gso_type, tun_vnet16_to_cpu(flags, hdr->gso_size),
  175. tun_vnet16_to_cpu(flags, hdr->hdr_len));
  176. print_hex_dump(KERN_ERR, "tun: ",
  177. DUMP_PREFIX_NONE,
  178. 16, 1, skb->head,
  179. min(tun_vnet16_to_cpu(flags, hdr->hdr_len), 64), true);
  180. }
  181. WARN_ON_ONCE(1);
  182. return -EINVAL;
  183. }
  184. return 0;
  185. }
  186. static inline int
  187. tun_vnet_hdr_tnl_from_skb(unsigned int flags,
  188. const struct net_device *dev,
  189. const struct sk_buff *skb,
  190. struct virtio_net_hdr_v1_hash_tunnel *tnl_hdr)
  191. {
  192. bool has_tnl_offload = !!(dev->features & NETIF_F_GSO_UDP_TUNNEL);
  193. int vlan_hlen = skb_vlan_tag_present(skb) ? VLAN_HLEN : 0;
  194. if (virtio_net_hdr_tnl_from_skb(skb, tnl_hdr, has_tnl_offload,
  195. tun_vnet_is_little_endian(flags),
  196. vlan_hlen, true, false)) {
  197. struct virtio_net_hdr_v1 *hdr = &tnl_hdr->hash_hdr.hdr;
  198. struct skb_shared_info *sinfo = skb_shinfo(skb);
  199. if (net_ratelimit()) {
  200. int hdr_len = tun_vnet16_to_cpu(flags, hdr->hdr_len);
  201. netdev_err(dev, "unexpected GSO type: 0x%x, gso_size %d, hdr_len %d\n",
  202. sinfo->gso_type,
  203. tun_vnet16_to_cpu(flags, hdr->gso_size),
  204. tun_vnet16_to_cpu(flags, hdr->hdr_len));
  205. print_hex_dump(KERN_ERR, "tun: ", DUMP_PREFIX_NONE,
  206. 16, 1, skb->head, min(hdr_len, 64),
  207. true);
  208. }
  209. WARN_ON_ONCE(1);
  210. return -EINVAL;
  211. }
  212. return 0;
  213. }
  214. #endif /* TUN_VNET_H */