'mm-ctl',
'modprobe',
'ovs-vsctl',
+ 'passt',
'pdwtags',
'rmmod',
'scrub',
src/qemu/qemu_monitor_json.c
src/qemu/qemu_monitor_text.c
src/qemu/qemu_namespace.c
+src/qemu/qemu_passt.c
src/qemu/qemu_process.c
src/qemu/qemu_qapi.c
src/qemu/qemu_saveimage.c
'qemu_monitor_json.c',
'qemu_monitor_text.c',
'qemu_namespace.c',
+ 'qemu_passt.c',
'qemu_process.c',
'qemu_qapi.c',
'qemu_saveimage.c',
localstatedir / 'log' / 'swtpm' / 'libvirt' / 'qemu',
runstatedir / 'libvirt' / 'qemu',
runstatedir / 'libvirt' / 'qemu' / 'dbus',
+ runstatedir / 'libvirt' / 'qemu' / 'passt',
runstatedir / 'libvirt' / 'qemu' / 'slirp',
runstatedir / 'libvirt' / 'qemu' / 'swtpm',
]
#include "qemu_interface.h"
#include "qemu_alias.h"
#include "qemu_security.h"
+#include "qemu_passt.h"
#include "qemu_slirp.h"
#include "qemu_block.h"
#include "qemu_fd.h"
virJSONValue *
-qemuBuildHostNetProps(virDomainNetDef *net)
+qemuBuildHostNetProps(virDomainObj *vm,
+ virDomainNetDef *net)
{
virDomainNetType netType = virDomainNetGetActualType(net);
size_t i;
break;
case VIR_DOMAIN_NET_TYPE_USER:
- if (netpriv->slirpfd) {
+ if (net->backend.type == VIR_DOMAIN_NET_BACKEND_PASST) {
+ if (qemuPasstAddNetProps(vm, net, &netprops) < 0)
+ return NULL;
+ } else if (netpriv->slirpfd) {
if (virJSONValueObjectAdd(&netprops,
"s:type", "socket",
"s:fd", qemuFDPassDirectGetPath(netpriv->slirpfd),
qemuFDPassDirectTransferCommand(netpriv->slirpfd, cmd);
qemuFDPassTransferCommand(netpriv->vdpafd, cmd);
- if (!(hostnetprops = qemuBuildHostNetProps(net)))
+ if (!(hostnetprops = qemuBuildHostNetProps(vm, net)))
goto cleanup;
if (qemuBuildNetdevCommandlineFromJSON(cmd, hostnetprops, qemuCaps) < 0)
qemuBuildChannelGuestfwdNetdevProps(virDomainChrDef *chr);
virJSONValue *
-qemuBuildHostNetProps(virDomainNetDef *net);
+qemuBuildHostNetProps(virDomainObj *vm,
+ virDomainNetDef *net);
int
qemuBuildInterfaceConnect(virDomainObj *vm,
/* if there is a builtin slirp, prevent slirp-helper */
if (net->type == VIR_DOMAIN_NET_TYPE_USER &&
+ net->backend.type == VIR_DOMAIN_NET_BACKEND_DEFAULT &&
!QEMU_DOMAIN_NETWORK_PRIVATE(net)->slirp)
return false;
}
}
bool
-qemuDomainNetSupportsMTU(virDomainNetType type)
+qemuDomainNetSupportsMTU(virDomainNetType type,
+ virDomainNetBackendType backend)
{
switch (type) {
case VIR_DOMAIN_NET_TYPE_NETWORK:
case VIR_DOMAIN_NET_TYPE_VHOSTUSER:
return true;
case VIR_DOMAIN_NET_TYPE_USER:
+ return backend == VIR_DOMAIN_NET_BACKEND_PASST;
case VIR_DOMAIN_NET_TYPE_SERVER:
case VIR_DOMAIN_NET_TYPE_CLIENT:
case VIR_DOMAIN_NET_TYPE_MCAST:
bool qemuDomainSupportsNicdev(virDomainDef *def,
virDomainNetDef *net);
-bool qemuDomainNetSupportsMTU(virDomainNetType type);
+bool qemuDomainNetSupportsMTU(virDomainNetType type,
+ virDomainNetBackendType backend);
int qemuDomainSetPrivatePaths(virQEMUDriver *driver,
virDomainObj *vm);
#include "qemu_dbus.h"
#include "qemu_domain.h"
#include "qemu_tpm.h"
+#include "qemu_passt.h"
#include "qemu_slirp.h"
#include "qemu_virtiofs.h"
for (i = 0; i < def->nnets; i++) {
virDomainNetDef *net = def->nets[i];
- if (qemuSlirpStart(vm, net, incomingMigration) < 0)
- return -1;
+ if (net->type != VIR_DOMAIN_NET_TYPE_USER)
+ continue;
+
+ if (net->backend.type == VIR_DOMAIN_NET_BACKEND_PASST) {
+ if (qemuPasstStart(vm, net) < 0)
+ return -1;
+ } else {
+ if (qemuSlirpStart(vm, net, incomingMigration) < 0)
+ return -1;
+ }
}
for (i = 0; i < def->nfss; i++) {
if (slirp)
qemuSlirpStop(slirp, vm, driver, net);
+
+ if (net->type == VIR_DOMAIN_NET_TYPE_USER &&
+ net->backend.type == VIR_DOMAIN_NET_BACKEND_PASST) {
+ qemuPasstStop(vm, net);
+ }
+
if (actualType == VIR_DOMAIN_NET_TYPE_ETHERNET && net->downscript)
virNetDevRunEthernetScript(net->ifname, net->downscript);
}
if (slirp && qemuSlirpSetupCgroup(slirp, cgroup) < 0)
return -1;
+
+ if (net->type == VIR_DOMAIN_NET_TYPE_USER &&
+ net->backend.type == VIR_DOMAIN_NET_BACKEND_PASST &&
+ qemuPasstSetupCgroup(vm, net, cgroup) < 0) {
+ return -1;
+ }
}
for (i = 0; i < def->ntpms; i++) {
#include "qemu_command.h"
#include "qemu_hostdev.h"
#include "qemu_interface.h"
+#include "qemu_passt.h"
#include "qemu_process.h"
#include "qemu_security.h"
#include "qemu_block.h"
break;
case VIR_DOMAIN_NET_TYPE_USER:
- if (!priv->disableSlirp &&
- virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DBUS_VMSTATE)) {
+ if (net->backend.type == VIR_DOMAIN_NET_BACKEND_PASST) {
+
+ if (qemuPasstStart(vm, net) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("Failed to start passt"));
+ goto cleanup;
+ }
+
+ } else if (!priv->disableSlirp &&
+ virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DBUS_VMSTATE)) {
if (qemuInterfacePrepareSlirp(driver, net) < 0)
goto cleanup;
virNetDevSetMTU(net->ifname, net->mtu) < 0)
goto cleanup;
- if (!(netprops = qemuBuildHostNetProps(net)))
+ if (!(netprops = qemuBuildHostNetProps(vm, net)))
goto cleanup;
qemuDomainObjEnterMonitor(vm);
netdev_name = g_strdup_printf("host%s", net->info.alias);
if (QEMU_DOMAIN_NETWORK_PRIVATE(net)->slirp)
qemuSlirpStop(QEMU_DOMAIN_NETWORK_PRIVATE(net)->slirp, vm, driver, net);
+
+ if (net->type == VIR_DOMAIN_NET_TYPE_USER &&
+ net->backend.type == VIR_DOMAIN_NET_BACKEND_PASST) {
+ qemuPasstStop(vm, net);
+ }
+
qemuDomainObjEnterMonitor(vm);
if (charDevPlugged &&
qemuMonitorDetachCharDev(priv->mon, charDevAlias) < 0)
if (QEMU_DOMAIN_NETWORK_PRIVATE(net)->slirp)
qemuSlirpStop(QEMU_DOMAIN_NETWORK_PRIVATE(net)->slirp, vm, driver, net);
+ if (net->type == VIR_DOMAIN_NET_TYPE_USER &&
+ net->backend.type == VIR_DOMAIN_NET_BACKEND_PASST) {
+ qemuPasstStop(vm, net);
+ }
+
virDomainAuditNet(vm, net, NULL, "detach", true);
for (i = 0; i < vm->def->nnets; i++) {
--- /dev/null
+/*
+ * qemu_passt.c: QEMU passt support
+ *
+ * Copyright (C) 2022 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include "qemu_dbus.h"
+#include "qemu_extdevice.h"
+#include "qemu_security.h"
+#include "qemu_passt.h"
+#include "virenum.h"
+#include "virerror.h"
+#include "virjson.h"
+#include "virlog.h"
+#include "virpidfile.h"
+
+#define VIR_FROM_THIS VIR_FROM_NONE
+
+VIR_LOG_INIT("qemu.passt");
+
+
+static char *
+qemuPasstCreatePidFilename(virDomainObj *vm,
+ virDomainNetDef *net)
+{
+ qemuDomainObjPrivate *priv = vm->privateData;
+ virQEMUDriver *driver = priv->driver;
+ g_autofree char *shortName = virDomainDefGetShortName(vm->def);
+ g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
+ g_autofree char *name = NULL;
+
+ name = g_strdup_printf("%s-%s-passt", shortName, net->info.alias);
+
+ return virPidFileBuildPath(cfg->passtStateDir, name);
+}
+
+
+static char *
+qemuPasstCreateSocketPath(virDomainObj *vm,
+ virDomainNetDef *net)
+{
+ qemuDomainObjPrivate *priv = vm->privateData;
+ virQEMUDriver *driver = priv->driver;
+ g_autofree char *shortName = virDomainDefGetShortName(vm->def);
+ g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
+
+ return g_strdup_printf("%s/%s-%s.socket", cfg->passtStateDir,
+ shortName, net->info.alias);
+}
+
+
+static int
+qemuPasstGetPid(virDomainObj *vm,
+ virDomainNetDef *net,
+ pid_t *pid)
+{
+ g_autofree char *pidfile = qemuPasstCreatePidFilename(vm, net);
+
+ return virPidFileReadPathIfLocked(pidfile, pid);
+}
+
+
+int
+qemuPasstAddNetProps(virDomainObj *vm,
+ virDomainNetDef *net,
+ virJSONValue **netprops)
+{
+ g_autofree char *passtSocketName = qemuPasstCreateSocketPath(vm, net);
+ g_autoptr(virJSONValue) addrprops = NULL;
+
+ if (virJSONValueObjectAdd(&addrprops,
+ "s:type", "unix",
+ "s:path", passtSocketName,
+ NULL) < 0) {
+ return -1;
+ }
+
+ if (virJSONValueObjectAdd(netprops,
+ "s:type", "stream",
+ "a:addr", &addrprops,
+ "b:server", false,
+ /* "u:reconnect", 5, */
+ NULL) < 0) {
+ return -1;
+ }
+ return 0;
+}
+
+
+void
+qemuPasstStop(virDomainObj *vm,
+ virDomainNetDef *net)
+{
+ g_autofree char *pidfile = qemuPasstCreatePidFilename(vm, net);
+ virErrorPtr orig_err;
+
+ virErrorPreserveLast(&orig_err);
+
+ if (virPidFileForceCleanupPath(pidfile) < 0)
+ VIR_WARN("Unable to kill passt process");
+
+ virErrorRestore(&orig_err);
+}
+
+
+int
+qemuPasstSetupCgroup(virDomainObj *vm,
+ virDomainNetDef *net,
+ virCgroup *cgroup)
+{
+ pid_t pid = (pid_t) -1;
+
+ if (qemuPasstGetPid(vm, net, &pid) < 0 || pid <= 0)
+ return -1;
+
+ return virCgroupAddProcess(cgroup, pid);
+}
+
+
+int
+qemuPasstStart(virDomainObj *vm,
+ virDomainNetDef *net)
+{
+ qemuDomainObjPrivate *priv = vm->privateData;
+ virQEMUDriver *driver = priv->driver;
+ g_autofree char *passtSocketName = qemuPasstCreateSocketPath(vm, net);
+ g_autoptr(virCommand) cmd = NULL;
+ g_autofree char *pidfile = qemuPasstCreatePidFilename(vm, net);
+ char macaddr[VIR_MAC_STRING_BUFLEN];
+ size_t i;
+ pid_t pid = (pid_t) -1;
+ int exitstatus = 0;
+ int cmdret = 0;
+ VIR_AUTOCLOSE errfd = -1;
+
+ cmd = virCommandNew(PASST);
+
+ virCommandClearCaps(cmd);
+ virCommandSetPidFile(cmd, pidfile);
+ virCommandSetErrorFD(cmd, &errfd);
+ virCommandDaemonize(cmd);
+
+ virCommandAddArgList(cmd,
+ "--one-off",
+ "--socket", passtSocketName,
+ "--mac-addr", virMacAddrFormat(&net->mac, macaddr),
+ NULL);
+
+ if (net->mtu) {
+ virCommandAddArg(cmd, "--mtu");
+ virCommandAddArgFormat(cmd, "%u", net->mtu);
+ }
+
+ if (net->backend.upstream)
+ virCommandAddArgList(cmd, "--interface", net->backend.upstream, NULL);
+
+ if (net->backend.logFile)
+ virCommandAddArgList(cmd, "--log-file", net->backend.logFile, NULL);
+
+ /* Add IP address info */
+ for (i = 0; i < net->guestIP.nips; i++) {
+ const virNetDevIPAddr *ip = net->guestIP.ips[i];
+ g_autofree char *addr = NULL;
+
+ /* validation has already limited us to
+ * a single IPv4 and single IPv6 address
+ */
+ if (!(addr = virSocketAddrFormat(&ip->address)))
+ return -1;
+
+ virCommandAddArgList(cmd, "--address", addr, NULL);
+
+ if (ip->prefix && VIR_SOCKET_ADDR_IS_FAMILY(&ip->address, AF_INET)) {
+ /* validation already made sure no prefix is
+ * specified for IPv6 (not allowed by passt)
+ */
+ virCommandAddArg(cmd, "--netmask");
+ virCommandAddArgFormat(cmd, "%u", ip->prefix);
+ }
+ }
+
+ /* Add port forwarding info */
+
+ for (i = 0; i < net->nPortForwards; i++) {
+ virDomainNetPortForward *pf = net->portForwards[i];
+ g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
+
+ if (pf->proto == VIR_DOMAIN_NET_PROTO_TCP) {
+ virCommandAddArg(cmd, "--tcp-ports");
+ } else if (pf->proto == VIR_DOMAIN_NET_PROTO_UDP) {
+ virCommandAddArg(cmd, "--udp-ports");
+ } else {
+ /* validation guarantees this will never happen */
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Invalid portForward proto value %u"), pf->proto);
+ goto error;
+ }
+
+ if (VIR_SOCKET_ADDR_VALID(&pf->address)) {
+ g_autofree char *addr = NULL;
+
+ if (!(addr = virSocketAddrFormat(&pf->address)))
+ goto error;
+
+ virBufferAddStr(&buf, addr);
+
+ if (pf->dev)
+ virBufferAsprintf(&buf, "%%%s", pf->dev);
+
+ virBufferAddChar(&buf, '/');
+ }
+
+ if (!pf->nRanges) {
+ virBufferAddLit(&buf, "all");
+ } else {
+ size_t r;
+
+ for (r = 0; r < pf->nRanges; r++) {
+ virDomainNetPortForwardRange *range = pf->ranges[r];
+
+ if (r > 0)
+ virBufferAddChar(&buf, ',');
+
+ if (range->exclude == VIR_TRISTATE_BOOL_YES)
+ virBufferAddChar(&buf, '~');
+
+ virBufferAsprintf(&buf, "%u", range->start);
+
+ if (range->end)
+ virBufferAsprintf(&buf, "-%u", range->end);
+ if (range->to) {
+ virBufferAsprintf(&buf, ":%u", range->to);
+ if (range->end) {
+ virBufferAsprintf(&buf, "-%u",
+ range->end + range->to - range->start);
+ }
+ }
+ }
+ }
+ virCommandAddArg(cmd, virBufferCurrentContent(&buf));
+ }
+
+
+ if (qemuExtDeviceLogCommand(driver, vm, cmd, "passt") < 0)
+ goto error;
+
+ if (qemuSecurityCommandRun(driver, vm, cmd, -1, -1, &exitstatus, &cmdret) < 0)
+ goto error;
+
+ if (cmdret < 0 || exitstatus != 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Could not start 'passt'. exitstatus: %d"), exitstatus);
+ goto error;
+ }
+
+ return 0;
+
+ error:
+ ignore_value(virPidFileReadPathIfLocked(pidfile, &pid));
+ if (pid != -1)
+ virProcessKillPainfully(pid, true);
+ if (pidfile)
+ unlink(pidfile);
+
+ return -1;
+}
--- /dev/null
+/*
+ * qemu_passt.h: QEMU passt support
+ *
+ * Copyright (C) 2022 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "qemu_conf.h"
+
+int
+qemuPasstAddNetProps(virDomainObj *vm,
+ virDomainNetDef *net,
+ virJSONValue **netprops);
+
+int qemuPasstStart(virDomainObj *vm,
+ virDomainNetDef *net);
+
+void qemuPasstStop(virDomainObj *vm,
+ virDomainNetDef *net);
+
+int qemuPasstSetupCgroup(virDomainObj *vm,
+ virDomainNetDef *net,
+ virCgroup *cgroup);
if (virDomainHostdevInsert(def, hostdev) < 0)
return -1;
} else if (actualType == VIR_DOMAIN_NET_TYPE_USER &&
+ net->backend.type == VIR_DOMAIN_NET_BACKEND_DEFAULT &&
!priv->disableSlirp &&
virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DBUS_VMSTATE)) {
if (qemuInterfacePrepareSlirp(driver, net) < 0)
}
hasIPv6 = true;
+ if (ip->prefix && ip->prefix != 64) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("unsupported IPv6 address prefix='%u' - must be 64"),
+ ip->prefix);
+ return -1;
+ }
+
if (ip->prefix > 120) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("prefix too long"));
}
if (net->mtu &&
- !qemuDomainNetSupportsMTU(net->type)) {
+ !qemuDomainNetSupportsMTU(net->type, net->backend.type)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("setting MTU on interface type %s is not supported yet"),
virDomainNetTypeToString(net->type));
--- /dev/null
+LC_ALL=C \
+PATH=/bin \
+HOME=/tmp/lib/domain--1-QEMUGuest1 \
+USER=test \
+LOGNAME=test \
+XDG_DATA_HOME=/tmp/lib/domain--1-QEMUGuest1/.local/share \
+XDG_CACHE_HOME=/tmp/lib/domain--1-QEMUGuest1/.cache \
+XDG_CONFIG_HOME=/tmp/lib/domain--1-QEMUGuest1/.config \
+/usr/bin/qemu-system-x86_64 \
+-name guest=QEMUGuest1,debug-threads=on \
+-S \
+-object secret,id=masterKey0,format=raw,file=/tmp/lib/domain--1-QEMUGuest1/master-key.aes \
+-machine pc,usb=off,dump-guest-core=off \
+-accel tcg \
+-m 214 \
+-overcommit mem-lock=off \
+-smp 1,sockets=1,cores=1,threads=1 \
+-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \
+-display none \
+-no-user-config \
+-nodefaults \
+-chardev socket,id=charmonitor,fd=1729,server=on,wait=off \
+-mon chardev=charmonitor,id=monitor,mode=control \
+-rtc base=utc \
+-no-shutdown \
+-no-acpi \
+-boot strict=on \
+-blockdev '{"driver":"host_device","filename":"/dev/HostVG/QEMUGuest1","node-name":"libvirt-1-storage","auto-read-only":true,"discard":"unmap"}' \
+-blockdev '{"node-name":"libvirt-1-format","read-only":false,"driver":"raw","file":"libvirt-1-storage"}' \
+-device ide-hd,bus=ide.0,unit=0,drive=libvirt-1-format,id=ide0-0-0,bootindex=1 \
+-netdev stream,addr.path=/var/run/libvirt/qemu/passt/UUID-net0.socket,server=off,reconnect=5,id=hostnet0 \
+-device rtl8139,netdev=hostnet0,id=net0,mac=00:11:22:33:44:55,bus=pci.0,addr=0x2 \
+-audiodev '{"id":"audio1","driver":"none"}' \
+-msg timestamp=on
--- /dev/null
+LC_ALL=C \
+PATH=/bin \
+HOME=/tmp/lib/domain--1-QEMUGuest1 \
+USER=test \
+LOGNAME=test \
+XDG_DATA_HOME=/tmp/lib/domain--1-QEMUGuest1/.local/share \
+XDG_CACHE_HOME=/tmp/lib/domain--1-QEMUGuest1/.cache \
+XDG_CONFIG_HOME=/tmp/lib/domain--1-QEMUGuest1/.config \
+/usr/bin/qemu-system-x86_64 \
+-name guest=QEMUGuest1,debug-threads=on \
+-S \
+-object '{"qom-type":"secret","id":"masterKey0","format":"raw","file":"/tmp/lib/domain--1-QEMUGuest1/master-key.aes"}' \
+-machine pc,usb=off,dump-guest-core=off,memory-backend=pc.ram \
+-accel tcg \
+-cpu qemu64 \
+-m 214 \
+-object '{"qom-type":"memory-backend-ram","id":"pc.ram","size":224395264}' \
+-overcommit mem-lock=off \
+-smp 1,sockets=1,cores=1,threads=1 \
+-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \
+-display none \
+-no-user-config \
+-nodefaults \
+-chardev socket,id=charmonitor,fd=1729,server=on,wait=off \
+-mon chardev=charmonitor,id=monitor,mode=control \
+-rtc base=utc \
+-no-shutdown \
+-no-acpi \
+-boot strict=on \
+-blockdev '{"driver":"host_device","filename":"/dev/HostVG/QEMUGuest1","node-name":"libvirt-1-storage","auto-read-only":true,"discard":"unmap"}' \
+-blockdev '{"node-name":"libvirt-1-format","read-only":false,"driver":"raw","file":"libvirt-1-storage"}' \
+-device '{"driver":"ide-hd","bus":"ide.0","unit":0,"drive":"libvirt-1-format","id":"ide0-0-0","bootindex":1}' \
+-netdev '{"type":"stream","addr":{"type":"unix","path":"/bad-test-used-env-xdg-runtime-dir/libvirt/qemu/run/passt/-1-QEMUGuest1-net0.socket"},"server":false,"id":"hostnet0"}' \
+-device '{"driver":"rtl8139","netdev":"hostnet0","id":"net0","mac":"00:11:22:33:44:55","bus":"pci.0","addr":"0x2"}' \
+-audiodev '{"id":"audio1","driver":"none"}' \
+-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \
+-msg timestamp=on
virDomainNetDef *net = vm->def->nets[i];
if (net->type == VIR_DOMAIN_NET_TYPE_USER &&
+ net->backend.type == VIR_DOMAIN_NET_BACKEND_DEFAULT &&
virQEMUCapsGet(info->qemuCaps, QEMU_CAPS_DBUS_VMSTATE)) {
qemuSlirp *slirp = qemuSlirpNew();
slirp->fd[0] = 42;
DO_TEST_NOCAPS("net-user");
DO_TEST_CAPS_ARCH_LATEST_FULL("net-user", "x86_64", ARG_FLAGS, FLAG_SLIRP_HELPER);
DO_TEST_NOCAPS("net-user-addr");
+ DO_TEST_CAPS_LATEST("net-user-passt");
DO_TEST_NOCAPS("net-virtio");
DO_TEST_NOCAPS("net-virtio-device");
DO_TEST_NOCAPS("net-virtio-disable-offloads");