]> git.proxmox.com Git - mirror_qemu.git/blame - net/eth.c
net: Remove useless local var pkt
[mirror_qemu.git] / net / eth.c
CommitLineData
75020a70
DF
1/*
2 * QEMU network structures definitions and helper functions
3 *
4 * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com)
5 *
6 * Developed by Daynix Computing LTD (http://www.daynix.com)
7 *
8 * Authors:
9 * Dmitry Fleytman <dmitry@daynix.com>
10 * Tamir Shomer <tamirs@daynix.com>
11 * Yan Vugenfirer <yan@daynix.com>
12 *
13 * This work is licensed under the terms of the GNU GPL, version 2 or later.
14 * See the COPYING file in the top-level directory.
15 *
16 */
17
2744d920 18#include "qemu/osdep.h"
75020a70
DF
19#include "net/eth.h"
20#include "net/checksum.h"
21#include "qemu-common.h"
22#include "net/tap.h"
23
eb700029
DF
24void eth_setup_vlan_headers_ex(struct eth_header *ehdr, uint16_t vlan_tag,
25 uint16_t vlan_ethtype, bool *is_new)
75020a70
DF
26{
27 struct vlan_header *vhdr = PKT_GET_VLAN_HDR(ehdr);
28
29 switch (be16_to_cpu(ehdr->h_proto)) {
30 case ETH_P_VLAN:
31 case ETH_P_DVLAN:
32 /* vlan hdr exists */
33 *is_new = false;
34 break;
35
36 default:
37 /* No VLAN header, put a new one */
38 vhdr->h_proto = ehdr->h_proto;
eb700029 39 ehdr->h_proto = cpu_to_be16(vlan_ethtype);
75020a70
DF
40 *is_new = true;
41 break;
42 }
43 vhdr->h_tci = cpu_to_be16(vlan_tag);
44}
45
46uint8_t
47eth_get_gso_type(uint16_t l3_proto, uint8_t *l3_hdr, uint8_t l4proto)
48{
49 uint8_t ecn_state = 0;
50
51 if (l3_proto == ETH_P_IP) {
52 struct ip_header *iphdr = (struct ip_header *) l3_hdr;
53
54 if (IP_HEADER_VERSION(iphdr) == IP_HEADER_VERSION_4) {
55 if (IPTOS_ECN(iphdr->ip_tos) == IPTOS_ECN_CE) {
56 ecn_state = VIRTIO_NET_HDR_GSO_ECN;
57 }
58 if (l4proto == IP_PROTO_TCP) {
59 return VIRTIO_NET_HDR_GSO_TCPV4 | ecn_state;
60 } else if (l4proto == IP_PROTO_UDP) {
61 return VIRTIO_NET_HDR_GSO_UDP | ecn_state;
62 }
63 }
64 } else if (l3_proto == ETH_P_IPV6) {
65 struct ip6_header *ip6hdr = (struct ip6_header *) l3_hdr;
66
67 if (IP6_ECN(ip6hdr->ip6_ecn_acc) == IP6_ECN_CE) {
68 ecn_state = VIRTIO_NET_HDR_GSO_ECN;
69 }
70
71 if (l4proto == IP_PROTO_TCP) {
72 return VIRTIO_NET_HDR_GSO_TCPV6 | ecn_state;
73 }
74 }
75
76 /* Unsupported offload */
dfc6f865 77 g_assert_not_reached();
75020a70
DF
78
79 return VIRTIO_NET_HDR_GSO_NONE | ecn_state;
80}
81
eb700029
DF
82uint16_t
83eth_get_l3_proto(const struct iovec *l2hdr_iov, int iovcnt, size_t l2hdr_len)
84{
85 uint16_t proto;
86 size_t copied;
87 size_t size = iov_size(l2hdr_iov, iovcnt);
88 size_t proto_offset = l2hdr_len - sizeof(proto);
89
90 if (size < proto_offset) {
91 return ETH_P_UNKNOWN;
92 }
93
94 copied = iov_to_buf(l2hdr_iov, iovcnt, proto_offset,
95 &proto, sizeof(proto));
96
97 return (copied == sizeof(proto)) ? be16_to_cpu(proto) : ETH_P_UNKNOWN;
98}
99
100static bool
101_eth_copy_chunk(size_t input_size,
102 const struct iovec *iov, int iovcnt,
103 size_t offset, size_t length,
104 void *buffer)
105{
106 size_t copied;
107
108 if (input_size < offset) {
109 return false;
110 }
111
112 copied = iov_to_buf(iov, iovcnt, offset, buffer, length);
113
114 if (copied < length) {
115 return false;
116 }
117
118 return true;
119}
120
121static bool
122_eth_tcp_has_data(bool is_ip4,
123 const struct ip_header *ip4_hdr,
124 const struct ip6_header *ip6_hdr,
125 size_t full_ip6hdr_len,
126 const struct tcp_header *tcp)
127{
128 uint32_t l4len;
129
130 if (is_ip4) {
131 l4len = be16_to_cpu(ip4_hdr->ip_len) - IP_HDR_GET_LEN(ip4_hdr);
132 } else {
133 size_t opts_len = full_ip6hdr_len - sizeof(struct ip6_header);
134 l4len = be16_to_cpu(ip6_hdr->ip6_ctlun.ip6_un1.ip6_un1_plen) - opts_len;
135 }
136
137 return l4len > TCP_HEADER_DATA_OFFSET(tcp);
138}
139
140void eth_get_protocols(const struct iovec *iov, int iovcnt,
75020a70 141 bool *isip4, bool *isip6,
eb700029
DF
142 bool *isudp, bool *istcp,
143 size_t *l3hdr_off,
144 size_t *l4hdr_off,
145 size_t *l5hdr_off,
146 eth_ip6_hdr_info *ip6hdr_info,
147 eth_ip4_hdr_info *ip4hdr_info,
148 eth_l4_hdr_info *l4hdr_info)
75020a70
DF
149{
150 int proto;
eb700029
DF
151 bool fragment = false;
152 size_t l2hdr_len = eth_get_l2_hdr_length_iov(iov, iovcnt);
153 size_t input_size = iov_size(iov, iovcnt);
154 size_t copied;
155
75020a70
DF
156 *isip4 = *isip6 = *isudp = *istcp = false;
157
eb700029
DF
158 proto = eth_get_l3_proto(iov, iovcnt, l2hdr_len);
159
160 *l3hdr_off = l2hdr_len;
161
75020a70 162 if (proto == ETH_P_IP) {
eb700029 163 struct ip_header *iphdr = &ip4hdr_info->ip4_hdr;
75020a70 164
eb700029
DF
165 if (input_size < l2hdr_len) {
166 return;
167 }
168
169 copied = iov_to_buf(iov, iovcnt, l2hdr_len, iphdr, sizeof(*iphdr));
75020a70 170
eb700029 171 *isip4 = true;
75020a70 172
eb700029
DF
173 if (copied < sizeof(*iphdr)) {
174 return;
175 }
75020a70
DF
176
177 if (IP_HEADER_VERSION(iphdr) == IP_HEADER_VERSION_4) {
178 if (iphdr->ip_p == IP_PROTO_TCP) {
179 *istcp = true;
180 } else if (iphdr->ip_p == IP_PROTO_UDP) {
181 *isudp = true;
182 }
183 }
75020a70 184
eb700029
DF
185 ip4hdr_info->fragment = IP4_IS_FRAGMENT(iphdr);
186 *l4hdr_off = l2hdr_len + IP_HDR_GET_LEN(iphdr);
187
188 fragment = ip4hdr_info->fragment;
189 } else if (proto == ETH_P_IPV6) {
75020a70
DF
190
191 *isip6 = true;
eb700029
DF
192 if (eth_parse_ipv6_hdr(iov, iovcnt, l2hdr_len,
193 ip6hdr_info)) {
194 if (ip6hdr_info->l4proto == IP_PROTO_TCP) {
75020a70 195 *istcp = true;
eb700029 196 } else if (ip6hdr_info->l4proto == IP_PROTO_UDP) {
75020a70
DF
197 *isudp = true;
198 }
eb700029
DF
199 } else {
200 return;
201 }
202
203 *l4hdr_off = l2hdr_len + ip6hdr_info->full_hdr_len;
204 fragment = ip6hdr_info->fragment;
205 }
206
207 if (!fragment) {
208 if (*istcp) {
209 *istcp = _eth_copy_chunk(input_size,
210 iov, iovcnt,
211 *l4hdr_off, sizeof(l4hdr_info->hdr.tcp),
212 &l4hdr_info->hdr.tcp);
213
2c5e564f 214 if (*istcp) {
eb700029
DF
215 *l5hdr_off = *l4hdr_off +
216 TCP_HEADER_DATA_OFFSET(&l4hdr_info->hdr.tcp);
217
218 l4hdr_info->has_tcp_data =
219 _eth_tcp_has_data(proto == ETH_P_IP,
220 &ip4hdr_info->ip4_hdr,
221 &ip6hdr_info->ip6_hdr,
222 *l4hdr_off - *l3hdr_off,
223 &l4hdr_info->hdr.tcp);
224 }
225 } else if (*isudp) {
226 *isudp = _eth_copy_chunk(input_size,
227 iov, iovcnt,
228 *l4hdr_off, sizeof(l4hdr_info->hdr.udp),
229 &l4hdr_info->hdr.udp);
230 *l5hdr_off = *l4hdr_off + sizeof(l4hdr_info->hdr.udp);
231 }
232 }
233}
234
235bool
236eth_strip_vlan(const struct iovec *iov, int iovcnt, size_t iovoff,
237 uint8_t *new_ehdr_buf,
238 uint16_t *payload_offset, uint16_t *tci)
239{
240 struct vlan_header vlan_hdr;
241 struct eth_header *new_ehdr = (struct eth_header *) new_ehdr_buf;
242
243 size_t copied = iov_to_buf(iov, iovcnt, iovoff,
244 new_ehdr, sizeof(*new_ehdr));
245
246 if (copied < sizeof(*new_ehdr)) {
247 return false;
248 }
249
250 switch (be16_to_cpu(new_ehdr->h_proto)) {
251 case ETH_P_VLAN:
252 case ETH_P_DVLAN:
253 copied = iov_to_buf(iov, iovcnt, iovoff + sizeof(*new_ehdr),
254 &vlan_hdr, sizeof(vlan_hdr));
255
256 if (copied < sizeof(vlan_hdr)) {
257 return false;
258 }
259
260 new_ehdr->h_proto = vlan_hdr.h_proto;
261
262 *tci = be16_to_cpu(vlan_hdr.h_tci);
263 *payload_offset = iovoff + sizeof(*new_ehdr) + sizeof(vlan_hdr);
264
265 if (be16_to_cpu(new_ehdr->h_proto) == ETH_P_VLAN) {
266
267 copied = iov_to_buf(iov, iovcnt, *payload_offset,
268 PKT_GET_VLAN_HDR(new_ehdr), sizeof(vlan_hdr));
269
270 if (copied < sizeof(vlan_hdr)) {
271 return false;
272 }
273
274 *payload_offset += sizeof(vlan_hdr);
275 }
276 return true;
277 default:
278 return false;
279 }
280}
281
282bool
283eth_strip_vlan_ex(const struct iovec *iov, int iovcnt, size_t iovoff,
284 uint16_t vet, uint8_t *new_ehdr_buf,
285 uint16_t *payload_offset, uint16_t *tci)
286{
287 struct vlan_header vlan_hdr;
288 struct eth_header *new_ehdr = (struct eth_header *) new_ehdr_buf;
289
290 size_t copied = iov_to_buf(iov, iovcnt, iovoff,
291 new_ehdr, sizeof(*new_ehdr));
292
293 if (copied < sizeof(*new_ehdr)) {
294 return false;
295 }
296
297 if (be16_to_cpu(new_ehdr->h_proto) == vet) {
298 copied = iov_to_buf(iov, iovcnt, iovoff + sizeof(*new_ehdr),
299 &vlan_hdr, sizeof(vlan_hdr));
300
301 if (copied < sizeof(vlan_hdr)) {
302 return false;
75020a70 303 }
eb700029
DF
304
305 new_ehdr->h_proto = vlan_hdr.h_proto;
306
307 *tci = be16_to_cpu(vlan_hdr.h_tci);
308 *payload_offset = iovoff + sizeof(*new_ehdr) + sizeof(vlan_hdr);
309 return true;
75020a70 310 }
eb700029
DF
311
312 return false;
75020a70
DF
313}
314
315void
316eth_setup_ip4_fragmentation(const void *l2hdr, size_t l2hdr_len,
317 void *l3hdr, size_t l3hdr_len,
318 size_t l3payload_len,
319 size_t frag_offset, bool more_frags)
320{
eb700029
DF
321 const struct iovec l2vec = {
322 .iov_base = (void *) l2hdr,
323 .iov_len = l2hdr_len
324 };
325
326 if (eth_get_l3_proto(&l2vec, 1, l2hdr_len) == ETH_P_IP) {
75020a70
DF
327 uint16_t orig_flags;
328 struct ip_header *iphdr = (struct ip_header *) l3hdr;
329 uint16_t frag_off_units = frag_offset / IP_FRAG_UNIT_SIZE;
330 uint16_t new_ip_off;
331
332 assert(frag_offset % IP_FRAG_UNIT_SIZE == 0);
333 assert((frag_off_units & ~IP_OFFMASK) == 0);
334
335 orig_flags = be16_to_cpu(iphdr->ip_off) & ~(IP_OFFMASK|IP_MF);
336 new_ip_off = frag_off_units | orig_flags | (more_frags ? IP_MF : 0);
337 iphdr->ip_off = cpu_to_be16(new_ip_off);
338 iphdr->ip_len = cpu_to_be16(l3payload_len + l3hdr_len);
339 }
340}
341
342void
343eth_fix_ip4_checksum(void *l3hdr, size_t l3hdr_len)
344{
345 struct ip_header *iphdr = (struct ip_header *) l3hdr;
346 iphdr->ip_sum = 0;
347 iphdr->ip_sum = cpu_to_be16(net_raw_checksum(l3hdr, l3hdr_len));
348}
349
350uint32_t
eb700029
DF
351eth_calc_ip4_pseudo_hdr_csum(struct ip_header *iphdr,
352 uint16_t csl,
353 uint32_t *cso)
75020a70
DF
354{
355 struct ip_pseudo_header ipph;
356 ipph.ip_src = iphdr->ip_src;
357 ipph.ip_dst = iphdr->ip_dst;
358 ipph.ip_payload = cpu_to_be16(csl);
359 ipph.ip_proto = iphdr->ip_p;
360 ipph.zeros = 0;
eb700029
DF
361 *cso = sizeof(ipph);
362 return net_checksum_add(*cso, (uint8_t *) &ipph);
363}
364
365uint32_t
366eth_calc_ip6_pseudo_hdr_csum(struct ip6_header *iphdr,
367 uint16_t csl,
368 uint8_t l4_proto,
369 uint32_t *cso)
370{
371 struct ip6_pseudo_header ipph;
372 ipph.ip6_src = iphdr->ip6_src;
373 ipph.ip6_dst = iphdr->ip6_dst;
374 ipph.len = cpu_to_be16(csl);
375 ipph.zero[0] = 0;
376 ipph.zero[1] = 0;
377 ipph.zero[2] = 0;
378 ipph.next_hdr = l4_proto;
379 *cso = sizeof(ipph);
380 return net_checksum_add(*cso, (uint8_t *)&ipph);
75020a70
DF
381}
382
383static bool
384eth_is_ip6_extension_header_type(uint8_t hdr_type)
385{
386 switch (hdr_type) {
387 case IP6_HOP_BY_HOP:
388 case IP6_ROUTING:
389 case IP6_FRAGMENT:
390 case IP6_ESP:
391 case IP6_AUTHENTICATION:
392 case IP6_DESTINATON:
393 case IP6_MOBILITY:
394 return true;
395 default:
396 return false;
397 }
398}
399
eb700029
DF
400static bool
401_eth_get_rss_ex_dst_addr(const struct iovec *pkt, int pkt_frags,
402 size_t rthdr_offset,
403 struct ip6_ext_hdr *ext_hdr,
404 struct in6_address *dst_addr)
405{
406 struct ip6_ext_hdr_routing *rthdr = (struct ip6_ext_hdr_routing *) ext_hdr;
407
408 if ((rthdr->rtype == 2) &&
409 (rthdr->len == sizeof(struct in6_address) / 8) &&
410 (rthdr->segleft == 1)) {
411
412 size_t input_size = iov_size(pkt, pkt_frags);
413 size_t bytes_read;
414
415 if (input_size < rthdr_offset + sizeof(*ext_hdr)) {
416 return false;
417 }
418
419 bytes_read = iov_to_buf(pkt, pkt_frags,
420 rthdr_offset + sizeof(*ext_hdr),
4555ca68 421 dst_addr, sizeof(*dst_addr));
eb700029
DF
422
423 return bytes_read == sizeof(dst_addr);
424 }
425
426 return false;
427}
428
429static bool
430_eth_get_rss_ex_src_addr(const struct iovec *pkt, int pkt_frags,
431 size_t dsthdr_offset,
432 struct ip6_ext_hdr *ext_hdr,
433 struct in6_address *src_addr)
434{
435 size_t bytes_left = (ext_hdr->ip6r_len + 1) * 8 - sizeof(*ext_hdr);
436 struct ip6_option_hdr opthdr;
437 size_t opt_offset = dsthdr_offset + sizeof(*ext_hdr);
438
439 while (bytes_left > sizeof(opthdr)) {
440 size_t input_size = iov_size(pkt, pkt_frags);
441 size_t bytes_read, optlen;
442
443 if (input_size < opt_offset) {
444 return false;
445 }
446
447 bytes_read = iov_to_buf(pkt, pkt_frags, opt_offset,
448 &opthdr, sizeof(opthdr));
449
450 if (bytes_read != sizeof(opthdr)) {
451 return false;
452 }
453
454 optlen = (opthdr.type == IP6_OPT_PAD1) ? 1
455 : (opthdr.len + sizeof(opthdr));
456
457 if (optlen > bytes_left) {
458 return false;
459 }
460
461 if (opthdr.type == IP6_OPT_HOME) {
462 size_t input_size = iov_size(pkt, pkt_frags);
463
464 if (input_size < opt_offset + sizeof(opthdr)) {
465 return false;
466 }
467
468 bytes_read = iov_to_buf(pkt, pkt_frags,
469 opt_offset + sizeof(opthdr),
4555ca68 470 src_addr, sizeof(*src_addr));
eb700029
DF
471
472 return bytes_read == sizeof(src_addr);
473 }
474
475 opt_offset += optlen;
476 bytes_left -= optlen;
477 }
478
479 return false;
480}
481
482bool eth_parse_ipv6_hdr(const struct iovec *pkt, int pkt_frags,
483 size_t ip6hdr_off, eth_ip6_hdr_info *info)
75020a70 484{
75020a70
DF
485 struct ip6_ext_hdr ext_hdr;
486 size_t bytes_read;
eb700029
DF
487 uint8_t curr_ext_hdr_type;
488 size_t input_size = iov_size(pkt, pkt_frags);
489
490 info->rss_ex_dst_valid = false;
491 info->rss_ex_src_valid = false;
492 info->fragment = false;
493
494 if (input_size < ip6hdr_off) {
495 return false;
496 }
75020a70
DF
497
498 bytes_read = iov_to_buf(pkt, pkt_frags, ip6hdr_off,
eb700029
DF
499 &info->ip6_hdr, sizeof(info->ip6_hdr));
500 if (bytes_read < sizeof(info->ip6_hdr)) {
75020a70
DF
501 return false;
502 }
503
eb700029
DF
504 info->full_hdr_len = sizeof(struct ip6_header);
505
506 curr_ext_hdr_type = info->ip6_hdr.ip6_nxt;
75020a70 507
eb700029
DF
508 if (!eth_is_ip6_extension_header_type(curr_ext_hdr_type)) {
509 info->l4proto = info->ip6_hdr.ip6_nxt;
510 info->has_ext_hdrs = false;
75020a70
DF
511 return true;
512 }
513
eb700029
DF
514 info->has_ext_hdrs = true;
515
75020a70 516 do {
eb700029
DF
517 if (input_size < ip6hdr_off + info->full_hdr_len) {
518 return false;
519 }
520
521 bytes_read = iov_to_buf(pkt, pkt_frags, ip6hdr_off + info->full_hdr_len,
75020a70 522 &ext_hdr, sizeof(ext_hdr));
75020a70 523
eb700029
DF
524 if (bytes_read < sizeof(ext_hdr)) {
525 return false;
526 }
527
528 if (curr_ext_hdr_type == IP6_ROUTING) {
529 info->rss_ex_dst_valid =
530 _eth_get_rss_ex_dst_addr(pkt, pkt_frags,
531 ip6hdr_off + info->full_hdr_len,
532 &ext_hdr, &info->rss_ex_dst);
533 } else if (curr_ext_hdr_type == IP6_DESTINATON) {
534 info->rss_ex_src_valid =
535 _eth_get_rss_ex_src_addr(pkt, pkt_frags,
536 ip6hdr_off + info->full_hdr_len,
537 &ext_hdr, &info->rss_ex_src);
538 } else if (curr_ext_hdr_type == IP6_FRAGMENT) {
539 info->fragment = true;
540 }
541
542 info->full_hdr_len += (ext_hdr.ip6r_len + 1) * IP6_EXT_GRANULARITY;
543 curr_ext_hdr_type = ext_hdr.ip6r_nxt;
544 } while (eth_is_ip6_extension_header_type(curr_ext_hdr_type));
545
546 info->l4proto = ext_hdr.ip6r_nxt;
75020a70
DF
547 return true;
548}