summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
authorJunfeng Dong <junfeng.dong@intel.com>2013-11-19 17:45:23 +0800
committerJunfeng Dong <junfeng.dong@intel.com>2013-11-19 17:45:23 +0800
commit340f06c9eaee097e626c251bf7a013350649c091 (patch)
tree107e5705050a12da68fc80a56ae37afd50a2cc94 /net
parent42bf3037d458a330856a0be584200c1e41c3f417 (diff)
downloadqemu-340f06c9eaee097e626c251bf7a013350649c091.tar.gz
qemu-340f06c9eaee097e626c251bf7a013350649c091.tar.bz2
qemu-340f06c9eaee097e626c251bf7a013350649c091.zip
Import upstream 1.6.0.upstream/1.6.0
Change-Id: Icf52b556470cac8677297f2ef14ded16684f7887 Signed-off-by: Junfeng Dong <junfeng.dong@intel.com>
Diffstat (limited to 'net')
-rw-r--r--net/Makefile.objs3
-rw-r--r--net/checksum.c42
-rw-r--r--net/checksum.h29
-rw-r--r--net/clients.h2
-rw-r--r--net/dump.c6
-rw-r--r--net/eth.c217
-rw-r--r--net/hub.c22
-rw-r--r--net/hub.h3
-rw-r--r--net/net.c1250
-rw-r--r--net/queue.c19
-rw-r--r--net/queue.h58
-rw-r--r--net/slirp.c22
-rw-r--r--net/slirp.h47
-rw-r--r--net/socket.c54
-rw-r--r--net/tap-aix.c21
-rw-r--r--net/tap-bsd.c27
-rw-r--r--net/tap-haiku.c20
-rw-r--r--net/tap-linux.c92
-rw-r--r--net/tap-linux.h31
-rw-r--r--net/tap-solaris.c24
-rw-r--r--net/tap-win32.c34
-rw-r--r--net/tap.c368
-rw-r--r--net/tap_int.h (renamed from net/tap.h)24
-rw-r--r--net/util.c2
-rw-r--r--net/vde.c8
25 files changed, 2053 insertions, 372 deletions
diff --git a/net/Makefile.objs b/net/Makefile.objs
index cf0418771..4854a14fe 100644
--- a/net/Makefile.objs
+++ b/net/Makefile.objs
@@ -1,6 +1,7 @@
-common-obj-y = queue.o checksum.o util.o hub.o
+common-obj-y = net.o queue.o checksum.o util.o hub.o
common-obj-y += socket.o
common-obj-y += dump.o
+common-obj-y += eth.o
common-obj-$(CONFIG_POSIX) += tap.o
common-obj-$(CONFIG_LINUX) += tap-linux.o
common-obj-$(CONFIG_WIN32) += tap-win32.o
diff --git a/net/checksum.c b/net/checksum.c
index 9919b2e5f..14c08550e 100644
--- a/net/checksum.c
+++ b/net/checksum.c
@@ -15,21 +15,23 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "qemu-common.h"
#include "net/checksum.h"
#define PROTO_TCP 6
#define PROTO_UDP 17
-uint32_t net_checksum_add(int len, uint8_t *buf)
+uint32_t net_checksum_add_cont(int len, uint8_t *buf, int seq)
{
uint32_t sum = 0;
int i;
- for (i = 0; i < len; i++) {
- if (i & 1)
- sum += (uint32_t)buf[i];
- else
- sum += (uint32_t)buf[i] << 8;
+ for (i = seq; i < seq + len; i++) {
+ if (i & 1) {
+ sum += (uint32_t)buf[i - seq];
+ } else {
+ sum += (uint32_t)buf[i - seq] << 8;
+ }
}
return sum;
}
@@ -83,3 +85,31 @@ void net_checksum_calculate(uint8_t *data, int length)
data[14+hlen+csum_offset] = csum >> 8;
data[14+hlen+csum_offset+1] = csum & 0xff;
}
+
+uint32_t
+net_checksum_add_iov(const struct iovec *iov, const unsigned int iov_cnt,
+ uint32_t iov_off, uint32_t size)
+{
+ size_t iovec_off, buf_off;
+ unsigned int i;
+ uint32_t res = 0;
+ uint32_t seq = 0;
+
+ iovec_off = 0;
+ buf_off = 0;
+ for (i = 0; i < iov_cnt && size; i++) {
+ if (iov_off < (iovec_off + iov[i].iov_len)) {
+ 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;
+
+ buf_off += len;
+ iov_off += len;
+ size -= len;
+ }
+ iovec_off += iov[i].iov_len;
+ }
+ return res;
+}
diff --git a/net/checksum.h b/net/checksum.h
deleted file mode 100644
index 1f052986e..000000000
--- a/net/checksum.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * IP checksumming functions.
- * (c) 2008 Gerd Hoffmann <kraxel@redhat.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; under version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef QEMU_NET_CHECKSUM_H
-#define QEMU_NET_CHECKSUM_H
-
-#include <stdint.h>
-
-uint32_t net_checksum_add(int len, uint8_t *buf);
-uint16_t net_checksum_finish(uint32_t sum);
-uint16_t net_checksum_tcpudp(uint16_t length, uint16_t proto,
- uint8_t *addrs, uint8_t *buf);
-void net_checksum_calculate(uint8_t *data, int length);
-
-#endif /* QEMU_NET_CHECKSUM_H */
diff --git a/net/clients.h b/net/clients.h
index c58cc6087..77932942b 100644
--- a/net/clients.h
+++ b/net/clients.h
@@ -24,7 +24,7 @@
#ifndef QEMU_NET_CLIENTS_H
#define QEMU_NET_CLIENTS_H
-#include "net.h"
+#include "net/net.h"
#include "qapi-types.h"
int net_init_dump(const NetClientOptions *opts, const char *name,
diff --git a/net/dump.c b/net/dump.c
index e0a5d7464..411972172 100644
--- a/net/dump.c
+++ b/net/dump.c
@@ -24,9 +24,9 @@
#include "clients.h"
#include "qemu-common.h"
-#include "qemu-error.h"
-#include "qemu-log.h"
-#include "qemu-timer.h"
+#include "qemu/error-report.h"
+#include "qemu/log.h"
+#include "qemu/timer.h"
#include "hub.h"
typedef struct DumpState {
diff --git a/net/eth.c b/net/eth.c
new file mode 100644
index 000000000..7c61132cb
--- /dev/null
+++ b/net/eth.c
@@ -0,0 +1,217 @@
+/*
+ * 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 "net/eth.h"
+#include "net/checksum.h"
+#include "qemu-common.h"
+#include "net/tap.h"
+
+void eth_setup_vlan_headers(struct eth_header *ehdr, uint16_t vlan_tag,
+ 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(ETH_P_VLAN);
+ *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;
+}
+
+void eth_get_protocols(const uint8_t *headers,
+ uint32_t hdr_length,
+ bool *isip4, bool *isip6,
+ bool *isudp, bool *istcp)
+{
+ int proto;
+ size_t l2hdr_len = eth_get_l2_hdr_length(headers);
+ assert(hdr_length >= eth_get_l2_hdr_length(headers));
+ *isip4 = *isip6 = *isudp = *istcp = false;
+
+ proto = eth_get_l3_proto(headers, l2hdr_len);
+ if (proto == ETH_P_IP) {
+ *isip4 = true;
+
+ struct ip_header *iphdr;
+
+ assert(hdr_length >=
+ eth_get_l2_hdr_length(headers) + sizeof(struct ip_header));
+
+ iphdr = PKT_GET_IP_HDR(headers);
+
+ 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;
+ }
+ }
+ } 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;
+
+ *isip6 = true;
+ if (eth_parse_ipv6_hdr(&hdr_vec, 1, l2hdr_len,
+ &l4proto, &full_ip6hdr_len)) {
+ if (l4proto == IP_PROTO_TCP) {
+ *istcp = true;
+ } else if (l4proto == IP_PROTO_UDP) {
+ *isudp = true;
+ }
+ }
+ }
+}
+
+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)
+{
+ if (eth_get_l3_proto(l2hdr, 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_pseudo_hdr_csum(struct ip_header *iphdr, uint16_t csl)
+{
+ 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;
+ return net_checksum_add(sizeof(ipph), (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;
+ }
+}
+
+bool eth_parse_ipv6_hdr(struct iovec *pkt, int pkt_frags,
+ size_t ip6hdr_off, uint8_t *l4proto,
+ size_t *full_hdr_len)
+{
+ struct ip6_header ip6_hdr;
+ struct ip6_ext_hdr ext_hdr;
+ size_t bytes_read;
+
+ bytes_read = iov_to_buf(pkt, pkt_frags, ip6hdr_off,
+ &ip6_hdr, sizeof(ip6_hdr));
+ if (bytes_read < sizeof(ip6_hdr)) {
+ return false;
+ }
+
+ *full_hdr_len = sizeof(struct ip6_header);
+
+ if (!eth_is_ip6_extension_header_type(ip6_hdr.ip6_nxt)) {
+ *l4proto = ip6_hdr.ip6_nxt;
+ return true;
+ }
+
+ do {
+ bytes_read = iov_to_buf(pkt, pkt_frags, ip6hdr_off + *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;
+ return true;
+}
diff --git a/net/hub.c b/net/hub.c
index be413012b..df32074de 100644
--- a/net/hub.c
+++ b/net/hub.c
@@ -12,11 +12,11 @@
*
*/
-#include "monitor.h"
-#include "net.h"
+#include "monitor/monitor.h"
+#include "net/net.h"
#include "clients.h"
#include "hub.h"
-#include "iov.h"
+#include "qemu/iov.h"
/*
* A hub broadcasts incoming packets to all its ports except the source port.
@@ -256,7 +256,7 @@ void net_hub_info(Monitor *mon)
/**
* Get the hub id that a client is connected to
*
- * @id Pointer for hub id output, may be NULL
+ * @id: Pointer for hub id output, may be NULL
*/
int net_hub_id_for_client(NetClientState *nc, int *id)
{
@@ -338,3 +338,17 @@ void net_hub_check_clients(void)
}
}
}
+
+bool net_hub_flush(NetClientState *nc)
+{
+ NetHubPort *port;
+ NetHubPort *source_port = DO_UPCAST(NetHubPort, nc, nc);
+ int ret = 0;
+
+ QLIST_FOREACH(port, &source_port->hub->ports, next) {
+ if (port != source_port) {
+ ret += qemu_net_queue_flush(port->nc.send_queue);
+ }
+ }
+ return ret ? true : false;
+}
diff --git a/net/hub.h b/net/hub.h
index 4cbfdb128..a625effe0 100644
--- a/net/hub.h
+++ b/net/hub.h
@@ -20,8 +20,7 @@
NetClientState *net_hub_add_port(int hub_id, const char *name);
NetClientState *net_hub_find_client_by_name(int hub_id, const char *name);
void net_hub_info(Monitor *mon);
-int net_hub_id_for_client(NetClientState *nc, int *id);
void net_hub_check_clients(void);
-NetClientState *net_hub_port_find(int hub_id);
+bool net_hub_flush(NetClientState *nc);
#endif /* NET_HUB_H */
diff --git a/net/net.c b/net/net.c
new file mode 100644
index 000000000..c0d61bf78
--- /dev/null
+++ b/net/net.c
@@ -0,0 +1,1250 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "config-host.h"
+
+#include "net/net.h"
+#include "clients.h"
+#include "hub.h"
+#include "net/slirp.h"
+#include "util.h"
+
+#include "monitor/monitor.h"
+#include "qemu-common.h"
+#include "qemu/sockets.h"
+#include "qemu/config-file.h"
+#include "qmp-commands.h"
+#include "hw/qdev.h"
+#include "qemu/iov.h"
+#include "qapi-visit.h"
+#include "qapi/opts-visitor.h"
+#include "qapi/dealloc-visitor.h"
+
+/* Net bridge is currently not supported for W32. */
+#if !defined(_WIN32)
+# define CONFIG_NET_BRIDGE
+#endif
+
+static QTAILQ_HEAD(, NetClientState) net_clients;
+
+int default_net = 1;
+
+/***********************************************************/
+/* network device redirectors */
+
+#if defined(DEBUG_NET)
+static void hex_dump(FILE *f, const uint8_t *buf, int size)
+{
+ int len, i, j, c;
+
+ for(i=0;i<size;i+=16) {
+ len = size - i;
+ if (len > 16)
+ len = 16;
+ fprintf(f, "%08x ", i);
+ for(j=0;j<16;j++) {
+ if (j < len)
+ fprintf(f, " %02x", buf[i+j]);
+ else
+ fprintf(f, " ");
+ }
+ fprintf(f, " ");
+ for(j=0;j<len;j++) {
+ c = buf[i+j];
+ if (c < ' ' || c > '~')
+ c = '.';
+ fprintf(f, "%c", c);
+ }
+ fprintf(f, "\n");
+ }
+}
+#endif
+
+static int get_str_sep(char *buf, int buf_size, const char **pp, int sep)
+{
+ const char *p, *p1;
+ int len;
+ p = *pp;
+ p1 = strchr(p, sep);
+ if (!p1)
+ return -1;
+ len = p1 - p;
+ p1++;
+ if (buf_size > 0) {
+ if (len > buf_size - 1)
+ len = buf_size - 1;
+ memcpy(buf, p, len);
+ buf[len] = '\0';
+ }
+ *pp = p1;
+ return 0;
+}
+
+int parse_host_port(struct sockaddr_in *saddr, const char *str)
+{
+ char buf[512];
+ struct hostent *he;
+ const char *p, *r;
+ int port;
+
+ p = str;
+ if (get_str_sep(buf, sizeof(buf), &p, ':') < 0)
+ return -1;
+ saddr->sin_family = AF_INET;
+ if (buf[0] == '\0') {
+ saddr->sin_addr.s_addr = 0;
+ } else {
+ if (qemu_isdigit(buf[0])) {
+ if (!inet_aton(buf, &saddr->sin_addr))
+ return -1;
+ } else {
+ if ((he = gethostbyname(buf)) == NULL)
+ return - 1;
+ saddr->sin_addr = *(struct in_addr *)he->h_addr;
+ }
+ }
+ port = strtol(p, (char **)&r, 0);
+ if (r == p)
+ return -1;
+ saddr->sin_port = htons(port);
+ return 0;
+}
+
+void qemu_format_nic_info_str(NetClientState *nc, uint8_t macaddr[6])
+{
+ snprintf(nc->info_str, sizeof(nc->info_str),
+ "model=%s,macaddr=%02x:%02x:%02x:%02x:%02x:%02x",
+ nc->model,
+ macaddr[0], macaddr[1], macaddr[2],
+ macaddr[3], macaddr[4], macaddr[5]);
+}
+
+void qemu_macaddr_default_if_unset(MACAddr *macaddr)
+{
+ static int index = 0;
+ static const MACAddr zero = { .a = { 0,0,0,0,0,0 } };
+
+ if (memcmp(macaddr, &zero, sizeof(zero)) != 0)
+ return;
+ macaddr->a[0] = 0x52;
+ macaddr->a[1] = 0x54;
+ macaddr->a[2] = 0x00;
+ macaddr->a[3] = 0x12;
+ macaddr->a[4] = 0x34;
+ macaddr->a[5] = 0x56 + index++;
+}
+
+/**
+ * Generate a name for net client
+ *
+ * Only net clients created with the legacy -net option and NICs need this.
+ */
+static char *assign_name(NetClientState *nc1, const char *model)
+{
+ NetClientState *nc;
+ char buf[256];
+ int id = 0;
+
+ QTAILQ_FOREACH(nc, &net_clients, next) {
+ if (nc == nc1) {
+ continue;
+ }
+ if (strcmp(nc->model, model) == 0) {
+ id++;
+ }
+ }
+
+ snprintf(buf, sizeof(buf), "%s.%d", model, id);
+
+ return g_strdup(buf);
+}
+
+static void qemu_net_client_destructor(NetClientState *nc)
+{
+ g_free(nc);
+}
+
+static void qemu_net_client_setup(NetClientState *nc,
+ NetClientInfo *info,
+ NetClientState *peer,
+ const char *model,
+ const char *name,
+ NetClientDestructor *destructor)
+{
+ nc->info = info;
+ nc->model = g_strdup(model);
+ if (name) {
+ nc->name = g_strdup(name);
+ } else {
+ nc->name = assign_name(nc, model);
+ }
+
+ if (peer) {
+ assert(!peer->peer);
+ nc->peer = peer;
+ peer->peer = nc;
+ }
+ QTAILQ_INSERT_TAIL(&net_clients, nc, next);
+
+ nc->send_queue = qemu_new_net_queue(nc);
+ nc->destructor = destructor;
+}
+
+NetClientState *qemu_new_net_client(NetClientInfo *info,
+ NetClientState *peer,
+ const char *model,
+ const char *name)
+{
+ NetClientState *nc;
+
+ assert(info->size >= sizeof(NetClientState));
+
+ nc = g_malloc0(info->size);
+ qemu_net_client_setup(nc, info, peer, model, name,
+ qemu_net_client_destructor);
+
+ return nc;
+}
+
+NICState *qemu_new_nic(NetClientInfo *info,
+ NICConf *conf,
+ const char *model,
+ const char *name,
+ void *opaque)
+{
+ NetClientState **peers = conf->peers.ncs;
+ NICState *nic;
+ int i, queues = MAX(1, conf->queues);
+
+ assert(info->type == NET_CLIENT_OPTIONS_KIND_NIC);
+ assert(info->size >= sizeof(NICState));
+
+ nic = g_malloc0(info->size + sizeof(NetClientState) * queues);
+ nic->ncs = (void *)nic + info->size;
+ nic->conf = conf;
+ nic->opaque = opaque;
+
+ for (i = 0; i < queues; i++) {
+ qemu_net_client_setup(&nic->ncs[i], info, peers[i], model, name,
+ NULL);
+ nic->ncs[i].queue_index = i;
+ }
+
+ return nic;
+}
+
+NetClientState *qemu_get_subqueue(NICState *nic, int queue_index)
+{
+ return nic->ncs + queue_index;
+}
+
+NetClientState *qemu_get_queue(NICState *nic)
+{
+ return qemu_get_subqueue(nic, 0);
+}
+
+NICState *qemu_get_nic(NetClientState *nc)
+{
+ NetClientState *nc0 = nc - nc->queue_index;
+
+ return (NICState *)((void *)nc0 - nc->info->size);
+}
+
+void *qemu_get_nic_opaque(NetClientState *nc)
+{
+ NICState *nic = qemu_get_nic(nc);
+
+ return nic->opaque;
+}
+
+static void qemu_cleanup_net_client(NetClientState *nc)
+{
+ QTAILQ_REMOVE(&net_clients, nc, next);
+
+ if (nc->info->cleanup) {
+ nc->info->cleanup(nc);
+ }
+}
+
+static void qemu_free_net_client(NetClientState *nc)
+{
+ if (nc->send_queue) {
+ qemu_del_net_queue(nc->send_queue);
+ }
+ if (nc->peer) {
+ nc->peer->peer = NULL;
+ }
+ g_free(nc->name);
+ g_free(nc->model);
+ if (nc->destructor) {
+ nc->destructor(nc);
+ }
+}
+
+void qemu_del_net_client(NetClientState *nc)
+{
+ NetClientState *ncs[MAX_QUEUE_NUM];
+ int queues, i;
+
+ /* If the NetClientState belongs to a multiqueue backend, we will change all
+ * other NetClientStates also.
+ */
+ queues = qemu_find_net_clients_except(nc->name, ncs,
+ NET_CLIENT_OPTIONS_KIND_NIC,
+ MAX_QUEUE_NUM);
+ assert(queues != 0);
+
+ /* If there is a peer NIC, delete and cleanup client, but do not free. */
+ if (nc->peer && nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
+ NICState *nic = qemu_get_nic(nc->peer);
+ if (nic->peer_deleted) {
+ return;
+ }
+ nic->peer_deleted = true;
+
+ for (i = 0; i < queues; i++) {
+ ncs[i]->peer->link_down = true;
+ }
+
+ if (nc->peer->info->link_status_changed) {
+ nc->peer->info->link_status_changed(nc->peer);
+ }
+
+ for (i = 0; i < queues; i++) {
+ qemu_cleanup_net_client(ncs[i]);
+ }
+
+ return;
+ }
+
+ assert(nc->info->type != NET_CLIENT_OPTIONS_KIND_NIC);
+
+ for (i = 0; i < queues; i++) {
+ qemu_cleanup_net_client(ncs[i]);
+ qemu_free_net_client(ncs[i]);
+ }
+}
+
+void qemu_del_nic(NICState *nic)
+{
+ int i, queues = MAX(nic->conf->queues, 1);
+
+ /* If this is a peer NIC and peer has already been deleted, free it now. */
+ if (nic->peer_deleted) {
+ for (i = 0; i < queues; i++) {
+ qemu_free_net_client(qemu_get_subqueue(nic, i)->peer);
+ }
+ }
+
+ for (i = queues - 1; i >= 0; i--) {
+ NetClientState *nc = qemu_get_subqueue(nic, i);
+
+ qemu_cleanup_net_client(nc);
+ qemu_free_net_client(nc);
+ }
+
+ g_free(nic);
+}
+
+void qemu_foreach_nic(qemu_nic_foreach func, void *opaque)
+{
+ NetClientState *nc;
+
+ QTAILQ_FOREACH(nc, &net_clients, next) {
+ if (nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
+ if (nc->queue_index == 0) {
+ func(qemu_get_nic(nc), opaque);
+ }
+ }
+ }
+}
+
+int qemu_can_send_packet(NetClientState *sender)
+{
+ if (!sender->peer) {
+ return 1;
+ }
+
+ if (sender->peer->receive_disabled) {
+ return 0;
+ } else if (sender->peer->info->can_receive &&
+ !sender->peer->info->can_receive(sender->peer)) {
+ return 0;
+ }
+ return 1;
+}
+
+ssize_t qemu_deliver_packet(NetClientState *sender,
+ unsigned flags,
+ const uint8_t *data,
+ size_t size,
+ void *opaque)
+{
+ NetClientState *nc = opaque;
+ ssize_t ret;
+
+ if (nc->link_down) {
+ return size;
+ }
+
+ if (nc->receive_disabled) {
+ return 0;
+ }
+
+ if (flags & QEMU_NET_PACKET_FLAG_RAW && nc->info->receive_raw) {
+ ret = nc->info->receive_raw(nc, data, size);
+ } else {
+ ret = nc->info->receive(nc, data, size);
+ }
+
+ if (ret == 0) {
+ nc->receive_disabled = 1;
+ };
+
+ return ret;
+}
+
+void qemu_purge_queued_packets(NetClientState *nc)
+{
+ if (!nc->peer) {
+ return;
+ }
+
+ qemu_net_queue_purge(nc->peer->send_queue, nc);
+}
+
+void qemu_flush_queued_packets(NetClientState *nc)
+{
+ nc->receive_disabled = 0;
+
+ if (nc->peer && nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_HUBPORT) {
+ if (net_hub_flush(nc->peer)) {
+ qemu_notify_event();
+ }
+ return;
+ }
+ if (qemu_net_queue_flush(nc->send_queue)) {
+ /* We emptied the queue successfully, signal to the IO thread to repoll
+ * the file descriptor (for tap, for example).
+ */
+ qemu_notify_event();
+ }
+}
+
+static ssize_t qemu_send_packet_async_with_flags(NetClientState *sender,
+ unsigned flags,
+ const uint8_t *buf, int size,
+ NetPacketSent *sent_cb)
+{
+ NetQueue *queue;
+
+#ifdef DEBUG_NET
+ printf("qemu_send_packet_async:\n");
+ hex_dump(stdout, buf, size);
+#endif
+
+ if (sender->link_down || !sender->peer) {
+ return size;
+ }
+
+ queue = sender->peer->send_queue;
+
+ return qemu_net_queue_send(queue, sender, flags, buf, size, sent_cb);
+}
+
+ssize_t qemu_send_packet_async(NetClientState *sender,
+ const uint8_t *buf, int size,
+ NetPacketSent *sent_cb)
+{
+ return qemu_send_packet_async_with_flags(sender, QEMU_NET_PACKET_FLAG_NONE,
+ buf, size, sent_cb);
+}
+
+void qemu_send_packet(NetClientState *nc, const uint8_t *buf, int size)
+{
+ qemu_send_packet_async(nc, buf, size, NULL);
+}
+
+ssize_t qemu_send_packet_raw(NetClientState *nc, const uint8_t *buf, int size)
+{
+ return qemu_send_packet_async_with_flags(nc, QEMU_NET_PACKET_FLAG_RAW,
+ buf, size, NULL);
+}
+
+static ssize_t nc_sendv_compat(NetClientState *nc, const struct iovec *iov,
+ int iovcnt)
+{
+ uint8_t buffer[NET_BUFSIZE];
+ size_t offset;
+
+ offset = iov_to_buf(iov, iovcnt, 0, buffer, sizeof(buffer));
+
+ return nc->info->receive(nc, buffer, offset);
+}
+
+ssize_t qemu_deliver_packet_iov(NetClientState *sender,
+ unsigned flags,
+ const struct iovec *iov,
+ int iovcnt,
+ void *opaque)
+{
+ NetClientState *nc = opaque;
+ int ret;
+
+ if (nc->link_down) {
+ return iov_size(iov, iovcnt);
+ }
+
+ if (nc->receive_disabled) {
+ return 0;
+ }
+
+ if (nc->info->receive_iov) {
+ ret = nc->info->receive_iov(nc, iov, iovcnt);
+ } else {
+ ret = nc_sendv_compat(nc, iov, iovcnt);
+ }
+
+ if (ret == 0) {
+ nc->receive_disabled = 1;
+ }
+
+ return ret;
+}
+
+ssize_t qemu_sendv_packet_async(NetClientState *sender,
+ const struct iovec *iov, int iovcnt,
+ NetPacketSent *sent_cb)
+{
+ NetQueue *queue;
+
+ if (sender->link_down || !sender->peer) {
+ return iov_size(iov, iovcnt);
+ }
+
+ queue = sender->peer->send_queue;
+
+ return qemu_net_queue_send_iov(queue, sender,
+ QEMU_NET_PACKET_FLAG_NONE,
+ iov, iovcnt, sent_cb);
+}
+
+ssize_t
+qemu_sendv_packet(NetClientState *nc, const struct iovec *iov, int iovcnt)
+{
+ return qemu_sendv_packet_async(nc, iov, iovcnt, NULL);
+}
+
+NetClientState *qemu_find_netdev(const char *id)
+{
+ NetClientState *nc;
+
+ QTAILQ_FOREACH(nc, &net_clients, next) {
+ if (nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC)
+ continue;
+ if (!strcmp(nc->name, id)) {
+ return nc;
+ }
+ }
+
+ return NULL;
+}
+
+int qemu_find_net_clients_except(const char *id, NetClientState **ncs,
+ NetClientOptionsKind type, int max)
+{
+ NetClientState *nc;
+ int ret = 0;
+
+ QTAILQ_FOREACH(nc, &net_clients, next) {
+ if (nc->info->type == type) {
+ continue;
+ }
+ if (!strcmp(nc->name, id)) {
+ if (ret < max) {
+ ncs[ret] = nc;
+ }
+ ret++;
+ }
+ }
+
+ return ret;
+}
+
+static int nic_get_free_idx(void)
+{
+ int index;
+
+ for (index = 0; index < MAX_NICS; index++)
+ if (!nd_table[index].used)
+ return index;
+ return -1;
+}
+
+int qemu_show_nic_models(const char *arg, const char *const *models)
+{
+ int i;
+
+ if (!arg || !is_help_option(arg)) {
+ return 0;
+ }
+
+ fprintf(stderr, "qemu: Supported NIC models: ");
+ for (i = 0 ; models[i]; i++)
+ fprintf(stderr, "%s%c", models[i], models[i+1] ? ',' : '\n');
+ return 1;
+}
+
+void qemu_check_nic_model(NICInfo *nd, const char *model)
+{
+ const char *models[2];
+
+ models[0] = model;
+ models[1] = NULL;
+
+ if (qemu_show_nic_models(nd->model, models))
+ exit(0);
+ if (qemu_find_nic_model(nd, models, model) < 0)
+ exit(1);
+}
+
+int qemu_find_nic_model(NICInfo *nd, const char * const *models,
+ const char *default_model)
+{
+ int i;
+
+ if (!nd->model)
+ nd->model = g_strdup(default_model);
+
+ for (i = 0 ; models[i]; i++) {
+ if (strcmp(nd->model, models[i]) == 0)
+ return i;
+ }
+
+ error_report("Unsupported NIC model: %s", nd->model);
+ return -1;
+}
+
+static int net_init_nic(const NetClientOptions *opts, const char *name,
+ NetClientState *peer)
+{
+ int idx;
+ NICInfo *nd;
+ const NetLegacyNicOptions *nic;
+
+ assert(opts->kind == NET_CLIENT_OPTIONS_KIND_NIC);
+ nic = opts->nic;
+
+ idx = nic_get_free_idx();
+ if (idx == -1 || nb_nics >= MAX_NICS) {
+ error_report("Too Many NICs");
+ return -1;
+ }
+
+ nd = &nd_table[idx];
+
+ memset(nd, 0, sizeof(*nd));
+
+ if (nic->has_netdev) {
+ nd->netdev = qemu_find_netdev(nic->netdev);
+ if (!nd->netdev) {
+ error_report("netdev '%s' not found", nic->netdev);
+ return -1;
+ }
+ } else {
+ assert(peer);
+ nd->netdev = peer;
+ }
+ nd->name = g_strdup(name);
+ if (nic->has_model) {
+ nd->model = g_strdup(nic->model);
+ }
+ if (nic->has_addr) {
+ nd->devaddr = g_strdup(nic->addr);
+ }
+
+ if (nic->has_macaddr &&
+ net_parse_macaddr(nd->macaddr.a, nic->macaddr) < 0) {
+ error_report("invalid syntax for ethernet address");
+ return -1;
+ }
+ qemu_macaddr_default_if_unset(&nd->macaddr);
+
+ if (nic->has_vectors) {
+ if (nic->vectors > 0x7ffffff) {
+ error_report("invalid # of vectors: %"PRIu32, nic->vectors);
+ return -1;
+ }
+ nd->nvectors = nic->vectors;
+ } else {
+ nd->nvectors = DEV_NVECTORS_UNSPECIFIED;
+ }
+
+ nd->used = 1;
+ nb_nics++;
+
+ return idx;
+}
+
+
+static int (* const net_client_init_fun[NET_CLIENT_OPTIONS_KIND_MAX])(
+ const NetClientOptions *opts,
+ const char *name,
+ NetClientState *peer) = {
+ [NET_CLIENT_OPTIONS_KIND_NIC] = net_init_nic,
+#ifdef CONFIG_SLIRP
+ [NET_CLIENT_OPTIONS_KIND_USER] = net_init_slirp,
+#endif
+ [NET_CLIENT_OPTIONS_KIND_TAP] = net_init_tap,
+ [NET_CLIENT_OPTIONS_KIND_SOCKET] = net_init_socket,
+#ifdef CONFIG_VDE
+ [NET_CLIENT_OPTIONS_KIND_VDE] = net_init_vde,
+#endif
+ [NET_CLIENT_OPTIONS_KIND_DUMP] = net_init_dump,
+#ifdef CONFIG_NET_BRIDGE
+ [NET_CLIENT_OPTIONS_KIND_BRIDGE] = net_init_bridge,
+#endif
+ [NET_CLIENT_OPTIONS_KIND_HUBPORT] = net_init_hubport,
+};
+
+
+static int net_client_init1(const void *object, int is_netdev, Error **errp)
+{
+ union {
+ const Netdev *netdev;
+ const NetLegacy *net;
+ } u;
+ const NetClientOptions *opts;
+ const char *name;
+
+ if (is_netdev) {
+ u.netdev = object;
+ opts = u.netdev->opts;
+ name = u.netdev->id;
+
+ switch (opts->kind) {
+#ifdef CONFIG_SLIRP
+ case NET_CLIENT_OPTIONS_KIND_USER:
+#endif
+ case NET_CLIENT_OPTIONS_KIND_TAP:
+ case NET_CLIENT_OPTIONS_KIND_SOCKET:
+#ifdef CONFIG_VDE
+ case NET_CLIENT_OPTIONS_KIND_VDE:
+#endif
+#ifdef CONFIG_NET_BRIDGE
+ case NET_CLIENT_OPTIONS_KIND_BRIDGE:
+#endif
+ case NET_CLIENT_OPTIONS_KIND_HUBPORT:
+ break;
+
+ default:
+ error_set(errp, QERR_INVALID_PARAMETER_VALUE, "type",
+ "a netdev backend type");
+ return -1;
+ }
+ } else {
+ u.net = object;
+ opts = u.net->opts;
+ /* missing optional values have been initialized to "all bits zero" */
+ name = u.net->has_id ? u.net->id : u.net->name;
+ }
+
+ if (net_client_init_fun[opts->kind]) {
+ NetClientState *peer = NULL;
+
+ /* Do not add to a vlan if it's a -netdev or a nic with a netdev=
+ * parameter. */
+ if (!is_netdev &&
+ (opts->kind != NET_CLIENT_OPTIONS_KIND_NIC ||
+ !opts->nic->has_netdev)) {
+ peer = net_hub_add_port(u.net->has_vlan ? u.net->vlan : 0, NULL);
+ }
+
+ if (net_client_init_fun[opts->kind](opts, name, peer) < 0) {
+ /* TODO push error reporting into init() methods */
+ error_set(errp, QERR_DEVICE_INIT_FAILED,
+ NetClientOptionsKind_lookup[opts->kind]);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+
+static void net_visit(Visitor *v, int is_netdev, void **object, Error **errp)
+{
+ if (is_netdev) {
+ visit_type_Netdev(v, (Netdev **)object, NULL, errp);
+ } else {
+ visit_type_NetLegacy(v, (NetLegacy **)object, NULL, errp);
+ }
+}
+
+
+int net_client_init(QemuOpts *opts, int is_netdev, Error **errp)
+{
+ void *object = NULL;
+ Error *err = NULL;
+ int ret = -1;
+
+ {
+ OptsVisitor *ov = opts_visitor_new(opts);
+
+ net_visit(opts_get_visitor(ov), is_netdev, &object, &err);
+ opts_visitor_cleanup(ov);
+ }
+
+ if (!err) {
+ ret = net_client_init1(object, is_netdev, &err);
+ }
+
+ if (object) {
+ QapiDeallocVisitor *dv = qapi_dealloc_visitor_new();
+
+ net_visit(qapi_dealloc_get_visitor(dv), is_netdev, &object, NULL);
+ qapi_dealloc_visitor_cleanup(dv);
+ }
+
+ error_propagate(errp, err);
+ return ret;
+}
+
+
+static int net_host_check_device(const char *device)
+{
+ int i;
+ const char *valid_param_list[] = { "tap", "socket", "dump"
+#ifdef CONFIG_NET_BRIDGE
+ , "bridge"
+#endif
+#ifdef CONFIG_SLIRP
+ ,"user"
+#endif
+#ifdef CONFIG_VDE
+ ,"vde"
+#endif
+ };
+ for (i = 0; i < sizeof(valid_param_list) / sizeof(char *); i++) {
+ if (!strncmp(valid_param_list[i], device,
+ strlen(valid_param_list[i])))
+ return 1;
+ }
+
+ return 0;
+}
+
+void net_host_device_add(Monitor *mon, const QDict *qdict)
+{
+ const char *device = qdict_get_str(qdict, "device");
+ const char *opts_str = qdict_get_try_str(qdict, "opts");
+ Error *local_err = NULL;
+ QemuOpts *opts;
+
+ if (!net_host_check_device(device)) {
+ monitor_printf(mon, "invalid host network device %s\n", device);
+ return;
+ }
+
+ opts = qemu_opts_parse(qemu_find_opts("net"), opts_str ? opts_str : "", 0);
+ if (!opts) {
+ return;
+ }
+
+ qemu_opt_set(opts, "type", device);
+
+ net_client_init(opts, 0, &local_err);
+ if (error_is_set(&local_err)) {
+ qerror_report_err(local_err);
+ error_free(local_err);
+ monitor_printf(mon, "adding host network device %s failed\n", device);
+ }
+}
+
+void net_host_device_remove(Monitor *mon, const QDict *qdict)
+{
+ NetClientState *nc;
+ int vlan_id = qdict_get_int(qdict, "vlan_id");
+ const char *device = qdict_get_str(qdict, "device");
+
+ nc = net_hub_find_client_by_name(vlan_id, device);
+ if (!nc) {
+ return;
+ }
+ if (!net_host_check_device(nc->model)) {
+ monitor_printf(mon, "invalid host network device %s\n", device);
+ return;
+ }
+ qemu_del_net_client(nc);
+}
+
+void netdev_add(QemuOpts *opts, Error **errp)
+{
+ net_client_init(opts, 1, errp);
+}
+
+int qmp_netdev_add(Monitor *mon, const QDict *qdict, QObject **ret)
+{
+ Error *local_err = NULL;
+ QemuOptsList *opts_list;
+ QemuOpts *opts;
+
+ opts_list = qemu_find_opts_err("netdev", &local_err);
+ if (error_is_set(&local_err)) {
+ goto exit_err;
+ }
+
+ opts = qemu_opts_from_qdict(opts_list, qdict, &local_err);
+ if (error_is_set(&local_err)) {
+ goto exit_err;
+ }
+
+ netdev_add(opts, &local_err);
+ if (error_is_set(&local_err)) {
+ qemu_opts_del(opts);
+ goto exit_err;
+ }
+
+ return 0;
+
+exit_err:
+ qerror_report_err(local_err);
+ error_free(local_err);
+ return -1;
+}
+
+void qmp_netdev_del(const char *id, Error **errp)
+{
+ NetClientState *nc;
+ QemuOpts *opts;
+
+ nc = qemu_find_netdev(id);
+ if (!nc) {
+ error_set(errp, QERR_DEVICE_NOT_FOUND, id);
+ return;
+ }
+
+ opts = qemu_opts_find(qemu_find_opts_err("netdev", NULL), id);
+ if (!opts) {
+ error_setg(errp, "Device '%s' is not a netdev", id);
+ return;
+ }
+
+ qemu_del_net_client(nc);
+ qemu_opts_del(opts);
+}
+
+void print_net_client(Monitor *mon, NetClientState *nc)
+{
+ monitor_printf(mon, "%s: index=%d,type=%s,%s\n", nc->name,
+ nc->queue_index,
+ NetClientOptionsKind_lookup[nc->info->type],
+ nc->info_str);
+}
+
+RxFilterInfoList *qmp_query_rx_filter(bool has_name, const char *name,
+ Error **errp)
+{
+ NetClientState *nc;
+ RxFilterInfoList *filter_list = NULL, *last_entry = NULL;
+
+ QTAILQ_FOREACH(nc, &net_clients, next) {
+ RxFilterInfoList *entry;
+ RxFilterInfo *info;
+
+ if (has_name && strcmp(nc->name, name) != 0) {
+ continue;
+ }
+
+ /* only query rx-filter information of NIC */
+ if (nc->info->type != NET_CLIENT_OPTIONS_KIND_NIC) {
+ if (has_name) {
+ error_setg(errp, "net client(%s) isn't a NIC", name);
+ break;
+ }
+ continue;
+ }
+
+ if (nc->info->query_rx_filter) {
+ info = nc->info->query_rx_filter(nc);
+ entry = g_malloc0(sizeof(*entry));
+ entry->value = info;
+
+ if (!filter_list) {
+ filter_list = entry;
+ } else {
+ last_entry->next = entry;
+ }
+ last_entry = entry;
+ } else if (has_name) {
+ error_setg(errp, "net client(%s) doesn't support"
+ " rx-filter querying", name);
+ break;
+ }
+ }
+
+ if (filter_list == NULL && !error_is_set(errp) && has_name) {
+ error_setg(errp, "invalid net client name: %s", name);
+ }
+
+ return filter_list;
+}
+
+void do_info_network(Monitor *mon, const QDict *qdict)
+{
+ NetClientState *nc, *peer;
+ NetClientOptionsKind type;
+
+ net_hub_info(mon);
+
+ QTAILQ_FOREACH(nc, &net_clients, next) {
+ peer = nc->peer;
+ type = nc->info->type;
+
+ /* Skip if already printed in hub info */
+ if (net_hub_id_for_client(nc, NULL) == 0) {
+ continue;
+ }
+
+ if (!peer || type == NET_CLIENT_OPTIONS_KIND_NIC) {
+ print_net_client(mon, nc);
+ } /* else it's a netdev connected to a NIC, printed with the NIC */
+ if (peer && type == NET_CLIENT_OPTIONS_KIND_NIC) {
+ monitor_printf(mon, " \\ ");
+ print_net_client(mon, peer);
+ }
+ }
+}
+
+void qmp_set_link(const char *name, bool up, Error **errp)
+{
+ NetClientState *ncs[MAX_QUEUE_NUM];
+ NetClientState *nc;
+ int queues, i;
+
+ queues = qemu_find_net_clients_except(name, ncs,
+ NET_CLIENT_OPTIONS_KIND_MAX,
+ MAX_QUEUE_NUM);
+
+ if (queues == 0) {
+ error_set(errp, QERR_DEVICE_NOT_FOUND, name);
+ return;
+ }
+ nc = ncs[0];
+
+ for (i = 0; i < queues; i++) {
+ ncs[i]->link_down = !up;
+ }
+
+ if (nc->info->link_status_changed) {
+ nc->info->link_status_changed(nc);
+ }
+
+ /* Notify peer. Don't update peer link status: this makes it possible to
+ * disconnect from host network without notifying the guest.
+ * FIXME: is disconnected link status change operation useful?
+ *
+ * Current behaviour is compatible with qemu vlans where there could be
+ * multiple clients that can still communicate with each other in
+ * disconnected mode. For now maintain this compatibility. */
+ if (nc->peer && nc->peer->info->link_status_changed) {
+ nc->peer->info->link_status_changed(nc->peer);
+ }
+}
+
+void net_cleanup(void)
+{
+ NetClientState *nc;
+
+ /* We may del multiple entries during qemu_del_net_client(),
+ * so QTAILQ_FOREACH_SAFE() is also not safe here.
+ */
+ while (!QTAILQ_EMPTY(&net_clients)) {
+ nc = QTAILQ_FIRST(&net_clients);
+ if (nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
+ qemu_del_nic(qemu_get_nic(nc));
+ } else {
+ qemu_del_net_client(nc);
+ }
+ }
+}
+
+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) {
+ if (!nc->peer) {
+ fprintf(stderr, "Warning: %s %s has no peer\n",
+ nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC ?
+ "nic" : "netdev", nc->name);
+ }
+ }
+
+ /* Check that all NICs requested via -net nic actually got created.
+ * NICs created via -device don't need to be checked here because
+ * they are always instantiated.
+ */
+ for (i = 0; i < MAX_NICS; i++) {
+ NICInfo *nd = &nd_table[i];
+ if (nd->used && !nd->instantiated) {
+ fprintf(stderr, "Warning: requested NIC (%s, model %s) "
+ "was not created (not supported by this machine?)\n",
+ nd->name ? nd->name : "anonymous",
+ nd->model ? nd->model : "unspecified");
+ }
+ }
+}
+
+static int net_init_client(QemuOpts *opts, void *dummy)
+{
+ Error *local_err = NULL;
+
+ net_client_init(opts, 0, &local_err);
+ if (error_is_set(&local_err)) {
+ qerror_report_err(local_err);
+ error_free(local_err);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int net_init_netdev(QemuOpts *opts, void *dummy)
+{
+ Error *local_err = NULL;
+ int ret;
+
+ ret = net_client_init(opts, 1, &local_err);
+ if (error_is_set(&local_err)) {
+ qerror_report_err(local_err);
+ error_free(local_err);
+ return -1;
+ }
+
+ return ret;
+}
+
+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");
+#ifdef CONFIG_SLIRP
+ qemu_opts_set(net, NULL, "type", "user");
+#endif
+ }
+
+ QTAILQ_INIT(&net_clients);
+
+ if (qemu_opts_foreach(qemu_find_opts("netdev"), net_init_netdev, NULL, 1) == -1)
+ return -1;
+
+ if (qemu_opts_foreach(net, net_init_client, NULL, 1) == -1) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int net_client_parse(QemuOptsList *opts_list, const char *optarg)
+{
+#if defined(CONFIG_SLIRP)
+ int ret;
+ if (net_slirp_parse_legacy(opts_list, optarg, &ret)) {
+ return ret;
+ }
+#endif
+
+ if (!qemu_opts_parse(opts_list, optarg, 1)) {
+ return -1;
+ }
+
+ default_net = 0;
+ return 0;
+}
+
+/* From FreeBSD */
+/* XXX: optimize */
+unsigned compute_mcast_idx(const uint8_t *ep)
+{
+ uint32_t crc;
+ int carry, i, j;
+ uint8_t b;
+
+ crc = 0xffffffff;
+ for (i = 0; i < 6; i++) {
+ b = *ep++;
+ for (j = 0; j < 8; j++) {
+ carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01);
+ crc <<= 1;
+ b >>= 1;
+ if (carry) {
+ crc = ((crc ^ POLYNOMIAL) | carry);
+ }
+ }
+ }
+ return crc >> 26;
+}
+
+QemuOptsList qemu_netdev_opts = {
+ .name = "netdev",
+ .implied_opt_name = "type",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_netdev_opts.head),
+ .desc = {
+ /*
+ * no elements => accept any params
+ * validation will happen later
+ */
+ { /* end of list */ }
+ },
+};
+
+QemuOptsList qemu_net_opts = {
+ .name = "net",
+ .implied_opt_name = "type",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_net_opts.head),
+ .desc = {
+ /*
+ * no elements => accept any params
+ * validation will happen later
+ */
+ { /* end of list */ }
+ },
+};
diff --git a/net/queue.c b/net/queue.c
index 254f28013..859d02a13 100644
--- a/net/queue.c
+++ b/net/queue.c
@@ -22,8 +22,8 @@
*/
#include "net/queue.h"
-#include "qemu-queue.h"
-#include "net.h"
+#include "qemu/queue.h"
+#include "net/net.h"
/* The delivery handler may only return zero if it will call
* qemu_net_queue_flush() when it determines that it is once again able
@@ -50,6 +50,8 @@ struct NetPacket {
struct NetQueue {
void *opaque;
+ uint32_t nq_maxlen;
+ uint32_t nq_count;
QTAILQ_HEAD(packets, NetPacket) packets;
@@ -63,6 +65,8 @@ NetQueue *qemu_new_net_queue(void *opaque)
queue = g_malloc0(sizeof(NetQueue));
queue->opaque = opaque;
+ queue->nq_maxlen = 10000;
+ queue->nq_count = 0;
QTAILQ_INIT(&queue->packets);
@@ -92,6 +96,9 @@ static void qemu_net_queue_append(NetQueue *queue,
{
NetPacket *packet;
+ if (queue->nq_count >= queue->nq_maxlen && !sent_cb) {
+ return; /* drop if queue full and no callback */
+ }
packet = g_malloc(sizeof(NetPacket) + size);
packet->sender = sender;
packet->flags = flags;
@@ -99,6 +106,7 @@ static void qemu_net_queue_append(NetQueue *queue,
packet->sent_cb = sent_cb;
memcpy(packet->data, buf, size);
+ queue->nq_count++;
QTAILQ_INSERT_TAIL(&queue->packets, packet, entry);
}
@@ -113,6 +121,9 @@ static void qemu_net_queue_append_iov(NetQueue *queue,
size_t max_len = 0;
int i;
+ if (queue->nq_count >= queue->nq_maxlen && !sent_cb) {
+ return; /* drop if queue full and no callback */
+ }
for (i = 0; i < iovcnt; i++) {
max_len += iov[i].iov_len;
}
@@ -130,6 +141,7 @@ static void qemu_net_queue_append_iov(NetQueue *queue,
packet->size += len;
}
+ queue->nq_count++;
QTAILQ_INSERT_TAIL(&queue->packets, packet, entry);
}
@@ -220,6 +232,7 @@ void qemu_net_queue_purge(NetQueue *queue, NetClientState *from)
QTAILQ_FOREACH_SAFE(packet, &queue->packets, entry, next) {
if (packet->sender == from) {
QTAILQ_REMOVE(&queue->packets, packet, entry);
+ queue->nq_count--;
g_free(packet);
}
}
@@ -233,6 +246,7 @@ bool qemu_net_queue_flush(NetQueue *queue)
packet = QTAILQ_FIRST(&queue->packets);
QTAILQ_REMOVE(&queue->packets, packet, entry);
+ queue->nq_count--;
ret = qemu_net_queue_deliver(queue,
packet->sender,
@@ -240,6 +254,7 @@ bool qemu_net_queue_flush(NetQueue *queue)
packet->data,
packet->size);
if (ret == 0) {
+ queue->nq_count++;
QTAILQ_INSERT_HEAD(&queue->packets, packet, entry);
return false;
}
diff --git a/net/queue.h b/net/queue.h
deleted file mode 100644
index fc02b3391..000000000
--- a/net/queue.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (c) 2003-2008 Fabrice Bellard
- * Copyright (c) 2009 Red Hat, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#ifndef QEMU_NET_QUEUE_H
-#define QEMU_NET_QUEUE_H
-
-#include "qemu-common.h"
-
-typedef struct NetPacket NetPacket;
-typedef struct NetQueue NetQueue;
-
-typedef void (NetPacketSent) (NetClientState *sender, ssize_t ret);
-
-#define QEMU_NET_PACKET_FLAG_NONE 0
-#define QEMU_NET_PACKET_FLAG_RAW (1<<0)
-
-NetQueue *qemu_new_net_queue(void *opaque);
-
-void qemu_del_net_queue(NetQueue *queue);
-
-ssize_t qemu_net_queue_send(NetQueue *queue,
- NetClientState *sender,
- unsigned flags,
- const uint8_t *data,
- size_t size,
- NetPacketSent *sent_cb);
-
-ssize_t qemu_net_queue_send_iov(NetQueue *queue,
- NetClientState *sender,
- unsigned flags,
- const struct iovec *iov,
- int iovcnt,
- NetPacketSent *sent_cb);
-
-void qemu_net_queue_purge(NetQueue *queue, NetClientState *from);
-bool qemu_net_queue_flush(NetQueue *queue);
-
-#endif /* QEMU_NET_QUEUE_H */
diff --git a/net/slirp.c b/net/slirp.c
index afb52c3af..124e953d9 100644
--- a/net/slirp.c
+++ b/net/slirp.c
@@ -29,12 +29,13 @@
#include <pwd.h>
#include <sys/wait.h>
#endif
-#include "net.h"
+#include "net/net.h"
#include "clients.h"
#include "hub.h"
-#include "monitor.h"
-#include "qemu_socket.h"
+#include "monitor/monitor.h"
+#include "qemu/sockets.h"
#include "slirp/libslirp.h"
+#include "sysemu/char.h"
static int get_str_sep(char *buf, int buf_size, const char **pp, int sep)
{
@@ -211,19 +212,19 @@ static int net_slirp_init(NetClientState *peer, const char *model,
return -1;
}
- if (vdhcp_start && !inet_aton(vdhcp_start, &dhcp)) {
+ if (vnameserver && !inet_aton(vnameserver, &dns)) {
return -1;
}
- if ((dhcp.s_addr & mask.s_addr) != net.s_addr ||
- dhcp.s_addr == host.s_addr || dhcp.s_addr == dns.s_addr) {
+ if ((dns.s_addr & mask.s_addr) != net.s_addr ||
+ dns.s_addr == host.s_addr) {
return -1;
}
- if (vnameserver && !inet_aton(vnameserver, &dns)) {
+ if (vdhcp_start && !inet_aton(vdhcp_start, &dhcp)) {
return -1;
}
- if ((dns.s_addr & mask.s_addr) != net.s_addr ||
- dns.s_addr == host.s_addr) {
+ if ((dhcp.s_addr & mask.s_addr) != net.s_addr ||
+ dhcp.s_addr == host.s_addr || dhcp.s_addr == dns.s_addr) {
return -1;
}
@@ -659,6 +660,7 @@ static int slirp_guestfwd(SlirpState *s, const char *config_str,
fwd->port = port;
fwd->slirp = s->slirp;
+ qemu_chr_fe_claim_no_fail(fwd->hd);
qemu_chr_add_handlers(fwd->hd, guestfwd_can_read, guestfwd_read,
NULL, fwd);
}
@@ -669,7 +671,7 @@ static int slirp_guestfwd(SlirpState *s, const char *config_str,
return -1;
}
-void do_info_usernet(Monitor *mon)
+void do_info_usernet(Monitor *mon, const QDict *qdict)
{
SlirpState *s;
diff --git a/net/slirp.h b/net/slirp.h
deleted file mode 100644
index 2ca09b65b..000000000
--- a/net/slirp.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * QEMU System Emulator
- *
- * Copyright (c) 2003-2008 Fabrice Bellard
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-#ifndef QEMU_NET_SLIRP_H
-#define QEMU_NET_SLIRP_H
-
-#include "qemu-common.h"
-#include "qdict.h"
-#include "qemu-option.h"
-#include "qapi-types.h"
-
-#ifdef CONFIG_SLIRP
-
-void net_slirp_hostfwd_add(Monitor *mon, const QDict *qdict);
-void net_slirp_hostfwd_remove(Monitor *mon, const QDict *qdict);
-
-int net_slirp_redir(const char *redir_str);
-
-int net_slirp_parse_legacy(QemuOptsList *opts_list, const char *optarg, int *ret);
-
-int net_slirp_smb(const char *exported_dir);
-
-void do_info_usernet(Monitor *mon);
-
-#endif
-
-#endif /* QEMU_NET_SLIRP_H */
diff --git a/net/socket.c b/net/socket.c
index c01323d4b..87af1d3d3 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -23,15 +23,14 @@
*/
#include "config-host.h"
-#include "net.h"
+#include "net/net.h"
#include "clients.h"
-#include "monitor.h"
-#include "qemu-char.h"
+#include "monitor/monitor.h"
#include "qemu-common.h"
-#include "qemu-error.h"
-#include "qemu-option.h"
-#include "qemu_socket.h"
-#include "iov.h"
+#include "qemu/error-report.h"
+#include "qemu/option.h"
+#include "qemu/sockets.h"
+#include "qemu/iov.h"
typedef struct NetSocketState {
NetClientState nc;
@@ -41,7 +40,7 @@ typedef struct NetSocketState {
unsigned int index;
unsigned int packet_len;
unsigned int send_index; /* number of bytes sent (only SOCK_STREAM) */
- uint8_t buf[4096];
+ 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? */
@@ -147,7 +146,7 @@ static void net_socket_send(void *opaque)
NetSocketState *s = opaque;
int size, err;
unsigned l;
- uint8_t buf1[4096];
+ uint8_t buf1[NET_BUFSIZE];
const uint8_t *buf;
size = qemu_recv(s->fd, buf1, sizeof(buf1), 0);
@@ -263,8 +262,7 @@ static int net_socket_mcast_create(struct sockaddr_in *mcastaddr, struct in_addr
}
val = 1;
- ret=setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
- (const char *)&val, sizeof(val));
+ ret = qemu_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
if (ret < 0) {
perror("setsockopt(SOL_SOCKET, SO_REUSEADDR)");
goto fail;
@@ -284,8 +282,8 @@ static int net_socket_mcast_create(struct sockaddr_in *mcastaddr, struct in_addr
imr.imr_interface.s_addr = htonl(INADDR_ANY);
}
- ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
- (const char *)&imr, sizeof(struct ip_mreq));
+ ret = qemu_setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ &imr, sizeof(struct ip_mreq));
if (ret < 0) {
perror("setsockopt(IP_ADD_MEMBERSHIP)");
goto fail;
@@ -293,8 +291,8 @@ static int net_socket_mcast_create(struct sockaddr_in *mcastaddr, struct in_addr
/* Force mcast msgs to loopback (eg. several QEMUs in same host */
loop = 1;
- ret=setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
- (const char *)&loop, sizeof(loop));
+ ret = qemu_setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
+ &loop, sizeof(loop));
if (ret < 0) {
perror("setsockopt(SOL_IP, IP_MULTICAST_LOOP)");
goto fail;
@@ -302,15 +300,15 @@ static int net_socket_mcast_create(struct sockaddr_in *mcastaddr, struct in_addr
/* If a bind address is given, only send packets from that address */
if (localaddr != NULL) {
- ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
- (const char *)localaddr, sizeof(*localaddr));
+ ret = qemu_setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
+ localaddr, sizeof(*localaddr));
if (ret < 0) {
perror("setsockopt(IP_MULTICAST_IF)");
goto fail;
}
}
- socket_set_nonblock(fd);
+ qemu_set_nonblock(fd);
return fd;
fail:
if (fd >= 0)
@@ -440,6 +438,9 @@ static NetSocketState *net_socket_fd_init_stream(NetClientState *peer,
s->fd = fd;
s->listen_fd = -1;
+ /* Disable Nagle algorithm on TCP sockets to reduce latency */
+ socket_set_nodelay(fd);
+
if (is_connected) {
net_socket_connect(s);
} else {
@@ -518,11 +519,11 @@ static int net_socket_listen_init(NetClientState *peer,
perror("socket");
return -1;
}
- socket_set_nonblock(fd);
+ qemu_set_nonblock(fd);
/* allow fast reuse */
val = 1;
- setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&val, sizeof(val));
+ qemu_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
ret = bind(fd, (struct sockaddr *)&saddr, sizeof(saddr));
if (ret < 0) {
@@ -564,7 +565,7 @@ static int net_socket_connect_init(NetClientState *peer,
perror("socket");
return -1;
}
- socket_set_nonblock(fd);
+ qemu_set_nonblock(fd);
connected = 0;
for(;;) {
@@ -660,8 +661,8 @@ static int net_socket_udp_init(NetClientState *peer,
return -1;
}
val = 1;
- ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
- (const char *)&val, sizeof(val));
+ ret = qemu_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+ &val, sizeof(val));
if (ret < 0) {
perror("setsockopt(SOL_SOCKET, SO_REUSEADDR)");
closesocket(fd);
@@ -673,6 +674,7 @@ static int net_socket_udp_init(NetClientState *peer,
closesocket(fd);
return -1;
}
+ qemu_set_nonblock(fd);
s = net_socket_fd_init(peer, model, name, fd, 0);
if (!s) {
@@ -711,7 +713,11 @@ int net_init_socket(const NetClientOptions *opts, const char *name,
int fd;
fd = monitor_handle_fd_param(cur_mon, sock->fd);
- if (fd == -1 || !net_socket_fd_init(peer, "socket", name, fd, 1)) {
+ if (fd == -1) {
+ return -1;
+ }
+ qemu_set_nonblock(fd);
+ if (!net_socket_fd_init(peer, "socket", name, fd, 1)) {
return -1;
}
return 0;
diff --git a/net/tap-aix.c b/net/tap-aix.c
index f27c17729..804d16448 100644
--- a/net/tap-aix.c
+++ b/net/tap-aix.c
@@ -22,10 +22,11 @@
* THE SOFTWARE.
*/
-#include "net/tap.h"
+#include "tap_int.h"
#include <stdio.h>
-int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required)
+int tap_open(char *ifname, int ifname_size, int *vnet_hdr,
+ int vnet_hdr_required, int mq_required)
{
fprintf(stderr, "no tap on AIX\n");
return -1;
@@ -59,3 +60,19 @@ void tap_fd_set_offload(int fd, int csum, int tso4,
int tso6, int ecn, int ufo)
{
}
+
+int tap_fd_enable(int fd)
+{
+ return -1;
+}
+
+int tap_fd_disable(int fd)
+{
+ return -1;
+}
+
+int tap_fd_get_ifname(int fd, char *ifname)
+{
+ return -1;
+}
+
diff --git a/net/tap-bsd.c b/net/tap-bsd.c
index a3b717dd1..f61d58096 100644
--- a/net/tap-bsd.c
+++ b/net/tap-bsd.c
@@ -22,10 +22,10 @@
* THE SOFTWARE.
*/
-#include "net/tap.h"
+#include "tap_int.h"
#include "qemu-common.h"
-#include "sysemu.h"
-#include "qemu-error.h"
+#include "sysemu/sysemu.h"
+#include "qemu/error-report.h"
#ifdef __NetBSD__
#include <sys/ioctl.h>
@@ -33,7 +33,8 @@
#include <net/if_tap.h>
#endif
-int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required)
+int tap_open(char *ifname, int ifname_size, int *vnet_hdr,
+ int vnet_hdr_required, int mq_required)
{
int fd;
#ifdef TAPGIFNAME
@@ -43,7 +44,8 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required
struct stat s;
#endif
-#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__)
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
+ defined(__OpenBSD__) || defined(__APPLE__)
/* if no ifname is given, always start the search from tap0/tun0. */
int i;
char dname[100];
@@ -145,3 +147,18 @@ void tap_fd_set_offload(int fd, int csum, int tso4,
int tso6, int ecn, int ufo)
{
}
+
+int tap_fd_enable(int fd)
+{
+ return -1;
+}
+
+int tap_fd_disable(int fd)
+{
+ return -1;
+}
+
+int tap_fd_get_ifname(int fd, char *ifname)
+{
+ return -1;
+}
diff --git a/net/tap-haiku.c b/net/tap-haiku.c
index 34739d156..e5ce436d2 100644
--- a/net/tap-haiku.c
+++ b/net/tap-haiku.c
@@ -22,10 +22,11 @@
* THE SOFTWARE.
*/
-#include "net/tap.h"
+#include "tap_int.h"
#include <stdio.h>
-int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required)
+int tap_open(char *ifname, int ifname_size, int *vnet_hdr,
+ int vnet_hdr_required, int mq_required)
{
fprintf(stderr, "no tap on Haiku\n");
return -1;
@@ -59,3 +60,18 @@ void tap_fd_set_offload(int fd, int csum, int tso4,
int tso6, int ecn, int ufo)
{
}
+
+int tap_fd_enable(int fd)
+{
+ return -1;
+}
+
+int tap_fd_disable(int fd)
+{
+ return -1;
+}
+
+int tap_fd_get_ifname(int fd, char *ifname)
+{
+ return -1;
+}
diff --git a/net/tap-linux.c b/net/tap-linux.c
index c6521bec3..36c09e24d 100644
--- a/net/tap-linux.c
+++ b/net/tap-linux.c
@@ -23,22 +23,26 @@
* THE SOFTWARE.
*/
+#include "tap_int.h"
+#include "tap-linux.h"
#include "net/tap.h"
-#include "net/tap-linux.h"
#include <net/if.h>
#include <sys/ioctl.h>
-#include "sysemu.h"
+#include "sysemu/sysemu.h"
#include "qemu-common.h"
-#include "qemu-error.h"
+#include "qemu/error-report.h"
#define PATH_NET_TUN "/dev/net/tun"
-int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required)
+int tap_open(char *ifname, int ifname_size, int *vnet_hdr,
+ int vnet_hdr_required, int mq_required)
{
struct ifreq ifr;
int fd, ret;
+ int len = sizeof(struct virtio_net_hdr);
+ unsigned int features;
TFR(fd = open(PATH_NET_TUN, O_RDWR));
if (fd < 0) {
@@ -48,9 +52,12 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
- if (*vnet_hdr) {
- unsigned int features;
+ if (ioctl(fd, TUNGETFEATURES, &features) == 0 &&
+ features & IFF_ONE_QUEUE) {
+ ifr.ifr_flags |= IFF_ONE_QUEUE;
+ }
+ if (*vnet_hdr) {
if (ioctl(fd, TUNGETFEATURES, &features) == 0 &&
features & IFF_VNET_HDR) {
*vnet_hdr = 1;
@@ -65,6 +72,25 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required
close(fd);
return -1;
}
+ /*
+ * Make sure vnet header size has the default value: for a persistent
+ * tap it might have been modified e.g. by another instance of qemu.
+ * Ignore errors since old kernels do not support this ioctl: in this
+ * case the header size implicitly has the correct value.
+ */
+ ioctl(fd, TUNSETVNETHDRSZ, &len);
+ }
+
+ if (mq_required) {
+ if ((ioctl(fd, TUNGETFEATURES, &features) != 0) ||
+ !(features & IFF_MULTI_QUEUE)) {
+ error_report("multiqueue required, but no kernel "
+ "support for IFF_MULTI_QUEUE available");
+ close(fd);
+ return -1;
+ } else {
+ ifr.ifr_flags |= IFF_MULTI_QUEUE;
+ }
}
if (ifname[0] != '\0')
@@ -155,7 +181,7 @@ int tap_probe_vnet_hdr_len(int fd, int len)
if (ioctl(fd, TUNSETVNETHDRSZ, &orig) == -1) {
fprintf(stderr, "TUNGETVNETHDRSZ ioctl() failed: %s. Exiting.\n",
strerror(errno));
- assert(0);
+ abort();
return -errno;
}
return 1;
@@ -166,7 +192,7 @@ void tap_fd_set_vnet_hdr_len(int fd, int len)
if (ioctl(fd, TUNSETVNETHDRSZ, &len) == -1) {
fprintf(stderr, "TUNSETVNETHDRSZ ioctl() failed: %s. Exiting.\n",
strerror(errno));
- assert(0);
+ abort();
}
}
@@ -200,3 +226,53 @@ void tap_fd_set_offload(int fd, int csum, int tso4,
}
}
}
+
+/* Enable a specific queue of tap. */
+int tap_fd_enable(int fd)
+{
+ struct ifreq ifr;
+ int ret;
+
+ memset(&ifr, 0, sizeof(ifr));
+
+ ifr.ifr_flags = IFF_ATTACH_QUEUE;
+ ret = ioctl(fd, TUNSETQUEUE, (void *) &ifr);
+
+ if (ret != 0) {
+ error_report("could not enable queue");
+ }
+
+ return ret;
+}
+
+/* Disable a specific queue of tap/ */
+int tap_fd_disable(int fd)
+{
+ struct ifreq ifr;
+ int ret;
+
+ memset(&ifr, 0, sizeof(ifr));
+
+ ifr.ifr_flags = IFF_DETACH_QUEUE;
+ ret = ioctl(fd, TUNSETQUEUE, (void *) &ifr);
+
+ if (ret != 0) {
+ error_report("could not disable queue");
+ }
+
+ return ret;
+}
+
+int tap_fd_get_ifname(int fd, char *ifname)
+{
+ struct ifreq ifr;
+
+ if (ioctl(fd, TUNGETIFF, &ifr) != 0) {
+ error_report("TUNGETIFF ioctl() failed: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ pstrcpy(ifname, sizeof(ifr.ifr_name), ifr.ifr_name);
+ return 0;
+}
diff --git a/net/tap-linux.h b/net/tap-linux.h
index 659e98122..1cf35d41b 100644
--- a/net/tap-linux.h
+++ b/net/tap-linux.h
@@ -13,8 +13,8 @@
* GNU General Public License for more details.
*/
-#ifndef QEMU_TAP_H
-#define QEMU_TAP_H
+#ifndef QEMU_TAP_LINUX_H
+#define QEMU_TAP_LINUX_H
#include <stdint.h>
#ifdef __linux__
@@ -29,13 +29,18 @@
#define TUNSETSNDBUF _IOW('T', 212, int)
#define TUNGETVNETHDRSZ _IOR('T', 215, int)
#define TUNSETVNETHDRSZ _IOW('T', 216, int)
+#define TUNSETQUEUE _IOW('T', 217, int)
#endif
/* TUNSETIFF ifr flags */
-#define IFF_TAP 0x0002
-#define IFF_NO_PI 0x1000
-#define IFF_VNET_HDR 0x4000
+#define IFF_TAP 0x0002
+#define IFF_NO_PI 0x1000
+#define IFF_ONE_QUEUE 0x2000
+#define IFF_VNET_HDR 0x4000
+#define IFF_MULTI_QUEUE 0x0100
+#define IFF_ATTACH_QUEUE 0x0200
+#define IFF_DETACH_QUEUE 0x0400
/* Features for GSO (TUNSETOFFLOAD). */
#define TUN_F_CSUM 0x01 /* You can hand me unchecksummed packets. */
@@ -44,20 +49,4 @@
#define TUN_F_TSO_ECN 0x08 /* I can handle TSO with ECN bits. */
#define TUN_F_UFO 0x10 /* I can handle UFO packets */
-struct virtio_net_hdr
-{
- uint8_t flags;
- uint8_t gso_type;
- uint16_t hdr_len;
- uint16_t gso_size;
- uint16_t csum_start;
- uint16_t csum_offset;
-};
-
-struct virtio_net_hdr_mrg_rxbuf
-{
- struct virtio_net_hdr hdr;
- uint16_t num_buffers; /* Number of merged rx buffers */
-};
-
#endif /* QEMU_TAP_H */
diff --git a/net/tap-solaris.c b/net/tap-solaris.c
index 5d6ac42f2..9c7278f1b 100644
--- a/net/tap-solaris.c
+++ b/net/tap-solaris.c
@@ -22,8 +22,8 @@
* THE SOFTWARE.
*/
-#include "net/tap.h"
-#include "sysemu.h"
+#include "tap_int.h"
+#include "sysemu/sysemu.h"
#include <sys/stat.h>
#include <sys/ethernet.h>
@@ -38,7 +38,7 @@
#include <net/if.h>
#include <syslog.h>
#include <stropts.h>
-#include "qemu-error.h"
+#include "qemu/error-report.h"
ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen)
{
@@ -173,7 +173,8 @@ static int tap_alloc(char *dev, size_t dev_size)
return tap_fd;
}
-int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required)
+int tap_open(char *ifname, int ifname_size, int *vnet_hdr,
+ int vnet_hdr_required, int mq_required)
{
char dev[10]="";
int fd;
@@ -225,3 +226,18 @@ void tap_fd_set_offload(int fd, int csum, int tso4,
int tso6, int ecn, int ufo)
{
}
+
+int tap_fd_enable(int fd)
+{
+ return -1;
+}
+
+int tap_fd_disable(int fd)
+{
+ return -1;
+}
+
+int tap_fd_get_ifname(int fd, char *ifname)
+{
+ return -1;
+}
diff --git a/net/tap-win32.c b/net/tap-win32.c
index 8d2d32b1c..91e9e844a 100644
--- a/net/tap-win32.c
+++ b/net/tap-win32.c
@@ -26,13 +26,14 @@
* distribution); if not, see <http://www.gnu.org/licenses/>.
*/
-#include "tap.h"
+#include "tap_int.h"
#include "qemu-common.h"
#include "clients.h" /* net_init_tap */
-#include "net.h"
-#include "sysemu.h"
-#include "qemu-error.h"
+#include "net/net.h"
+#include "net/tap.h" /* tap_has_ufo, ... */
+#include "sysemu/sysemu.h"
+#include "qemu/error-report.h"
#include <stdio.h>
#include <windows.h>
#include <winioctl.h>
@@ -565,7 +566,7 @@ static void tap_win32_free_buffer(tap_win32_overlapped_t *overlapped,
}
static int tap_win32_open(tap_win32_overlapped_t **phandle,
- const char *prefered_name)
+ const char *preferred_name)
{
char device_path[256];
char device_guid[0x100];
@@ -581,8 +582,9 @@ static int tap_win32_open(tap_win32_overlapped_t **phandle,
DWORD version_len;
DWORD idThread;
- if (prefered_name != NULL)
- snprintf(name_buffer, sizeof(name_buffer), "%s", prefered_name);
+ if (preferred_name != NULL) {
+ snprintf(name_buffer, sizeof(name_buffer), "%s", preferred_name);
+ }
rc = get_device_guid(device_guid, sizeof(device_guid), name_buffer, sizeof(name_buffer));
if (rc)
@@ -720,9 +722,9 @@ int net_init_tap(const NetClientOptions *opts, const char *name,
return 0;
}
-int tap_has_ufo(NetClientState *nc)
+bool tap_has_ufo(NetClientState *nc)
{
- return 0;
+ return false;
}
int tap_has_vnet_hdr(NetClientState *nc)
@@ -739,7 +741,7 @@ void tap_fd_set_vnet_hdr_len(int fd, int len)
{
}
-void tap_using_vnet_hdr(NetClientState *nc, int using_vnet_hdr)
+void tap_using_vnet_hdr(NetClientState *nc, bool using_vnet_hdr)
{
}
@@ -760,5 +762,15 @@ int tap_has_vnet_hdr_len(NetClientState *nc, int len)
void tap_set_vnet_hdr_len(NetClientState *nc, int len)
{
- assert(0);
+ abort();
+}
+
+int tap_enable(NetClientState *nc)
+{
+ abort();
+}
+
+int tap_disable(NetClientState *nc)
+{
+ abort();
}
diff --git a/net/tap.c b/net/tap.c
index 1abfd44bd..39c1cda3e 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -23,7 +23,7 @@
* THE SOFTWARE.
*/
-#include "tap.h"
+#include "tap_int.h"
#include "config-host.h"
@@ -33,33 +33,28 @@
#include <sys/socket.h>
#include <net/if.h>
-#include "net.h"
+#include "net/net.h"
#include "clients.h"
-#include "monitor.h"
-#include "sysemu.h"
-#include "qemu-char.h"
+#include "monitor/monitor.h"
+#include "sysemu/sysemu.h"
#include "qemu-common.h"
-#include "qemu-error.h"
+#include "qemu/error-report.h"
-#include "net/tap-linux.h"
+#include "net/tap.h"
-#include "hw/vhost_net.h"
-
-/* Maximum GSO packet size (64k) plus plenty of room for
- * the ethernet and virtio_net headers
- */
-#define TAP_BUFSIZE (4096 + 65536)
+#include "net/vhost_net.h"
typedef struct TAPState {
NetClientState nc;
int fd;
char down_script[1024];
char down_script_arg[128];
- uint8_t buf[TAP_BUFSIZE];
- unsigned int read_poll : 1;
- unsigned int write_poll : 1;
- unsigned int using_vnet_hdr : 1;
- unsigned int has_ufo: 1;
+ uint8_t buf[NET_BUFSIZE];
+ bool read_poll;
+ bool write_poll;
+ bool using_vnet_hdr;
+ bool has_ufo;
+ bool enabled;
VHostNetState *vhost_net;
unsigned host_vnet_hdr_len;
} TAPState;
@@ -73,21 +68,21 @@ static void tap_writable(void *opaque);
static void tap_update_fd_handler(TAPState *s)
{
qemu_set_fd_handler2(s->fd,
- s->read_poll ? tap_can_send : NULL,
- s->read_poll ? tap_send : NULL,
- s->write_poll ? tap_writable : NULL,
+ s->read_poll && s->enabled ? tap_can_send : NULL,
+ s->read_poll && s->enabled ? tap_send : NULL,
+ s->write_poll && s->enabled ? tap_writable : NULL,
s);
}
-static void tap_read_poll(TAPState *s, int enable)
+static void tap_read_poll(TAPState *s, bool enable)
{
- s->read_poll = !!enable;
+ s->read_poll = enable;
tap_update_fd_handler(s);
}
-static void tap_write_poll(TAPState *s, int enable)
+static void tap_write_poll(TAPState *s, bool enable)
{
- s->write_poll = !!enable;
+ s->write_poll = enable;
tap_update_fd_handler(s);
}
@@ -95,7 +90,7 @@ static void tap_writable(void *opaque)
{
TAPState *s = opaque;
- tap_write_poll(s, 0);
+ tap_write_poll(s, false);
qemu_flush_queued_packets(&s->nc);
}
@@ -109,7 +104,7 @@ static ssize_t tap_write_packet(TAPState *s, const struct iovec *iov, int iovcnt
} while (len == -1 && errno == EINTR);
if (len == -1 && errno == EAGAIN) {
- tap_write_poll(s, 1);
+ tap_write_poll(s, true);
return 0;
}
@@ -187,7 +182,7 @@ ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen)
static void tap_send_completed(NetClientState *nc, ssize_t len)
{
TAPState *s = DO_UPCAST(TAPState, nc, nc);
- tap_read_poll(s, 1);
+ tap_read_poll(s, true);
}
static void tap_send(void *opaque)
@@ -210,12 +205,12 @@ static void tap_send(void *opaque)
size = qemu_send_packet_async(&s->nc, buf, size, tap_send_completed);
if (size == 0) {
- tap_read_poll(s, 0);
+ tap_read_poll(s, false);
}
} while (size > 0 && qemu_can_send_packet(&s->nc));
}
-int tap_has_ufo(NetClientState *nc)
+bool tap_has_ufo(NetClientState *nc)
{
TAPState *s = DO_UPCAST(TAPState, nc, nc);
@@ -254,12 +249,10 @@ void tap_set_vnet_hdr_len(NetClientState *nc, int len)
s->host_vnet_hdr_len = len;
}
-void tap_using_vnet_hdr(NetClientState *nc, int using_vnet_hdr)
+void tap_using_vnet_hdr(NetClientState *nc, bool using_vnet_hdr)
{
TAPState *s = DO_UPCAST(TAPState, nc, nc);
- using_vnet_hdr = using_vnet_hdr != 0;
-
assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
assert(!!s->host_vnet_hdr_len == using_vnet_hdr);
@@ -291,8 +284,8 @@ static void tap_cleanup(NetClientState *nc)
if (s->down_script[0])
launch_script(s->down_script, s->down_script_arg, s->fd);
- tap_read_poll(s, 0);
- tap_write_poll(s, 0);
+ tap_read_poll(s, false);
+ tap_write_poll(s, false);
close(s->fd);
s->fd = -1;
}
@@ -338,8 +331,9 @@ static TAPState *net_tap_fd_init(NetClientState *peer,
s->fd = fd;
s->host_vnet_hdr_len = vnet_hdr ? sizeof(struct virtio_net_hdr) : 0;
- s->using_vnet_hdr = 0;
+ s->using_vnet_hdr = false;
s->has_ufo = tap_probe_has_ufo(s->fd);
+ s->enabled = true;
tap_set_offload(&s->nc, 0, 0, 0, 0, 0);
/*
* Make sure host header length is set correctly in tap:
@@ -348,7 +342,7 @@ static TAPState *net_tap_fd_init(NetClientState *peer,
if (tap_probe_vnet_hdr_len(s->fd, s->host_vnet_hdr_len)) {
tap_fd_set_vnet_hdr_len(s->fd, s->host_vnet_hdr_len);
}
- tap_read_poll(s, 1);
+ tap_read_poll(s, true);
s->vhost_net = NULL;
return s;
}
@@ -559,17 +553,10 @@ int net_init_bridge(const NetClientOptions *opts, const char *name,
static int net_tap_init(const NetdevTapOptions *tap, int *vnet_hdr,
const char *setup_script, char *ifname,
- size_t ifname_sz)
+ size_t ifname_sz, int mq_required)
{
int fd, vnet_hdr_required;
- if (tap->has_ifname) {
- pstrcpy(ifname, ifname_sz, tap->ifname);
- } else {
- assert(ifname_sz > 0);
- ifname[0] = '\0';
- }
-
if (tap->has_vnet_hdr) {
*vnet_hdr = tap->vnet_hdr;
vnet_hdr_required = *vnet_hdr;
@@ -578,7 +565,8 @@ static int net_tap_init(const NetdevTapOptions *tap, int *vnet_hdr,
vnet_hdr_required = 0;
}
- TFR(fd = tap_open(ifname, ifname_sz, vnet_hdr, vnet_hdr_required));
+ TFR(fd = tap_open(ifname, ifname_sz, vnet_hdr, vnet_hdr_required,
+ mq_required));
if (fd < 0) {
return -1;
}
@@ -594,27 +582,126 @@ static int net_tap_init(const NetdevTapOptions *tap, int *vnet_hdr,
return fd;
}
+#define MAX_TAP_QUEUES 1024
+
+static int net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer,
+ const char *model, const char *name,
+ const char *ifname, const char *script,
+ const char *downscript, const char *vhostfdname,
+ int vnet_hdr, int fd)
+{
+ TAPState *s;
+
+ s = net_tap_fd_init(peer, model, name, fd, vnet_hdr);
+ if (!s) {
+ close(fd);
+ return -1;
+ }
+
+ if (tap_set_sndbuf(s->fd, tap) < 0) {
+ return -1;
+ }
+
+ if (tap->has_fd || tap->has_fds) {
+ snprintf(s->nc.info_str, sizeof(s->nc.info_str), "fd=%d", fd);
+ } else if (tap->has_helper) {
+ snprintf(s->nc.info_str, sizeof(s->nc.info_str), "helper=%s",
+ tap->helper);
+ } else {
+ snprintf(s->nc.info_str, sizeof(s->nc.info_str),
+ "ifname=%s,script=%s,downscript=%s", ifname, script,
+ downscript);
+
+ if (strcmp(downscript, "no") != 0) {
+ snprintf(s->down_script, sizeof(s->down_script), "%s", downscript);
+ snprintf(s->down_script_arg, sizeof(s->down_script_arg),
+ "%s", ifname);
+ }
+ }
+
+ if (tap->has_vhost ? tap->vhost :
+ vhostfdname || (tap->has_vhostforce && tap->vhostforce)) {
+ int vhostfd;
+
+ if (tap->has_vhostfd || tap->has_vhostfds) {
+ vhostfd = monitor_handle_fd_param(cur_mon, vhostfdname);
+ if (vhostfd == -1) {
+ return -1;
+ }
+ } else {
+ vhostfd = -1;
+ }
+
+ s->vhost_net = vhost_net_init(&s->nc, vhostfd,
+ tap->has_vhostforce && tap->vhostforce);
+ if (!s->vhost_net) {
+ error_report("vhost-net requested but could not be initialized");
+ return -1;
+ }
+ } else if (tap->has_vhostfd || tap->has_vhostfds) {
+ error_report("vhostfd= is not valid without vhost");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int get_fds(char *str, char *fds[], int max)
+{
+ char *ptr = str, *this;
+ size_t len = strlen(str);
+ int i = 0;
+
+ while (i < max && ptr < str + len) {
+ this = strchr(ptr, ':');
+
+ if (this == NULL) {
+ fds[i] = g_strdup(ptr);
+ } else {
+ fds[i] = g_strndup(ptr, this - ptr);
+ }
+
+ i++;
+ if (this == NULL) {
+ break;
+ } else {
+ ptr = this + 1;
+ }
+ }
+
+ return i;
+}
+
int net_init_tap(const NetClientOptions *opts, const char *name,
NetClientState *peer)
{
const NetdevTapOptions *tap;
-
- int fd, vnet_hdr = 0;
- const char *model;
- TAPState *s;
-
+ int fd, vnet_hdr = 0, i = 0, queues;
/* for the no-fd, no-helper case */
const char *script = NULL; /* suppress wrong "uninit'd use" gcc warning */
+ const char *downscript = NULL;
+ const char *vhostfdname;
char ifname[128];
assert(opts->kind == NET_CLIENT_OPTIONS_KIND_TAP);
tap = opts->tap;
+ queues = tap->has_queues ? tap->queues : 1;
+ vhostfdname = tap->has_vhostfd ? tap->vhostfd : NULL;
+
+ /* QEMU vlans does not support multiqueue tap, in this case peer is set.
+ * For -netdev, peer is always NULL. */
+ if (peer && (tap->has_queues || tap->has_fds || tap->has_vhostfds)) {
+ error_report("Multiqueue tap cannot be used with QEMU vlans");
+ return -1;
+ }
if (tap->has_fd) {
if (tap->has_ifname || tap->has_script || tap->has_downscript ||
- tap->has_vnet_hdr || tap->has_helper) {
+ tap->has_vnet_hdr || tap->has_helper || tap->has_queues ||
+ tap->has_fds || tap->has_vhostfds) {
error_report("ifname=, script=, downscript=, vnet_hdr=, "
- "and helper= are invalid with fd=");
+ "helper=, queues=, fds=, and vhostfds= "
+ "are invalid with fd=");
return -1;
}
@@ -627,13 +714,62 @@ int net_init_tap(const NetClientOptions *opts, const char *name,
vnet_hdr = tap_probe_vnet_hdr(fd);
- model = "tap";
+ if (net_init_tap_one(tap, peer, "tap", name, NULL,
+ script, downscript,
+ vhostfdname, vnet_hdr, fd)) {
+ return -1;
+ }
+ } else if (tap->has_fds) {
+ char *fds[MAX_TAP_QUEUES];
+ char *vhost_fds[MAX_TAP_QUEUES];
+ int nfds, nvhosts;
+
+ if (tap->has_ifname || tap->has_script || tap->has_downscript ||
+ tap->has_vnet_hdr || tap->has_helper || tap->has_queues ||
+ tap->has_vhostfd) {
+ error_report("ifname=, script=, downscript=, vnet_hdr=, "
+ "helper=, queues=, and vhostfd= "
+ "are invalid with fds=");
+ return -1;
+ }
+
+ nfds = get_fds(tap->fds, fds, MAX_TAP_QUEUES);
+ if (tap->has_vhostfds) {
+ nvhosts = get_fds(tap->vhostfds, vhost_fds, MAX_TAP_QUEUES);
+ if (nfds != nvhosts) {
+ error_report("The number of fds passed does not match the "
+ "number of vhostfds passed");
+ return -1;
+ }
+ }
+
+ for (i = 0; i < nfds; i++) {
+ fd = monitor_handle_fd_param(cur_mon, fds[i]);
+ if (fd == -1) {
+ return -1;
+ }
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+
+ if (i == 0) {
+ vnet_hdr = tap_probe_vnet_hdr(fd);
+ } else if (vnet_hdr != tap_probe_vnet_hdr(fd)) {
+ error_report("vnet_hdr not consistent across given tap fds");
+ return -1;
+ }
+
+ if (net_init_tap_one(tap, peer, "tap", name, ifname,
+ script, downscript,
+ tap->has_vhostfds ? vhost_fds[i] : NULL,
+ vnet_hdr, fd)) {
+ return -1;
+ }
+ }
} else if (tap->has_helper) {
if (tap->has_ifname || tap->has_script || tap->has_downscript ||
- tap->has_vnet_hdr) {
+ tap->has_vnet_hdr || tap->has_queues || tap->has_vhostfds) {
error_report("ifname=, script=, downscript=, and vnet_hdr= "
- "are invalid with helper=");
+ "queues=, and vhostfds= are invalid with helper=");
return -1;
}
@@ -643,74 +779,49 @@ int net_init_tap(const NetClientOptions *opts, const char *name,
}
fcntl(fd, F_SETFL, O_NONBLOCK);
-
vnet_hdr = tap_probe_vnet_hdr(fd);
- model = "bridge";
-
- } else {
- script = tap->has_script ? tap->script : DEFAULT_NETWORK_SCRIPT;
- fd = net_tap_init(tap, &vnet_hdr, script, ifname, sizeof ifname);
- if (fd == -1) {
+ if (net_init_tap_one(tap, peer, "bridge", name, ifname,
+ script, downscript, vhostfdname,
+ vnet_hdr, fd)) {
return -1;
}
-
- model = "tap";
- }
-
- s = net_tap_fd_init(peer, model, name, fd, vnet_hdr);
- if (!s) {
- close(fd);
- return -1;
- }
-
- if (tap_set_sndbuf(s->fd, tap) < 0) {
- return -1;
- }
-
- if (tap->has_fd) {
- snprintf(s->nc.info_str, sizeof(s->nc.info_str), "fd=%d", fd);
- } else if (tap->has_helper) {
- snprintf(s->nc.info_str, sizeof(s->nc.info_str), "helper=%s",
- tap->helper);
} else {
- const char *downscript;
-
+ if (tap->has_vhostfds) {
+ error_report("vhostfds= is invalid if fds= wasn't specified");
+ return -1;
+ }
+ script = tap->has_script ? tap->script : DEFAULT_NETWORK_SCRIPT;
downscript = tap->has_downscript ? tap->downscript :
- DEFAULT_NETWORK_DOWN_SCRIPT;
-
- snprintf(s->nc.info_str, sizeof(s->nc.info_str),
- "ifname=%s,script=%s,downscript=%s", ifname, script,
- downscript);
+ DEFAULT_NETWORK_DOWN_SCRIPT;
- if (strcmp(downscript, "no") != 0) {
- snprintf(s->down_script, sizeof(s->down_script), "%s", downscript);
- snprintf(s->down_script_arg, sizeof(s->down_script_arg), "%s", ifname);
+ if (tap->has_ifname) {
+ pstrcpy(ifname, sizeof ifname, tap->ifname);
+ } else {
+ ifname[0] = '\0';
}
- }
- if (tap->has_vhost ? tap->vhost :
- tap->has_vhostfd || (tap->has_vhostforce && tap->vhostforce)) {
- int vhostfd;
-
- if (tap->has_vhostfd) {
- vhostfd = monitor_handle_fd_param(cur_mon, tap->vhostfd);
- if (vhostfd == -1) {
+ for (i = 0; i < queues; i++) {
+ fd = net_tap_init(tap, &vnet_hdr, i >= 1 ? "no" : script,
+ ifname, sizeof ifname, queues > 1);
+ if (fd == -1) {
return -1;
}
- } else {
- vhostfd = -1;
- }
- s->vhost_net = vhost_net_init(&s->nc, vhostfd,
- tap->has_vhostforce && tap->vhostforce);
- if (!s->vhost_net) {
- error_report("vhost-net requested but could not be initialized");
- return -1;
+ if (queues > 1 && i == 0 && !tap->has_ifname) {
+ if (tap_fd_get_ifname(fd, ifname)) {
+ error_report("Fail to get ifname");
+ return -1;
+ }
+ }
+
+ if (net_init_tap_one(tap, peer, "tap", name, ifname,
+ i >= 1 ? "no" : script,
+ i >= 1 ? "no" : downscript,
+ vhostfdname, vnet_hdr, fd)) {
+ return -1;
+ }
}
- } else if (tap->has_vhostfd) {
- error_report("vhostfd= is not valid without vhost");
- return -1;
}
return 0;
@@ -722,3 +833,38 @@ VHostNetState *tap_get_vhost_net(NetClientState *nc)
assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
return s->vhost_net;
}
+
+int tap_enable(NetClientState *nc)
+{
+ TAPState *s = DO_UPCAST(TAPState, nc, nc);
+ int ret;
+
+ if (s->enabled) {
+ return 0;
+ } else {
+ ret = tap_fd_enable(s->fd);
+ if (ret == 0) {
+ s->enabled = true;
+ tap_update_fd_handler(s);
+ }
+ return ret;
+ }
+}
+
+int tap_disable(NetClientState *nc)
+{
+ TAPState *s = DO_UPCAST(TAPState, nc, nc);
+ int ret;
+
+ if (s->enabled == 0) {
+ return 0;
+ } else {
+ ret = tap_fd_disable(s->fd);
+ if (ret == 0) {
+ qemu_purge_queued_packets(nc);
+ s->enabled = false;
+ tap_update_fd_handler(s);
+ }
+ return ret;
+ }
+}
diff --git a/net/tap.h b/net/tap_int.h
index d44d83ae7..86bb224bc 100644
--- a/net/tap.h
+++ b/net/tap_int.h
@@ -23,8 +23,8 @@
* THE SOFTWARE.
*/
-#ifndef QEMU_NET_TAP_H
-#define QEMU_NET_TAP_H
+#ifndef QEMU_TAP_H
+#define QEMU_TAP_H
#include "qemu-common.h"
#include "qapi-types.h"
@@ -32,27 +32,19 @@
#define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup"
#define DEFAULT_NETWORK_DOWN_SCRIPT "/etc/qemu-ifdown"
-int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required);
+int tap_open(char *ifname, int ifname_size, int *vnet_hdr,
+ int vnet_hdr_required, int mq_required);
ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen);
-int tap_has_ufo(NetClientState *nc);
-int tap_has_vnet_hdr(NetClientState *nc);
-int tap_has_vnet_hdr_len(NetClientState *nc, int len);
-void tap_using_vnet_hdr(NetClientState *nc, int using_vnet_hdr);
-void tap_set_offload(NetClientState *nc, int csum, int tso4, int tso6, int ecn, int ufo);
-void tap_set_vnet_hdr_len(NetClientState *nc, int len);
-
int tap_set_sndbuf(int fd, const NetdevTapOptions *tap);
int tap_probe_vnet_hdr(int fd);
int tap_probe_vnet_hdr_len(int fd, int len);
int tap_probe_has_ufo(int fd);
void tap_fd_set_offload(int fd, int csum, int tso4, int tso6, int ecn, int ufo);
void tap_fd_set_vnet_hdr_len(int fd, int len);
+int tap_fd_enable(int fd);
+int tap_fd_disable(int fd);
+int tap_fd_get_ifname(int fd, char *ifname);
-int tap_get_fd(NetClientState *nc);
-
-struct vhost_net;
-struct vhost_net *tap_get_vhost_net(NetClientState *nc);
-
-#endif /* QEMU_NET_TAP_H */
+#endif /* QEMU_TAP_H */
diff --git a/net/util.c b/net/util.c
index 1e9afbc1a..7e9507679 100644
--- a/net/util.c
+++ b/net/util.c
@@ -22,7 +22,7 @@
* THE SOFTWARE.
*/
-#include "net/util.h"
+#include "util.h"
#include <errno.h>
#include <stdlib.h>
diff --git a/net/vde.c b/net/vde.c
index 275bda92c..2a619fbc8 100644
--- a/net/vde.c
+++ b/net/vde.c
@@ -25,11 +25,11 @@
#include <libvdeplug.h>
-#include "net.h"
+#include "net/net.h"
#include "clients.h"
-#include "qemu-char.h"
#include "qemu-common.h"
-#include "qemu-option.h"
+#include "qemu/option.h"
+#include "qemu/main-loop.h"
typedef struct VDEState {
NetClientState nc;
@@ -39,7 +39,7 @@ typedef struct VDEState {
static void vde_to_qemu(void *opaque)
{
VDEState *s = opaque;
- uint8_t buf[4096];
+ uint8_t buf[NET_BUFSIZE];
int size;
size = vde_recv(s->vde, (char *)buf, sizeof(buf), 0);