1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) 2017 - Cambridge Greys Limited |
4 | * Copyright (C) 2011 - 2014 Cisco Systems Inc |
5 | */ |
6 | |
7 | #include <linux/etherdevice.h> |
8 | #include <linux/netdevice.h> |
9 | #include <linux/skbuff.h> |
10 | #include <linux/slab.h> |
11 | #include <asm/byteorder.h> |
12 | #include <uapi/linux/ip.h> |
13 | #include <uapi/linux/virtio_net.h> |
14 | #include <linux/virtio_net.h> |
15 | #include <linux/virtio_byteorder.h> |
16 | #include <linux/netdev_features.h> |
17 | #include "vector_user.h" |
18 | #include "vector_kern.h" |
19 | |
20 | #define GOOD_LINEAR 512 |
21 | #define GSO_ERROR "Incoming GSO frames and GRO disabled on the interface" |
22 | |
23 | struct { |
24 | uint16_t ; |
25 | uint16_t ; |
26 | }; |
27 | |
28 | |
29 | struct uml_gre_data { |
30 | uint32_t rx_key; |
31 | uint32_t tx_key; |
32 | uint32_t sequence; |
33 | |
34 | bool ipv6; |
35 | bool has_sequence; |
36 | bool pin_sequence; |
37 | bool checksum; |
38 | bool key; |
39 | struct gre_minimal_header ; |
40 | |
41 | uint32_t checksum_offset; |
42 | uint32_t key_offset; |
43 | uint32_t sequence_offset; |
44 | |
45 | }; |
46 | |
47 | struct uml_l2tpv3_data { |
48 | uint64_t rx_cookie; |
49 | uint64_t tx_cookie; |
50 | uint64_t rx_session; |
51 | uint64_t tx_session; |
52 | uint32_t counter; |
53 | |
54 | bool udp; |
55 | bool ipv6; |
56 | bool has_counter; |
57 | bool pin_counter; |
58 | bool cookie; |
59 | bool cookie_is_64; |
60 | |
61 | uint32_t cookie_offset; |
62 | uint32_t session_offset; |
63 | uint32_t counter_offset; |
64 | }; |
65 | |
66 | static int (uint8_t *, |
67 | struct sk_buff *skb, struct vector_private *vp) |
68 | { |
69 | struct uml_l2tpv3_data *td = vp->transport_data; |
70 | uint32_t *counter; |
71 | |
72 | if (td->udp) |
73 | *(uint32_t *) header = cpu_to_be32(L2TPV3_DATA_PACKET); |
74 | (*(uint32_t *) (header + td->session_offset)) = td->tx_session; |
75 | |
76 | if (td->cookie) { |
77 | if (td->cookie_is_64) |
78 | (*(uint64_t *)(header + td->cookie_offset)) = |
79 | td->tx_cookie; |
80 | else |
81 | (*(uint32_t *)(header + td->cookie_offset)) = |
82 | td->tx_cookie; |
83 | } |
84 | if (td->has_counter) { |
85 | counter = (uint32_t *)(header + td->counter_offset); |
86 | if (td->pin_counter) { |
87 | *counter = 0; |
88 | } else { |
89 | td->counter++; |
90 | *counter = cpu_to_be32(td->counter); |
91 | } |
92 | } |
93 | return 0; |
94 | } |
95 | |
96 | static int (uint8_t *, |
97 | struct sk_buff *skb, struct vector_private *vp) |
98 | { |
99 | struct uml_gre_data *td = vp->transport_data; |
100 | uint32_t *sequence; |
101 | *((uint32_t *) header) = *((uint32_t *) &td->expected_header); |
102 | if (td->key) |
103 | (*(uint32_t *) (header + td->key_offset)) = td->tx_key; |
104 | if (td->has_sequence) { |
105 | sequence = (uint32_t *)(header + td->sequence_offset); |
106 | if (td->pin_sequence) |
107 | *sequence = 0; |
108 | else |
109 | *sequence = cpu_to_be32(++td->sequence); |
110 | } |
111 | return 0; |
112 | } |
113 | |
114 | static int (uint8_t *, |
115 | struct sk_buff *skb, struct vector_private *vp) |
116 | { |
117 | struct virtio_net_hdr * = (struct virtio_net_hdr *) header; |
118 | |
119 | virtio_net_hdr_from_skb( |
120 | skb, |
121 | hdr: vheader, |
122 | little_endian: virtio_legacy_is_little_endian(), |
123 | has_data_valid: false, |
124 | vlan_hlen: 0 |
125 | ); |
126 | |
127 | return 0; |
128 | } |
129 | |
130 | static int ( |
131 | uint8_t *, struct sk_buff *skb, struct vector_private *vp) |
132 | { |
133 | struct uml_l2tpv3_data *td = vp->transport_data; |
134 | uint32_t *session; |
135 | uint64_t cookie; |
136 | |
137 | if ((!td->udp) && (!td->ipv6)) |
138 | header += sizeof(struct iphdr) /* fix for ipv4 raw */; |
139 | |
140 | /* we do not do a strict check for "data" packets as per |
141 | * the RFC spec because the pure IP spec does not have |
142 | * that anyway. |
143 | */ |
144 | |
145 | if (td->cookie) { |
146 | if (td->cookie_is_64) |
147 | cookie = *(uint64_t *)(header + td->cookie_offset); |
148 | else |
149 | cookie = *(uint32_t *)(header + td->cookie_offset); |
150 | if (cookie != td->rx_cookie) { |
151 | if (net_ratelimit()) |
152 | netdev_err(dev: vp->dev, format: "uml_l2tpv3: unknown cookie id" ); |
153 | return -1; |
154 | } |
155 | } |
156 | session = (uint32_t *) (header + td->session_offset); |
157 | if (*session != td->rx_session) { |
158 | if (net_ratelimit()) |
159 | netdev_err(dev: vp->dev, format: "uml_l2tpv3: session mismatch" ); |
160 | return -1; |
161 | } |
162 | return 0; |
163 | } |
164 | |
165 | static int ( |
166 | uint8_t *, struct sk_buff *skb, struct vector_private *vp) |
167 | { |
168 | |
169 | uint32_t key; |
170 | struct uml_gre_data *td = vp->transport_data; |
171 | |
172 | if (!td->ipv6) |
173 | header += sizeof(struct iphdr) /* fix for ipv4 raw */; |
174 | |
175 | if (*((uint32_t *) header) != *((uint32_t *) &td->expected_header)) { |
176 | if (net_ratelimit()) |
177 | netdev_err(dev: vp->dev, format: "header type disagreement, expecting %0x, got %0x" , |
178 | *((uint32_t *) &td->expected_header), |
179 | *((uint32_t *) header) |
180 | ); |
181 | return -1; |
182 | } |
183 | |
184 | if (td->key) { |
185 | key = (*(uint32_t *)(header + td->key_offset)); |
186 | if (key != td->rx_key) { |
187 | if (net_ratelimit()) |
188 | netdev_err(dev: vp->dev, format: "unknown key id %0x, expecting %0x" , |
189 | key, td->rx_key); |
190 | return -1; |
191 | } |
192 | } |
193 | return 0; |
194 | } |
195 | |
196 | static int ( |
197 | uint8_t *, struct sk_buff *skb, struct vector_private *vp) |
198 | { |
199 | struct virtio_net_hdr * = (struct virtio_net_hdr *) header; |
200 | |
201 | if ((vheader->gso_type != VIRTIO_NET_HDR_GSO_NONE) && |
202 | (vp->req_size != 65536)) { |
203 | if (net_ratelimit()) |
204 | netdev_err( |
205 | dev: vp->dev, |
206 | GSO_ERROR |
207 | ); |
208 | } |
209 | if ((vheader->flags & VIRTIO_NET_HDR_F_DATA_VALID) > 0) |
210 | return 1; |
211 | |
212 | virtio_net_hdr_to_skb(skb, hdr: vheader, little_endian: virtio_legacy_is_little_endian()); |
213 | return 0; |
214 | } |
215 | |
216 | static bool get_uint_param( |
217 | struct arglist *def, char *param, unsigned int *result) |
218 | { |
219 | char *arg = uml_vector_fetch_arg(ifspec: def, token: param); |
220 | |
221 | if (arg != NULL) { |
222 | if (kstrtoint(s: arg, base: 0, res: result) == 0) |
223 | return true; |
224 | } |
225 | return false; |
226 | } |
227 | |
228 | static bool get_ulong_param( |
229 | struct arglist *def, char *param, unsigned long *result) |
230 | { |
231 | char *arg = uml_vector_fetch_arg(ifspec: def, token: param); |
232 | |
233 | if (arg != NULL) { |
234 | if (kstrtoul(s: arg, base: 0, res: result) == 0) |
235 | return true; |
236 | return true; |
237 | } |
238 | return false; |
239 | } |
240 | |
241 | static int build_gre_transport_data(struct vector_private *vp) |
242 | { |
243 | struct uml_gre_data *td; |
244 | int temp_int; |
245 | int temp_rx; |
246 | int temp_tx; |
247 | |
248 | vp->transport_data = kmalloc(size: sizeof(struct uml_gre_data), GFP_KERNEL); |
249 | if (vp->transport_data == NULL) |
250 | return -ENOMEM; |
251 | td = vp->transport_data; |
252 | td->sequence = 0; |
253 | |
254 | td->expected_header.arptype = GRE_IRB; |
255 | td->expected_header.header = 0; |
256 | |
257 | vp->form_header = &gre_form_header; |
258 | vp->verify_header = &gre_verify_header; |
259 | vp->header_size = 4; |
260 | td->key_offset = 4; |
261 | td->sequence_offset = 4; |
262 | td->checksum_offset = 4; |
263 | |
264 | td->ipv6 = false; |
265 | if (get_uint_param(def: vp->parsed, param: "v6" , result: &temp_int)) { |
266 | if (temp_int > 0) |
267 | td->ipv6 = true; |
268 | } |
269 | td->key = false; |
270 | if (get_uint_param(def: vp->parsed, param: "rx_key" , result: &temp_rx)) { |
271 | if (get_uint_param(def: vp->parsed, param: "tx_key" , result: &temp_tx)) { |
272 | td->key = true; |
273 | td->expected_header.header |= GRE_MODE_KEY; |
274 | td->rx_key = cpu_to_be32(temp_rx); |
275 | td->tx_key = cpu_to_be32(temp_tx); |
276 | vp->header_size += 4; |
277 | td->sequence_offset += 4; |
278 | } else { |
279 | return -EINVAL; |
280 | } |
281 | } |
282 | |
283 | td->sequence = false; |
284 | if (get_uint_param(def: vp->parsed, param: "sequence" , result: &temp_int)) { |
285 | if (temp_int > 0) { |
286 | vp->header_size += 4; |
287 | td->has_sequence = true; |
288 | td->expected_header.header |= GRE_MODE_SEQUENCE; |
289 | if (get_uint_param( |
290 | def: vp->parsed, param: "pin_sequence" , result: &temp_int)) { |
291 | if (temp_int > 0) |
292 | td->pin_sequence = true; |
293 | } |
294 | } |
295 | } |
296 | vp->rx_header_size = vp->header_size; |
297 | if (!td->ipv6) |
298 | vp->rx_header_size += sizeof(struct iphdr); |
299 | return 0; |
300 | } |
301 | |
302 | static int build_l2tpv3_transport_data(struct vector_private *vp) |
303 | { |
304 | |
305 | struct uml_l2tpv3_data *td; |
306 | int temp_int, temp_rxs, temp_txs; |
307 | unsigned long temp_rx; |
308 | unsigned long temp_tx; |
309 | |
310 | vp->transport_data = kmalloc( |
311 | size: sizeof(struct uml_l2tpv3_data), GFP_KERNEL); |
312 | |
313 | if (vp->transport_data == NULL) |
314 | return -ENOMEM; |
315 | |
316 | td = vp->transport_data; |
317 | |
318 | vp->form_header = &l2tpv3_form_header; |
319 | vp->verify_header = &l2tpv3_verify_header; |
320 | td->counter = 0; |
321 | |
322 | vp->header_size = 4; |
323 | td->session_offset = 0; |
324 | td->cookie_offset = 4; |
325 | td->counter_offset = 4; |
326 | |
327 | |
328 | td->ipv6 = false; |
329 | if (get_uint_param(def: vp->parsed, param: "v6" , result: &temp_int)) { |
330 | if (temp_int > 0) |
331 | td->ipv6 = true; |
332 | } |
333 | |
334 | if (get_uint_param(def: vp->parsed, param: "rx_session" , result: &temp_rxs)) { |
335 | if (get_uint_param(def: vp->parsed, param: "tx_session" , result: &temp_txs)) { |
336 | td->tx_session = cpu_to_be32(temp_txs); |
337 | td->rx_session = cpu_to_be32(temp_rxs); |
338 | } else { |
339 | return -EINVAL; |
340 | } |
341 | } else { |
342 | return -EINVAL; |
343 | } |
344 | |
345 | td->cookie_is_64 = false; |
346 | if (get_uint_param(def: vp->parsed, param: "cookie64" , result: &temp_int)) { |
347 | if (temp_int > 0) |
348 | td->cookie_is_64 = true; |
349 | } |
350 | td->cookie = false; |
351 | if (get_ulong_param(def: vp->parsed, param: "rx_cookie" , result: &temp_rx)) { |
352 | if (get_ulong_param(def: vp->parsed, param: "tx_cookie" , result: &temp_tx)) { |
353 | td->cookie = true; |
354 | if (td->cookie_is_64) { |
355 | td->rx_cookie = cpu_to_be64(temp_rx); |
356 | td->tx_cookie = cpu_to_be64(temp_tx); |
357 | vp->header_size += 8; |
358 | td->counter_offset += 8; |
359 | } else { |
360 | td->rx_cookie = cpu_to_be32(temp_rx); |
361 | td->tx_cookie = cpu_to_be32(temp_tx); |
362 | vp->header_size += 4; |
363 | td->counter_offset += 4; |
364 | } |
365 | } else { |
366 | return -EINVAL; |
367 | } |
368 | } |
369 | |
370 | td->has_counter = false; |
371 | if (get_uint_param(def: vp->parsed, param: "counter" , result: &temp_int)) { |
372 | if (temp_int > 0) { |
373 | td->has_counter = true; |
374 | vp->header_size += 4; |
375 | if (get_uint_param( |
376 | def: vp->parsed, param: "pin_counter" , result: &temp_int)) { |
377 | if (temp_int > 0) |
378 | td->pin_counter = true; |
379 | } |
380 | } |
381 | } |
382 | |
383 | if (get_uint_param(def: vp->parsed, param: "udp" , result: &temp_int)) { |
384 | if (temp_int > 0) { |
385 | td->udp = true; |
386 | vp->header_size += 4; |
387 | td->counter_offset += 4; |
388 | td->session_offset += 4; |
389 | td->cookie_offset += 4; |
390 | } |
391 | } |
392 | |
393 | vp->rx_header_size = vp->header_size; |
394 | if ((!td->ipv6) && (!td->udp)) |
395 | vp->rx_header_size += sizeof(struct iphdr); |
396 | |
397 | return 0; |
398 | } |
399 | |
400 | static int build_raw_transport_data(struct vector_private *vp) |
401 | { |
402 | if (uml_raw_enable_vnet_headers(fd: vp->fds->rx_fd)) { |
403 | if (!uml_raw_enable_vnet_headers(fd: vp->fds->tx_fd)) |
404 | return -1; |
405 | vp->form_header = &raw_form_header; |
406 | vp->verify_header = &raw_verify_header; |
407 | vp->header_size = sizeof(struct virtio_net_hdr); |
408 | vp->rx_header_size = sizeof(struct virtio_net_hdr); |
409 | vp->dev->hw_features |= (NETIF_F_TSO | NETIF_F_GRO); |
410 | vp->dev->features |= |
411 | (NETIF_F_RXCSUM | NETIF_F_HW_CSUM | |
412 | NETIF_F_TSO | NETIF_F_GRO); |
413 | netdev_info( |
414 | dev: vp->dev, |
415 | format: "raw: using vnet headers for tso and tx/rx checksum" |
416 | ); |
417 | } |
418 | return 0; |
419 | } |
420 | |
421 | static int build_hybrid_transport_data(struct vector_private *vp) |
422 | { |
423 | if (uml_raw_enable_vnet_headers(fd: vp->fds->rx_fd)) { |
424 | vp->form_header = &raw_form_header; |
425 | vp->verify_header = &raw_verify_header; |
426 | vp->header_size = sizeof(struct virtio_net_hdr); |
427 | vp->rx_header_size = sizeof(struct virtio_net_hdr); |
428 | vp->dev->hw_features |= |
429 | (NETIF_F_TSO | NETIF_F_GSO | NETIF_F_GRO); |
430 | vp->dev->features |= |
431 | (NETIF_F_RXCSUM | NETIF_F_HW_CSUM | |
432 | NETIF_F_TSO | NETIF_F_GSO | NETIF_F_GRO); |
433 | netdev_info( |
434 | dev: vp->dev, |
435 | format: "tap/raw hybrid: using vnet headers for tso and tx/rx checksum" |
436 | ); |
437 | } else { |
438 | return 0; /* do not try to enable tap too if raw failed */ |
439 | } |
440 | if (uml_tap_enable_vnet_headers(fd: vp->fds->tx_fd)) |
441 | return 0; |
442 | return -1; |
443 | } |
444 | |
445 | static int build_tap_transport_data(struct vector_private *vp) |
446 | { |
447 | /* "Pure" tap uses the same fd for rx and tx */ |
448 | if (uml_tap_enable_vnet_headers(fd: vp->fds->tx_fd)) { |
449 | vp->form_header = &raw_form_header; |
450 | vp->verify_header = &raw_verify_header; |
451 | vp->header_size = sizeof(struct virtio_net_hdr); |
452 | vp->rx_header_size = sizeof(struct virtio_net_hdr); |
453 | vp->dev->hw_features |= |
454 | (NETIF_F_TSO | NETIF_F_GSO | NETIF_F_GRO); |
455 | vp->dev->features |= |
456 | (NETIF_F_RXCSUM | NETIF_F_HW_CSUM | |
457 | NETIF_F_TSO | NETIF_F_GSO | NETIF_F_GRO); |
458 | netdev_info( |
459 | dev: vp->dev, |
460 | format: "tap: using vnet headers for tso and tx/rx checksum" |
461 | ); |
462 | return 0; |
463 | } |
464 | return -1; |
465 | } |
466 | |
467 | |
468 | static int build_bess_transport_data(struct vector_private *vp) |
469 | { |
470 | vp->form_header = NULL; |
471 | vp->verify_header = NULL; |
472 | vp->header_size = 0; |
473 | vp->rx_header_size = 0; |
474 | return 0; |
475 | } |
476 | |
477 | int build_transport_data(struct vector_private *vp) |
478 | { |
479 | char *transport = uml_vector_fetch_arg(ifspec: vp->parsed, token: "transport" ); |
480 | |
481 | if (strncmp(transport, TRANS_GRE, TRANS_GRE_LEN) == 0) |
482 | return build_gre_transport_data(vp); |
483 | if (strncmp(transport, TRANS_L2TPV3, TRANS_L2TPV3_LEN) == 0) |
484 | return build_l2tpv3_transport_data(vp); |
485 | if (strncmp(transport, TRANS_RAW, TRANS_RAW_LEN) == 0) |
486 | return build_raw_transport_data(vp); |
487 | if (strncmp(transport, TRANS_TAP, TRANS_TAP_LEN) == 0) |
488 | return build_tap_transport_data(vp); |
489 | if (strncmp(transport, TRANS_HYBRID, TRANS_HYBRID_LEN) == 0) |
490 | return build_hybrid_transport_data(vp); |
491 | if (strncmp(transport, TRANS_BESS, TRANS_BESS_LEN) == 0) |
492 | return build_bess_transport_data(vp); |
493 | return 0; |
494 | } |
495 | |
496 | |