From 62e95cb4b5708eca7860139dffef22e65b29514c Mon Sep 17 00:00:00 2001 From: Stefano Brivio Date: Sat, 20 Mar 2021 07:16:15 +0100 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 --- 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 9392c80113..b5b642e91a 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -4995,18 +4995,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: :: @@ -5024,6 +5026,25 @@ appropriate routing. ... +Named UNIX domain sockets can be specified as follows: +:since:`Since 7.2.0, qemu` + +:: + + ... + + + + + + ... + + + + + + ... + :anchor:`` UDP unicast tunnel diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 1dbfc68f18..350d6969c0 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -3125,10 +3125,7 @@ - - mcast - client - + mcast @@ -3143,6 +3140,30 @@ + + + client + + + + + + + + + + + + + + + + + + + + + udp @@ -3174,14 +3195,21 @@ - - - + + + + + + + + + + + + + - - - - + diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 7671050134..55543c47ce 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -2569,8 +2569,9 @@ virDomainNetDefFree(virDomainNetDefPtr 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: @@ -10792,6 +10793,7 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr 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; @@ -10935,7 +10937,7 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr 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 || @@ -10943,6 +10945,7 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr 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; @@ -11186,6 +11189,27 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr 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) { @@ -11194,7 +11218,7 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr 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")); @@ -11211,7 +11235,7 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr 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) @@ -11223,7 +11247,8 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr 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")); @@ -11236,7 +11261,7 @@ virDomainNetDefParseXML(virDomainXMLOptionPtr 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; @@ -26219,15 +26244,22 @@ virDomainNetDefFormat(virBufferPtr 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++; @@ -26239,8 +26271,8 @@ virDomainNetDefFormat(virBufferPtr 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 87bc7e8625..3cc0842eed 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1042,11 +1042,14 @@ struct _virDomainNetDef { virDomainNetTeamingInfoPtr teaming; union { virDomainChrSourceDefPtr 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 5717f7b98d..c1687e582e 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -3639,37 +3639,55 @@ qemuBuildHostNetStr(virDomainNetDefPtr 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 a66354426d..8ca86f9c53 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -3752,9 +3752,11 @@ qemuDomainChangeNet(virQEMUDriverPtr 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.28.0