]> git.proxmox.com Git - qemu.git/blobdiff - net.c
Fix linker scripts
[qemu.git] / net.c
diff --git a/net.c b/net.c
index a9ebaa2db4cbaa3ff7727006de2d790081214577..31c23389c835f8e7fe2c31190c0063a06e16cbe4 100644 (file)
--- a/net.c
+++ b/net.c
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  * THE SOFTWARE.
  */
-#include <unistd.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <time.h>
-#include <errno.h>
-#include <sys/time.h>
-#include <zlib.h>
-
-/* Needed early for CONFIG_BSD etc. */
-#include "config-host.h"
-
-#ifndef _WIN32
-#include <sys/times.h>
-#include <sys/wait.h>
-#include <termios.h>
-#include <sys/mman.h>
-#include <sys/ioctl.h>
-#include <sys/resource.h>
-#include <sys/socket.h>
-#include <net/if.h>
-#include <dirent.h>
-#include <netdb.h>
-#include <sys/select.h>
-#ifdef CONFIG_BSD
-#include <sys/stat.h>
-#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
-#include <libutil.h>
-#else
-#include <util.h>
-#endif
-#ifdef __linux__
-#include <pty.h>
-#include <malloc.h>
-#include <linux/rtc.h>
-
-/* For the benefit of older linux systems which don't supply it,
-   we use a local copy of hpet.h. */
-/* #include <linux/hpet.h> */
-#include "hpet.h"
-
-#include <linux/ppdev.h>
-#include <linux/parport.h>
-#endif
-#ifdef __sun__
-#include <sys/stat.h>
-#include <sys/ethernet.h>
-#include <sys/sockio.h>
-#include <netinet/arp.h>
-#include <netinet/in.h>
-#include <netinet/in_systm.h>
-#include <netinet/ip.h>
-#include <netinet/ip_icmp.h> // must come after ip.h
-#include <netinet/udp.h>
-#include <netinet/tcp.h>
-#include <net/if.h>
-#include <syslog.h>
-#include <stropts.h>
-#endif
-#endif
-#endif
+#include "net.h"
 
-#if defined(__OpenBSD__)
-#include <util.h>
-#endif
+#include "config-host.h"
 
-#include "qemu-common.h"
-#include "net.h"
 #include "net/tap.h"
+#include "net/socket.h"
+#include "net/dump.h"
 #include "net/slirp.h"
 #include "net/vde.h"
+#include "net/util.h"
 #include "monitor.h"
-#include "sysemu.h"
-#include "qemu-timer.h"
-#include "qemu-char.h"
-#include "audio/audio.h"
+#include "qemu-common.h"
 #include "qemu_socket.h"
-#include "qemu-log.h"
-#include "qemu-config.h"
+#include "hw/qdev.h"
+#include "iov.h"
 
 static QTAILQ_HEAD(, VLANState) vlans;
 static QTAILQ_HEAD(, VLANClientState) non_vlan_clients;
 
+int default_net = 1;
+
 /***********************************************************/
 /* network device redirectors */
 
@@ -134,38 +73,6 @@ static void hex_dump(FILE *f, const uint8_t *buf, int size)
 }
 #endif
 
-static int parse_macaddr(uint8_t *macaddr, const char *p)
-{
-    int i;
-    char *last_char;
-    long int offset;
-
-    errno = 0;
-    offset = strtol(p, &last_char, 0);    
-    if (0 == errno && '\0' == *last_char &&
-            offset >= 0 && offset <= 0xFFFFFF) {
-        macaddr[3] = (offset & 0xFF0000) >> 16;
-        macaddr[4] = (offset & 0xFF00) >> 8;
-        macaddr[5] = offset & 0xFF;
-        return 0;
-    } else {
-        for(i = 0; i < 6; i++) {
-            macaddr[i] = strtol(p, (char **)&p, 16);
-            if (i == 5) {
-                if (*p != '\0')
-                    return -1;
-            } else {
-                if (*p != ':' && *p != '-')
-                    return -1;
-                p++;
-            }
-        }
-        return 0;    
-    }
-
-    return -1;
-}
-
 static int get_str_sep(char *buf, int buf_size, const char **pp, int sep)
 {
     const char *p, *p1;
@@ -186,47 +93,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 = 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];
@@ -313,34 +179,25 @@ static ssize_t qemu_deliver_packet_iov(VLANClientState *sender,
                                        int iovcnt,
                                        void *opaque);
 
-VLANClientState *qemu_new_vlan_client(net_client_type type,
-                                      VLANState *vlan,
-                                      VLANClientState *peer,
-                                      const char *model,
-                                      const char *name,
-                                      NetCanReceive *can_receive,
-                                      NetReceive *receive,
-                                      NetReceive *receive_raw,
-                                      NetReceiveIOV *receive_iov,
-                                      NetCleanup *cleanup,
-                                      void *opaque)
+VLANClientState *qemu_new_net_client(NetClientInfo *info,
+                                     VLANState *vlan,
+                                     VLANClientState *peer,
+                                     const char *model,
+                                     const char *name)
 {
     VLANClientState *vc;
 
-    vc = qemu_mallocz(sizeof(VLANClientState));
+    assert(info->size >= sizeof(VLANClientState));
+
+    vc = qemu_mallocz(info->size);
 
-    vc->type = type;
+    vc->info = info;
     vc->model = qemu_strdup(model);
-    if (name)
+    if (name) {
         vc->name = qemu_strdup(name);
-    else
+    } else {
         vc->name = assign_name(vc, model);
-    vc->can_receive = can_receive;
-    vc->receive = receive;
-    vc->receive_raw = receive_raw;
-    vc->receive_iov = receive_iov;
-    vc->cleanup = cleanup;
-    vc->opaque = opaque;
+    }
 
     if (vlan) {
         assert(!peer);
@@ -348,6 +205,7 @@ VLANClientState *qemu_new_vlan_client(net_client_type type,
         QTAILQ_INSERT_TAIL(&vc->vlan->clients, vc, next);
     } else {
         if (peer) {
+            assert(!peer->peer);
             vc->peer = peer;
             peer->peer = vc;
         }
@@ -361,40 +219,83 @@ VLANClientState *qemu_new_vlan_client(net_client_type type,
     return vc;
 }
 
-void qemu_del_vlan_client(VLANClientState *vc)
+NICState *qemu_new_nic(NetClientInfo *info,
+                       NICConf *conf,
+                       const char *model,
+                       const char *name,
+                       void *opaque)
+{
+    VLANClientState *nc;
+    NICState *nic;
+
+    assert(info->type == NET_CLIENT_TYPE_NIC);
+    assert(info->size >= sizeof(NICState));
+
+    nc = qemu_new_net_client(info, conf->vlan, conf->peer, model, name);
+
+    nic = DO_UPCAST(NICState, nc, nc);
+    nic->conf = conf;
+    nic->opaque = opaque;
+
+    return nic;
+}
+
+static void qemu_cleanup_vlan_client(VLANClientState *vc)
 {
     if (vc->vlan) {
         QTAILQ_REMOVE(&vc->vlan->clients, vc, next);
     } else {
+        QTAILQ_REMOVE(&non_vlan_clients, vc, next);
+    }
+
+    if (vc->info->cleanup) {
+        vc->info->cleanup(vc);
+    }
+}
+
+static void qemu_free_vlan_client(VLANClientState *vc)
+{
+    if (!vc->vlan) {
         if (vc->send_queue) {
             qemu_del_net_queue(vc->send_queue);
         }
-        QTAILQ_REMOVE(&non_vlan_clients, vc, next);
         if (vc->peer) {
             vc->peer->peer = NULL;
         }
     }
-
-    if (vc->cleanup) {
-        vc->cleanup(vc);
-    }
-
     qemu_free(vc->name);
     qemu_free(vc->model);
     qemu_free(vc);
 }
 
-VLANClientState *qemu_find_vlan_client(VLANState *vlan, void *opaque)
+void qemu_del_vlan_client(VLANClientState *vc)
 {
-    VLANClientState *vc;
+    /* 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 (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);
+        }
+        qemu_cleanup_vlan_client(vc);
+        return;
+    }
 
-    QTAILQ_FOREACH(vc, &vlan->clients, next) {
-        if (vc->opaque == opaque) {
-            return vc;
+    /* 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 (nic->peer_deleted) {
+            qemu_free_vlan_client(vc->peer);
         }
     }
 
-    return NULL;
+    qemu_cleanup_vlan_client(vc);
+    qemu_free_vlan_client(vc);
 }
 
 VLANClientState *
@@ -423,6 +324,26 @@ qemu_find_vlan_client_by_name(Monitor *mon, int vlan_id,
     return vc;
 }
 
+void qemu_foreach_nic(qemu_nic_foreach func, void *opaque)
+{
+    VLANClientState *nc;
+    VLANState *vlan;
+
+    QTAILQ_FOREACH(nc, &non_vlan_clients, next) {
+        if (nc->info->type == NET_CLIENT_TYPE_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)
 {
     VLANState *vlan = sender->vlan;
@@ -431,8 +352,8 @@ int qemu_can_send_packet(VLANClientState *sender)
     if (sender->peer) {
         if (sender->peer->receive_disabled) {
             return 0;
-        } else if (sender->peer->can_receive &&
-                   !sender->peer->can_receive(sender->peer)) {
+        } else if (sender->peer->info->can_receive &&
+                   !sender->peer->info->can_receive(sender->peer)) {
             return 0;
         } else {
             return 1;
@@ -449,11 +370,11 @@ int qemu_can_send_packet(VLANClientState *sender)
         }
 
         /* no can_receive() handler, they can always receive */
-        if (!vc->can_receive || vc->can_receive(vc)) {
-            return 1;
+        if (vc->info->can_receive && !vc->info->can_receive(vc)) {
+            return 0;
         }
     }
-    return 0;
+    return 1;
 }
 
 static ssize_t qemu_deliver_packet(VLANClientState *sender,
@@ -473,10 +394,10 @@ static ssize_t qemu_deliver_packet(VLANClientState *sender,
         return 0;
     }
 
-    if (flags & QEMU_NET_PACKET_FLAG_RAW && vc->receive_raw) {
-        ret = vc->receive_raw(vc, data, size);
+    if (flags & QEMU_NET_PACKET_FLAG_RAW && vc->info->receive_raw) {
+        ret = vc->info->receive_raw(vc, data, size);
     } else {
-        ret = vc->receive(vc, data, size);
+        ret = vc->info->receive(vc, data, size);
     }
 
     if (ret == 0) {
@@ -513,10 +434,10 @@ static ssize_t qemu_vlan_deliver_packet(VLANClientState *sender,
             continue;
         }
 
-        if (flags & QEMU_NET_PACKET_FLAG_RAW && vc->receive_raw) {
-            len = vc->receive_raw(vc, buf, size);
+        if (flags & QEMU_NET_PACKET_FLAG_RAW && vc->info->receive_raw) {
+            len = vc->info->receive_raw(vc, buf, size);
         } else {
-            len = vc->receive(vc, buf, size);
+            len = vc->info->receive(vc, buf, size);
         }
 
         if (len == 0) {
@@ -610,28 +531,11 @@ static ssize_t vc_sendv_compat(VLANClientState *vc, const struct iovec *iov,
                                int iovcnt)
 {
     uint8_t buffer[4096];
-    size_t offset = 0;
-    int i;
-
-    for (i = 0; i < iovcnt; i++) {
-        size_t len;
-
-        len = MIN(sizeof(buffer) - offset, iov[i].iov_len);
-        memcpy(buffer + offset, iov[i].iov_base, len);
-        offset += len;
-    }
-
-    return vc->receive(vc, buffer, offset);
-}
+    size_t offset;
 
-static ssize_t calc_iov_length(const struct iovec *iov, int iovcnt)
-{
-    size_t offset = 0;
-    int i;
+    offset = iov_to_buf(iov, iovcnt, buffer, 0, sizeof(buffer));
 
-    for (i = 0; i < iovcnt; i++)
-        offset += iov[i].iov_len;
-    return offset;
+    return vc->info->receive(vc, buffer, offset);
 }
 
 static ssize_t qemu_deliver_packet_iov(VLANClientState *sender,
@@ -643,11 +547,11 @@ static ssize_t qemu_deliver_packet_iov(VLANClientState *sender,
     VLANClientState *vc = opaque;
 
     if (vc->link_down) {
-        return calc_iov_length(iov, iovcnt);
+        return iov_size(iov, iovcnt);
     }
 
-    if (vc->receive_iov) {
-        return vc->receive_iov(vc, iov, iovcnt);
+    if (vc->info->receive_iov) {
+        return vc->info->receive_iov(vc, iov, iovcnt);
     } else {
         return vc_sendv_compat(vc, iov, iovcnt);
     }
@@ -671,14 +575,14 @@ static ssize_t qemu_vlan_deliver_packet_iov(VLANClientState *sender,
         }
 
         if (vc->link_down) {
-            ret = calc_iov_length(iov, iovcnt);
+            ret = iov_size(iov, iovcnt);
             continue;
         }
 
         assert(!(flags & QEMU_NET_PACKET_FLAG_RAW));
 
-        if (vc->receive_iov) {
-            len = vc->receive_iov(vc, iov, iovcnt);
+        if (vc->info->receive_iov) {
+            len = vc->info->receive_iov(vc, iov, iovcnt);
         } else {
             len = vc_sendv_compat(vc, iov, iovcnt);
         }
@@ -696,7 +600,7 @@ ssize_t qemu_sendv_packet_async(VLANClientState *sender,
     NetQueue *queue;
 
     if (sender->link_down || (!sender->peer && !sender->vlan)) {
-        return calc_iov_length(iov, iovcnt);
+        return iov_size(iov, iovcnt);
     }
 
     if (sender->peer) {
@@ -716,558 +620,6 @@ qemu_sendv_packet(VLANClientState *vc, const struct iovec *iov, int iovcnt)
     return qemu_sendv_packet_async(vc, iov, iovcnt, NULL);
 }
 
-/* network connection */
-typedef struct NetSocketState {
-    VLANClientState *vc;
-    int fd;
-    int state; /* 0 = getting length, 1 = getting data */
-    unsigned int index;
-    unsigned int packet_len;
-    uint8_t buf[4096];
-    struct sockaddr_in dgram_dst; /* contains inet host and port destination iff connectionless (SOCK_DGRAM) */
-} NetSocketState;
-
-typedef struct NetSocketListenState {
-    VLANState *vlan;
-    char *model;
-    char *name;
-    int fd;
-} NetSocketListenState;
-
-/* XXX: we consider we can send the whole packet without blocking */
-static ssize_t net_socket_receive(VLANClientState *vc, const uint8_t *buf, size_t size)
-{
-    NetSocketState *s = vc->opaque;
-    uint32_t len;
-    len = htonl(size);
-
-    send_all(s->fd, (const uint8_t *)&len, sizeof(len));
-    return send_all(s->fd, buf, size);
-}
-
-static ssize_t net_socket_receive_dgram(VLANClientState *vc, const uint8_t *buf, size_t size)
-{
-    NetSocketState *s = vc->opaque;
-
-    return sendto(s->fd, (const void *)buf, size, 0,
-                  (struct sockaddr *)&s->dgram_dst, sizeof(s->dgram_dst));
-}
-
-static void net_socket_send(void *opaque)
-{
-    NetSocketState *s = opaque;
-    int size, err;
-    unsigned l;
-    uint8_t buf1[4096];
-    const uint8_t *buf;
-
-    size = recv(s->fd, (void *)buf1, sizeof(buf1), 0);
-    if (size < 0) {
-        err = socket_error();
-        if (err != EWOULDBLOCK)
-            goto eoc;
-    } else if (size == 0) {
-        /* end of connection */
-    eoc:
-        qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
-        closesocket(s->fd);
-        return;
-    }
-    buf = buf1;
-    while (size > 0) {
-        /* reassemble a packet from the network */
-        switch(s->state) {
-        case 0:
-            l = 4 - s->index;
-            if (l > size)
-                l = size;
-            memcpy(s->buf + s->index, buf, l);
-            buf += l;
-            size -= l;
-            s->index += l;
-            if (s->index == 4) {
-                /* got length */
-                s->packet_len = ntohl(*(uint32_t *)s->buf);
-                s->index = 0;
-                s->state = 1;
-            }
-            break;
-        case 1:
-            l = s->packet_len - s->index;
-            if (l > size)
-                l = size;
-            if (s->index + l <= sizeof(s->buf)) {
-                memcpy(s->buf + s->index, buf, l);
-            } else {
-                fprintf(stderr, "serious error: oversized packet received,"
-                    "connection terminated.\n");
-                s->state = 0;
-                goto eoc;
-            }
-
-            s->index += l;
-            buf += l;
-            size -= l;
-            if (s->index >= s->packet_len) {
-                qemu_send_packet(s->vc, s->buf, s->packet_len);
-                s->index = 0;
-                s->state = 0;
-            }
-            break;
-        }
-    }
-}
-
-static void net_socket_send_dgram(void *opaque)
-{
-    NetSocketState *s = opaque;
-    int size;
-
-    size = recv(s->fd, (void *)s->buf, sizeof(s->buf), 0);
-    if (size < 0)
-        return;
-    if (size == 0) {
-        /* end of connection */
-        qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
-        return;
-    }
-    qemu_send_packet(s->vc, s->buf, size);
-}
-
-static int net_socket_mcast_create(struct sockaddr_in *mcastaddr)
-{
-    struct ip_mreq imr;
-    int fd;
-    int val, ret;
-    if (!IN_MULTICAST(ntohl(mcastaddr->sin_addr.s_addr))) {
-       fprintf(stderr, "qemu: error: specified mcastaddr \"%s\" (0x%08x) does not contain a multicast address\n",
-               inet_ntoa(mcastaddr->sin_addr),
-                (int)ntohl(mcastaddr->sin_addr.s_addr));
-       return -1;
-
-    }
-    fd = socket(PF_INET, SOCK_DGRAM, 0);
-    if (fd < 0) {
-        perror("socket(PF_INET, SOCK_DGRAM)");
-        return -1;
-    }
-
-    val = 1;
-    ret=setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
-                   (const char *)&val, sizeof(val));
-    if (ret < 0) {
-       perror("setsockopt(SOL_SOCKET, SO_REUSEADDR)");
-       goto fail;
-    }
-
-    ret = bind(fd, (struct sockaddr *)mcastaddr, sizeof(*mcastaddr));
-    if (ret < 0) {
-        perror("bind");
-        goto fail;
-    }
-
-    /* Add host to multicast group */
-    imr.imr_multiaddr = mcastaddr->sin_addr;
-    imr.imr_interface.s_addr = htonl(INADDR_ANY);
-
-    ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
-                     (const char *)&imr, sizeof(struct ip_mreq));
-    if (ret < 0) {
-       perror("setsockopt(IP_ADD_MEMBERSHIP)");
-       goto fail;
-    }
-
-    /* Force mcast msgs to loopback (eg. several QEMUs in same host */
-    val = 1;
-    ret=setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
-                   (const char *)&val, sizeof(val));
-    if (ret < 0) {
-       perror("setsockopt(SOL_IP, IP_MULTICAST_LOOP)");
-       goto fail;
-    }
-
-    socket_set_nonblock(fd);
-    return fd;
-fail:
-    if (fd >= 0)
-        closesocket(fd);
-    return -1;
-}
-
-static void net_socket_cleanup(VLANClientState *vc)
-{
-    NetSocketState *s = vc->opaque;
-    qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
-    close(s->fd);
-    qemu_free(s);
-}
-
-static NetSocketState *net_socket_fd_init_dgram(VLANState *vlan,
-                                                const char *model,
-                                                const char *name,
-                                                int fd, int is_connected)
-{
-    struct sockaddr_in saddr;
-    int newfd;
-    socklen_t saddr_len;
-    NetSocketState *s;
-
-    /* fd passed: multicast: "learn" dgram_dst address from bound address and save it
-     * Because this may be "shared" socket from a "master" process, datagrams would be recv()
-     * by ONLY ONE process: we must "clone" this dgram socket --jjo
-     */
-
-    if (is_connected) {
-       if (getsockname(fd, (struct sockaddr *) &saddr, &saddr_len) == 0) {
-           /* must be bound */
-           if (saddr.sin_addr.s_addr==0) {
-               fprintf(stderr, "qemu: error: init_dgram: fd=%d unbound, cannot setup multicast dst addr\n",
-                       fd);
-               return NULL;
-           }
-           /* clone dgram socket */
-           newfd = net_socket_mcast_create(&saddr);
-           if (newfd < 0) {
-               /* error already reported by net_socket_mcast_create() */
-               close(fd);
-               return NULL;
-           }
-           /* clone newfd to fd, close newfd */
-           dup2(newfd, fd);
-           close(newfd);
-
-       } else {
-           fprintf(stderr, "qemu: error: init_dgram: fd=%d failed getsockname(): %s\n",
-                   fd, strerror(errno));
-           return NULL;
-       }
-    }
-
-    s = qemu_mallocz(sizeof(NetSocketState));
-    s->fd = fd;
-
-    s->vc = qemu_new_vlan_client(NET_CLIENT_TYPE_SOCKET,
-                                 vlan, NULL, model, name, NULL,
-                                 net_socket_receive_dgram, NULL, NULL,
-                                 net_socket_cleanup, s);
-    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;
-
-    snprintf(s->vc->info_str, sizeof(s->vc->info_str),
-           "socket: fd=%d (%s mcast=%s:%d)",
-           fd, is_connected? "cloned" : "",
-           inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
-    return s;
-}
-
-static void net_socket_connect(void *opaque)
-{
-    NetSocketState *s = opaque;
-    qemu_set_fd_handler(s->fd, net_socket_send, NULL, s);
-}
-
-static NetSocketState *net_socket_fd_init_stream(VLANState *vlan,
-                                                 const char *model,
-                                                 const char *name,
-                                                 int fd, int is_connected)
-{
-    NetSocketState *s;
-    s = qemu_mallocz(sizeof(NetSocketState));
-    s->fd = fd;
-    s->vc = qemu_new_vlan_client(NET_CLIENT_TYPE_SOCKET,
-                                 vlan, NULL, model, name, NULL,
-                                 net_socket_receive, NULL, NULL,
-                                 net_socket_cleanup, s);
-    snprintf(s->vc->info_str, sizeof(s->vc->info_str),
-             "socket: fd=%d", fd);
-    if (is_connected) {
-        net_socket_connect(s);
-    } else {
-        qemu_set_fd_handler(s->fd, NULL, net_socket_connect, s);
-    }
-    return s;
-}
-
-static NetSocketState *net_socket_fd_init(VLANState *vlan,
-                                          const char *model, const char *name,
-                                          int fd, int is_connected)
-{
-    int so_type = -1, optlen=sizeof(so_type);
-
-    if(getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&so_type,
-        (socklen_t *)&optlen)< 0) {
-       fprintf(stderr, "qemu: error: getsockopt(SO_TYPE) for fd=%d failed\n", fd);
-       return NULL;
-    }
-    switch(so_type) {
-    case SOCK_DGRAM:
-        return net_socket_fd_init_dgram(vlan, model, name, fd, is_connected);
-    case SOCK_STREAM:
-        return net_socket_fd_init_stream(vlan, 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 NULL;
-}
-
-static void net_socket_accept(void *opaque)
-{
-    NetSocketListenState *s = opaque;
-    NetSocketState *s1;
-    struct sockaddr_in saddr;
-    socklen_t len;
-    int fd;
-
-    for(;;) {
-        len = sizeof(saddr);
-        fd = accept(s->fd, (struct sockaddr *)&saddr, &len);
-        if (fd < 0 && errno != EINTR) {
-            return;
-        } else if (fd >= 0) {
-            break;
-        }
-    }
-    s1 = net_socket_fd_init(s->vlan, s->model, s->name, fd, 1);
-    if (!s1) {
-        closesocket(fd);
-    } else {
-        snprintf(s1->vc->info_str, sizeof(s1->vc->info_str),
-                 "socket: connection from %s:%d",
-                 inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
-    }
-}
-
-static int net_socket_listen_init(VLANState *vlan,
-                                  const char *model,
-                                  const char *name,
-                                  const char *host_str)
-{
-    NetSocketListenState *s;
-    int fd, val, ret;
-    struct sockaddr_in saddr;
-
-    if (parse_host_port(&saddr, host_str) < 0)
-        return -1;
-
-    s = qemu_mallocz(sizeof(NetSocketListenState));
-
-    fd = socket(PF_INET, SOCK_STREAM, 0);
-    if (fd < 0) {
-        perror("socket");
-        return -1;
-    }
-    socket_set_nonblock(fd);
-
-    /* allow fast reuse */
-    val = 1;
-    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&val, sizeof(val));
-
-    ret = bind(fd, (struct sockaddr *)&saddr, sizeof(saddr));
-    if (ret < 0) {
-        perror("bind");
-        return -1;
-    }
-    ret = listen(fd, 0);
-    if (ret < 0) {
-        perror("listen");
-        return -1;
-    }
-    s->vlan = vlan;
-    s->model = qemu_strdup(model);
-    s->name = name ? qemu_strdup(name) : NULL;
-    s->fd = fd;
-    qemu_set_fd_handler(fd, net_socket_accept, NULL, s);
-    return 0;
-}
-
-static int net_socket_connect_init(VLANState *vlan,
-                                   const char *model,
-                                   const char *name,
-                                   const char *host_str)
-{
-    NetSocketState *s;
-    int fd, connected, ret, err;
-    struct sockaddr_in saddr;
-
-    if (parse_host_port(&saddr, host_str) < 0)
-        return -1;
-
-    fd = socket(PF_INET, SOCK_STREAM, 0);
-    if (fd < 0) {
-        perror("socket");
-        return -1;
-    }
-    socket_set_nonblock(fd);
-
-    connected = 0;
-    for(;;) {
-        ret = connect(fd, (struct sockaddr *)&saddr, sizeof(saddr));
-        if (ret < 0) {
-            err = socket_error();
-            if (err == EINTR || err == EWOULDBLOCK) {
-            } else if (err == EINPROGRESS) {
-                break;
-#ifdef _WIN32
-            } else if (err == WSAEALREADY) {
-                break;
-#endif
-            } else {
-                perror("connect");
-                closesocket(fd);
-                return -1;
-            }
-        } else {
-            connected = 1;
-            break;
-        }
-    }
-    s = net_socket_fd_init(vlan, model, name, fd, connected);
-    if (!s)
-        return -1;
-    snprintf(s->vc->info_str, sizeof(s->vc->info_str),
-             "socket: connect to %s:%d",
-             inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
-    return 0;
-}
-
-static int net_socket_mcast_init(VLANState *vlan,
-                                 const char *model,
-                                 const char *name,
-                                 const char *host_str)
-{
-    NetSocketState *s;
-    int fd;
-    struct sockaddr_in saddr;
-
-    if (parse_host_port(&saddr, host_str) < 0)
-        return -1;
-
-
-    fd = net_socket_mcast_create(&saddr);
-    if (fd < 0)
-       return -1;
-
-    s = net_socket_fd_init(vlan, model, name, fd, 0);
-    if (!s)
-        return -1;
-
-    s->dgram_dst = saddr;
-
-    snprintf(s->vc->info_str, sizeof(s->vc->info_str),
-             "socket: mcast=%s:%d",
-             inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
-    return 0;
-
-}
-
-typedef struct DumpState {
-    VLANClientState *pcap_vc;
-    int fd;
-    int pcap_caplen;
-} DumpState;
-
-#define PCAP_MAGIC 0xa1b2c3d4
-
-struct pcap_file_hdr {
-    uint32_t magic;
-    uint16_t version_major;
-    uint16_t version_minor;
-    int32_t thiszone;
-    uint32_t sigfigs;
-    uint32_t snaplen;
-    uint32_t linktype;
-};
-
-struct pcap_sf_pkthdr {
-    struct {
-        int32_t tv_sec;
-        int32_t tv_usec;
-    } ts;
-    uint32_t caplen;
-    uint32_t len;
-};
-
-static ssize_t dump_receive(VLANClientState *vc, const uint8_t *buf, size_t size)
-{
-    DumpState *s = vc->opaque;
-    struct pcap_sf_pkthdr hdr;
-    int64_t ts;
-    int caplen;
-
-    /* Early return in case of previous error. */
-    if (s->fd < 0) {
-        return size;
-    }
-
-    ts = muldiv64(qemu_get_clock(vm_clock), 1000000, get_ticks_per_sec());
-    caplen = size > s->pcap_caplen ? s->pcap_caplen : size;
-
-    hdr.ts.tv_sec = ts / 1000000;
-    hdr.ts.tv_usec = ts % 1000000;
-    hdr.caplen = caplen;
-    hdr.len = size;
-    if (write(s->fd, &hdr, sizeof(hdr)) != sizeof(hdr) ||
-        write(s->fd, buf, caplen) != caplen) {
-        qemu_log("-net dump write error - stop dump\n");
-        close(s->fd);
-        s->fd = -1;
-    }
-
-    return size;
-}
-
-static void net_dump_cleanup(VLANClientState *vc)
-{
-    DumpState *s = vc->opaque;
-
-    close(s->fd);
-    qemu_free(s);
-}
-
-static int net_dump_init(VLANState *vlan, const char *device,
-                         const char *name, const char *filename, int len)
-{
-    struct pcap_file_hdr hdr;
-    DumpState *s;
-
-    s = qemu_malloc(sizeof(DumpState));
-
-    s->fd = open(filename, O_CREAT | O_WRONLY | O_BINARY, 0644);
-    if (s->fd < 0) {
-        qemu_error("-net dump: can't open %s\n", filename);
-        return -1;
-    }
-
-    s->pcap_caplen = len;
-
-    hdr.magic = PCAP_MAGIC;
-    hdr.version_major = 2;
-    hdr.version_minor = 4;
-    hdr.thiszone = 0;
-    hdr.sigfigs = 0;
-    hdr.snaplen = s->pcap_caplen;
-    hdr.linktype = 1;
-
-    if (write(s->fd, &hdr, sizeof(hdr)) < sizeof(hdr)) {
-        qemu_error("-net dump write error: %s\n", strerror(errno));
-        close(s->fd);
-        qemu_free(s);
-        return -1;
-    }
-
-    s->pcap_vc = qemu_new_vlan_client(NET_CLIENT_TYPE_DUMP,
-                                      vlan, NULL, device, name, NULL,
-                                      dump_receive, NULL, NULL,
-                                      net_dump_cleanup, s);
-    snprintf(s->pcap_vc->info_str, sizeof(s->pcap_vc->info_str),
-             "dump to %s (len=%d)", filename, len);
-    return 0;
-}
-
 /* find or alloc a new VLAN */
 VLANState *qemu_find_vlan(int id, int allocate)
 {
@@ -1358,25 +710,31 @@ int qemu_find_nic_model(NICInfo *nd, const char * const *models,
             return i;
     }
 
-    qemu_error("qemu: Unsupported NIC model: %s\n", nd->model);
+    error_report("Unsupported NIC model: %s", nd->model);
     return -1;
 }
 
 int net_handle_fd_param(Monitor *mon, const char *param)
 {
-    if (!qemu_isdigit(param[0])) {
-        int fd;
+    int fd;
+
+    if (!qemu_isdigit(param[0]) && mon) {
 
         fd = monitor_get_fd(mon, param);
         if (fd == -1) {
-            qemu_error("No file descriptor named %s found", param);
+            error_report("No file descriptor named %s found", param);
             return -1;
         }
-
-        return fd;
     } else {
-        return strtol(param, NULL, 0);
+        char *endptr = NULL;
+
+        fd = strtol(param, &endptr, 10);
+        if (*endptr || (fd == 0 && param == endptr)) {
+            return -1;
+        }
     }
+
+    return fd;
 }
 
 static int net_init_nic(QemuOpts *opts,
@@ -1390,7 +748,7 @@ static int net_init_nic(QemuOpts *opts,
 
     idx = nic_get_free_idx();
     if (idx == -1 || nb_nics >= MAX_NICS) {
-        qemu_error("Too Many NICs\n");
+        error_report("Too Many NICs");
         return -1;
     }
 
@@ -1401,7 +759,7 @@ static int net_init_nic(QemuOpts *opts,
     if ((netdev = qemu_opt_get(opts, "netdev"))) {
         nd->netdev = qemu_find_netdev(netdev);
         if (!nd->netdev) {
-            qemu_error("netdev '%s' not found\n", netdev);
+            error_report("netdev '%s' not found", netdev);
             return -1;
         }
     } else {
@@ -1418,138 +776,27 @@ static int net_init_nic(QemuOpts *opts,
         nd->devaddr = qemu_strdup(qemu_opt_get(opts, "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") &&
-        parse_macaddr(nd->macaddr, qemu_opt_get(opts, "macaddr")) < 0) {
-        qemu_error("invalid syntax for ethernet address\n");
+        net_parse_macaddr(nd->macaddr.a, qemu_opt_get(opts, "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", NIC_NVECTORS_UNSPECIFIED);
-    if (nd->nvectors != NIC_NVECTORS_UNSPECIFIED &&
+    nd->nvectors = qemu_opt_get_number(opts, "vectors",
+                                       DEV_NVECTORS_UNSPECIFIED);
+    if (nd->nvectors != DEV_NVECTORS_UNSPECIFIED &&
         (nd->nvectors < 0 || nd->nvectors > 0x7ffffff)) {
-        qemu_error("invalid # of vectors: %d\n", nd->nvectors);
+        error_report("invalid # of vectors: %d", nd->nvectors);
         return -1;
     }
 
     nd->used = 1;
-    if (vlan) {
-        nd->vlan->nb_guest_devs++;
-    }
     nb_nics++;
 
     return idx;
 }
 
-static int net_init_socket(QemuOpts *opts,
-                           Monitor *mon,
-                           const char *name,
-                           VLANState *vlan)
-{
-    if (qemu_opt_get(opts, "fd")) {
-        int fd;
-
-        if (qemu_opt_get(opts, "listen") ||
-            qemu_opt_get(opts, "connect") ||
-            qemu_opt_get(opts, "mcast")) {
-            qemu_error("listen=, connect= and mcast= is invalid with fd=\n");
-            return -1;
-        }
-
-        fd = net_handle_fd_param(mon, qemu_opt_get(opts, "fd"));
-        if (fd == -1) {
-            return -1;
-        }
-
-        if (!net_socket_fd_init(vlan, "socket", name, fd, 1)) {
-            close(fd);
-            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_error("fd=, connect= and mcast= is invalid with listen=\n");
-            return -1;
-        }
-
-        listen = qemu_opt_get(opts, "listen");
-
-        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_error("fd=, listen= and mcast= is invalid with connect=\n");
-            return -1;
-        }
-
-        connect = qemu_opt_get(opts, "connect");
-
-        if (net_socket_connect_init(vlan, "socket", name, connect) == -1) {
-            return -1;
-        }
-    } else if (qemu_opt_get(opts, "mcast")) {
-        const char *mcast;
-
-        if (qemu_opt_get(opts, "fd") ||
-            qemu_opt_get(opts, "connect") ||
-            qemu_opt_get(opts, "listen")) {
-            qemu_error("fd=, connect= and listen= is invalid with mcast=\n");
-            return -1;
-        }
-
-        mcast = qemu_opt_get(opts, "mcast");
-
-        if (net_socket_mcast_init(vlan, "socket", name, mcast) == -1) {
-            return -1;
-        }
-    } else {
-        qemu_error("-socket requires fd=, listen=, connect= or mcast=\n");
-        return -1;
-    }
-
-    if (vlan) {
-        vlan->nb_host_devs++;
-    }
-
-    return 0;
-}
-
-static int net_init_dump(QemuOpts *opts,
-                         Monitor *mon,
-                         const char *name,
-                         VLANState *vlan)
-{
-    int len;
-    const char *file;
-    char def_file[128];
-
-    assert(vlan);
-
-    file = qemu_opt_get(opts, "file");
-    if (!file) {
-        snprintf(def_file, sizeof(def_file), "qemu-vlan%d.pcap", vlan->id);
-        file = def_file;
-    }
-
-    len = qemu_opt_get_size(opts, "len", 65536);
-
-    return net_dump_init(vlan, "dump", name, file, len);
-}
-
 #define NET_COMMON_PARAMS_DESC                     \
     {                                              \
         .name = "type",                            \
@@ -1573,18 +820,19 @@ typedef int (*net_client_init_func)(QemuOpts *opts,
 /* magic number, but compiler will warn if too small */
 #define NET_MAX_DESC 20
 
-static struct {
+static const struct {
     const char *type;
     net_client_init_func init;
     QemuOptDesc desc[NET_MAX_DESC];
-} net_client_types[] = {
-    {
+} net_client_types[NET_CLIENT_TYPE_MAX] = {
+    [NET_CLIENT_TYPE_NONE] = {
         .type = "none",
         .desc = {
             NET_COMMON_PARAMS_DESC,
             { /* end of list */ }
         },
-    }, {
+    },
+    [NET_CLIENT_TYPE_NIC] = {
         .type = "nic",
         .init = net_init_nic,
         .desc = {
@@ -1613,8 +861,9 @@ static struct {
             },
             { /* end of list */ }
         },
+    },
 #ifdef CONFIG_SLIRP
-    }, {
+    [NET_CLIENT_TYPE_USER] = {
         .type = "user",
         .init = net_init_slirp,
         .desc = {
@@ -1674,8 +923,9 @@ static struct {
             },
             { /* end of list */ }
         },
+    },
 #endif
-    }, {
+    [NET_CLIENT_TYPE_TAP] = {
         .type = "tap",
         .init = net_init_tap,
         .desc = {
@@ -1706,11 +956,24 @@ static struct {
                 .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",
+            }, {
+                .name = "vhostforce",
+                .type = QEMU_OPT_BOOL,
+                .help = "force vhost on for non-MSIX virtio guests",
+        },
 #endif /* _WIN32 */
             { /* end of list */ }
         },
-    }, {
+    },
+    [NET_CLIENT_TYPE_SOCKET] = {
         .type = "socket",
         .init = net_init_socket,
         .desc = {
@@ -1731,11 +994,16 @@ static struct {
                 .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 */ }
         },
+    },
 #ifdef CONFIG_VDE
-    }, {
+    [NET_CLIENT_TYPE_VDE] = {
         .type = "vde",
         .init = net_init_vde,
         .desc = {
@@ -1759,8 +1027,9 @@ static struct {
             },
             { /* end of list */ }
         },
+    },
 #endif
-    }, {
+    [NET_CLIENT_TYPE_DUMP] = {
         .type = "dump",
         .init = net_init_dump,
         .desc = {
@@ -1777,7 +1046,6 @@ static struct {
             { /* end of list */ }
         },
     },
-    { /* end of list */ }
 };
 
 int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev)
@@ -1788,7 +1056,7 @@ int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev)
 
     type = qemu_opt_get(opts, "type");
     if (!type) {
-        qemu_error("No type specified for -net\n");
+        qerror_report(QERR_MISSING_PARAMETER, "type");
         return -1;
     }
 
@@ -1801,21 +1069,21 @@ int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev)
             strcmp(type, "vde") != 0 &&
 #endif
             strcmp(type, "socket") != 0) {
-            qemu_error("The '%s' network backend type is not valid with -netdev\n",
-                       type);
+            qerror_report(QERR_INVALID_PARAMETER_VALUE, "type",
+                          "a netdev backend type");
             return -1;
         }
 
         if (qemu_opt_get(opts, "vlan")) {
-            qemu_error("The 'vlan' parameter is not valid with -netdev\n");
+            qerror_report(QERR_INVALID_PARAMETER, "vlan");
             return -1;
         }
         if (qemu_opt_get(opts, "name")) {
-            qemu_error("The 'name' parameter is not valid with -netdev\n");
+            qerror_report(QERR_INVALID_PARAMETER, "name");
             return -1;
         }
         if (!qemu_opts_id(opts)) {
-            qemu_error("The id= parameter is required with -netdev\n");
+            qerror_report(QERR_MISSING_PARAMETER, "id");
             return -1;
         }
     }
@@ -1825,9 +1093,11 @@ int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev)
         name = qemu_opt_get(opts, "name");
     }
 
-    for (i = 0; net_client_types[i].type != NULL; i++) {
-        if (!strcmp(net_client_types[i].type, type)) {
+    for (i = 0; i < NET_CLIENT_TYPE_MAX; i++) {
+        if (net_client_types[i].type != NULL &&
+            !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;
@@ -1840,32 +1110,24 @@ int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev)
                 vlan = qemu_find_vlan(qemu_opt_get_number(opts, "vlan", 0), 1);
             }
 
+            ret = 0;
             if (net_client_types[i].init) {
-                return net_client_types[i].init(opts, mon, name, vlan);
-            } else {
-                return 0;
+                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;
         }
     }
 
-    qemu_error("Invalid -net type '%s'\n", type);
+    qerror_report(QERR_INVALID_PARAMETER_VALUE, "type",
+                  "a network client type");
     return -1;
 }
 
-void net_client_uninit(NICInfo *nd)
-{
-    if (nd->vlan) {
-        nd->vlan->nb_guest_devs--;
-    }
-    nb_nics--;
-
-    qemu_free(nd->model);
-    qemu_free(nd->name);
-    qemu_free(nd->devaddr);
-
-    nd->used = 0;
-}
-
 static int net_host_check_device(const char *device)
 {
     int i;
@@ -1897,10 +1159,8 @@ void net_host_device_add(Monitor *mon, const QDict *qdict)
         return;
     }
 
-    opts = qemu_opts_parse(&qemu_net_opts, opts_str ? opts_str : "", NULL);
+    opts = qemu_opts_parse(qemu_find_opts("net"), opts_str ? opts_str : "", 0);
     if (!opts) {
-        monitor_printf(mon, "parsing network options '%s' failed\n",
-                       opts_str ? opts_str : "");
         return;
     }
 
@@ -1928,47 +1188,80 @@ void net_host_device_remove(Monitor *mon, const QDict *qdict)
     qemu_del_vlan_client(vc);
 }
 
-void net_set_boot_mask(int net_boot_mask)
+int do_netdev_add(Monitor *mon, const QDict *qdict, QObject **ret_data)
 {
-    int i;
+    QemuOpts *opts;
+    int res;
 
-    /* Only the first four NICs may be bootable */
-    net_boot_mask = net_boot_mask & 0xF;
+    opts = qemu_opts_from_qdict(qemu_find_opts("netdev"), qdict);
+    if (!opts) {
+        return -1;
+    }
 
-    for (i = 0; i < nb_nics; i++) {
-        if (net_boot_mask & (1 << i)) {
-            nd_table[i].bootable = 1;
-            net_boot_mask &= ~(1 << i);
-        }
+    res = net_client_init(mon, opts, 1);
+    if (res < 0) {
+        qemu_opts_del(opts);
     }
 
-    if (net_boot_mask) {
-        fprintf(stderr, "Cannot boot from non-existent NIC\n");
-        exit(1);
+    return res;
+}
+
+int do_netdev_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
+{
+    const char *id = qdict_get_str(qdict, "id");
+    VLANClientState *vc;
+
+    vc = qemu_find_netdev(id);
+    if (!vc || vc->info->type == NET_CLIENT_TYPE_NIC) {
+        qerror_report(QERR_DEVICE_NOT_FOUND, id);
+        return -1;
     }
+    qemu_del_vlan_client(vc);
+    qemu_opts_del(qemu_opts_find(qemu_find_opts("netdev"), id));
+    return 0;
+}
+
+static void print_net_client(Monitor *mon, VLANClientState *vc)
+{
+    monitor_printf(mon, "%s: type=%s,%s\n", vc->name,
+                   net_client_types[vc->info->type].type, vc->info_str);
 }
 
 void do_info_network(Monitor *mon)
 {
     VLANState *vlan;
+    VLANClientState *vc, *peer;
+    net_client_type type;
 
     QTAILQ_FOREACH(vlan, &vlans, next) {
-        VLANClientState *vc;
-
         monitor_printf(mon, "VLAN %d devices:\n", vlan->id);
 
         QTAILQ_FOREACH(vc, &vlan->clients, next) {
-            monitor_printf(mon, "  %s: %s\n", vc->name, vc->info_str);
+            monitor_printf(mon, "  ");
+            print_net_client(mon, vc);
+        }
+    }
+    monitor_printf(mon, "Devices not on any VLAN:\n");
+    QTAILQ_FOREACH(vc, &non_vlan_clients, next) {
+        peer = vc->peer;
+        type = vc->info->type;
+        if (!peer || type == NET_CLIENT_TYPE_NIC) {
+            monitor_printf(mon, "  ");
+            print_net_client(mon, vc);
+        } /* else it's a netdev connected to a NIC, printed with the NIC */
+        if (peer && type == NET_CLIENT_TYPE_NIC) {
+            monitor_printf(mon, "   \\ ");
+            print_net_client(mon, peer);
         }
     }
 }
 
-void do_set_link(Monitor *mon, const QDict *qdict)
+int do_set_link(Monitor *mon, const QDict *qdict, QObject **ret_data)
 {
     VLANState *vlan;
     VLANClientState *vc = NULL;
     const char *name = qdict_get_str(qdict, "name");
-    const char *up_or_down = qdict_get_str(qdict, "up_or_down");
+    int up = qdict_get_bool(qdict, "up");
 
     QTAILQ_FOREACH(vlan, &vlans, next) {
         QTAILQ_FOREACH(vc, &vlan->clients, next) {
@@ -1977,23 +1270,31 @@ void do_set_link(Monitor *mon, const QDict *qdict)
             }
         }
     }
+    vc = qemu_find_netdev(name);
 done:
 
     if (!vc) {
-        monitor_printf(mon, "could not find network device '%s'\n", name);
-        return;
+        qerror_report(QERR_DEVICE_NOT_FOUND, name);
+        return -1;
     }
 
-    if (strcmp(up_or_down, "up") == 0)
-        vc->link_down = 0;
-    else if (strcmp(up_or_down, "down") == 0)
-        vc->link_down = 1;
-    else
-        monitor_printf(mon, "invalid link status '%s'; only 'up' or 'down' "
-                       "valid\n", up_or_down);
+    vc->link_down = !up;
+
+    if (vc->info->link_status_changed) {
+        vc->info->link_status_changed(vc);
+    }
 
-    if (vc->link_status_changed)
-        vc->link_status_changed(vc);
+    /* Notify peer. Don't update peer link status: this makes it possible to
+     * disconnect from host network without notifying the guest.
+     * FIXME: is disconnected link status change operation useful?
+     *
+     * Current behaviour is compatible with qemu vlans where there could be
+     * multiple clients that can still communicate with each other in
+     * disconnected mode. For now maintain this compatibility. */
+    if (vc->peer && vc->peer->info->link_status_changed) {
+        vc->peer->info->link_status_changed(vc->peer);
+    }
+    return 0;
 }
 
 void net_cleanup(void)
@@ -2012,20 +1313,69 @@ void net_cleanup(void)
     }
 }
 
-static void net_check_clients(void)
+void net_check_clients(void)
 {
     VLANState *vlan;
+    VLANClientState *vc;
+    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;
+    }
 
     QTAILQ_FOREACH(vlan, &vlans, next) {
-        if (vlan->nb_guest_devs == 0 && vlan->nb_host_devs == 0)
-            continue;
-        if (vlan->nb_guest_devs == 0)
+        int has_nic = 0, has_host_dev = 0;
+
+        QTAILQ_FOREACH(vc, &vlan->clients, next) {
+            switch (vc->info->type) {
+            case NET_CLIENT_TYPE_NIC:
+                has_nic = 1;
+                break;
+            case NET_CLIENT_TYPE_USER:
+            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 (vlan->nb_host_devs == 0)
+        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) {
+            fprintf(stderr, "Warning: %s %s has no peer\n",
+                    vc->info->type == NET_CLIENT_TYPE_NIC ? "nic" : "netdev",
+                    vc->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)
@@ -2042,26 +1392,26 @@ static int net_init_netdev(QemuOpts *opts, void *dummy)
 
 int net_init_clients(void)
 {
-    if (QTAILQ_EMPTY(&qemu_net_opts.head)) {
+    QemuOptsList *net = qemu_find_opts("net");
+
+    if (default_net) {
         /* if no clients, we use a default config */
-        qemu_opts_set(&qemu_net_opts, NULL, "type", "nic");
+        qemu_opts_set(net, NULL, "type", "nic");
 #ifdef CONFIG_SLIRP
-        qemu_opts_set(&qemu_net_opts, NULL, "type", "user");
+        qemu_opts_set(net, NULL, "type", "user");
 #endif
     }
 
     QTAILQ_INIT(&vlans);
     QTAILQ_INIT(&non_vlan_clients);
 
-    if (qemu_opts_foreach(&qemu_netdev_opts, net_init_netdev, NULL, 1) == -1)
+    if (qemu_opts_foreach(qemu_find_opts("netdev"), net_init_netdev, NULL, 1) == -1)
         return -1;
 
-    if (qemu_opts_foreach(&qemu_net_opts, net_init_client, NULL, 1) == -1) {
+    if (qemu_opts_foreach(net, net_init_client, NULL, 1) == -1) {
         return -1;
     }
 
-    net_check_clients();
-
     return 0;
 }
 
@@ -2074,9 +1424,10 @@ int net_client_parse(QemuOptsList *opts_list, const char *optarg)
     }
 #endif
 
-    if (!qemu_opts_parse(opts_list, optarg, "type")) {
+    if (!qemu_opts_parse(opts_list, optarg, 1)) {
         return -1;
     }
 
+    default_net = 0;
     return 0;
 }