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