 b2caa3b82e
			
		
	
	
		b2caa3b82e
		
	
	
	
	
		
			
			So we have sizeof(struct in6_address) != sizeof(uintptr_t)
and Clang > Coverity on this, see 4555ca6816c :)
net/eth.c:426:30: warning: The code calls sizeof() on a pointer type. This can produce an unexpected result
        return bytes_read == sizeof(dst_addr);
                             ^     ~~~~~~~~~~
net/eth.c:475:34: warning: The code calls sizeof() on a pointer type. This can produce an unexpected result
            return bytes_read == sizeof(src_addr);
                                 ^     ~~~~~~~~~~
Reported-by: Clang Static Analyzer
Signed-off-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Reviewed-by: Dmitry Fleytman <dmitry@daynix.com>
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
		
	
			
		
			
				
	
	
		
			552 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			552 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * QEMU network structures definitions and helper functions
 | |
|  *
 | |
|  * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com)
 | |
|  *
 | |
|  * Developed by Daynix Computing LTD (http://www.daynix.com)
 | |
|  *
 | |
|  * Authors:
 | |
|  * Dmitry Fleytman <dmitry@daynix.com>
 | |
|  * Tamir Shomer <tamirs@daynix.com>
 | |
|  * Yan Vugenfirer <yan@daynix.com>
 | |
|  *
 | |
|  * This work is licensed under the terms of the GNU GPL, version 2 or later.
 | |
|  * See the COPYING file in the top-level directory.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #include "qemu/osdep.h"
 | |
| #include "net/eth.h"
 | |
| #include "net/checksum.h"
 | |
| #include "qemu-common.h"
 | |
| #include "net/tap.h"
 | |
| 
 | |
| void eth_setup_vlan_headers_ex(struct eth_header *ehdr, uint16_t vlan_tag,
 | |
|     uint16_t vlan_ethtype, bool *is_new)
 | |
| {
 | |
|     struct vlan_header *vhdr = PKT_GET_VLAN_HDR(ehdr);
 | |
| 
 | |
|     switch (be16_to_cpu(ehdr->h_proto)) {
 | |
|     case ETH_P_VLAN:
 | |
|     case ETH_P_DVLAN:
 | |
|         /* vlan hdr exists */
 | |
|         *is_new = false;
 | |
|         break;
 | |
| 
 | |
|     default:
 | |
|         /* No VLAN header, put a new one */
 | |
|         vhdr->h_proto = ehdr->h_proto;
 | |
|         ehdr->h_proto = cpu_to_be16(vlan_ethtype);
 | |
|         *is_new = true;
 | |
|         break;
 | |
|     }
 | |
|     vhdr->h_tci = cpu_to_be16(vlan_tag);
 | |
| }
 | |
| 
 | |
| uint8_t
 | |
| eth_get_gso_type(uint16_t l3_proto, uint8_t *l3_hdr, uint8_t l4proto)
 | |
| {
 | |
|     uint8_t ecn_state = 0;
 | |
| 
 | |
|     if (l3_proto == ETH_P_IP) {
 | |
|         struct ip_header *iphdr = (struct ip_header *) l3_hdr;
 | |
| 
 | |
|         if (IP_HEADER_VERSION(iphdr) == IP_HEADER_VERSION_4) {
 | |
|             if (IPTOS_ECN(iphdr->ip_tos) == IPTOS_ECN_CE) {
 | |
|                 ecn_state = VIRTIO_NET_HDR_GSO_ECN;
 | |
|             }
 | |
|             if (l4proto == IP_PROTO_TCP) {
 | |
|                 return VIRTIO_NET_HDR_GSO_TCPV4 | ecn_state;
 | |
|             } else if (l4proto == IP_PROTO_UDP) {
 | |
|                 return VIRTIO_NET_HDR_GSO_UDP | ecn_state;
 | |
|             }
 | |
|         }
 | |
|     } else if (l3_proto == ETH_P_IPV6) {
 | |
|         struct ip6_header *ip6hdr = (struct ip6_header *) l3_hdr;
 | |
| 
 | |
|         if (IP6_ECN(ip6hdr->ip6_ecn_acc) == IP6_ECN_CE) {
 | |
|             ecn_state = VIRTIO_NET_HDR_GSO_ECN;
 | |
|         }
 | |
| 
 | |
|         if (l4proto == IP_PROTO_TCP) {
 | |
|             return VIRTIO_NET_HDR_GSO_TCPV6 | ecn_state;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* Unsupported offload */
 | |
|     g_assert_not_reached();
 | |
| 
 | |
|     return VIRTIO_NET_HDR_GSO_NONE | ecn_state;
 | |
| }
 | |
| 
 | |
| uint16_t
 | |
| eth_get_l3_proto(const struct iovec *l2hdr_iov, int iovcnt, size_t l2hdr_len)
 | |
| {
 | |
|     uint16_t proto;
 | |
|     size_t copied;
 | |
|     size_t size = iov_size(l2hdr_iov, iovcnt);
 | |
|     size_t proto_offset = l2hdr_len - sizeof(proto);
 | |
| 
 | |
|     if (size < proto_offset) {
 | |
|         return ETH_P_UNKNOWN;
 | |
|     }
 | |
| 
 | |
|     copied = iov_to_buf(l2hdr_iov, iovcnt, proto_offset,
 | |
|                         &proto, sizeof(proto));
 | |
| 
 | |
|     return (copied == sizeof(proto)) ? be16_to_cpu(proto) : ETH_P_UNKNOWN;
 | |
| }
 | |
| 
 | |
| static bool
 | |
| _eth_copy_chunk(size_t input_size,
 | |
|                 const struct iovec *iov, int iovcnt,
 | |
|                 size_t offset, size_t length,
 | |
|                 void *buffer)
 | |
| {
 | |
|     size_t copied;
 | |
| 
 | |
|     if (input_size < offset) {
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     copied = iov_to_buf(iov, iovcnt, offset, buffer, length);
 | |
| 
 | |
|     if (copied < length) {
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| static bool
 | |
| _eth_tcp_has_data(bool is_ip4,
 | |
|                   const struct ip_header  *ip4_hdr,
 | |
|                   const struct ip6_header *ip6_hdr,
 | |
|                   size_t full_ip6hdr_len,
 | |
|                   const struct tcp_header *tcp)
 | |
| {
 | |
|     uint32_t l4len;
 | |
| 
 | |
|     if (is_ip4) {
 | |
|         l4len = be16_to_cpu(ip4_hdr->ip_len) - IP_HDR_GET_LEN(ip4_hdr);
 | |
|     } else {
 | |
|         size_t opts_len = full_ip6hdr_len - sizeof(struct ip6_header);
 | |
|         l4len = be16_to_cpu(ip6_hdr->ip6_ctlun.ip6_un1.ip6_un1_plen) - opts_len;
 | |
|     }
 | |
| 
 | |
|     return l4len > TCP_HEADER_DATA_OFFSET(tcp);
 | |
| }
 | |
| 
 | |
| void eth_get_protocols(const struct iovec *iov, int iovcnt,
 | |
|                        bool *isip4, bool *isip6,
 | |
|                        bool *isudp, bool *istcp,
 | |
|                        size_t *l3hdr_off,
 | |
|                        size_t *l4hdr_off,
 | |
|                        size_t *l5hdr_off,
 | |
|                        eth_ip6_hdr_info *ip6hdr_info,
 | |
|                        eth_ip4_hdr_info *ip4hdr_info,
 | |
|                        eth_l4_hdr_info  *l4hdr_info)
 | |
| {
 | |
|     int proto;
 | |
|     bool fragment = false;
 | |
|     size_t l2hdr_len = eth_get_l2_hdr_length_iov(iov, iovcnt);
 | |
|     size_t input_size = iov_size(iov, iovcnt);
 | |
|     size_t copied;
 | |
| 
 | |
|     *isip4 = *isip6 = *isudp = *istcp = false;
 | |
| 
 | |
|     proto = eth_get_l3_proto(iov, iovcnt, l2hdr_len);
 | |
| 
 | |
|     *l3hdr_off = l2hdr_len;
 | |
| 
 | |
|     if (proto == ETH_P_IP) {
 | |
|         struct ip_header *iphdr = &ip4hdr_info->ip4_hdr;
 | |
| 
 | |
|         if (input_size < l2hdr_len) {
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         copied = iov_to_buf(iov, iovcnt, l2hdr_len, iphdr, sizeof(*iphdr));
 | |
| 
 | |
|         *isip4 = true;
 | |
| 
 | |
|         if (copied < sizeof(*iphdr)) {
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         if (IP_HEADER_VERSION(iphdr) == IP_HEADER_VERSION_4) {
 | |
|             if (iphdr->ip_p == IP_PROTO_TCP) {
 | |
|                 *istcp = true;
 | |
|             } else if (iphdr->ip_p == IP_PROTO_UDP) {
 | |
|                 *isudp = true;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         ip4hdr_info->fragment = IP4_IS_FRAGMENT(iphdr);
 | |
|         *l4hdr_off = l2hdr_len + IP_HDR_GET_LEN(iphdr);
 | |
| 
 | |
|         fragment = ip4hdr_info->fragment;
 | |
|     } else if (proto == ETH_P_IPV6) {
 | |
| 
 | |
|         *isip6 = true;
 | |
|         if (eth_parse_ipv6_hdr(iov, iovcnt, l2hdr_len,
 | |
|                                ip6hdr_info)) {
 | |
|             if (ip6hdr_info->l4proto == IP_PROTO_TCP) {
 | |
|                 *istcp = true;
 | |
|             } else if (ip6hdr_info->l4proto == IP_PROTO_UDP) {
 | |
|                 *isudp = true;
 | |
|             }
 | |
|         } else {
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         *l4hdr_off = l2hdr_len + ip6hdr_info->full_hdr_len;
 | |
|         fragment = ip6hdr_info->fragment;
 | |
|     }
 | |
| 
 | |
|     if (!fragment) {
 | |
|         if (*istcp) {
 | |
|             *istcp = _eth_copy_chunk(input_size,
 | |
|                                      iov, iovcnt,
 | |
|                                      *l4hdr_off, sizeof(l4hdr_info->hdr.tcp),
 | |
|                                      &l4hdr_info->hdr.tcp);
 | |
| 
 | |
|             if (*istcp) {
 | |
|                 *l5hdr_off = *l4hdr_off +
 | |
|                     TCP_HEADER_DATA_OFFSET(&l4hdr_info->hdr.tcp);
 | |
| 
 | |
|                 l4hdr_info->has_tcp_data =
 | |
|                     _eth_tcp_has_data(proto == ETH_P_IP,
 | |
|                                       &ip4hdr_info->ip4_hdr,
 | |
|                                       &ip6hdr_info->ip6_hdr,
 | |
|                                       *l4hdr_off - *l3hdr_off,
 | |
|                                       &l4hdr_info->hdr.tcp);
 | |
|             }
 | |
|         } else if (*isudp) {
 | |
|             *isudp = _eth_copy_chunk(input_size,
 | |
|                                      iov, iovcnt,
 | |
|                                      *l4hdr_off, sizeof(l4hdr_info->hdr.udp),
 | |
|                                      &l4hdr_info->hdr.udp);
 | |
|             *l5hdr_off = *l4hdr_off + sizeof(l4hdr_info->hdr.udp);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| size_t
 | |
| eth_strip_vlan(const struct iovec *iov, int iovcnt, size_t iovoff,
 | |
|                uint8_t *new_ehdr_buf,
 | |
|                uint16_t *payload_offset, uint16_t *tci)
 | |
| {
 | |
|     struct vlan_header vlan_hdr;
 | |
|     struct eth_header *new_ehdr = (struct eth_header *) new_ehdr_buf;
 | |
| 
 | |
|     size_t copied = iov_to_buf(iov, iovcnt, iovoff,
 | |
|                                new_ehdr, sizeof(*new_ehdr));
 | |
| 
 | |
|     if (copied < sizeof(*new_ehdr)) {
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     switch (be16_to_cpu(new_ehdr->h_proto)) {
 | |
|     case ETH_P_VLAN:
 | |
|     case ETH_P_DVLAN:
 | |
|         copied = iov_to_buf(iov, iovcnt, iovoff + sizeof(*new_ehdr),
 | |
|                             &vlan_hdr, sizeof(vlan_hdr));
 | |
| 
 | |
|         if (copied < sizeof(vlan_hdr)) {
 | |
|             return 0;
 | |
|         }
 | |
| 
 | |
|         new_ehdr->h_proto = vlan_hdr.h_proto;
 | |
| 
 | |
|         *tci = be16_to_cpu(vlan_hdr.h_tci);
 | |
|         *payload_offset = iovoff + sizeof(*new_ehdr) + sizeof(vlan_hdr);
 | |
| 
 | |
|         if (be16_to_cpu(new_ehdr->h_proto) == ETH_P_VLAN) {
 | |
| 
 | |
|             copied = iov_to_buf(iov, iovcnt, *payload_offset,
 | |
|                                 PKT_GET_VLAN_HDR(new_ehdr), sizeof(vlan_hdr));
 | |
| 
 | |
|             if (copied < sizeof(vlan_hdr)) {
 | |
|                 return 0;
 | |
|             }
 | |
| 
 | |
|             *payload_offset += sizeof(vlan_hdr);
 | |
| 
 | |
|             return sizeof(struct eth_header) + sizeof(struct vlan_header);
 | |
|         } else {
 | |
|             return sizeof(struct eth_header);
 | |
|         }
 | |
|     default:
 | |
|         return 0;
 | |
|     }
 | |
| }
 | |
| 
 | |
| size_t
 | |
| eth_strip_vlan_ex(const struct iovec *iov, int iovcnt, size_t iovoff,
 | |
|                   uint16_t vet, uint8_t *new_ehdr_buf,
 | |
|                   uint16_t *payload_offset, uint16_t *tci)
 | |
| {
 | |
|     struct vlan_header vlan_hdr;
 | |
|     struct eth_header *new_ehdr = (struct eth_header *) new_ehdr_buf;
 | |
| 
 | |
|     size_t copied = iov_to_buf(iov, iovcnt, iovoff,
 | |
|                                new_ehdr, sizeof(*new_ehdr));
 | |
| 
 | |
|     if (copied < sizeof(*new_ehdr)) {
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     if (be16_to_cpu(new_ehdr->h_proto) == vet) {
 | |
|         copied = iov_to_buf(iov, iovcnt, iovoff + sizeof(*new_ehdr),
 | |
|                             &vlan_hdr, sizeof(vlan_hdr));
 | |
| 
 | |
|         if (copied < sizeof(vlan_hdr)) {
 | |
|             return 0;
 | |
|         }
 | |
| 
 | |
|         new_ehdr->h_proto = vlan_hdr.h_proto;
 | |
| 
 | |
|         *tci = be16_to_cpu(vlan_hdr.h_tci);
 | |
|         *payload_offset = iovoff + sizeof(*new_ehdr) + sizeof(vlan_hdr);
 | |
|         return sizeof(struct eth_header);
 | |
|     }
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| void
 | |
| eth_setup_ip4_fragmentation(const void *l2hdr, size_t l2hdr_len,
 | |
|                             void *l3hdr, size_t l3hdr_len,
 | |
|                             size_t l3payload_len,
 | |
|                             size_t frag_offset, bool more_frags)
 | |
| {
 | |
|     const struct iovec l2vec = {
 | |
|         .iov_base = (void *) l2hdr,
 | |
|         .iov_len = l2hdr_len
 | |
|     };
 | |
| 
 | |
|     if (eth_get_l3_proto(&l2vec, 1, l2hdr_len) == ETH_P_IP) {
 | |
|         uint16_t orig_flags;
 | |
|         struct ip_header *iphdr = (struct ip_header *) l3hdr;
 | |
|         uint16_t frag_off_units = frag_offset / IP_FRAG_UNIT_SIZE;
 | |
|         uint16_t new_ip_off;
 | |
| 
 | |
|         assert(frag_offset % IP_FRAG_UNIT_SIZE == 0);
 | |
|         assert((frag_off_units & ~IP_OFFMASK) == 0);
 | |
| 
 | |
|         orig_flags = be16_to_cpu(iphdr->ip_off) & ~(IP_OFFMASK|IP_MF);
 | |
|         new_ip_off = frag_off_units | orig_flags  | (more_frags ? IP_MF : 0);
 | |
|         iphdr->ip_off = cpu_to_be16(new_ip_off);
 | |
|         iphdr->ip_len = cpu_to_be16(l3payload_len + l3hdr_len);
 | |
|     }
 | |
| }
 | |
| 
 | |
| void
 | |
| eth_fix_ip4_checksum(void *l3hdr, size_t l3hdr_len)
 | |
| {
 | |
|     struct ip_header *iphdr = (struct ip_header *) l3hdr;
 | |
|     iphdr->ip_sum = 0;
 | |
|     iphdr->ip_sum = cpu_to_be16(net_raw_checksum(l3hdr, l3hdr_len));
 | |
| }
 | |
| 
 | |
| uint32_t
 | |
| eth_calc_ip4_pseudo_hdr_csum(struct ip_header *iphdr,
 | |
|                              uint16_t csl,
 | |
|                              uint32_t *cso)
 | |
| {
 | |
|     struct ip_pseudo_header ipph;
 | |
|     ipph.ip_src = iphdr->ip_src;
 | |
|     ipph.ip_dst = iphdr->ip_dst;
 | |
|     ipph.ip_payload = cpu_to_be16(csl);
 | |
|     ipph.ip_proto = iphdr->ip_p;
 | |
|     ipph.zeros = 0;
 | |
|     *cso = sizeof(ipph);
 | |
|     return net_checksum_add(*cso, (uint8_t *) &ipph);
 | |
| }
 | |
| 
 | |
| uint32_t
 | |
| eth_calc_ip6_pseudo_hdr_csum(struct ip6_header *iphdr,
 | |
|                              uint16_t csl,
 | |
|                              uint8_t l4_proto,
 | |
|                              uint32_t *cso)
 | |
| {
 | |
|     struct ip6_pseudo_header ipph;
 | |
|     ipph.ip6_src = iphdr->ip6_src;
 | |
|     ipph.ip6_dst = iphdr->ip6_dst;
 | |
|     ipph.len = cpu_to_be16(csl);
 | |
|     ipph.zero[0] = 0;
 | |
|     ipph.zero[1] = 0;
 | |
|     ipph.zero[2] = 0;
 | |
|     ipph.next_hdr = l4_proto;
 | |
|     *cso = sizeof(ipph);
 | |
|     return net_checksum_add(*cso, (uint8_t *)&ipph);
 | |
| }
 | |
| 
 | |
| static bool
 | |
| eth_is_ip6_extension_header_type(uint8_t hdr_type)
 | |
| {
 | |
|     switch (hdr_type) {
 | |
|     case IP6_HOP_BY_HOP:
 | |
|     case IP6_ROUTING:
 | |
|     case IP6_FRAGMENT:
 | |
|     case IP6_ESP:
 | |
|     case IP6_AUTHENTICATION:
 | |
|     case IP6_DESTINATON:
 | |
|     case IP6_MOBILITY:
 | |
|         return true;
 | |
|     default:
 | |
|         return false;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static bool
 | |
| _eth_get_rss_ex_dst_addr(const struct iovec *pkt, int pkt_frags,
 | |
|                         size_t rthdr_offset,
 | |
|                         struct ip6_ext_hdr *ext_hdr,
 | |
|                         struct in6_address *dst_addr)
 | |
| {
 | |
|     struct ip6_ext_hdr_routing *rthdr = (struct ip6_ext_hdr_routing *) ext_hdr;
 | |
| 
 | |
|     if ((rthdr->rtype == 2) &&
 | |
|         (rthdr->len == sizeof(struct in6_address) / 8) &&
 | |
|         (rthdr->segleft == 1)) {
 | |
| 
 | |
|         size_t input_size = iov_size(pkt, pkt_frags);
 | |
|         size_t bytes_read;
 | |
| 
 | |
|         if (input_size < rthdr_offset + sizeof(*ext_hdr)) {
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         bytes_read = iov_to_buf(pkt, pkt_frags,
 | |
|                                 rthdr_offset + sizeof(*ext_hdr),
 | |
|                                 dst_addr, sizeof(*dst_addr));
 | |
| 
 | |
|         return bytes_read == sizeof(*dst_addr);
 | |
|     }
 | |
| 
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| static bool
 | |
| _eth_get_rss_ex_src_addr(const struct iovec *pkt, int pkt_frags,
 | |
|                         size_t dsthdr_offset,
 | |
|                         struct ip6_ext_hdr *ext_hdr,
 | |
|                         struct in6_address *src_addr)
 | |
| {
 | |
|     size_t bytes_left = (ext_hdr->ip6r_len + 1) * 8 - sizeof(*ext_hdr);
 | |
|     struct ip6_option_hdr opthdr;
 | |
|     size_t opt_offset = dsthdr_offset + sizeof(*ext_hdr);
 | |
| 
 | |
|     while (bytes_left > sizeof(opthdr)) {
 | |
|         size_t input_size = iov_size(pkt, pkt_frags);
 | |
|         size_t bytes_read, optlen;
 | |
| 
 | |
|         if (input_size < opt_offset) {
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         bytes_read = iov_to_buf(pkt, pkt_frags, opt_offset,
 | |
|                                 &opthdr, sizeof(opthdr));
 | |
| 
 | |
|         if (bytes_read != sizeof(opthdr)) {
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         optlen = (opthdr.type == IP6_OPT_PAD1) ? 1
 | |
|                                                : (opthdr.len + sizeof(opthdr));
 | |
| 
 | |
|         if (optlen > bytes_left) {
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         if (opthdr.type == IP6_OPT_HOME) {
 | |
|             size_t input_size = iov_size(pkt, pkt_frags);
 | |
| 
 | |
|             if (input_size < opt_offset + sizeof(opthdr)) {
 | |
|                 return false;
 | |
|             }
 | |
| 
 | |
|             bytes_read = iov_to_buf(pkt, pkt_frags,
 | |
|                                     opt_offset + sizeof(opthdr),
 | |
|                                     src_addr, sizeof(*src_addr));
 | |
| 
 | |
|             return bytes_read == sizeof(*src_addr);
 | |
|         }
 | |
| 
 | |
|         opt_offset += optlen;
 | |
|         bytes_left -= optlen;
 | |
|     }
 | |
| 
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| bool eth_parse_ipv6_hdr(const struct iovec *pkt, int pkt_frags,
 | |
|                         size_t ip6hdr_off, eth_ip6_hdr_info *info)
 | |
| {
 | |
|     struct ip6_ext_hdr ext_hdr;
 | |
|     size_t bytes_read;
 | |
|     uint8_t curr_ext_hdr_type;
 | |
|     size_t input_size = iov_size(pkt, pkt_frags);
 | |
| 
 | |
|     info->rss_ex_dst_valid = false;
 | |
|     info->rss_ex_src_valid = false;
 | |
|     info->fragment = false;
 | |
| 
 | |
|     if (input_size < ip6hdr_off) {
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     bytes_read = iov_to_buf(pkt, pkt_frags, ip6hdr_off,
 | |
|                             &info->ip6_hdr, sizeof(info->ip6_hdr));
 | |
|     if (bytes_read < sizeof(info->ip6_hdr)) {
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     info->full_hdr_len = sizeof(struct ip6_header);
 | |
| 
 | |
|     curr_ext_hdr_type = info->ip6_hdr.ip6_nxt;
 | |
| 
 | |
|     if (!eth_is_ip6_extension_header_type(curr_ext_hdr_type)) {
 | |
|         info->l4proto = info->ip6_hdr.ip6_nxt;
 | |
|         info->has_ext_hdrs = false;
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     info->has_ext_hdrs = true;
 | |
| 
 | |
|     do {
 | |
|         if (input_size < ip6hdr_off + info->full_hdr_len) {
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         bytes_read = iov_to_buf(pkt, pkt_frags, ip6hdr_off + info->full_hdr_len,
 | |
|                                 &ext_hdr, sizeof(ext_hdr));
 | |
| 
 | |
|         if (bytes_read < sizeof(ext_hdr)) {
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         if (curr_ext_hdr_type == IP6_ROUTING) {
 | |
|             info->rss_ex_dst_valid =
 | |
|                 _eth_get_rss_ex_dst_addr(pkt, pkt_frags,
 | |
|                                          ip6hdr_off + info->full_hdr_len,
 | |
|                                          &ext_hdr, &info->rss_ex_dst);
 | |
|         } else if (curr_ext_hdr_type == IP6_DESTINATON) {
 | |
|             info->rss_ex_src_valid =
 | |
|                 _eth_get_rss_ex_src_addr(pkt, pkt_frags,
 | |
|                                          ip6hdr_off + info->full_hdr_len,
 | |
|                                          &ext_hdr, &info->rss_ex_src);
 | |
|         } else if (curr_ext_hdr_type == IP6_FRAGMENT) {
 | |
|             info->fragment = true;
 | |
|         }
 | |
| 
 | |
|         info->full_hdr_len += (ext_hdr.ip6r_len + 1) * IP6_EXT_GRANULARITY;
 | |
|         curr_ext_hdr_type = ext_hdr.ip6r_nxt;
 | |
|     } while (eth_is_ip6_extension_header_type(curr_ext_hdr_type));
 | |
| 
 | |
|     info->l4proto = ext_hdr.ip6r_nxt;
 | |
|     return true;
 | |
| }
 |