summary refs log tree commit diff stats
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/Makefile.objs2
-rw-r--r--net/dump.c49
-rw-r--r--net/dump.h5
-rw-r--r--net/hub.c339
-rw-r--r--net/hub.h29
-rw-r--r--net/queue.c38
-rw-r--r--net/queue.h25
-rw-r--r--net/slirp.c196
-rw-r--r--net/slirp.h4
-rw-r--r--net/socket.c246
-rw-r--r--net/socket.h5
-rw-r--r--net/tap-aix.c2
-rw-r--r--net/tap-bsd.c2
-rw-r--r--net/tap-haiku.c2
-rw-r--r--net/tap-linux.c9
-rw-r--r--net/tap-solaris.c2
-rw-r--r--net/tap-win32.c36
-rw-r--r--net/tap.c192
-rw-r--r--net/tap.h26
-rw-r--r--net/vde.c32
-rw-r--r--net/vde.h5
21 files changed, 801 insertions, 445 deletions
diff --git a/net/Makefile.objs b/net/Makefile.objs
index 72f50bc903..cf04187717 100644
--- a/net/Makefile.objs
+++ b/net/Makefile.objs
@@ -1,4 +1,4 @@
-common-obj-y = queue.o checksum.o util.o
+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
diff --git a/net/dump.c b/net/dump.c
index f835c51187..004231d481 100644
--- a/net/dump.c
+++ b/net/dump.c
@@ -27,9 +27,10 @@
 #include "qemu-error.h"
 #include "qemu-log.h"
 #include "qemu-timer.h"
+#include "hub.h"
 
 typedef struct DumpState {
-    VLANClientState nc;
+    NetClientState nc;
     int64_t start_ts;
     int fd;
     int pcap_caplen;
@@ -56,7 +57,7 @@ struct pcap_sf_pkthdr {
     uint32_t len;
 };
 
-static ssize_t dump_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
+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;
@@ -85,7 +86,7 @@ static ssize_t dump_receive(VLANClientState *nc, const uint8_t *buf, size_t size
     return size;
 }
 
-static void dump_cleanup(VLANClientState *nc)
+static void dump_cleanup(NetClientState *nc)
 {
     DumpState *s = DO_UPCAST(DumpState, nc, nc);
 
@@ -93,17 +94,17 @@ static void dump_cleanup(VLANClientState *nc)
 }
 
 static NetClientInfo net_dump_info = {
-    .type = NET_CLIENT_TYPE_DUMP,
+    .type = NET_CLIENT_OPTIONS_KIND_DUMP,
     .size = sizeof(DumpState),
     .receive = dump_receive,
     .cleanup = dump_cleanup,
 };
 
-static int net_dump_init(VLANState *vlan, const char *device,
+static int net_dump_init(NetClientState *peer, const char *device,
                          const char *name, const char *filename, int len)
 {
     struct pcap_file_hdr hdr;
-    VLANClientState *nc;
+    NetClientState *nc;
     DumpState *s;
     struct tm tm;
     int fd;
@@ -128,7 +129,7 @@ static int net_dump_init(VLANState *vlan, const char *device,
         return -1;
     }
 
-    nc = qemu_new_net_client(&net_dump_info, vlan, NULL, device, name);
+    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);
@@ -144,21 +145,41 @@ static int net_dump_init(VLANState *vlan, const char *device,
     return 0;
 }
 
-int net_init_dump(QemuOpts *opts, const char *name, VLANState *vlan)
+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(vlan);
+    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_DUMP);
+    dump = opts->dump;
 
-    file = qemu_opt_get(opts, "file");
-    if (!file) {
-        snprintf(def_file, sizeof(def_file), "qemu-vlan%d.pcap", vlan->id);
+    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;
     }
 
-    len = qemu_opt_get_size(opts, "len", 65536);
+    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(vlan, "dump", name, file, len);
+    return net_dump_init(peer, "dump", name, file, len);
 }
diff --git a/net/dump.h b/net/dump.h
index 2b5d9ba644..33f152b460 100644
--- a/net/dump.h
+++ b/net/dump.h
@@ -25,8 +25,9 @@
 #define QEMU_NET_DUMP_H
 
 #include "net.h"
-#include "qemu-common.h"
+#include "qapi-types.h"
 
-int net_init_dump(QemuOpts *opts, const char *name, VLANState *vlan);
+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
index 1ab5247a32..e8030aafe4 100644
--- a/net/queue.c
+++ b/net/queue.c
@@ -23,6 +23,7 @@
 
 #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
@@ -40,7 +41,7 @@
 
 struct NetPacket {
     QTAILQ_ENTRY(NetPacket) entry;
-    VLANClientState *sender;
+    NetClientState *sender;
     unsigned flags;
     int size;
     NetPacketSent *sent_cb;
@@ -48,8 +49,6 @@ struct NetPacket {
 };
 
 struct NetQueue {
-    NetPacketDeliver *deliver;
-    NetPacketDeliverIOV *deliver_iov;
     void *opaque;
 
     QTAILQ_HEAD(packets, NetPacket) packets;
@@ -57,16 +56,12 @@ struct NetQueue {
     unsigned delivering : 1;
 };
 
-NetQueue *qemu_new_net_queue(NetPacketDeliver *deliver,
-                             NetPacketDeliverIOV *deliver_iov,
-                             void *opaque)
+NetQueue *qemu_new_net_queue(void *opaque)
 {
     NetQueue *queue;
 
     queue = g_malloc0(sizeof(NetQueue));
 
-    queue->deliver = deliver;
-    queue->deliver_iov = deliver_iov;
     queue->opaque = opaque;
 
     QTAILQ_INIT(&queue->packets);
@@ -89,7 +84,7 @@ void qemu_del_net_queue(NetQueue *queue)
 }
 
 static ssize_t qemu_net_queue_append(NetQueue *queue,
-                                     VLANClientState *sender,
+                                     NetClientState *sender,
                                      unsigned flags,
                                      const uint8_t *buf,
                                      size_t size,
@@ -110,7 +105,7 @@ static ssize_t qemu_net_queue_append(NetQueue *queue,
 }
 
 static ssize_t qemu_net_queue_append_iov(NetQueue *queue,
-                                         VLANClientState *sender,
+                                         NetClientState *sender,
                                          unsigned flags,
                                          const struct iovec *iov,
                                          int iovcnt,
@@ -143,7 +138,7 @@ static ssize_t qemu_net_queue_append_iov(NetQueue *queue,
 }
 
 static ssize_t qemu_net_queue_deliver(NetQueue *queue,
-                                      VLANClientState *sender,
+                                      NetClientState *sender,
                                       unsigned flags,
                                       const uint8_t *data,
                                       size_t size)
@@ -151,14 +146,14 @@ static ssize_t qemu_net_queue_deliver(NetQueue *queue,
     ssize_t ret = -1;
 
     queue->delivering = 1;
-    ret = queue->deliver(sender, flags, data, size, queue->opaque);
+    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,
-                                          VLANClientState *sender,
+                                          NetClientState *sender,
                                           unsigned flags,
                                           const struct iovec *iov,
                                           int iovcnt)
@@ -166,14 +161,14 @@ static ssize_t qemu_net_queue_deliver_iov(NetQueue *queue,
     ssize_t ret = -1;
 
     queue->delivering = 1;
-    ret = queue->deliver_iov(sender, flags, iov, iovcnt, queue->opaque);
+    ret = qemu_deliver_packet_iov(sender, flags, iov, iovcnt, queue->opaque);
     queue->delivering = 0;
 
     return ret;
 }
 
 ssize_t qemu_net_queue_send(NetQueue *queue,
-                            VLANClientState *sender,
+                            NetClientState *sender,
                             unsigned flags,
                             const uint8_t *data,
                             size_t size,
@@ -181,8 +176,8 @@ ssize_t qemu_net_queue_send(NetQueue *queue,
 {
     ssize_t ret;
 
-    if (queue->delivering) {
-        return qemu_net_queue_append(queue, sender, flags, data, size, NULL);
+    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);
@@ -197,7 +192,7 @@ ssize_t qemu_net_queue_send(NetQueue *queue,
 }
 
 ssize_t qemu_net_queue_send_iov(NetQueue *queue,
-                                VLANClientState *sender,
+                                NetClientState *sender,
                                 unsigned flags,
                                 const struct iovec *iov,
                                 int iovcnt,
@@ -205,8 +200,9 @@ ssize_t qemu_net_queue_send_iov(NetQueue *queue,
 {
     ssize_t ret;
 
-    if (queue->delivering) {
-        return qemu_net_queue_append_iov(queue, sender, flags, iov, iovcnt, NULL);
+    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);
@@ -220,7 +216,7 @@ ssize_t qemu_net_queue_send_iov(NetQueue *queue,
     return ret;
 }
 
-void qemu_net_queue_purge(NetQueue *queue, VLANClientState *from)
+void qemu_net_queue_purge(NetQueue *queue, NetClientState *from)
 {
     NetPacket *packet, *next;
 
diff --git a/net/queue.h b/net/queue.h
index a31958e3c6..9d44a9b3b8 100644
--- a/net/queue.h
+++ b/net/queue.h
@@ -29,43 +29,30 @@
 typedef struct NetPacket NetPacket;
 typedef struct NetQueue NetQueue;
 
-typedef void (NetPacketSent) (VLANClientState *sender, ssize_t ret);
-
-typedef ssize_t (NetPacketDeliver) (VLANClientState *sender,
-                                    unsigned flags,
-                                    const uint8_t *buf,
-                                    size_t size,
-                                    void *opaque);
-
-typedef ssize_t (NetPacketDeliverIOV) (VLANClientState *sender,
-                                       unsigned flags,
-                                       const struct iovec *iov,
-                                       int iovcnt,
-                                       void *opaque);
+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(NetPacketDeliver *deliver,
-                             NetPacketDeliverIOV *deliver_iov,
-                             void *opaque);
+NetQueue *qemu_new_net_queue(void *opaque);
+
 void qemu_del_net_queue(NetQueue *queue);
 
 ssize_t qemu_net_queue_send(NetQueue *queue,
-                            VLANClientState *sender,
+                            NetClientState *sender,
                             unsigned flags,
                             const uint8_t *data,
                             size_t size,
                             NetPacketSent *sent_cb);
 
 ssize_t qemu_net_queue_send_iov(NetQueue *queue,
-                                VLANClientState *sender,
+                                NetClientState *sender,
                                 unsigned flags,
                                 const struct iovec *iov,
                                 int iovcnt,
                                 NetPacketSent *sent_cb);
 
-void qemu_net_queue_purge(NetQueue *queue, VLANClientState *from);
+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
index 37b6ccfde9..8db66ea539 100644
--- a/net/slirp.c
+++ b/net/slirp.c
@@ -26,9 +26,11 @@
 #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"
@@ -66,7 +68,7 @@ struct slirp_config_str {
 };
 
 typedef struct SlirpState {
-    VLANClientState nc;
+    NetClientState nc;
     QTAILQ_ENTRY(SlirpState) entry;
     Slirp *slirp;
 #ifndef _WIN32
@@ -95,13 +97,6 @@ static void slirp_smb_cleanup(SlirpState *s);
 static inline void slirp_smb_cleanup(SlirpState *s) { }
 #endif
 
-int slirp_can_output(void *opaque)
-{
-    SlirpState *s = opaque;
-
-    return qemu_can_send_packet(&s->nc);
-}
-
 void slirp_output(void *opaque, const uint8_t *pkt, int pkt_len)
 {
     SlirpState *s = opaque;
@@ -109,7 +104,7 @@ void slirp_output(void *opaque, const uint8_t *pkt, int pkt_len)
     qemu_send_packet(&s->nc, pkt, pkt_len);
 }
 
-static ssize_t net_slirp_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
+static ssize_t net_slirp_receive(NetClientState *nc, const uint8_t *buf, size_t size)
 {
     SlirpState *s = DO_UPCAST(SlirpState, nc, nc);
 
@@ -118,7 +113,7 @@ static ssize_t net_slirp_receive(VLANClientState *nc, const uint8_t *buf, size_t
     return size;
 }
 
-static void net_slirp_cleanup(VLANClientState *nc)
+static void net_slirp_cleanup(NetClientState *nc)
 {
     SlirpState *s = DO_UPCAST(SlirpState, nc, nc);
 
@@ -128,13 +123,13 @@ static void net_slirp_cleanup(VLANClientState *nc)
 }
 
 static NetClientInfo net_slirp_info = {
-    .type = NET_CLIENT_TYPE_USER,
+    .type = NET_CLIENT_OPTIONS_KIND_USER,
     .size = sizeof(SlirpState),
     .receive = net_slirp_receive,
     .cleanup = net_slirp_cleanup,
 };
 
-static int net_slirp_init(VLANState *vlan, const char *model,
+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,
@@ -151,7 +146,7 @@ static int net_slirp_init(VLANState *vlan, const char *model,
 #ifndef _WIN32
     struct in_addr smbsrv = { .s_addr = 0 };
 #endif
-    VLANClientState *nc;
+    NetClientState *nc;
     SlirpState *s;
     char buf[20];
     uint32_t addr;
@@ -237,7 +232,7 @@ static int net_slirp_init(VLANState *vlan, const char *model,
     }
 #endif
 
-    nc = qemu_new_net_client(&net_slirp_info, vlan, NULL, model, name);
+    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),
@@ -273,7 +268,7 @@ static int net_slirp_init(VLANState *vlan, const char *model,
     return 0;
 
 error:
-    qemu_del_vlan_client(nc);
+    qemu_del_net_client(nc);
     return -1;
 }
 
@@ -282,8 +277,8 @@ static SlirpState *slirp_lookup(Monitor *mon, const char *vlan,
 {
 
     if (vlan) {
-        VLANClientState *nc;
-        nc = qemu_find_vlan_client_by_name(mon, strtol(vlan, NULL, 0), stack);
+        NetClientState *nc;
+        nc = net_hub_find_client_by_name(strtol(vlan, NULL, 0), stack);
         if (!nc) {
             return NULL;
         }
@@ -487,8 +482,27 @@ static int slirp_smb(SlirpState* s, const char *exported_dir,
     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) {
@@ -517,14 +531,16 @@ static int slirp_smb(SlirpState* s, const char *exported_dir,
             "[qemu]\n"
             "path=%s\n"
             "read only=no\n"
-            "guest ok=yes\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
+            exported_dir,
+            passwd->pw_name
             );
     fclose(f);
 
@@ -616,25 +632,35 @@ static int slirp_guestfwd(SlirpState *s, const char *config_str,
 
     fwd = g_malloc(sizeof(struct GuestFwd));
     snprintf(buf, sizeof(buf), "guestfwd.tcp.%d", port);
-    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;
+    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;
+        }
 
-    qemu_chr_add_handlers(fwd->hd, guestfwd_can_read, guestfwd_read,
-                          NULL, fwd);
+        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:
@@ -647,95 +673,55 @@ 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",
-                       s->nc.vlan ? s->nc.vlan->id : -1,
+                       got_vlan_id ? id : -1,
                        s->nc.name);
         slirp_connection_info(s->slirp, mon);
     }
 }
 
-static int net_init_slirp_configs(const char *name, const char *value, void *opaque)
+static void
+net_init_slirp_configs(const StringList *fwd, int flags)
 {
-    struct slirp_config_str *config;
-
-    if (strcmp(name, "hostfwd") != 0 && strcmp(name, "guestfwd") != 0) {
-        return 0;
-    }
-
-    config = g_malloc0(sizeof(*config));
+    while (fwd) {
+        struct slirp_config_str *config;
 
-    pstrcpy(config->str, sizeof(config->str), value);
+        config = g_malloc0(sizeof(*config));
+        pstrcpy(config->str, sizeof(config->str), fwd->value->str);
+        config->flags = flags;
+        config->next = slirp_configs;
+        slirp_configs = config;
 
-    if (!strcmp(name, "hostfwd")) {
-        config->flags = SLIRP_CFG_HOSTFWD;
+        fwd = fwd->next;
     }
-
-    config->next = slirp_configs;
-    slirp_configs = config;
-
-    return 0;
 }
 
-int net_init_slirp(QemuOpts *opts, const char *name, VLANState *vlan)
+int net_init_slirp(const NetClientOptions *opts, const char *name,
+                   NetClientState *peer)
 {
     struct slirp_config_str *config;
-    const char *vhost;
-    const char *vhostname;
-    const char *vdhcp_start;
-    const char *vnamesrv;
-    const char *tftp_export;
-    const char *bootfile;
-    const char *smb_export;
-    const char *vsmbsrv;
-    const char *restrict_opt;
-    char *vnet = NULL;
-    int restricted = 0;
+    char *vnet;
     int ret;
+    const NetdevUserOptions *user;
 
-    vhost       = qemu_opt_get(opts, "host");
-    vhostname   = qemu_opt_get(opts, "hostname");
-    vdhcp_start = qemu_opt_get(opts, "dhcpstart");
-    vnamesrv    = qemu_opt_get(opts, "dns");
-    tftp_export = qemu_opt_get(opts, "tftp");
-    bootfile    = qemu_opt_get(opts, "bootfile");
-    smb_export  = qemu_opt_get(opts, "smb");
-    vsmbsrv     = qemu_opt_get(opts, "smbserver");
-
-    restrict_opt = qemu_opt_get(opts, "restrict");
-    if (restrict_opt) {
-        if (!strcmp(restrict_opt, "on") ||
-            !strcmp(restrict_opt, "yes") || !strcmp(restrict_opt, "y")) {
-            restricted = 1;
-        } else if (strcmp(restrict_opt, "off") &&
-            strcmp(restrict_opt, "no") && strcmp(restrict_opt, "n")) {
-            error_report("invalid option: 'restrict=%s'", restrict_opt);
-            return -1;
-        }
-    }
+    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_USER);
+    user = opts->user;
 
-    if (qemu_opt_get(opts, "ip")) {
-        const char *ip = qemu_opt_get(opts, "ip");
-        int l = strlen(ip) + strlen("/24") + 1;
+    vnet = user->has_net ? g_strdup(user->net) :
+           user->has_ip  ? g_strdup_printf("%s/24", user->ip) :
+           NULL;
 
-        vnet = g_malloc(l);
-
-        /* emulate legacy ip= parameter */
-        pstrcpy(vnet, l, ip);
-        pstrcat(vnet, l, "/24");
-    }
-
-    if (qemu_opt_get(opts, "net")) {
-        if (vnet) {
-            g_free(vnet);
-        }
-        vnet = g_strdup(qemu_opt_get(opts, "net"));
-    }
+    /* all optional fields are initialized to "all bits zero" */
 
-    qemu_opt_foreach(opts, net_init_slirp_configs, NULL, 0);
+    net_init_slirp_configs(user->hostfwd, SLIRP_CFG_HOSTFWD);
+    net_init_slirp_configs(user->guestfwd, 0);
 
-    ret = net_slirp_init(vlan, "user", name, restricted, vnet, vhost,
-                         vhostname, tftp_export, bootfile, vdhcp_start,
-                         vnamesrv, smb_export, vsmbsrv);
+    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;
diff --git a/net/slirp.h b/net/slirp.h
index 53fe95dc12..5f685c4fb1 100644
--- a/net/slirp.h
+++ b/net/slirp.h
@@ -27,10 +27,12 @@
 #include "qemu-common.h"
 #include "qdict.h"
 #include "qemu-option.h"
+#include "qapi-types.h"
 
 #ifdef CONFIG_SLIRP
 
-int net_init_slirp(QemuOpts *opts, const char *name, VLANState *vlan);
+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);
diff --git a/net/socket.c b/net/socket.c
index fcd0a3c162..c172c249be 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -34,7 +34,8 @@
 #include "qemu_socket.h"
 
 typedef struct NetSocketState {
-    VLANClientState nc;
+    NetClientState nc;
+    int listen_fd;
     int fd;
     int state; /* 0 = getting length, 1 = getting data */
     unsigned int index;
@@ -43,15 +44,10 @@ typedef struct NetSocketState {
     struct sockaddr_in dgram_dst; /* contains inet host and port destination iff connectionless (SOCK_DGRAM) */
 } NetSocketState;
 
-typedef struct NetSocketListenState {
-    VLANState *vlan;
-    char *model;
-    char *name;
-    int fd;
-} NetSocketListenState;
+static void net_socket_accept(void *opaque);
 
 /* XXX: we consider we can send the whole packet without blocking */
-static ssize_t net_socket_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
+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;
@@ -61,7 +57,7 @@ static ssize_t net_socket_receive(VLANClientState *nc, const uint8_t *buf, size_
     return send_all(s->fd, buf, size);
 }
 
-static ssize_t net_socket_receive_dgram(VLANClientState *nc, const uint8_t *buf, size_t size)
+static ssize_t net_socket_receive_dgram(NetClientState *nc, const uint8_t *buf, size_t size)
 {
     NetSocketState *s = DO_UPCAST(NetSocketState, nc, nc);
 
@@ -86,7 +82,19 @@ static void net_socket_send(void *opaque)
         /* 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;
@@ -231,21 +239,29 @@ fail:
     return -1;
 }
 
-static void net_socket_cleanup(VLANClientState *nc)
+static void net_socket_cleanup(NetClientState *nc)
 {
     NetSocketState *s = DO_UPCAST(NetSocketState, nc, nc);
-    qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
-    close(s->fd);
+    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_TYPE_SOCKET,
+    .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(VLANState *vlan,
+static NetSocketState *net_socket_fd_init_dgram(NetClientState *peer,
                                                 const char *model,
                                                 const char *name,
                                                 int fd, int is_connected)
@@ -253,7 +269,7 @@ static NetSocketState *net_socket_fd_init_dgram(VLANState *vlan,
     struct sockaddr_in saddr;
     int newfd;
     socklen_t saddr_len;
-    VLANClientState *nc;
+    NetClientState *nc;
     NetSocketState *s;
 
     /* fd passed: multicast: "learn" dgram_dst address from bound address and save it
@@ -287,7 +303,7 @@ static NetSocketState *net_socket_fd_init_dgram(VLANState *vlan,
         }
     }
 
-    nc = qemu_new_net_client(&net_dgram_socket_info, vlan, NULL, model, name);
+    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)",
@@ -297,11 +313,14 @@ static NetSocketState *net_socket_fd_init_dgram(VLANState *vlan,
     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;
+    if (is_connected) {
+        s->dgram_dst = saddr;
+    }
 
     return s;
 
@@ -317,27 +336,28 @@ static void net_socket_connect(void *opaque)
 }
 
 static NetClientInfo net_socket_info = {
-    .type = NET_CLIENT_TYPE_SOCKET,
+    .type = NET_CLIENT_OPTIONS_KIND_SOCKET,
     .size = sizeof(NetSocketState),
     .receive = net_socket_receive,
     .cleanup = net_socket_cleanup,
 };
 
-static NetSocketState *net_socket_fd_init_stream(VLANState *vlan,
+static NetSocketState *net_socket_fd_init_stream(NetClientState *peer,
                                                  const char *model,
                                                  const char *name,
                                                  int fd, int is_connected)
 {
-    VLANClientState *nc;
+    NetClientState *nc;
     NetSocketState *s;
 
-    nc = qemu_new_net_client(&net_socket_info, vlan, NULL, model, name);
+    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);
@@ -347,7 +367,7 @@ static NetSocketState *net_socket_fd_init_stream(VLANState *vlan,
     return s;
 }
 
-static NetSocketState *net_socket_fd_init(VLANState *vlan,
+static NetSocketState *net_socket_fd_init(NetClientState *peer,
                                           const char *model, const char *name,
                                           int fd, int is_connected)
 {
@@ -362,60 +382,59 @@ static NetSocketState *net_socket_fd_init(VLANState *vlan,
     }
     switch(so_type) {
     case SOCK_DGRAM:
-        return net_socket_fd_init_dgram(vlan, model, name, fd, is_connected);
+        return net_socket_fd_init_dgram(peer, model, name, fd, is_connected);
     case SOCK_STREAM:
-        return net_socket_fd_init_stream(vlan, model, name, fd, is_connected);
+        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(vlan, model, name, fd, is_connected);
+        return net_socket_fd_init_stream(peer, model, name, fd, is_connected);
     }
     return NULL;
 }
 
 static void net_socket_accept(void *opaque)
 {
-    NetSocketListenState *s = opaque;
-    NetSocketState *s1;
+    NetSocketState *s = opaque;
     struct sockaddr_in saddr;
     socklen_t len;
     int fd;
 
     for(;;) {
         len = sizeof(saddr);
-        fd = qemu_accept(s->fd, (struct sockaddr *)&saddr, &len);
+        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;
         }
     }
-    s1 = net_socket_fd_init(s->vlan, s->model, s->name, fd, 1);
-    if (s1) {
-        snprintf(s1->nc.info_str, sizeof(s1->nc.info_str),
-                 "socket: connection from %s:%d",
-                 inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
-    }
+
+    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(VLANState *vlan,
+static int net_socket_listen_init(NetClientState *peer,
                                   const char *model,
                                   const char *name,
                                   const char *host_str)
 {
-    NetSocketListenState *s;
-    int fd, val, ret;
+    NetClientState *nc;
+    NetSocketState *s;
     struct sockaddr_in saddr;
+    int fd, val, ret;
 
     if (parse_host_port(&saddr, host_str) < 0)
         return -1;
 
-    s = g_malloc0(sizeof(NetSocketListenState));
-
     fd = qemu_socket(PF_INET, SOCK_STREAM, 0);
     if (fd < 0) {
         perror("socket");
-        g_free(s);
         return -1;
     }
     socket_set_nonblock(fd);
@@ -427,26 +446,27 @@ static int net_socket_listen_init(VLANState *vlan,
     ret = bind(fd, (struct sockaddr *)&saddr, sizeof(saddr));
     if (ret < 0) {
         perror("bind");
-        g_free(s);
         closesocket(fd);
         return -1;
     }
     ret = listen(fd, 0);
     if (ret < 0) {
         perror("listen");
-        g_free(s);
         closesocket(fd);
         return -1;
     }
-    s->vlan = vlan;
-    s->model = g_strdup(model);
-    s->name = name ? g_strdup(name) : NULL;
-    s->fd = fd;
-    qemu_set_fd_handler(fd, net_socket_accept, NULL, s);
+
+    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(VLANState *vlan,
+static int net_socket_connect_init(NetClientState *peer,
                                    const char *model,
                                    const char *name,
                                    const char *host_str)
@@ -487,7 +507,7 @@ static int net_socket_connect_init(VLANState *vlan,
             break;
         }
     }
-    s = net_socket_fd_init(vlan, model, name, fd, connected);
+    s = net_socket_fd_init(peer, model, name, fd, connected);
     if (!s)
         return -1;
     snprintf(s->nc.info_str, sizeof(s->nc.info_str),
@@ -496,7 +516,7 @@ static int net_socket_connect_init(VLANState *vlan,
     return 0;
 }
 
-static int net_socket_mcast_init(VLANState *vlan,
+static int net_socket_mcast_init(NetClientState *peer,
                                  const char *model,
                                  const char *name,
                                  const char *host_str,
@@ -522,7 +542,7 @@ static int net_socket_mcast_init(VLANState *vlan,
     if (fd < 0)
         return -1;
 
-    s = net_socket_fd_init(vlan, model, name, fd, 0);
+    s = net_socket_fd_init(peer, model, name, fd, 0);
     if (!s)
         return -1;
 
@@ -535,7 +555,7 @@ static int net_socket_mcast_init(VLANState *vlan,
 
 }
 
-static int net_socket_udp_init(VLANState *vlan,
+static int net_socket_udp_init(NetClientState *peer,
                                  const char *model,
                                  const char *name,
                                  const char *rhost,
@@ -573,7 +593,7 @@ static int net_socket_udp_init(VLANState *vlan,
         return -1;
     }
 
-    s = net_socket_fd_init(vlan, model, name, fd, 0);
+    s = net_socket_fd_init(peer, model, name, fd, 0);
     if (!s) {
         return -1;
     }
@@ -586,100 +606,68 @@ static int net_socket_udp_init(VLANState *vlan,
     return 0;
 }
 
-int net_init_socket(QemuOpts *opts, const char *name, VLANState *vlan)
+int net_init_socket(const NetClientOptions *opts, const char *name,
+                    NetClientState *peer)
 {
-    if (qemu_opt_get(opts, "fd")) {
-        int fd;
+    const NetdevSocketOptions *sock;
 
-        if (qemu_opt_get(opts, "listen") ||
-            qemu_opt_get(opts, "connect") ||
-            qemu_opt_get(opts, "mcast") ||
-            qemu_opt_get(opts, "localaddr")) {
-            error_report("listen=, connect=, mcast= and localaddr= is invalid with fd=");
-            return -1;
-        }
+    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_SOCKET);
+    sock = opts->socket;
 
-        fd = net_handle_fd_param(cur_mon, qemu_opt_get(opts, "fd"));
-        if (fd == -1) {
-            return -1;
-        }
+    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 (!net_socket_fd_init(vlan, "socket", name, fd, 1)) {
-            return -1;
-        }
-    } else if (qemu_opt_get(opts, "listen")) {
-        const char *listen;
-
-        if (qemu_opt_get(opts, "fd") ||
-            qemu_opt_get(opts, "connect") ||
-            qemu_opt_get(opts, "mcast") ||
-            qemu_opt_get(opts, "localaddr")) {
-            error_report("fd=, connect=, mcast= and localaddr= is invalid with listen=");
-            return -1;
-        }
+    if (sock->has_localaddr && !sock->has_mcast && !sock->has_udp) {
+        error_report("localaddr= is only valid with mcast= or udp=");
+        return -1;
+    }
 
-        listen = qemu_opt_get(opts, "listen");
+    if (sock->has_fd) {
+        int fd;
 
-        if (net_socket_listen_init(vlan, "socket", name, listen) == -1) {
-            return -1;
-        }
-    } else if (qemu_opt_get(opts, "connect")) {
-        const char *connect;
-
-        if (qemu_opt_get(opts, "fd") ||
-            qemu_opt_get(opts, "listen") ||
-            qemu_opt_get(opts, "mcast") ||
-            qemu_opt_get(opts, "localaddr")) {
-            error_report("fd=, listen=, mcast= and localaddr= is invalid with connect=");
+        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;
+    }
 
-        connect = qemu_opt_get(opts, "connect");
-
-        if (net_socket_connect_init(vlan, "socket", name, connect) == -1) {
+    if (sock->has_listen) {
+        if (net_socket_listen_init(peer, "socket", name, sock->listen) == -1) {
             return -1;
         }
-    } else if (qemu_opt_get(opts, "mcast")) {
-        const char *mcast, *localaddr;
+        return 0;
+    }
 
-        if (qemu_opt_get(opts, "fd") ||
-            qemu_opt_get(opts, "connect") ||
-            qemu_opt_get(opts, "listen")) {
-            error_report("fd=, connect= and listen= is invalid with mcast=");
+    if (sock->has_connect) {
+        if (net_socket_connect_init(peer, "socket", name, sock->connect) ==
+            -1) {
             return -1;
         }
+        return 0;
+    }
 
-        mcast = qemu_opt_get(opts, "mcast");
-        localaddr = qemu_opt_get(opts, "localaddr");
-
-        if (net_socket_mcast_init(vlan, "socket", name, mcast, localaddr) == -1) {
-            return -1;
-        }
-    } else if (qemu_opt_get(opts, "udp")) {
-        const char *udp, *localaddr;
-
-        if (qemu_opt_get(opts, "fd") ||
-            qemu_opt_get(opts, "connect") ||
-            qemu_opt_get(opts, "listen") ||
-            qemu_opt_get(opts, "mcast")) {
-            error_report("fd=, connect=, listen="
-                         " and mcast= is invalid with udp=");
+    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;
+    }
 
-        udp = qemu_opt_get(opts, "udp");
-        localaddr = qemu_opt_get(opts, "localaddr");
-        if (localaddr == NULL) {
-                error_report("localaddr= is mandatory with udp=");
-                return -1;
-        }
-
-        if (net_socket_udp_init(vlan, "udp", name, udp, localaddr) == -1) {
-            return -1;
-        }
-    } else {
-        error_report("-socket requires fd=, listen=,"
-                     " connect=, mcast= or udp=");
+    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
index e1fe959412..3f8a092459 100644
--- a/net/socket.h
+++ b/net/socket.h
@@ -25,8 +25,9 @@
 #define QEMU_NET_SOCKET_H
 
 #include "net.h"
-#include "qemu-common.h"
+#include "qapi-types.h"
 
-int net_init_socket(QemuOpts *opts, const char *name, VLANState *vlan);
+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
index e19aaba110..f27c17729e 100644
--- a/net/tap-aix.c
+++ b/net/tap-aix.c
@@ -31,7 +31,7 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required
     return -1;
 }
 
-int tap_set_sndbuf(int fd, QemuOpts *opts)
+int tap_set_sndbuf(int fd, const NetdevTapOptions *tap)
 {
     return 0;
 }
diff --git a/net/tap-bsd.c b/net/tap-bsd.c
index 937a94b11f..a3b717dd1c 100644
--- a/net/tap-bsd.c
+++ b/net/tap-bsd.c
@@ -117,7 +117,7 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required
     return fd;
 }
 
-int tap_set_sndbuf(int fd, QemuOpts *opts)
+int tap_set_sndbuf(int fd, const NetdevTapOptions *tap)
 {
     return 0;
 }
diff --git a/net/tap-haiku.c b/net/tap-haiku.c
index 91dda8ebc0..34739d1562 100644
--- a/net/tap-haiku.c
+++ b/net/tap-haiku.c
@@ -31,7 +31,7 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required
     return -1;
 }
 
-int tap_set_sndbuf(int fd, QemuOpts *opts)
+int tap_set_sndbuf(int fd, const NetdevTapOptions *tap)
 {
     return 0;
 }
diff --git a/net/tap-linux.c b/net/tap-linux.c
index 41d581b734..c6521bec34 100644
--- a/net/tap-linux.c
+++ b/net/tap-linux.c
@@ -98,16 +98,19 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required
  */
 #define TAP_DEFAULT_SNDBUF 0
 
-int tap_set_sndbuf(int fd, QemuOpts *opts)
+int tap_set_sndbuf(int fd, const NetdevTapOptions *tap)
 {
     int sndbuf;
 
-    sndbuf = qemu_opt_get_size(opts, "sndbuf", TAP_DEFAULT_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 && qemu_opt_get(opts, "sndbuf")) {
+    if (ioctl(fd, TUNSETSNDBUF, &sndbuf) == -1 && tap->has_sndbuf) {
         error_report("TUNSETSNDBUF ioctl failed: %s", strerror(errno));
         return -1;
     }
diff --git a/net/tap-solaris.c b/net/tap-solaris.c
index cf764634ef..5d6ac42f24 100644
--- a/net/tap-solaris.c
+++ b/net/tap-solaris.c
@@ -197,7 +197,7 @@ int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required
     return fd;
 }
 
-int tap_set_sndbuf(int fd, QemuOpts *opts)
+int tap_set_sndbuf(int fd, const NetdevTapOptions *tap)
 {
     return 0;
 }
diff --git a/net/tap-win32.c b/net/tap-win32.c
index a801a553c4..c0ea954ca1 100644
--- a/net/tap-win32.c
+++ b/net/tap-win32.c
@@ -630,11 +630,11 @@ static int tap_win32_open(tap_win32_overlapped_t **phandle,
 /********************************************/
 
  typedef struct TAPState {
-     VLANClientState nc;
+     NetClientState nc;
      tap_win32_overlapped_t *handle;
  } TAPState;
 
-static void tap_cleanup(VLANClientState *nc)
+static void tap_cleanup(NetClientState *nc)
 {
     TAPState *s = DO_UPCAST(TAPState, nc, nc);
 
@@ -645,7 +645,7 @@ static void tap_cleanup(VLANClientState *nc)
     */
 }
 
-static ssize_t tap_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
+static ssize_t tap_receive(NetClientState *nc, const uint8_t *buf, size_t size)
 {
     TAPState *s = DO_UPCAST(TAPState, nc, nc);
 
@@ -667,16 +667,16 @@ static void tap_win32_send(void *opaque)
 }
 
 static NetClientInfo net_tap_win32_info = {
-    .type = NET_CLIENT_TYPE_TAP,
+    .type = NET_CLIENT_OPTIONS_KIND_TAP,
     .size = sizeof(TAPState),
     .receive = tap_receive,
     .cleanup = tap_cleanup,
 };
 
-static int tap_win32_init(VLANState *vlan, const char *model,
+static int tap_win32_init(NetClientState *peer, const char *model,
                           const char *name, const char *ifname)
 {
-    VLANClientState *nc;
+    NetClientState *nc;
     TAPState *s;
     tap_win32_overlapped_t *handle;
 
@@ -685,7 +685,7 @@ static int tap_win32_init(VLANState *vlan, const char *model,
         return -1;
     }
 
-    nc = qemu_new_net_client(&net_tap_win32_info, vlan, NULL, model, name);
+    nc = qemu_new_net_client(&net_tap_win32_info, peer, model, name);
 
     s = DO_UPCAST(TAPState, nc, nc);
 
@@ -699,30 +699,32 @@ static int tap_win32_init(VLANState *vlan, const char *model,
     return 0;
 }
 
-int net_init_tap(QemuOpts *opts, const char *name, VLANState *vlan)
+int net_init_tap(const NetClientOptions *opts, const char *name,
+                 NetClientState *peer)
 {
-    const char *ifname;
+    const NetdevTapOptions *tap;
 
-    ifname = qemu_opt_get(opts, "ifname");
+    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_TAP);
+    tap = opts->tap;
 
-    if (!ifname) {
+    if (!tap->has_ifname) {
         error_report("tap: no interface name");
         return -1;
     }
 
-    if (tap_win32_init(vlan, "tap", name, ifname) == -1) {
+    if (tap_win32_init(peer, "tap", name, tap->ifname) == -1) {
         return -1;
     }
 
     return 0;
 }
 
-int tap_has_ufo(VLANClientState *vc)
+int tap_has_ufo(NetClientState *nc)
 {
     return 0;
 }
 
-int tap_has_vnet_hdr(VLANClientState *vc)
+int tap_has_vnet_hdr(NetClientState *nc)
 {
     return 0;
 }
@@ -736,16 +738,16 @@ void tap_fd_set_vnet_hdr_len(int fd, int len)
 {
 }
 
-void tap_using_vnet_hdr(VLANClientState *vc, int using_vnet_hdr)
+void tap_using_vnet_hdr(NetClientState *nc, int using_vnet_hdr)
 {
 }
 
-void tap_set_offload(VLANClientState *vc, int csum, int tso4,
+void tap_set_offload(NetClientState *nc, int csum, int tso4,
                      int tso6, int ecn, int ufo)
 {
 }
 
-struct vhost_net *tap_get_vhost_net(VLANClientState *nc)
+struct vhost_net *tap_get_vhost_net(NetClientState *nc)
 {
     return NULL;
 }
diff --git a/net/tap.c b/net/tap.c
index 17e91355ce..1971525794 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -50,7 +50,7 @@
 #define TAP_BUFSIZE (4096 + 65536)
 
 typedef struct TAPState {
-    VLANClientState nc;
+    NetClientState nc;
     int fd;
     char down_script[1024];
     char down_script_arg[128];
@@ -115,7 +115,7 @@ static ssize_t tap_write_packet(TAPState *s, const struct iovec *iov, int iovcnt
     return len;
 }
 
-static ssize_t tap_receive_iov(VLANClientState *nc, const struct iovec *iov,
+static ssize_t tap_receive_iov(NetClientState *nc, const struct iovec *iov,
                                int iovcnt)
 {
     TAPState *s = DO_UPCAST(TAPState, nc, nc);
@@ -134,7 +134,7 @@ static ssize_t tap_receive_iov(VLANClientState *nc, const struct iovec *iov,
     return tap_write_packet(s, iovp, iovcnt);
 }
 
-static ssize_t tap_receive_raw(VLANClientState *nc, const uint8_t *buf, size_t size)
+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];
@@ -154,7 +154,7 @@ static ssize_t tap_receive_raw(VLANClientState *nc, const uint8_t *buf, size_t s
     return tap_write_packet(s, iov, iovcnt);
 }
 
-static ssize_t tap_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
+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];
@@ -183,7 +183,7 @@ ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen)
 }
 #endif
 
-static void tap_send_completed(VLANClientState *nc, ssize_t len)
+static void tap_send_completed(NetClientState *nc, ssize_t len)
 {
     TAPState *s = DO_UPCAST(TAPState, nc, nc);
     tap_read_poll(s, 1);
@@ -214,38 +214,38 @@ static void tap_send(void *opaque)
     } while (size > 0 && qemu_can_send_packet(&s->nc));
 }
 
-int tap_has_ufo(VLANClientState *nc)
+int tap_has_ufo(NetClientState *nc)
 {
     TAPState *s = DO_UPCAST(TAPState, nc, nc);
 
-    assert(nc->info->type == NET_CLIENT_TYPE_TAP);
+    assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
 
     return s->has_ufo;
 }
 
-int tap_has_vnet_hdr(VLANClientState *nc)
+int tap_has_vnet_hdr(NetClientState *nc)
 {
     TAPState *s = DO_UPCAST(TAPState, nc, nc);
 
-    assert(nc->info->type == NET_CLIENT_TYPE_TAP);
+    assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
 
     return !!s->host_vnet_hdr_len;
 }
 
-int tap_has_vnet_hdr_len(VLANClientState *nc, int len)
+int tap_has_vnet_hdr_len(NetClientState *nc, int len)
 {
     TAPState *s = DO_UPCAST(TAPState, nc, nc);
 
-    assert(nc->info->type == NET_CLIENT_TYPE_TAP);
+    assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
 
     return tap_probe_vnet_hdr_len(s->fd, len);
 }
 
-void tap_set_vnet_hdr_len(VLANClientState *nc, int len)
+void tap_set_vnet_hdr_len(NetClientState *nc, int len)
 {
     TAPState *s = DO_UPCAST(TAPState, nc, nc);
 
-    assert(nc->info->type == NET_CLIENT_TYPE_TAP);
+    assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
     assert(len == sizeof(struct virtio_net_hdr_mrg_rxbuf) ||
            len == sizeof(struct virtio_net_hdr));
 
@@ -253,19 +253,19 @@ void tap_set_vnet_hdr_len(VLANClientState *nc, int len)
     s->host_vnet_hdr_len = len;
 }
 
-void tap_using_vnet_hdr(VLANClientState *nc, int using_vnet_hdr)
+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_TYPE_TAP);
+    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(VLANClientState *nc, int csum, int tso4,
+void tap_set_offload(NetClientState *nc, int csum, int tso4,
                      int tso6, int ecn, int ufo)
 {
     TAPState *s = DO_UPCAST(TAPState, nc, nc);
@@ -276,7 +276,7 @@ void tap_set_offload(VLANClientState *nc, int csum, int tso4,
     tap_fd_set_offload(s->fd, csum, tso4, tso6, ecn, ufo);
 }
 
-static void tap_cleanup(VLANClientState *nc)
+static void tap_cleanup(NetClientState *nc)
 {
     TAPState *s = DO_UPCAST(TAPState, nc, nc);
 
@@ -296,24 +296,24 @@ static void tap_cleanup(VLANClientState *nc)
     s->fd = -1;
 }
 
-static void tap_poll(VLANClientState *nc, bool enable)
+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(VLANClientState *nc)
+int tap_get_fd(NetClientState *nc)
 {
     TAPState *s = DO_UPCAST(TAPState, nc, nc);
-    assert(nc->info->type == NET_CLIENT_TYPE_TAP);
+    assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
     return s->fd;
 }
 
 /* fd support */
 
 static NetClientInfo net_tap_info = {
-    .type = NET_CLIENT_TYPE_TAP,
+    .type = NET_CLIENT_OPTIONS_KIND_TAP,
     .size = sizeof(TAPState),
     .receive = tap_receive,
     .receive_raw = tap_receive_raw,
@@ -322,16 +322,16 @@ static NetClientInfo net_tap_info = {
     .cleanup = tap_cleanup,
 };
 
-static TAPState *net_tap_fd_init(VLANState *vlan,
+static TAPState *net_tap_fd_init(NetClientState *peer,
                                  const char *model,
                                  const char *name,
                                  int fd,
                                  int vnet_hdr)
 {
-    VLANClientState *nc;
+    NetClientState *nc;
     TAPState *s;
 
-    nc = qemu_new_net_client(&net_tap_info, vlan, NULL, model, name);
+    nc = qemu_new_net_client(&net_tap_info, peer, model, name);
 
     s = DO_UPCAST(TAPState, nc, nc);
 
@@ -513,20 +513,22 @@ static int net_bridge_run_helper(const char *helper, const char *bridge)
     return -1;
 }
 
-int net_init_bridge(QemuOpts *opts, const char *name, VLANState *vlan)
+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;
 
-    if (!qemu_opt_get(opts, "br")) {
-        qemu_opt_set(opts, "br", DEFAULT_BRIDGE_INTERFACE);
-    }
-    if (!qemu_opt_get(opts, "helper")) {
-        qemu_opt_set(opts, "helper", DEFAULT_BRIDGE_HELPER);
-    }
+    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_BRIDGE);
+    bridge = opts->bridge;
 
-    fd = net_bridge_run_helper(qemu_opt_get(opts, "helper"),
-                               qemu_opt_get(opts, "br"));
+    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;
     }
@@ -535,41 +537,44 @@ int net_init_bridge(QemuOpts *opts, const char *name, VLANState *vlan)
 
     vnet_hdr = tap_probe_vnet_hdr(fd);
 
-    s = net_tap_fd_init(vlan, "bridge", name, fd, vnet_hdr);
+    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",
-             qemu_opt_get(opts, "helper"), qemu_opt_get(opts, "br"));
+    snprintf(s->nc.info_str, sizeof(s->nc.info_str), "helper=%s,br=%s", helper,
+             br);
 
     return 0;
 }
 
-static int net_tap_init(QemuOpts *opts, int *vnet_hdr)
+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;
-    char ifname[128] = {0,};
-    const char *setup_script;
 
-    if (qemu_opt_get(opts, "ifname")) {
-        pstrcpy(ifname, sizeof(ifname), qemu_opt_get(opts, "ifname"));
+    if (tap->has_ifname) {
+        pstrcpy(ifname, ifname_sz, tap->ifname);
+    } else {
+        assert(ifname_sz > 0);
+        ifname[0] = '\0';
     }
 
-    *vnet_hdr = qemu_opt_get_bool(opts, "vnet_hdr", 1);
-    if (qemu_opt_get(opts, "vnet_hdr")) {
+    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, sizeof(ifname), vnet_hdr, vnet_hdr_required));
+    TFR(fd = tap_open(ifname, ifname_sz, vnet_hdr, vnet_hdr_required));
     if (fd < 0) {
         return -1;
     }
 
-    setup_script = qemu_opt_get(opts, "script");
     if (setup_script &&
         setup_script[0] != '\0' &&
         strcmp(setup_script, "no") != 0 &&
@@ -578,29 +583,34 @@ static int net_tap_init(QemuOpts *opts, int *vnet_hdr)
         return -1;
     }
 
-    qemu_opt_set(opts, "ifname", ifname);
-
     return fd;
 }
 
-int net_init_tap(QemuOpts *opts, const char *name, VLANState *vlan)
+int net_init_tap(const NetClientOptions *opts, const char *name,
+                 NetClientState *peer)
 {
-    TAPState *s;
+    const NetdevTapOptions *tap;
+
     int fd, vnet_hdr = 0;
     const char *model;
+    TAPState *s;
 
-    if (qemu_opt_get(opts, "fd")) {
-        if (qemu_opt_get(opts, "ifname") ||
-            qemu_opt_get(opts, "script") ||
-            qemu_opt_get(opts, "downscript") ||
-            qemu_opt_get(opts, "vnet_hdr") ||
-            qemu_opt_get(opts, "helper")) {
+    /* 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, qemu_opt_get(opts, "fd"));
+        fd = net_handle_fd_param(cur_mon, tap->fd);
         if (fd == -1) {
             return -1;
         }
@@ -611,18 +621,15 @@ int net_init_tap(QemuOpts *opts, const char *name, VLANState *vlan)
 
         model = "tap";
 
-    } else if (qemu_opt_get(opts, "helper")) {
-        if (qemu_opt_get(opts, "ifname") ||
-            qemu_opt_get(opts, "script") ||
-            qemu_opt_get(opts, "downscript") ||
-            qemu_opt_get(opts, "vnet_hdr")) {
+    } 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(qemu_opt_get(opts, "helper"),
-                                   DEFAULT_BRIDGE_INTERFACE);
+        fd = net_bridge_run_helper(tap->helper, DEFAULT_BRIDGE_INTERFACE);
         if (fd == -1) {
             return -1;
         }
@@ -634,15 +641,8 @@ int net_init_tap(QemuOpts *opts, const char *name, VLANState *vlan)
         model = "bridge";
 
     } else {
-        if (!qemu_opt_get(opts, "script")) {
-            qemu_opt_set(opts, "script", DEFAULT_NETWORK_SCRIPT);
-        }
-
-        if (!qemu_opt_get(opts, "downscript")) {
-            qemu_opt_set(opts, "downscript", DEFAULT_NETWORK_DOWN_SCRIPT);
-        }
-
-        fd = net_tap_init(opts, &vnet_hdr);
+        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;
         }
@@ -650,31 +650,30 @@ int net_init_tap(QemuOpts *opts, const char *name, VLANState *vlan)
         model = "tap";
     }
 
-    s = net_tap_fd_init(vlan, model, name, fd, vnet_hdr);
+    s = net_tap_fd_init(peer, model, name, fd, vnet_hdr);
     if (!s) {
         close(fd);
         return -1;
     }
 
-    if (tap_set_sndbuf(s->fd, opts) < 0) {
+    if (tap_set_sndbuf(s->fd, tap) < 0) {
         return -1;
     }
 
-    if (qemu_opt_get(opts, "fd")) {
+    if (tap->has_fd) {
         snprintf(s->nc.info_str, sizeof(s->nc.info_str), "fd=%d", fd);
-    } else if (qemu_opt_get(opts, "helper")) {
-        snprintf(s->nc.info_str, sizeof(s->nc.info_str),
-                 "helper=%s", qemu_opt_get(opts, "helper"));
+    } else if (tap->has_helper) {
+        snprintf(s->nc.info_str, sizeof(s->nc.info_str), "helper=%s",
+                 tap->helper);
     } else {
-        const char *ifname, *script, *downscript;
+        const char *downscript;
 
-        ifname     = qemu_opt_get(opts, "ifname");
-        script     = qemu_opt_get(opts, "script");
-        downscript = qemu_opt_get(opts, "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);
+                 "ifname=%s,script=%s,downscript=%s", ifname, script,
+                 downscript);
 
         if (strcmp(downscript, "no") != 0) {
             snprintf(s->down_script, sizeof(s->down_script), "%s", downscript);
@@ -682,25 +681,26 @@ int net_init_tap(QemuOpts *opts, const char *name, VLANState *vlan)
         }
     }
 
-    if (qemu_opt_get_bool(opts, "vhost", !!qemu_opt_get(opts, "vhostfd") ||
-                          qemu_opt_get_bool(opts, "vhostforce", false))) {
-        int vhostfd, r;
-        bool force = qemu_opt_get_bool(opts, "vhostforce", false);
-        if (qemu_opt_get(opts, "vhostfd")) {
-            r = net_handle_fd_param(cur_mon, qemu_opt_get(opts, "vhostfd"));
-            if (r == -1) {
+    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;
             }
-            vhostfd = r;
         } else {
             vhostfd = -1;
         }
-        s->vhost_net = vhost_net_init(&s->nc, vhostfd, force);
+
+        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 (qemu_opt_get(opts, "vhostfd")) {
+    } else if (tap->has_vhostfd) {
         error_report("vhostfd= is not valid without vhost");
         return -1;
     }
@@ -708,9 +708,9 @@ int net_init_tap(QemuOpts *opts, const char *name, VLANState *vlan)
     return 0;
 }
 
-VHostNetState *tap_get_vhost_net(VLANClientState *nc)
+VHostNetState *tap_get_vhost_net(NetClientState *nc)
 {
     TAPState *s = DO_UPCAST(TAPState, nc, nc);
-    assert(nc->info->type == NET_CLIENT_TYPE_TAP);
+    assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
     return s->vhost_net;
 }
diff --git a/net/tap.h b/net/tap.h
index b2a9450aab..0fb018c4b7 100644
--- a/net/tap.h
+++ b/net/tap.h
@@ -27,36 +27,38 @@
 #define QEMU_NET_TAP_H
 
 #include "qemu-common.h"
-#include "qemu-option.h"
+#include "qapi-types.h"
 
 #define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup"
 #define DEFAULT_NETWORK_DOWN_SCRIPT "/etc/qemu-ifdown"
 
-int net_init_tap(QemuOpts *opts, const char *name, VLANState *vlan);
+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(VLANClientState *vc);
-int tap_has_vnet_hdr(VLANClientState *vc);
-int tap_has_vnet_hdr_len(VLANClientState *vc, int len);
-void tap_using_vnet_hdr(VLANClientState *vc, int using_vnet_hdr);
-void tap_set_offload(VLANClientState *vc, int csum, int tso4, int tso6, int ecn, int ufo);
-void tap_set_vnet_hdr_len(VLANClientState *vc, int len);
+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, QemuOpts *opts);
+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(VLANClientState *vc);
+int tap_get_fd(NetClientState *nc);
 
 struct vhost_net;
-struct vhost_net *tap_get_vhost_net(VLANClientState *vc);
+struct vhost_net *tap_get_vhost_net(NetClientState *nc);
 
-int net_init_bridge(QemuOpts *opts, const char *name, VLANState *vlan);
+int net_init_bridge(const NetClientOptions *opts, const char *name,
+                    NetClientState *peer);
 
 #endif /* QEMU_NET_TAP_H */
diff --git a/net/vde.c b/net/vde.c
index 6b9d45294a..b91a6c799b 100644
--- a/net/vde.c
+++ b/net/vde.c
@@ -33,7 +33,7 @@
 #include "qemu-option.h"
 
 typedef struct VDEState {
-    VLANClientState nc;
+    NetClientState nc;
     VDECONN *vde;
 } VDEState;
 
@@ -49,7 +49,7 @@ static void vde_to_qemu(void *opaque)
     }
 }
 
-static ssize_t vde_receive(VLANClientState *nc, const uint8_t *buf, size_t 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;
@@ -61,7 +61,7 @@ static ssize_t vde_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
     return ret;
 }
 
-static void vde_cleanup(VLANClientState *nc)
+static void vde_cleanup(NetClientState *nc)
 {
     VDEState *s = DO_UPCAST(VDEState, nc, nc);
     qemu_set_fd_handler(vde_datafd(s->vde), NULL, NULL, NULL);
@@ -69,17 +69,17 @@ static void vde_cleanup(VLANClientState *nc)
 }
 
 static NetClientInfo net_vde_info = {
-    .type = NET_CLIENT_TYPE_VDE,
+    .type = NET_CLIENT_OPTIONS_KIND_VDE,
     .size = sizeof(VDEState),
     .receive = vde_receive,
     .cleanup = vde_cleanup,
 };
 
-static int net_vde_init(VLANState *vlan, const char *model,
+static int net_vde_init(NetClientState *peer, const char *model,
                         const char *name, const char *sock,
                         int port, const char *group, int mode)
 {
-    VLANClientState *nc;
+    NetClientState *nc;
     VDEState *s;
     VDECONN *vde;
     char *init_group = (char *)group;
@@ -96,7 +96,7 @@ static int net_vde_init(VLANState *vlan, const char *model,
         return -1;
     }
 
-    nc = qemu_new_net_client(&net_vde_info, vlan, NULL, model, name);
+    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));
@@ -110,19 +110,17 @@ static int net_vde_init(VLANState *vlan, const char *model,
     return 0;
 }
 
-int net_init_vde(QemuOpts *opts, const char *name, VLANState *vlan)
+int net_init_vde(const NetClientOptions *opts, const char *name,
+                 NetClientState *peer)
 {
-    const char *sock;
-    const char *group;
-    int port, mode;
+    const NetdevVdeOptions *vde;
 
-    sock  = qemu_opt_get(opts, "sock");
-    group = qemu_opt_get(opts, "group");
+    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_VDE);
+    vde = opts->vde;
 
-    port = qemu_opt_get_number(opts, "port", 0);
-    mode = qemu_opt_get_number(opts, "mode", 0700);
-
-    if (net_vde_init(vlan, "vde", name, sock, port, group, mode) == -1) {
+    /* 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;
     }
 
diff --git a/net/vde.h b/net/vde.h
index 732e5756f6..6ce6698937 100644
--- a/net/vde.h
+++ b/net/vde.h
@@ -25,11 +25,12 @@
 #define QEMU_NET_VDE_H
 
 #include "qemu-common.h"
-#include "qemu-option.h"
+#include "qapi-types.h"
 
 #ifdef CONFIG_VDE
 
-int net_init_vde(QemuOpts *opts, const char *name, VLANState *vlan);
+int net_init_vde(const NetClientOptions *opts, const char *name,
+                 NetClientState *peer);
 
 #endif /* CONFIG_VDE */