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