[PATCH] NEWS: Mention switch to virtio-scsi on ARM
by Jim Fehlig
From: Jim Fehlig <jfehlig(a)suse.com>
Signed-off-by: Jim Fehlig <jfehlig(a)suse.com>
---
NEWS.rst | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/NEWS.rst b/NEWS.rst
index d8bd2559f4..59494cb380 100644
--- a/NEWS.rst
+++ b/NEWS.rst
@@ -19,6 +19,12 @@ v11.6.0 (unreleased)
* **Improvements**
+ * qemu: Change default SCSI controller model to ``virtio-scsi`` for ARM
+
+ The previous default of ``lsilogic`` is unsupported by modern operating
+ systems. ``virtio-scsi`` is a more suitable default for ARM ``virt``
+ machine types.
+
* **Bug fixes**
--
2.43.0
6 days
How to process after sending a patch assisted with AI
by Enrique Llorente Pastora
Hi,
How should I process after sending a patch that has code generated by AI [1] ?
Is creating a new AI free version with some explanation enough to
prevent the submission from being suspicious ?
Possible example for explanation would be "I am using how
qemu:commandline is implemented as inspiration for the feature and not
AI was used to generate code or tests as opposed to v3".
BR.
[1] https://lists.libvirt.org/archives/list/devel@lists.libvirt.org/message/Z...
--
Quique Llorente
CNV networking Senior Software Engineer
Red Hat EMEA
ellorent(a)redhat.com
@RedHat Red Hat Red Hat
6 days, 5 hours
[PATCH v3] qemu: passt: add support for custom command line arguments
by Enrique Llorente
This adds support for custom command line arguments for the passt
backend, similar to qemu:commandline. The feature allows passing
additional arguments to the passt process for development and testing
purposes.
The implementation:
- Adds a passt XML namespace for custom arguments
- Properly taints the domain when custom args are used
- Includes comprehensive test coverage
- Adds documentation for the new feature
Usage example:
<interface type='user'>
<backend type='passt' xmlns:passt='http://libvirt.org/schemas/domain/passt/1.0'>
<passt:commandline>
<passt:arg value='--debug'/>
<passt:arg value='--verbose'/>
</passt:commandline>
</backend>
</interface>
Signed-off-by: Enrique Llorente <ellorent(a)redhat.com>
---
v3:
- Fix all test problems
- Refactor domain_conf.c to use libvirt xml constructs to have proper
indent
- Rework documentation and make it more concise
- Add domainpassttest.c to check that arguments are passed to passt
docs/formatdomain.rst | 38 ++++
src/conf/domain_conf.c | 61 ++++++-
src/conf/domain_conf.h | 6 +
src/conf/schemas/domaincommon.rng | 15 ++
src/qemu/qemu_passt.c | 9 +
tests/meson.build | 1 +
tests/qemupassttest.c | 162 ++++++++++++++++++
...-user-passt-custom-args.x86_64-latest.args | 35 ++++
...t-user-passt-custom-args.x86_64-latest.xml | 67 ++++++++
.../net-user-passt-custom-args.xml | 64 +++++++
tests/qemuxmlconftest.c | 1 +
11 files changed, 458 insertions(+), 1 deletion(-)
create mode 100644 tests/qemupassttest.c
create mode 100644 tests/qemuxmlconfdata/net-user-passt-custom-args.x86_64-latest.args
create mode 100644 tests/qemuxmlconfdata/net-user-passt-custom-args.x86_64-latest.xml
create mode 100644 tests/qemuxmlconfdata/net-user-passt-custom-args.xml
diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst
index 9a2f065590..4c01a07135 100644
--- a/docs/formatdomain.rst
+++ b/docs/formatdomain.rst
@@ -5464,6 +5464,44 @@ ports **with the exception of some subset**.
</devices>
...
+Custom passt commandline arguments
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. warning::
+
+ **This is an unsupported feature for development and testing only.**
+ Using it will taint the domain. Users are strongly advised to use the
+ proper libvirt XML elements for configuring passt instead.
+
+
+:since:`Since 11.7.0` For development and testing purposes, it is
+sometimes useful to be able to pass additional command-line arguments
+directly to the passt process. This can be accomplished using a
+special passt namespace in the domain XML that is similar to the qemu
+commandline namespace:
+
+::
+
+ ...
+ <devices>
+ ...
+ <interface type='user'>
+ <backend type='passt'>
+ <passt:commandline xmlns:passt='http://libvirt.org/schemas/domain/passt/1.0'>
+ <passt:arg value='--debug'/>
+ <passt:arg value='--verbose'/>
+ </passt:commandline>
+ </backend>
+ </interface>
+ </devices>
+ ...
+
+Any arguments provided using this method will be appended to the passt
+command line, and will therefore override any default options set by
+libvirt in the case of conflicts. **This can lead to unexpected behavior
+and libvirt cannot guarantee functionality when its default configuration
+is overridden.**
+
Generic ethernet connection
^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 1e24e41a48..9721763622 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -2918,6 +2918,10 @@ virDomainNetDefFree(virDomainNetDef *def)
g_free(def->backend.tap);
g_free(def->backend.vhost);
g_free(def->backend.logFile);
+ if (def->backend.passtCommandline) {
+ g_strfreev(def->backend.passtCommandline->args);
+ g_free(def->backend.passtCommandline);
+ }
virDomainNetTeamingInfoFree(def->teaming);
g_free(def->virtPortProfile);
g_free(def->script);
@@ -9772,6 +9776,7 @@ virDomainNetBackendParseXML(xmlNodePtr node,
{
g_autofree char *tap = virXMLPropString(node, "tap");
g_autofree char *vhost = virXMLPropString(node, "vhost");
+ xmlNodePtr cur;
/* In the case of NET_TYPE_USER, backend type can be unspecified
* (i.e. VIR_DOMAIN_NET_BACKEND_DEFAULT) and that means 'use
@@ -9808,6 +9813,38 @@ virDomainNetBackendParseXML(xmlNodePtr node,
def->backend.vhost = virFileSanitizePath(vhost);
}
+ /* Parse passt namespace commandline */
+ cur = node->children;
+ while (cur != NULL) {
+ if (cur->type == XML_ELEMENT_NODE) {
+ if (cur->ns &&
+ STREQ((const char *)cur->ns->href, "http://libvirt.org/schemas/domain/passt/1.0") &&
+ STREQ((const char *)cur->name, "commandline")) {
+ xmlNodePtr arg_node = cur->children;
+ g_autoptr(GPtrArray) args = g_ptr_array_new();
+
+ while (arg_node != NULL) {
+ if (arg_node->type == XML_ELEMENT_NODE &&
+ arg_node->ns &&
+ STREQ((const char *)arg_node->ns->href, "http://libvirt.org/schemas/domain/passt/1.0") &&
+ STREQ((const char *)arg_node->name, "arg")) {
+ g_autofree char *value = virXMLPropString(arg_node, "value");
+ if (value)
+ g_ptr_array_add(args, g_strdup(value));
+ }
+ arg_node = arg_node->next;
+ }
+
+ if (args->len > 0) {
+ def->backend.passtCommandline = g_new0(virDomainNetBackendPasstCommandline, 1);
+ g_ptr_array_add(args, NULL); /* NULL-terminate */
+ def->backend.passtCommandline->args = (char **)g_ptr_array_steal(args, NULL);
+ }
+ }
+ }
+ cur = cur->next;
+ }
+
return 0;
}
@@ -20802,6 +20839,7 @@ virDomainNetBackendIsEqual(virDomainNetBackend *src,
STRNEQ_NULLABLE(src->logFile, dst->logFile)) {
return false;
}
+
return true;
}
@@ -24921,11 +24959,29 @@ virDomainNetTeamingInfoFormat(virDomainNetTeamingInfo *teaming,
}
+static void
+virDomainNetBackendPasstCommandLineFormat(virBuffer *buf,
+ virDomainNetBackend *backend)
+{
+ g_auto(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER;
+ g_auto(virBuffer) childBuf = VIR_BUFFER_INIT_CHILD(buf);
+ GStrv n;
+
+ if (backend->passtCommandline && backend->passtCommandline->args) {
+ for (n = backend->passtCommandline->args; n && *n; n++)
+ virBufferEscapeString(&childBuf, "<passt:arg value='%s'/>\n", *n);
+ virBufferAddLit(&attrBuf, " xmlns:passt='http://libvirt.org/schemas/domain/passt/1.0'");
+ virXMLFormatElement(buf, "passt:commandline", &attrBuf, &childBuf);
+ }
+
+}
+
static void
virDomainNetBackendFormat(virBuffer *buf,
virDomainNetBackend *backend)
{
g_auto(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER;
+ g_auto(virBuffer) childBuf = VIR_BUFFER_INIT_CHILD(buf);
if (backend->type) {
virBufferAsprintf(&attrBuf, " type='%s'",
@@ -24934,7 +24990,10 @@ virDomainNetBackendFormat(virBuffer *buf,
virBufferEscapeString(&attrBuf, " tap='%s'", backend->tap);
virBufferEscapeString(&attrBuf, " vhost='%s'", backend->vhost);
virBufferEscapeString(&attrBuf, " logFile='%s'", backend->logFile);
- virXMLFormatElement(buf, "backend", &attrBuf, NULL);
+
+ virDomainNetBackendPasstCommandLineFormat(&childBuf, backend);
+
+ virXMLFormatElement(buf, "backend", &attrBuf, &childBuf);
}
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 6997cf7c09..1f51bad546 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -1070,12 +1070,18 @@ struct _virDomainActualNetDef {
unsigned int class_id; /* class ID for bandwidth 'floor' */
};
+typedef struct _virDomainNetBackendPasstCommandline virDomainNetBackendPasstCommandline;
+struct _virDomainNetBackendPasstCommandline {
+ char **args; /* NULL-terminated array of arguments */
+};
+
struct _virDomainNetBackend {
virDomainNetBackendType type;
char *tap;
char *vhost;
/* The following are currently only valid/used when backend type='passt' */
char *logFile; /* path to logfile used by passt process */
+ virDomainNetBackendPasstCommandline *passtCommandline; /* for passt overrides */
};
struct _virDomainNetPortForwardRange {
diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng
index 183dd5db5e..e176073c6a 100644
--- a/src/conf/schemas/domaincommon.rng
+++ b/src/conf/schemas/domaincommon.rng
@@ -3908,6 +3908,9 @@
</optional>
<optional>
<element name="backend">
+ <optional>
+ <ref name="passtcmdline"/>
+ </optional>
<optional>
<attribute name="type">
<choice>
@@ -8877,6 +8880,18 @@
</attribute>
</define>
+ <define name="passtcmdline">
+ <element name="commandline" ns="http://libvirt.org/schemas/domain/passt/1.0">
+ <interleave>
+ <zeroOrMore>
+ <element name="arg">
+ <attribute name="value"/>
+ </element>
+ </zeroOrMore>
+ </interleave>
+ </element>
+ </define>
+
<define name="coalesce">
<element name="coalesce">
<interleave>
diff --git a/src/qemu/qemu_passt.c b/src/qemu/qemu_passt.c
index fcc34de384..0163553cee 100644
--- a/src/qemu/qemu_passt.c
+++ b/src/qemu/qemu_passt.c
@@ -317,6 +317,15 @@ qemuPasstStart(virDomainObj *vm,
virCommandAddArg(cmd, virBufferCurrentContent(&buf));
}
+ /* Add custom passt arguments from namespace */
+ if (net->backend.passtCommandline && net->backend.passtCommandline->args) {
+ for (i = 0; net->backend.passtCommandline->args[i]; i++) {
+ virCommandAddArg(cmd, net->backend.passtCommandline->args[i]);
+ }
+
+ /* Taint the domain when using custom passt arguments */
+ qemuDomainObjTaint(driver, vm, VIR_DOMAIN_TAINT_CUSTOM_ARGV, NULL);
+ }
if (qemuExtDeviceLogCommand(driver, vm, cmd, "passt") < 0)
return -1;
diff --git a/tests/meson.build b/tests/meson.build
index 0d76d37959..fe9013b600 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -269,6 +269,7 @@ tests += [
{ 'name': 'networkxml2xmlupdatetest' },
{ 'name': 'nodedevxml2xmltest' },
{ 'name': 'nwfilterxml2xmltest' },
+ { 'name': 'qemupassttest' },
{ 'name': 'seclabeltest' },
{ 'name': 'secretxml2xmltest' },
{ 'name': 'sockettest' },
diff --git a/tests/qemupassttest.c b/tests/qemupassttest.c
new file mode 100644
index 0000000000..84f4c1510a
--- /dev/null
+++ b/tests/qemupassttest.c
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2024 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 "testutils.h"
+#include "conf/domain_conf.h"
+#include "viralloc.h"
+#include "virstring.h"
+#include "virlog.h"
+
+#define VIR_FROM_THIS VIR_FROM_QEMU
+
+VIR_LOG_INIT("tests.qemupassttest");
+
+struct testPasstData {
+ const char *name;
+ const char *xmlfile;
+ const char * const *expectedArgs;
+ size_t nExpectedArgs;
+ bool expectCustomArgs;
+};
+
+static virDomainDef *
+testParseDomainXML(const char *xmlfile)
+{
+ g_autofree char *xmlpath = NULL;
+ g_autofree char *xmldata = NULL;
+ virDomainDef *def = NULL;
+ g_autoptr(virDomainXMLOption) xmlopt = NULL;
+
+ xmlpath = g_strdup_printf("%s/qemuxmlconfdata/%s", abs_srcdir, xmlfile);
+
+ if (virTestLoadFile(xmlpath, &xmldata) < 0)
+ return NULL;
+
+ if (!(xmlopt = virDomainXMLOptionNew(NULL, NULL, NULL, NULL, NULL, NULL)))
+ return NULL;
+
+ def = virDomainDefParseString(xmldata, xmlopt, NULL,
+ VIR_DOMAIN_DEF_PARSE_INACTIVE);
+
+ return def;
+}
+
+static int
+testPasstParseCustomArgs(const void *opaque)
+{
+ const struct testPasstData *data = opaque;
+ g_autoptr(virDomainDef) def = NULL;
+ virDomainNetDef *net = NULL;
+ size_t i;
+
+ if (!(def = testParseDomainXML(data->xmlfile))) {
+ VIR_TEST_DEBUG("Failed to parse domain XML");
+ return -1;
+ }
+
+ /* Find the interface with passt backend */
+ for (i = 0; i < def->nnets; i++) {
+ if (def->nets[i]->backend.type == VIR_DOMAIN_NET_BACKEND_PASST) {
+ net = def->nets[i];
+ break;
+ }
+ }
+
+ if (!net) {
+ VIR_TEST_DEBUG("No passt interface found in domain XML");
+ return -1;
+ }
+
+ /* Check if we have custom arguments */
+ if (data->expectCustomArgs) {
+ char **args;
+
+ if (!net->backend.passtCommandline || !net->backend.passtCommandline->args) {
+ VIR_TEST_DEBUG("Expected custom args but none found");
+ return -1;
+ }
+
+ args = net->backend.passtCommandline->args;
+
+ if (g_strv_length(args) != data->nExpectedArgs) {
+ VIR_TEST_DEBUG("Expected %zu arguments but found %u",
+ data->nExpectedArgs, g_strv_length(args));
+ return -1;
+ }
+
+ /* Verify all expected arguments are present */
+ for (i = 0; i < data->nExpectedArgs; i++) {
+ if (!g_strv_contains((const char * const *)args, data->expectedArgs[i])) {
+ VIR_TEST_DEBUG("Missing expected argument: %s", data->expectedArgs[i]);
+ return -1;
+ }
+ }
+ } else {
+ /* Should not have custom arguments */
+ if (net->backend.passtCommandline &&
+ net->backend.passtCommandline->args &&
+ *net->backend.passtCommandline->args) {
+ VIR_TEST_DEBUG("Found custom args but none expected");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int
+mymain(void)
+{
+ int ret = 0;
+
+ static const char * const customArgsExpected[] = {
+ "--debug",
+ "--verbose",
+ "--socket=/tmp/foo.socket"
+ };
+
+ struct testPasstData customArgsData = {
+ .name = "custom-args",
+ .xmlfile = "net-user-passt-custom-args.xml",
+ .expectedArgs = customArgsExpected,
+ .nExpectedArgs = G_N_ELEMENTS(customArgsExpected),
+ .expectCustomArgs = true,
+ };
+
+ struct testPasstData noCustomArgsData = {
+ .name = "no-custom-args",
+ .xmlfile = "net-user-passt.xml",
+ .expectedArgs = NULL,
+ .nExpectedArgs = 0,
+ .expectCustomArgs = false,
+ };
+
+ if (virTestRun("passt XML parsing with custom args",
+ testPasstParseCustomArgs, &customArgsData) < 0)
+ ret = -1;
+
+ if (virTestRun("passt XML parsing without custom args",
+ testPasstParseCustomArgs, &noCustomArgsData) < 0)
+ ret = -1;
+
+ return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+VIR_TEST_MAIN(mymain)
diff --git a/tests/qemuxmlconfdata/net-user-passt-custom-args.x86_64-latest.args b/tests/qemuxmlconfdata/net-user-passt-custom-args.x86_64-latest.args
new file mode 100644
index 0000000000..48d2596594
--- /dev/null
+++ b/tests/qemuxmlconfdata/net-user-passt-custom-args.x86_64-latest.args
@@ -0,0 +1,35 @@
+LC_ALL=C \
+PATH=/bin \
+HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1 \
+USER=test \
+LOGNAME=test \
+XDG_DATA_HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1/.local/share \
+XDG_CACHE_HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1/.cache \
+XDG_CONFIG_HOME=/var/lib/libvirt/qemu/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":"/var/lib/libvirt/qemu/domain--1-QEMUGuest1/master-key.aes"}' \
+-machine pc,usb=off,dump-guest-core=off,memory-backend=pc.ram,acpi=off \
+-accel tcg \
+-cpu qemu64 \
+-m size=219136k \
+-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 \
+-boot strict=on \
+-blockdev '{"driver":"host_device","filename":"/dev/HostVG/QEMUGuest1","node-name":"libvirt-1-storage","read-only":false}' \
+-device '{"driver":"ide-hd","bus":"ide.0","unit":0,"drive":"libvirt-1-storage","id":"ide0-0-0","bootindex":1}' \
+-netdev '{"type":"stream","addr":{"type":"unix","path":"/var/run/libvirt/qemu/passt/-1-QEMUGuest1-net0.socket"},"server":false,"reconnect-ms":5000,"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/qemuxmlconfdata/net-user-passt-custom-args.x86_64-latest.xml b/tests/qemuxmlconfdata/net-user-passt-custom-args.x86_64-latest.xml
new file mode 100644
index 0000000000..6718893a52
--- /dev/null
+++ b/tests/qemuxmlconfdata/net-user-passt-custom-args.x86_64-latest.xml
@@ -0,0 +1,67 @@
+<domain type='qemu'>
+ <name>QEMUGuest1</name>
+ <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
+ <memory unit='KiB'>219136</memory>
+ <currentMemory unit='KiB'>219136</currentMemory>
+ <vcpu placement='static'>1</vcpu>
+ <os>
+ <type arch='x86_64' machine='pc'>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <cpu mode='custom' match='exact' check='none'>
+ <model fallback='forbid'>qemu64</model>
+ </cpu>
+ <clock offset='utc'/>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>destroy</on_crash>
+ <devices>
+ <emulator>/usr/bin/qemu-system-x86_64</emulator>
+ <disk type='block' device='disk'>
+ <driver name='qemu' type='raw'/>
+ <source dev='/dev/HostVG/QEMUGuest1'/>
+ <target dev='hda' bus='ide'/>
+ <address type='drive' controller='0' bus='0' target='0' unit='0'/>
+ </disk>
+ <controller type='usb' index='0' model='none'/>
+ <controller type='ide' index='0'>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/>
+ </controller>
+ <controller type='pci' index='0' model='pci-root'/>
+ <interface type='user'>
+ <mac address='00:11:22:33:44:55'/>
+ <source dev='eth42'/>
+ <ip address='172.17.2.0' family='ipv4' prefix='24'/>
+ <ip address='2001:db8:ac10:fd01::feed' family='ipv6'/>
+ <portForward proto='tcp' address='2001:db8:ac10:fd01::1:10'>
+ <range start='22' to='2022'/>
+ <range start='1000' end='1050'/>
+ <range start='1020' exclude='yes'/>
+ <range start='1030' end='1040' exclude='yes'/>
+ </portForward>
+ <portForward proto='udp' address='1.2.3.4' dev='eth0'>
+ <range start='5000' end='5020' to='6000'/>
+ <range start='5010' end='5015' exclude='yes'/>
+ </portForward>
+ <portForward proto='tcp'>
+ <range start='80'/>
+ </portForward>
+ <portForward proto='tcp'>
+ <range start='443' to='344'/>
+ </portForward>
+ <model type='rtl8139'/>
+ <backend type='passt' logFile='/var/log/loglaw.blog'>
+ <passt:commandline xmlns:passt='http://libvirt.org/schemas/domain/passt/1.0'>
+ <passt:arg value='--debug'/>
+ <passt:arg value='--verbose'/>
+ <passt:arg value='--socket=/tmp/foo.socket'/>
+ </passt:commandline>
+ </backend>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
+ </interface>
+ <input type='mouse' bus='ps2'/>
+ <input type='keyboard' bus='ps2'/>
+ <audio id='1' type='none'/>
+ <memballoon model='none'/>
+ </devices>
+</domain>
diff --git a/tests/qemuxmlconfdata/net-user-passt-custom-args.xml b/tests/qemuxmlconfdata/net-user-passt-custom-args.xml
new file mode 100644
index 0000000000..a2a0f4c245
--- /dev/null
+++ b/tests/qemuxmlconfdata/net-user-passt-custom-args.xml
@@ -0,0 +1,64 @@
+<domain type='qemu'>
+ <name>QEMUGuest1</name>
+ <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
+ <memory unit='KiB'>219136</memory>
+ <currentMemory unit='KiB'>219136</currentMemory>
+ <vcpu placement='static'>1</vcpu>
+ <os>
+ <type arch='x86_64' machine='pc'>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <clock offset='utc'/>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>destroy</on_crash>
+ <devices>
+ <emulator>/usr/bin/qemu-system-x86_64</emulator>
+ <disk type='block' device='disk'>
+ <driver name='qemu' type='raw'/>
+ <source dev='/dev/HostVG/QEMUGuest1'/>
+ <target dev='hda' bus='ide'/>
+ <address type='drive' controller='0' bus='0' target='0' unit='0'/>
+ </disk>
+ <controller type='usb' index='0' model='none'/>
+ <controller type='ide' index='0'>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/>
+ </controller>
+ <controller type='pci' index='0' model='pci-root'/>
+ <interface type='user'>
+ <mac address='00:11:22:33:44:55'/>
+ <source dev='eth42'/>
+ <ip address='172.17.2.0' family='ipv4' prefix='24'/>
+ <ip address='2001:db8:ac10:fd01::feed' family='ipv6'/>
+ <portForward proto='tcp' address='2001:db8:ac10:fd01::1:10'>
+ <range start='22' to='2022'/>
+ <range start='1000' end='1050'/>
+ <range start='1020' exclude='yes'/>
+ <range start='1030' end='1040' exclude='yes'/>
+ </portForward>
+ <portForward proto='udp' address='1.2.3.4' dev='eth0'>
+ <range start='5000' end='5020' to='6000'/>
+ <range start='5010' end='5015' exclude='yes'/>
+ </portForward>
+ <portForward proto='tcp'>
+ <range start='80'/>
+ </portForward>
+ <portForward proto='tcp'>
+ <range start='443' to='344'/>
+ </portForward>
+ <model type='rtl8139'/>
+ <backend type='passt' logFile='/var/log/loglaw.blog'>
+ <passt:commandline xmlns:passt='http://libvirt.org/schemas/domain/passt/1.0'>
+ <passt:arg value='--debug'/>
+ <passt:arg value='--verbose'/>
+ <passt:arg value='--socket=/tmp/foo.socket'/>
+ </passt:commandline>
+ </backend>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
+ </interface>
+ <input type='mouse' bus='ps2'/>
+ <input type='keyboard' bus='ps2'/>
+ <audio id='1' type='none'/>
+ <memballoon model='none'/>
+ </devices>
+</domain>
diff --git a/tests/qemuxmlconftest.c b/tests/qemuxmlconftest.c
index 9fba984290..839ae49ed4 100644
--- a/tests/qemuxmlconftest.c
+++ b/tests/qemuxmlconftest.c
@@ -1805,6 +1805,7 @@ mymain(void)
DO_TEST_CAPS_LATEST("net-user-addr");
DO_TEST_CAPS_LATEST("net-user-passt");
DO_TEST_CAPS_VER("net-user-passt", "7.2.0");
+ DO_TEST_CAPS_LATEST("net-user-passt-custom-args");
DO_TEST_CAPS_LATEST_PARSE_ERROR("net-user-slirp-portforward");
DO_TEST_CAPS_LATEST("net-vhostuser-passt");
DO_TEST_CAPS_LATEST_PARSE_ERROR("net-vhostuser-passt-no-shmem");
--
2.50.0
6 days, 6 hours
[PATCH 0/2] Fix problems with local pkg-config files.
by Daniel P. Berrangé
Daniel P. Berrangé (2):
src: add missing libvirt-admin.pc.in for local usage
src: fix package name in local pkg-config files
src/libvirt-admin.pc.in | 19 +++++++++++++++++++
src/libvirt-lxc.pc.in | 2 +-
src/libvirt-qemu.pc.in | 2 +-
src/meson.build | 1 +
4 files changed, 22 insertions(+), 2 deletions(-)
create mode 100644 src/libvirt-admin.pc.in
--
2.49.0
6 days, 8 hours
[PATCH] virdevmapper: Always use device name for finding targets
by Bhavin
From: Bhavin Gandhi <bhavin192(a)geeksocket.in>
DM_TABLE_DEPS expects a device name in dm_ioctl.name. In one of the
cases, full path of the device was getting returned causing the ioctl
call to fail with `ENXIO (No such device or address)`.
Also rename the function and variable names to better reflect that we
are dealing with DM device names and not paths.
This got introduced in 22494556542c676d1b9e7f1c1f2ea13ac17e1e3e
Resolves: https://gitlab.com/libvirt/libvirt/-/issues/790
Signed-off-by: Bhavin Gandhi <bhavin192(a)geeksocket.in>
---
src/util/virdevmapper.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/src/util/virdevmapper.c b/src/util/virdevmapper.c
index d0eae671ab..42c86d89cf 100644
--- a/src/util/virdevmapper.c
+++ b/src/util/virdevmapper.c
@@ -164,7 +164,7 @@ virDMOpen(void)
static char *
-virDMSanitizepath(const char *path)
+virDMGetDeviceName(const char *path)
{
g_autofree char *dmDirPath = NULL;
struct dirent *ent = NULL;
@@ -205,7 +205,7 @@ virDMSanitizepath(const char *path)
if (stat(tmp, &sb[1]) == 0 &&
sb[0].st_rdev == sb[1].st_rdev) {
- return g_steal_pointer(&tmp);
+ return g_strdup(ent->d_name);
}
}
@@ -219,7 +219,7 @@ virDevMapperGetTargetsImpl(int controlFD,
GSList **devPaths,
unsigned int ttl)
{
- g_autofree char *sanitizedPath = NULL;
+ g_autofree char *deviceName = NULL;
g_autofree char *buf = NULL;
struct dm_ioctl dm = { 0 };
struct dm_target_deps *deps = NULL;
@@ -233,10 +233,10 @@ virDevMapperGetTargetsImpl(int controlFD,
if (!virIsDevMapperDevice(path))
return 0;
- if (!(sanitizedPath = virDMSanitizepath(path)))
+ if (!(deviceName = virDMGetDeviceName(path)))
return 0;
- if (virStrcpy(dm.name, sanitizedPath, DM_NAME_LEN) < 0) {
+ if (virStrcpy(dm.name, deviceName, DM_NAME_LEN) < 0) {
virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
_("Resolved device mapper name too long"));
return -1;
--
2.49.0
6 days, 8 hours
[PATCH v2] qemu: passt: add support for custom command line arguments
by Enrique Llorente
This adds support for custom command line arguments for the passt
backend, similar to qemu:commandline. The feature allows passing
additional arguments to the passt process for development and testing
purposes.
The implementation:
- Adds a passt XML namespace for custom arguments
- Properly taints the domain when custom args are used
- Includes comprehensive test coverage
- Adds documentation for the new feature
Usage example:
<interface type='user'>
<backend type='passt' xmlns:passt='http://libvirt.org/schemas/domain/passt/1.0'>
<passt:commandline>
<passt:arg value='--debug'/>
<passt:arg value='--verbose'/>
</passt:commandline>
</backend>
</interface>
Signed-off-by: Enrique Llorente <ellorent(a)redhat.com>
---
docs/formatdomain.rst | 40 +++++++++
src/conf/domain_conf.c | 84 ++++++++++++++++++-
src/conf/domain_conf.h | 6 ++
src/qemu/qemu_passt.c | 9 ++
...-user-passt-custom-args.x86_64-latest.args | 39 +++++++++
...t-user-passt-custom-args.x86_64-latest.xml | 57 +++++++++++++
.../net-user-passt-custom-args.xml | 52 ++++++++++++
tests/qemuxmlconftest.c | 1 +
8 files changed, 287 insertions(+), 1 deletion(-)
create mode 100644 tests/qemuxmlconfdata/net-user-passt-custom-args.x86_64-latest.args
create mode 100644 tests/qemuxmlconfdata/net-user-passt-custom-args.x86_64-latest.xml
create mode 100644 tests/qemuxmlconfdata/net-user-passt-custom-args.xml
diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst
index 9a2f065590..59899aaa4a 100644
--- a/docs/formatdomain.rst
+++ b/docs/formatdomain.rst
@@ -5464,6 +5464,46 @@ ports **with the exception of some subset**.
</devices>
...
+Custom passt commandline arguments
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+:since:`Since 11.7.0` For development and testing purposes, it is
+sometimes useful to be able to pass additional command-line arguments
+directly to the passt process. This can be accomplished using a
+special passt namespace in the domain XML that is similar to the qemu
+commandline namespace:
+
+::
+
+ ...
+ <devices>
+ ...
+ <interface type='user'>
+ <backend type='passt' xmlns:passt='http://libvirt.org/schemas/domain/passt/1.0'>
+ <passt:commandline>
+ <passt:arg value='--debug'/>
+ <passt:arg value='--verbose'/>
+ </passt:commandline>
+ </backend>
+ </interface>
+ </devices>
+ ...
+
+Any arguments provided using this method will be appended to the passt
+command line, and will therefore override any default options set by
+libvirt in the case of conflicts.
+
+**This feature is intended for development and testing only.**
+Arguments are used as specified in the XML, and no validation beyond
+basic libvirt XML schema validation is performed. It is expected that
+this feature will primarily be used for testing new passt options, so
+that they can be evaluated for inclusion in libvirt's schema in a
+future release. It should not be used as a substitute for configuring
+passt with the proper libvirt XML elements and attributes. **When a
+domain uses this feature, it will be marked as "tainted" in the logs and
+the ``virsh dominfo`` output will include "custom-argv" in the list
+of reasons.**
+
Generic ethernet connection
^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 1e24e41a48..9d39bb39bd 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -2918,6 +2918,10 @@ virDomainNetDefFree(virDomainNetDef *def)
g_free(def->backend.tap);
g_free(def->backend.vhost);
g_free(def->backend.logFile);
+ if (def->backend.passtCommandline) {
+ g_strfreev(def->backend.passtCommandline->args);
+ g_free(def->backend.passtCommandline);
+ }
virDomainNetTeamingInfoFree(def->teaming);
g_free(def->virtPortProfile);
g_free(def->script);
@@ -9772,6 +9776,7 @@ virDomainNetBackendParseXML(xmlNodePtr node,
{
g_autofree char *tap = virXMLPropString(node, "tap");
g_autofree char *vhost = virXMLPropString(node, "vhost");
+ xmlNodePtr cur;
/* In the case of NET_TYPE_USER, backend type can be unspecified
* (i.e. VIR_DOMAIN_NET_BACKEND_DEFAULT) and that means 'use
@@ -9808,6 +9813,40 @@ virDomainNetBackendParseXML(xmlNodePtr node,
def->backend.vhost = virFileSanitizePath(vhost);
}
+ /* Parse passt namespace commandline */
+ cur = node->children;
+ while (cur != NULL) {
+ if (cur->type == XML_ELEMENT_NODE) {
+ if (cur->ns &&
+ STREQ((const char *)cur->ns->href, "http://libvirt.org/schemas/domain/passt/1.0") &&
+ STREQ((const char *)cur->name, "commandline")) {
+ xmlNodePtr arg_node = cur->children;
+ GPtrArray *args = g_ptr_array_new();
+
+ while (arg_node != NULL) {
+ if (arg_node->type == XML_ELEMENT_NODE &&
+ arg_node->ns &&
+ STREQ((const char *)arg_node->ns->href, "http://libvirt.org/schemas/domain/passt/1.0") &&
+ STREQ((const char *)arg_node->name, "arg")) {
+ g_autofree char *value = virXMLPropString(arg_node, "value");
+ if (value)
+ g_ptr_array_add(args, g_strdup(value));
+ }
+ arg_node = arg_node->next;
+ }
+
+ if (args->len > 0) {
+ def->backend.passtCommandline = g_new0(virDomainNetBackendPasstCommandline, 1);
+ g_ptr_array_add(args, NULL); /* NULL-terminate */
+ def->backend.passtCommandline->args = (char **)g_ptr_array_free(args, FALSE);
+ } else {
+ g_ptr_array_unref(args);
+ }
+ }
+ }
+ cur = cur->next;
+ }
+
return 0;
}
@@ -20802,6 +20841,33 @@ virDomainNetBackendIsEqual(virDomainNetBackend *src,
STRNEQ_NULLABLE(src->logFile, dst->logFile)) {
return false;
}
+
+ /* Compare passt commandline */
+ if ((src->passtCommandline && dst->passtCommandline) ||
+ (!src->passtCommandline && !dst->passtCommandline)) {
+ if (src->passtCommandline && dst->passtCommandline) {
+ /* Compare argument arrays */
+ char **srcArgs = src->passtCommandline->args;
+ char **dstArgs = dst->passtCommandline->args;
+
+ if (!srcArgs && !dstArgs) {
+ /* Both are NULL, continue */
+ } else if (!srcArgs || !dstArgs) {
+ return false; /* One is NULL, other is not */
+ } else {
+ /* Compare each argument */
+ int i = 0;
+ while (srcArgs[i] || dstArgs[i]) {
+ if (!srcArgs[i] || !dstArgs[i] || STRNEQ(srcArgs[i], dstArgs[i]))
+ return false;
+ i++;
+ }
+ }
+ }
+ } else {
+ return false;
+ }
+
return true;
}
@@ -24926,6 +24992,7 @@ virDomainNetBackendFormat(virBuffer *buf,
virDomainNetBackend *backend)
{
g_auto(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER;
+ g_auto(virBuffer) childBuf = VIR_BUFFER_INITIALIZER;
if (backend->type) {
virBufferAsprintf(&attrBuf, " type='%s'",
@@ -24934,7 +25001,22 @@ virDomainNetBackendFormat(virBuffer *buf,
virBufferEscapeString(&attrBuf, " tap='%s'", backend->tap);
virBufferEscapeString(&attrBuf, " vhost='%s'", backend->vhost);
virBufferEscapeString(&attrBuf, " logFile='%s'", backend->logFile);
- virXMLFormatElement(buf, "backend", &attrBuf, NULL);
+
+ /* Format passt commandline with namespace */
+ if (backend->passtCommandline && backend->passtCommandline->args) {
+ virBufferAddLit(&childBuf, "<passt:commandline xmlns:passt='http://libvirt.org/schemas/domain/passt/1.0'>\n");
+ virBufferAdjustIndent(&childBuf, 2);
+
+ for (int i = 0; backend->passtCommandline->args[i]; i++) {
+ virBufferAsprintf(&childBuf, "<passt:arg value='%s'/>\n",
+ backend->passtCommandline->args[i]);
+ }
+
+ virBufferAdjustIndent(&childBuf, -2);
+ virBufferAddLit(&childBuf, "</passt:commandline>\n");
+ }
+
+ virXMLFormatElement(buf, "backend", &attrBuf, &childBuf);
}
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 6997cf7c09..1f51bad546 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -1070,12 +1070,18 @@ struct _virDomainActualNetDef {
unsigned int class_id; /* class ID for bandwidth 'floor' */
};
+typedef struct _virDomainNetBackendPasstCommandline virDomainNetBackendPasstCommandline;
+struct _virDomainNetBackendPasstCommandline {
+ char **args; /* NULL-terminated array of arguments */
+};
+
struct _virDomainNetBackend {
virDomainNetBackendType type;
char *tap;
char *vhost;
/* The following are currently only valid/used when backend type='passt' */
char *logFile; /* path to logfile used by passt process */
+ virDomainNetBackendPasstCommandline *passtCommandline; /* for passt overrides */
};
struct _virDomainNetPortForwardRange {
diff --git a/src/qemu/qemu_passt.c b/src/qemu/qemu_passt.c
index fcc34de384..e64ccfa5aa 100644
--- a/src/qemu/qemu_passt.c
+++ b/src/qemu/qemu_passt.c
@@ -317,6 +317,15 @@ qemuPasstStart(virDomainObj *vm,
virCommandAddArg(cmd, virBufferCurrentContent(&buf));
}
+ /* Add custom passt arguments from namespace */
+ if (net->backend.passtCommandline && net->backend.passtCommandline->args) {
+ for (i = 0; net->backend.passtCommandline->args[i]; i++) {
+ virCommandAddArg(cmd, net->backend.passtCommandline->args[i]);
+ }
+
+ /* Taint the domain when using custom passt arguments */
+ qemuDomainObjTaint(driver, vm, VIR_DOMAIN_TAINT_CUSTOM_ARGV, NULL);
+ }
if (qemuExtDeviceLogCommand(driver, vm, cmd, "passt") < 0)
return -1;
diff --git a/tests/qemuxmlconfdata/net-user-passt-custom-args.x86_64-latest.args b/tests/qemuxmlconfdata/net-user-passt-custom-args.x86_64-latest.args
new file mode 100644
index 0000000000..1715d4253c
--- /dev/null
+++ b/tests/qemuxmlconfdata/net-user-passt-custom-args.x86_64-latest.args
@@ -0,0 +1,39 @@
+LC_ALL=C \
+PATH=/bin \
+HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1 \
+USER=test \
+LOGNAME=test \
+XDG_DATA_HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1/.local/share \
+XDG_CACHE_HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1/.cache \
+XDG_CONFIG_HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest1/.config \
+/usr/bin/qemu-system-i386 \
+-name guest=QEMUGuest1,debug-threads=on \
+-S \
+-object '{"qom-type":"secret","id":"masterKey0","format":"raw","file":"/var/lib/libvirt/qemu/domain--1-QEMUGuest1/master-key.aes"}' \
+-machine pc,usb=off,dump-guest-core=off,memory-backend=pc.ram,acpi=off \
+-accel tcg \
+-cpu qemu32 \
+-m size=219136k \
+-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 \
+-boot strict=on \
+-device '{"driver":"piix3-usb-uhci","id":"usb","bus":"pci.0","addr":"0x1.0x2"}' \
+-drive file=/dev/HostVG/QEMUGuest1,format=raw,if=none,id=drive-ide0-0-0 \
+-device '{"driver":"ide-hd","bus":"ide.0","unit":0,"drive":"drive-ide0-0-0","id":"ide0-0-0","bootindex":1}' \
+-netdev '{"type":"stream","addr":{"type":"unix","path":"/var/run/libvirt/qemu/passt/-1-QEMUGuest1-net0.socket"},"server":false,"reconnect-ms":5000,"id":"hostnet0"}' \
+-device '{"driver":"rtl8139","netdev":"hostnet0","id":"net0","mac":"52:54:00:12:34:56","bus":"pci.0","addr":"0x3"}' \
+-vnc 127.0.0.1:0 \
+-device '{"driver":"cirrus-vga","id":"video0","bus":"pci.0","addr":"0x2"}' \
+-device '{"driver":"virtio-balloon-pci","id":"balloon0","bus":"pci.0","addr":"0x4"}' \
+-audiodev '{"id":"audio1","driver":"none"}' \
+-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \
+-msg timestamp=on
diff --git a/tests/qemuxmlconfdata/net-user-passt-custom-args.x86_64-latest.xml b/tests/qemuxmlconfdata/net-user-passt-custom-args.x86_64-latest.xml
new file mode 100644
index 0000000000..310ff6c39e
--- /dev/null
+++ b/tests/qemuxmlconfdata/net-user-passt-custom-args.x86_64-latest.xml
@@ -0,0 +1,57 @@
+<domain type='qemu'>
+ <name>QEMUGuest1</name>
+ <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
+ <memory unit='KiB'>219136</memory>
+ <currentMemory unit='KiB'>219136</currentMemory>
+ <vcpu placement='static'>1</vcpu>
+ <os>
+ <type arch='i686' machine='pc'>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <cpu mode='custom' match='exact' check='none'>
+ <model fallback='forbid'>qemu32</model>
+ </cpu>
+ <clock offset='utc'/>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>destroy</on_crash>
+ <devices>
+ <emulator>/usr/bin/qemu-system-i386</emulator>
+ <disk type='block' device='disk'>
+ <driver name='qemu' type='raw'/>
+ <source dev='/dev/HostVG/QEMUGuest1'/>
+ <target dev='hda' bus='ide'/>
+ <address type='drive' controller='0' bus='0' target='0' unit='0'/>
+ </disk>
+ <controller type='usb' index='0' model='piix3-uhci'>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/>
+ </controller>
+ <controller type='ide' index='0'>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/>
+ </controller>
+ <controller type='pci' index='0' model='pci-root'/>
+ <interface type='user'>
+ <mac address='52:54:00:12:34:56'/>
+ <model type='rtl8139'/>
+ <backend type='passt' logFile='/tmp/passt.log'>
+ <passt:commandline xmlns:passt='http://libvirt.org/schemas/domain/passt/1.0'>
+ <passt:arg value='--debug'/>
+ <passt:arg value='--verbose'/>
+ <passt:arg value='--no-dhcp'/>
+ </passt:commandline>
+ </backend>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
+ </interface>
+ <input type='mouse' bus='ps2'/>
+ <input type='keyboard' bus='ps2'/>
+ <graphics type='vnc' port='-1' autoport='yes'/>
+ <video>
+ <model type='cirrus' vram='16384' heads='1'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
+ </video>
+ <memballoon model='virtio'>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
+ </memballoon>
+ <audio id='1' type='none'/>
+ </devices>
+</domain>
diff --git a/tests/qemuxmlconfdata/net-user-passt-custom-args.xml b/tests/qemuxmlconfdata/net-user-passt-custom-args.xml
new file mode 100644
index 0000000000..15eca80b5e
--- /dev/null
+++ b/tests/qemuxmlconfdata/net-user-passt-custom-args.xml
@@ -0,0 +1,52 @@
+<domain type='qemu'>
+ <name>QEMUGuest1</name>
+ <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
+ <memory unit='KiB'>219136</memory>
+ <currentMemory unit='KiB'>219136</currentMemory>
+ <vcpu placement='static'>1</vcpu>
+ <os>
+ <type arch='i686' machine='pc'>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <clock offset='utc'/>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>destroy</on_crash>
+ <devices>
+ <emulator>/usr/bin/qemu-system-i386</emulator>
+ <disk type='block' device='disk'>
+ <driver name='qemu' type='raw'/>
+ <source dev='/dev/HostVG/QEMUGuest1'/>
+ <target dev='hda' bus='ide'/>
+ <address type='drive' controller='0' bus='0' target='0' unit='0'/>
+ </disk>
+ <controller type='usb' index='0' model='piix3-uhci'>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/>
+ </controller>
+ <controller type='ide' index='0'>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/>
+ </controller>
+ <controller type='pci' index='0' model='pci-root'/>
+ <interface type='user'>
+ <mac address='52:54:00:12:34:56'/>
+ <backend type='passt' logFile='/tmp/passt.log'>
+ <passt:commandline xmlns:passt='http://libvirt.org/schemas/domain/passt/1.0'>
+ <passt:arg value='--debug'/>
+ <passt:arg value='--verbose'/>
+ <passt:arg value='--no-dhcp'/>
+ </passt:commandline>
+ </backend>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
+ </interface>
+ <input type='mouse' bus='ps2'/>
+ <input type='keyboard' bus='ps2'/>
+ <graphics type='vnc' port='-1' autoport='yes'/>
+ <video>
+ <model type='cirrus' vram='16384' heads='1'/>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
+ </video>
+ <memballoon model='virtio'>
+ <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
+ </memballoon>
+ </devices>
+</domain>
\ No newline at end of file
diff --git a/tests/qemuxmlconftest.c b/tests/qemuxmlconftest.c
index 9fba984290..839ae49ed4 100644
--- a/tests/qemuxmlconftest.c
+++ b/tests/qemuxmlconftest.c
@@ -1805,6 +1805,7 @@ mymain(void)
DO_TEST_CAPS_LATEST("net-user-addr");
DO_TEST_CAPS_LATEST("net-user-passt");
DO_TEST_CAPS_VER("net-user-passt", "7.2.0");
+ DO_TEST_CAPS_LATEST("net-user-passt-custom-args");
DO_TEST_CAPS_LATEST_PARSE_ERROR("net-user-slirp-portforward");
DO_TEST_CAPS_LATEST("net-vhostuser-passt");
DO_TEST_CAPS_LATEST_PARSE_ERROR("net-vhostuser-passt-no-shmem");
--
2.50.0
6 days, 10 hours
[PATCH v3 00/21] LIBVIRT: X86: TDX support
by Zhenzhong Duan
Hi,
This series brings libvirt the x86 TDX support.
* What's TDX?
TDX stands for Trust Domain Extensions which isolates VMs from
the virtual-machine manager (VMM)/hypervisor and any other software on
the platform.
This patchset extends libvirt to support TDX, with which one can start a TDX
guest from high level rather than running qemu directly.
* Misc
As QEMU use a software emulated way to reset guest which isn't supported by TDX
guest for security reason. We simulate reboot for TDX guest by kill and create a
new one in FakeReboot framework.
Complete code can be found at [1].
* Test
Tested with upstream qemu v10.0.0-1724-gf9a3def17b
shutdown/reboot/reset with virsh
shutdown/reboot trigger in guest
shutdown with on_poweroff=destroy/restart
reboot with on_reboot=destroy/restart
* Patch organization
- patch 1-4: Some preparing work
- patch 5-6: Support query of TDX capabilities
- patch 7-13: Add TDX type to launchsecurity framework
- patch 14-19: Add reboot/reset support to TDX guest
- patch 20: Add conf test dump/cases for '+inteltdx' variant
- patch 21: Add docs
TODO:
- add reconnect logic in virsh command
[1] https://github.com/intel/libvirt-tdx/commits/tdx_for_upstream_v3
Thanks
Zhenzhong
Changelog:
v3:
- fix a hiden failure in qemuBuildTDXQGSCommandLine() (Peter Krempa)
- avoid the use of the ternary operator (Peter Krempa)
- add capability test dump before capability introduced (Peter Krempa)
- change tests version number from 11.0.0 to 10.1.0 (Peter Krempa)
v2:
- add capability and xmlconf test (Peter Krempa)
v1:
- s/virQEMUCapsKVMSupportsSecureGuestINTEL/virQEMUCapsKVMSupportsSecureGuestTDX (Daniel)
- make policy element optional and expose to QEMU directly (Daniel)
- s/qemuProcessSecFakeReboot/qemuProcessFakeRebootViaRecreate (Daniel)
- simplify QGS element schema by supporting only UNIX socket (Daniel)
- add new events VIR_DOMAIN_EVENT_[STOPPED|STARTED] for control plane (Daniel)
- s/quoteGenerationService/quoteGenerationSocket as QEMU
- add virsh reset support
rfcv4:
- add a check to tools/virt-host-validate-qemu.c (Daniel)
- remove check of q35 (Daniel)
- model 'SocktetAddress' QAPI in xml schema (Daniel)
- s/Quote-Generation-Service/quoteGenerationService/ (Daniel)
- define bits in tdx->policy and add validating logic (Daniel)
- presume QEMU choose split kernel irqchip for TDX guest by default (Daniel)
- utilize existing FakeReboot framework to do reboot for TDX guest (Daniel)
- drop patch11 'conf: Add support to keep same domid for hard reboot' (Daniel)
- add test in tests/ to validate parsing and formatting logic (Daniel)
- add doc in docs/formatdomain.rst (Daniel)
- add R-B
rfcv3:
- Change to generate qemu cmdline with -bios
- drop firmware auto match as -bios is used
- add a hard reboot method to reboot TDX guest
rfcv3: https://www.mail-archive.com/devel@lists.libvirt.org/msg00385.html
rfcv2:
- give up using qmp cmd and check TDX directly on host for TDX capabilities.
- use launchsecurity framework to support TDX
- use <os>.<loader> for general loader
- add auto firmware match feature for TDX
A example TDVF fimware description file 70-edk2-x86_64-tdx.json:
{
"description": "UEFI firmware for x86_64, supporting Intel TDX",
"interface-types": [
"uefi"
],
"mapping": {
"device": "generic",
"filename": "/usr/share/OVMF/OVMF_CODE-tdx.fd"
},
"targets": [
{
"architecture": "x86_64",
"machines": [
"pc-q35-*"
]
}
],
"features": [
"intel-tdx",
"verbose-dynamic"
],
"tags": [
]
}
rfcv2: https://www.mail-archive.com/libvir-list@redhat.com/msg219378.html
Zhenzhong Duan (21):
tools: Secure guest check for Intel in virt-host-validate
qemu: Check if INTEL Trust Domain Extention support is enabled
qemucapabilitiesdata: Document '+inteltdx' variant
qemucapabilitiestest: Add data for the qemu-10.1.0 dev cycle on x86_64
for the '+inteltdx' variant
qemu: Add TDX capability
conf: Expose TDX feature in domain capabilities
conf: Add tdx as launch security type
conf: Validate TDX launchSecurity element
mrConfigId/mrOwner/mrOwnerConfig
qemu: Add command line and validation for TDX type
conf: Expose TDX type in domain launch security capability
qemu: Force special parameters enabled for TDX guest
conf: Add Intel TDX Quote Generation Service(QGS) support
qemu: Add command line for TDX Quote Generation Service(QGS)
qemu: Add FakeReboot support for TDX guest
qemu: Support reboot command in guest
qemu: Avoid duplicate FakeReboot for secure guest
qemu: Send event VIR_DOMAIN_EVENT_[STOPPED|STARTED] during recreation
qemu: Bypass sending VIR_DOMAIN_EVENT_RESUMED event when TD VM reboot
qemu: Support domain reset command for TDX guest
qemuxmlconftest: Add latest version of 'launch-security-tdx*' test
data
docs: domain: Add documentation for Intel TDX guest
docs/formatdomain.rst | 63 +
docs/formatdomaincaps.rst | 1 +
examples/c/misc/event-test.c | 6 +
include/libvirt/libvirt-domain.h | 2 +
src/conf/domain_capabilities.c | 1 +
src/conf/domain_capabilities.h | 1 +
src/conf/domain_conf.c | 82 +
src/conf/domain_conf.h | 21 +
src/conf/domain_validate.c | 11 +
src/conf/schemas/domaincaps.rng | 9 +
src/conf/schemas/domaincommon.rng | 41 +
src/conf/virconftypes.h | 2 +
src/qemu/qemu_capabilities.c | 38 +-
src/qemu/qemu_capabilities.h | 1 +
src/qemu/qemu_cgroup.c | 1 +
src/qemu/qemu_command.c | 43 +
src/qemu/qemu_domain.h | 1 +
src/qemu/qemu_driver.c | 11 +-
src/qemu/qemu_firmware.c | 1 +
src/qemu/qemu_monitor.c | 34 +-
src/qemu/qemu_monitor.h | 2 +-
src/qemu/qemu_monitor_json.c | 6 +-
src/qemu/qemu_namespace.c | 1 +
src/qemu/qemu_process.c | 104 +-
src/qemu/qemu_process.h | 2 +
src/qemu/qemu_validate.c | 45 +
src/security/security_dac.c | 2 +
.../qemu_10.1.0-q35.x86_64+inteltdx.xml | 783 +
.../qemu_10.1.0-tcg.x86_64+inteltdx.xml | 1830 +
.../qemu_10.1.0.x86_64+inteltdx.xml | 783 +
tests/domaincapsmock.c | 3 +-
tests/qemucapabilitiesdata/README.rst | 5 +
.../caps_10.1.0_x86_64+inteltdx.replies | 44552 ++++++++++++++++
.../caps_10.1.0_x86_64+inteltdx.xml | 3585 ++
.../caps.x86_64+inteltdx.xml | 29 +
...h-security-tdx.x86_64-latest+inteltdx.args | 44 +
...ch-security-tdx.x86_64-latest+inteltdx.xml | 74 +
tests/qemuxmlconfdata/launch-security-tdx.xml | 27 +
tests/qemuxmlconftest.c | 3 +
tools/virsh-domain-event.c | 6 +-
tools/virt-host-validate-common.c | 31 +-
tools/virt-host-validate-common.h | 1 +
42 files changed, 52273 insertions(+), 15 deletions(-)
create mode 100644 tests/domaincapsdata/qemu_10.1.0-q35.x86_64+inteltdx.xml
create mode 100644 tests/domaincapsdata/qemu_10.1.0-tcg.x86_64+inteltdx.xml
create mode 100644 tests/domaincapsdata/qemu_10.1.0.x86_64+inteltdx.xml
create mode 100644 tests/qemucapabilitiesdata/caps_10.1.0_x86_64+inteltdx.replies
create mode 100644 tests/qemucapabilitiesdata/caps_10.1.0_x86_64+inteltdx.xml
create mode 100644 tests/qemucaps2xmloutdata/caps.x86_64+inteltdx.xml
create mode 100644 tests/qemuxmlconfdata/launch-security-tdx.x86_64-latest+inteltdx.args
create mode 100644 tests/qemuxmlconfdata/launch-security-tdx.x86_64-latest+inteltdx.xml
create mode 100644 tests/qemuxmlconfdata/launch-security-tdx.xml
--
2.34.1
6 days, 17 hours
[RFC] x86 Host CPU features detection by MSRs
by Hector Cao
Hello,
This mail is a Request for Comment.
On recent Intel CPUs, some of the CPU features (mostly vmx-* subfeatures)
are listed and controlled
via the MSRs (Model Specific Registers) instead of the traditional CPUID
instruction method.
Right now, libvirt reads the MSR's values via /dev/cpu/*/cpu populated by
the msr kernel module.
src/cpu/cpu_x86.c:
...
/* This is best effort since there might be no way to read the MSR
* when we are not running as root. */
for (i = 0; i < nmsrs; i++) {
if (virHostCPUGetMSR(msrs[i], &msr) == 0) {
virCPUx86DataItem item = {
.type = VIR_CPU_X86_DATA_MSR,
.data.msr = {
...
There are 2 potential issues:
1) As stated in the source code comment above, MSR values read might fail
when libvirt is not run as root.
2) MSR values read might fail if the MSR kernel module is not loaded, this
is still the case in some of the Linux distros.
The issue 2) has been brought up in Ubuntu/Debian :
https://salsa.debian.org/libvirt-team/libvirt/-/merge_requests/271
but the feedback was to try to fix the issue in the libvirt source code
itself. Indeed, libvirt already loads some kernel modules it depends on.
For example the NBD kernel module in src/util/virtfile.c
...
static bool
virFileNBDLoadDriver(void)
{
if (virKModIsProhibited(NBD_DRIVER)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Failed to load nbd module: administratively
prohibited"));
return false;
} else {
g_autofree char *errbuf = NULL;
if ((errbuf = virKModLoad(NBD_DRIVER))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Failed to load nbd module"));
...
Andrea Bolognani's proposal of an approach that could work:
1. try using the unprivileged KVM API is available (or maybe the
necessary information is exposed via QMP too?) like how QEMU does;
2. if that fails, try using the privileged /dev API;
3. if that fails too, load the msr module and try again;
I would like to have some feedback from the libvirt community about if that
is worth tackling these issues and the right way to do that.
If we can end up with an agreement in this mail thread, I can work on the
solution implementation and send it in a separate submission.
Best regards,
Hector
1 week
[PATCH] nwfilter: Avoid firewall hole during VM startup by checking rule presence
by Dion Bosschieter
Upon VM bootstrapping (start,restore,incoming migration)
iptablesCreateBaseChainsFW is called and unconditionally deletes and
reinserts top-level firewall chain jumps (e.g. INPUT, FORWARD rules).
This briefly opens a hole in the firewall, allowing packets through
until the insertions complete.
This commit ensures that the base chains are only created once per layer
(IPV4/IPV6) and checks whether the expected rules already exist using
`iptables -C`. If they do, no delete/insert operations are performed.
This eliminates the short window where packets could bypass filters during
VM lifecycle operations.
Signed-off-by: Dion Bosschieter <dionbosschieter(a)gmail.com>
---
src/nwfilter/nwfilter_ebiptables_driver.c | 79 ++++++++++++++---------
1 file changed, 47 insertions(+), 32 deletions(-)
diff --git a/src/nwfilter/nwfilter_ebiptables_driver.c b/src/nwfilter/nwfilter_ebiptables_driver.c
index 067df6e612..42a0133159 100644
--- a/src/nwfilter/nwfilter_ebiptables_driver.c
+++ b/src/nwfilter/nwfilter_ebiptables_driver.c
@@ -131,6 +131,14 @@ static char chainprefixes_host_temp[3] = {
0
};
+typedef struct {
+ const char *chain;
+ const char *position;
+ const char *targetChain;
+} iptablesBaseChainFW;
+
+static bool baseChainFWDefined[VIR_FIREWALL_LAYER_LAST] = { false };
+
static int
printVar(virNWFilterVarCombIter *vars,
char *buf, int bufsize,
@@ -403,38 +411,45 @@ static void
iptablesCreateBaseChainsFW(virFirewall *fw,
virFirewallLayer layer)
{
- virFirewallAddCmdFull(fw, layer,
- true, NULL, NULL,
- "-N", VIRT_IN_CHAIN, NULL);
- virFirewallAddCmdFull(fw, layer,
- true, NULL, NULL,
- "-N", VIRT_OUT_CHAIN, NULL);
- virFirewallAddCmdFull(fw, layer,
- true, NULL, NULL,
- "-N", VIRT_IN_POST_CHAIN, NULL);
- virFirewallAddCmdFull(fw, layer,
- true, NULL, NULL,
- "-N", HOST_IN_CHAIN, NULL);
- virFirewallAddCmdFull(fw, layer,
- true, NULL, NULL,
- "-D", "FORWARD", "-j", VIRT_IN_CHAIN, NULL);
- virFirewallAddCmdFull(fw, layer,
- true, NULL, NULL,
- "-D", "FORWARD", "-j", VIRT_OUT_CHAIN, NULL);
- virFirewallAddCmdFull(fw, layer,
- true, NULL, NULL,
- "-D", "FORWARD", "-j", VIRT_IN_POST_CHAIN, NULL);
- virFirewallAddCmdFull(fw, layer,
- true, NULL, NULL,
- "-D", "INPUT", "-j", HOST_IN_CHAIN, NULL);
- virFirewallAddCmd(fw, layer,
- "-I", "FORWARD", "1", "-j", VIRT_IN_CHAIN, NULL);
- virFirewallAddCmd(fw, layer,
- "-I", "FORWARD", "2", "-j", VIRT_OUT_CHAIN, NULL);
- virFirewallAddCmd(fw, layer,
- "-I", "FORWARD", "3", "-j", VIRT_IN_POST_CHAIN, NULL);
- virFirewallAddCmd(fw, layer,
- "-I", "INPUT", "1", "-j", HOST_IN_CHAIN, NULL);
+ iptablesBaseChainFW fw_chains[] = {
+ {"FORWARD", "1", VIRT_IN_CHAIN},
+ {"FORWARD", "2", VIRT_OUT_CHAIN},
+ {"FORWARD", "3", VIRT_IN_POST_CHAIN},
+ {"INPUT", "1", HOST_IN_CHAIN},
+ };
+ size_t i;
+
+ // iptablesCreateBaseChainsFW already ran once for this layer,
+ // we don't have to recreate the base chains on every firewall update
+ if (baseChainFWDefined[layer])
+ return;
+
+ // set defined state so we skip the following logic next run
+ baseChainFWDefined[layer] = true;
+
+ virFirewallStartTransaction(fw, 0);
+
+ for (i = 0; i < G_N_ELEMENTS(fw_chains); i++)
+ virFirewallAddCmd(fw, layer,
+ "-C", fw_chains[i].chain,
+ "-j", fw_chains[i].targetChain, NULL);
+
+ if (virFirewallApply(fw) == 0)
+ // rules already in place
+ return;
+
+ for (i = 0; i < G_N_ELEMENTS(fw_chains); i++) {
+ virFirewallAddCmdFull(fw, layer,
+ true, NULL, NULL,
+ "-N", fw_chains[i].targetChain, NULL);
+ virFirewallAddCmdFull(fw, layer,
+ true, NULL, NULL,
+ "-D", fw_chains[i].chain, "-j",
+ fw_chains[i].targetChain, NULL);
+ virFirewallAddCmd(fw, layer,
+ "-I", fw_chains[i].chain, fw_chains[i].position,
+ "-j", fw_chains[i].targetChain, NULL);
+ }
}
--
2.39.3 (Apple Git-146)
1 week, 1 day