diff --git a/meson.build b/meson.build
index 177009c44d..6604ba20c8 100644
--- a/meson.build
+++ b/meson.build
@@ -780,6 +780,7 @@ optional_programs = [
'mm-ctl',
'modprobe',
'ovs-vsctl',
+ 'passt',
'pdwtags',
'rmmod',
'scrub',
diff --git a/po/POTFILES b/po/POTFILES
index b2297be84e..4e446aaf40 100644
--- a/po/POTFILES
+++ b/po/POTFILES
@@ -179,6 +179,7 @@ src/qemu/qemu_monitor.c
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
diff --git a/src/qemu/meson.build b/src/qemu/meson.build
index 96952cc52d..c8806bbc36 100644
--- a/src/qemu/meson.build
+++ b/src/qemu/meson.build
@@ -28,6 +28,7 @@ qemu_driver_sources = [
'qemu_monitor_json.c',
'qemu_monitor_text.c',
'qemu_namespace.c',
+ 'qemu_passt.c',
'qemu_process.c',
'qemu_qapi.c',
'qemu_saveimage.c',
@@ -216,6 +217,7 @@ if conf.has('WITH_QEMU')
localstatedir / 'log' / 'swtpm' / 'libvirt' / 'qemu',
runstatedir / 'libvirt' / 'qemu',
runstatedir / 'libvirt' / 'qemu' / 'dbus',
+ runstatedir / 'libvirt' / 'qemu' / 'passt',
runstatedir / 'libvirt' / 'qemu' / 'slirp',
runstatedir / 'libvirt' / 'qemu' / 'swtpm',
]
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index 9c05750d66..d03ddb0886 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -27,6 +27,7 @@
#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"
@@ -3817,7 +3818,8 @@ qemuBuildNicDevProps(virDomainDef *def,
virJSONValue *
-qemuBuildHostNetProps(virDomainNetDef *net)
+qemuBuildHostNetProps(virDomainObj *vm,
+ virDomainNetDef *net)
{
virDomainNetType netType = virDomainNetGetActualType(net);
size_t i;
@@ -3932,7 +3934,10 @@ qemuBuildHostNetProps(virDomainNetDef *net)
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),
@@ -8503,7 +8508,7 @@ qemuBuildInterfaceCommandLine(virQEMUDriver *driver,
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)
diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h
index 761cc5d865..c49096a057 100644
--- a/src/qemu/qemu_command.h
+++ b/src/qemu/qemu_command.h
@@ -78,7 +78,8 @@ virJSONValue *
qemuBuildChannelGuestfwdNetdevProps(virDomainChrDef *chr);
virJSONValue *
-qemuBuildHostNetProps(virDomainNetDef *net);
+qemuBuildHostNetProps(virDomainObj *vm,
+ virDomainNetDef *net);
int
qemuBuildInterfaceConnect(virDomainObj *vm,
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index d84e7832b5..2eb5653254 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -2379,6 +2379,7 @@ qemuDomainGetSlirpHelperOk(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;
}
@@ -9848,7 +9849,8 @@ qemuDomainSupportsNicdev(virDomainDef *def,
}
bool
-qemuDomainNetSupportsMTU(virDomainNetType type)
+qemuDomainNetSupportsMTU(virDomainNetType type,
+ virDomainNetBackendType backend)
{
switch (type) {
case VIR_DOMAIN_NET_TYPE_NETWORK:
@@ -9857,6 +9859,7 @@ qemuDomainNetSupportsMTU(virDomainNetType type)
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:
diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h
index 5114a311fb..08430b67b9 100644
--- a/src/qemu/qemu_domain.h
+++ b/src/qemu/qemu_domain.h
@@ -882,7 +882,8 @@ int qemuDomainRefreshVcpuHalted(virDomainObj *vm,
bool qemuDomainSupportsNicdev(virDomainDef *def,
virDomainNetDef *net);
-bool qemuDomainNetSupportsMTU(virDomainNetType type);
+bool qemuDomainNetSupportsMTU(virDomainNetType type,
+ virDomainNetBackendType backend);
int qemuDomainSetPrivatePaths(virQEMUDriver *driver,
virDomainObj *vm);
diff --git a/src/qemu/qemu_extdevice.c b/src/qemu/qemu_extdevice.c
index d5c3e8ed71..f7b2e2e653 100644
--- a/src/qemu/qemu_extdevice.c
+++ b/src/qemu/qemu_extdevice.c
@@ -25,6 +25,7 @@
#include "qemu_dbus.h"
#include "qemu_domain.h"
#include "qemu_tpm.h"
+#include "qemu_passt.h"
#include "qemu_slirp.h"
#include "qemu_virtiofs.h"
@@ -194,8 +195,16 @@ qemuExtDevicesStart(virQEMUDriver *driver,
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++) {
@@ -254,6 +263,12 @@ qemuExtDevicesStop(virQEMUDriver *driver,
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);
}
@@ -319,6 +334,12 @@ qemuExtDevicesSetupCgroup(virQEMUDriver *driver,
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++) {
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
index dba699a8a8..026e1ee5ad 100644
--- a/src/qemu/qemu_hotplug.c
+++ b/src/qemu/qemu_hotplug.c
@@ -31,6 +31,7 @@
#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"
@@ -1202,8 +1203,16 @@ qemuDomainAttachNetDevice(virQEMUDriver *driver,
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;
@@ -1270,7 +1279,7 @@ qemuDomainAttachNetDevice(virQEMUDriver *driver,
virNetDevSetMTU(net->ifname, net->mtu) < 0)
goto cleanup;
- if (!(netprops = qemuBuildHostNetProps(net)))
+ if (!(netprops = qemuBuildHostNetProps(vm, net)))
goto cleanup;
qemuDomainObjEnterMonitor(vm);
@@ -1418,6 +1427,12 @@ qemuDomainAttachNetDevice(virQEMUDriver *driver,
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)
@@ -4619,6 +4634,11 @@ qemuDomainRemoveNetDevice(virQEMUDriver *driver,
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++) {
diff --git a/src/qemu/qemu_passt.c b/src/qemu/qemu_passt.c
new file mode 100644
index 0000000000..3355f7b2fa
--- /dev/null
+++ b/src/qemu/qemu_passt.c
@@ -0,0 +1,282 @@
+/*
+ * 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
+ * .
+ */
+
+#include
+
+#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;
+}
diff --git a/src/qemu/qemu_passt.h b/src/qemu/qemu_passt.h
new file mode 100644
index 0000000000..623b494b7a
--- /dev/null
+++ b/src/qemu/qemu_passt.h
@@ -0,0 +1,38 @@
+/*
+ * 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
+ * .
+ */
+
+#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);
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index 9fc7eada52..ee9f0784d3 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -5846,6 +5846,7 @@ qemuProcessNetworkPrepareDevices(virQEMUDriver *driver,
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)
diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c
index c687df0bfc..6e04b22da4 100644
--- a/src/qemu/qemu_validate.c
+++ b/src/qemu/qemu_validate.c
@@ -1830,6 +1830,13 @@ qemuValidateDomainDeviceDefNetwork(const virDomainNetDef *net,
}
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"));
@@ -1892,7 +1899,7 @@ qemuValidateDomainDeviceDefNetwork(const virDomainNetDef *net,
}
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));
diff --git a/tests/qemuxml2argvdata/net-user-passt.args b/tests/qemuxml2argvdata/net-user-passt.args
new file mode 100644
index 0000000000..8c887ca210
--- /dev/null
+++ b/tests/qemuxml2argvdata/net-user-passt.args
@@ -0,0 +1,34 @@
+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
diff --git a/tests/qemuxml2argvdata/net-user-passt.x86_64-latest.args b/tests/qemuxml2argvdata/net-user-passt.x86_64-latest.args
new file mode 100644
index 0000000000..48e3e8ca8b
--- /dev/null
+++ b/tests/qemuxml2argvdata/net-user-passt.x86_64-latest.args
@@ -0,0 +1,37 @@
+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
diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c
index c9e291c450..8c52feb83c 100644
--- a/tests/qemuxml2argvtest.c
+++ b/tests/qemuxml2argvtest.c
@@ -471,6 +471,7 @@ testCompareXMLToArgvCreateArgs(virQEMUDriver *drv,
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;
@@ -1469,6 +1470,7 @@ mymain(void)
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");