aboutgitcodebugslistschat
diff options
context:
space:
mode:
authorStefano Brivio <sbrivio@redhat.com>2021-03-20 07:22:09 +0100
committerStefano Brivio <sbrivio@redhat.com>2021-03-21 00:08:42 +0100
commite653f9b3ed1b60037e3bc661d53b3f9407243fc2 (patch)
tree5e1b2f06fc451f5c474d17728e434ee1934209a5
parent35015ce72ea47208e207ef6c5e75d550fa095862 (diff)
downloadpasst-e653f9b3ed1b60037e3bc661d53b3f9407243fc2.tar
passt-e653f9b3ed1b60037e3bc661d53b3f9407243fc2.tar.gz
passt-e653f9b3ed1b60037e3bc661d53b3f9407243fc2.tar.bz2
passt-e653f9b3ed1b60037e3bc661d53b3f9407243fc2.tar.lz
passt-e653f9b3ed1b60037e3bc661d53b3f9407243fc2.tar.xz
passt-e653f9b3ed1b60037e3bc661d53b3f9407243fc2.tar.zst
passt-e653f9b3ed1b60037e3bc661d53b3f9407243fc2.zip
passt: Add libvirt patch for qemu UNIX socket domain back-end
...and mention it in the README. While at it, remove useless escaping in the README, and fix indentation in the syslog message with the qemu command line example. Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
-rw-r--r--README.md15
-rw-r--r--libvirt/0001-conf-Introduce-support-for-UNIX-domain-socket-as-qem.patch423
-rw-r--r--passt.c2
3 files changed, 438 insertions, 2 deletions
diff --git a/README.md b/README.md
index 48f4f6d..8ae54ec 100644
--- a/README.md
+++ b/README.md
@@ -5,6 +5,8 @@ and native Layer-4 sockets (TCP, UDP, ICMP/ICMPv6 echo) on a host. It doesn't
require any capabilities or privileges, and it can be used as a simple
replacement for Slirp.
+![overview diagram](/builds/passt_overview.png)
+
- [General idea](#general-idea)
- [Non-functional Targets](#non-functional-targets)
- [Interfaces and Environment](#interfaces-and-environment)
@@ -50,7 +52,7 @@ solution comes with a number of downsides:
_passt_ implements a thinner layer between guest and host, that only implements
what's strictly needed to pretend processes are running locally. A further, full
TCP/IP stack is not necessarily needed. Some sort of TCP adaptation is needed,
-however, as this layer runs without the `CAP\_NET\_RAW` capability: we can't
+however, as this layer runs without the `CAP_NET_RAW` capability: we can't
create raw IP sockets on the pod, and therefore need to map packets at Layer-2
to Layer-4 sockets offered by the host kernel.
@@ -163,6 +165,17 @@ before _passt_ starts.
qemu-system-x86_64 ... -net socket,connect=/tmp/passt.socket -net nic,model=virtio
+* alternatively, you can use libvirt, with [this patch](https://passt.top/passt/tree/libvirt/0001-conf-Introduce-support-for-UNIX-domain-socket-as-qem.patch),
+ to start qemu (with the patch mentioned above), with this kind of network
+ interface configuration:
+
+ <interface type='client'>
+ <mac address='52:54:00:02:6b:60'/>
+ <source path='/tmp/passt.socket'/>
+ <model type='virtio'/>
+ <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
+ </interface>
+
* and that's it, you should now have TCP connections, UDP, and ICMP/ICMPv6
echo working from/to the guest for IPv4 and IPv6
diff --git a/libvirt/0001-conf-Introduce-support-for-UNIX-domain-socket-as-qem.patch b/libvirt/0001-conf-Introduce-support-for-UNIX-domain-socket-as-qem.patch
new file mode 100644
index 0000000..c0e3337
--- /dev/null
+++ b/libvirt/0001-conf-Introduce-support-for-UNIX-domain-socket-as-qem.patch
@@ -0,0 +1,423 @@
+From 62e95cb4b5708eca7860139dffef22e65b29514c Mon Sep 17 00:00:00 2001
+From: Stefano Brivio <sbrivio@redhat.com>
+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 <sbrivio@redhat.com>
+---
+ 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.
+ </devices>
+ ...
+
+-:anchor:`<a id="elementsNICSTCP"/>`
++:anchor:`<a id="elementsNICSStream"/>`
+
+-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.
+ </devices>
+ ...
+
++Named UNIX domain sockets can be specified as follows:
++:since:`Since 7.2.0, qemu`
++
++::
++
++ ...
++ <devices>
++ <interface type='server'>
++ <mac address='52:54:00:22:c9:42'/>
++ <source path='/tmp/qemu.socket'/>
++ </interface>
++ ...
++ <interface type='client'>
++ <mac address='52:54:00:8b:c9:51'/>
++ <source path='/tmp/qemu.socket'/>
++ </interface>
++ </devices>
++ ...
++
+ :anchor:`<a id="elementsNICSUDP"/>`
+
+ 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 @@
+ </group>
+ <group>
+ <attribute name="type">
+- <choice>
+- <value>mcast</value>
+- <value>client</value>
+- </choice>
++ <value>mcast</value>
+ </attribute>
+ <interleave>
+ <element name="source">
+@@ -3143,6 +3140,30 @@
+ <ref name="interface-options"/>
+ </interleave>
+ </group>
++ <group>
++ <attribute name="type">
++ <value>client</value>
++ </attribute>
++ <interleave>
++ <element name="source">
++ <choice>
++ <group>
++ <attribute name="address">
++ <ref name="ipv4Addr"/>
++ </attribute>
++ <attribute name="port">
++ <ref name="PortNumber"/>
++ </attribute>
++ </group>
++ <attribute name="path">
++ <ref name="absFilePath"/>
++ </attribute>
++ </choice>
++ <empty/>
++ </element>
++ <ref name="interface-options"/>
++ </interleave>
++ </group>
+ <group>
+ <attribute name="type">
+ <value>udp</value>
+@@ -3174,14 +3195,21 @@
+ </attribute>
+ <interleave>
+ <element name="source">
+- <optional>
+- <attribute name="address">
+- <ref name="ipv4Addr"/>
++ <choice>
++ <group>
++ <optional>
++ <attribute name="address">
++ <ref name="ipv4Addr"/>
++ </attribute>
++ </optional>
++ <attribute name="port">
++ <ref name="PortNumber"/>
++ </attribute>
++ </group>
++ <attribute name="path">
++ <ref name="absFilePath"/>
+ </attribute>
+- </optional>
+- <attribute name="port">
+- <ref name="PortNumber"/>
+- </attribute>
++ </choice>
+ <empty/>
+ </element>
+ <ref name="interface-options"/>
+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,
+ " <interface type='%s'>"), 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",
++ _("<source> '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 <source> '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 <source> '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 <local> '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, "<source path='%s'",
++ def->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, "<source address='%s' port='%d'",
+- def->data.socket.address,
+- def->data.socket.port);
++ def->data.socket.net.address,
++ def->data.socket.net.port);
+ } else {
+ virBufferAsprintf(buf, "<source port='%d'",
+- def->data.socket.port);
++ def->data.socket.net.port);
+ }
+ sourceLines++;
+
+@@ -26239,8 +26271,8 @@ virDomainNetDefFormat(virBufferPtr buf,
+ virBufferAdjustIndent(buf, 2);
+
+ virBufferAsprintf(buf, "<local address='%s' port='%d'/>\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
+
diff --git a/passt.c b/passt.c
index 51b76c9..8bc7098 100644
--- a/passt.c
+++ b/passt.c
@@ -591,7 +591,7 @@ listen:
info("or directly qemu, patched with:");
info(" qemu/0001-net-Allow-also-UNIX-domain-sockets-to-be-used-as-net.patch");
info("as follows:");
- info("kvm ... -net socket,connect="
+ info(" kvm ... -net socket,connect="
UNIX_SOCK_PATH " -net nic,model=virtio");
c.fd_unix = accept(fd_unix, NULL, NULL);