summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/Makefile.objs12
-rw-r--r--net/checksum.c85
-rw-r--r--net/checksum.h29
-rw-r--r--net/dump.c185
-rw-r--r--net/dump.h33
-rw-r--r--net/hub.c339
-rw-r--r--net/hub.h29
-rw-r--r--net/queue.c256
-rw-r--r--net/queue.h58
-rw-r--r--net/slirp.c762
-rw-r--r--net/slirp.h50
-rw-r--r--net/socket.c674
-rw-r--r--net/socket.h33
-rw-r--r--net/tap-aix.c61
-rw-r--r--net/tap-bsd.c147
-rw-r--r--net/tap-haiku.c61
-rw-r--r--net/tap-linux.c202
-rw-r--r--net/tap-linux.h63
-rw-r--r--net/tap-solaris.c227
-rw-r--r--net/tap-win32.c763
-rw-r--r--net/tap.c716
-rw-r--r--net/tap.h64
-rw-r--r--net/util.c60
-rw-r--r--net/util.h32
-rw-r--r--net/vde.c128
-rw-r--r--net/vde.h37
26 files changed, 5106 insertions, 0 deletions
diff --git a/net/Makefile.objs b/net/Makefile.objs
new file mode 100644
index 0000000000..cf04187717
--- /dev/null
+++ b/net/Makefile.objs
@@ -0,0 +1,12 @@
+common-obj-y = queue.o checksum.o util.o hub.o
+common-obj-y += socket.o
+common-obj-y += dump.o
+common-obj-$(CONFIG_POSIX) += tap.o
+common-obj-$(CONFIG_LINUX) += tap-linux.o
+common-obj-$(CONFIG_WIN32) += tap-win32.o
+common-obj-$(CONFIG_BSD) += tap-bsd.o
+common-obj-$(CONFIG_SOLARIS) += tap-solaris.o
+common-obj-$(CONFIG_AIX) += tap-aix.o
+common-obj-$(CONFIG_HAIKU) += tap-haiku.o
+common-obj-$(CONFIG_SLIRP) += slirp.o
+common-obj-$(CONFIG_VDE) += vde.o
diff --git a/net/checksum.c b/net/checksum.c
new file mode 100644
index 0000000000..9919b2e5fd
--- /dev/null
+++ b/net/checksum.c
@@ -0,0 +1,85 @@
+/*
+ * 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 or later 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/>.
+ */
+
+#include "net/checksum.h"
+
+#define PROTO_TCP 6
+#define PROTO_UDP 17
+
+uint32_t net_checksum_add(int len, uint8_t *buf)
+{
+ 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;
+ }
+ return sum;
+}
+
+uint16_t net_checksum_finish(uint32_t sum)
+{
+ while (sum>>16)
+ sum = (sum & 0xFFFF)+(sum >> 16);
+ return ~sum;
+}
+
+uint16_t net_checksum_tcpudp(uint16_t length, uint16_t proto,
+ uint8_t *addrs, uint8_t *buf)
+{
+ uint32_t sum = 0;
+
+ sum += net_checksum_add(length, buf); // payload
+ sum += net_checksum_add(8, addrs); // src + dst address
+ sum += proto + length; // protocol & length
+ return net_checksum_finish(sum);
+}
+
+void net_checksum_calculate(uint8_t *data, int length)
+{
+ int hlen, plen, proto, csum_offset;
+ uint16_t csum;
+
+ 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;
+ default:
+ return;
+ }
+
+ if (plen < csum_offset+2)
+ 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;
+}
diff --git a/net/checksum.h b/net/checksum.h
new file mode 100644
index 0000000000..1f052986e6
--- /dev/null
+++ b/net/checksum.h
@@ -0,0 +1,29 @@
+/*
+ * 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/dump.c b/net/dump.c
new file mode 100644
index 0000000000..004231d481
--- /dev/null
+++ b/net/dump.c
@@ -0,0 +1,185 @@
+/*
+ * 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 "dump.h"
+#include "qemu-common.h"
+#include "qemu-error.h"
+#include "qemu-log.h"
+#include "qemu-timer.h"
+#include "hub.h"
+
+typedef struct DumpState {
+ NetClientState nc;
+ int64_t start_ts;
+ int fd;
+ int pcap_caplen;
+} DumpState;
+
+#define PCAP_MAGIC 0xa1b2c3d4
+
+struct pcap_file_hdr {
+ uint32_t magic;
+ uint16_t version_major;
+ uint16_t version_minor;
+ int32_t thiszone;
+ uint32_t sigfigs;
+ uint32_t snaplen;
+ uint32_t linktype;
+};
+
+struct pcap_sf_pkthdr {
+ struct {
+ int32_t tv_sec;
+ int32_t tv_usec;
+ } ts;
+ uint32_t caplen;
+ uint32_t len;
+};
+
+static ssize_t dump_receive(NetClientState *nc, const uint8_t *buf, size_t size)
+{
+ DumpState *s = DO_UPCAST(DumpState, nc, nc);
+ struct pcap_sf_pkthdr hdr;
+ int64_t ts;
+ int caplen;
+
+ /* Early return in case of previous error. */
+ if (s->fd < 0) {
+ return size;
+ }
+
+ ts = muldiv64(qemu_get_clock_ns(vm_clock), 1000000, get_ticks_per_sec());
+ caplen = size > s->pcap_caplen ? s->pcap_caplen : size;
+
+ hdr.ts.tv_sec = ts / 1000000 + s->start_ts;
+ hdr.ts.tv_usec = ts % 1000000;
+ hdr.caplen = caplen;
+ hdr.len = size;
+ if (write(s->fd, &hdr, sizeof(hdr)) != sizeof(hdr) ||
+ write(s->fd, buf, caplen) != caplen) {
+ qemu_log("-net dump write error - stop dump\n");
+ close(s->fd);
+ s->fd = -1;
+ }
+
+ return size;
+}
+
+static void dump_cleanup(NetClientState *nc)
+{
+ DumpState *s = DO_UPCAST(DumpState, nc, nc);
+
+ close(s->fd);
+}
+
+static NetClientInfo net_dump_info = {
+ .type = NET_CLIENT_OPTIONS_KIND_DUMP,
+ .size = sizeof(DumpState),
+ .receive = dump_receive,
+ .cleanup = dump_cleanup,
+};
+
+static int net_dump_init(NetClientState *peer, const char *device,
+ const char *name, const char *filename, int len)
+{
+ struct pcap_file_hdr hdr;
+ NetClientState *nc;
+ DumpState *s;
+ struct tm tm;
+ int fd;
+
+ fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, 0644);
+ if (fd < 0) {
+ error_report("-net dump: can't open %s", filename);
+ return -1;
+ }
+
+ hdr.magic = PCAP_MAGIC;
+ hdr.version_major = 2;
+ hdr.version_minor = 4;
+ hdr.thiszone = 0;
+ hdr.sigfigs = 0;
+ hdr.snaplen = len;
+ hdr.linktype = 1;
+
+ if (write(fd, &hdr, sizeof(hdr)) < sizeof(hdr)) {
+ error_report("-net dump write error: %s", strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ nc = qemu_new_net_client(&net_dump_info, peer, device, name);
+
+ snprintf(nc->info_str, sizeof(nc->info_str),
+ "dump to %s (len=%d)", filename, len);
+
+ s = DO_UPCAST(DumpState, nc, nc);
+
+ s->fd = fd;
+ s->pcap_caplen = len;
+
+ qemu_get_timedate(&tm, 0);
+ s->start_ts = mktime(&tm);
+
+ return 0;
+}
+
+int net_init_dump(const NetClientOptions *opts, const char *name,
+ NetClientState *peer)
+{
+ int len;
+ const char *file;
+ char def_file[128];
+ const NetdevDumpOptions *dump;
+
+ assert(opts->kind == NET_CLIENT_OPTIONS_KIND_DUMP);
+ dump = opts->dump;
+
+ assert(peer);
+
+ if (dump->has_file) {
+ file = dump->file;
+ } else {
+ int id;
+ int ret;
+
+ ret = net_hub_id_for_client(peer, &id);
+ assert(ret == 0); /* peer must be on a hub */
+
+ snprintf(def_file, sizeof(def_file), "qemu-vlan%d.pcap", id);
+ file = def_file;
+ }
+
+ if (dump->has_len) {
+ if (dump->len > INT_MAX) {
+ error_report("invalid length: %"PRIu64, dump->len);
+ return -1;
+ }
+ len = dump->len;
+ } else {
+ len = 65536;
+ }
+
+ return net_dump_init(peer, "dump", name, file, len);
+}
diff --git a/net/dump.h b/net/dump.h
new file mode 100644
index 0000000000..33f152b460
--- /dev/null
+++ b/net/dump.h
@@ -0,0 +1,33 @@
+/*
+ * 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_DUMP_H
+#define QEMU_NET_DUMP_H
+
+#include "net.h"
+#include "qapi-types.h"
+
+int net_init_dump(const NetClientOptions *opts, const char *name,
+ NetClientState *peer);
+
+#endif /* QEMU_NET_DUMP_H */
diff --git a/net/hub.c b/net/hub.c
new file mode 100644
index 0000000000..ac157e32ee
--- /dev/null
+++ b/net/hub.c
@@ -0,0 +1,339 @@
+/*
+ * Hub net client
+ *
+ * Copyright IBM, Corp. 2012
+ *
+ * Authors:
+ * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
+ * Zhi Yong Wu <wuzhy@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "monitor.h"
+#include "net.h"
+#include "hub.h"
+#include "iov.h"
+
+/*
+ * A hub broadcasts incoming packets to all its ports except the source port.
+ * Hubs can be used to provide independent network segments, also confusingly
+ * named the QEMU 'vlan' feature.
+ */
+
+typedef struct NetHub NetHub;
+
+typedef struct NetHubPort {
+ NetClientState nc;
+ QLIST_ENTRY(NetHubPort) next;
+ NetHub *hub;
+ int id;
+} NetHubPort;
+
+struct NetHub {
+ int id;
+ QLIST_ENTRY(NetHub) next;
+ int num_ports;
+ QLIST_HEAD(, NetHubPort) ports;
+};
+
+static QLIST_HEAD(, NetHub) hubs = QLIST_HEAD_INITIALIZER(&hubs);
+
+static ssize_t net_hub_receive(NetHub *hub, NetHubPort *source_port,
+ const uint8_t *buf, size_t len)
+{
+ NetHubPort *port;
+
+ QLIST_FOREACH(port, &hub->ports, next) {
+ if (port == source_port) {
+ continue;
+ }
+
+ qemu_send_packet(&port->nc, buf, len);
+ }
+ return len;
+}
+
+static ssize_t net_hub_receive_iov(NetHub *hub, NetHubPort *source_port,
+ const struct iovec *iov, int iovcnt)
+{
+ NetHubPort *port;
+ ssize_t len = iov_size(iov, iovcnt);
+
+ QLIST_FOREACH(port, &hub->ports, next) {
+ if (port == source_port) {
+ continue;
+ }
+
+ qemu_sendv_packet(&port->nc, iov, iovcnt);
+ }
+ return len;
+}
+
+static NetHub *net_hub_new(int id)
+{
+ NetHub *hub;
+
+ hub = g_malloc(sizeof(*hub));
+ hub->id = id;
+ hub->num_ports = 0;
+ QLIST_INIT(&hub->ports);
+
+ QLIST_INSERT_HEAD(&hubs, hub, next);
+
+ return hub;
+}
+
+static int net_hub_port_can_receive(NetClientState *nc)
+{
+ NetHubPort *port;
+ NetHubPort *src_port = DO_UPCAST(NetHubPort, nc, nc);
+ NetHub *hub = src_port->hub;
+
+ QLIST_FOREACH(port, &hub->ports, next) {
+ if (port == src_port) {
+ continue;
+ }
+
+ if (!qemu_can_send_packet(&port->nc)) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static ssize_t net_hub_port_receive(NetClientState *nc,
+ const uint8_t *buf, size_t len)
+{
+ NetHubPort *port = DO_UPCAST(NetHubPort, nc, nc);
+
+ return net_hub_receive(port->hub, port, buf, len);
+}
+
+static ssize_t net_hub_port_receive_iov(NetClientState *nc,
+ const struct iovec *iov, int iovcnt)
+{
+ NetHubPort *port = DO_UPCAST(NetHubPort, nc, nc);
+
+ return net_hub_receive_iov(port->hub, port, iov, iovcnt);
+}
+
+static void net_hub_port_cleanup(NetClientState *nc)
+{
+ NetHubPort *port = DO_UPCAST(NetHubPort, nc, nc);
+
+ QLIST_REMOVE(port, next);
+}
+
+static NetClientInfo net_hub_port_info = {
+ .type = NET_CLIENT_OPTIONS_KIND_HUBPORT,
+ .size = sizeof(NetHubPort),
+ .can_receive = net_hub_port_can_receive,
+ .receive = net_hub_port_receive,
+ .receive_iov = net_hub_port_receive_iov,
+ .cleanup = net_hub_port_cleanup,
+};
+
+static NetHubPort *net_hub_port_new(NetHub *hub, const char *name)
+{
+ NetClientState *nc;
+ NetHubPort *port;
+ int id = hub->num_ports++;
+ char default_name[128];
+
+ if (!name) {
+ snprintf(default_name, sizeof(default_name),
+ "hub%dport%d", hub->id, id);
+ name = default_name;
+ }
+
+ nc = qemu_new_net_client(&net_hub_port_info, NULL, "hub", name);
+ port = DO_UPCAST(NetHubPort, nc, nc);
+ port->id = id;
+ port->hub = hub;
+
+ QLIST_INSERT_HEAD(&hub->ports, port, next);
+
+ return port;
+}
+
+/**
+ * Create a port on a given hub
+ * @name: Net client name or NULL for default name.
+ *
+ * If there is no existing hub with the given id then a new hub is created.
+ */
+NetClientState *net_hub_add_port(int hub_id, const char *name)
+{
+ NetHub *hub;
+ NetHubPort *port;
+
+ QLIST_FOREACH(hub, &hubs, next) {
+ if (hub->id == hub_id) {
+ break;
+ }
+ }
+
+ if (!hub) {
+ hub = net_hub_new(hub_id);
+ }
+
+ port = net_hub_port_new(hub, name);
+ return &port->nc;
+}
+
+/**
+ * Find a specific client on a hub
+ */
+NetClientState *net_hub_find_client_by_name(int hub_id, const char *name)
+{
+ NetHub *hub;
+ NetHubPort *port;
+ NetClientState *peer;
+
+ QLIST_FOREACH(hub, &hubs, next) {
+ if (hub->id == hub_id) {
+ QLIST_FOREACH(port, &hub->ports, next) {
+ peer = port->nc.peer;
+
+ if (peer && strcmp(peer->name, name) == 0) {
+ return peer;
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Find a available port on a hub; otherwise create one new port
+ */
+NetClientState *net_hub_port_find(int hub_id)
+{
+ NetHub *hub;
+ NetHubPort *port;
+ NetClientState *nc;
+
+ QLIST_FOREACH(hub, &hubs, next) {
+ if (hub->id == hub_id) {
+ QLIST_FOREACH(port, &hub->ports, next) {
+ nc = port->nc.peer;
+ if (!nc) {
+ return &(port->nc);
+ }
+ }
+ break;
+ }
+ }
+
+ nc = net_hub_add_port(hub_id, NULL);
+ return nc;
+}
+
+/**
+ * Print hub configuration
+ */
+void net_hub_info(Monitor *mon)
+{
+ NetHub *hub;
+ NetHubPort *port;
+
+ QLIST_FOREACH(hub, &hubs, next) {
+ monitor_printf(mon, "hub %d\n", hub->id);
+ QLIST_FOREACH(port, &hub->ports, next) {
+ if (port->nc.peer) {
+ monitor_printf(mon, " \\ ");
+ print_net_client(mon, port->nc.peer);
+ }
+ }
+ }
+}
+
+/**
+ * Get the hub id that a client is connected to
+ *
+ * @id Pointer for hub id output, may be NULL
+ */
+int net_hub_id_for_client(NetClientState *nc, int *id)
+{
+ NetHubPort *port;
+
+ if (nc->info->type == NET_CLIENT_OPTIONS_KIND_HUBPORT) {
+ port = DO_UPCAST(NetHubPort, nc, nc);
+ } else if (nc->peer != NULL && nc->peer->info->type ==
+ NET_CLIENT_OPTIONS_KIND_HUBPORT) {
+ port = DO_UPCAST(NetHubPort, nc, nc->peer);
+ } else {
+ return -ENOENT;
+ }
+
+ if (id) {
+ *id = port->hub->id;
+ }
+ return 0;
+}
+
+int net_init_hubport(const NetClientOptions *opts, const char *name,
+ NetClientState *peer)
+{
+ const NetdevHubPortOptions *hubport;
+
+ assert(opts->kind == NET_CLIENT_OPTIONS_KIND_HUBPORT);
+ hubport = opts->hubport;
+
+ /* Treat hub port like a backend, NIC must be the one to peer */
+ if (peer) {
+ return -EINVAL;
+ }
+
+ net_hub_add_port(hubport->hubid, name);
+ return 0;
+}
+
+/**
+ * Warn if hub configurations are likely wrong
+ */
+void net_hub_check_clients(void)
+{
+ NetHub *hub;
+ NetHubPort *port;
+ NetClientState *peer;
+
+ QLIST_FOREACH(hub, &hubs, next) {
+ int has_nic = 0, has_host_dev = 0;
+
+ QLIST_FOREACH(port, &hub->ports, next) {
+ peer = port->nc.peer;
+ if (!peer) {
+ fprintf(stderr, "Warning: hub port %s has no peer\n",
+ port->nc.name);
+ continue;
+ }
+
+ switch (peer->info->type) {
+ case NET_CLIENT_OPTIONS_KIND_NIC:
+ has_nic = 1;
+ break;
+ case NET_CLIENT_OPTIONS_KIND_USER:
+ case NET_CLIENT_OPTIONS_KIND_TAP:
+ case NET_CLIENT_OPTIONS_KIND_SOCKET:
+ case NET_CLIENT_OPTIONS_KIND_VDE:
+ has_host_dev = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ if (has_host_dev && !has_nic) {
+ fprintf(stderr, "Warning: vlan %d with no nics\n", hub->id);
+ }
+ if (has_nic && !has_host_dev) {
+ fprintf(stderr,
+ "Warning: vlan %d is not connected to host network\n",
+ hub->id);
+ }
+ }
+}
diff --git a/net/hub.h b/net/hub.h
new file mode 100644
index 0000000000..26a1ade1f9
--- /dev/null
+++ b/net/hub.h
@@ -0,0 +1,29 @@
+/*
+ * Hub net client
+ *
+ * Copyright IBM, Corp. 2012
+ *
+ * Authors:
+ * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
+ * Zhi Yong Wu <wuzhy@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#ifndef NET_HUB_H
+#define NET_HUB_H
+
+#include "qemu-common.h"
+
+int net_init_hubport(const NetClientOptions *opts, const char *name,
+ NetClientState *peer);
+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);
+
+#endif /* NET_HUB_H */
diff --git a/net/queue.c b/net/queue.c
new file mode 100644
index 0000000000..e8030aafe4
--- /dev/null
+++ b/net/queue.c
@@ -0,0 +1,256 @@
+/*
+ * 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.
+ */
+
+#include "net/queue.h"
+#include "qemu-queue.h"
+#include "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
+ * to deliver packets. It must also call qemu_net_queue_purge() in its
+ * cleanup path.
+ *
+ * If a sent callback is provided to send(), the caller must handle a
+ * zero return from the delivery handler by not sending any more packets
+ * until we have invoked the callback. Only in that case will we queue
+ * the packet.
+ *
+ * If a sent callback isn't provided, we just drop the packet to avoid
+ * unbounded queueing.
+ */
+
+struct NetPacket {
+ QTAILQ_ENTRY(NetPacket) entry;
+ NetClientState *sender;
+ unsigned flags;
+ int size;
+ NetPacketSent *sent_cb;
+ uint8_t data[0];
+};
+
+struct NetQueue {
+ void *opaque;
+
+ QTAILQ_HEAD(packets, NetPacket) packets;
+
+ unsigned delivering : 1;
+};
+
+NetQueue *qemu_new_net_queue(void *opaque)
+{
+ NetQueue *queue;
+
+ queue = g_malloc0(sizeof(NetQueue));
+
+ queue->opaque = opaque;
+
+ QTAILQ_INIT(&queue->packets);
+
+ queue->delivering = 0;
+
+ return queue;
+}
+
+void qemu_del_net_queue(NetQueue *queue)
+{
+ NetPacket *packet, *next;
+
+ QTAILQ_FOREACH_SAFE(packet, &queue->packets, entry, next) {
+ QTAILQ_REMOVE(&queue->packets, packet, entry);
+ g_free(packet);
+ }
+
+ g_free(queue);
+}
+
+static ssize_t qemu_net_queue_append(NetQueue *queue,
+ NetClientState *sender,
+ unsigned flags,
+ const uint8_t *buf,
+ size_t size,
+ NetPacketSent *sent_cb)
+{
+ NetPacket *packet;
+
+ packet = g_malloc(sizeof(NetPacket) + size);
+ packet->sender = sender;
+ packet->flags = flags;
+ packet->size = size;
+ packet->sent_cb = sent_cb;
+ memcpy(packet->data, buf, size);
+
+ QTAILQ_INSERT_TAIL(&queue->packets, packet, entry);
+
+ return size;
+}
+
+static ssize_t qemu_net_queue_append_iov(NetQueue *queue,
+ NetClientState *sender,
+ unsigned flags,
+ const struct iovec *iov,
+ int iovcnt,
+ NetPacketSent *sent_cb)
+{
+ NetPacket *packet;
+ size_t max_len = 0;
+ int i;
+
+ for (i = 0; i < iovcnt; i++) {
+ max_len += iov[i].iov_len;
+ }
+
+ packet = g_malloc(sizeof(NetPacket) + max_len);
+ packet->sender = sender;
+ packet->sent_cb = sent_cb;
+ packet->flags = flags;
+ packet->size = 0;
+
+ for (i = 0; i < iovcnt; i++) {
+ size_t len = iov[i].iov_len;
+
+ memcpy(packet->data + packet->size, iov[i].iov_base, len);
+ packet->size += len;
+ }
+
+ QTAILQ_INSERT_TAIL(&queue->packets, packet, entry);
+
+ return packet->size;
+}
+
+static ssize_t qemu_net_queue_deliver(NetQueue *queue,
+ NetClientState *sender,
+ unsigned flags,
+ const uint8_t *data,
+ size_t size)
+{
+ ssize_t ret = -1;
+
+ queue->delivering = 1;
+ ret = qemu_deliver_packet(sender, flags, data, size, queue->opaque);
+ queue->delivering = 0;
+
+ return ret;
+}
+
+static ssize_t qemu_net_queue_deliver_iov(NetQueue *queue,
+ NetClientState *sender,
+ unsigned flags,
+ const struct iovec *iov,
+ int iovcnt)
+{
+ ssize_t ret = -1;
+
+ queue->delivering = 1;
+ ret = qemu_deliver_packet_iov(sender, flags, iov, iovcnt, queue->opaque);
+ queue->delivering = 0;
+
+ return ret;
+}
+
+ssize_t qemu_net_queue_send(NetQueue *queue,
+ NetClientState *sender,
+ unsigned flags,
+ const uint8_t *data,
+ size_t size,
+ NetPacketSent *sent_cb)
+{
+ ssize_t ret;
+
+ if (queue->delivering || !qemu_can_send_packet(sender)) {
+ return qemu_net_queue_append(queue, sender, flags, data, size, sent_cb);
+ }
+
+ ret = qemu_net_queue_deliver(queue, sender, flags, data, size);
+ if (ret == 0) {
+ qemu_net_queue_append(queue, sender, flags, data, size, sent_cb);
+ return 0;
+ }
+
+ qemu_net_queue_flush(queue);
+
+ return ret;
+}
+
+ssize_t qemu_net_queue_send_iov(NetQueue *queue,
+ NetClientState *sender,
+ unsigned flags,
+ const struct iovec *iov,
+ int iovcnt,
+ NetPacketSent *sent_cb)
+{
+ ssize_t ret;
+
+ if (queue->delivering || !qemu_can_send_packet(sender)) {
+ return qemu_net_queue_append_iov(queue, sender, flags,
+ iov, iovcnt, sent_cb);
+ }
+
+ ret = qemu_net_queue_deliver_iov(queue, sender, flags, iov, iovcnt);
+ if (ret == 0) {
+ qemu_net_queue_append_iov(queue, sender, flags, iov, iovcnt, sent_cb);
+ return 0;
+ }
+
+ qemu_net_queue_flush(queue);
+
+ return ret;
+}
+
+void qemu_net_queue_purge(NetQueue *queue, NetClientState *from)
+{
+ NetPacket *packet, *next;
+
+ QTAILQ_FOREACH_SAFE(packet, &queue->packets, entry, next) {
+ if (packet->sender == from) {
+ QTAILQ_REMOVE(&queue->packets, packet, entry);
+ g_free(packet);
+ }
+ }
+}
+
+void qemu_net_queue_flush(NetQueue *queue)
+{
+ while (!QTAILQ_EMPTY(&queue->packets)) {
+ NetPacket *packet;
+ int ret;
+
+ packet = QTAILQ_FIRST(&queue->packets);
+ QTAILQ_REMOVE(&queue->packets, packet, entry);
+
+ ret = qemu_net_queue_deliver(queue,
+ packet->sender,
+ packet->flags,
+ packet->data,
+ packet->size);
+ if (ret == 0) {
+ QTAILQ_INSERT_HEAD(&queue->packets, packet, entry);
+ break;
+ }
+
+ if (packet->sent_cb) {
+ packet->sent_cb(packet->sender, ret);
+ }
+
+ g_free(packet);
+ }
+}
diff --git a/net/queue.h b/net/queue.h
new file mode 100644
index 0000000000..9d44a9b3b8
--- /dev/null
+++ b/net/queue.h
@@ -0,0 +1,58 @@
+/*
+ * 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);
+void qemu_net_queue_flush(NetQueue *queue);
+
+#endif /* QEMU_NET_QUEUE_H */
diff --git a/net/slirp.c b/net/slirp.c
new file mode 100644
index 0000000000..8db66ea539
--- /dev/null
+++ b/net/slirp.c
@@ -0,0 +1,762 @@
+/*
+ * 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 "net/slirp.h"
+
+#include "config-host.h"
+
+#ifndef _WIN32
+#include <pwd.h>
+#include <sys/wait.h>
+#endif
+#include "net.h"
+#include "net/hub.h"
+#include "monitor.h"
+#include "qemu_socket.h"
+#include "slirp/libslirp.h"
+
+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;
+}
+
+/* slirp network adapter */
+
+#define SLIRP_CFG_HOSTFWD 1
+#define SLIRP_CFG_LEGACY 2
+
+struct slirp_config_str {
+ struct slirp_config_str *next;
+ int flags;
+ char str[1024];
+ int legacy_format;
+};
+
+typedef struct SlirpState {
+ NetClientState nc;
+ QTAILQ_ENTRY(SlirpState) entry;
+ Slirp *slirp;
+#ifndef _WIN32
+ char smb_dir[128];
+#endif
+} SlirpState;
+
+static struct slirp_config_str *slirp_configs;
+const char *legacy_tftp_prefix;
+const char *legacy_bootp_filename;
+static QTAILQ_HEAD(slirp_stacks, SlirpState) slirp_stacks =
+ QTAILQ_HEAD_INITIALIZER(slirp_stacks);
+
+static int slirp_hostfwd(SlirpState *s, const char *redir_str,
+ int legacy_format);
+static int slirp_guestfwd(SlirpState *s, const char *config_str,
+ int legacy_format);
+
+#ifndef _WIN32
+static const char *legacy_smb_export;
+
+static int slirp_smb(SlirpState *s, const char *exported_dir,
+ struct in_addr vserver_addr);
+static void slirp_smb_cleanup(SlirpState *s);
+#else
+static inline void slirp_smb_cleanup(SlirpState *s) { }
+#endif
+
+void slirp_output(void *opaque, const uint8_t *pkt, int pkt_len)
+{
+ SlirpState *s = opaque;
+
+ qemu_send_packet(&s->nc, pkt, pkt_len);
+}
+
+static ssize_t net_slirp_receive(NetClientState *nc, const uint8_t *buf, size_t size)
+{
+ SlirpState *s = DO_UPCAST(SlirpState, nc, nc);
+
+ slirp_input(s->slirp, buf, size);
+
+ return size;
+}
+
+static void net_slirp_cleanup(NetClientState *nc)
+{
+ SlirpState *s = DO_UPCAST(SlirpState, nc, nc);
+
+ slirp_cleanup(s->slirp);
+ slirp_smb_cleanup(s);
+ QTAILQ_REMOVE(&slirp_stacks, s, entry);
+}
+
+static NetClientInfo net_slirp_info = {
+ .type = NET_CLIENT_OPTIONS_KIND_USER,
+ .size = sizeof(SlirpState),
+ .receive = net_slirp_receive,
+ .cleanup = net_slirp_cleanup,
+};
+
+static int net_slirp_init(NetClientState *peer, const char *model,
+ const char *name, int restricted,
+ const char *vnetwork, const char *vhost,
+ const char *vhostname, const char *tftp_export,
+ const char *bootfile, const char *vdhcp_start,
+ const char *vnameserver, const char *smb_export,
+ const char *vsmbserver)
+{
+ /* default settings according to historic slirp */
+ struct in_addr net = { .s_addr = htonl(0x0a000200) }; /* 10.0.2.0 */
+ struct in_addr mask = { .s_addr = htonl(0xffffff00) }; /* 255.255.255.0 */
+ struct in_addr host = { .s_addr = htonl(0x0a000202) }; /* 10.0.2.2 */
+ struct in_addr dhcp = { .s_addr = htonl(0x0a00020f) }; /* 10.0.2.15 */
+ struct in_addr dns = { .s_addr = htonl(0x0a000203) }; /* 10.0.2.3 */
+#ifndef _WIN32
+ struct in_addr smbsrv = { .s_addr = 0 };
+#endif
+ NetClientState *nc;
+ SlirpState *s;
+ char buf[20];
+ uint32_t addr;
+ int shift;
+ char *end;
+ struct slirp_config_str *config;
+
+ if (!tftp_export) {
+ tftp_export = legacy_tftp_prefix;
+ }
+ if (!bootfile) {
+ bootfile = legacy_bootp_filename;
+ }
+
+ if (vnetwork) {
+ if (get_str_sep(buf, sizeof(buf), &vnetwork, '/') < 0) {
+ if (!inet_aton(vnetwork, &net)) {
+ return -1;
+ }
+ addr = ntohl(net.s_addr);
+ if (!(addr & 0x80000000)) {
+ mask.s_addr = htonl(0xff000000); /* class A */
+ } else if ((addr & 0xfff00000) == 0xac100000) {
+ mask.s_addr = htonl(0xfff00000); /* priv. 172.16.0.0/12 */
+ } else if ((addr & 0xc0000000) == 0x80000000) {
+ mask.s_addr = htonl(0xffff0000); /* class B */
+ } else if ((addr & 0xffff0000) == 0xc0a80000) {
+ mask.s_addr = htonl(0xffff0000); /* priv. 192.168.0.0/16 */
+ } else if ((addr & 0xffff0000) == 0xc6120000) {
+ mask.s_addr = htonl(0xfffe0000); /* tests 198.18.0.0/15 */
+ } else if ((addr & 0xe0000000) == 0xe0000000) {
+ mask.s_addr = htonl(0xffffff00); /* class C */
+ } else {
+ mask.s_addr = htonl(0xfffffff0); /* multicast/reserved */
+ }
+ } else {
+ if (!inet_aton(buf, &net)) {
+ return -1;
+ }
+ shift = strtol(vnetwork, &end, 10);
+ if (*end != '\0') {
+ if (!inet_aton(vnetwork, &mask)) {
+ return -1;
+ }
+ } else if (shift < 4 || shift > 32) {
+ return -1;
+ } else {
+ mask.s_addr = htonl(0xffffffff << (32 - shift));
+ }
+ }
+ net.s_addr &= mask.s_addr;
+ host.s_addr = net.s_addr | (htonl(0x0202) & ~mask.s_addr);
+ dhcp.s_addr = net.s_addr | (htonl(0x020f) & ~mask.s_addr);
+ dns.s_addr = net.s_addr | (htonl(0x0203) & ~mask.s_addr);
+ }
+
+ if (vhost && !inet_aton(vhost, &host)) {
+ return -1;
+ }
+ if ((host.s_addr & mask.s_addr) != net.s_addr) {
+ return -1;
+ }
+
+ if (vdhcp_start && !inet_aton(vdhcp_start, &dhcp)) {
+ return -1;
+ }
+ if ((dhcp.s_addr & mask.s_addr) != net.s_addr ||
+ dhcp.s_addr == host.s_addr || dhcp.s_addr == dns.s_addr) {
+ return -1;
+ }
+
+ if (vnameserver && !inet_aton(vnameserver, &dns)) {
+ return -1;
+ }
+ if ((dns.s_addr & mask.s_addr) != net.s_addr ||
+ dns.s_addr == host.s_addr) {
+ return -1;
+ }
+
+#ifndef _WIN32
+ if (vsmbserver && !inet_aton(vsmbserver, &smbsrv)) {
+ return -1;
+ }
+#endif
+
+ nc = qemu_new_net_client(&net_slirp_info, peer, model, name);
+
+ snprintf(nc->info_str, sizeof(nc->info_str),
+ "net=%s,restrict=%s", inet_ntoa(net),
+ restricted ? "on" : "off");
+
+ s = DO_UPCAST(SlirpState, nc, nc);
+
+ s->slirp = slirp_init(restricted, net, mask, host, vhostname,
+ tftp_export, bootfile, dhcp, dns, s);
+ QTAILQ_INSERT_TAIL(&slirp_stacks, s, entry);
+
+ for (config = slirp_configs; config; config = config->next) {
+ if (config->flags & SLIRP_CFG_HOSTFWD) {
+ if (slirp_hostfwd(s, config->str,
+ config->flags & SLIRP_CFG_LEGACY) < 0)
+ goto error;
+ } else {
+ if (slirp_guestfwd(s, config->str,
+ config->flags & SLIRP_CFG_LEGACY) < 0)
+ goto error;
+ }
+ }
+#ifndef _WIN32
+ if (!smb_export) {
+ smb_export = legacy_smb_export;
+ }
+ if (smb_export) {
+ if (slirp_smb(s, smb_export, smbsrv) < 0)
+ goto error;
+ }
+#endif
+
+ return 0;
+
+error:
+ qemu_del_net_client(nc);
+ return -1;
+}
+
+static SlirpState *slirp_lookup(Monitor *mon, const char *vlan,
+ const char *stack)
+{
+
+ if (vlan) {
+ NetClientState *nc;
+ nc = net_hub_find_client_by_name(strtol(vlan, NULL, 0), stack);
+ if (!nc) {
+ return NULL;
+ }
+ if (strcmp(nc->model, "user")) {
+ monitor_printf(mon, "invalid device specified\n");
+ return NULL;
+ }
+ return DO_UPCAST(SlirpState, nc, nc);
+ } else {
+ if (QTAILQ_EMPTY(&slirp_stacks)) {
+ monitor_printf(mon, "user mode network stack not in use\n");
+ return NULL;
+ }
+ return QTAILQ_FIRST(&slirp_stacks);
+ }
+}
+
+void net_slirp_hostfwd_remove(Monitor *mon, const QDict *qdict)
+{
+ struct in_addr host_addr = { .s_addr = INADDR_ANY };
+ int host_port;
+ char buf[256];
+ const char *src_str, *p;
+ SlirpState *s;
+ int is_udp = 0;
+ int err;
+ const char *arg1 = qdict_get_str(qdict, "arg1");
+ const char *arg2 = qdict_get_try_str(qdict, "arg2");
+ const char *arg3 = qdict_get_try_str(qdict, "arg3");
+
+ if (arg2) {
+ s = slirp_lookup(mon, arg1, arg2);
+ src_str = arg3;
+ } else {
+ s = slirp_lookup(mon, NULL, NULL);
+ src_str = arg1;
+ }
+ if (!s) {
+ return;
+ }
+
+ p = src_str;
+ if (!p || get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
+ goto fail_syntax;
+ }
+
+ if (!strcmp(buf, "tcp") || buf[0] == '\0') {
+ is_udp = 0;
+ } else if (!strcmp(buf, "udp")) {
+ is_udp = 1;
+ } else {
+ goto fail_syntax;
+ }
+
+ if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
+ goto fail_syntax;
+ }
+ if (buf[0] != '\0' && !inet_aton(buf, &host_addr)) {
+ goto fail_syntax;
+ }
+
+ host_port = atoi(p);
+
+ err = slirp_remove_hostfwd(QTAILQ_FIRST(&slirp_stacks)->slirp, is_udp,
+ host_addr, host_port);
+
+ monitor_printf(mon, "host forwarding rule for %s %s\n", src_str,
+ err ? "not found" : "removed");
+ return;
+
+ fail_syntax:
+ monitor_printf(mon, "invalid format\n");
+}
+
+static int slirp_hostfwd(SlirpState *s, const char *redir_str,
+ int legacy_format)
+{
+ struct in_addr host_addr = { .s_addr = INADDR_ANY };
+ struct in_addr guest_addr = { .s_addr = 0 };
+ int host_port, guest_port;
+ const char *p;
+ char buf[256];
+ int is_udp;
+ char *end;
+
+ p = redir_str;
+ if (!p || get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
+ goto fail_syntax;
+ }
+ if (!strcmp(buf, "tcp") || buf[0] == '\0') {
+ is_udp = 0;
+ } else if (!strcmp(buf, "udp")) {
+ is_udp = 1;
+ } else {
+ goto fail_syntax;
+ }
+
+ if (!legacy_format) {
+ if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
+ goto fail_syntax;
+ }
+ if (buf[0] != '\0' && !inet_aton(buf, &host_addr)) {
+ goto fail_syntax;
+ }
+ }
+
+ if (get_str_sep(buf, sizeof(buf), &p, legacy_format ? ':' : '-') < 0) {
+ goto fail_syntax;
+ }
+ host_port = strtol(buf, &end, 0);
+ if (*end != '\0' || host_port < 1 || host_port > 65535) {
+ goto fail_syntax;
+ }
+
+ if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
+ goto fail_syntax;
+ }
+ if (buf[0] != '\0' && !inet_aton(buf, &guest_addr)) {
+ goto fail_syntax;
+ }
+
+ guest_port = strtol(p, &end, 0);
+ if (*end != '\0' || guest_port < 1 || guest_port > 65535) {
+ goto fail_syntax;
+ }
+
+ if (slirp_add_hostfwd(s->slirp, is_udp, host_addr, host_port, guest_addr,
+ guest_port) < 0) {
+ error_report("could not set up host forwarding rule '%s'",
+ redir_str);
+ return -1;
+ }
+ return 0;
+
+ fail_syntax:
+ error_report("invalid host forwarding rule '%s'", redir_str);
+ return -1;
+}
+
+void net_slirp_hostfwd_add(Monitor *mon, const QDict *qdict)
+{
+ const char *redir_str;
+ SlirpState *s;
+ const char *arg1 = qdict_get_str(qdict, "arg1");
+ const char *arg2 = qdict_get_try_str(qdict, "arg2");
+ const char *arg3 = qdict_get_try_str(qdict, "arg3");
+
+ if (arg2) {
+ s = slirp_lookup(mon, arg1, arg2);
+ redir_str = arg3;
+ } else {
+ s = slirp_lookup(mon, NULL, NULL);
+ redir_str = arg1;
+ }
+ if (s) {
+ slirp_hostfwd(s, redir_str, 0);
+ }
+
+}
+
+int net_slirp_redir(const char *redir_str)
+{
+ struct slirp_config_str *config;
+
+ if (QTAILQ_EMPTY(&slirp_stacks)) {
+ config = g_malloc(sizeof(*config));
+ pstrcpy(config->str, sizeof(config->str), redir_str);
+ config->flags = SLIRP_CFG_HOSTFWD | SLIRP_CFG_LEGACY;
+ config->next = slirp_configs;
+ slirp_configs = config;
+ return 0;
+ }
+
+ return slirp_hostfwd(QTAILQ_FIRST(&slirp_stacks), redir_str, 1);
+}
+
+#ifndef _WIN32
+
+/* automatic user mode samba server configuration */
+static void slirp_smb_cleanup(SlirpState *s)
+{
+ char cmd[128];
+ int ret;
+
+ if (s->smb_dir[0] != '\0') {
+ snprintf(cmd, sizeof(cmd), "rm -rf %s", s->smb_dir);
+ ret = system(cmd);
+ if (ret == -1 || !WIFEXITED(ret)) {
+ error_report("'%s' failed.", cmd);
+ } else if (WEXITSTATUS(ret)) {
+ error_report("'%s' failed. Error code: %d",
+ cmd, WEXITSTATUS(ret));
+ }
+ s->smb_dir[0] = '\0';
+ }
+}
+
+static int slirp_smb(SlirpState* s, const char *exported_dir,
+ struct in_addr vserver_addr)
+{
+ static int instance;
+ char smb_conf[128];
+ char smb_cmdline[128];
+ struct passwd *passwd;
+ FILE *f;
+
+ passwd = getpwuid(geteuid());
+ if (!passwd) {
+ error_report("failed to retrieve user name");
+ return -1;
+ }
+
+ if (access(CONFIG_SMBD_COMMAND, F_OK)) {
+ error_report("could not find '%s', please install it",
+ CONFIG_SMBD_COMMAND);
+ return -1;
+ }
+
+ if (access(exported_dir, R_OK | X_OK)) {
+ error_report("error accessing shared directory '%s': %s",
+ exported_dir, strerror(errno));
+ return -1;
+ }
+
+ snprintf(s->smb_dir, sizeof(s->smb_dir), "/tmp/qemu-smb.%ld-%d",
+ (long)getpid(), instance++);
+ if (mkdir(s->smb_dir, 0700) < 0) {
+ error_report("could not create samba server dir '%s'", s->smb_dir);
+ return -1;
+ }
+ snprintf(smb_conf, sizeof(smb_conf), "%s/%s", s->smb_dir, "smb.conf");
+
+ f = fopen(smb_conf, "w");
+ if (!f) {
+ slirp_smb_cleanup(s);
+ error_report("could not create samba server configuration file '%s'",
+ smb_conf);
+ return -1;
+ }
+ fprintf(f,
+ "[global]\n"
+ "private dir=%s\n"
+ "socket address=127.0.0.1\n"
+ "pid directory=%s\n"
+ "lock directory=%s\n"
+ "state directory=%s\n"
+ "log file=%s/log.smbd\n"
+ "smb passwd file=%s/smbpasswd\n"
+ "security = share\n"
+ "[qemu]\n"
+ "path=%s\n"
+ "read only=no\n"
+ "guest ok=yes\n"
+ "force user=%s\n",
+ s->smb_dir,
+ s->smb_dir,
+ s->smb_dir,
+ s->smb_dir,
+ s->smb_dir,
+ s->smb_dir,
+ exported_dir,
+ passwd->pw_name
+ );
+ fclose(f);
+
+ snprintf(smb_cmdline, sizeof(smb_cmdline), "%s -s %s",
+ CONFIG_SMBD_COMMAND, smb_conf);
+
+ if (slirp_add_exec(s->slirp, 0, smb_cmdline, &vserver_addr, 139) < 0) {
+ slirp_smb_cleanup(s);
+ error_report("conflicting/invalid smbserver address");
+ return -1;
+ }
+ return 0;
+}
+
+/* automatic user mode samba server configuration (legacy interface) */
+int net_slirp_smb(const char *exported_dir)
+{
+ struct in_addr vserver_addr = { .s_addr = 0 };
+
+ if (legacy_smb_export) {
+ fprintf(stderr, "-smb given twice\n");
+ return -1;
+ }
+ legacy_smb_export = exported_dir;
+ if (!QTAILQ_EMPTY(&slirp_stacks)) {
+ return slirp_smb(QTAILQ_FIRST(&slirp_stacks), exported_dir,
+ vserver_addr);
+ }
+ return 0;
+}
+
+#endif /* !defined(_WIN32) */
+
+struct GuestFwd {
+ CharDriverState *hd;
+ struct in_addr server;
+ int port;
+ Slirp *slirp;
+};
+
+static int guestfwd_can_read(void *opaque)
+{
+ struct GuestFwd *fwd = opaque;
+ return slirp_socket_can_recv(fwd->slirp, fwd->server, fwd->port);
+}
+
+static void guestfwd_read(void *opaque, const uint8_t *buf, int size)
+{
+ struct GuestFwd *fwd = opaque;
+ slirp_socket_recv(fwd->slirp, fwd->server, fwd->port, buf, size);
+}
+
+static int slirp_guestfwd(SlirpState *s, const char *config_str,
+ int legacy_format)
+{
+ struct in_addr server = { .s_addr = 0 };
+ struct GuestFwd *fwd;
+ const char *p;
+ char buf[128];
+ char *end;
+ int port;
+
+ p = config_str;
+ if (legacy_format) {
+ if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
+ goto fail_syntax;
+ }
+ } else {
+ if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
+ goto fail_syntax;
+ }
+ if (strcmp(buf, "tcp") && buf[0] != '\0') {
+ goto fail_syntax;
+ }
+ if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) {
+ goto fail_syntax;
+ }
+ if (buf[0] != '\0' && !inet_aton(buf, &server)) {
+ goto fail_syntax;
+ }
+ if (get_str_sep(buf, sizeof(buf), &p, '-') < 0) {
+ goto fail_syntax;
+ }
+ }
+ port = strtol(buf, &end, 10);
+ if (*end != '\0' || port < 1 || port > 65535) {
+ goto fail_syntax;
+ }
+
+ fwd = g_malloc(sizeof(struct GuestFwd));
+ snprintf(buf, sizeof(buf), "guestfwd.tcp.%d", port);
+
+ if ((strlen(p) > 4) && !strncmp(p, "cmd:", 4)) {
+ if (slirp_add_exec(s->slirp, 0, &p[4], &server, port) < 0) {
+ error_report("conflicting/invalid host:port in guest forwarding "
+ "rule '%s'", config_str);
+ g_free(fwd);
+ return -1;
+ }
+ } else {
+ fwd->hd = qemu_chr_new(buf, p, NULL);
+ if (!fwd->hd) {
+ error_report("could not open guest forwarding device '%s'", buf);
+ g_free(fwd);
+ return -1;
+ }
+
+ if (slirp_add_exec(s->slirp, 3, fwd->hd, &server, port) < 0) {
+ error_report("conflicting/invalid host:port in guest forwarding "
+ "rule '%s'", config_str);
+ g_free(fwd);
+ return -1;
+ }
+ fwd->server = server;
+ fwd->port = port;
+ fwd->slirp = s->slirp;
+
+ qemu_chr_add_handlers(fwd->hd, guestfwd_can_read, guestfwd_read,
+ NULL, fwd);
+ }
+ return 0;
+
+ fail_syntax:
+ error_report("invalid guest forwarding rule '%s'", config_str);
+ return -1;
+}
+
+void do_info_usernet(Monitor *mon)
+{
+ SlirpState *s;
+
+ QTAILQ_FOREACH(s, &slirp_stacks, entry) {
+ int id;
+ bool got_vlan_id = net_hub_id_for_client(&s->nc, &id) == 0;
+ monitor_printf(mon, "VLAN %d (%s):\n",
+ got_vlan_id ? id : -1,
+ s->nc.name);
+ slirp_connection_info(s->slirp, mon);
+ }
+}
+
+static void
+net_init_slirp_configs(const StringList *fwd, int flags)
+{
+ while (fwd) {
+ struct slirp_config_str *config;
+
+ config = g_malloc0(sizeof(*config));
+ pstrcpy(config->str, sizeof(config->str), fwd->value->str);
+ config->flags = flags;
+ config->next = slirp_configs;
+ slirp_configs = config;
+
+ fwd = fwd->next;
+ }
+}
+
+int net_init_slirp(const NetClientOptions *opts, const char *name,
+ NetClientState *peer)
+{
+ struct slirp_config_str *config;
+ char *vnet;
+ int ret;
+ const NetdevUserOptions *user;
+
+ assert(opts->kind == NET_CLIENT_OPTIONS_KIND_USER);
+ user = opts->user;
+
+ vnet = user->has_net ? g_strdup(user->net) :
+ user->has_ip ? g_strdup_printf("%s/24", user->ip) :
+ NULL;
+
+ /* all optional fields are initialized to "all bits zero" */
+
+ net_init_slirp_configs(user->hostfwd, SLIRP_CFG_HOSTFWD);
+ net_init_slirp_configs(user->guestfwd, 0);
+
+ ret = net_slirp_init(peer, "user", name, user->q_restrict, vnet,
+ user->host, user->hostname, user->tftp,
+ user->bootfile, user->dhcpstart, user->dns, user->smb,
+ user->smbserver);
+
+ while (slirp_configs) {
+ config = slirp_configs;
+ slirp_configs = config->next;
+ g_free(config);
+ }
+
+ g_free(vnet);
+
+ return ret;
+}
+
+int net_slirp_parse_legacy(QemuOptsList *opts_list, const char *optarg, int *ret)
+{
+ if (strcmp(opts_list->name, "net") != 0 ||
+ strncmp(optarg, "channel,", strlen("channel,")) != 0) {
+ return 0;
+ }
+
+ /* handle legacy -net channel,port:chr */
+ optarg += strlen("channel,");
+
+ if (QTAILQ_EMPTY(&slirp_stacks)) {
+ struct slirp_config_str *config;
+
+ config = g_malloc(sizeof(*config));
+ pstrcpy(config->str, sizeof(config->str), optarg);
+ config->flags = SLIRP_CFG_LEGACY;
+ config->next = slirp_configs;
+ slirp_configs = config;
+ *ret = 0;
+ } else {
+ *ret = slirp_guestfwd(QTAILQ_FIRST(&slirp_stacks), optarg, 1);
+ }
+
+ return 1;
+}
+
diff --git a/net/slirp.h b/net/slirp.h
new file mode 100644
index 0000000000..5f685c4fb1
--- /dev/null
+++ b/net/slirp.h
@@ -0,0 +1,50 @@
+/*
+ * 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
+
+int net_init_slirp(const NetClientOptions *opts, const char *name,
+ NetClientState *peer);
+
+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
new file mode 100644
index 0000000000..c172c249be
--- /dev/null
+++ b/net/socket.c
@@ -0,0 +1,674 @@
+/*
+ * 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 "net/socket.h"
+
+#include "config-host.h"
+
+#include "net.h"
+#include "monitor.h"
+#include "qemu-char.h"
+#include "qemu-common.h"
+#include "qemu-error.h"
+#include "qemu-option.h"
+#include "qemu_socket.h"
+
+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;
+ uint8_t buf[4096];
+ struct sockaddr_in dgram_dst; /* contains inet host and port destination iff connectionless (SOCK_DGRAM) */
+} NetSocketState;
+
+static void net_socket_accept(void *opaque);
+
+/* XXX: we consider we can send the whole packet without blocking */
+static ssize_t net_socket_receive(NetClientState *nc, const uint8_t *buf, size_t size)
+{
+ NetSocketState *s = DO_UPCAST(NetSocketState, nc, nc);
+ uint32_t len;
+ len = htonl(size);
+
+ send_all(s->fd, (const uint8_t *)&len, sizeof(len));
+ return send_all(s->fd, buf, size);
+}
+
+static ssize_t net_socket_receive_dgram(NetClientState *nc, const uint8_t *buf, size_t size)
+{
+ NetSocketState *s = DO_UPCAST(NetSocketState, nc, nc);
+
+ return sendto(s->fd, (const void *)buf, size, 0,
+ (struct sockaddr *)&s->dgram_dst, sizeof(s->dgram_dst));
+}
+
+static void net_socket_send(void *opaque)
+{
+ NetSocketState *s = opaque;
+ int size, err;
+ unsigned l;
+ uint8_t buf1[4096];
+ const uint8_t *buf;
+
+ size = qemu_recv(s->fd, buf1, sizeof(buf1), 0);
+ if (size < 0) {
+ err = socket_error();
+ if (err != EWOULDBLOCK)
+ goto eoc;
+ } else if (size == 0) {
+ /* end of connection */
+ eoc:
+ qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
+ if (s->listen_fd != -1) {
+ qemu_set_fd_handler(s->listen_fd, net_socket_accept, NULL, s);
+ }
+ closesocket(s->fd);
+
+ s->fd = -1;
+ s->state = 0;
+ s->index = 0;
+ s->packet_len = 0;
+ 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) {
+ qemu_send_packet(&s->nc, s->buf, s->packet_len);
+ s->index = 0;
+ s->state = 0;
+ }
+ break;
+ }
+ }
+}
+
+static void net_socket_send_dgram(void *opaque)
+{
+ NetSocketState *s = opaque;
+ int size;
+
+ size = qemu_recv(s->fd, s->buf, sizeof(s->buf), 0);
+ if (size < 0)
+ return;
+ if (size == 0) {
+ /* end of connection */
+ qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
+ return;
+ }
+ qemu_send_packet(&s->nc, s->buf, size);
+}
+
+static int net_socket_mcast_create(struct sockaddr_in *mcastaddr, struct in_addr *localaddr)
+{
+ struct ip_mreq imr;
+ int fd;
+ int val, ret;
+#ifdef __OpenBSD__
+ unsigned char loop;
+#else
+ int loop;
+#endif
+
+ if (!IN_MULTICAST(ntohl(mcastaddr->sin_addr.s_addr))) {
+ fprintf(stderr, "qemu: error: specified mcastaddr \"%s\" (0x%08x) "
+ "does not contain a multicast address\n",
+ inet_ntoa(mcastaddr->sin_addr),
+ (int)ntohl(mcastaddr->sin_addr.s_addr));
+ return -1;
+
+ }
+ fd = qemu_socket(PF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ perror("socket(PF_INET, SOCK_DGRAM)");
+ return -1;
+ }
+
+ val = 1;
+ ret=setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+ (const char *)&val, sizeof(val));
+ if (ret < 0) {
+ perror("setsockopt(SOL_SOCKET, SO_REUSEADDR)");
+ goto fail;
+ }
+
+ ret = bind(fd, (struct sockaddr *)mcastaddr, sizeof(*mcastaddr));
+ if (ret < 0) {
+ perror("bind");
+ goto fail;
+ }
+
+ /* Add host to multicast group */
+ imr.imr_multiaddr = mcastaddr->sin_addr;
+ if (localaddr) {
+ imr.imr_interface = *localaddr;
+ } else {
+ imr.imr_interface.s_addr = htonl(INADDR_ANY);
+ }
+
+ ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ (const char *)&imr, sizeof(struct ip_mreq));
+ if (ret < 0) {
+ perror("setsockopt(IP_ADD_MEMBERSHIP)");
+ goto fail;
+ }
+
+ /* 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));
+ if (ret < 0) {
+ perror("setsockopt(SOL_IP, IP_MULTICAST_LOOP)");
+ goto fail;
+ }
+
+ /* 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));
+ if (ret < 0) {
+ perror("setsockopt(IP_MULTICAST_IF)");
+ goto fail;
+ }
+ }
+
+ socket_set_nonblock(fd);
+ return fd;
+fail:
+ if (fd >= 0)
+ closesocket(fd);
+ return -1;
+}
+
+static void net_socket_cleanup(NetClientState *nc)
+{
+ NetSocketState *s = DO_UPCAST(NetSocketState, nc, nc);
+ if (s->fd != -1) {
+ qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
+ close(s->fd);
+ s->fd = -1;
+ }
+ if (s->listen_fd != -1) {
+ qemu_set_fd_handler(s->listen_fd, NULL, NULL, NULL);
+ closesocket(s->listen_fd);
+ s->listen_fd = -1;
+ }
+}
+
+static NetClientInfo net_dgram_socket_info = {
+ .type = NET_CLIENT_OPTIONS_KIND_SOCKET,
+ .size = sizeof(NetSocketState),
+ .receive = net_socket_receive_dgram,
+ .cleanup = net_socket_cleanup,
+};
+
+static NetSocketState *net_socket_fd_init_dgram(NetClientState *peer,
+ const char *model,
+ const char *name,
+ int fd, int is_connected)
+{
+ struct sockaddr_in saddr;
+ int newfd;
+ socklen_t saddr_len;
+ NetClientState *nc;
+ NetSocketState *s;
+
+ /* fd passed: multicast: "learn" dgram_dst address from bound address and save it
+ * Because this may be "shared" socket from a "master" process, datagrams would be recv()
+ * by ONLY ONE process: we must "clone" this dgram socket --jjo
+ */
+
+ if (is_connected) {
+ if (getsockname(fd, (struct sockaddr *) &saddr, &saddr_len) == 0) {
+ /* must be bound */
+ if (saddr.sin_addr.s_addr == 0) {
+ fprintf(stderr, "qemu: error: init_dgram: fd=%d unbound, "
+ "cannot setup multicast dst addr\n", fd);
+ goto err;
+ }
+ /* clone dgram socket */
+ newfd = net_socket_mcast_create(&saddr, NULL);
+ if (newfd < 0) {
+ /* error already reported by net_socket_mcast_create() */
+ goto err;
+ }
+ /* clone newfd to fd, close newfd */
+ dup2(newfd, fd);
+ close(newfd);
+
+ } else {
+ fprintf(stderr,
+ "qemu: error: init_dgram: fd=%d failed getsockname(): %s\n",
+ fd, strerror(errno));
+ goto err;
+ }
+ }
+
+ nc = qemu_new_net_client(&net_dgram_socket_info, peer, model, name);
+
+ snprintf(nc->info_str, sizeof(nc->info_str),
+ "socket: fd=%d (%s mcast=%s:%d)",
+ fd, is_connected ? "cloned" : "",
+ inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
+
+ s = DO_UPCAST(NetSocketState, nc, nc);
+
+ s->fd = fd;
+ s->listen_fd = -1;
+
+ qemu_set_fd_handler(s->fd, net_socket_send_dgram, NULL, s);
+
+ /* mcast: save bound address as dst */
+ if (is_connected) {
+ s->dgram_dst = saddr;
+ }
+
+ return s;
+
+err:
+ closesocket(fd);
+ return NULL;
+}
+
+static void net_socket_connect(void *opaque)
+{
+ NetSocketState *s = opaque;
+ qemu_set_fd_handler(s->fd, net_socket_send, NULL, s);
+}
+
+static NetClientInfo net_socket_info = {
+ .type = NET_CLIENT_OPTIONS_KIND_SOCKET,
+ .size = sizeof(NetSocketState),
+ .receive = net_socket_receive,
+ .cleanup = net_socket_cleanup,
+};
+
+static NetSocketState *net_socket_fd_init_stream(NetClientState *peer,
+ const char *model,
+ const char *name,
+ int fd, int is_connected)
+{
+ NetClientState *nc;
+ NetSocketState *s;
+
+ nc = qemu_new_net_client(&net_socket_info, peer, model, name);
+
+ snprintf(nc->info_str, sizeof(nc->info_str), "socket: fd=%d", fd);
+
+ s = DO_UPCAST(NetSocketState, nc, nc);
+
+ s->fd = fd;
+ s->listen_fd = -1;
+
+ if (is_connected) {
+ net_socket_connect(s);
+ } else {
+ qemu_set_fd_handler(s->fd, NULL, net_socket_connect, s);
+ }
+ return s;
+}
+
+static NetSocketState *net_socket_fd_init(NetClientState *peer,
+ const char *model, const char *name,
+ int fd, int is_connected)
+{
+ int so_type = -1, optlen=sizeof(so_type);
+
+ if(getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&so_type,
+ (socklen_t *)&optlen)< 0) {
+ fprintf(stderr, "qemu: error: getsockopt(SO_TYPE) for fd=%d failed\n",
+ fd);
+ closesocket(fd);
+ return NULL;
+ }
+ switch(so_type) {
+ case SOCK_DGRAM:
+ return net_socket_fd_init_dgram(peer, model, name, fd, is_connected);
+ case SOCK_STREAM:
+ return net_socket_fd_init_stream(peer, model, name, fd, is_connected);
+ default:
+ /* who knows ... this could be a eg. a pty, do warn and continue as stream */
+ fprintf(stderr, "qemu: warning: socket type=%d for fd=%d is not SOCK_DGRAM or SOCK_STREAM\n", so_type, fd);
+ return net_socket_fd_init_stream(peer, model, name, fd, is_connected);
+ }
+ return NULL;
+}
+
+static void net_socket_accept(void *opaque)
+{
+ NetSocketState *s = opaque;
+ struct sockaddr_in saddr;
+ socklen_t len;
+ int fd;
+
+ for(;;) {
+ len = sizeof(saddr);
+ fd = qemu_accept(s->listen_fd, (struct sockaddr *)&saddr, &len);
+ if (fd < 0 && errno != EINTR) {
+ return;
+ } else if (fd >= 0) {
+ qemu_set_fd_handler(s->listen_fd, NULL, NULL, NULL);
+ break;
+ }
+ }
+
+ s->fd = fd;
+ s->nc.link_down = false;
+ net_socket_connect(s);
+ snprintf(s->nc.info_str, sizeof(s->nc.info_str),
+ "socket: connection from %s:%d",
+ inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
+}
+
+static int net_socket_listen_init(NetClientState *peer,
+ const char *model,
+ const char *name,
+ const char *host_str)
+{
+ NetClientState *nc;
+ NetSocketState *s;
+ struct sockaddr_in saddr;
+ int fd, val, ret;
+
+ if (parse_host_port(&saddr, host_str) < 0)
+ return -1;
+
+ fd = qemu_socket(PF_INET, SOCK_STREAM, 0);
+ if (fd < 0) {
+ perror("socket");
+ return -1;
+ }
+ socket_set_nonblock(fd);
+
+ /* allow fast reuse */
+ val = 1;
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&val, sizeof(val));
+
+ ret = bind(fd, (struct sockaddr *)&saddr, sizeof(saddr));
+ if (ret < 0) {
+ perror("bind");
+ closesocket(fd);
+ return -1;
+ }
+ ret = listen(fd, 0);
+ if (ret < 0) {
+ perror("listen");
+ closesocket(fd);
+ return -1;
+ }
+
+ nc = qemu_new_net_client(&net_socket_info, peer, model, name);
+ s = DO_UPCAST(NetSocketState, nc, nc);
+ s->fd = -1;
+ s->listen_fd = fd;
+ s->nc.link_down = true;
+
+ qemu_set_fd_handler(s->listen_fd, net_socket_accept, NULL, s);
+ return 0;
+}
+
+static int net_socket_connect_init(NetClientState *peer,
+ const char *model,
+ const char *name,
+ const char *host_str)
+{
+ NetSocketState *s;
+ int fd, connected, ret, err;
+ struct sockaddr_in saddr;
+
+ if (parse_host_port(&saddr, host_str) < 0)
+ return -1;
+
+ fd = qemu_socket(PF_INET, SOCK_STREAM, 0);
+ if (fd < 0) {
+ perror("socket");
+ return -1;
+ }
+ socket_set_nonblock(fd);
+
+ connected = 0;
+ for(;;) {
+ ret = connect(fd, (struct sockaddr *)&saddr, sizeof(saddr));
+ if (ret < 0) {
+ err = socket_error();
+ if (err == EINTR || err == EWOULDBLOCK) {
+ } else if (err == EINPROGRESS) {
+ break;
+#ifdef _WIN32
+ } else if (err == WSAEALREADY || err == WSAEINVAL) {
+ break;
+#endif
+ } else {
+ perror("connect");
+ closesocket(fd);
+ return -1;
+ }
+ } else {
+ connected = 1;
+ break;
+ }
+ }
+ s = net_socket_fd_init(peer, model, name, fd, connected);
+ if (!s)
+ return -1;
+ snprintf(s->nc.info_str, sizeof(s->nc.info_str),
+ "socket: connect to %s:%d",
+ inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
+ return 0;
+}
+
+static int net_socket_mcast_init(NetClientState *peer,
+ const char *model,
+ const char *name,
+ const char *host_str,
+ const char *localaddr_str)
+{
+ NetSocketState *s;
+ int fd;
+ struct sockaddr_in saddr;
+ struct in_addr localaddr, *param_localaddr;
+
+ if (parse_host_port(&saddr, host_str) < 0)
+ return -1;
+
+ if (localaddr_str != NULL) {
+ if (inet_aton(localaddr_str, &localaddr) == 0)
+ return -1;
+ param_localaddr = &localaddr;
+ } else {
+ param_localaddr = NULL;
+ }
+
+ fd = net_socket_mcast_create(&saddr, param_localaddr);
+ if (fd < 0)
+ return -1;
+
+ s = net_socket_fd_init(peer, model, name, fd, 0);
+ if (!s)
+ return -1;
+
+ s->dgram_dst = saddr;
+
+ snprintf(s->nc.info_str, sizeof(s->nc.info_str),
+ "socket: mcast=%s:%d",
+ inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
+ return 0;
+
+}
+
+static int net_socket_udp_init(NetClientState *peer,
+ const char *model,
+ const char *name,
+ const char *rhost,
+ const char *lhost)
+{
+ NetSocketState *s;
+ int fd, val, ret;
+ struct sockaddr_in laddr, raddr;
+
+ if (parse_host_port(&laddr, lhost) < 0) {
+ return -1;
+ }
+
+ if (parse_host_port(&raddr, rhost) < 0) {
+ return -1;
+ }
+
+ fd = qemu_socket(PF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ perror("socket(PF_INET, SOCK_DGRAM)");
+ return -1;
+ }
+ val = 1;
+ ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+ (const char *)&val, sizeof(val));
+ if (ret < 0) {
+ perror("setsockopt(SOL_SOCKET, SO_REUSEADDR)");
+ closesocket(fd);
+ return -1;
+ }
+ ret = bind(fd, (struct sockaddr *)&laddr, sizeof(laddr));
+ if (ret < 0) {
+ perror("bind");
+ closesocket(fd);
+ return -1;
+ }
+
+ s = net_socket_fd_init(peer, model, name, fd, 0);
+ if (!s) {
+ return -1;
+ }
+
+ s->dgram_dst = raddr;
+
+ snprintf(s->nc.info_str, sizeof(s->nc.info_str),
+ "socket: udp=%s:%d",
+ inet_ntoa(raddr.sin_addr), ntohs(raddr.sin_port));
+ return 0;
+}
+
+int net_init_socket(const NetClientOptions *opts, const char *name,
+ NetClientState *peer)
+{
+ const NetdevSocketOptions *sock;
+
+ assert(opts->kind == NET_CLIENT_OPTIONS_KIND_SOCKET);
+ sock = opts->socket;
+
+ if (sock->has_fd + sock->has_listen + sock->has_connect + sock->has_mcast +
+ sock->has_udp != 1) {
+ error_report("exactly one of fd=, listen=, connect=, mcast= or udp="
+ " is required");
+ return -1;
+ }
+
+ if (sock->has_localaddr && !sock->has_mcast && !sock->has_udp) {
+ error_report("localaddr= is only valid with mcast= or udp=");
+ return -1;
+ }
+
+ if (sock->has_fd) {
+ int fd;
+
+ fd = net_handle_fd_param(cur_mon, sock->fd);
+ if (fd == -1 || !net_socket_fd_init(peer, "socket", name, fd, 1)) {
+ return -1;
+ }
+ return 0;
+ }
+
+ if (sock->has_listen) {
+ if (net_socket_listen_init(peer, "socket", name, sock->listen) == -1) {
+ return -1;
+ }
+ return 0;
+ }
+
+ if (sock->has_connect) {
+ if (net_socket_connect_init(peer, "socket", name, sock->connect) ==
+ -1) {
+ return -1;
+ }
+ return 0;
+ }
+
+ if (sock->has_mcast) {
+ /* if sock->localaddr is missing, it has been initialized to "all bits
+ * zero" */
+ if (net_socket_mcast_init(peer, "socket", name, sock->mcast,
+ sock->localaddr) == -1) {
+ return -1;
+ }
+ return 0;
+ }
+
+ assert(sock->has_udp);
+ if (!sock->has_localaddr) {
+ error_report("localaddr= is mandatory with udp=");
+ return -1;
+ }
+ if (net_socket_udp_init(peer, "udp", name, sock->udp, sock->localaddr) ==
+ -1) {
+ return -1;
+ }
+ return 0;
+}
diff --git a/net/socket.h b/net/socket.h
new file mode 100644
index 0000000000..3f8a092459
--- /dev/null
+++ b/net/socket.h
@@ -0,0 +1,33 @@
+/*
+ * 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_SOCKET_H
+#define QEMU_NET_SOCKET_H
+
+#include "net.h"
+#include "qapi-types.h"
+
+int net_init_socket(const NetClientOptions *opts, const char *name,
+ NetClientState *peer);
+
+#endif /* QEMU_NET_SOCKET_H */
diff --git a/net/tap-aix.c b/net/tap-aix.c
new file mode 100644
index 0000000000..f27c17729e
--- /dev/null
+++ b/net/tap-aix.c
@@ -0,0 +1,61 @@
+/*
+ * 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 "net/tap.h"
+#include <stdio.h>
+
+int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required)
+{
+ fprintf(stderr, "no tap on AIX\n");
+ return -1;
+}
+
+int tap_set_sndbuf(int fd, const NetdevTapOptions *tap)
+{
+ return 0;
+}
+
+int tap_probe_vnet_hdr(int fd)
+{
+ return 0;
+}
+
+int tap_probe_has_ufo(int fd)
+{
+ return 0;
+}
+
+int tap_probe_vnet_hdr_len(int fd, int len)
+{
+ return 0;
+}
+
+void tap_fd_set_vnet_hdr_len(int fd, int len)
+{
+}
+
+void tap_fd_set_offload(int fd, int csum, int tso4,
+ int tso6, int ecn, int ufo)
+{
+}
diff --git a/net/tap-bsd.c b/net/tap-bsd.c
new file mode 100644
index 0000000000..a3b717dd1c
--- /dev/null
+++ b/net/tap-bsd.c
@@ -0,0 +1,147 @@
+/*
+ * 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 "net/tap.h"
+#include "qemu-common.h"
+#include "sysemu.h"
+#include "qemu-error.h"
+
+#ifdef __NetBSD__
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <net/if_tap.h>
+#endif
+
+int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required)
+{
+ int fd;
+#ifdef TAPGIFNAME
+ struct ifreq ifr;
+#else
+ char *dev;
+ struct stat s;
+#endif
+
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__)
+ /* if no ifname is given, always start the search from tap0/tun0. */
+ int i;
+ char dname[100];
+
+ for (i = 0; i < 10; i++) {
+ if (*ifname) {
+ snprintf(dname, sizeof dname, "/dev/%s", ifname);
+ } else {
+#if defined(__OpenBSD__)
+ snprintf(dname, sizeof dname, "/dev/tun%d", i);
+#else
+ snprintf(dname, sizeof dname, "/dev/tap%d", i);
+#endif
+ }
+ TFR(fd = open(dname, O_RDWR));
+ if (fd >= 0) {
+ break;
+ }
+ else if (errno == ENXIO || errno == ENOENT) {
+ break;
+ }
+ if (*ifname) {
+ break;
+ }
+ }
+ if (fd < 0) {
+ error_report("warning: could not open %s (%s): no virtual network emulation",
+ dname, strerror(errno));
+ return -1;
+ }
+#else
+ TFR(fd = open("/dev/tap", O_RDWR));
+ if (fd < 0) {
+ fprintf(stderr,
+ "warning: could not open /dev/tap: no virtual network emulation: %s\n",
+ strerror(errno));
+ return -1;
+ }
+#endif
+
+#ifdef TAPGIFNAME
+ if (ioctl(fd, TAPGIFNAME, (void *)&ifr) < 0) {
+ fprintf(stderr, "warning: could not get tap name: %s\n",
+ strerror(errno));
+ return -1;
+ }
+ pstrcpy(ifname, ifname_size, ifr.ifr_name);
+#else
+ if (fstat(fd, &s) < 0) {
+ fprintf(stderr,
+ "warning: could not stat /dev/tap: no virtual network emulation: %s\n",
+ strerror(errno));
+ return -1;
+ }
+ dev = devname(s.st_rdev, S_IFCHR);
+ pstrcpy(ifname, ifname_size, dev);
+#endif
+
+ if (*vnet_hdr) {
+ /* BSD doesn't have IFF_VNET_HDR */
+ *vnet_hdr = 0;
+
+ if (vnet_hdr_required && !*vnet_hdr) {
+ error_report("vnet_hdr=1 requested, but no kernel "
+ "support for IFF_VNET_HDR available");
+ close(fd);
+ return -1;
+ }
+ }
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+ return fd;
+}
+
+int tap_set_sndbuf(int fd, const NetdevTapOptions *tap)
+{
+ return 0;
+}
+
+int tap_probe_vnet_hdr(int fd)
+{
+ return 0;
+}
+
+int tap_probe_has_ufo(int fd)
+{
+ return 0;
+}
+
+int tap_probe_vnet_hdr_len(int fd, int len)
+{
+ return 0;
+}
+
+void tap_fd_set_vnet_hdr_len(int fd, int len)
+{
+}
+
+void tap_fd_set_offload(int fd, int csum, int tso4,
+ int tso6, int ecn, int ufo)
+{
+}
diff --git a/net/tap-haiku.c b/net/tap-haiku.c
new file mode 100644
index 0000000000..34739d1562
--- /dev/null
+++ b/net/tap-haiku.c
@@ -0,0 +1,61 @@
+/*
+ * 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 "net/tap.h"
+#include <stdio.h>
+
+int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required)
+{
+ fprintf(stderr, "no tap on Haiku\n");
+ return -1;
+}
+
+int tap_set_sndbuf(int fd, const NetdevTapOptions *tap)
+{
+ return 0;
+}
+
+int tap_probe_vnet_hdr(int fd)
+{
+ return 0;
+}
+
+int tap_probe_has_ufo(int fd)
+{
+ return 0;
+}
+
+int tap_probe_vnet_hdr_len(int fd, int len)
+{
+ return 0;
+}
+
+void tap_fd_set_vnet_hdr_len(int fd, int len)
+{
+}
+
+void tap_fd_set_offload(int fd, int csum, int tso4,
+ int tso6, int ecn, int ufo)
+{
+}
diff --git a/net/tap-linux.c b/net/tap-linux.c
new file mode 100644
index 0000000000..c6521bec34
--- /dev/null
+++ b/net/tap-linux.c
@@ -0,0 +1,202 @@
+/*
+ * QEMU System Emulator
+ *
+ * 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.
+ */
+
+#include "net/tap.h"
+#include "net/tap-linux.h"
+
+#include <net/if.h>
+#include <sys/ioctl.h>
+
+#include "sysemu.h"
+#include "qemu-common.h"
+#include "qemu-error.h"
+
+#define PATH_NET_TUN "/dev/net/tun"
+
+int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required)
+{
+ struct ifreq ifr;
+ int fd, ret;
+
+ TFR(fd = open(PATH_NET_TUN, O_RDWR));
+ if (fd < 0) {
+ error_report("could not open %s: %m", PATH_NET_TUN);
+ return -1;
+ }
+ 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_VNET_HDR) {
+ *vnet_hdr = 1;
+ ifr.ifr_flags |= IFF_VNET_HDR;
+ } else {
+ *vnet_hdr = 0;
+ }
+
+ if (vnet_hdr_required && !*vnet_hdr) {
+ error_report("vnet_hdr=1 requested, but no kernel "
+ "support for IFF_VNET_HDR available");
+ close(fd);
+ return -1;
+ }
+ }
+
+ if (ifname[0] != '\0')
+ pstrcpy(ifr.ifr_name, IFNAMSIZ, ifname);
+ else
+ pstrcpy(ifr.ifr_name, IFNAMSIZ, "tap%d");
+ ret = ioctl(fd, TUNSETIFF, (void *) &ifr);
+ if (ret != 0) {
+ if (ifname[0] != '\0') {
+ error_report("could not configure %s (%s): %m", PATH_NET_TUN, ifr.ifr_name);
+ } else {
+ error_report("could not configure %s: %m", PATH_NET_TUN);
+ }
+ close(fd);
+ return -1;
+ }
+ pstrcpy(ifname, ifname_size, ifr.ifr_name);
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+ return fd;
+}
+
+/* sndbuf implements a kind of flow control for tap.
+ * Unfortunately when it's enabled, and packets are sent
+ * to other guests on the same host, the receiver
+ * can lock up the transmitter indefinitely.
+ *
+ * To avoid packet loss, sndbuf should be set to a value lower than the tx
+ * queue capacity of any destination network interface.
+ * Ethernet NICs generally have txqueuelen=1000, so 1Mb is
+ * a good value, given a 1500 byte MTU.
+ */
+#define TAP_DEFAULT_SNDBUF 0
+
+int tap_set_sndbuf(int fd, const NetdevTapOptions *tap)
+{
+ int sndbuf;
+
+ sndbuf = !tap->has_sndbuf ? TAP_DEFAULT_SNDBUF :
+ tap->sndbuf > INT_MAX ? INT_MAX :
+ tap->sndbuf;
+
+ if (!sndbuf) {
+ sndbuf = INT_MAX;
+ }
+
+ if (ioctl(fd, TUNSETSNDBUF, &sndbuf) == -1 && tap->has_sndbuf) {
+ error_report("TUNSETSNDBUF ioctl failed: %s", strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+int tap_probe_vnet_hdr(int fd)
+{
+ struct ifreq ifr;
+
+ if (ioctl(fd, TUNGETIFF, &ifr) != 0) {
+ error_report("TUNGETIFF ioctl() failed: %s", strerror(errno));
+ return 0;
+ }
+
+ return ifr.ifr_flags & IFF_VNET_HDR;
+}
+
+int tap_probe_has_ufo(int fd)
+{
+ unsigned offload;
+
+ offload = TUN_F_CSUM | TUN_F_UFO;
+
+ if (ioctl(fd, TUNSETOFFLOAD, offload) < 0)
+ return 0;
+
+ return 1;
+}
+
+/* Verify that we can assign given length */
+int tap_probe_vnet_hdr_len(int fd, int len)
+{
+ int orig;
+ if (ioctl(fd, TUNGETVNETHDRSZ, &orig) == -1) {
+ return 0;
+ }
+ if (ioctl(fd, TUNSETVNETHDRSZ, &len) == -1) {
+ return 0;
+ }
+ /* Restore original length: we can't handle failure. */
+ if (ioctl(fd, TUNSETVNETHDRSZ, &orig) == -1) {
+ fprintf(stderr, "TUNGETVNETHDRSZ ioctl() failed: %s. Exiting.\n",
+ strerror(errno));
+ assert(0);
+ return -errno;
+ }
+ return 1;
+}
+
+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);
+ }
+}
+
+void tap_fd_set_offload(int fd, int csum, int tso4,
+ int tso6, int ecn, int ufo)
+{
+ unsigned int offload = 0;
+
+ /* Check if our kernel supports TUNSETOFFLOAD */
+ if (ioctl(fd, TUNSETOFFLOAD, 0) != 0 && errno == EINVAL) {
+ return;
+ }
+
+ if (csum) {
+ offload |= TUN_F_CSUM;
+ if (tso4)
+ offload |= TUN_F_TSO4;
+ if (tso6)
+ offload |= TUN_F_TSO6;
+ if ((tso4 || tso6) && ecn)
+ offload |= TUN_F_TSO_ECN;
+ if (ufo)
+ offload |= TUN_F_UFO;
+ }
+
+ if (ioctl(fd, TUNSETOFFLOAD, offload) != 0) {
+ offload &= ~TUN_F_UFO;
+ if (ioctl(fd, TUNSETOFFLOAD, offload) != 0) {
+ fprintf(stderr, "TUNSETOFFLOAD ioctl() failed: %s\n",
+ strerror(errno));
+ }
+ }
+}
diff --git a/net/tap-linux.h b/net/tap-linux.h
new file mode 100644
index 0000000000..659e98122b
--- /dev/null
+++ b/net/tap-linux.h
@@ -0,0 +1,63 @@
+/*
+ * Universal TUN/TAP device driver.
+ * Copyright (C) 1999-2000 Maxim Krasnyansky <max_mk@yahoo.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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ */
+
+#ifndef QEMU_TAP_H
+#define QEMU_TAP_H
+
+#include <stdint.h>
+#ifdef __linux__
+
+#include <linux/ioctl.h>
+
+/* Ioctl defines */
+#define TUNSETIFF _IOW('T', 202, int)
+#define TUNGETFEATURES _IOR('T', 207, unsigned int)
+#define TUNSETOFFLOAD _IOW('T', 208, unsigned int)
+#define TUNGETIFF _IOR('T', 210, unsigned int)
+#define TUNSETSNDBUF _IOW('T', 212, int)
+#define TUNGETVNETHDRSZ _IOR('T', 215, int)
+#define TUNSETVNETHDRSZ _IOW('T', 216, int)
+
+#endif
+
+/* TUNSETIFF ifr flags */
+#define IFF_TAP 0x0002
+#define IFF_NO_PI 0x1000
+#define IFF_VNET_HDR 0x4000
+
+/* Features for GSO (TUNSETOFFLOAD). */
+#define TUN_F_CSUM 0x01 /* You can hand me unchecksummed packets. */
+#define TUN_F_TSO4 0x02 /* I can handle TSO for IPv4 packets */
+#define TUN_F_TSO6 0x04 /* I can handle TSO for IPv6 packets */
+#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
new file mode 100644
index 0000000000..5d6ac42f24
--- /dev/null
+++ b/net/tap-solaris.c
@@ -0,0 +1,227 @@
+/*
+ * 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 "net/tap.h"
+#include "sysemu.h"
+
+#include <sys/stat.h>
+#include <sys/ethernet.h>
+#include <sys/sockio.h>
+#include <netinet/arp.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h> // must come after ip.h
+#include <netinet/udp.h>
+#include <netinet/tcp.h>
+#include <net/if.h>
+#include <syslog.h>
+#include <stropts.h>
+#include "qemu-error.h"
+
+ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen)
+{
+ struct strbuf sbuf;
+ int f = 0;
+
+ sbuf.maxlen = maxlen;
+ sbuf.buf = (char *)buf;
+
+ return getmsg(tapfd, NULL, &sbuf, &f) >= 0 ? sbuf.len : -1;
+}
+
+#define TUNNEWPPA (('T'<<16) | 0x0001)
+/*
+ * Allocate TAP device, returns opened fd.
+ * Stores dev name in the first arg(must be large enough).
+ */
+static int tap_alloc(char *dev, size_t dev_size)
+{
+ int tap_fd, if_fd, ppa = -1;
+ static int ip_fd = 0;
+ char *ptr;
+
+ static int arp_fd = 0;
+ int ip_muxid, arp_muxid;
+ struct strioctl strioc_if, strioc_ppa;
+ int link_type = I_PLINK;
+ struct lifreq ifr;
+ char actual_name[32] = "";
+
+ memset(&ifr, 0x0, sizeof(ifr));
+
+ if( *dev ){
+ ptr = dev;
+ while( *ptr && !qemu_isdigit((int)*ptr) ) ptr++;
+ ppa = atoi(ptr);
+ }
+
+ /* Check if IP device was opened */
+ if( ip_fd )
+ close(ip_fd);
+
+ TFR(ip_fd = open("/dev/udp", O_RDWR, 0));
+ if (ip_fd < 0) {
+ syslog(LOG_ERR, "Can't open /dev/ip (actually /dev/udp)");
+ return -1;
+ }
+
+ TFR(tap_fd = open("/dev/tap", O_RDWR, 0));
+ if (tap_fd < 0) {
+ syslog(LOG_ERR, "Can't open /dev/tap");
+ return -1;
+ }
+
+ /* Assign a new PPA and get its unit number. */
+ strioc_ppa.ic_cmd = TUNNEWPPA;
+ strioc_ppa.ic_timout = 0;
+ strioc_ppa.ic_len = sizeof(ppa);
+ strioc_ppa.ic_dp = (char *)&ppa;
+ if ((ppa = ioctl (tap_fd, I_STR, &strioc_ppa)) < 0)
+ syslog (LOG_ERR, "Can't assign new interface");
+
+ TFR(if_fd = open("/dev/tap", O_RDWR, 0));
+ if (if_fd < 0) {
+ syslog(LOG_ERR, "Can't open /dev/tap (2)");
+ return -1;
+ }
+ if(ioctl(if_fd, I_PUSH, "ip") < 0){
+ syslog(LOG_ERR, "Can't push IP module");
+ return -1;
+ }
+
+ if (ioctl(if_fd, SIOCGLIFFLAGS, &ifr) < 0)
+ syslog(LOG_ERR, "Can't get flags\n");
+
+ snprintf (actual_name, 32, "tap%d", ppa);
+ pstrcpy(ifr.lifr_name, sizeof(ifr.lifr_name), actual_name);
+
+ ifr.lifr_ppa = ppa;
+ /* Assign ppa according to the unit number returned by tun device */
+
+ if (ioctl (if_fd, SIOCSLIFNAME, &ifr) < 0)
+ syslog (LOG_ERR, "Can't set PPA %d", ppa);
+ if (ioctl(if_fd, SIOCGLIFFLAGS, &ifr) <0)
+ syslog (LOG_ERR, "Can't get flags\n");
+ /* Push arp module to if_fd */
+ if (ioctl (if_fd, I_PUSH, "arp") < 0)
+ syslog (LOG_ERR, "Can't push ARP module (2)");
+
+ /* Push arp module to ip_fd */
+ if (ioctl (ip_fd, I_POP, NULL) < 0)
+ syslog (LOG_ERR, "I_POP failed\n");
+ if (ioctl (ip_fd, I_PUSH, "arp") < 0)
+ syslog (LOG_ERR, "Can't push ARP module (3)\n");
+ /* Open arp_fd */
+ TFR(arp_fd = open ("/dev/tap", O_RDWR, 0));
+ if (arp_fd < 0)
+ syslog (LOG_ERR, "Can't open %s\n", "/dev/tap");
+
+ /* Set ifname to arp */
+ strioc_if.ic_cmd = SIOCSLIFNAME;
+ strioc_if.ic_timout = 0;
+ strioc_if.ic_len = sizeof(ifr);
+ strioc_if.ic_dp = (char *)&ifr;
+ if (ioctl(arp_fd, I_STR, &strioc_if) < 0){
+ syslog (LOG_ERR, "Can't set ifname to arp\n");
+ }
+
+ if((ip_muxid = ioctl(ip_fd, I_LINK, if_fd)) < 0){
+ syslog(LOG_ERR, "Can't link TAP device to IP");
+ return -1;
+ }
+
+ if ((arp_muxid = ioctl (ip_fd, link_type, arp_fd)) < 0)
+ syslog (LOG_ERR, "Can't link TAP device to ARP");
+
+ close (if_fd);
+
+ memset(&ifr, 0x0, sizeof(ifr));
+ pstrcpy(ifr.lifr_name, sizeof(ifr.lifr_name), actual_name);
+ ifr.lifr_ip_muxid = ip_muxid;
+ ifr.lifr_arp_muxid = arp_muxid;
+
+ if (ioctl (ip_fd, SIOCSLIFMUXID, &ifr) < 0)
+ {
+ ioctl (ip_fd, I_PUNLINK , arp_muxid);
+ ioctl (ip_fd, I_PUNLINK, ip_muxid);
+ syslog (LOG_ERR, "Can't set multiplexor id");
+ }
+
+ snprintf(dev, dev_size, "tap%d", ppa);
+ return tap_fd;
+}
+
+int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required)
+{
+ char dev[10]="";
+ int fd;
+ if( (fd = tap_alloc(dev, sizeof(dev))) < 0 ){
+ fprintf(stderr, "Cannot allocate TAP device\n");
+ return -1;
+ }
+ pstrcpy(ifname, ifname_size, dev);
+ if (*vnet_hdr) {
+ /* Solaris doesn't have IFF_VNET_HDR */
+ *vnet_hdr = 0;
+
+ if (vnet_hdr_required && !*vnet_hdr) {
+ error_report("vnet_hdr=1 requested, but no kernel "
+ "support for IFF_VNET_HDR available");
+ close(fd);
+ return -1;
+ }
+ }
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+ return fd;
+}
+
+int tap_set_sndbuf(int fd, const NetdevTapOptions *tap)
+{
+ return 0;
+}
+
+int tap_probe_vnet_hdr(int fd)
+{
+ return 0;
+}
+
+int tap_probe_has_ufo(int fd)
+{
+ return 0;
+}
+
+int tap_probe_vnet_hdr_len(int fd, int len)
+{
+ return 0;
+}
+
+void tap_fd_set_vnet_hdr_len(int fd, int len)
+{
+}
+
+void tap_fd_set_offload(int fd, int csum, int tso4,
+ int tso6, int ecn, int ufo)
+{
+}
diff --git a/net/tap-win32.c b/net/tap-win32.c
new file mode 100644
index 0000000000..d65fcf2479
--- /dev/null
+++ b/net/tap-win32.c
@@ -0,0 +1,763 @@
+/*
+ * TAP-Win32 -- A kernel driver to provide virtual tap device functionality
+ * on Windows. Originally derived from the CIPE-Win32
+ * project by Damion K. Wilson, with extensive modifications by
+ * James Yonan.
+ *
+ * All source code which derives from the CIPE-Win32 project is
+ * Copyright (C) Damion K. Wilson, 2003, and is released under the
+ * GPL version 2 (see below).
+ *
+ * All other source code is Copyright (C) James Yonan, 2003-2004,
+ * and is released under the GPL version 2 (see below).
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 (see the file COPYING included with this
+ * distribution); if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "net/tap.h"
+
+#include "qemu-common.h"
+#include "net.h"
+#include "sysemu.h"
+#include "qemu-error.h"
+#include <stdio.h>
+#include <windows.h>
+#include <winioctl.h>
+
+//=============
+// TAP IOCTLs
+//=============
+
+#define TAP_CONTROL_CODE(request,method) \
+ CTL_CODE (FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS)
+
+#define TAP_IOCTL_GET_MAC TAP_CONTROL_CODE (1, METHOD_BUFFERED)
+#define TAP_IOCTL_GET_VERSION TAP_CONTROL_CODE (2, METHOD_BUFFERED)
+#define TAP_IOCTL_GET_MTU TAP_CONTROL_CODE (3, METHOD_BUFFERED)
+#define TAP_IOCTL_GET_INFO TAP_CONTROL_CODE (4, METHOD_BUFFERED)
+#define TAP_IOCTL_CONFIG_POINT_TO_POINT TAP_CONTROL_CODE (5, METHOD_BUFFERED)
+#define TAP_IOCTL_SET_MEDIA_STATUS TAP_CONTROL_CODE (6, METHOD_BUFFERED)
+#define TAP_IOCTL_CONFIG_DHCP_MASQ TAP_CONTROL_CODE (7, METHOD_BUFFERED)
+#define TAP_IOCTL_GET_LOG_LINE TAP_CONTROL_CODE (8, METHOD_BUFFERED)
+#define TAP_IOCTL_CONFIG_DHCP_SET_OPT TAP_CONTROL_CODE (9, METHOD_BUFFERED)
+
+//=================
+// Registry keys
+//=================
+
+#define ADAPTER_KEY "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
+
+#define NETWORK_CONNECTIONS_KEY "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
+
+//======================
+// Filesystem prefixes
+//======================
+
+#define USERMODEDEVICEDIR "\\\\.\\Global\\"
+#define TAPSUFFIX ".tap"
+
+
+//======================
+// Compile time configuration
+//======================
+
+//#define DEBUG_TAP_WIN32
+
+#define TUN_ASYNCHRONOUS_WRITES 1
+
+#define TUN_BUFFER_SIZE 1560
+#define TUN_MAX_BUFFER_COUNT 32
+
+/*
+ * The data member "buffer" must be the first element in the tun_buffer
+ * structure. See the function, tap_win32_free_buffer.
+ */
+typedef struct tun_buffer_s {
+ unsigned char buffer [TUN_BUFFER_SIZE];
+ unsigned long read_size;
+ struct tun_buffer_s* next;
+} tun_buffer_t;
+
+typedef struct tap_win32_overlapped {
+ HANDLE handle;
+ HANDLE read_event;
+ HANDLE write_event;
+ HANDLE output_queue_semaphore;
+ HANDLE free_list_semaphore;
+ HANDLE tap_semaphore;
+ CRITICAL_SECTION output_queue_cs;
+ CRITICAL_SECTION free_list_cs;
+ OVERLAPPED read_overlapped;
+ OVERLAPPED write_overlapped;
+ tun_buffer_t buffers[TUN_MAX_BUFFER_COUNT];
+ tun_buffer_t* free_list;
+ tun_buffer_t* output_queue_front;
+ tun_buffer_t* output_queue_back;
+} tap_win32_overlapped_t;
+
+static tap_win32_overlapped_t tap_overlapped;
+
+static tun_buffer_t* get_buffer_from_free_list(tap_win32_overlapped_t* const overlapped)
+{
+ tun_buffer_t* buffer = NULL;
+ WaitForSingleObject(overlapped->free_list_semaphore, INFINITE);
+ EnterCriticalSection(&overlapped->free_list_cs);
+ buffer = overlapped->free_list;
+// assert(buffer != NULL);
+ overlapped->free_list = buffer->next;
+ LeaveCriticalSection(&overlapped->free_list_cs);
+ buffer->next = NULL;
+ return buffer;
+}
+
+static void put_buffer_on_free_list(tap_win32_overlapped_t* const overlapped, tun_buffer_t* const buffer)
+{
+ EnterCriticalSection(&overlapped->free_list_cs);
+ buffer->next = overlapped->free_list;
+ overlapped->free_list = buffer;
+ LeaveCriticalSection(&overlapped->free_list_cs);
+ ReleaseSemaphore(overlapped->free_list_semaphore, 1, NULL);
+}
+
+static tun_buffer_t* get_buffer_from_output_queue(tap_win32_overlapped_t* const overlapped, const int block)
+{
+ tun_buffer_t* buffer = NULL;
+ DWORD result, timeout = block ? INFINITE : 0L;
+
+ // Non-blocking call
+ result = WaitForSingleObject(overlapped->output_queue_semaphore, timeout);
+
+ switch (result)
+ {
+ // The semaphore object was signaled.
+ case WAIT_OBJECT_0:
+ EnterCriticalSection(&overlapped->output_queue_cs);
+
+ buffer = overlapped->output_queue_front;
+ overlapped->output_queue_front = buffer->next;
+
+ if(overlapped->output_queue_front == NULL) {
+ overlapped->output_queue_back = NULL;
+ }
+
+ LeaveCriticalSection(&overlapped->output_queue_cs);
+ break;
+
+ // Semaphore was nonsignaled, so a time-out occurred.
+ case WAIT_TIMEOUT:
+ // Cannot open another window.
+ break;
+ }
+
+ return buffer;
+}
+
+static tun_buffer_t* get_buffer_from_output_queue_immediate (tap_win32_overlapped_t* const overlapped)
+{
+ return get_buffer_from_output_queue(overlapped, 0);
+}
+
+static void put_buffer_on_output_queue(tap_win32_overlapped_t* const overlapped, tun_buffer_t* const buffer)
+{
+ EnterCriticalSection(&overlapped->output_queue_cs);
+
+ if(overlapped->output_queue_front == NULL && overlapped->output_queue_back == NULL) {
+ overlapped->output_queue_front = overlapped->output_queue_back = buffer;
+ } else {
+ buffer->next = NULL;
+ overlapped->output_queue_back->next = buffer;
+ overlapped->output_queue_back = buffer;
+ }
+
+ LeaveCriticalSection(&overlapped->output_queue_cs);
+
+ ReleaseSemaphore(overlapped->output_queue_semaphore, 1, NULL);
+}
+
+
+static int is_tap_win32_dev(const char *guid)
+{
+ HKEY netcard_key;
+ LONG status;
+ DWORD len;
+ int i = 0;
+
+ status = RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE,
+ ADAPTER_KEY,
+ 0,
+ KEY_READ,
+ &netcard_key);
+
+ if (status != ERROR_SUCCESS) {
+ return FALSE;
+ }
+
+ for (;;) {
+ char enum_name[256];
+ char unit_string[256];
+ HKEY unit_key;
+ char component_id_string[] = "ComponentId";
+ char component_id[256];
+ char net_cfg_instance_id_string[] = "NetCfgInstanceId";
+ char net_cfg_instance_id[256];
+ DWORD data_type;
+
+ len = sizeof (enum_name);
+ status = RegEnumKeyEx(
+ netcard_key,
+ i,
+ enum_name,
+ &len,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+
+ if (status == ERROR_NO_MORE_ITEMS)
+ break;
+ else if (status != ERROR_SUCCESS) {
+ return FALSE;
+ }
+
+ snprintf (unit_string, sizeof(unit_string), "%s\\%s",
+ ADAPTER_KEY, enum_name);
+
+ status = RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE,
+ unit_string,
+ 0,
+ KEY_READ,
+ &unit_key);
+
+ if (status != ERROR_SUCCESS) {
+ return FALSE;
+ } else {
+ len = sizeof (component_id);
+ status = RegQueryValueEx(
+ unit_key,
+ component_id_string,
+ NULL,
+ &data_type,
+ (LPBYTE)component_id,
+ &len);
+
+ if (!(status != ERROR_SUCCESS || data_type != REG_SZ)) {
+ len = sizeof (net_cfg_instance_id);
+ status = RegQueryValueEx(
+ unit_key,
+ net_cfg_instance_id_string,
+ NULL,
+ &data_type,
+ (LPBYTE)net_cfg_instance_id,
+ &len);
+
+ if (status == ERROR_SUCCESS && data_type == REG_SZ) {
+ if (/* !strcmp (component_id, TAP_COMPONENT_ID) &&*/
+ !strcmp (net_cfg_instance_id, guid)) {
+ RegCloseKey (unit_key);
+ RegCloseKey (netcard_key);
+ return TRUE;
+ }
+ }
+ }
+ RegCloseKey (unit_key);
+ }
+ ++i;
+ }
+
+ RegCloseKey (netcard_key);
+ return FALSE;
+}
+
+static int get_device_guid(
+ char *name,
+ int name_size,
+ char *actual_name,
+ int actual_name_size)
+{
+ LONG status;
+ HKEY control_net_key;
+ DWORD len;
+ int i = 0;
+ int stop = 0;
+
+ status = RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE,
+ NETWORK_CONNECTIONS_KEY,
+ 0,
+ KEY_READ,
+ &control_net_key);
+
+ if (status != ERROR_SUCCESS) {
+ return -1;
+ }
+
+ while (!stop)
+ {
+ char enum_name[256];
+ char connection_string[256];
+ HKEY connection_key;
+ char name_data[256];
+ DWORD name_type;
+ const char name_string[] = "Name";
+
+ len = sizeof (enum_name);
+ status = RegEnumKeyEx(
+ control_net_key,
+ i,
+ enum_name,
+ &len,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+
+ if (status == ERROR_NO_MORE_ITEMS)
+ break;
+ else if (status != ERROR_SUCCESS) {
+ return -1;
+ }
+
+ snprintf(connection_string,
+ sizeof(connection_string),
+ "%s\\%s\\Connection",
+ NETWORK_CONNECTIONS_KEY, enum_name);
+
+ status = RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE,
+ connection_string,
+ 0,
+ KEY_READ,
+ &connection_key);
+
+ if (status == ERROR_SUCCESS) {
+ len = sizeof (name_data);
+ status = RegQueryValueEx(
+ connection_key,
+ name_string,
+ NULL,
+ &name_type,
+ (LPBYTE)name_data,
+ &len);
+
+ if (status != ERROR_SUCCESS || name_type != REG_SZ) {
+ return -1;
+ }
+ else {
+ if (is_tap_win32_dev(enum_name)) {
+ snprintf(name, name_size, "%s", enum_name);
+ if (actual_name) {
+ if (strcmp(actual_name, "") != 0) {
+ if (strcmp(name_data, actual_name) != 0) {
+ RegCloseKey (connection_key);
+ ++i;
+ continue;
+ }
+ }
+ else {
+ snprintf(actual_name, actual_name_size, "%s", name_data);
+ }
+ }
+ stop = 1;
+ }
+ }
+
+ RegCloseKey (connection_key);
+ }
+ ++i;
+ }
+
+ RegCloseKey (control_net_key);
+
+ if (stop == 0)
+ return -1;
+
+ return 0;
+}
+
+static int tap_win32_set_status(HANDLE handle, int status)
+{
+ unsigned long len = 0;
+
+ return DeviceIoControl(handle, TAP_IOCTL_SET_MEDIA_STATUS,
+ &status, sizeof (status),
+ &status, sizeof (status), &len, NULL);
+}
+
+static void tap_win32_overlapped_init(tap_win32_overlapped_t* const overlapped, const HANDLE handle)
+{
+ overlapped->handle = handle;
+
+ overlapped->read_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+ overlapped->write_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+
+ overlapped->read_overlapped.Offset = 0;
+ overlapped->read_overlapped.OffsetHigh = 0;
+ overlapped->read_overlapped.hEvent = overlapped->read_event;
+
+ overlapped->write_overlapped.Offset = 0;
+ overlapped->write_overlapped.OffsetHigh = 0;
+ overlapped->write_overlapped.hEvent = overlapped->write_event;
+
+ InitializeCriticalSection(&overlapped->output_queue_cs);
+ InitializeCriticalSection(&overlapped->free_list_cs);
+
+ overlapped->output_queue_semaphore = CreateSemaphore(
+ NULL, // default security attributes
+ 0, // initial count
+ TUN_MAX_BUFFER_COUNT, // maximum count
+ NULL); // unnamed semaphore
+
+ if(!overlapped->output_queue_semaphore) {
+ fprintf(stderr, "error creating output queue semaphore!\n");
+ }
+
+ overlapped->free_list_semaphore = CreateSemaphore(
+ NULL, // default security attributes
+ TUN_MAX_BUFFER_COUNT, // initial count
+ TUN_MAX_BUFFER_COUNT, // maximum count
+ NULL); // unnamed semaphore
+
+ if(!overlapped->free_list_semaphore) {
+ fprintf(stderr, "error creating free list semaphore!\n");
+ }
+
+ overlapped->free_list = overlapped->output_queue_front = overlapped->output_queue_back = NULL;
+
+ {
+ unsigned index;
+ for(index = 0; index < TUN_MAX_BUFFER_COUNT; index++) {
+ tun_buffer_t* element = &overlapped->buffers[index];
+ element->next = overlapped->free_list;
+ overlapped->free_list = element;
+ }
+ }
+ /* To count buffers, initially no-signal. */
+ overlapped->tap_semaphore = CreateSemaphore(NULL, 0, TUN_MAX_BUFFER_COUNT, NULL);
+ if(!overlapped->tap_semaphore)
+ fprintf(stderr, "error creating tap_semaphore.\n");
+}
+
+static int tap_win32_write(tap_win32_overlapped_t *overlapped,
+ const void *buffer, unsigned long size)
+{
+ unsigned long write_size;
+ BOOL result;
+ DWORD error;
+
+ result = GetOverlappedResult( overlapped->handle, &overlapped->write_overlapped,
+ &write_size, FALSE);
+
+ if (!result && GetLastError() == ERROR_IO_INCOMPLETE)
+ WaitForSingleObject(overlapped->write_event, INFINITE);
+
+ result = WriteFile(overlapped->handle, buffer, size,
+ &write_size, &overlapped->write_overlapped);
+
+ if (!result) {
+ switch (error = GetLastError())
+ {
+ case ERROR_IO_PENDING:
+#ifndef TUN_ASYNCHRONOUS_WRITES
+ WaitForSingleObject(overlapped->write_event, INFINITE);
+#endif
+ break;
+ default:
+ return -1;
+ }
+ }
+
+ return write_size;
+}
+
+static DWORD WINAPI tap_win32_thread_entry(LPVOID param)
+{
+ tap_win32_overlapped_t *overlapped = (tap_win32_overlapped_t*)param;
+ unsigned long read_size;
+ BOOL result;
+ DWORD dwError;
+ tun_buffer_t* buffer = get_buffer_from_free_list(overlapped);
+
+
+ for (;;) {
+ result = ReadFile(overlapped->handle,
+ buffer->buffer,
+ sizeof(buffer->buffer),
+ &read_size,
+ &overlapped->read_overlapped);
+ if (!result) {
+ dwError = GetLastError();
+ if (dwError == ERROR_IO_PENDING) {
+ WaitForSingleObject(overlapped->read_event, INFINITE);
+ result = GetOverlappedResult( overlapped->handle, &overlapped->read_overlapped,
+ &read_size, FALSE);
+ if (!result) {
+#ifdef DEBUG_TAP_WIN32
+ LPVOID lpBuffer;
+ dwError = GetLastError();
+ FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) & lpBuffer, 0, NULL );
+ fprintf(stderr, "Tap-Win32: Error GetOverlappedResult %d - %s\n", dwError, lpBuffer);
+ LocalFree( lpBuffer );
+#endif
+ }
+ } else {
+#ifdef DEBUG_TAP_WIN32
+ LPVOID lpBuffer;
+ FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) & lpBuffer, 0, NULL );
+ fprintf(stderr, "Tap-Win32: Error ReadFile %d - %s\n", dwError, lpBuffer);
+ LocalFree( lpBuffer );
+#endif
+ }
+ }
+
+ if(read_size > 0) {
+ buffer->read_size = read_size;
+ put_buffer_on_output_queue(overlapped, buffer);
+ ReleaseSemaphore(overlapped->tap_semaphore, 1, NULL);
+ buffer = get_buffer_from_free_list(overlapped);
+ }
+ }
+
+ return 0;
+}
+
+static int tap_win32_read(tap_win32_overlapped_t *overlapped,
+ uint8_t **pbuf, int max_size)
+{
+ int size = 0;
+
+ tun_buffer_t* buffer = get_buffer_from_output_queue_immediate(overlapped);
+
+ if(buffer != NULL) {
+ *pbuf = buffer->buffer;
+ size = (int)buffer->read_size;
+ if(size > max_size) {
+ size = max_size;
+ }
+ }
+
+ return size;
+}
+
+static void tap_win32_free_buffer(tap_win32_overlapped_t *overlapped,
+ uint8_t *pbuf)
+{
+ tun_buffer_t* buffer = (tun_buffer_t*)pbuf;
+ put_buffer_on_free_list(overlapped, buffer);
+}
+
+static int tap_win32_open(tap_win32_overlapped_t **phandle,
+ const char *prefered_name)
+{
+ char device_path[256];
+ char device_guid[0x100];
+ int rc;
+ HANDLE handle;
+ BOOL bret;
+ char name_buffer[0x100] = {0, };
+ struct {
+ unsigned long major;
+ unsigned long minor;
+ unsigned long debug;
+ } version;
+ DWORD version_len;
+ DWORD idThread;
+
+ if (prefered_name != NULL)
+ snprintf(name_buffer, sizeof(name_buffer), "%s", prefered_name);
+
+ rc = get_device_guid(device_guid, sizeof(device_guid), name_buffer, sizeof(name_buffer));
+ if (rc)
+ return -1;
+
+ snprintf (device_path, sizeof(device_path), "%s%s%s",
+ USERMODEDEVICEDIR,
+ device_guid,
+ TAPSUFFIX);
+#ifndef CONFIG_MARU
+ handle = CreateFile (
+ device_path,
+ GENERIC_READ | GENERIC_WRITE,
+ 0,
+ 0,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
+ 0 );
+#else
+ handle = CreateFile (
+ g_win32_locale_filename_from_utf8(device_path),
+ GENERIC_READ | GENERIC_WRITE,
+ 0,
+ 0,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
+ 0 );
+
+#endif
+ if (handle == INVALID_HANDLE_VALUE) {
+ return -1;
+ }
+
+ bret = DeviceIoControl(handle, TAP_IOCTL_GET_VERSION,
+ &version, sizeof (version),
+ &version, sizeof (version), &version_len, NULL);
+
+ if (bret == FALSE) {
+ CloseHandle(handle);
+ return -1;
+ }
+
+ if (!tap_win32_set_status(handle, TRUE)) {
+ return -1;
+ }
+
+ tap_win32_overlapped_init(&tap_overlapped, handle);
+
+ *phandle = &tap_overlapped;
+
+ CreateThread(NULL, 0, tap_win32_thread_entry,
+ (LPVOID)&tap_overlapped, 0, &idThread);
+ return 0;
+}
+
+/********************************************/
+
+ typedef struct TAPState {
+ NetClientState nc;
+ tap_win32_overlapped_t *handle;
+ } TAPState;
+
+static void tap_cleanup(NetClientState *nc)
+{
+ TAPState *s = DO_UPCAST(TAPState, nc, nc);
+
+ qemu_del_wait_object(s->handle->tap_semaphore, NULL, NULL);
+
+ /* FIXME: need to kill thread and close file handle:
+ tap_win32_close(s);
+ */
+}
+
+static ssize_t tap_receive(NetClientState *nc, const uint8_t *buf, size_t size)
+{
+ TAPState *s = DO_UPCAST(TAPState, nc, nc);
+
+ return tap_win32_write(s->handle, buf, size);
+}
+
+static void tap_win32_send(void *opaque)
+{
+ TAPState *s = opaque;
+ uint8_t *buf;
+ int max_size = 4096;
+ int size;
+
+ size = tap_win32_read(s->handle, &buf, max_size);
+ if (size > 0) {
+ qemu_send_packet(&s->nc, buf, size);
+ tap_win32_free_buffer(s->handle, buf);
+ }
+}
+
+static NetClientInfo net_tap_win32_info = {
+ .type = NET_CLIENT_OPTIONS_KIND_TAP,
+ .size = sizeof(TAPState),
+ .receive = tap_receive,
+ .cleanup = tap_cleanup,
+};
+
+static int tap_win32_init(NetClientState *peer, const char *model,
+ const char *name, const char *ifname)
+{
+ NetClientState *nc;
+ TAPState *s;
+ tap_win32_overlapped_t *handle;
+
+ if (tap_win32_open(&handle, ifname) < 0) {
+ printf("tap: Could not open '%s'\n", ifname);
+ return -1;
+ }
+
+ nc = qemu_new_net_client(&net_tap_win32_info, peer, model, name);
+
+ s = DO_UPCAST(TAPState, nc, nc);
+
+ snprintf(s->nc.info_str, sizeof(s->nc.info_str),
+ "tap: ifname=%s", ifname);
+
+ s->handle = handle;
+
+ qemu_add_wait_object(s->handle->tap_semaphore, tap_win32_send, s);
+
+ return 0;
+}
+
+int net_init_tap(const NetClientOptions *opts, const char *name,
+ NetClientState *peer)
+{
+ const NetdevTapOptions *tap;
+
+ assert(opts->kind == NET_CLIENT_OPTIONS_KIND_TAP);
+ tap = opts->tap;
+
+ if (!tap->has_ifname) {
+ error_report("tap: no interface name");
+ return -1;
+ }
+
+ if (tap_win32_init(peer, "tap", name, tap->ifname) == -1) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int tap_has_ufo(NetClientState *nc)
+{
+ return 0;
+}
+
+int tap_has_vnet_hdr(NetClientState *nc)
+{
+ return 0;
+}
+
+int tap_probe_vnet_hdr_len(int fd, int len)
+{
+ return 0;
+}
+
+void tap_fd_set_vnet_hdr_len(int fd, 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)
+{
+}
+
+struct vhost_net *tap_get_vhost_net(NetClientState *nc)
+{
+ return NULL;
+}
diff --git a/net/tap.c b/net/tap.c
new file mode 100644
index 0000000000..1971525794
--- /dev/null
+++ b/net/tap.c
@@ -0,0 +1,716 @@
+/*
+ * QEMU System Emulator
+ *
+ * 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.
+ */
+
+#include "net/tap.h"
+
+#include "config-host.h"
+
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <net/if.h>
+
+#include "net.h"
+#include "monitor.h"
+#include "sysemu.h"
+#include "qemu-char.h"
+#include "qemu-common.h"
+#include "qemu-error.h"
+
+#include "net/tap-linux.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)
+
+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;
+ VHostNetState *vhost_net;
+ unsigned host_vnet_hdr_len;
+} TAPState;
+
+static int launch_script(const char *setup_script, const char *ifname, int fd);
+
+static int tap_can_send(void *opaque);
+static void tap_send(void *opaque);
+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);
+}
+
+static void tap_read_poll(TAPState *s, int enable)
+{
+ s->read_poll = !!enable;
+ tap_update_fd_handler(s);
+}
+
+static void tap_write_poll(TAPState *s, int enable)
+{
+ s->write_poll = !!enable;
+ tap_update_fd_handler(s);
+}
+
+static void tap_writable(void *opaque)
+{
+ TAPState *s = opaque;
+
+ tap_write_poll(s, 0);
+
+ qemu_flush_queued_packets(&s->nc);
+}
+
+static ssize_t tap_write_packet(TAPState *s, const struct iovec *iov, int iovcnt)
+{
+ ssize_t len;
+
+ do {
+ len = writev(s->fd, iov, iovcnt);
+ } while (len == -1 && errno == EINTR);
+
+ if (len == -1 && errno == EAGAIN) {
+ tap_write_poll(s, 1);
+ return 0;
+ }
+
+ return len;
+}
+
+static ssize_t tap_receive_iov(NetClientState *nc, const struct iovec *iov,
+ int iovcnt)
+{
+ TAPState *s = DO_UPCAST(TAPState, nc, nc);
+ const struct iovec *iovp = iov;
+ struct iovec iov_copy[iovcnt + 1];
+ struct virtio_net_hdr_mrg_rxbuf hdr = { };
+
+ if (s->host_vnet_hdr_len && !s->using_vnet_hdr) {
+ iov_copy[0].iov_base = &hdr;
+ iov_copy[0].iov_len = s->host_vnet_hdr_len;
+ memcpy(&iov_copy[1], iov, iovcnt * sizeof(*iov));
+ iovp = iov_copy;
+ iovcnt++;
+ }
+
+ return tap_write_packet(s, iovp, iovcnt);
+}
+
+static ssize_t tap_receive_raw(NetClientState *nc, const uint8_t *buf, size_t size)
+{
+ TAPState *s = DO_UPCAST(TAPState, nc, nc);
+ struct iovec iov[2];
+ int iovcnt = 0;
+ struct virtio_net_hdr_mrg_rxbuf hdr = { };
+
+ if (s->host_vnet_hdr_len) {
+ iov[iovcnt].iov_base = &hdr;
+ iov[iovcnt].iov_len = s->host_vnet_hdr_len;
+ iovcnt++;
+ }
+
+ iov[iovcnt].iov_base = (char *)buf;
+ iov[iovcnt].iov_len = size;
+ iovcnt++;
+
+ return tap_write_packet(s, iov, iovcnt);
+}
+
+static ssize_t tap_receive(NetClientState *nc, const uint8_t *buf, size_t size)
+{
+ TAPState *s = DO_UPCAST(TAPState, nc, nc);
+ struct iovec iov[1];
+
+ if (s->host_vnet_hdr_len && !s->using_vnet_hdr) {
+ return tap_receive_raw(nc, buf, size);
+ }
+
+ iov[0].iov_base = (char *)buf;
+ iov[0].iov_len = size;
+
+ return tap_write_packet(s, iov, 1);
+}
+
+static int tap_can_send(void *opaque)
+{
+ TAPState *s = opaque;
+
+ return qemu_can_send_packet(&s->nc);
+}
+
+#ifndef __sun__
+ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen)
+{
+ return read(tapfd, buf, maxlen);
+}
+#endif
+
+static void tap_send_completed(NetClientState *nc, ssize_t len)
+{
+ TAPState *s = DO_UPCAST(TAPState, nc, nc);
+ tap_read_poll(s, 1);
+}
+
+static void tap_send(void *opaque)
+{
+ TAPState *s = opaque;
+ int size;
+
+ do {
+ uint8_t *buf = s->buf;
+
+ size = tap_read_packet(s->fd, s->buf, sizeof(s->buf));
+ if (size <= 0) {
+ break;
+ }
+
+ if (s->host_vnet_hdr_len && !s->using_vnet_hdr) {
+ buf += s->host_vnet_hdr_len;
+ size -= s->host_vnet_hdr_len;
+ }
+
+ size = qemu_send_packet_async(&s->nc, buf, size, tap_send_completed);
+ if (size == 0) {
+ tap_read_poll(s, 0);
+ }
+ } while (size > 0 && qemu_can_send_packet(&s->nc));
+}
+
+int tap_has_ufo(NetClientState *nc)
+{
+ TAPState *s = DO_UPCAST(TAPState, nc, nc);
+
+ assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
+
+ return s->has_ufo;
+}
+
+int tap_has_vnet_hdr(NetClientState *nc)
+{
+ TAPState *s = DO_UPCAST(TAPState, nc, nc);
+
+ assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
+
+ return !!s->host_vnet_hdr_len;
+}
+
+int tap_has_vnet_hdr_len(NetClientState *nc, int len)
+{
+ TAPState *s = DO_UPCAST(TAPState, nc, nc);
+
+ assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
+
+ return tap_probe_vnet_hdr_len(s->fd, len);
+}
+
+void tap_set_vnet_hdr_len(NetClientState *nc, int len)
+{
+ TAPState *s = DO_UPCAST(TAPState, nc, nc);
+
+ assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
+ assert(len == sizeof(struct virtio_net_hdr_mrg_rxbuf) ||
+ len == sizeof(struct virtio_net_hdr));
+
+ tap_fd_set_vnet_hdr_len(s->fd, len);
+ s->host_vnet_hdr_len = len;
+}
+
+void tap_using_vnet_hdr(NetClientState *nc, int 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);
+
+ s->using_vnet_hdr = using_vnet_hdr;
+}
+
+void tap_set_offload(NetClientState *nc, int csum, int tso4,
+ int tso6, int ecn, int ufo)
+{
+ TAPState *s = DO_UPCAST(TAPState, nc, nc);
+ if (s->fd < 0) {
+ return;
+ }
+
+ tap_fd_set_offload(s->fd, csum, tso4, tso6, ecn, ufo);
+}
+
+static void tap_cleanup(NetClientState *nc)
+{
+ TAPState *s = DO_UPCAST(TAPState, nc, nc);
+
+ if (s->vhost_net) {
+ vhost_net_cleanup(s->vhost_net);
+ s->vhost_net = NULL;
+ }
+
+ qemu_purge_queued_packets(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);
+ close(s->fd);
+ s->fd = -1;
+}
+
+static void tap_poll(NetClientState *nc, bool enable)
+{
+ TAPState *s = DO_UPCAST(TAPState, nc, nc);
+ tap_read_poll(s, enable);
+ tap_write_poll(s, enable);
+}
+
+int tap_get_fd(NetClientState *nc)
+{
+ TAPState *s = DO_UPCAST(TAPState, nc, nc);
+ assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
+ return s->fd;
+}
+
+/* fd support */
+
+static NetClientInfo net_tap_info = {
+ .type = NET_CLIENT_OPTIONS_KIND_TAP,
+ .size = sizeof(TAPState),
+ .receive = tap_receive,
+ .receive_raw = tap_receive_raw,
+ .receive_iov = tap_receive_iov,
+ .poll = tap_poll,
+ .cleanup = tap_cleanup,
+};
+
+static TAPState *net_tap_fd_init(NetClientState *peer,
+ const char *model,
+ const char *name,
+ int fd,
+ int vnet_hdr)
+{
+ NetClientState *nc;
+ TAPState *s;
+
+ nc = qemu_new_net_client(&net_tap_info, peer, model, name);
+
+ s = DO_UPCAST(TAPState, nc, nc);
+
+ s->fd = fd;
+ s->host_vnet_hdr_len = vnet_hdr ? sizeof(struct virtio_net_hdr) : 0;
+ s->using_vnet_hdr = 0;
+ s->has_ufo = tap_probe_has_ufo(s->fd);
+ tap_set_offload(&s->nc, 0, 0, 0, 0, 0);
+ tap_read_poll(s, 1);
+ s->vhost_net = NULL;
+ return s;
+}
+
+static int launch_script(const char *setup_script, const char *ifname, int fd)
+{
+ int pid, status;
+ char *args[3];
+ char **parg;
+
+ /* try to launch network script */
+ pid = fork();
+ if (pid == 0) {
+ int open_max = sysconf(_SC_OPEN_MAX), i;
+
+ for (i = 0; i < open_max; i++) {
+ if (i != STDIN_FILENO &&
+ i != STDOUT_FILENO &&
+ i != STDERR_FILENO &&
+ i != fd) {
+ close(i);
+ }
+ }
+ parg = args;
+ *parg++ = (char *)setup_script;
+ *parg++ = (char *)ifname;
+ *parg = NULL;
+ execv(setup_script, args);
+ _exit(1);
+ } else if (pid > 0) {
+ while (waitpid(pid, &status, 0) != pid) {
+ /* loop */
+ }
+
+ if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
+ return 0;
+ }
+ }
+ fprintf(stderr, "%s: could not launch network script\n", setup_script);
+ return -1;
+}
+
+static int recv_fd(int c)
+{
+ int fd;
+ uint8_t msgbuf[CMSG_SPACE(sizeof(fd))];
+ struct msghdr msg = {
+ .msg_control = msgbuf,
+ .msg_controllen = sizeof(msgbuf),
+ };
+ struct cmsghdr *cmsg;
+ struct iovec iov;
+ uint8_t req[1];
+ ssize_t len;
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
+ msg.msg_controllen = cmsg->cmsg_len;
+
+ iov.iov_base = req;
+ iov.iov_len = sizeof(req);
+
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ len = recvmsg(c, &msg, 0);
+ if (len > 0) {
+ memcpy(&fd, CMSG_DATA(cmsg), sizeof(fd));
+ return fd;
+ }
+
+ return len;
+}
+
+static int net_bridge_run_helper(const char *helper, const char *bridge)
+{
+ sigset_t oldmask, mask;
+ int pid, status;
+ char *args[5];
+ char **parg;
+ int sv[2];
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGCHLD);
+ sigprocmask(SIG_BLOCK, &mask, &oldmask);
+
+ if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) == -1) {
+ return -1;
+ }
+
+ /* try to launch bridge helper */
+ pid = fork();
+ if (pid == 0) {
+ int open_max = sysconf(_SC_OPEN_MAX), i;
+ char fd_buf[6+10];
+ char br_buf[6+IFNAMSIZ] = {0};
+ char helper_cmd[PATH_MAX + sizeof(fd_buf) + sizeof(br_buf) + 15];
+
+ for (i = 0; i < open_max; i++) {
+ if (i != STDIN_FILENO &&
+ i != STDOUT_FILENO &&
+ i != STDERR_FILENO &&
+ i != sv[1]) {
+ close(i);
+ }
+ }
+
+ snprintf(fd_buf, sizeof(fd_buf), "%s%d", "--fd=", sv[1]);
+
+ if (strrchr(helper, ' ') || strrchr(helper, '\t')) {
+ /* assume helper is a command */
+
+ if (strstr(helper, "--br=") == NULL) {
+ snprintf(br_buf, sizeof(br_buf), "%s%s", "--br=", bridge);
+ }
+
+ snprintf(helper_cmd, sizeof(helper_cmd), "%s %s %s %s",
+ helper, "--use-vnet", fd_buf, br_buf);
+
+ parg = args;
+ *parg++ = (char *)"sh";
+ *parg++ = (char *)"-c";
+ *parg++ = helper_cmd;
+ *parg++ = NULL;
+
+ execv("/bin/sh", args);
+ } else {
+ /* assume helper is just the executable path name */
+
+ snprintf(br_buf, sizeof(br_buf), "%s%s", "--br=", bridge);
+
+ parg = args;
+ *parg++ = (char *)helper;
+ *parg++ = (char *)"--use-vnet";
+ *parg++ = fd_buf;
+ *parg++ = br_buf;
+ *parg++ = NULL;
+
+ execv(helper, args);
+ }
+ _exit(1);
+
+ } else if (pid > 0) {
+ int fd;
+
+ close(sv[1]);
+
+ do {
+ fd = recv_fd(sv[0]);
+ } while (fd == -1 && errno == EINTR);
+
+ close(sv[0]);
+
+ while (waitpid(pid, &status, 0) != pid) {
+ /* loop */
+ }
+ sigprocmask(SIG_SETMASK, &oldmask, NULL);
+ if (fd < 0) {
+ fprintf(stderr, "failed to recv file descriptor\n");
+ return -1;
+ }
+
+ if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
+ return fd;
+ }
+ }
+ fprintf(stderr, "failed to launch bridge helper\n");
+ return -1;
+}
+
+int net_init_bridge(const NetClientOptions *opts, const char *name,
+ NetClientState *peer)
+{
+ const NetdevBridgeOptions *bridge;
+ const char *helper, *br;
+
+ TAPState *s;
+ int fd, vnet_hdr;
+
+ assert(opts->kind == NET_CLIENT_OPTIONS_KIND_BRIDGE);
+ bridge = opts->bridge;
+
+ helper = bridge->has_helper ? bridge->helper : DEFAULT_BRIDGE_HELPER;
+ br = bridge->has_br ? bridge->br : DEFAULT_BRIDGE_INTERFACE;
+
+ fd = net_bridge_run_helper(helper, br);
+ if (fd == -1) {
+ return -1;
+ }
+
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+
+ vnet_hdr = tap_probe_vnet_hdr(fd);
+
+ s = net_tap_fd_init(peer, "bridge", name, fd, vnet_hdr);
+ if (!s) {
+ close(fd);
+ return -1;
+ }
+
+ snprintf(s->nc.info_str, sizeof(s->nc.info_str), "helper=%s,br=%s", helper,
+ br);
+
+ return 0;
+}
+
+static int net_tap_init(const NetdevTapOptions *tap, int *vnet_hdr,
+ const char *setup_script, char *ifname,
+ size_t ifname_sz)
+{
+ 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;
+ } else {
+ *vnet_hdr = 1;
+ vnet_hdr_required = 0;
+ }
+
+ TFR(fd = tap_open(ifname, ifname_sz, vnet_hdr, vnet_hdr_required));
+ if (fd < 0) {
+ return -1;
+ }
+
+ if (setup_script &&
+ setup_script[0] != '\0' &&
+ strcmp(setup_script, "no") != 0 &&
+ launch_script(setup_script, ifname, fd)) {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+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;
+
+ /* for the no-fd, no-helper case */
+ const char *script = NULL; /* suppress wrong "uninit'd use" gcc warning */
+ char ifname[128];
+
+ assert(opts->kind == NET_CLIENT_OPTIONS_KIND_TAP);
+ tap = opts->tap;
+
+ if (tap->has_fd) {
+ if (tap->has_ifname || tap->has_script || tap->has_downscript ||
+ tap->has_vnet_hdr || tap->has_helper) {
+ error_report("ifname=, script=, downscript=, vnet_hdr=, "
+ "and helper= are invalid with fd=");
+ return -1;
+ }
+
+ fd = net_handle_fd_param(cur_mon, tap->fd);
+ if (fd == -1) {
+ return -1;
+ }
+
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+
+ vnet_hdr = tap_probe_vnet_hdr(fd);
+
+ model = "tap";
+
+ } else if (tap->has_helper) {
+ if (tap->has_ifname || tap->has_script || tap->has_downscript ||
+ tap->has_vnet_hdr) {
+ error_report("ifname=, script=, downscript=, and vnet_hdr= "
+ "are invalid with helper=");
+ return -1;
+ }
+
+ fd = net_bridge_run_helper(tap->helper, DEFAULT_BRIDGE_INTERFACE);
+ if (fd == -1) {
+ return -1;
+ }
+
+ 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) {
+ 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;
+
+ 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);
+
+ 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 :
+ tap->has_vhostfd || (tap->has_vhostforce && tap->vhostforce)) {
+ int vhostfd;
+
+ if (tap->has_vhostfd) {
+ vhostfd = net_handle_fd_param(cur_mon, tap->vhostfd);
+ 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) {
+ error_report("vhostfd= is not valid without vhost");
+ return -1;
+ }
+
+ return 0;
+}
+
+VHostNetState *tap_get_vhost_net(NetClientState *nc)
+{
+ TAPState *s = DO_UPCAST(TAPState, nc, nc);
+ assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
+ return s->vhost_net;
+}
diff --git a/net/tap.h b/net/tap.h
new file mode 100644
index 0000000000..0fb018c4b7
--- /dev/null
+++ b/net/tap.h
@@ -0,0 +1,64 @@
+/*
+ * QEMU System Emulator
+ *
+ * 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_TAP_H
+#define QEMU_NET_TAP_H
+
+#include "qemu-common.h"
+#include "qapi-types.h"
+
+#define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup"
+#define DEFAULT_NETWORK_DOWN_SCRIPT "/etc/qemu-ifdown"
+
+int net_init_tap(const NetClientOptions *opts, const char *name,
+ NetClientState *peer);
+
+int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_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_get_fd(NetClientState *nc);
+
+struct vhost_net;
+struct vhost_net *tap_get_vhost_net(NetClientState *nc);
+
+int net_init_bridge(const NetClientOptions *opts, const char *name,
+ NetClientState *peer);
+
+#endif /* QEMU_NET_TAP_H */
diff --git a/net/util.c b/net/util.c
new file mode 100644
index 0000000000..1e9afbc1ae
--- /dev/null
+++ b/net/util.c
@@ -0,0 +1,60 @@
+/*
+ * 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 "net/util.h"
+#include <errno.h>
+#include <stdlib.h>
+
+int net_parse_macaddr(uint8_t *macaddr, const char *p)
+{
+ int i;
+ char *last_char;
+ long int offset;
+
+ errno = 0;
+ offset = strtol(p, &last_char, 0);
+ if (errno == 0 && *last_char == '\0' &&
+ offset >= 0 && offset <= 0xFFFFFF) {
+ macaddr[3] = (offset & 0xFF0000) >> 16;
+ macaddr[4] = (offset & 0xFF00) >> 8;
+ macaddr[5] = offset & 0xFF;
+ return 0;
+ }
+
+ for (i = 0; i < 6; i++) {
+ macaddr[i] = strtol(p, (char **)&p, 16);
+ if (i == 5) {
+ if (*p != '\0') {
+ return -1;
+ }
+ } else {
+ if (*p != ':' && *p != '-') {
+ return -1;
+ }
+ p++;
+ }
+ }
+
+ return 0;
+}
diff --git a/net/util.h b/net/util.h
new file mode 100644
index 0000000000..10c7da95f0
--- /dev/null
+++ b/net/util.h
@@ -0,0 +1,32 @@
+/*
+ * 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_UTIL_H
+#define QEMU_NET_UTIL_H
+
+#include <stdint.h>
+
+int net_parse_macaddr(uint8_t *macaddr, const char *p);
+
+#endif /* QEMU_NET_UTIL_H */
diff --git a/net/vde.c b/net/vde.c
new file mode 100644
index 0000000000..b91a6c799b
--- /dev/null
+++ b/net/vde.c
@@ -0,0 +1,128 @@
+/*
+ * 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 "net/vde.h"
+
+#include "config-host.h"
+
+#include <libvdeplug.h>
+
+#include "net.h"
+#include "qemu-char.h"
+#include "qemu-common.h"
+#include "qemu-option.h"
+
+typedef struct VDEState {
+ NetClientState nc;
+ VDECONN *vde;
+} VDEState;
+
+static void vde_to_qemu(void *opaque)
+{
+ VDEState *s = opaque;
+ uint8_t buf[4096];
+ int size;
+
+ size = vde_recv(s->vde, (char *)buf, sizeof(buf), 0);
+ if (size > 0) {
+ qemu_send_packet(&s->nc, buf, size);
+ }
+}
+
+static ssize_t vde_receive(NetClientState *nc, const uint8_t *buf, size_t size)
+{
+ VDEState *s = DO_UPCAST(VDEState, nc, nc);
+ ssize_t ret;
+
+ do {
+ ret = vde_send(s->vde, (const char *)buf, size, 0);
+ } while (ret < 0 && errno == EINTR);
+
+ return ret;
+}
+
+static void vde_cleanup(NetClientState *nc)
+{
+ VDEState *s = DO_UPCAST(VDEState, nc, nc);
+ qemu_set_fd_handler(vde_datafd(s->vde), NULL, NULL, NULL);
+ vde_close(s->vde);
+}
+
+static NetClientInfo net_vde_info = {
+ .type = NET_CLIENT_OPTIONS_KIND_VDE,
+ .size = sizeof(VDEState),
+ .receive = vde_receive,
+ .cleanup = vde_cleanup,
+};
+
+static int net_vde_init(NetClientState *peer, const char *model,
+ const char *name, const char *sock,
+ int port, const char *group, int mode)
+{
+ NetClientState *nc;
+ VDEState *s;
+ VDECONN *vde;
+ char *init_group = (char *)group;
+ char *init_sock = (char *)sock;
+
+ struct vde_open_args args = {
+ .port = port,
+ .group = init_group,
+ .mode = mode,
+ };
+
+ vde = vde_open(init_sock, (char *)"QEMU", &args);
+ if (!vde){
+ return -1;
+ }
+
+ nc = qemu_new_net_client(&net_vde_info, peer, model, name);
+
+ snprintf(nc->info_str, sizeof(nc->info_str), "sock=%s,fd=%d",
+ sock, vde_datafd(vde));
+
+ s = DO_UPCAST(VDEState, nc, nc);
+
+ s->vde = vde;
+
+ qemu_set_fd_handler(vde_datafd(s->vde), vde_to_qemu, NULL, s);
+
+ return 0;
+}
+
+int net_init_vde(const NetClientOptions *opts, const char *name,
+ NetClientState *peer)
+{
+ const NetdevVdeOptions *vde;
+
+ assert(opts->kind == NET_CLIENT_OPTIONS_KIND_VDE);
+ vde = opts->vde;
+
+ /* missing optional values have been initialized to "all bits zero" */
+ if (net_vde_init(peer, "vde", name, vde->sock, vde->port, vde->group,
+ vde->has_mode ? vde->mode : 0700) == -1) {
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/net/vde.h b/net/vde.h
new file mode 100644
index 0000000000..6ce6698937
--- /dev/null
+++ b/net/vde.h
@@ -0,0 +1,37 @@
+/*
+ * 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_VDE_H
+#define QEMU_NET_VDE_H
+
+#include "qemu-common.h"
+#include "qapi-types.h"
+
+#ifdef CONFIG_VDE
+
+int net_init_vde(const NetClientOptions *opts, const char *name,
+ NetClientState *peer);
+
+#endif /* CONFIG_VDE */
+
+#endif /* QEMU_NET_VDE_H */