]> git.proxmox.com Git - qemu.git/blobdiff - net.c
Avoid asprintf() which is not available on mingw
[qemu.git] / net.c
diff --git a/net.c b/net.c
index 436c24c59851b49f1397482c5d3bcded8c2af759..60043ddec694ef914cd340d250163954d25976e4 100644 (file)
--- a/net.c
+++ b/net.c
 #include "net/dump.h"
 #include "net/slirp.h"
 #include "net/vde.h"
+#include "net/hub.h"
 #include "net/util.h"
 #include "monitor.h"
-#include "sysemu.h"
 #include "qemu-common.h"
 #include "qemu_socket.h"
+#include "qmp-commands.h"
 #include "hw/qdev.h"
 #include "iov.h"
+#include "qapi-visit.h"
+#include "qapi/opts-visitor.h"
+#include "qapi/qapi-dealloc-visitor.h"
 
-static QTAILQ_HEAD(, VLANState) vlans;
-static QTAILQ_HEAD(, VLANClientState) non_vlan_clients;
+/* Net bridge is currently not supported for W32. */
+#if !defined(_WIN32)
+# define CONFIG_NET_BRIDGE
+#endif
+
+static QTAILQ_HEAD(, NetClientState) net_clients;
 
 int default_net = 1;
 
@@ -94,47 +102,6 @@ static int get_str_sep(char *buf, int buf_size, const char **pp, int sep)
     return 0;
 }
 
-int parse_host_src_port(struct sockaddr_in *haddr,
-                        struct sockaddr_in *saddr,
-                        const char *input_str)
-{
-    char *str = qemu_strdup(input_str);
-    char *host_str = str;
-    char *src_str;
-    const char *src_str2;
-    char *ptr;
-
-    /*
-     * Chop off any extra arguments at the end of the string which
-     * would start with a comma, then fill in the src port information
-     * if it was provided else use the "any address" and "any port".
-     */
-    if ((ptr = strchr(str,',')))
-        *ptr = '\0';
-
-    if ((src_str = strchr(input_str,'@'))) {
-        *src_str = '\0';
-        src_str++;
-    }
-
-    if (parse_host_port(haddr, host_str) < 0)
-        goto fail;
-
-    src_str2 = src_str;
-    if (!src_str || *src_str == '\0')
-        src_str2 = ":0";
-
-    if (parse_host_port(saddr, src_str2) < 0)
-        goto fail;
-
-    free(str);
-    return(0);
-
-fail:
-    free(str);
-    return -1;
-}
-
 int parse_host_port(struct sockaddr_in *saddr, const char *str)
 {
     char buf[512];
@@ -165,11 +132,11 @@ int parse_host_port(struct sockaddr_in *saddr, const char *str)
     return 0;
 }
 
-void qemu_format_nic_info_str(VLANClientState *vc, uint8_t macaddr[6])
+void qemu_format_nic_info_str(NetClientState *nc, uint8_t macaddr[6])
 {
-    snprintf(vc->info_str, sizeof(vc->info_str),
+    snprintf(nc->info_str, sizeof(nc->info_str),
              "model=%s,macaddr=%02x:%02x:%02x:%02x:%02x:%02x",
-             vc->model,
+             nc->model,
              macaddr[0], macaddr[1], macaddr[2],
              macaddr[3], macaddr[4], macaddr[5]);
 }
@@ -189,76 +156,63 @@ void qemu_macaddr_default_if_unset(MACAddr *macaddr)
     macaddr->a[5] = 0x56 + index++;
 }
 
-static char *assign_name(VLANClientState *vc1, const char *model)
+/**
+ * Generate a name for net client
+ *
+ * Only net clients created with the legacy -net option need this.  Naming is
+ * mandatory for net clients created with -netdev.
+ */
+static char *assign_name(NetClientState *nc1, const char *model)
 {
-    VLANState *vlan;
+    NetClientState *nc;
     char buf[256];
     int id = 0;
 
-    QTAILQ_FOREACH(vlan, &vlans, next) {
-        VLANClientState *vc;
-
-        QTAILQ_FOREACH(vc, &vlan->clients, next) {
-            if (vc != vc1 && strcmp(vc->model, model) == 0) {
-                id++;
-            }
+    QTAILQ_FOREACH(nc, &net_clients, next) {
+        if (nc == nc1) {
+            continue;
+        }
+        /* For compatibility only bump id for net clients on a vlan */
+        if (strcmp(nc->model, model) == 0 &&
+            net_hub_id_for_client(nc, NULL) == 0) {
+            id++;
         }
     }
 
     snprintf(buf, sizeof(buf), "%s.%d", model, id);
 
-    return qemu_strdup(buf);
+    return g_strdup(buf);
 }
 
-static ssize_t qemu_deliver_packet(VLANClientState *sender,
-                                   unsigned flags,
-                                   const uint8_t *data,
-                                   size_t size,
-                                   void *opaque);
-static ssize_t qemu_deliver_packet_iov(VLANClientState *sender,
-                                       unsigned flags,
-                                       const struct iovec *iov,
-                                       int iovcnt,
-                                       void *opaque);
-
-VLANClientState *qemu_new_net_client(NetClientInfo *info,
-                                     VLANState *vlan,
-                                     VLANClientState *peer,
-                                     const char *model,
-                                     const char *name)
+NetClientState *qemu_new_net_client(NetClientInfo *info,
+                                    NetClientState *peer,
+                                    const char *model,
+                                    const char *name)
 {
-    VLANClientState *vc;
+    NetClientState *nc;
 
-    assert(info->size >= sizeof(VLANClientState));
+    assert(info->size >= sizeof(NetClientState));
 
-    vc = qemu_mallocz(info->size);
+    nc = g_malloc0(info->size);
 
-    vc->info = info;
-    vc->model = qemu_strdup(model);
+    nc->info = info;
+    nc->model = g_strdup(model);
     if (name) {
-        vc->name = qemu_strdup(name);
+        nc->name = g_strdup(name);
     } else {
-        vc->name = assign_name(vc, model);
+        nc->name = assign_name(nc, model);
     }
 
-    if (vlan) {
-        assert(!peer);
-        vc->vlan = vlan;
-        QTAILQ_INSERT_TAIL(&vc->vlan->clients, vc, next);
-    } else {
-        if (peer) {
-            assert(!peer->peer);
-            vc->peer = peer;
-            peer->peer = vc;
-        }
-        QTAILQ_INSERT_TAIL(&non_vlan_clients, vc, next);
-
-        vc->send_queue = qemu_new_net_queue(qemu_deliver_packet,
-                                            qemu_deliver_packet_iov,
-                                            vc);
+    if (peer) {
+        assert(!peer->peer);
+        nc->peer = peer;
+        peer->peer = nc;
     }
+    QTAILQ_INSERT_TAIL(&net_clients, nc, next);
 
-    return vc;
+    nc->send_queue = qemu_new_net_queue(nc);
+
+    return nc;
 }
 
 NICState *qemu_new_nic(NetClientInfo *info,
@@ -267,13 +221,13 @@ NICState *qemu_new_nic(NetClientInfo *info,
                        const char *name,
                        void *opaque)
 {
-    VLANClientState *nc;
+    NetClientState *nc;
     NICState *nic;
 
-    assert(info->type == NET_CLIENT_TYPE_NIC);
+    assert(info->type == NET_CLIENT_OPTIONS_KIND_NIC);
     assert(info->size >= sizeof(NICState));
 
-    nc = qemu_new_net_client(info, conf->vlan, conf->peer, model, name);
+    nc = qemu_new_net_client(info, conf->peer, model, name);
 
     nic = DO_UPCAST(NICState, nc, nc);
     nic->conf = conf;
@@ -282,250 +236,131 @@ NICState *qemu_new_nic(NetClientInfo *info,
     return nic;
 }
 
-static void qemu_cleanup_vlan_client(VLANClientState *vc)
+static void qemu_cleanup_net_client(NetClientState *nc)
 {
-    if (vc->vlan) {
-        QTAILQ_REMOVE(&vc->vlan->clients, vc, next);
-    } else {
-        QTAILQ_REMOVE(&non_vlan_clients, vc, next);
-    }
+    QTAILQ_REMOVE(&net_clients, nc, next);
 
-    if (vc->info->cleanup) {
-        vc->info->cleanup(vc);
+    if (nc->info->cleanup) {
+        nc->info->cleanup(nc);
     }
 }
 
-static void qemu_free_vlan_client(VLANClientState *vc)
+static void qemu_free_net_client(NetClientState *nc)
 {
-    if (!vc->vlan) {
-        if (vc->send_queue) {
-            qemu_del_net_queue(vc->send_queue);
-        }
-        if (vc->peer) {
-            vc->peer->peer = NULL;
-        }
+    if (nc->send_queue) {
+        qemu_del_net_queue(nc->send_queue);
+    }
+    if (nc->peer) {
+        nc->peer->peer = NULL;
     }
-    qemu_free(vc->name);
-    qemu_free(vc->model);
-    qemu_free(vc);
+    g_free(nc->name);
+    g_free(nc->model);
+    g_free(nc);
 }
 
-void qemu_del_vlan_client(VLANClientState *vc)
+void qemu_del_net_client(NetClientState *nc)
 {
     /* If there is a peer NIC, delete and cleanup client, but do not free. */
-    if (!vc->vlan && vc->peer && vc->peer->info->type == NET_CLIENT_TYPE_NIC) {
-        NICState *nic = DO_UPCAST(NICState, nc, vc->peer);
+    if (nc->peer && nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
+        NICState *nic = DO_UPCAST(NICState, nc, nc->peer);
         if (nic->peer_deleted) {
             return;
         }
         nic->peer_deleted = true;
         /* Let NIC know peer is gone. */
-        vc->peer->link_down = true;
-        if (vc->peer->info->link_status_changed) {
-            vc->peer->info->link_status_changed(vc->peer);
+        nc->peer->link_down = true;
+        if (nc->peer->info->link_status_changed) {
+            nc->peer->info->link_status_changed(nc->peer);
         }
-        qemu_cleanup_vlan_client(vc);
+        qemu_cleanup_net_client(nc);
         return;
     }
 
     /* If this is a peer NIC and peer has already been deleted, free it now. */
-    if (!vc->vlan && vc->peer && vc->info->type == NET_CLIENT_TYPE_NIC) {
-        NICState *nic = DO_UPCAST(NICState, nc, vc);
+    if (nc->peer && nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
+        NICState *nic = DO_UPCAST(NICState, nc, nc);
         if (nic->peer_deleted) {
-            qemu_free_vlan_client(vc->peer);
+            qemu_free_net_client(nc->peer);
         }
     }
 
-    qemu_cleanup_vlan_client(vc);
-    qemu_free_vlan_client(vc);
-}
-
-VLANClientState *
-qemu_find_vlan_client_by_name(Monitor *mon, int vlan_id,
-                              const char *client_str)
-{
-    VLANState *vlan;
-    VLANClientState *vc;
-
-    vlan = qemu_find_vlan(vlan_id, 0);
-    if (!vlan) {
-        monitor_printf(mon, "unknown VLAN %d\n", vlan_id);
-        return NULL;
-    }
-
-    QTAILQ_FOREACH(vc, &vlan->clients, next) {
-        if (!strcmp(vc->name, client_str)) {
-            break;
-        }
-    }
-    if (!vc) {
-        monitor_printf(mon, "can't find device %s on VLAN %d\n",
-                       client_str, vlan_id);
-    }
-
-    return vc;
+    qemu_cleanup_net_client(nc);
+    qemu_free_net_client(nc);
 }
 
 void qemu_foreach_nic(qemu_nic_foreach func, void *opaque)
 {
-    VLANClientState *nc;
-    VLANState *vlan;
+    NetClientState *nc;
 
-    QTAILQ_FOREACH(nc, &non_vlan_clients, next) {
-        if (nc->info->type == NET_CLIENT_TYPE_NIC) {
+    QTAILQ_FOREACH(nc, &net_clients, next) {
+        if (nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
             func(DO_UPCAST(NICState, nc, nc), opaque);
         }
     }
-
-    QTAILQ_FOREACH(vlan, &vlans, next) {
-        QTAILQ_FOREACH(nc, &vlan->clients, next) {
-            if (nc->info->type == NET_CLIENT_TYPE_NIC) {
-                func(DO_UPCAST(NICState, nc, nc), opaque);
-            }
-        }
-    }
 }
 
-int qemu_can_send_packet(VLANClientState *sender)
+int qemu_can_send_packet(NetClientState *sender)
 {
-    VLANState *vlan = sender->vlan;
-    VLANClientState *vc;
-
-    if (sender->peer) {
-        if (sender->peer->receive_disabled) {
-            return 0;
-        } else if (sender->peer->info->can_receive &&
-                   !sender->peer->info->can_receive(sender->peer)) {
-            return 0;
-        } else {
-            return 1;
-        }
-    }
-
-    if (!sender->vlan) {
+    if (!sender->peer) {
         return 1;
     }
 
-    QTAILQ_FOREACH(vc, &vlan->clients, next) {
-        if (vc == sender) {
-            continue;
-        }
-
-        /* no can_receive() handler, they can always receive */
-        if (vc->info->can_receive && !vc->info->can_receive(vc)) {
-            return 0;
-        }
+    if (sender->peer->receive_disabled) {
+        return 0;
+    } else if (sender->peer->info->can_receive &&
+               !sender->peer->info->can_receive(sender->peer)) {
+        return 0;
     }
     return 1;
 }
 
-static ssize_t qemu_deliver_packet(VLANClientState *sender,
-                                   unsigned flags,
-                                   const uint8_t *data,
-                                   size_t size,
-                                   void *opaque)
+ssize_t qemu_deliver_packet(NetClientState *sender,
+                            unsigned flags,
+                            const uint8_t *data,
+                            size_t size,
+                            void *opaque)
 {
-    VLANClientState *vc = opaque;
+    NetClientState *nc = opaque;
     ssize_t ret;
 
-    if (vc->link_down) {
+    if (nc->link_down) {
         return size;
     }
 
-    if (vc->receive_disabled) {
+    if (nc->receive_disabled) {
         return 0;
     }
 
-    if (flags & QEMU_NET_PACKET_FLAG_RAW && vc->info->receive_raw) {
-        ret = vc->info->receive_raw(vc, data, size);
+    if (flags & QEMU_NET_PACKET_FLAG_RAW && nc->info->receive_raw) {
+        ret = nc->info->receive_raw(nc, data, size);
     } else {
-        ret = vc->info->receive(vc, data, size);
+        ret = nc->info->receive(nc, data, size);
     }
 
     if (ret == 0) {
-        vc->receive_disabled = 1;
+        nc->receive_disabled = 1;
     };
 
     return ret;
 }
 
-static ssize_t qemu_vlan_deliver_packet(VLANClientState *sender,
-                                        unsigned flags,
-                                        const uint8_t *buf,
-                                        size_t size,
-                                        void *opaque)
+void qemu_purge_queued_packets(NetClientState *nc)
 {
-    VLANState *vlan = opaque;
-    VLANClientState *vc;
-    ssize_t ret = -1;
-
-    QTAILQ_FOREACH(vc, &vlan->clients, next) {
-        ssize_t len;
-
-        if (vc == sender) {
-            continue;
-        }
-
-        if (vc->link_down) {
-            ret = size;
-            continue;
-        }
-
-        if (vc->receive_disabled) {
-            ret = 0;
-            continue;
-        }
-
-        if (flags & QEMU_NET_PACKET_FLAG_RAW && vc->info->receive_raw) {
-            len = vc->info->receive_raw(vc, buf, size);
-        } else {
-            len = vc->info->receive(vc, buf, size);
-        }
-
-        if (len == 0) {
-            vc->receive_disabled = 1;
-        }
-
-        ret = (ret >= 0) ? ret : len;
-
-    }
-
-    return ret;
-}
-
-void qemu_purge_queued_packets(VLANClientState *vc)
-{
-    NetQueue *queue;
-
-    if (!vc->peer && !vc->vlan) {
+    if (!nc->peer) {
         return;
     }
 
-    if (vc->peer) {
-        queue = vc->peer->send_queue;
-    } else {
-        queue = vc->vlan->send_queue;
-    }
-
-    qemu_net_queue_purge(queue, vc);
+    qemu_net_queue_purge(nc->peer->send_queue, nc);
 }
 
-void qemu_flush_queued_packets(VLANClientState *vc)
+void qemu_flush_queued_packets(NetClientState *nc)
 {
-    NetQueue *queue;
-
-    vc->receive_disabled = 0;
-
-    if (vc->vlan) {
-        queue = vc->vlan->send_queue;
-    } else {
-        queue = vc->send_queue;
-    }
+    nc->receive_disabled = 0;
 
-    qemu_net_queue_flush(queue);
+    qemu_net_queue_flush(nc->send_queue);
 }
 
-static ssize_t qemu_send_packet_async_with_flags(VLANClientState *sender,
+static ssize_t qemu_send_packet_async_with_flags(NetClientState *sender,
                                                  unsigned flags,
                                                  const uint8_t *buf, int size,
                                                  NetPacketSent *sent_cb)
@@ -537,20 +372,16 @@ static ssize_t qemu_send_packet_async_with_flags(VLANClientState *sender,
     hex_dump(stdout, buf, size);
 #endif
 
-    if (sender->link_down || (!sender->peer && !sender->vlan)) {
+    if (sender->link_down || !sender->peer) {
         return size;
     }
 
-    if (sender->peer) {
-        queue = sender->peer->send_queue;
-    } else {
-        queue = sender->vlan->send_queue;
-    }
+    queue = sender->peer->send_queue;
 
     return qemu_net_queue_send(queue, sender, flags, buf, size, sent_cb);
 }
 
-ssize_t qemu_send_packet_async(VLANClientState *sender,
+ssize_t qemu_send_packet_async(NetClientState *sender,
                                const uint8_t *buf, int size,
                                NetPacketSent *sent_cb)
 {
@@ -558,98 +389,58 @@ ssize_t qemu_send_packet_async(VLANClientState *sender,
                                              buf, size, sent_cb);
 }
 
-void qemu_send_packet(VLANClientState *vc, const uint8_t *buf, int size)
+void qemu_send_packet(NetClientState *nc, const uint8_t *buf, int size)
 {
-    qemu_send_packet_async(vc, buf, size, NULL);
+    qemu_send_packet_async(nc, buf, size, NULL);
 }
 
-ssize_t qemu_send_packet_raw(VLANClientState *vc, const uint8_t *buf, int size)
+ssize_t qemu_send_packet_raw(NetClientState *nc, const uint8_t *buf, int size)
 {
-    return qemu_send_packet_async_with_flags(vc, QEMU_NET_PACKET_FLAG_RAW,
+    return qemu_send_packet_async_with_flags(nc, QEMU_NET_PACKET_FLAG_RAW,
                                              buf, size, NULL);
 }
 
-static ssize_t vc_sendv_compat(VLANClientState *vc, const struct iovec *iov,
+static ssize_t nc_sendv_compat(NetClientState *nc, const struct iovec *iov,
                                int iovcnt)
 {
     uint8_t buffer[4096];
     size_t offset;
 
-    offset = iov_to_buf(iov, iovcnt, buffer, 0, sizeof(buffer));
+    offset = iov_to_buf(iov, iovcnt, 0, buffer, sizeof(buffer));
 
-    return vc->info->receive(vc, buffer, offset);
+    return nc->info->receive(nc, buffer, offset);
 }
 
-static ssize_t qemu_deliver_packet_iov(VLANClientState *sender,
-                                       unsigned flags,
-                                       const struct iovec *iov,
-                                       int iovcnt,
-                                       void *opaque)
+ssize_t qemu_deliver_packet_iov(NetClientState *sender,
+                                unsigned flags,
+                                const struct iovec *iov,
+                                int iovcnt,
+                                void *opaque)
 {
-    VLANClientState *vc = opaque;
+    NetClientState *nc = opaque;
 
-    if (vc->link_down) {
+    if (nc->link_down) {
         return iov_size(iov, iovcnt);
     }
 
-    if (vc->info->receive_iov) {
-        return vc->info->receive_iov(vc, iov, iovcnt);
+    if (nc->info->receive_iov) {
+        return nc->info->receive_iov(nc, iov, iovcnt);
     } else {
-        return vc_sendv_compat(vc, iov, iovcnt);
+        return nc_sendv_compat(nc, iov, iovcnt);
     }
 }
 
-static ssize_t qemu_vlan_deliver_packet_iov(VLANClientState *sender,
-                                            unsigned flags,
-                                            const struct iovec *iov,
-                                            int iovcnt,
-                                            void *opaque)
-{
-    VLANState *vlan = opaque;
-    VLANClientState *vc;
-    ssize_t ret = -1;
-
-    QTAILQ_FOREACH(vc, &vlan->clients, next) {
-        ssize_t len;
-
-        if (vc == sender) {
-            continue;
-        }
-
-        if (vc->link_down) {
-            ret = iov_size(iov, iovcnt);
-            continue;
-        }
-
-        assert(!(flags & QEMU_NET_PACKET_FLAG_RAW));
-
-        if (vc->info->receive_iov) {
-            len = vc->info->receive_iov(vc, iov, iovcnt);
-        } else {
-            len = vc_sendv_compat(vc, iov, iovcnt);
-        }
-
-        ret = (ret >= 0) ? ret : len;
-    }
-
-    return ret;
-}
-
-ssize_t qemu_sendv_packet_async(VLANClientState *sender,
+ssize_t qemu_sendv_packet_async(NetClientState *sender,
                                 const struct iovec *iov, int iovcnt,
                                 NetPacketSent *sent_cb)
 {
     NetQueue *queue;
 
-    if (sender->link_down || (!sender->peer && !sender->vlan)) {
+    if (sender->link_down || !sender->peer) {
         return iov_size(iov, iovcnt);
     }
 
-    if (sender->peer) {
-        queue = sender->peer->send_queue;
-    } else {
-        queue = sender->vlan->send_queue;
-    }
+    queue = sender->peer->send_queue;
 
     return qemu_net_queue_send_iov(queue, sender,
                                    QEMU_NET_PACKET_FLAG_NONE,
@@ -657,46 +448,20 @@ ssize_t qemu_sendv_packet_async(VLANClientState *sender,
 }
 
 ssize_t
-qemu_sendv_packet(VLANClientState *vc, const struct iovec *iov, int iovcnt)
+qemu_sendv_packet(NetClientState *nc, const struct iovec *iov, int iovcnt)
 {
-    return qemu_sendv_packet_async(vc, iov, iovcnt, NULL);
-}
-
-/* find or alloc a new VLAN */
-VLANState *qemu_find_vlan(int id, int allocate)
-{
-    VLANState *vlan;
-
-    QTAILQ_FOREACH(vlan, &vlans, next) {
-        if (vlan->id == id) {
-            return vlan;
-        }
-    }
-
-    if (!allocate) {
-        return NULL;
-    }
-
-    vlan = qemu_mallocz(sizeof(VLANState));
-    vlan->id = id;
-    QTAILQ_INIT(&vlan->clients);
-
-    vlan->send_queue = qemu_new_net_queue(qemu_vlan_deliver_packet,
-                                          qemu_vlan_deliver_packet_iov,
-                                          vlan);
-
-    QTAILQ_INSERT_TAIL(&vlans, vlan, next);
-
-    return vlan;
+    return qemu_sendv_packet_async(nc, iov, iovcnt, NULL);
 }
 
-VLANClientState *qemu_find_netdev(const char *id)
+NetClientState *qemu_find_netdev(const char *id)
 {
-    VLANClientState *vc;
+    NetClientState *nc;
 
-    QTAILQ_FOREACH(vc, &non_vlan_clients, next) {
-        if (!strcmp(vc->name, id)) {
-            return vc;
+    QTAILQ_FOREACH(nc, &net_clients, next) {
+        if (nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC)
+            continue;
+        if (!strcmp(nc->name, id)) {
+            return nc;
         }
     }
 
@@ -717,8 +482,9 @@ int qemu_show_nic_models(const char *arg, const char *const *models)
 {
     int i;
 
-    if (!arg || strcmp(arg, "?"))
+    if (!arg || !is_help_option(arg)) {
         return 0;
+    }
 
     fprintf(stderr, "qemu: Supported NIC models: ");
     for (i = 0 ; models[i]; i++)
@@ -745,14 +511,14 @@ int qemu_find_nic_model(NICInfo *nd, const char * const *models,
     int i;
 
     if (!nd->model)
-        nd->model = qemu_strdup(default_model);
+        nd->model = g_strdup(default_model);
 
     for (i = 0 ; models[i]; i++) {
         if (strcmp(nd->model, models[i]) == 0)
             return i;
     }
 
-    error_report("qemu: Unsupported NIC model: %s", nd->model);
+    error_report("Unsupported NIC model: %s", nd->model);
     return -1;
 }
 
@@ -768,25 +534,21 @@ int net_handle_fd_param(Monitor *mon, const char *param)
             return -1;
         }
     } else {
-        char *endptr = NULL;
-
-        fd = strtol(param, &endptr, 10);
-        if (*endptr || (fd == 0 && param == endptr)) {
-            return -1;
-        }
+        fd = qemu_parse_fd(param);
     }
 
     return fd;
 }
 
-static int net_init_nic(QemuOpts *opts,
-                        Monitor *mon,
-                        const char *name,
-                        VLANState *vlan)
+static int net_init_nic(const NetClientOptions *opts, const char *name,
+                        NetClientState *peer)
 {
     int idx;
     NICInfo *nd;
-    const char *netdev;
+    const NetLegacyNicOptions *nic;
+
+    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_NIC);
+    nic = opts->nic;
 
     idx = nic_get_free_idx();
     if (idx == -1 || nb_nics >= MAX_NICS) {
@@ -798,45 +560,41 @@ static int net_init_nic(QemuOpts *opts,
 
     memset(nd, 0, sizeof(*nd));
 
-    if ((netdev = qemu_opt_get(opts, "netdev"))) {
-        nd->netdev = qemu_find_netdev(netdev);
+    if (nic->has_netdev) {
+        nd->netdev = qemu_find_netdev(nic->netdev);
         if (!nd->netdev) {
-            error_report("netdev '%s' not found", netdev);
+            error_report("netdev '%s' not found", nic->netdev);
             return -1;
         }
     } else {
-        assert(vlan);
-        nd->vlan = vlan;
+        assert(peer);
+        nd->netdev = peer;
     }
     if (name) {
-        nd->name = qemu_strdup(name);
+        nd->name = g_strdup(name);
     }
-    if (qemu_opt_get(opts, "model")) {
-        nd->model = qemu_strdup(qemu_opt_get(opts, "model"));
+    if (nic->has_model) {
+        nd->model = g_strdup(nic->model);
     }
-    if (qemu_opt_get(opts, "addr")) {
-        nd->devaddr = qemu_strdup(qemu_opt_get(opts, "addr"));
+    if (nic->has_addr) {
+        nd->devaddr = g_strdup(nic->addr);
     }
 
-    nd->macaddr[0] = 0x52;
-    nd->macaddr[1] = 0x54;
-    nd->macaddr[2] = 0x00;
-    nd->macaddr[3] = 0x12;
-    nd->macaddr[4] = 0x34;
-    nd->macaddr[5] = 0x56 + idx;
-
-    if (qemu_opt_get(opts, "macaddr") &&
-        net_parse_macaddr(nd->macaddr, qemu_opt_get(opts, "macaddr")) < 0) {
+    if (nic->has_macaddr &&
+        net_parse_macaddr(nd->macaddr.a, nic->macaddr) < 0) {
         error_report("invalid syntax for ethernet address");
         return -1;
     }
+    qemu_macaddr_default_if_unset(&nd->macaddr);
 
-    nd->nvectors = qemu_opt_get_number(opts, "vectors",
-                                       DEV_NVECTORS_UNSPECIFIED);
-    if (nd->nvectors != DEV_NVECTORS_UNSPECIFIED &&
-        (nd->nvectors < 0 || nd->nvectors > 0x7ffffff)) {
-        error_report("invalid # of vectors: %d", nd->nvectors);
-        return -1;
+    if (nic->has_vectors) {
+        if (nic->vectors > 0x7ffffff) {
+            error_report("invalid # of vectors: %"PRIu32, nic->vectors);
+            return -1;
+        }
+        nd->nvectors = nic->vectors;
+    } else {
+        nd->nvectors = DEV_NVECTORS_UNSPECIFIED;
     }
 
     nd->used = 1;
@@ -845,331 +603,137 @@ static int net_init_nic(QemuOpts *opts,
     return idx;
 }
 
-#define NET_COMMON_PARAMS_DESC                     \
-    {                                              \
-        .name = "type",                            \
-        .type = QEMU_OPT_STRING,                   \
-        .help = "net client type (nic, tap etc.)", \
-     }, {                                          \
-        .name = "vlan",                            \
-        .type = QEMU_OPT_NUMBER,                   \
-        .help = "vlan number",                     \
-     }, {                                          \
-        .name = "name",                            \
-        .type = QEMU_OPT_STRING,                   \
-        .help = "identifier for monitor commands", \
-     }
-
-typedef int (*net_client_init_func)(QemuOpts *opts,
-                                    Monitor *mon,
-                                    const char *name,
-                                    VLANState *vlan);
-
-/* magic number, but compiler will warn if too small */
-#define NET_MAX_DESC 20
-
-static const struct {
-    const char *type;
-    net_client_init_func init;
-    QemuOptDesc desc[NET_MAX_DESC];
-} net_client_types[] = {
-    {
-        .type = "none",
-        .desc = {
-            NET_COMMON_PARAMS_DESC,
-            { /* end of list */ }
-        },
-    }, {
-        .type = "nic",
-        .init = net_init_nic,
-        .desc = {
-            NET_COMMON_PARAMS_DESC,
-            {
-                .name = "netdev",
-                .type = QEMU_OPT_STRING,
-                .help = "id of -netdev to connect to",
-            },
-            {
-                .name = "macaddr",
-                .type = QEMU_OPT_STRING,
-                .help = "MAC address",
-            }, {
-                .name = "model",
-                .type = QEMU_OPT_STRING,
-                .help = "device model (e1000, rtl8139, virtio etc.)",
-            }, {
-                .name = "addr",
-                .type = QEMU_OPT_STRING,
-                .help = "PCI device address",
-            }, {
-                .name = "vectors",
-                .type = QEMU_OPT_NUMBER,
-                .help = "number of MSI-x vectors, 0 to disable MSI-X",
-            },
-            { /* end of list */ }
-        },
+
+static int (* const net_client_init_fun[NET_CLIENT_OPTIONS_KIND_MAX])(
+    const NetClientOptions *opts,
+    const char *name,
+    NetClientState *peer) = {
+        [NET_CLIENT_OPTIONS_KIND_NIC]       = net_init_nic,
 #ifdef CONFIG_SLIRP
-    }, {
-        .type = "user",
-        .init = net_init_slirp,
-        .desc = {
-            NET_COMMON_PARAMS_DESC,
-            {
-                .name = "hostname",
-                .type = QEMU_OPT_STRING,
-                .help = "client hostname reported by the builtin DHCP server",
-            }, {
-                .name = "restrict",
-                .type = QEMU_OPT_STRING,
-                .help = "isolate the guest from the host (y|yes|n|no)",
-            }, {
-                .name = "ip",
-                .type = QEMU_OPT_STRING,
-                .help = "legacy parameter, use net= instead",
-            }, {
-                .name = "net",
-                .type = QEMU_OPT_STRING,
-                .help = "IP address and optional netmask",
-            }, {
-                .name = "host",
-                .type = QEMU_OPT_STRING,
-                .help = "guest-visible address of the host",
-            }, {
-                .name = "tftp",
-                .type = QEMU_OPT_STRING,
-                .help = "root directory of the built-in TFTP server",
-            }, {
-                .name = "bootfile",
-                .type = QEMU_OPT_STRING,
-                .help = "BOOTP filename, for use with tftp=",
-            }, {
-                .name = "dhcpstart",
-                .type = QEMU_OPT_STRING,
-                .help = "the first of the 16 IPs the built-in DHCP server can assign",
-            }, {
-                .name = "dns",
-                .type = QEMU_OPT_STRING,
-                .help = "guest-visible address of the virtual nameserver",
-            }, {
-                .name = "smb",
-                .type = QEMU_OPT_STRING,
-                .help = "root directory of the built-in SMB server",
-            }, {
-                .name = "smbserver",
-                .type = QEMU_OPT_STRING,
-                .help = "IP address of the built-in SMB server",
-            }, {
-                .name = "hostfwd",
-                .type = QEMU_OPT_STRING,
-                .help = "guest port number to forward incoming TCP or UDP connections",
-            }, {
-                .name = "guestfwd",
-                .type = QEMU_OPT_STRING,
-                .help = "IP address and port to forward guest TCP connections",
-            },
-            { /* end of list */ }
-        },
+        [NET_CLIENT_OPTIONS_KIND_USER]      = net_init_slirp,
 #endif
-    }, {
-        .type = "tap",
-        .init = net_init_tap,
-        .desc = {
-            NET_COMMON_PARAMS_DESC,
-            {
-                .name = "ifname",
-                .type = QEMU_OPT_STRING,
-                .help = "interface name",
-            },
-#ifndef _WIN32
-            {
-                .name = "fd",
-                .type = QEMU_OPT_STRING,
-                .help = "file descriptor of an already opened tap",
-            }, {
-                .name = "script",
-                .type = QEMU_OPT_STRING,
-                .help = "script to initialize the interface",
-            }, {
-                .name = "downscript",
-                .type = QEMU_OPT_STRING,
-                .help = "script to shut down the interface",
-            }, {
-                .name = "sndbuf",
-                .type = QEMU_OPT_SIZE,
-                .help = "send buffer limit"
-            }, {
-                .name = "vnet_hdr",
-                .type = QEMU_OPT_BOOL,
-                .help = "enable the IFF_VNET_HDR flag on the tap interface"
-            }, {
-                .name = "vhost",
-                .type = QEMU_OPT_BOOL,
-                .help = "enable vhost-net network accelerator",
-            }, {
-                .name = "vhostfd",
-                .type = QEMU_OPT_STRING,
-                .help = "file descriptor of an already opened vhost net device",
-            },
-#endif /* _WIN32 */
-            { /* end of list */ }
-        },
-    }, {
-        .type = "socket",
-        .init = net_init_socket,
-        .desc = {
-            NET_COMMON_PARAMS_DESC,
-            {
-                .name = "fd",
-                .type = QEMU_OPT_STRING,
-                .help = "file descriptor of an already opened socket",
-            }, {
-                .name = "listen",
-                .type = QEMU_OPT_STRING,
-                .help = "port number, and optional hostname, to listen on",
-            }, {
-                .name = "connect",
-                .type = QEMU_OPT_STRING,
-                .help = "port number, and optional hostname, to connect to",
-            }, {
-                .name = "mcast",
-                .type = QEMU_OPT_STRING,
-                .help = "UDP multicast address and port number",
-            }, {
-                .name = "localaddr",
-                .type = QEMU_OPT_STRING,
-                .help = "source address for multicast packets",
-            },
-            { /* end of list */ }
-        },
+        [NET_CLIENT_OPTIONS_KIND_TAP]       = net_init_tap,
+        [NET_CLIENT_OPTIONS_KIND_SOCKET]    = net_init_socket,
 #ifdef CONFIG_VDE
-    }, {
-        .type = "vde",
-        .init = net_init_vde,
-        .desc = {
-            NET_COMMON_PARAMS_DESC,
-            {
-                .name = "sock",
-                .type = QEMU_OPT_STRING,
-                .help = "socket path",
-            }, {
-                .name = "port",
-                .type = QEMU_OPT_NUMBER,
-                .help = "port number",
-            }, {
-                .name = "group",
-                .type = QEMU_OPT_STRING,
-                .help = "group owner of socket",
-            }, {
-                .name = "mode",
-                .type = QEMU_OPT_NUMBER,
-                .help = "permissions for socket",
-            },
-            { /* end of list */ }
-        },
+        [NET_CLIENT_OPTIONS_KIND_VDE]       = net_init_vde,
+#endif
+        [NET_CLIENT_OPTIONS_KIND_DUMP]      = net_init_dump,
+#ifdef CONFIG_NET_BRIDGE
+        [NET_CLIENT_OPTIONS_KIND_BRIDGE]    = net_init_bridge,
 #endif
-    }, {
-        .type = "dump",
-        .init = net_init_dump,
-        .desc = {
-            NET_COMMON_PARAMS_DESC,
-            {
-                .name = "len",
-                .type = QEMU_OPT_SIZE,
-                .help = "per-packet size limit (64k default)",
-            }, {
-                .name = "file",
-                .type = QEMU_OPT_STRING,
-                .help = "dump file path (default is qemu-vlan0.pcap)",
-            },
-            { /* end of list */ }
-        },
-    },
-    { /* end of list */ }
+        [NET_CLIENT_OPTIONS_KIND_HUBPORT]   = net_init_hubport,
 };
 
-int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev)
+
+static int net_client_init1(const void *object, int is_netdev, Error **errp)
 {
+    union {
+        const Netdev    *netdev;
+        const NetLegacy *net;
+    } u;
+    const NetClientOptions *opts;
     const char *name;
-    const char *type;
-    int i;
-
-    type = qemu_opt_get(opts, "type");
-    if (!type) {
-        qerror_report(QERR_MISSING_PARAMETER, "type");
-        return -1;
-    }
 
     if (is_netdev) {
-        if (strcmp(type, "tap") != 0 &&
+        u.netdev = object;
+        opts = u.netdev->opts;
+        name = u.netdev->id;
+
+        switch (opts->kind) {
 #ifdef CONFIG_SLIRP
-            strcmp(type, "user") != 0 &&
+        case NET_CLIENT_OPTIONS_KIND_USER:
 #endif
+        case NET_CLIENT_OPTIONS_KIND_TAP:
+        case NET_CLIENT_OPTIONS_KIND_SOCKET:
 #ifdef CONFIG_VDE
-            strcmp(type, "vde") != 0 &&
+        case NET_CLIENT_OPTIONS_KIND_VDE:
 #endif
-            strcmp(type, "socket") != 0) {
-            qerror_report(QERR_INVALID_PARAMETER_VALUE, "type",
-                          "a netdev backend type");
-            return -1;
-        }
+#ifdef CONFIG_NET_BRIDGE
+        case NET_CLIENT_OPTIONS_KIND_BRIDGE:
+#endif
+        case NET_CLIENT_OPTIONS_KIND_HUBPORT:
+            break;
 
-        if (qemu_opt_get(opts, "vlan")) {
-            qerror_report(QERR_INVALID_PARAMETER, "vlan");
+        default:
+            error_set(errp, QERR_INVALID_PARAMETER_VALUE, "type",
+                      "a netdev backend type");
             return -1;
         }
-        if (qemu_opt_get(opts, "name")) {
-            qerror_report(QERR_INVALID_PARAMETER, "name");
-            return -1;
+    } else {
+        u.net = object;
+        opts = u.net->opts;
+        /* missing optional values have been initialized to "all bits zero" */
+        name = u.net->has_id ? u.net->id : u.net->name;
+    }
+
+    if (net_client_init_fun[opts->kind]) {
+        NetClientState *peer = NULL;
+
+        /* Do not add to a vlan if it's a -netdev or a nic with a netdev=
+         * parameter. */
+        if (!is_netdev &&
+            (opts->kind != NET_CLIENT_OPTIONS_KIND_NIC ||
+             !opts->nic->has_netdev)) {
+            peer = net_hub_add_port(u.net->has_vlan ? u.net->vlan : 0, NULL);
         }
-        if (!qemu_opts_id(opts)) {
-            qerror_report(QERR_MISSING_PARAMETER, "id");
+
+        if (net_client_init_fun[opts->kind](opts, name, peer) < 0) {
+            /* TODO push error reporting into init() methods */
+            error_set(errp, QERR_DEVICE_INIT_FAILED,
+                      NetClientOptionsKind_lookup[opts->kind]);
             return -1;
         }
     }
+    return 0;
+}
+
 
-    name = qemu_opts_id(opts);
-    if (!name) {
-        name = qemu_opt_get(opts, "name");
+static void net_visit(Visitor *v, int is_netdev, void **object, Error **errp)
+{
+    if (is_netdev) {
+        visit_type_Netdev(v, (Netdev **)object, NULL, errp);
+    } else {
+        visit_type_NetLegacy(v, (NetLegacy **)object, NULL, errp);
     }
+}
 
-    for (i = 0; net_client_types[i].type != NULL; i++) {
-        if (!strcmp(net_client_types[i].type, type)) {
-            VLANState *vlan = NULL;
-            int ret;
 
-            if (qemu_opts_validate(opts, &net_client_types[i].desc[0]) == -1) {
-                return -1;
-            }
+int net_client_init(QemuOpts *opts, int is_netdev, Error **errp)
+{
+    void *object = NULL;
+    Error *err = NULL;
+    int ret = -1;
 
-            /* Do not add to a vlan if it's a -netdev or a nic with a
-             * netdev= parameter. */
-            if (!(is_netdev ||
-                  (strcmp(type, "nic") == 0 && qemu_opt_get(opts, "netdev")))) {
-                vlan = qemu_find_vlan(qemu_opt_get_number(opts, "vlan", 0), 1);
-            }
+    {
+        OptsVisitor *ov = opts_visitor_new(opts);
 
-            ret = 0;
-            if (net_client_types[i].init) {
-                ret = net_client_types[i].init(opts, mon, name, vlan);
-                if (ret < 0) {
-                    /* TODO push error reporting into init() methods */
-                    qerror_report(QERR_DEVICE_INIT_FAILED, type);
-                    return -1;
-                }
-            }
-            return ret;
-        }
+        net_visit(opts_get_visitor(ov), is_netdev, &object, &err);
+        opts_visitor_cleanup(ov);
     }
 
-    qerror_report(QERR_INVALID_PARAMETER_VALUE, "type",
-                  "a network client type");
-    return -1;
+    if (!err) {
+        ret = net_client_init1(object, is_netdev, &err);
+    }
+
+    if (object) {
+        QapiDeallocVisitor *dv = qapi_dealloc_visitor_new();
+
+        net_visit(qapi_dealloc_get_visitor(dv), is_netdev, &object, NULL);
+        qapi_dealloc_visitor_cleanup(dv);
+    }
+
+    error_propagate(errp, err);
+    return ret;
 }
 
+
 static int net_host_check_device(const char *device)
 {
     int i;
     const char *valid_param_list[] = { "tap", "socket", "dump"
+#ifdef CONFIG_NET_BRIDGE
+                                       , "bridge"
+#endif
 #ifdef CONFIG_SLIRP
                                        ,"user"
 #endif
@@ -1190,6 +754,7 @@ void net_host_device_add(Monitor *mon, const QDict *qdict)
 {
     const char *device = qdict_get_str(qdict, "device");
     const char *opts_str = qdict_get_try_str(qdict, "opts");
+    Error *local_err = NULL;
     QemuOpts *opts;
 
     if (!net_host_check_device(device)) {
@@ -1204,109 +769,131 @@ void net_host_device_add(Monitor *mon, const QDict *qdict)
 
     qemu_opt_set(opts, "type", device);
 
-    if (net_client_init(mon, opts, 0) < 0) {
+    net_client_init(opts, 0, &local_err);
+    if (error_is_set(&local_err)) {
+        qerror_report_err(local_err);
+        error_free(local_err);
         monitor_printf(mon, "adding host network device %s failed\n", device);
     }
 }
 
 void net_host_device_remove(Monitor *mon, const QDict *qdict)
 {
-    VLANClientState *vc;
+    NetClientState *nc;
     int vlan_id = qdict_get_int(qdict, "vlan_id");
     const char *device = qdict_get_str(qdict, "device");
 
-    vc = qemu_find_vlan_client_by_name(mon, vlan_id, device);
-    if (!vc) {
+    nc = net_hub_find_client_by_name(vlan_id, device);
+    if (!nc) {
         return;
     }
-    if (!net_host_check_device(vc->model)) {
+    if (!net_host_check_device(nc->model)) {
         monitor_printf(mon, "invalid host network device %s\n", device);
         return;
     }
-    qemu_del_vlan_client(vc);
+    qemu_del_net_client(nc);
 }
 
-int do_netdev_add(Monitor *mon, const QDict *qdict, QObject **ret_data)
+void netdev_add(QemuOpts *opts, Error **errp)
 {
+    net_client_init(opts, 1, errp);
+}
+
+int qmp_netdev_add(Monitor *mon, const QDict *qdict, QObject **ret)
+{
+    Error *local_err = NULL;
+    QemuOptsList *opts_list;
     QemuOpts *opts;
-    int res;
 
-    opts = qemu_opts_from_qdict(qemu_find_opts("netdev"), qdict);
-    if (!opts) {
-        return -1;
+    opts_list = qemu_find_opts_err("netdev", &local_err);
+    if (error_is_set(&local_err)) {
+        goto exit_err;
     }
 
-    res = net_client_init(mon, opts, 1);
-    if (res < 0) {
+    opts = qemu_opts_from_qdict(opts_list, qdict, &local_err);
+    if (error_is_set(&local_err)) {
+        goto exit_err;
+    }
+
+    netdev_add(opts, &local_err);
+    if (error_is_set(&local_err)) {
         qemu_opts_del(opts);
+        goto exit_err;
     }
 
-    return res;
+    return 0;
+
+exit_err:
+    qerror_report_err(local_err);
+    error_free(local_err);
+    return -1;
 }
 
-int do_netdev_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
+void qmp_netdev_del(const char *id, Error **errp)
 {
-    const char *id = qdict_get_str(qdict, "id");
-    VLANClientState *vc;
+    NetClientState *nc;
 
-    vc = qemu_find_netdev(id);
-    if (!vc || vc->info->type == NET_CLIENT_TYPE_NIC) {
-        qerror_report(QERR_DEVICE_NOT_FOUND, id);
-        return -1;
+    nc = qemu_find_netdev(id);
+    if (!nc) {
+        error_set(errp, QERR_DEVICE_NOT_FOUND, id);
+        return;
     }
-    qemu_del_vlan_client(vc);
-    qemu_opts_del(qemu_opts_find(qemu_find_opts("netdev"), id));
-    return 0;
+
+    qemu_del_net_client(nc);
+    qemu_opts_del(qemu_opts_find(qemu_find_opts_err("netdev", errp), id));
+}
+
+void print_net_client(Monitor *mon, NetClientState *nc)
+{
+    monitor_printf(mon, "%s: type=%s,%s\n", nc->name,
+                   NetClientOptionsKind_lookup[nc->info->type], nc->info_str);
 }
 
 void do_info_network(Monitor *mon)
 {
-    VLANState *vlan;
-    VLANClientState *vc;
+    NetClientState *nc, *peer;
+    NetClientOptionsKind type;
+
+    net_hub_info(mon);
 
-    QTAILQ_FOREACH(vlan, &vlans, next) {
-        monitor_printf(mon, "VLAN %d devices:\n", vlan->id);
+    QTAILQ_FOREACH(nc, &net_clients, next) {
+        peer = nc->peer;
+        type = nc->info->type;
 
-        QTAILQ_FOREACH(vc, &vlan->clients, next) {
-            monitor_printf(mon, "  %s: %s\n", vc->name, vc->info_str);
+        /* Skip if already printed in hub info */
+        if (net_hub_id_for_client(nc, NULL) == 0) {
+            continue;
         }
-    }
-    monitor_printf(mon, "Devices not on any VLAN:\n");
-    QTAILQ_FOREACH(vc, &non_vlan_clients, next) {
-        monitor_printf(mon, "  %s: %s", vc->name, vc->info_str);
-        if (vc->peer) {
-            monitor_printf(mon, " peer=%s", vc->peer->name);
+
+        if (!peer || type == NET_CLIENT_OPTIONS_KIND_NIC) {
+            print_net_client(mon, nc);
+        } /* else it's a netdev connected to a NIC, printed with the NIC */
+        if (peer && type == NET_CLIENT_OPTIONS_KIND_NIC) {
+            monitor_printf(mon, " \\ ");
+            print_net_client(mon, peer);
         }
-        monitor_printf(mon, "\n");
     }
 }
 
-int do_set_link(Monitor *mon, const QDict *qdict, QObject **ret_data)
+void qmp_set_link(const char *name, bool up, Error **errp)
 {
-    VLANState *vlan;
-    VLANClientState *vc = NULL;
-    const char *name = qdict_get_str(qdict, "name");
-    int up = qdict_get_bool(qdict, "up");
-
-    QTAILQ_FOREACH(vlan, &vlans, next) {
-        QTAILQ_FOREACH(vc, &vlan->clients, next) {
-            if (strcmp(vc->name, name) == 0) {
-                goto done;
-            }
+    NetClientState *nc = NULL;
+
+    QTAILQ_FOREACH(nc, &net_clients, next) {
+        if (!strcmp(nc->name, name)) {
+            goto done;
         }
     }
-    vc = qemu_find_netdev(name);
 done:
-
-    if (!vc) {
-        qerror_report(QERR_DEVICE_NOT_FOUND, name);
-        return -1;
+    if (!nc) {
+        error_set(errp, QERR_DEVICE_NOT_FOUND, name);
+        return;
     }
 
-    vc->link_down = !up;
+    nc->link_down = !up;
 
-    if (vc->info->link_status_changed) {
-        vc->info->link_status_changed(vc);
+    if (nc->info->link_status_changed) {
+        nc->info->link_status_changed(nc);
     }
 
     /* Notify peer. Don't update peer link status: this makes it possible to
@@ -1316,75 +903,89 @@ done:
      * Current behaviour is compatible with qemu vlans where there could be
      * multiple clients that can still communicate with each other in
      * disconnected mode. For now maintain this compatibility. */
-    if (vc->peer && vc->peer->info->link_status_changed) {
-        vc->peer->info->link_status_changed(vc->peer);
+    if (nc->peer && nc->peer->info->link_status_changed) {
+        nc->peer->info->link_status_changed(nc->peer);
     }
-    return 0;
 }
 
 void net_cleanup(void)
 {
-    VLANState *vlan;
-    VLANClientState *vc, *next_vc;
-
-    QTAILQ_FOREACH(vlan, &vlans, next) {
-        QTAILQ_FOREACH_SAFE(vc, &vlan->clients, next, next_vc) {
-            qemu_del_vlan_client(vc);
-        }
-    }
+    NetClientState *nc, *next_vc;
 
-    QTAILQ_FOREACH_SAFE(vc, &non_vlan_clients, next, next_vc) {
-        qemu_del_vlan_client(vc);
+    QTAILQ_FOREACH_SAFE(nc, &net_clients, next, next_vc) {
+        qemu_del_net_client(nc);
     }
 }
 
 void net_check_clients(void)
 {
-    VLANState *vlan;
-    VLANClientState *vc;
-    int has_nic = 0, has_host_dev = 0;
-
-    QTAILQ_FOREACH(vlan, &vlans, next) {
-        QTAILQ_FOREACH(vc, &vlan->clients, next) {
-            switch (vc->info->type) {
-            case NET_CLIENT_TYPE_NIC:
-                has_nic = 1;
-                break;
-            case NET_CLIENT_TYPE_SLIRP:
-            case NET_CLIENT_TYPE_TAP:
-            case NET_CLIENT_TYPE_SOCKET:
-            case NET_CLIENT_TYPE_VDE:
-                has_host_dev = 1;
-                break;
-            default: ;
-            }
-        }
-        if (has_host_dev && !has_nic)
-            fprintf(stderr, "Warning: vlan %d with no nics\n", vlan->id);
-        if (has_nic && !has_host_dev)
-            fprintf(stderr,
-                    "Warning: vlan %d is not connected to host network\n",
-                    vlan->id);
-    }
-    QTAILQ_FOREACH(vc, &non_vlan_clients, next) {
-        if (!vc->peer) {
+    NetClientState *nc;
+    int i;
+
+    /* Don't warn about the default network setup that you get if
+     * no command line -net or -netdev options are specified. There
+     * are two cases that we would otherwise complain about:
+     * (1) board doesn't support a NIC but the implicit "-net nic"
+     * requested one
+     * (2) CONFIG_SLIRP not set, in which case the implicit "-net nic"
+     * sets up a nic that isn't connected to anything.
+     */
+    if (default_net) {
+        return;
+    }
+
+    net_hub_check_clients();
+
+    QTAILQ_FOREACH(nc, &net_clients, next) {
+        if (!nc->peer) {
             fprintf(stderr, "Warning: %s %s has no peer\n",
-                    vc->info->type == NET_CLIENT_TYPE_NIC ? "nic" : "netdev",
-                    vc->name);
+                    nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC ?
+                    "nic" : "netdev", nc->name);
+        }
+    }
+
+    /* Check that all NICs requested via -net nic actually got created.
+     * NICs created via -device don't need to be checked here because
+     * they are always instantiated.
+     */
+    for (i = 0; i < MAX_NICS; i++) {
+        NICInfo *nd = &nd_table[i];
+        if (nd->used && !nd->instantiated) {
+            fprintf(stderr, "Warning: requested NIC (%s, model %s) "
+                    "was not created (not supported by this machine?)\n",
+                    nd->name ? nd->name : "anonymous",
+                    nd->model ? nd->model : "unspecified");
         }
     }
 }
 
 static int net_init_client(QemuOpts *opts, void *dummy)
 {
-    if (net_client_init(NULL, opts, 0) < 0)
+    Error *local_err = NULL;
+
+    net_client_init(opts, 0, &local_err);
+    if (error_is_set(&local_err)) {
+        qerror_report_err(local_err);
+        error_free(local_err);
         return -1;
+    }
+
     return 0;
 }
 
 static int net_init_netdev(QemuOpts *opts, void *dummy)
 {
-    return net_client_init(NULL, opts, 1);
+    Error *local_err = NULL;
+    int ret;
+
+    ret = net_client_init(opts, 1, &local_err);
+    if (error_is_set(&local_err)) {
+        qerror_report_err(local_err);
+        error_free(local_err);
+        return -1;
+    }
+
+    return ret;
 }
 
 int net_init_clients(void)
@@ -1399,8 +1000,7 @@ int net_init_clients(void)
 #endif
     }
 
-    QTAILQ_INIT(&vlans);
-    QTAILQ_INIT(&non_vlan_clients);
+    QTAILQ_INIT(&net_clients);
 
     if (qemu_opts_foreach(qemu_find_opts("netdev"), net_init_netdev, NULL, 1) == -1)
         return -1;
@@ -1428,3 +1028,26 @@ int net_client_parse(QemuOptsList *opts_list, const char *optarg)
     default_net = 0;
     return 0;
 }
+
+/* From FreeBSD */
+/* XXX: optimize */
+unsigned compute_mcast_idx(const uint8_t *ep)
+{
+    uint32_t crc;
+    int carry, i, j;
+    uint8_t b;
+
+    crc = 0xffffffff;
+    for (i = 0; i < 6; i++) {
+        b = *ep++;
+        for (j = 0; j < 8; j++) {
+            carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01);
+            crc <<= 1;
+            b >>= 1;
+            if (carry) {
+                crc = ((crc ^ POLYNOMIAL) | carry);
+            }
+        }
+    }
+    return crc >> 26;
+}