From 7afbbab2ccada16c87e1095e85184bb21c028012 Mon Sep 17 00:00:00 2001 Message-Id: <7afbbab2ccada16c87e1095e85184bb21c028012.1619091487.git.sbrivio@redhat.com> From: Stefano Brivio Date: Wed, 21 Apr 2021 19:29:31 +0200 Subject: [PATCH] conf: Introduce support for UNIX domain socket as qemu netdev back-end Since qemu [TODO], named UNIX domain sockets can be used instead of TCP to establish a virtual network between VMs. The obvious difference compared with TCP is that we need pass a path instead of address and port. Signed-off-by: Stefano Brivio --- SPDX-FileCopyrightText: 2020-2021 Red Hat GmbH SPDX-License-Identifier: AGPL-3.0-or-later docs/formatdomain.rst | 41 +++++++++++++++++++------ docs/schemas/domaincommon.rng | 50 +++++++++++++++++++++++------- src/conf/domain_conf.c | 58 +++++++++++++++++++++++++++-------- src/conf/domain_conf.h | 13 +++++--- src/qemu/qemu_command.c | 46 ++++++++++++++++++--------- src/qemu/qemu_hotplug.c | 8 +++-- 6 files changed, 160 insertions(+), 56 deletions(-) diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index 1b9b2216111c..87c3c956fa23 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -5010,18 +5010,20 @@ must be from the multicast address block. ... -:anchor:`` +:anchor:`` -TCP tunnel -^^^^^^^^^^ +TCP or UNIX domain socket tunnel +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A stream-oriented client/server architecture provides a virtual network. One VM +provides the server end of the network, all other VMS are configured as clients. +All network traffic is routed between the VMs via the server. This mode is also +available to unprivileged users. There is no default DNS or DHCP support and no +outgoing network access. To provide outgoing network access, one of the VMs +should have a 2nd NIC which is connected to one of the first 4 network types and +do the appropriate routing. -A TCP client/server architecture provides a virtual network. One VM provides the -server end of the network, all other VMS are configured as clients. All network -traffic is routed between the VMs via the server. This mode is also available to -unprivileged users. There is no default DNS or DHCP support and no outgoing -network access. To provide outgoing network access, one of the VMs should have a -2nd NIC which is connected to one of the first 4 network types and do the -appropriate routing. +TCP endpoints can be specified as follows: :: @@ -5039,6 +5041,25 @@ appropriate routing. ... +Named UNIX domain sockets can be specified as follows: +:since:`Since 7.3.0, qemu` + +:: + + ... + + + + + + ... + + + + + + ... + :anchor:`` UDP unicast tunnel diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index a2e5c50c1d77..7c0a90ba199b 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -3161,10 +3161,7 @@ - - mcast - client - + mcast @@ -3179,6 +3176,30 @@ + + + client + + + + + + + + + + + + + + + + + + + + + udp @@ -3210,14 +3231,21 @@ - - - + + + + + + + + + + + + + - - - - + diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index f8a462fb3b99..8c6a5d4f974e 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -2562,8 +2562,9 @@ virDomainNetDefFree(virDomainNetDef *def) case VIR_DOMAIN_NET_TYPE_CLIENT: case VIR_DOMAIN_NET_TYPE_MCAST: case VIR_DOMAIN_NET_TYPE_UDP: - g_free(def->data.socket.address); - g_free(def->data.socket.localaddr); + g_free(def->data.socket.net.address); + g_free(def->data.socket.net.localaddr); + g_free(def->data.socket.path); break; case VIR_DOMAIN_NET_TYPE_NETWORK: @@ -10555,6 +10556,7 @@ virDomainNetDefParseXML(virDomainXMLOption *xmlopt, g_autofree char *downscript = NULL; g_autofree char *address = NULL; g_autofree char *port = NULL; + g_autofree char *path = NULL; g_autofree char *localaddr = NULL; g_autofree char *localport = NULL; g_autofree char *model = NULL; @@ -10699,7 +10701,7 @@ virDomainNetDefParseXML(virDomainXMLOption *xmlopt, " "), type); goto error; } - } else if (!address && + } else if (!address && !path && (def->type == VIR_DOMAIN_NET_TYPE_SERVER || def->type == VIR_DOMAIN_NET_TYPE_CLIENT || def->type == VIR_DOMAIN_NET_TYPE_MCAST || @@ -10707,6 +10709,7 @@ virDomainNetDefParseXML(virDomainXMLOption *xmlopt, virXMLNodeNameEqual(cur, "source")) { address = virXMLPropString(cur, "address"); port = virXMLPropString(cur, "port"); + path = virXMLPropString(cur, "path"); if (!localaddr && def->type == VIR_DOMAIN_NET_TYPE_UDP) { xmlNodePtr tmpnode = ctxt->node; ctxt->node = cur; @@ -10950,6 +10953,27 @@ virDomainNetDefParseXML(virDomainXMLOption *xmlopt, case VIR_DOMAIN_NET_TYPE_CLIENT: case VIR_DOMAIN_NET_TYPE_SERVER: + if (path != NULL) { + if (port != NULL || address != NULL || + localport != NULL || localaddr != NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _(" 'path' attribute " + "for socket interface cannot be specified " + "together with other attributes")); + goto error; + } + def->data.socket.path = g_steal_pointer(&path); + break; + } + + if (port == NULL) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Neither 'port' nor 'path' attribute " + "specified with socket interface")); + goto error; + } + + G_GNUC_FALLTHROUGH; case VIR_DOMAIN_NET_TYPE_MCAST: case VIR_DOMAIN_NET_TYPE_UDP: if (port == NULL) { @@ -10958,7 +10982,7 @@ virDomainNetDefParseXML(virDomainXMLOption *xmlopt, "specified with socket interface")); goto error; } - if (virStrToLong_i(port, NULL, 10, &def->data.socket.port) < 0) { + if (virStrToLong_i(port, NULL, 10, &def->data.socket.net.port) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Cannot parse 'port' attribute " "with socket interface")); @@ -10975,7 +10999,7 @@ virDomainNetDefParseXML(virDomainXMLOption *xmlopt, goto error; } } else { - def->data.socket.address = g_steal_pointer(&address); + def->data.socket.net.address = g_steal_pointer(&address); } if (def->type != VIR_DOMAIN_NET_TYPE_UDP) @@ -10987,7 +11011,8 @@ virDomainNetDefParseXML(virDomainXMLOption *xmlopt, "specified with socket interface")); goto error; } - if (virStrToLong_i(localport, NULL, 10, &def->data.socket.localport) < 0) { + if (virStrToLong_i(localport, NULL, 10, + &def->data.socket.net.localport) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Cannot parse 'port' attribute " "with socket interface")); @@ -11000,7 +11025,7 @@ virDomainNetDefParseXML(virDomainXMLOption *xmlopt, "specified with socket interface")); goto error; } else { - def->data.socket.localaddr = g_steal_pointer(&localaddr); + def->data.socket.net.localaddr = g_steal_pointer(&localaddr); } break; @@ -25940,15 +25965,22 @@ virDomainNetDefFormat(virBuffer *buf, case VIR_DOMAIN_NET_TYPE_SERVER: case VIR_DOMAIN_NET_TYPE_CLIENT: + if (def->data.socket.path) { + virBufferAsprintf(buf, "data.socket.path); + sourceLines++; + break; + } + G_GNUC_FALLTHROUGH; case VIR_DOMAIN_NET_TYPE_MCAST: case VIR_DOMAIN_NET_TYPE_UDP: - if (def->data.socket.address) { + if (def->data.socket.net.address) { virBufferAsprintf(buf, "data.socket.address, - def->data.socket.port); + def->data.socket.net.address, + def->data.socket.net.port); } else { virBufferAsprintf(buf, "data.socket.port); + def->data.socket.net.port); } sourceLines++; @@ -25960,8 +25992,8 @@ virDomainNetDefFormat(virBuffer *buf, virBufferAdjustIndent(buf, 2); virBufferAsprintf(buf, "\n", - def->data.socket.localaddr, - def->data.socket.localport); + def->data.socket.net.localaddr, + def->data.socket.net.localport); virBufferAdjustIndent(buf, -2); break; diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 7688f17b18cd..054b08330c8c 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1055,11 +1055,14 @@ struct _virDomainNetDef { virDomainNetTeamingInfo *teaming; union { virDomainChrSourceDef *vhostuser; - struct { - char *address; - int port; - char *localaddr; - int localport; + union { + struct { + char *address; + int port; + char *localaddr; + int localport; + } net; + char *path; } socket; /* any of NET_CLIENT or NET_SERVER or NET_MCAST */ struct { char *name; diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 2ceff155124e..dbef58f37879 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -3713,37 +3713,55 @@ qemuBuildHostNetStr(virDomainNetDef *net, break; case VIR_DOMAIN_NET_TYPE_CLIENT: - if (virJSONValueObjectCreate(&netprops, "s:type", "socket", NULL) < 0 || - virJSONValueObjectAppendStringPrintf(netprops, "connect", "%s:%d", - net->data.socket.address, - net->data.socket.port) < 0) + if (virJSONValueObjectCreate(&netprops, "s:type", "socket", NULL) < 0) + return NULL; + + if (net->data.socket.path != NULL) { + if (virJSONValueObjectAppendStringPrintf(netprops, "connect", "%s", + net->data.socket.path) < 0) + return NULL; + break; + } + + if (virJSONValueObjectAppendStringPrintf(netprops, "connect", "%s:%d", + net->data.socket.net.address, + net->data.socket.net.port) < 0) return NULL; break; case VIR_DOMAIN_NET_TYPE_SERVER: - if (virJSONValueObjectCreate(&netprops, "s:type", "socket", NULL) < 0 || - virJSONValueObjectAppendStringPrintf(netprops, "listen", "%s:%d", - NULLSTR_EMPTY(net->data.socket.address), - net->data.socket.port) < 0) + if (virJSONValueObjectCreate(&netprops, "s:type", "socket", NULL) < 0) + return NULL; + + if (net->data.socket.path != NULL) { + if (virJSONValueObjectAppendStringPrintf(netprops, "listen", "%s", + net->data.socket.path) < 0) + return NULL; + break; + } + + if (virJSONValueObjectAppendStringPrintf(netprops, "listen", "%s:%d", + NULLSTR_EMPTY(net->data.socket.net.address), + net->data.socket.net.port) < 0) return NULL; break; case VIR_DOMAIN_NET_TYPE_MCAST: if (virJSONValueObjectCreate(&netprops, "s:type", "socket", NULL) < 0 || virJSONValueObjectAppendStringPrintf(netprops, "mcast", "%s:%d", - net->data.socket.address, - net->data.socket.port) < 0) + net->data.socket.net.address, + net->data.socket.net.port) < 0) return NULL; break; case VIR_DOMAIN_NET_TYPE_UDP: if (virJSONValueObjectCreate(&netprops, "s:type", "socket", NULL) < 0 || virJSONValueObjectAppendStringPrintf(netprops, "udp", "%s:%d", - net->data.socket.address, - net->data.socket.port) < 0 || + net->data.socket.net.address, + net->data.socket.net.port) < 0 || virJSONValueObjectAppendStringPrintf(netprops, "localaddr", "%s:%d", - net->data.socket.localaddr, - net->data.socket.localport) < 0) + net->data.socket.net.localaddr, + net->data.socket.net.localport) < 0) return NULL; break; diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 4344edc75b80..69ef7abd0ee2 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -3741,9 +3741,11 @@ qemuDomainChangeNet(virQEMUDriver *driver, case VIR_DOMAIN_NET_TYPE_CLIENT: case VIR_DOMAIN_NET_TYPE_MCAST: case VIR_DOMAIN_NET_TYPE_UDP: - if (STRNEQ_NULLABLE(olddev->data.socket.address, - newdev->data.socket.address) || - olddev->data.socket.port != newdev->data.socket.port) { + if (STRNEQ_NULLABLE(olddev->data.socket.path, + newdev->data.socket.path) || + STRNEQ_NULLABLE(olddev->data.socket.net.address, + newdev->data.socket.net.address) || + olddev->data.socket.net.port != newdev->data.socket.net.port) { needReconnect = true; } break; -- 2.29.2