summary refs log tree commit diff stats
path: root/net
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2016-06-02 14:26:57 +0100
committerPeter Maydell <peter.maydell@linaro.org>2016-06-02 14:26:57 +0100
commit2c107d7684f9e3c4db4780d0756bbf35b06aec07 (patch)
treed4b68dfdd7efc8bd3fb5fe89bcbb5baab1a23c85 /net
parentcbd614870fce00f46088be7054a7bf5eadcc77ac (diff)
parent517b5e9a175fe7d47cc0fab6c2310241fd33c115 (diff)
downloadfocaccia-qemu-2c107d7684f9e3c4db4780d0756bbf35b06aec07.tar.gz
focaccia-qemu-2c107d7684f9e3c4db4780d0756bbf35b06aec07.zip
Merge remote-tracking branch 'remotes/jasowang/tags/net-pull-request' into staging
# gpg: Signature made Thu 02 Jun 2016 07:23:18 BST using RSA key ID 398D6211
# gpg: Good signature from "Jason Wang (Jason Wang on RedHat) <jasowang@redhat.com>"
# gpg: WARNING: This key is not certified with sufficiently trusted signatures!
# gpg:          It is not certain that the signature belongs to the owner.
# Primary key fingerprint: 215D 46F4 8246 689E C77F  3562 EF04 965B 398D 6211

* remotes/jasowang/tags/net-pull-request: (31 commits)
  Add ENET device to i.MX6 SOC.
  Add ENET/Gbps Ethernet support to FEC device
  i.MX: move FEC device to a register array structure.
  i.MX: Rename i.MX FEC defines to ENET_XXX
  i.MX: reset TX/RX descriptors when FEC is disabled.
  i.MX: Fix FEC code for ECR register reset value.
  i.MX: Fix FEC code for MDIO address selection
  i.MX: Fix FEC code for MDIO operation selection
  net: handle optional VLAN header in checksum computation.
  net: improve UDP/TCP checksum computation.
  e1000e: Introduce qtest for e1000e device
  net: Introduce e1000e device emulation
  e1000: Move out code that will be reused in e1000e
  e1000_regs: Add definitions for Intel 82574-specific bits
  vmxnet3: Use pci_dma_* API instead of cpu_physical_memory_*
  net_pkt: Extend packet abstraction as required by e1000e functionality
  rtl8139: Move more TCP definitions to common header
  net_pkt: Name vmxnet3 packet abstractions more generic
  vmxnet3: Use common MAC address tracing macros
  net: Add macros for MAC address tracing
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'net')
-rw-r--r--net/checksum.c128
-rw-r--r--net/eth.c410
-rw-r--r--net/filter-mirror.c66
-rw-r--r--net/net.c93
-rw-r--r--net/socket.c77
-rw-r--r--net/tap.c6
6 files changed, 579 insertions, 201 deletions
diff --git a/net/checksum.c b/net/checksum.c
index d0fa424cc1..23323b0760 100644
--- a/net/checksum.c
+++ b/net/checksum.c
@@ -18,9 +18,7 @@
 #include "qemu/osdep.h"
 #include "qemu-common.h"
 #include "net/checksum.h"
-
-#define PROTO_TCP  6
-#define PROTO_UDP 17
+#include "net/eth.h"
 
 uint32_t net_checksum_add_cont(int len, uint8_t *buf, int seq)
 {
@@ -57,50 +55,118 @@ uint16_t net_checksum_tcpudp(uint16_t length, uint16_t proto,
 
 void net_checksum_calculate(uint8_t *data, int length)
 {
-    int hlen, plen, proto, csum_offset;
-    uint16_t csum;
+    int mac_hdr_len, ip_len;
+    struct ip_header *ip;
+
+    /*
+     * Note: We cannot assume "data" is aligned, so the all code uses
+     * some macros that take care of possible unaligned access for
+     * struct members (just in case).
+     */
 
-    /* Ensure data has complete L2 & L3 headers. */
-    if (length < 14 + 20) {
+    /* Ensure we have at least an Eth header */
+    if (length < sizeof(struct eth_header)) {
         return;
     }
 
-    if ((data[14] & 0xf0) != 0x40)
-	return; /* not IPv4 */
-    hlen  = (data[14] & 0x0f) * 4;
-    plen  = (data[16] << 8 | data[17]) - hlen;
-    proto = data[23];
-
-    switch (proto) {
-    case PROTO_TCP:
-	csum_offset = 16;
-	break;
-    case PROTO_UDP:
-	csum_offset = 6;
-	break;
+    /* Handle the optionnal VLAN headers */
+    switch (lduw_be_p(&PKT_GET_ETH_HDR(data)->h_proto)) {
+    case ETH_P_VLAN:
+        mac_hdr_len = sizeof(struct eth_header) +
+                     sizeof(struct vlan_header);
+        break;
+    case ETH_P_DVLAN:
+        if (lduw_be_p(&PKT_GET_VLAN_HDR(data)->h_proto) == ETH_P_VLAN) {
+            mac_hdr_len = sizeof(struct eth_header) +
+                         2 * sizeof(struct vlan_header);
+        } else {
+            mac_hdr_len = sizeof(struct eth_header) +
+                         sizeof(struct vlan_header);
+        }
+        break;
     default:
-	return;
+        mac_hdr_len = sizeof(struct eth_header);
+        break;
     }
 
-    if (plen < csum_offset + 2 || 14 + hlen + plen > length) {
+    length -= mac_hdr_len;
+
+    /* Now check we have an IP header (with an optionnal VLAN header) */
+    if (length < sizeof(struct ip_header)) {
         return;
     }
 
-    data[14+hlen+csum_offset]   = 0;
-    data[14+hlen+csum_offset+1] = 0;
-    csum = net_checksum_tcpudp(plen, proto, data+14+12, data+14+hlen);
-    data[14+hlen+csum_offset]   = csum >> 8;
-    data[14+hlen+csum_offset+1] = csum & 0xff;
+    ip = (struct ip_header *)(data + mac_hdr_len);
+
+    if (IP_HEADER_VERSION(ip) != IP_HEADER_VERSION_4) {
+        return; /* not IPv4 */
+    }
+
+    ip_len = lduw_be_p(&ip->ip_len);
+
+    /* Last, check that we have enough data for the all IP frame */
+    if (length < ip_len) {
+        return;
+    }
+
+    ip_len -= IP_HDR_GET_LEN(ip);
+
+    switch (ip->ip_p) {
+    case IP_PROTO_TCP:
+    {
+        uint16_t csum;
+        tcp_header *tcp = (tcp_header *)(ip + 1);
+
+        if (ip_len < sizeof(tcp_header)) {
+            return;
+        }
+
+        /* Set csum to 0 */
+        stw_he_p(&tcp->th_sum, 0);
+
+        csum = net_checksum_tcpudp(ip_len, ip->ip_p,
+                                   (uint8_t *)&ip->ip_src,
+                                   (uint8_t *)tcp);
+
+        /* Store computed csum */
+        stw_be_p(&tcp->th_sum, csum);
+
+        break;
+    }
+    case IP_PROTO_UDP:
+    {
+        uint16_t csum;
+        udp_header *udp = (udp_header *)(ip + 1);
+
+        if (ip_len < sizeof(udp_header)) {
+            return;
+        }
+
+        /* Set csum to 0 */
+        stw_he_p(&udp->uh_sum, 0);
+
+        csum = net_checksum_tcpudp(ip_len, ip->ip_p,
+                                   (uint8_t *)&ip->ip_src,
+                                   (uint8_t *)udp);
+
+        /* Store computed csum */
+        stw_be_p(&udp->uh_sum, csum);
+
+        break;
+    }
+    default:
+        /* Can't handle any other protocol */
+        break;
+    }
 }
 
 uint32_t
 net_checksum_add_iov(const struct iovec *iov, const unsigned int iov_cnt,
-                     uint32_t iov_off, uint32_t size)
+                     uint32_t iov_off, uint32_t size, uint32_t csum_offset)
 {
     size_t iovec_off, buf_off;
     unsigned int i;
     uint32_t res = 0;
-    uint32_t seq = 0;
 
     iovec_off = 0;
     buf_off = 0;
@@ -109,8 +175,8 @@ net_checksum_add_iov(const struct iovec *iov, const unsigned int iov_cnt,
             size_t len = MIN((iovec_off + iov[i].iov_len) - iov_off , size);
             void *chunk_buf = iov[i].iov_base + (iov_off - iovec_off);
 
-            res += net_checksum_add_cont(len, chunk_buf, seq);
-            seq += len;
+            res += net_checksum_add_cont(len, chunk_buf, csum_offset);
+            csum_offset += len;
 
             buf_off += len;
             iov_off += len;
diff --git a/net/eth.c b/net/eth.c
index 7e32d274c7..95fe15c23f 100644
--- a/net/eth.c
+++ b/net/eth.c
@@ -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;
 }
diff --git a/net/filter-mirror.c b/net/filter-mirror.c
index c0c4dc60b6..35df37451d 100644
--- a/net/filter-mirror.c
+++ b/net/filter-mirror.c
@@ -40,10 +40,7 @@ typedef struct MirrorState {
     char *outdev;
     CharDriverState *chr_in;
     CharDriverState *chr_out;
-    int state; /* 0 = getting length, 1 = getting data */
-    unsigned int index;
-    unsigned int packet_len;
-    uint8_t buf[REDIRECTOR_MAX_LEN];
+    SocketReadState rs;
 } MirrorState;
 
 static int filter_mirror_send(CharDriverState *chr_out,
@@ -108,51 +105,12 @@ static void redirector_chr_read(void *opaque, const uint8_t *buf, int size)
 {
     NetFilterState *nf = opaque;
     MirrorState *s = FILTER_REDIRECTOR(nf);
-    unsigned int l;
-
-    while (size > 0) {
-        /* reassemble a packet from the network */
-        switch (s->state) { /* 0 = getting length, 1 = getting data */
-        case 0:
-            l = 4 - s->index;
-            if (l > size) {
-                l = size;
-            }
-            memcpy(s->buf + s->index, buf, l);
-            buf += l;
-            size -= l;
-            s->index += l;
-            if (s->index == 4) {
-                /* got length */
-                s->packet_len = ntohl(*(uint32_t *)s->buf);
-                s->index = 0;
-                s->state = 1;
-            }
-            break;
-        case 1:
-            l = s->packet_len - s->index;
-            if (l > size) {
-                l = size;
-            }
-            if (s->index + l <= sizeof(s->buf)) {
-                memcpy(s->buf + s->index, buf, l);
-            } else {
-                error_report("serious error: oversized packet received.");
-                s->index = s->state = 0;
-                qemu_chr_add_handlers(s->chr_in, NULL, NULL, NULL, NULL);
-                return;
-            }
-
-            s->index += l;
-            buf += l;
-            size -= l;
-            if (s->index >= s->packet_len) {
-                s->index = 0;
-                s->state = 0;
-                redirector_to_filter(nf, s->buf, s->packet_len);
-            }
-            break;
-        }
+    int ret;
+
+    ret = net_fill_rstate(&s->rs, buf, size);
+
+    if (ret == -1) {
+        qemu_chr_add_handlers(s->chr_in, NULL, NULL, NULL, NULL);
     }
 }
 
@@ -258,6 +216,14 @@ static void filter_mirror_setup(NetFilterState *nf, Error **errp)
     }
 }
 
+static void redirector_rs_finalize(SocketReadState *rs)
+{
+    MirrorState *s = container_of(rs, MirrorState, rs);
+    NetFilterState *nf = NETFILTER(s);
+
+    redirector_to_filter(nf, rs->buf, rs->packet_len);
+}
+
 static void filter_redirector_setup(NetFilterState *nf, Error **errp)
 {
     MirrorState *s = FILTER_REDIRECTOR(nf);
@@ -274,7 +240,7 @@ static void filter_redirector_setup(NetFilterState *nf, Error **errp)
         }
     }
 
-    s->state = s->index = 0;
+    net_socket_rs_init(&s->rs, redirector_rs_finalize);
 
     if (s->indev) {
         s->chr_in = qemu_chr_find(s->indev);
diff --git a/net/net.c b/net/net.c
index 0ad6217cb9..5f3e5a9ff5 100644
--- a/net/net.c
+++ b/net/net.c
@@ -76,8 +76,6 @@ const char *host_net_devices[] = {
     NULL,
 };
 
-int default_net = 1;
-
 /***********************************************************/
 /* network device redirectors */
 
@@ -1415,18 +1413,6 @@ void net_check_clients(void)
     NetClientState *nc;
     int i;
 
-    /* Don't warn about the default network setup that you get if
-     * no command line -net or -netdev options are specified. There
-     * are two cases that we would otherwise complain about:
-     * (1) board doesn't support a NIC but the implicit "-net nic"
-     * requested one
-     * (2) CONFIG_SLIRP not set, in which case the implicit "-net nic"
-     * sets up a nic that isn't connected to anything.
-     */
-    if (default_net) {
-        return;
-    }
-
     net_hub_check_clients();
 
     QTAILQ_FOREACH(nc, &net_clients, next) {
@@ -1483,14 +1469,6 @@ int net_init_clients(void)
 {
     QemuOptsList *net = qemu_find_opts("net");
 
-    if (default_net) {
-        /* if no clients, we use a default config */
-        qemu_opts_set(net, NULL, "type", "nic", &error_abort);
-#ifdef CONFIG_SLIRP
-        qemu_opts_set(net, NULL, "type", "user", &error_abort);
-#endif
-    }
-
     net_change_state_entry =
         qemu_add_vm_change_state_handler(net_vm_change_state_handler, NULL);
 
@@ -1521,7 +1499,6 @@ int net_client_parse(QemuOptsList *opts_list, const char *optarg)
         return -1;
     }
 
-    default_net = 0;
     return 0;
 }
 
@@ -1573,3 +1550,73 @@ QemuOptsList qemu_net_opts = {
         { /* end of list */ }
     },
 };
+
+void net_socket_rs_init(SocketReadState *rs,
+                        SocketReadStateFinalize *finalize)
+{
+    rs->state = 0;
+    rs->index = 0;
+    rs->packet_len = 0;
+    memset(rs->buf, 0, sizeof(rs->buf));
+    rs->finalize = finalize;
+}
+
+/*
+ * Returns
+ * 0: SocketReadState is not ready
+ * 1: SocketReadState is ready
+ * otherwise error occurs
+ */
+int net_fill_rstate(SocketReadState *rs, const uint8_t *buf, int size)
+{
+    unsigned int l;
+
+    while (size > 0) {
+        /* reassemble a packet from the network */
+        switch (rs->state) { /* 0 = getting length, 1 = getting data */
+        case 0:
+            l = 4 - rs->index;
+            if (l > size) {
+                l = size;
+            }
+            memcpy(rs->buf + rs->index, buf, l);
+            buf += l;
+            size -= l;
+            rs->index += l;
+            if (rs->index == 4) {
+                /* got length */
+                rs->packet_len = ntohl(*(uint32_t *)rs->buf);
+                rs->index = 0;
+                rs->state = 1;
+            }
+            break;
+        case 1:
+            l = rs->packet_len - rs->index;
+            if (l > size) {
+                l = size;
+            }
+            if (rs->index + l <= sizeof(rs->buf)) {
+                memcpy(rs->buf + rs->index, buf, l);
+            } else {
+                fprintf(stderr, "serious error: oversized packet received,"
+                    "connection terminated.\n");
+                rs->index = rs->state = 0;
+                return -1;
+            }
+
+            rs->index += l;
+            buf += l;
+            size -= l;
+            if (rs->index >= rs->packet_len) {
+                rs->index = 0;
+                rs->state = 0;
+                if (rs->finalize) {
+                    rs->finalize(rs);
+                }
+                return 1;
+            }
+            break;
+        }
+    }
+    return 0;
+}
diff --git a/net/socket.c b/net/socket.c
index 9fa2cd8d51..333fb9ecfa 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -38,11 +38,8 @@ typedef struct NetSocketState {
     NetClientState nc;
     int listen_fd;
     int fd;
-    int state; /* 0 = getting length, 1 = getting data */
-    unsigned int index;
-    unsigned int packet_len;
+    SocketReadState rs;
     unsigned int send_index;      /* number of bytes sent (only SOCK_STREAM) */
-    uint8_t buf[NET_BUFSIZE];
     struct sockaddr_in dgram_dst; /* contains inet host and port destination iff connectionless (SOCK_DGRAM) */
     IOHandler *send_fn;           /* differs between SOCK_STREAM/SOCK_DGRAM */
     bool read_poll;               /* waiting to receive data? */
@@ -143,11 +140,22 @@ static void net_socket_send_completed(NetClientState *nc, ssize_t len)
     }
 }
 
+static void net_socket_rs_finalize(SocketReadState *rs)
+{
+    NetSocketState *s = container_of(rs, NetSocketState, rs);
+
+    if (qemu_send_packet_async(&s->nc, rs->buf,
+                               rs->packet_len,
+                               net_socket_send_completed) == 0) {
+        net_socket_read_poll(s, false);
+    }
+}
+
 static void net_socket_send(void *opaque)
 {
     NetSocketState *s = opaque;
     int size;
-    unsigned l;
+    int ret;
     uint8_t buf1[NET_BUFSIZE];
     const uint8_t *buf;
 
@@ -166,61 +174,18 @@ static void net_socket_send(void *opaque)
         closesocket(s->fd);
 
         s->fd = -1;
-        s->state = 0;
-        s->index = 0;
-        s->packet_len = 0;
+        net_socket_rs_init(&s->rs, net_socket_rs_finalize);
         s->nc.link_down = true;
-        memset(s->buf, 0, sizeof(s->buf));
         memset(s->nc.info_str, 0, sizeof(s->nc.info_str));
 
         return;
     }
     buf = buf1;
-    while (size > 0) {
-        /* reassemble a packet from the network */
-        switch(s->state) {
-        case 0:
-            l = 4 - s->index;
-            if (l > size)
-                l = size;
-            memcpy(s->buf + s->index, buf, l);
-            buf += l;
-            size -= l;
-            s->index += l;
-            if (s->index == 4) {
-                /* got length */
-                s->packet_len = ntohl(*(uint32_t *)s->buf);
-                s->index = 0;
-                s->state = 1;
-            }
-            break;
-        case 1:
-            l = s->packet_len - s->index;
-            if (l > size)
-                l = size;
-            if (s->index + l <= sizeof(s->buf)) {
-                memcpy(s->buf + s->index, buf, l);
-            } else {
-                fprintf(stderr, "serious error: oversized packet received,"
-                    "connection terminated.\n");
-                s->state = 0;
-                goto eoc;
-            }
 
-            s->index += l;
-            buf += l;
-            size -= l;
-            if (s->index >= s->packet_len) {
-                s->index = 0;
-                s->state = 0;
-                if (qemu_send_packet_async(&s->nc, s->buf, s->packet_len,
-                                           net_socket_send_completed) == 0) {
-                    net_socket_read_poll(s, false);
-                    break;
-                }
-            }
-            break;
-        }
+    ret = net_fill_rstate(&s->rs, buf, size);
+
+    if (ret == -1) {
+        goto eoc;
     }
 }
 
@@ -229,7 +194,7 @@ static void net_socket_send_dgram(void *opaque)
     NetSocketState *s = opaque;
     int size;
 
-    size = qemu_recv(s->fd, s->buf, sizeof(s->buf), 0);
+    size = qemu_recv(s->fd, s->rs.buf, sizeof(s->rs.buf), 0);
     if (size < 0)
         return;
     if (size == 0) {
@@ -238,7 +203,7 @@ static void net_socket_send_dgram(void *opaque)
         net_socket_write_poll(s, false);
         return;
     }
-    if (qemu_send_packet_async(&s->nc, s->buf, size,
+    if (qemu_send_packet_async(&s->nc, s->rs.buf, size,
                                net_socket_send_completed) == 0) {
         net_socket_read_poll(s, false);
     }
@@ -401,6 +366,7 @@ static NetSocketState *net_socket_fd_init_dgram(NetClientState *peer,
     s->fd = fd;
     s->listen_fd = -1;
     s->send_fn = net_socket_send_dgram;
+    net_socket_rs_init(&s->rs, net_socket_rs_finalize);
     net_socket_read_poll(s, true);
 
     /* mcast: save bound address as dst */
@@ -451,6 +417,7 @@ static NetSocketState *net_socket_fd_init_stream(NetClientState *peer,
 
     s->fd = fd;
     s->listen_fd = -1;
+    net_socket_rs_init(&s->rs, net_socket_rs_finalize);
 
     /* Disable Nagle algorithm on TCP sockets to reduce latency */
     socket_set_nodelay(fd);
diff --git a/net/tap.c b/net/tap.c
index 740e8a2613..49817c70c1 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -769,8 +769,8 @@ int net_init_tap(const NetClientOptions *opts, const char *name,
             return -1;
         }
     } else if (tap->has_fds) {
-        char *fds[MAX_TAP_QUEUES];
-        char *vhost_fds[MAX_TAP_QUEUES];
+        char **fds = g_new(char *, MAX_TAP_QUEUES);
+        char **vhost_fds = g_new(char *, MAX_TAP_QUEUES);
         int nfds, nvhosts;
 
         if (tap->has_ifname || tap->has_script || tap->has_downscript ||
@@ -818,6 +818,8 @@ int net_init_tap(const NetClientOptions *opts, const char *name,
                 return -1;
             }
         }
+        g_free(fds);
+        g_free(vhost_fds);
     } else if (tap->has_helper) {
         if (tap->has_ifname || tap->has_script || tap->has_downscript ||
             tap->has_vnet_hdr || tap->has_queues || tap->has_vhostfds) {