2 * QEMU network structures definitions and helper functions
4 * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com)
6 * Developed by Daynix Computing LTD (http://www.daynix.com)
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.
18 #include "qemu/osdep.h"
20 #include "net/checksum.h"
23 void eth_setup_vlan_headers_ex(struct eth_header *ehdr, uint16_t vlan_tag,
24 uint16_t vlan_ethtype, bool *is_new)
26 struct vlan_header *vhdr = PKT_GET_VLAN_HDR(ehdr);
28 switch (be16_to_cpu(ehdr->h_proto)) {
36 /* No VLAN header, put a new one */
37 vhdr->h_proto = ehdr->h_proto;
38 ehdr->h_proto = cpu_to_be16(vlan_ethtype);
42 vhdr->h_tci = cpu_to_be16(vlan_tag);
46 eth_get_gso_type(uint16_t l3_proto, uint8_t *l3_hdr, uint8_t l4proto)
48 uint8_t ecn_state = 0;
50 if (l3_proto == ETH_P_IP) {
51 struct ip_header *iphdr = (struct ip_header *) l3_hdr;
53 if (IP_HEADER_VERSION(iphdr) == IP_HEADER_VERSION_4) {
54 if (IPTOS_ECN(iphdr->ip_tos) == IPTOS_ECN_CE) {
55 ecn_state = VIRTIO_NET_HDR_GSO_ECN;
57 if (l4proto == IP_PROTO_TCP) {
58 return VIRTIO_NET_HDR_GSO_TCPV4 | ecn_state;
59 } else if (l4proto == IP_PROTO_UDP) {
60 return VIRTIO_NET_HDR_GSO_UDP | ecn_state;
63 } else if (l3_proto == ETH_P_IPV6) {
64 struct ip6_header *ip6hdr = (struct ip6_header *) l3_hdr;
66 if (IP6_ECN(ip6hdr->ip6_ecn_acc) == IP6_ECN_CE) {
67 ecn_state = VIRTIO_NET_HDR_GSO_ECN;
70 if (l4proto == IP_PROTO_TCP) {
71 return VIRTIO_NET_HDR_GSO_TCPV6 | ecn_state;
75 /* Unsupported offload */
76 g_assert_not_reached();
78 return VIRTIO_NET_HDR_GSO_NONE | ecn_state;
82 eth_get_l3_proto(const struct iovec *l2hdr_iov, int iovcnt, size_t l2hdr_len)
86 size_t size = iov_size(l2hdr_iov, iovcnt);
87 size_t proto_offset = l2hdr_len - sizeof(proto);
89 if (size < proto_offset) {
93 copied = iov_to_buf(l2hdr_iov, iovcnt, proto_offset,
94 &proto, sizeof(proto));
96 return (copied == sizeof(proto)) ? be16_to_cpu(proto) : ETH_P_UNKNOWN;
100 _eth_copy_chunk(size_t input_size,
101 const struct iovec *iov, int iovcnt,
102 size_t offset, size_t length,
107 if (input_size < offset) {
111 copied = iov_to_buf(iov, iovcnt, offset, buffer, length);
113 if (copied < length) {
121 _eth_tcp_has_data(bool is_ip4,
122 const struct ip_header *ip4_hdr,
123 const struct ip6_header *ip6_hdr,
124 size_t full_ip6hdr_len,
125 const struct tcp_header *tcp)
130 l4len = be16_to_cpu(ip4_hdr->ip_len) - IP_HDR_GET_LEN(ip4_hdr);
132 size_t opts_len = full_ip6hdr_len - sizeof(struct ip6_header);
133 l4len = be16_to_cpu(ip6_hdr->ip6_ctlun.ip6_un1.ip6_un1_plen) - opts_len;
136 return l4len > TCP_HEADER_DATA_OFFSET(tcp);
139 void eth_get_protocols(const struct iovec *iov, int iovcnt,
140 bool *isip4, bool *isip6,
141 bool *isudp, bool *istcp,
145 eth_ip6_hdr_info *ip6hdr_info,
146 eth_ip4_hdr_info *ip4hdr_info,
147 eth_l4_hdr_info *l4hdr_info)
150 bool fragment = false;
151 size_t l2hdr_len = eth_get_l2_hdr_length_iov(iov, iovcnt);
152 size_t input_size = iov_size(iov, iovcnt);
155 *isip4 = *isip6 = *isudp = *istcp = false;
157 proto = eth_get_l3_proto(iov, iovcnt, l2hdr_len);
159 *l3hdr_off = l2hdr_len;
161 if (proto == ETH_P_IP) {
162 struct ip_header *iphdr = &ip4hdr_info->ip4_hdr;
164 if (input_size < l2hdr_len) {
168 copied = iov_to_buf(iov, iovcnt, l2hdr_len, iphdr, sizeof(*iphdr));
172 if (copied < sizeof(*iphdr)) {
176 if (IP_HEADER_VERSION(iphdr) == IP_HEADER_VERSION_4) {
177 if (iphdr->ip_p == IP_PROTO_TCP) {
179 } else if (iphdr->ip_p == IP_PROTO_UDP) {
184 ip4hdr_info->fragment = IP4_IS_FRAGMENT(iphdr);
185 *l4hdr_off = l2hdr_len + IP_HDR_GET_LEN(iphdr);
187 fragment = ip4hdr_info->fragment;
188 } else if (proto == ETH_P_IPV6) {
191 if (eth_parse_ipv6_hdr(iov, iovcnt, l2hdr_len,
193 if (ip6hdr_info->l4proto == IP_PROTO_TCP) {
195 } else if (ip6hdr_info->l4proto == IP_PROTO_UDP) {
202 *l4hdr_off = l2hdr_len + ip6hdr_info->full_hdr_len;
203 fragment = ip6hdr_info->fragment;
208 *istcp = _eth_copy_chunk(input_size,
210 *l4hdr_off, sizeof(l4hdr_info->hdr.tcp),
211 &l4hdr_info->hdr.tcp);
214 *l5hdr_off = *l4hdr_off +
215 TCP_HEADER_DATA_OFFSET(&l4hdr_info->hdr.tcp);
217 l4hdr_info->has_tcp_data =
218 _eth_tcp_has_data(proto == ETH_P_IP,
219 &ip4hdr_info->ip4_hdr,
220 &ip6hdr_info->ip6_hdr,
221 *l4hdr_off - *l3hdr_off,
222 &l4hdr_info->hdr.tcp);
225 *isudp = _eth_copy_chunk(input_size,
227 *l4hdr_off, sizeof(l4hdr_info->hdr.udp),
228 &l4hdr_info->hdr.udp);
229 *l5hdr_off = *l4hdr_off + sizeof(l4hdr_info->hdr.udp);
235 eth_strip_vlan(const struct iovec *iov, int iovcnt, size_t iovoff,
236 uint8_t *new_ehdr_buf,
237 uint16_t *payload_offset, uint16_t *tci)
239 struct vlan_header vlan_hdr;
240 struct eth_header *new_ehdr = (struct eth_header *) new_ehdr_buf;
242 size_t copied = iov_to_buf(iov, iovcnt, iovoff,
243 new_ehdr, sizeof(*new_ehdr));
245 if (copied < sizeof(*new_ehdr)) {
249 switch (be16_to_cpu(new_ehdr->h_proto)) {
252 copied = iov_to_buf(iov, iovcnt, iovoff + sizeof(*new_ehdr),
253 &vlan_hdr, sizeof(vlan_hdr));
255 if (copied < sizeof(vlan_hdr)) {
259 new_ehdr->h_proto = vlan_hdr.h_proto;
261 *tci = be16_to_cpu(vlan_hdr.h_tci);
262 *payload_offset = iovoff + sizeof(*new_ehdr) + sizeof(vlan_hdr);
264 if (be16_to_cpu(new_ehdr->h_proto) == ETH_P_VLAN) {
266 copied = iov_to_buf(iov, iovcnt, *payload_offset,
267 PKT_GET_VLAN_HDR(new_ehdr), sizeof(vlan_hdr));
269 if (copied < sizeof(vlan_hdr)) {
273 *payload_offset += sizeof(vlan_hdr);
275 return sizeof(struct eth_header) + sizeof(struct vlan_header);
277 return sizeof(struct eth_header);
285 eth_strip_vlan_ex(const struct iovec *iov, int iovcnt, size_t iovoff,
286 uint16_t vet, uint8_t *new_ehdr_buf,
287 uint16_t *payload_offset, uint16_t *tci)
289 struct vlan_header vlan_hdr;
290 struct eth_header *new_ehdr = (struct eth_header *) new_ehdr_buf;
292 size_t copied = iov_to_buf(iov, iovcnt, iovoff,
293 new_ehdr, sizeof(*new_ehdr));
295 if (copied < sizeof(*new_ehdr)) {
299 if (be16_to_cpu(new_ehdr->h_proto) == vet) {
300 copied = iov_to_buf(iov, iovcnt, iovoff + sizeof(*new_ehdr),
301 &vlan_hdr, sizeof(vlan_hdr));
303 if (copied < sizeof(vlan_hdr)) {
307 new_ehdr->h_proto = vlan_hdr.h_proto;
309 *tci = be16_to_cpu(vlan_hdr.h_tci);
310 *payload_offset = iovoff + sizeof(*new_ehdr) + sizeof(vlan_hdr);
311 return sizeof(struct eth_header);
318 eth_setup_ip4_fragmentation(const void *l2hdr, size_t l2hdr_len,
319 void *l3hdr, size_t l3hdr_len,
320 size_t l3payload_len,
321 size_t frag_offset, bool more_frags)
323 const struct iovec l2vec = {
324 .iov_base = (void *) l2hdr,
328 if (eth_get_l3_proto(&l2vec, 1, l2hdr_len) == ETH_P_IP) {
330 struct ip_header *iphdr = (struct ip_header *) l3hdr;
331 uint16_t frag_off_units = frag_offset / IP_FRAG_UNIT_SIZE;
334 assert(frag_offset % IP_FRAG_UNIT_SIZE == 0);
335 assert((frag_off_units & ~IP_OFFMASK) == 0);
337 orig_flags = be16_to_cpu(iphdr->ip_off) & ~(IP_OFFMASK|IP_MF);
338 new_ip_off = frag_off_units | orig_flags | (more_frags ? IP_MF : 0);
339 iphdr->ip_off = cpu_to_be16(new_ip_off);
340 iphdr->ip_len = cpu_to_be16(l3payload_len + l3hdr_len);
345 eth_fix_ip4_checksum(void *l3hdr, size_t l3hdr_len)
347 struct ip_header *iphdr = (struct ip_header *) l3hdr;
349 iphdr->ip_sum = cpu_to_be16(net_raw_checksum(l3hdr, l3hdr_len));
353 eth_calc_ip4_pseudo_hdr_csum(struct ip_header *iphdr,
357 struct ip_pseudo_header ipph;
358 ipph.ip_src = iphdr->ip_src;
359 ipph.ip_dst = iphdr->ip_dst;
360 ipph.ip_payload = cpu_to_be16(csl);
361 ipph.ip_proto = iphdr->ip_p;
364 return net_checksum_add(*cso, (uint8_t *) &ipph);
368 eth_calc_ip6_pseudo_hdr_csum(struct ip6_header *iphdr,
373 struct ip6_pseudo_header ipph;
374 ipph.ip6_src = iphdr->ip6_src;
375 ipph.ip6_dst = iphdr->ip6_dst;
376 ipph.len = cpu_to_be16(csl);
380 ipph.next_hdr = l4_proto;
382 return net_checksum_add(*cso, (uint8_t *)&ipph);
386 eth_is_ip6_extension_header_type(uint8_t hdr_type)
393 case IP6_AUTHENTICATION:
403 _eth_get_rss_ex_dst_addr(const struct iovec *pkt, int pkt_frags,
405 struct ip6_ext_hdr *ext_hdr,
406 struct in6_address *dst_addr)
408 struct ip6_ext_hdr_routing *rthdr = (struct ip6_ext_hdr_routing *) ext_hdr;
410 if ((rthdr->rtype == 2) &&
411 (rthdr->len == sizeof(struct in6_address) / 8) &&
412 (rthdr->segleft == 1)) {
414 size_t input_size = iov_size(pkt, pkt_frags);
417 if (input_size < rthdr_offset + sizeof(*ext_hdr)) {
421 bytes_read = iov_to_buf(pkt, pkt_frags,
422 rthdr_offset + sizeof(*ext_hdr),
423 dst_addr, sizeof(*dst_addr));
425 return bytes_read == sizeof(*dst_addr);
432 _eth_get_rss_ex_src_addr(const struct iovec *pkt, int pkt_frags,
433 size_t dsthdr_offset,
434 struct ip6_ext_hdr *ext_hdr,
435 struct in6_address *src_addr)
437 size_t bytes_left = (ext_hdr->ip6r_len + 1) * 8 - sizeof(*ext_hdr);
438 struct ip6_option_hdr opthdr;
439 size_t opt_offset = dsthdr_offset + sizeof(*ext_hdr);
441 while (bytes_left > sizeof(opthdr)) {
442 size_t input_size = iov_size(pkt, pkt_frags);
443 size_t bytes_read, optlen;
445 if (input_size < opt_offset) {
449 bytes_read = iov_to_buf(pkt, pkt_frags, opt_offset,
450 &opthdr, sizeof(opthdr));
452 if (bytes_read != sizeof(opthdr)) {
456 optlen = (opthdr.type == IP6_OPT_PAD1) ? 1
457 : (opthdr.len + sizeof(opthdr));
459 if (optlen > bytes_left) {
463 if (opthdr.type == IP6_OPT_HOME) {
464 size_t input_size = iov_size(pkt, pkt_frags);
466 if (input_size < opt_offset + sizeof(opthdr)) {
470 bytes_read = iov_to_buf(pkt, pkt_frags,
471 opt_offset + sizeof(opthdr),
472 src_addr, sizeof(*src_addr));
474 return bytes_read == sizeof(*src_addr);
477 opt_offset += optlen;
478 bytes_left -= optlen;
484 bool eth_parse_ipv6_hdr(const struct iovec *pkt, int pkt_frags,
485 size_t ip6hdr_off, eth_ip6_hdr_info *info)
487 struct ip6_ext_hdr ext_hdr;
489 uint8_t curr_ext_hdr_type;
490 size_t input_size = iov_size(pkt, pkt_frags);
492 info->rss_ex_dst_valid = false;
493 info->rss_ex_src_valid = false;
494 info->fragment = false;
496 if (input_size < ip6hdr_off) {
500 bytes_read = iov_to_buf(pkt, pkt_frags, ip6hdr_off,
501 &info->ip6_hdr, sizeof(info->ip6_hdr));
502 if (bytes_read < sizeof(info->ip6_hdr)) {
506 info->full_hdr_len = sizeof(struct ip6_header);
508 curr_ext_hdr_type = info->ip6_hdr.ip6_nxt;
510 if (!eth_is_ip6_extension_header_type(curr_ext_hdr_type)) {
511 info->l4proto = info->ip6_hdr.ip6_nxt;
512 info->has_ext_hdrs = false;
516 info->has_ext_hdrs = true;
519 if (input_size < ip6hdr_off + info->full_hdr_len) {
523 bytes_read = iov_to_buf(pkt, pkt_frags, ip6hdr_off + info->full_hdr_len,
524 &ext_hdr, sizeof(ext_hdr));
526 if (bytes_read < sizeof(ext_hdr)) {
530 if (curr_ext_hdr_type == IP6_ROUTING) {
531 info->rss_ex_dst_valid =
532 _eth_get_rss_ex_dst_addr(pkt, pkt_frags,
533 ip6hdr_off + info->full_hdr_len,
534 &ext_hdr, &info->rss_ex_dst);
535 } else if (curr_ext_hdr_type == IP6_DESTINATON) {
536 info->rss_ex_src_valid =
537 _eth_get_rss_ex_src_addr(pkt, pkt_frags,
538 ip6hdr_off + info->full_hdr_len,
539 &ext_hdr, &info->rss_ex_src);
540 } else if (curr_ext_hdr_type == IP6_FRAGMENT) {
541 info->fragment = true;
544 info->full_hdr_len += (ext_hdr.ip6r_len + 1) * IP6_EXT_GRANULARITY;
545 curr_ext_hdr_type = ext_hdr.ip6r_nxt;
546 } while (eth_is_ip6_extension_header_type(curr_ext_hdr_type));
548 info->l4proto = ext_hdr.ip6r_nxt;