|
@@ -21,8 +21,8 @@
|
|
|
#include "qemu-common.h"
|
|
|
#include "net/tap.h"
|
|
|
|
|
|
-void eth_setup_vlan_headers(struct eth_header *ehdr, uint16_t vlan_tag,
|
|
|
- bool *is_new)
|
|
|
+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);
|
|
|
|
|
@@ -36,7 +36,7 @@ void eth_setup_vlan_headers(struct eth_header *ehdr, uint16_t vlan_tag,
|
|
|
default:
|
|
|
/* No VLAN header, put a new one */
|
|
|
vhdr->h_proto = ehdr->h_proto;
|
|
|
- ehdr->h_proto = cpu_to_be16(ETH_P_VLAN);
|
|
|
+ ehdr->h_proto = cpu_to_be16(vlan_ethtype);
|
|
|
*is_new = true;
|
|
|
break;
|
|
|
}
|
|
@@ -79,26 +79,100 @@ eth_get_gso_type(uint16_t l3_proto, uint8_t *l3_hdr, uint8_t l4proto)
|
|
|
return VIRTIO_NET_HDR_GSO_NONE | ecn_state;
|
|
|
}
|
|
|
|
|
|
-void eth_get_protocols(const uint8_t *headers,
|
|
|
- uint32_t hdr_length,
|
|
|
+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)
|
|
|
+ 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;
|
|
|
- size_t l2hdr_len = eth_get_l2_hdr_length(headers);
|
|
|
- assert(hdr_length >= eth_get_l2_hdr_length(headers));
|
|
|
+ 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(headers, l2hdr_len);
|
|
|
+ proto = eth_get_l3_proto(iov, iovcnt, l2hdr_len);
|
|
|
+
|
|
|
+ *l3hdr_off = l2hdr_len;
|
|
|
+
|
|
|
if (proto == ETH_P_IP) {
|
|
|
- *isip4 = true;
|
|
|
+ struct ip_header *iphdr = &ip4hdr_info->ip4_hdr;
|
|
|
|
|
|
- struct ip_header *iphdr;
|
|
|
+ if (input_size < l2hdr_len) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ copied = iov_to_buf(iov, iovcnt, l2hdr_len, iphdr, sizeof(*iphdr));
|
|
|
|
|
|
- assert(hdr_length >=
|
|
|
- eth_get_l2_hdr_length(headers) + sizeof(struct ip_header));
|
|
|
+ *isip4 = true;
|
|
|
|
|
|
- iphdr = PKT_GET_IP_HDR(headers);
|
|
|
+ if (copied < sizeof(*iphdr)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
if (IP_HEADER_VERSION(iphdr) == IP_HEADER_VERSION_4) {
|
|
|
if (iphdr->ip_p == IP_PROTO_TCP) {
|
|
@@ -107,24 +181,135 @@ void eth_get_protocols(const uint8_t *headers,
|
|
|
*isudp = true;
|
|
|
}
|
|
|
}
|
|
|
- } else if (proto == ETH_P_IPV6) {
|
|
|
- uint8_t l4proto;
|
|
|
- size_t full_ip6hdr_len;
|
|
|
|
|
|
- struct iovec hdr_vec;
|
|
|
- hdr_vec.iov_base = (void *) headers;
|
|
|
- hdr_vec.iov_len = hdr_length;
|
|
|
+ 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(&hdr_vec, 1, l2hdr_len,
|
|
|
- &l4proto, &full_ip6hdr_len)) {
|
|
|
- if (l4proto == IP_PROTO_TCP) {
|
|
|
+ if (eth_parse_ipv6_hdr(iov, iovcnt, l2hdr_len,
|
|
|
+ ip6hdr_info)) {
|
|
|
+ if (ip6hdr_info->l4proto == IP_PROTO_TCP) {
|
|
|
*istcp = true;
|
|
|
- } else if (l4proto == IP_PROTO_UDP) {
|
|
|
+ } 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);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+bool
|
|
|
+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 false;
|
|
|
+ }
|
|
|
+
|
|
|
+ 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 false;
|
|
|
+ }
|
|
|
+
|
|
|
+ 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 false;
|
|
|
+ }
|
|
|
+
|
|
|
+ *payload_offset += sizeof(vlan_hdr);
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ default:
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+bool
|
|
|
+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 false;
|
|
|
+ }
|
|
|
+
|
|
|
+ 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 false;
|
|
|
}
|
|
|
+
|
|
|
+ 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 true;
|
|
|
}
|
|
|
+
|
|
|
+ return false;
|
|
|
}
|
|
|
|
|
|
void
|
|
@@ -133,7 +318,12 @@ eth_setup_ip4_fragmentation(const void *l2hdr, size_t l2hdr_len,
|
|
|
size_t l3payload_len,
|
|
|
size_t frag_offset, bool more_frags)
|
|
|
{
|
|
|
- if (eth_get_l3_proto(l2hdr, l2hdr_len) == ETH_P_IP) {
|
|
|
+ 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;
|
|
@@ -158,7 +348,9 @@ eth_fix_ip4_checksum(void *l3hdr, size_t l3hdr_len)
|
|
|
}
|
|
|
|
|
|
uint32_t
|
|
|
-eth_calc_pseudo_hdr_csum(struct ip_header *iphdr, uint16_t csl)
|
|
|
+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;
|
|
@@ -166,7 +358,26 @@ eth_calc_pseudo_hdr_csum(struct ip_header *iphdr, uint16_t csl)
|
|
|
ipph.ip_payload = cpu_to_be16(csl);
|
|
|
ipph.ip_proto = iphdr->ip_p;
|
|
|
ipph.zeros = 0;
|
|
|
- return net_checksum_add(sizeof(ipph), (uint8_t *) &ipph);
|
|
|
+ *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
|
|
@@ -186,33 +397,152 @@ eth_is_ip6_extension_header_type(uint8_t hdr_type)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-bool eth_parse_ipv6_hdr(struct iovec *pkt, int pkt_frags,
|
|
|
- size_t ip6hdr_off, uint8_t *l4proto,
|
|
|
- size_t *full_hdr_len)
|
|
|
+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_header ip6_hdr;
|
|
|
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,
|
|
|
- &ip6_hdr, sizeof(ip6_hdr));
|
|
|
- if (bytes_read < sizeof(ip6_hdr)) {
|
|
|
+ &info->ip6_hdr, sizeof(info->ip6_hdr));
|
|
|
+ if (bytes_read < sizeof(info->ip6_hdr)) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
- *full_hdr_len = sizeof(struct ip6_header);
|
|
|
+ info->full_hdr_len = sizeof(struct ip6_header);
|
|
|
+
|
|
|
+ curr_ext_hdr_type = info->ip6_hdr.ip6_nxt;
|
|
|
|
|
|
- if (!eth_is_ip6_extension_header_type(ip6_hdr.ip6_nxt)) {
|
|
|
- *l4proto = 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 {
|
|
|
- bytes_read = iov_to_buf(pkt, pkt_frags, ip6hdr_off + *full_hdr_len,
|
|
|
+ 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));
|
|
|
- *full_hdr_len += (ext_hdr.ip6r_len + 1) * IP6_EXT_GRANULARITY;
|
|
|
- } while (eth_is_ip6_extension_header_type(ext_hdr.ip6r_nxt));
|
|
|
|
|
|
- *l4proto = ext_hdr.ip6r_nxt;
|
|
|
+ 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;
|
|
|
}
|