From patchwork Mon Jan 13 14:25:18 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Antonios Motakis X-Patchwork-Id: 309959 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 40BF12C0040 for ; Tue, 14 Jan 2014 01:31:55 +1100 (EST) Received: from localhost ([::1]:42892 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1W2iYT-0000S9-4Y for incoming@patchwork.ozlabs.org; Mon, 13 Jan 2014 09:31:53 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:48256) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1W2iT5-00009B-97 for qemu-devel@nongnu.org; Mon, 13 Jan 2014 09:26:25 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1W2iSz-0001NF-82 for qemu-devel@nongnu.org; Mon, 13 Jan 2014 09:26:19 -0500 Received: from mail-wi0-f174.google.com ([209.85.212.174]:61133) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1W2iSy-0001LF-Qz for qemu-devel@nongnu.org; Mon, 13 Jan 2014 09:26:13 -0500 Received: by mail-wi0-f174.google.com with SMTP id g10so1172970wiw.13 for ; Mon, 13 Jan 2014 06:26:12 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=a+FKENPXErGUZy5kvjxnZ6IczxvZ5aYidNFP8ckCtBc=; b=fCgLmRjYX7CBKHyPCI2/DA9I5xkrFd1rd6+dxBEPmXfV+20fEBLhvSU9Eb1h1CmVy2 mvhzySOSOHc8Uuo5UavhxVVg7w8+stbBQidjE8tGMKgATfsVGi8NH4k6vKH7jvnpy81f RK8lE4V9WaAvV8UJO4pMfbgYW6X5wfXLZ18TxS87R1DrqKJVVOJktKANxKhJXxBwlQ1H Tw2Xw+KJkDcd5KFQlWBMvNJKqBqBZSwcNuWnUaD7pVIOBzg/rr+Gs2BB8CIWyIMsm3UH QJhHSG7M+Ffj2I+zpc5XkEpwNJ7KwbcT7R2hso6S5wvvpDrmI1iRU2m0QjtU1zGduB7j 4c5w== X-Gm-Message-State: ALoCoQkc2FVBwayVySHLOiy+d3SpZViYAzs/mRUTSlN8KA/hzbkQEIX+X1HRhzaDG52CJlp1S6iG X-Received: by 10.194.133.135 with SMTP id pc7mr22040181wjb.51.1389623171575; Mon, 13 Jan 2014 06:26:11 -0800 (PST) Received: from localhost.localdomain (home.tvelocity.eu. [82.67.68.96]) by mx.google.com with ESMTPSA id k10sm11616662wjf.11.2014.01.13.06.26.08 for (version=TLSv1.1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Mon, 13 Jan 2014 06:26:10 -0800 (PST) From: Antonios Motakis To: qemu-devel@nongnu.org, snabb-devel@googlegroups.com Date: Mon, 13 Jan 2014 15:25:18 +0100 Message-Id: <1389623119-15863-8-git-send-email-a.motakis@virtualopensystems.com> X-Mailer: git-send-email 1.8.3.2 In-Reply-To: <1389623119-15863-1-git-send-email-a.motakis@virtualopensystems.com> References: <1389623119-15863-1-git-send-email-a.motakis@virtualopensystems.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.85.212.174 Cc: Stefan Hajnoczi , "Michael S. Tsirkin" , Jason Wang , Michael Tokarev , Markus Armbruster , n.nikolaev@virtualopensystems.com, Luiz Capitulino , Anthony Liguori , Paolo Bonzini , lukego@gmail.com, Antonios Motakis , tech@virtualopensystems.com Subject: [Qemu-devel] [PATCH v6 7/8] Add new vhost-user netdev backend X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Add a new QEMU netdev backend that is intended to invoke vhost_net with the vhost-user backend. Also decouple virtio-net from the tap backend. Signed-off-by: Antonios Motakis Signed-off-by: Nikolay Nikolaev --- hmp-commands.hx | 4 +- hw/net/vhost_net.c | 66 ++++++++++++++++++++++------ hw/net/virtio-net.c | 42 ++++++++---------- hw/virtio/vhost.c | 1 - include/net/vhost-user.h | 17 ++++++++ include/net/vhost_net.h | 1 + net/Makefile.objs | 2 +- net/clients.h | 3 ++ net/hub.c | 1 + net/net.c | 2 + net/vhost-user.c | 111 +++++++++++++++++++++++++++++++++++++++++++++++ qapi-schema.json | 18 +++++++- qemu-options.hx | 13 ++++++ 13 files changed, 237 insertions(+), 44 deletions(-) create mode 100644 include/net/vhost-user.h create mode 100644 net/vhost-user.c diff --git a/hmp-commands.hx b/hmp-commands.hx index ebe8e78..d5a3774 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1190,7 +1190,7 @@ ETEXI { .name = "host_net_add", .args_type = "device:s,opts:s?", - .params = "tap|user|socket|vde|netmap|dump [options]", + .params = "tap|user|socket|vde|netmap|vhost-user|dump [options]", .help = "add host VLAN client", .mhandler.cmd = net_host_device_add, }, @@ -1218,7 +1218,7 @@ ETEXI { .name = "netdev_add", .args_type = "netdev:O", - .params = "[user|tap|socket|hubport|netmap],id=str[,prop=value][,...]", + .params = "[user|tap|socket|hubport|netmap|vhost-user],id=str[,prop=value][,...]", .help = "add host network device", .mhandler.cmd = hmp_netdev_add, }, diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index 3614e6c..e42f4d6 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -15,6 +15,7 @@ #include "net/net.h" #include "net/tap.h" +#include "net/vhost-user.h" #include "hw/virtio/virtio-net.h" #include "net/vhost_net.h" @@ -174,15 +175,20 @@ static int vhost_net_start_one(struct vhost_net *net, goto fail_start; } - net->nc->info->poll(net->nc, false); - qemu_set_fd_handler(net->backend, NULL, NULL, NULL); - file.fd = net->backend; - for (file.index = 0; file.index < net->dev.nvqs; ++file.index) { - const VhostOps *vhost_ops = net->dev.vhost_ops; - r = vhost_ops->vhost_call(&net->dev, VHOST_NET_SET_BACKEND, &file); - if (r < 0) { - r = -errno; - goto fail; + if (net->nc->info->poll) { + net->nc->info->poll(net->nc, false); + } + + if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP) { + qemu_set_fd_handler(net->backend, NULL, NULL, NULL); + file.fd = net->backend; + for (file.index = 0; file.index < net->dev.nvqs; ++file.index) { + const VhostOps *vhost_ops = net->dev.vhost_ops; + r = vhost_ops->vhost_call(&net->dev, VHOST_NET_SET_BACKEND, &file); + if (r < 0) { + r = -errno; + goto fail; + } } } return 0; @@ -193,7 +199,9 @@ fail: int r = vhost_ops->vhost_call(&net->dev, VHOST_NET_SET_BACKEND, &file); assert(r >= 0); } - net->nc->info->poll(net->nc, true); + if (net->nc->info->poll) { + net->nc->info->poll(net->nc, true); + } vhost_dev_stop(&net->dev, dev); fail_start: vhost_dev_disable_notifiers(&net->dev, dev); @@ -215,7 +223,9 @@ static void vhost_net_stop_one(struct vhost_net *net, int r = vhost_ops->vhost_call(&net->dev, VHOST_NET_SET_BACKEND, &file); assert(r >= 0); } - net->nc->info->poll(net->nc, true); + if (net->nc->info->poll) { + net->nc->info->poll(net->nc, true); + } vhost_dev_stop(&net->dev, dev); vhost_dev_disable_notifiers(&net->dev, dev); } @@ -235,7 +245,7 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, } for (i = 0; i < total_queues; i++) { - r = vhost_net_start_one(tap_get_vhost_net(ncs[i].peer), dev, i * 2); + r = vhost_net_start_one(get_vhost_net(ncs[i].peer), dev, i * 2); if (r < 0) { goto err; @@ -252,7 +262,7 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, err: while (--i >= 0) { - vhost_net_stop_one(tap_get_vhost_net(ncs[i].peer), dev); + vhost_net_stop_one(get_vhost_net(ncs[i].peer), dev); } return r; } @@ -273,7 +283,7 @@ void vhost_net_stop(VirtIODevice *dev, NetClientState *ncs, assert(r >= 0); for (i = 0; i < total_queues; i++) { - vhost_net_stop_one(tap_get_vhost_net(ncs[i].peer), dev); + vhost_net_stop_one(get_vhost_net(ncs[i].peer), dev); } } @@ -293,6 +303,29 @@ void vhost_net_virtqueue_mask(VHostNetState *net, VirtIODevice *dev, { vhost_virtqueue_mask(&net->dev, dev, idx, mask); } + +VHostNetState *get_vhost_net(NetClientState *nc) +{ + VHostNetState *vhost_net = 0; + + if (!nc) { + return 0; + } + + switch (nc->info->type) { + case NET_CLIENT_OPTIONS_KIND_TAP: + vhost_net = tap_get_vhost_net(nc); + break; + case NET_CLIENT_OPTIONS_KIND_VHOST_USER: + vhost_net = vhost_user_get_vhost_net(nc); + break; + default: + break; + } + + return vhost_net; +} + #else struct vhost_net *vhost_net_init(VhostNetOptions *options) { @@ -338,4 +371,9 @@ void vhost_net_virtqueue_mask(VHostNetState *net, VirtIODevice *dev, int idx, bool mask) { } + +VHostNetState *get_vhost_net(NetClientState *nc) +{ + return 0; +} #endif diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 3626608..d49ee82 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -105,14 +105,7 @@ static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) NetClientState *nc = qemu_get_queue(n->nic); int queues = n->multiqueue ? n->max_queues : 1; - if (!nc->peer) { - return; - } - if (nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { - return; - } - - if (!tap_get_vhost_net(nc->peer)) { + if (!get_vhost_net(nc->peer)) { return; } @@ -122,7 +115,7 @@ static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) } if (!n->vhost_started) { int r; - if (!vhost_net_query(tap_get_vhost_net(nc->peer), vdev)) { + if (!vhost_net_query(get_vhost_net(nc->peer), vdev)) { return; } n->vhost_started = 1; @@ -325,11 +318,16 @@ static void peer_test_vnet_hdr(VirtIONet *n) return; } - if (nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { - return; + switch (nc->peer->info->type) { + case NET_CLIENT_OPTIONS_KIND_TAP: + n->has_vnet_hdr = tap_has_vnet_hdr(nc->peer); + break; + case NET_CLIENT_OPTIONS_KIND_VHOST_USER: + n->has_vnet_hdr = 0; + break; + default: + break; } - - n->has_vnet_hdr = tap_has_vnet_hdr(nc->peer); } static int peer_has_vnet_hdr(VirtIONet *n) @@ -437,13 +435,10 @@ static uint32_t virtio_net_get_features(VirtIODevice *vdev, uint32_t features) features &= ~(0x1 << VIRTIO_NET_F_HOST_UFO); } - if (!nc->peer || nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { + if (!get_vhost_net(nc->peer)) { return features; } - if (!tap_get_vhost_net(nc->peer)) { - return features; - } - return vhost_net_get_features(tap_get_vhost_net(nc->peer), features); + return vhost_net_get_features(get_vhost_net(nc->peer), features); } static uint32_t virtio_net_bad_features(VirtIODevice *vdev) @@ -507,13 +502,10 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features) for (i = 0; i < n->max_queues; i++) { NetClientState *nc = qemu_get_subqueue(n->nic, i); - if (!nc->peer || nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { - continue; - } - if (!tap_get_vhost_net(nc->peer)) { + if (!get_vhost_net(nc->peer)) { continue; } - vhost_net_ack_features(tap_get_vhost_net(nc->peer), features); + vhost_net_ack_features(get_vhost_net(nc->peer), features); } } @@ -1443,7 +1435,7 @@ static bool virtio_net_guest_notifier_pending(VirtIODevice *vdev, int idx) VirtIONet *n = VIRTIO_NET(vdev); NetClientState *nc = qemu_get_subqueue(n->nic, vq2q(idx)); assert(n->vhost_started); - return vhost_net_virtqueue_pending(tap_get_vhost_net(nc->peer), idx); + return vhost_net_virtqueue_pending(get_vhost_net(nc->peer), idx); } static void virtio_net_guest_notifier_mask(VirtIODevice *vdev, int idx, @@ -1452,7 +1444,7 @@ static void virtio_net_guest_notifier_mask(VirtIODevice *vdev, int idx, VirtIONet *n = VIRTIO_NET(vdev); NetClientState *nc = qemu_get_subqueue(n->nic, vq2q(idx)); assert(n->vhost_started); - vhost_net_virtqueue_mask(tap_get_vhost_net(nc->peer), + vhost_net_virtqueue_mask(get_vhost_net(nc->peer), vdev, idx, mask); } diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index a1137e1..fe622fb 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -14,7 +14,6 @@ */ #include "hw/virtio/vhost.h" -#include "hw/virtio/vhost-backend.h" #include "hw/hw.h" #include "qemu/atomic.h" #include "qemu/range.h" diff --git a/include/net/vhost-user.h b/include/net/vhost-user.h new file mode 100644 index 0000000..85109f6 --- /dev/null +++ b/include/net/vhost-user.h @@ -0,0 +1,17 @@ +/* + * vhost-user.h + * + * Copyright (c) 2013 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef VHOST_USER_H_ +#define VHOST_USER_H_ + +struct vhost_net; +struct vhost_net *vhost_user_get_vhost_net(NetClientState *nc); + +#endif /* VHOST_USER_H_ */ diff --git a/include/net/vhost_net.h b/include/net/vhost_net.h index 1169562..abd3d0b 100644 --- a/include/net/vhost_net.h +++ b/include/net/vhost_net.h @@ -31,4 +31,5 @@ void vhost_net_ack_features(VHostNetState *net, unsigned features); bool vhost_net_virtqueue_pending(VHostNetState *net, int n); void vhost_net_virtqueue_mask(VHostNetState *net, VirtIODevice *dev, int idx, bool mask); +VHostNetState *get_vhost_net(NetClientState *nc); #endif diff --git a/net/Makefile.objs b/net/Makefile.objs index c25fe69..301f6b6 100644 --- a/net/Makefile.objs +++ b/net/Makefile.objs @@ -2,7 +2,7 @@ common-obj-y = net.o queue.o checksum.o util.o hub.o common-obj-y += socket.o common-obj-y += dump.o common-obj-y += eth.o -common-obj-$(CONFIG_POSIX) += tap.o +common-obj-$(CONFIG_POSIX) += tap.o vhost-user.o common-obj-$(CONFIG_LINUX) += tap-linux.o common-obj-$(CONFIG_WIN32) += tap-win32.o common-obj-$(CONFIG_BSD) += tap-bsd.o diff --git a/net/clients.h b/net/clients.h index 7322ff5..7f3d4ae 100644 --- a/net/clients.h +++ b/net/clients.h @@ -57,4 +57,7 @@ int net_init_netmap(const NetClientOptions *opts, const char *name, NetClientState *peer); #endif +int net_init_vhost_user(const NetClientOptions *opts, const char *name, + NetClientState *peer); + #endif /* QEMU_NET_CLIENTS_H */ diff --git a/net/hub.c b/net/hub.c index 33a99c9..7e0f2d6 100644 --- a/net/hub.c +++ b/net/hub.c @@ -322,6 +322,7 @@ void net_hub_check_clients(void) case NET_CLIENT_OPTIONS_KIND_TAP: case NET_CLIENT_OPTIONS_KIND_SOCKET: case NET_CLIENT_OPTIONS_KIND_VDE: + case NET_CLIENT_OPTIONS_KIND_VHOST_USER: has_host_dev = 1; break; default: diff --git a/net/net.c b/net/net.c index f8db85f..c1963b3 100644 --- a/net/net.c +++ b/net/net.c @@ -734,6 +734,7 @@ static int (* const net_client_init_fun[NET_CLIENT_OPTIONS_KIND_MAX])( [NET_CLIENT_OPTIONS_KIND_BRIDGE] = net_init_bridge, #endif [NET_CLIENT_OPTIONS_KIND_HUBPORT] = net_init_hubport, + [NET_CLIENT_OPTIONS_KIND_VHOST_USER] = net_init_vhost_user, }; @@ -767,6 +768,7 @@ static int net_client_init1(const void *object, int is_netdev, Error **errp) case NET_CLIENT_OPTIONS_KIND_BRIDGE: #endif case NET_CLIENT_OPTIONS_KIND_HUBPORT: + case NET_CLIENT_OPTIONS_KIND_VHOST_USER: break; default: diff --git a/net/vhost-user.c b/net/vhost-user.c new file mode 100644 index 0000000..5ad8fd0 --- /dev/null +++ b/net/vhost-user.c @@ -0,0 +1,111 @@ +/* + * vhost-user.c + * + * Copyright (c) 2013 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "clients.h" +#include "net/vhost_net.h" +#include "net/vhost-user.h" +#include "qemu/error-report.h" + +typedef struct VhostUserState { + NetClientState nc; + VHostNetState *vhost_net; + char *devpath; +} VhostUserState; + +VHostNetState *vhost_user_get_vhost_net(NetClientState *nc) +{ + VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc); + assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER); + return s->vhost_net; +} + +static int vhost_user_running(VhostUserState *s) +{ + return (s->vhost_net) ? 1 : 0; +} + +static int vhost_user_start(VhostUserState *s) +{ + VhostNetOptions options; + + if (vhost_user_running(s)) { + return 1; + } + + options.backend_type = VHOST_BACKEND_TYPE_USER; + options.net_backend = &s->nc; + options.devpath = s->devpath; + options.devfd = -1; + options.force = 1; + + s->vhost_net = vhost_net_init(&options); + + return vhost_user_running(s) ? 0 : -1; +} + +static void vhost_user_stop(VhostUserState *s) +{ + if (vhost_user_running(s)) { + vhost_net_cleanup(s->vhost_net); + } + + s->vhost_net = 0; +} + +static void vhost_user_cleanup(NetClientState *nc) +{ + VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc); + + vhost_user_stop(s); + qemu_purge_queued_packets(nc); +} + +static NetClientInfo net_vhost_user_info = { + .type = NET_CLIENT_OPTIONS_KIND_VHOST_USER, + .size = sizeof(VhostUserState), + .cleanup = vhost_user_cleanup, +}; + +static int net_vhost_user_init(NetClientState *peer, const char *device, + const char *name, const char *path) +{ + NetClientState *nc; + VhostUserState *s; + int r; + + nc = qemu_new_net_client(&net_vhost_user_info, peer, device, name); + + snprintf(nc->info_str, sizeof(nc->info_str), "vhost-user to %s", path); + + s = DO_UPCAST(VhostUserState, nc, nc); + + /* We don't provide a receive callback */ + s->nc.receive_disabled = 1; + + s->devpath = g_strdup(path); + + r = vhost_user_start(s); + + return r; +} + +int net_init_vhost_user(const NetClientOptions *opts, const char *name, + NetClientState *peer) +{ + const char *path; + const NetdevVhostUserOptions *vhost_user; + + assert(opts->kind == NET_CLIENT_OPTIONS_KIND_VHOST_USER); + vhost_user = opts->vhost_user; + + path = vhost_user->path; + + return net_vhost_user_init(peer, "vhost_user", name, path); +} diff --git a/qapi-schema.json b/qapi-schema.json index c3c939c..d3e0363 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -3030,6 +3030,21 @@ '*devname': 'str' } } ## +# @NetdevVhostUserOptions +# +# Vhost-user network backend +# +# @path: control socket path +# +# Since 2.0 +## +{ 'type': 'NetdevVhostUserOptions', + 'data': { + 'path': 'str' } } + +## + +## # @NetClientOptions # # A discriminated record of network device traits. @@ -3047,7 +3062,8 @@ 'dump': 'NetdevDumpOptions', 'bridge': 'NetdevBridgeOptions', 'hubport': 'NetdevHubPortOptions', - 'netmap': 'NetdevNetmapOptions' } } + 'netmap': 'NetdevNetmapOptions', + 'vhost-user': 'NetdevVhostUserOptions' } } ## # @NetLegacy diff --git a/qemu-options.hx b/qemu-options.hx index a12af97..7fdb1f3 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1436,6 +1436,7 @@ DEF("netdev", HAS_ARG, QEMU_OPTION_netdev, #ifdef CONFIG_NETMAP "netmap|" #endif + "vhost-user|" "socket|" "hubport],id=str[,option][,option][,...]\n", QEMU_ARCH_ALL) STEXI @@ -1767,6 +1768,18 @@ The hubport netdev lets you connect a NIC to a QEMU "vlan" instead of a single netdev. @code{-net} and @code{-device} with parameter @option{vlan} create the required hub automatically. +@item -netdev vhost-user,path=@var{path} + +Connect to a unix domain socket @var{path} on which listens a server that +implements vhost-user backend. + +Example: +@example +qemu -m 1024 -mem-path /hugetlbfs,prealloc=on,share=on \ + -netdev type=vhost-user,id=net0,path=/path/to/sock \ + -device virtio-net-pci,netdev=net0 +@end example + @item -net dump[,vlan=@var{n}][,file=@var{file}][,len=@var{len}] Dump network traffic on VLAN @var{n} to file @var{file} (@file{qemu-vlan0.pcap} by default). At most @var{len} bytes (64k by default) per packet are stored. The file format is