[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#1032968: marked as done (unblock: passt/0.0~git20230309.7c7625d-1)



Your message dated Thu, 23 Mar 2023 10:36:30 +0100
with message-id <86016c0e-4e7e-f82f-d206-0c32544105b6@debian.org>
and subject line Re: Bug#1032968: unblock: passt/0.0~git20230309.7c7625d-1
has caused the Debian Bug report #1032968,
regarding unblock: passt/0.0~git20230309.7c7625d-1
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact owner@bugs.debian.org
immediately.)


-- 
1032968: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1032968
Debian Bug Tracking System
Contact owner@bugs.debian.org with problems
--- Begin Message ---
Package: release.debian.org
Severity: normal
User: release.debian.org@packages.debian.org
Usertags: unblock
X-Debbugs-Cc: passt@packages.debian.org
Control: affects -1 + src:passt

Please unblock package passt

[ Reason ]
Sorry, I think I misinterpreted the freeze policy (I'm a new
maintainer), I thought the hard freeze would block migrations of
uploads done *after* the start of the freeze, and not affect
migrations already in the migration delay phase, so I didn't plan
accordingly.

Now I see that the upload is stuck in unstable (at least according
to the tracker).

The update is actually a new upstream version consisting entirely
of bugfixes, some of them particularly critical.

[ Impact ]
Most substantial impacts:

- with containers ("pasta" mode of user-mode networking), inbound
  TCP segmentation might fail entirely, and lead to periodic spikes
  in CPU utilisation

- build is not reproducible on armhf

- full slirp4netns(1) compatibility not granted

The rest is listed in the changelog.

[ Tests ]
I ran the upstream test suite against the packaged version on a
Debian testing (Bookworm) x86_64 system.

[ Risks ]
The code changes are rather trivial and explained in minute details
in the upstream git log.

If needed, I can reduce them further to the bare essential, but
this is a new package and actually the version in unstable received
*more* testing than any partial patch I might come up with.

It's not a key package, slirp4netns and libslirp are partial
alternatives.

[ Checklist ]
  [x] all changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in testing

[ Other info ]

unblock passt/0.0~git20230309.7c7625d-1
diff -Nru passt-0.0~git20230227.c538ee8/conf.c passt-0.0~git20230309.7c7625d/conf.c
--- passt-0.0~git20230227.c538ee8/conf.c	2023-02-27 18:56:37.000000000 +0100
+++ passt-0.0~git20230309.7c7625d/conf.c	2023-03-09 03:44:21.000000000 +0100
@@ -23,8 +23,10 @@
 #include <limits.h>
 #include <grp.h>
 #include <pwd.h>
+#include <signal.h>
 #include <stdlib.h>
 #include <stdint.h>
+#include <stdio.h>
 #include <stdbool.h>
 #include <unistd.h>
 #include <syslog.h>
@@ -182,6 +184,7 @@
 	bool exclude_only = true, bound_one = false;
 	uint8_t exclude[PORT_BITMAP_SIZE] = { 0 };
 	sa_family_t af = AF_UNSPEC;
+	int ret;
 
 	if (!strcmp(optarg, "none")) {
 		if (fwd->mode)
@@ -216,11 +219,18 @@
 
 		for (i = 0; i < PORT_EPHEMERAL_MIN; i++) {
 			if (optname == 't') {
-				if (!tcp_sock_init(c, AF_UNSPEC, NULL, NULL, i))
+				ret = tcp_sock_init(c, AF_UNSPEC, NULL, NULL,
+						    i);
+				if (ret == -ENFILE || ret == -EMFILE)
+					goto enfile;
+				if (!ret)
 					bound_one = true;
 			} else if (optname == 'u') {
-				if (!udp_sock_init(c, 0, AF_UNSPEC, NULL, NULL,
-						   i))
+				ret = udp_sock_init(c, 0, AF_UNSPEC, NULL, NULL,
+						    i);
+				if (ret == -ENFILE || ret == -EMFILE)
+					goto enfile;
+				if (!ret)
 					bound_one = true;
 			}
 		}
@@ -301,10 +311,16 @@
 			bitmap_set(fwd->map, i);
 
 			if (optname == 't') {
-				if (!tcp_sock_init(c, af, addr, ifname, i))
+				ret = tcp_sock_init(c, af, addr, ifname, i);
+				if (ret == -ENFILE || ret == -EMFILE)
+					goto enfile;
+				if (!ret)
 					bound_one = true;
 			} else if (optname == 'u') {
-				if (!udp_sock_init(c, 0, af, addr, ifname, i))
+				ret = udp_sock_init(c, 0, af, addr, ifname, i);
+				if (ret == -ENFILE || ret == -EMFILE)
+					goto enfile;
+				if (!ret)
 					bound_one = true;
 			} else {
 				/* No way to check in advance for -T and -U */
@@ -356,10 +372,16 @@
 			fwd->delta[i] = mapped_range.first - orig_range.first;
 
 			if (optname == 't') {
-				if (!tcp_sock_init(c, af, addr, ifname, i))
+				ret = tcp_sock_init(c, af, addr, ifname, i);
+				if (ret == -ENFILE || ret == -EMFILE)
+					goto enfile;
+				if (!ret)
 					bound_one = true;
 			} else if (optname == 'u') {
-				if (!udp_sock_init(c, 0, af, addr, ifname, i))
+				ret = udp_sock_init(c, 0, af, addr, ifname, i);
+				if (ret == -ENFILE || ret == -EMFILE)
+					goto enfile;
+				if (!ret)
 					bound_one = true;
 			} else {
 				/* No way to check in advance for -T and -U */
@@ -372,6 +394,8 @@
 		goto bind_fail;
 
 	return;
+enfile:
+	die("Can't open enough sockets for port specifier: %s", optarg);
 bad:
 	die("Invalid port specifier %s", optarg);
 overlap:
@@ -775,7 +799,15 @@
 	info(   "  -g, --gateway ADDR	Pass IPv4 or IPv6 address as gateway");
 	info(   "    default: gateway from interface with default route");
 	info(   "  -i, --interface NAME	Interface for addresses and routes");
-	info(   "    default: interface with first default route");
+	info(   "    default: from --outbound-if4 and --outbound-if6, if any");
+	info(   "             otherwise interface with first default route");
+	info(   "  -o, --outbound ADDR	Bind to address as outbound source");
+	info(   "    can be specified zero to two times (for IPv4 and IPv6)");
+	info(   "    default: use source address from routing tables");
+	info(   "  --outbound-if4 NAME	Bind to outbound interface for IPv4");
+	info(   "    default: use interface from default route");
+	info(   "  --outbound-if6 NAME	Bind to outbound interface for IPv6");
+	info(   "    default: use interface from default route");
 	info(   "  -D, --dns ADDR	Use IPv4 or IPv6 address as DNS");
 	info(   "    can be specified multiple times");
 	info(   "    a single, empty option disables DNS information");
@@ -900,13 +932,36 @@
  */
 static void conf_print(const struct ctx *c)
 {
-	char buf4[INET_ADDRSTRLEN], ifn[IFNAMSIZ];
+	char buf4[INET_ADDRSTRLEN], buf6[INET6_ADDRSTRLEN], ifn[IFNAMSIZ];
 	int i;
 
-	if (c->ifi4)
-		info("Outbound interface (IPv4): %s", if_indextoname(c->ifi4, ifn));
-	if (c->ifi6)
-		info("Outbound interface (IPv6): %s", if_indextoname(c->ifi6, ifn));
+	info("Template interface: %s%s%s%s%s",
+	     c->ifi4 ? if_indextoname(c->ifi4, ifn) : "",
+	     c->ifi4 ? " (IPv4)" : "",
+	     (c->ifi4 && c->ifi6) ? ", " : "",
+	     c->ifi6 ? if_indextoname(c->ifi6, ifn) : "",
+	     c->ifi6 ? " (IPv6)" : "");
+
+	if (*c->ip4.ifname_out || *c->ip6.ifname_out) {
+		info("Outbound interface: %s%s%s%s%s",
+		     *c->ip4.ifname_out ? c->ip4.ifname_out : "",
+		     *c->ip4.ifname_out ? " (IPv4)" : "",
+		     (*c->ip4.ifname_out && *c->ip6.ifname_out) ? ", " : "",
+		     *c->ip6.ifname_out ? c->ip6.ifname_out : "",
+		     *c->ip6.ifname_out ? " (IPv6)" : "");
+	}
+
+	if (!IN4_IS_ADDR_UNSPECIFIED(&c->ip4.addr_out) ||
+	    !IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addr_out)) {
+		info("Outbound address: %s%s%s",
+		     IN4_IS_ADDR_UNSPECIFIED(&c->ip4.addr_out) ? "" :
+		     inet_ntop(AF_INET, &c->ip4.addr_out, buf4, sizeof(buf4)),
+		     (!IN4_IS_ADDR_UNSPECIFIED(&c->ip4.addr_out) &&
+		      !IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addr_out)) ? ", " : "",
+		     IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addr_out) ? "" :
+		     inet_ntop(AF_INET6, &c->ip6.addr_out, buf6, sizeof(buf6)));
+	}
+
 	if (c->mode == MODE_PASTA)
 		info("Namespace interface: %s", c->pasta_ifn);
 
@@ -945,8 +1000,6 @@
 	}
 
 	if (c->ifi6) {
-		char buf6[INET6_ADDRSTRLEN];
-
 		if (!c->no_ndp && !c->no_dhcpv6)
 			info("NDP/DHCPv6:");
 		else if (!c->no_ndp)
@@ -1122,6 +1175,7 @@
 		{"mac-addr",	required_argument,	NULL,		'M' },
 		{"gateway",	required_argument,	NULL,		'g' },
 		{"interface",	required_argument,	NULL,		'i' },
+		{"outbound",	required_argument,	NULL,		'o' },
 		{"dns",		required_argument,	NULL,		'D' },
 		{"search",	required_argument,	NULL,		'S' },
 		{"no-tcp",	no_argument,		&c->no_tcp,	1 },
@@ -1154,6 +1208,8 @@
 		{"runas",	required_argument,	NULL,		12 },
 		{"log-size",	required_argument,	NULL,		13 },
 		{"version",	no_argument,		NULL,		14 },
+		{"outbound-if4", required_argument,	NULL,		15 },
+		{"outbound-if6", required_argument,	NULL,		16 },
 		{ 0 },
 	};
 	struct get_bound_ports_ns_arg ns_ports_arg = { .c = c };
@@ -1163,8 +1219,8 @@
 	struct in6_addr *dns6 = c->ip6.dns;
 	struct fqdn *dnss = c->dns_search;
 	struct in_addr *dns4 = c->ip4.dns;
+	unsigned int ifi4 = 0, ifi6 = 0;
 	const char *optstring;
-	unsigned int ifi = 0;
 	int name, ret, b, i;
 	size_t logsize = 0;
 	uid_t uid;
@@ -1172,9 +1228,9 @@
 
 	if (c->mode == MODE_PASTA) {
 		c->no_dhcp_dns = c->no_dhcp_dns_search = 1;
-		optstring = "dqfel:hF:I:p:P:m:a:n:M:g:i:D:S:46t:u:T:U:";
+		optstring = "dqfel:hF:I:p:P:m:a:n:M:g:i:o:D:S:46t:u:T:U:";
 	} else {
-		optstring = "dqfel:hs:F:p:P:m:a:n:M:g:i:D:S:461t:u:";
+		optstring = "dqfel:hs:F:p:P:m:a:n:M:g:i:o:D:S:461t:u:";
 	}
 
 	c->tcp.fwd_in.mode = c->tcp.fwd_out.mode = 0;
@@ -1292,6 +1348,26 @@
 				c->mode == MODE_PASST ? "passt " : "pasta ");
 			fprintf(stdout, VERSION_BLOB);
 			exit(EXIT_SUCCESS);
+		case 15:
+			if (*c->ip4.ifname_out)
+				die("Redundant outbound interface: %s", optarg);
+
+			ret = snprintf(c->ip4.ifname_out,
+				       sizeof(c->ip4.ifname_out), "%s", optarg);
+			if (ret <= 0 || ret >= (int)sizeof(c->ip4.ifname_out))
+				die("Invalid interface name: %s", optarg);
+
+			break;
+		case 16:
+			if (*c->ip6.ifname_out)
+				die("Redundant outbound interface: %s", optarg);
+
+			ret = snprintf(c->ip6.ifname_out,
+				       sizeof(c->ip6.ifname_out), "%s", optarg);
+			if (ret <= 0 || ret >= (int)sizeof(c->ip6.ifname_out))
+				die("Invalid interface name: %s", optarg);
+
+			break;
 		case 'd':
 			if (c->debug)
 				die("Multiple --debug options given");
@@ -1305,13 +1381,13 @@
 			if (logfile)
 				die("Can't log to both file and stderr");
 
-			if (c->stderr)
+			if (c->force_stderr)
 				die("Multiple --stderr options given");
 
-			c->stderr = 1;
+			c->force_stderr = 1;
 			break;
 		case 'l':
-			if (c->stderr)
+			if (c->force_stderr)
 				die("Can't log to both stderr and file");
 
 			if (logfile)
@@ -1456,13 +1532,33 @@
 			die("Invalid gateway address: %s", optarg);
 			break;
 		case 'i':
-			if (ifi)
+			if (ifi4 || ifi6)
 				die("Redundant interface: %s", optarg);
 
-			if (!(ifi = if_nametoindex(optarg)))
+			if (!(ifi4 = ifi6 = if_nametoindex(optarg)))
 				die("Invalid interface name %s: %s", optarg,
 				    strerror(errno));
 			break;
+		case 'o':
+			if (IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addr_out)	  &&
+			    inet_pton(AF_INET6, optarg, &c->ip6.addr_out) &&
+			    !IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addr_out)	  &&
+			    !IN6_IS_ADDR_LOOPBACK(&c->ip6.addr_out)	  &&
+			    !IN6_IS_ADDR_V4MAPPED(&c->ip6.addr_out)	  &&
+			    !IN6_IS_ADDR_V4COMPAT(&c->ip6.addr_out)	  &&
+			    !IN6_IS_ADDR_MULTICAST(&c->ip6.addr_out))
+				break;
+
+			if (IN4_IS_ADDR_UNSPECIFIED(&c->ip4.addr_out)	 &&
+			    inet_pton(AF_INET, optarg, &c->ip4.addr_out) &&
+			    !IN4_IS_ADDR_UNSPECIFIED(&c->ip4.addr_out)	 &&
+			    !IN4_IS_ADDR_BROADCAST(&c->ip4.addr_out)	 &&
+			    !IN4_IS_ADDR_MULTICAST(&c->ip4.addr_out))
+				break;
+
+			die("Invalid or redundant outbound address: %s",
+			    optarg);
+			break;
 		case 'D':
 			if (!strcmp(optarg, "none")) {
 				if (c->no_dns)
@@ -1554,6 +1650,12 @@
 	if (*c->sock_path && c->fd_tap >= 0)
 		die("Options --socket and --fd are mutually exclusive");
 
+	if (!ifi4 && *c->ip4.ifname_out)
+		ifi4 = if_nametoindex(c->ip4.ifname_out);
+
+	if (!ifi6 && *c->ip6.ifname_out)
+		ifi6 = if_nametoindex(c->ip6.ifname_out);
+
 	conf_ugid(runas, &uid, &gid);
 
 	if (logfile) {
@@ -1563,10 +1665,12 @@
 
 	nl_sock_init(c, false);
 	if (!v6_only)
-		c->ifi4 = conf_ip4(ifi, &c->ip4, c->mac);
+		c->ifi4 = conf_ip4(ifi4, &c->ip4, c->mac);
 	if (!v4_only)
-		c->ifi6 = conf_ip6(ifi, &c->ip6, c->mac);
-	if (!c->ifi4 && !c->ifi6)
+		c->ifi6 = conf_ip6(ifi6, &c->ip6, c->mac);
+	if ((!c->ifi4 && !c->ifi6) ||
+	    (*c->ip4.ifname_out && !c->ifi4) ||
+	    (*c->ip6.ifname_out && !c->ifi6))
 		die("External interface not usable");
 
 	/* Inbound port options can be parsed now (after IPv4/IPv6 settings) */
diff -Nru passt-0.0~git20230227.c538ee8/contrib/libvirt/0001-conf-Introduce-support-for-UNIX-domain-socket-as-qem.patch passt-0.0~git20230309.7c7625d/contrib/libvirt/0001-conf-Introduce-support-for-UNIX-domain-socket-as-qem.patch
--- passt-0.0~git20230227.c538ee8/contrib/libvirt/0001-conf-Introduce-support-for-UNIX-domain-socket-as-qem.patch	2023-02-27 18:56:37.000000000 +0100
+++ passt-0.0~git20230309.7c7625d/contrib/libvirt/0001-conf-Introduce-support-for-UNIX-domain-socket-as-qem.patch	1970-01-01 01:00:00.000000000 +0100
@@ -1,427 +0,0 @@
-From 7afbbab2ccada16c87e1095e85184bb21c028012 Mon Sep 17 00:00:00 2001
-Message-Id: <7afbbab2ccada16c87e1095e85184bb21c028012.1619091487.git.sbrivio@redhat.com>
-From: Stefano Brivio <sbrivio@redhat.com>
-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 <sbrivio@redhat.com>
----
-SPDX-FileCopyrightText: 2020-2021 Red Hat GmbH <sbrivio@redhat.com>
-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.
-    </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:
- 
- ::
- 
-@@ -5039,6 +5041,25 @@ appropriate routing.
-    </devices>
-    ...
- 
-+Named UNIX domain sockets can be specified as follows:
-+:since:`Since 7.3.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 a2e5c50c1d77..7c0a90ba199b 100644
---- a/docs/schemas/domaincommon.rng
-+++ b/docs/schemas/domaincommon.rng
-@@ -3161,10 +3161,7 @@
-         </group>
-         <group>
-           <attribute name="type">
--            <choice>
--              <value>mcast</value>
--              <value>client</value>
--            </choice>
-+            <value>mcast</value>
-           </attribute>
-           <interleave>
-             <element name="source">
-@@ -3179,6 +3176,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>
-@@ -3210,14 +3231,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 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,
-                                      " <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 ||
-@@ -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",
-+                               _("<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) {
-@@ -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 <source> '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 <local> '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, "<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++;
- 
-@@ -25960,8 +25992,8 @@ virDomainNetDefFormat(virBuffer *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 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
-
diff -Nru passt-0.0~git20230227.c538ee8/contrib/podman/0001-libpod-Add-pasta-networking-mode.patch passt-0.0~git20230309.7c7625d/contrib/podman/0001-libpod-Add-pasta-networking-mode.patch
--- passt-0.0~git20230227.c538ee8/contrib/podman/0001-libpod-Add-pasta-networking-mode.patch	2023-02-27 18:56:37.000000000 +0100
+++ passt-0.0~git20230309.7c7625d/contrib/podman/0001-libpod-Add-pasta-networking-mode.patch	1970-01-01 01:00:00.000000000 +0100
@@ -1,605 +0,0 @@
-From 7294b62e15bee68f1920ea04aaab3284ce43e7ea Mon Sep 17 00:00:00 2001
-From: Stefano Brivio <sbrivio@redhat.com>
-Date: Mon, 2 May 2022 16:12:07 +0200
-Subject: [PATCH] libpod: Add pasta networking mode
-
-Conceptually equivalent to networking by means of slirp4netns(1),
-with a few practical differences:
-
-- pasta(1) forks to background once networking is configured in the
-  namespace and quits on its own once the namespace is deleted:
-  file descriptor synchronisation and PID tracking are not needed
-
-- port forwarding is configured via command line options at start-up,
-  instead of an API socket: this is taken care of right away as we're
-  about to start pasta
-
-- there's no need for further selection of port forwarding modes:
-  pasta behaves similarly to containers-rootlessport for local binds
-  (splice() instead of read()/write() pairs, without L2-L4
-  translation), and keeps the original source address for non-local
-  connections like slirp4netns does
-
-- IPv6 is not an experimental feature, and enabled by default. IPv6
-  port forwarding is supported
-
-- by default, addresses and routes are copied from the host, that is,
-  container users will see the same IP address and routes as if they
-  were in the init namespace context. The interface name is also
-  sourced from the host upstream interface with the first default
-  route in the routing table. This is also configurable as documented
-
-- by default, the host is reachable using the gateway address from
-  the container, unless the --no-map-gw option is passed
-
-- sandboxing and seccomp(2) policies cannot be disabled
-
-See https://passt.top for more details about pasta.
-
-Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
----
-SPDX-FileCopyrightText: 2021-2022 Red Hat GmbH <sbrivio@redhat.com>
-SPDX-License-Identifier: Apache-2.0
-
- docs/source/markdown/podman-create.1.md.in    |  48 +++++++-
- .../source/markdown/podman-pod-create.1.md.in |  44 +++++++
- docs/source/markdown/podman-run.1.md.in       |  49 +++++++-
- docs/source/markdown/podman.1.md              |   6 +-
- libpod/networking_common.go                   |   3 +-
- libpod/networking_linux.go                    |   3 +
- libpod/networking_pasta.go                    | 107 ++++++++++++++++++
- pkg/namespaces/namespaces.go                  |   6 +
- pkg/specgen/generate/namespaces.go            |  10 ++
- pkg/specgen/generate/pod_create.go            |   6 +
- pkg/specgen/namespaces.go                     |  16 ++-
- pkg/specgen/podspecgen.go                     |   2 +-
- 12 files changed, 286 insertions(+), 14 deletions(-)
- create mode 100644 libpod/networking_pasta.go
-
-diff --git a/docs/source/markdown/podman-create.1.md.in b/docs/source/markdown/podman-create.1.md.in
-index 742a32b5a..79fb3a3e1 100644
---- a/docs/source/markdown/podman-create.1.md.in
-+++ b/docs/source/markdown/podman-create.1.md.in
-@@ -277,10 +277,15 @@ Valid _mode_ values are:
-   - **interface_name**: Specify a name for the created network interface inside the container.
- 
-   For example to set a static ipv4 address and a static mac address, use `--network bridge:ip=10.88.0.10,mac=44:33:22:11:00:99`.
-+
- - \<network name or ID\>[:OPTIONS,...]: Connect to a user-defined network; this is the network name or ID from a network created by **[podman network create](podman-network-create.1.md)**. Using the network name implies the bridge network mode. It is possible to specify the same options described under the bridge mode above. You can use the **--network** option multiple times to specify additional networks.
-+
- - **none**: Create a network namespace for the container but do not configure network interfaces for it, thus the container has no network connectivity.
-+
- - **container:**_id_: Reuse another container's network stack.
-+
- - **host**: Do not create a network namespace, the container will use the host's network. Note: The host mode gives the container full access to local system services such as D-bus and is therefore considered insecure.
-+
- - **ns:**_path_: Path to a network namespace to join.
- - **private**: Create a new namespace for the container. This will use the **bridge** mode for rootful containers and **slirp4netns** for rootless ones.
- - **slirp4netns[:OPTIONS,...]**: use **slirp4netns**(1) to create a user network stack. This is the default for rootless containers. It is possible to specify these additional options, they can also be set with `network_cmd_options` in containers.conf:
-@@ -296,6 +301,40 @@ Valid _mode_ values are:
-   Note: Rootlesskit changes the source IP address of incoming packets to an IP address in the container network namespace, usually `10.0.2.100`. If your application requires the real source IP address, e.g. web server logs, use the slirp4netns port handler. The rootlesskit port handler is also used for rootless containers when connected to user-defined networks.
-   - **port_handler=slirp4netns**: Use the slirp4netns port forwarding, it is slower than rootlesskit but preserves the correct source IP address. This port handler cannot be used for user-defined networks.
- 
-+- **pasta[:OPTIONS,...]**: use **pasta**(1) to create a user-mode networking
-+stack. By default, IPv4 and IPv6 addresses and routes, as well as the pod
-+interface name, are copied from the host. Port forwarding preserves the original
-+source IP address. Options described in pasta(1) can be specified as
-+comma-separated arguments.
-+
-+In terms of pasta(1) options, **--config-net** is given by default, in order to
-+configure networking when the container is started. Also, **-t none** and
-+**-u none** are passed if, respectively, no TCP or UDP port forwarding from host
-+to container is configured, to disable automatic port forwarding based on bound
-+ports. Similarly, **-T none** and **-U none** are also given to disable the same
-+functionality from container to host.
-+
-+Some examples:
-+  - **pasta:--no-map-gw**: Don't allow the container to directly reach the host
-+    using the gateway address, which would normally be mapped to a loopback or
-+    link-local address.
-+  - **pasta:--mtu,1500**: Specify a 1500 bytes MTU for the _tap_ interface in
-+    the container.
-+  - **pasta:--ipv4-only,-a,10.0.2.0,-n,24,-g,10.0.2.2,--dns-forward,10.0.2.3,-m,1500,--no-ndp,--no-dhcpv6,--no-dhcp**,
-+    equivalent to default slirp4netns(1) options: disable IPv6, assign
-+    `10.0.2.0/24` to the `tap0` interface in the container, with gateway
-+    `10.0.2.3`, enable DNS forwarder reachable at `10.0.2.3`, set MTU to 1500
-+    bytes, disable NDP, DHCPv6 and DHCP support.
-+  - **pasta:--no-map-gw,-I,tap0,--ipv4-only,-a,10.0.2.0,-n,24,-g,10.0.2.2,--dns-forward,10.0.2.3,--no-ndp,--no-dhcpv6,--no-dhcp**,
-+    equivalent to default slirp4netns(1) options with Podman overrides: same as
-+    above, but leave the MTU to 65520 bytes, and don't map the gateway address
-+    from the container to a local address.
-+  - **pasta:-t,auto,-u,auto,-T,auto,-U,auto**: enable automatic port forwarding
-+    based on observed bound ports from both host and container sides
-+  - **pasta:-T,5201**: enable forwarding of TCP port 5201 from container to
-+    host, using the loopback interface instead of the tap interface for improved
-+    performance
-+
- @@option network-alias
- 
- @@option no-healthcheck
-@@ -550,8 +589,9 @@ In order for users to run rootless, there must be an entry for their username in
- 
- Rootless Podman works better if the fuse-overlayfs and slirp4netns packages are installed.
- The fuse-overlayfs package provides a userspace overlay storage driver, otherwise users need to use
--the vfs storage driver, which is diskspace expensive and does not perform well. slirp4netns is
--required for VPN, without it containers need to be run with the --network=host flag.
-+the vfs storage driver, which is diskspace expensive and does not perform well.
-+slirp4netns or pasta are required for VPN, without it containers need to be run
-+with the --network=host flag.
- 
- ## ENVIRONMENT
- 
-@@ -600,7 +640,9 @@ page.
- NOTE: Use the environment variable `TMPDIR` to change the temporary storage location of downloaded container images. Podman defaults to use `/var/tmp`.
- 
- ## SEE ALSO
--**[podman(1)](podman.1.md)**, **[podman-save(1)](podman-save.1.md)**, **[podman-ps(1)](podman-ps.1.md)**, **[podman-attach(1)](podman-attach.1.md)**, **[podman-pod-create(1)](podman-pod-create.1.md)**, **[podman-port(1)](podman-port.1.md)**, **[podman-start(1)](podman-start.1.md)**, **[podman-kill(1)](podman-kill.1.md)**, **[podman-stop(1)](podman-stop.1.md)**, **[podman-generate-systemd(1)](podman-generate-systemd.1.md)**, **[podman-rm(1)](podman-rm.1.md)**, **[subgid(5)](https://www.unix.com/man-page/linux/5/subgid)**, **[subuid(5)](https://www.unix.com/man-page/linux/5/subuid)**, **[containers.conf(5)](https://github.com/containers/common/blob/main/docs/containers.conf.5.md)**, **[systemd.unit(5)](https://www.freedesktop.org/software/systemd/man/systemd.unit.html)**, **[setsebool(8)](https://man7.org/linux/man-pages/man8/setsebool.8.html)**, **[slirp4netns(1)](https://github.com/rootless-containers/slirp4netns/blob/master/slirp4netns.1.md)**, **[fuse-overlayfs(1)](https://github.com/containers/fuse-overlayfs/blob/main/fuse-overlayfs.1.md)**, **proc(5)**, **[conmon(8)](https://github.com/containers/conmon/blob/main/docs/conmon.8.md)**, **personality(2)**
-+**[podman(1)](podman.1.md)**, **[podman-save(1)](podman-save.1.md)**, **[podman-ps(1)](podman-ps.1.md)**, **[podman-attach(1)](podman-attach.1.md)**, **[podman-pod-create(1)](podman-pod-create.1.md)**, **[podman-port(1)](podman-port.1.md)**, **[podman-start(1)](podman-start.1.md)**, **[podman-kill(1)](podman-kill.1.md)**, **[podman-stop(1)](podman-stop.1.md)**, **[podman-generate-systemd(1)](podman-generate-systemd.1.md)**, **[podman-rm(1)](podman-rm.1.md)**, **[subgid(5)](https://www.unix.com/man-page/linux/5/subgid)**, **[subuid(5)](https://www.unix.com/man-page/linux/5/subuid)**, **[containers.conf(5)](https://github.com/containers/common/blob/main/docs/containers.conf.5.md)**, **[systemd.unit(5)](https://www.freedesktop.org/software/systemd/man/systemd.unit.html)**, **[setsebool(8)](https://man7.org/linux/man-pages/man8/setsebool.8.html)**, **[slirp4netns(1)](https://github.com/rootless-containers/slirp4netns/blob/master/slirp4netns.1.md)**,
-+**[pasta(1)](https://passt.top/builds/latest/web/passt.1.html)**,
-+**[fuse-overlayfs(1)](https://github.com/containers/fuse-overlayfs/blob/main/fuse-overlayfs.1.md)**, **proc(5)**, **[conmon(8)](https://github.com/containers/conmon/blob/main/docs/conmon.8.md)**, **personality(2)**
- 
- ## HISTORY
- October 2017, converted from Docker documentation to Podman by Dan Walsh for Podman `<dwalsh@redhat.com>`
-diff --git a/docs/source/markdown/podman-pod-create.1.md.in b/docs/source/markdown/podman-pod-create.1.md.in
-index fdae1d249..104ad460b 100644
---- a/docs/source/markdown/podman-pod-create.1.md.in
-+++ b/docs/source/markdown/podman-pod-create.1.md.in
-@@ -131,10 +131,15 @@ Valid _mode_ values are:
-   - **interface_name**: Specify a name for the created network interface inside the container.
- 
-   For example to set a static ipv4 address and a static mac address, use `--network bridge:ip=10.88.0.10,mac=44:33:22:11:00:99`.
-+
- - \<network name or ID\>[:OPTIONS,...]: Connect to a user-defined network; this is the network name or ID from a network created by **[podman network create](podman-network-create.1.md)**. Using the network name implies the bridge network mode. It is possible to specify the same options described under the bridge mode above. You can use the **--network** option multiple times to specify additional networks.
-+
- - **none**: Create a network namespace for the container but do not configure network interfaces for it, thus the container has no network connectivity.
-+
- - **container:**_id_: Reuse another container's network stack.
-+
- - **host**: Do not create a network namespace, the container will use the host's network. Note: The host mode gives the container full access to local system services such as D-bus and is therefore considered insecure.
-+
- - **ns:**_path_: Path to a network namespace to join.
- - **private**: Create a new namespace for the container. This will use the **bridge** mode for rootful containers and **slirp4netns** for rootless ones.
- - **slirp4netns[:OPTIONS,...]**: use **slirp4netns**(1) to create a user network stack. This is the default for rootless containers. It is possible to specify these additional options, they can also be set with `network_cmd_options` in containers.conf:
-@@ -150,6 +155,43 @@ Valid _mode_ values are:
-   Note: Rootlesskit changes the source IP address of incoming packets to an IP address in the container network namespace, usually `10.0.2.100`. If your application requires the real source IP address, e.g. web server logs, use the slirp4netns port handler. The rootlesskit port handler is also used for rootless containers when connected to user-defined networks.
-   - **port_handler=slirp4netns**: Use the slirp4netns port forwarding, it is slower than rootlesskit but preserves the correct source IP address. This port handler cannot be used for user-defined networks.
- 
-+- **pasta[:OPTIONS,...]**: use **pasta**(1) to create a user-mode networking
-+stack. By default, IPv4 and IPv6 addresses and routes, as well as the pod
-+interface name, are copied from the host. If port forwarding isn't configured,
-+ports will be forwarded dynamically as services are bound on either side (init
-+namespace or container namespace). Port forwarding preserves the original source
-+IP address. Options described in pasta(1) can be specified as comma-separated
-+arguments.
-+
-+
-+In terms of pasta(1) options, **--config-net** is given by default, in order to
-+configure networking when the container is started. Also, **-t none** and
-+**-u none** are passed if, respectively, no TCP or UDP port forwarding from host
-+to container is configured, to disable automatic port forwarding based on bound
-+ports. Similarly, **-T none** and **-U none** are also given to disable the same
-+functionality from container to host.
-+
-+Some examples:
-+  - **pasta:--no-map-gw**: Don't allow the container to directly reach the host
-+    using the gateway address, which would normally be mapped to a loopback or
-+    link-local address.
-+  - **pasta:--mtu,1500**: Specify a 1500 bytes MTU for the _tap_ interface in
-+    the container.
-+  - **pasta:--ipv4-only,-a,10.0.2.0,-n,24,-g,10.0.2.2,--dns-forward,10.0.2.3,-m,1500,--no-ndp,--no-dhcpv6,--no-dhcp**,
-+    equivalent to default slirp4netns(1) options: disable IPv6, assign
-+    `10.0.2.0/24` to the `tap0` interface in the container, with gateway
-+    `10.0.2.3`, enable DNS forwarder reachable at `10.0.2.3`, set MTU to 1500
-+    bytes, disable NDP, DHCPv6 and DHCP support.
-+  - **pasta:--no-map-gw,-I,tap0,--ipv4-only,-a,10.0.2.0,-n,24,-g,10.0.2.2,--dns-forward,10.0.2.3,--no-ndp,--no-dhcpv6,--no-dhcp**,
-+    equivalent to default slirp4netns(1) options with Podman overrides: same as
-+    above, but leave the MTU to 65520 bytes, and don't map the gateway address
-+    from the container to a local address.
-+  - **pasta:-t,auto,-u,auto,-T,auto,-U,auto**: enable automatic port forwarding
-+    based on observed bound ports from both host and container sides
-+  - **pasta:-T,5201**: enable forwarding of TCP port 5201 from container to
-+    host, using the loopback interface instead of the tap interface for improved
-+    performance
-+
- @@option network-alias
- 
- @@option no-hosts
-@@ -248,6 +290,8 @@ $ podman pod create --network slirp4netns:outbound_addr=127.0.0.1,allow_host_loo
- 
- $ podman pod create --network slirp4netns:cidr=192.168.0.0/24
- 
-+$ podman pod create --network pasta
-+
- $ podman pod create --network net1:ip=10.89.1.5 --network net2:ip=10.89.10.10
- ```
- 
-diff --git a/docs/source/markdown/podman-run.1.md.in b/docs/source/markdown/podman-run.1.md.in
-index 2109a0e33..a9b212e81 100644
---- a/docs/source/markdown/podman-run.1.md.in
-+++ b/docs/source/markdown/podman-run.1.md.in
-@@ -298,10 +298,15 @@ Valid _mode_ values are:
-   - **interface_name**: Specify a name for the created network interface inside the container.
- 
-   For example to set a static ipv4 address and a static mac address, use `--network bridge:ip=10.88.0.10,mac=44:33:22:11:00:99`.
-+
- - \<network name or ID\>[:OPTIONS,...]: Connect to a user-defined network; this is the network name or ID from a network created by **[podman network create](podman-network-create.1.md)**. Using the network name implies the bridge network mode. It is possible to specify the same options described under the bridge mode above. You can use the **--network** option multiple times to specify additional networks.
-+
- - **none**: Create a network namespace for the container but do not configure network interfaces for it, thus the container has no network connectivity.
-+
- - **container:**_id_: Reuse another container's network stack.
-+
- - **host**: Do not create a network namespace, the container will use the host's network. Note: The host mode gives the container full access to local system services such as D-bus and is therefore considered insecure.
-+
- - **ns:**_path_: Path to a network namespace to join.
- - **private**: Create a new namespace for the container. This will use the **bridge** mode for rootful containers and **slirp4netns** for rootless ones.
- - **slirp4netns[:OPTIONS,...]**: use **slirp4netns**(1) to create a user network stack. This is the default for rootless containers. It is possible to specify these additional options, they can also be set with `network_cmd_options` in containers.conf:
-@@ -317,6 +322,43 @@ Valid _mode_ values are:
-   Note: Rootlesskit changes the source IP address of incoming packets to an IP address in the container network namespace, usually `10.0.2.100`. If your application requires the real source IP address, e.g. web server logs, use the slirp4netns port handler. The rootlesskit port handler is also used for rootless containers when connected to user-defined networks.
-   - **port_handler=slirp4netns**: Use the slirp4netns port forwarding, it is slower than rootlesskit but preserves the correct source IP address. This port handler cannot be used for user-defined networks.
- 
-+- **pasta[:OPTIONS,...]**: use **pasta**(1) to create a user-mode networking
-+stack. By default, IPv4 and IPv6 addresses and routes, as well as the pod
-+interface name, are copied from the host. If port forwarding isn't configured,
-+ports will be forwarded dynamically as services are bound on either side (init
-+namespace or container namespace). Port forwarding preserves the original source
-+IP address. Options described in pasta(1) can be specified as comma-separated
-+arguments.
-+
-+
-+In terms of pasta(1) options, **--config-net** is given by default, in order to
-+configure networking when the container is started. Also, **-t none** and
-+**-u none** are passed if, respectively, no TCP or UDP port forwarding from host
-+to container is configured, to disable automatic port forwarding based on bound
-+ports. Similarly, **-T none** and **-U none** are also given to disable the same
-+functionality from container to host.
-+
-+Some examples:
-+  - **pasta:--no-map-gw**: Don't allow the container to directly reach the host
-+    using the gateway address, which would normally be mapped to a loopback or
-+    link-local address.
-+  - **pasta:--mtu,1500**: Specify a 1500 bytes MTU for the _tap_ interface in
-+    the container.
-+  - **pasta:--ipv4-only,-a,10.0.2.0,-n,24,-g,10.0.2.2,--dns-forward,10.0.2.3,-m,1500,--no-ndp,--no-dhcpv6,--no-dhcp**,
-+    equivalent to default slirp4netns(1) options: disable IPv6, assign
-+    `10.0.2.0/24` to the `tap0` interface in the container, with gateway
-+    `10.0.2.3`, enable DNS forwarder reachable at `10.0.2.3`, set MTU to 1500
-+    bytes, disable NDP, DHCPv6 and DHCP support.
-+  - **pasta:--no-map-gw,-I,tap0,--ipv4-only,-a,10.0.2.0,-n,24,-g,10.0.2.2,--dns-forward,10.0.2.3,--no-ndp,--no-dhcpv6,--no-dhcp**,
-+    equivalent to default slirp4netns(1) options with Podman overrides: same as
-+    above, but leave the MTU to 65520 bytes, and don't map the gateway address
-+    from the container to a local address.
-+  - **pasta:-t,auto,-u,auto,-T,auto,-U,auto**: enable automatic port forwarding
-+    based on observed bound ports from both host and container sides
-+  - **pasta:-T,5201**: enable forwarding of TCP port 5201 from container to
-+    host, using the loopback interface instead of the tap interface for improved
-+    performance
-+
- @@option network-alias
- 
- @@option no-healthcheck
-@@ -900,8 +942,9 @@ In order for users to run rootless, there must be an entry for their username in
- 
- Rootless Podman works better if the fuse-overlayfs and slirp4netns packages are installed.
- The **fuse-overlayfs** package provides a userspace overlay storage driver, otherwise users need to use
--the **vfs** storage driver, which is diskspace expensive and does not perform well. slirp4netns is
--required for VPN, without it containers need to be run with the **--network=host** flag.
-+the **vfs** storage driver, which is diskspace expensive and does not perform
-+well. slirp4netns or pasta are required for VPN, without it containers need to
-+be run with the **--network=host** flag.
- 
- ## ENVIRONMENT
- 
-@@ -948,7 +991,7 @@ page.
- NOTE: Use the environment variable `TMPDIR` to change the temporary storage location of downloaded container images. Podman defaults to use `/var/tmp`.
- 
- ## SEE ALSO
--**[podman(1)](podman.1.md)**, **[podman-save(1)](podman-save.1.md)**, **[podman-ps(1)](podman-ps.1.md)**, **[podman-attach(1)](podman-attach.1.md)**, **[podman-pod-create(1)](podman-pod-create.1.md)**, **[podman-port(1)](podman-port.1.md)**, **[podman-start(1)](podman-start.1.md)**, **[podman-kill(1)](podman-kill.1.md)**, **[podman-stop(1)](podman-stop.1.md)**, **[podman-generate-systemd(1)](podman-generate-systemd.1.md)**, **[podman-rm(1)](podman-rm.1.md)**, **[subgid(5)](https://www.unix.com/man-page/linux/5/subgid)**, **[subuid(5)](https://www.unix.com/man-page/linux/5/subuid)**, **[containers.conf(5)](https://github.com/containers/common/blob/main/docs/containers.conf.5.md)**, **[systemd.unit(5)](https://www.freedesktop.org/software/systemd/man/systemd.unit.html)**, **[setsebool(8)](https://man7.org/linux/man-pages/man8/setsebool.8.html)**, **[slirp4netns(1)](https://github.com/rootless-containers/slirp4netns/blob/master/slirp4netns.1.md)**, **[fuse-overlayfs(1)](https://github.com/containers/fuse-overlayfs/blob/main/fuse-overlayfs.1.md)**, **proc(5)**, **[conmon(8)](https://github.com/containers/conmon/blob/main/docs/conmon.8.md)**, **personality(2)**
-+**[podman(1)](podman.1.md)**, **[podman-save(1)](podman-save.1.md)**, **[podman-ps(1)](podman-ps.1.md)**, **[podman-attach(1)](podman-attach.1.md)**, **[podman-pod-create(1)](podman-pod-create.1.md)**, **[podman-port(1)](podman-port.1.md)**, **[podman-start(1)](podman-start.1.md)**, **[podman-kill(1)](podman-kill.1.md)**, **[podman-stop(1)](podman-stop.1.md)**, **[podman-generate-systemd(1)](podman-generate-systemd.1.md)**, **[podman-rm(1)](podman-rm.1.md)**, **[subgid(5)](https://www.unix.com/man-page/linux/5/subgid)**, **[subuid(5)](https://www.unix.com/man-page/linux/5/subuid)**, **[containers.conf(5)](https://github.com/containers/common/blob/main/docs/containers.conf.5.md)**, **[systemd.unit(5)](https://www.freedesktop.org/software/systemd/man/systemd.unit.html)**, **[setsebool(8)](https://man7.org/linux/man-pages/man8/setsebool.8.html)**, **[slirp4netns(1)](https://github.com/rootless-containers/slirp4netns/blob/master/slirp4netns.1.md)**, **[pasta(1)](https://passt.top/builds/latest/web/passt.1.html)**, **[fuse-overlayfs(1)](https://github.com/containers/fuse-overlayfs/blob/main/fuse-overlayfs.1.md)**, **proc(5)**, **[conmon(8)](https://github.com/containers/conmon/blob/main/docs/conmon.8.md)**, **personality(2)**
- 
- ## HISTORY
- September 2018, updated by Kunal Kushwaha `<kushwaha_kunal_v7@lab.ntt.co.jp>`
-diff --git a/docs/source/markdown/podman.1.md b/docs/source/markdown/podman.1.md
-index 7a8dd7043..a54288bb0 100644
---- a/docs/source/markdown/podman.1.md
-+++ b/docs/source/markdown/podman.1.md
-@@ -88,7 +88,7 @@ Set libpod namespace. Namespaces are used to separate groups of containers and p
- When namespace is set, created containers and pods will join the given namespace, and only containers and pods in the given namespace will be visible to Podman.
- 
- #### **--network-cmd-path**=*path*
--Path to the command binary to use for setting up a network.  It is currently only used for setting up a slirp4netns network.  If "" is used then the binary is looked up using the $PATH environment variable.
-+Path to the command binary to use for setting up a network.  It is currently only used for setting up a slirp4netns(1) or pasta(1) network.  If "" is used then the binary is looked up using the $PATH environment variable.
- 
- #### **--network-config-dir**=*directory*
- 
-@@ -422,7 +422,7 @@ See the `subuid(5)` and `subgid(5)` man pages for more information.
- 
- Images are pulled under `XDG_DATA_HOME` when specified, otherwise in the home directory of the user under `.local/share/containers/storage`.
- 
--Currently the slirp4netns package is required to be installed to create a network device, otherwise rootless containers need to run in the network namespace of the host.
-+Currently either slirp4netns or pasta are required to be installed to create a network device, otherwise rootless containers need to run in the network namespace of the host.
- 
- In certain environments like HPC (High Performance Computing), users cannot take advantage of the additional UIDs and GIDs from the /etc/subuid and /etc/subgid systems.  However, in this environment, rootless Podman can operate with a single UID.  To make this work, set the `ignore_chown_errors` option in the /etc/containers/storage.conf or in ~/.config/containers/storage.conf files. This option tells Podman when pulling an image to ignore chown errors when attempting to change a file in a container image to match the non-root UID in the image. This means all files get saved as the user's UID. Note this could cause issues when running the container.
- 
-@@ -435,7 +435,7 @@ The Network File System (NFS) and other distributed file systems (for example: L
- For more information, please refer to the [Podman Troubleshooting Page](https://github.com/containers/podman/blob/main/troubleshooting.md).
- 
- ## SEE ALSO
--**[containers-mounts.conf(5)](https://github.com/containers/common/blob/main/docs/containers-mounts.conf.5.md)**, **[containers.conf(5)](https://github.com/containers/common/blob/main/docs/containers.conf.5.md)**, **[containers-registries.conf(5)](https://github.com/containers/image/blob/main/docs/containers-registries.conf.5.md)**, **[containers-storage.conf(5)](https://github.com/containers/storage/blob/main/docs/containers-storage.conf.5.md)**, **[buildah(1)](https://github.com/containers/buildah/blob/main/docs/buildah.1.md)**, **oci-hooks(5)**, **[containers-policy.json(5)](https://github.com/containers/image/blob/main/docs/containers-policy.json.5.md)**, **[crun(1)](https://github.com/containers/crun/blob/main/crun.1.md)**, **[runc(8)](https://github.com/opencontainers/runc/blob/master/man/runc.8.md)**, **[subuid(5)](https://www.unix.com/man-page/linux/5/subuid)**, **[subgid(5)](https://www.unix.com/man-page/linux/5/subgid)**, **[slirp4netns(1)](https://github.com/rootless-containers/slirp4netns/blob/master/slirp4netns.1.md)**, **[conmon(8)](https://github.com/containers/conmon/blob/main/docs/conmon.8.md)**
-+**[containers-mounts.conf(5)](https://github.com/containers/common/blob/main/docs/containers-mounts.conf.5.md)**, **[containers.conf(5)](https://github.com/containers/common/blob/main/docs/containers.conf.5.md)**, **[containers-registries.conf(5)](https://github.com/containers/image/blob/main/docs/containers-registries.conf.5.md)**, **[containers-storage.conf(5)](https://github.com/containers/storage/blob/main/docs/containers-storage.conf.5.md)**, **[buildah(1)](https://github.com/containers/buildah/blob/main/docs/buildah.1.md)**, **oci-hooks(5)**, **[containers-policy.json(5)](https://github.com/containers/image/blob/main/docs/containers-policy.json.5.md)**, **[crun(1)](https://github.com/containers/crun/blob/main/crun.1.md)**, **[runc(8)](https://github.com/opencontainers/runc/blob/master/man/runc.8.md)**, **[subuid(5)](https://www.unix.com/man-page/linux/5/subuid)**, **[subgid(5)](https://www.unix.com/man-page/linux/5/subgid)**, **[slirp4netns(1)](https://github.com/rootless-containers/slirp4netns/blob/master/slirp4netns.1.md)**, **[pasta(1)](https://passt.top/builds/latest/web/passt.1.html)**, **[conmon(8)](https://github.com/containers/conmon/blob/main/docs/conmon.8.md)**
- 
- ## HISTORY
- Dec 2016, Originally compiled by Dan Walsh <dwalsh@redhat.com>
-diff --git a/libpod/networking_common.go b/libpod/networking_common.go
-index fa444e26a..2590a4477 100644
---- a/libpod/networking_common.go
-+++ b/libpod/networking_common.go
-@@ -133,7 +133,8 @@ func (r *Runtime) teardownCNI(ctr *Container) error {
- 		return err
- 	}
- 
--	if !ctr.config.NetMode.IsSlirp4netns() && len(networks) > 0 {
-+	if !ctr.config.NetMode.IsSlirp4netns() &&
-+	   !ctr.config.NetMode.IsPasta() && len(networks) > 0 {
- 		netOpts := ctr.getNetworkOptions(networks)
- 		return r.teardownNetwork(ctr.state.NetNS.Path(), netOpts)
- 	}
-diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go
-index 6ea56ade5..822004a7c 100644
---- a/libpod/networking_linux.go
-+++ b/libpod/networking_linux.go
-@@ -563,6 +563,9 @@ func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) (status map[str
- 	if ctr.config.NetMode.IsSlirp4netns() {
- 		return nil, r.setupSlirp4netns(ctr, ctrNS)
- 	}
-+	if ctr.config.NetMode.IsPasta() {
-+		return nil, r.setupPasta(ctr, ctrNS)
-+	}
- 	networks, err := ctr.networks()
- 	if err != nil {
- 		return nil, err
-diff --git a/libpod/networking_pasta.go b/libpod/networking_pasta.go
-new file mode 100644
-index 000000000..f179c803a
---- /dev/null
-+++ b/libpod/networking_pasta.go
-@@ -0,0 +1,107 @@
-+// SPDX-License-Identifier: Apache-2.0
-+//
-+// networking_pasta.go - Start pasta(1) to provide connectivity to the container
-+//
-+// Copyright (c) 2022 Red Hat GmbH
-+// Author: Stefano Brivio <sbrivio@redhat.com>
-+//
-+// +build linux
-+
-+package libpod
-+
-+import (
-+	"os/exec"
-+	"fmt"
-+	"strings"
-+
-+	"github.com/containernetworking/plugins/pkg/ns"
-+	"github.com/pkg/errors"
-+	"github.com/sirupsen/logrus"
-+)
-+
-+func (r *Runtime) setupPasta(ctr *Container, netns ns.NetNS) error {
-+	var NoTCPInitPorts = true
-+	var NoUDPInitPorts = true
-+	var NoTCPNamespacePorts = true
-+	var NoUDPNamespacePorts = true
-+
-+	path := r.config.Engine.NetworkCmdPath
-+	if path == "" {
-+		var err error
-+		path, err = exec.LookPath("pasta")
-+		if err != nil {
-+			return fmt.Errorf("could not find pasta, the network namespace can't be configured: %w", err)
-+		}
-+	}
-+
-+	cmdArgs := []string{}
-+	cmdArgs = append(cmdArgs, "--config-net")
-+
-+	for _, i := range ctr.convertPortMappings() {
-+		protocols := strings.Split(i.Protocol, ",")
-+		for _, protocol := range protocols {
-+			var addr string
-+
-+			if (i.HostIP != "") {
-+				addr = fmt.Sprintf("%s/", i.HostIP)
-+			} else {
-+				addr = ""
-+			}
-+
-+			if protocol == "tcp" {
-+				cmdArgs = append(cmdArgs, "-t")
-+			} else if protocol == "udp" {
-+				cmdArgs = append(cmdArgs, "-u")
-+			} else {
-+				return fmt.Errorf("can't forward protocol: %s", protocol)
-+			}
-+
-+			arg := fmt.Sprintf("%s%d-%d:%d-%d", addr,
-+					   i.HostPort,
-+					   i.HostPort + i.Range - 1,
-+					   i.ContainerPort,
-+					   i.ContainerPort + i.Range - 1)
-+			cmdArgs = append(cmdArgs, arg)
-+		}
-+	}
-+
-+	cmdArgs = append(cmdArgs, ctr.config.NetworkOptions["pasta"]...)
-+
-+	for _, i := range cmdArgs {
-+		if (i == "-t" || i == "--tcp-ports") {
-+			NoTCPInitPorts = false
-+		} else if (i == "-u" || i == "--udp-ports") {
-+			NoUDPInitPorts = false
-+		} else if (i == "-T" || i == "--tcp-ns") {
-+			NoTCPNamespacePorts = false
-+		} else if (i == "-U" || i == "--udp-ns") {
-+			NoUDPNamespacePorts = false
-+		}
-+	}
-+
-+	if (NoTCPInitPorts) {
-+		cmdArgs = append(cmdArgs, "-t", "none")
-+	}
-+	if (NoUDPInitPorts) {
-+		cmdArgs = append(cmdArgs, "-u", "none")
-+	}
-+	if (NoTCPNamespacePorts) {
-+		cmdArgs = append(cmdArgs, "-T", "none")
-+	}
-+	if (NoUDPNamespacePorts) {
-+		cmdArgs = append(cmdArgs, "-U", "none")
-+	}
-+
-+	cmdArgs = append(cmdArgs, "--netns", netns.Path())
-+
-+	logrus.Debugf("pasta arguments: %s", strings.Join(cmdArgs, " "))
-+
-+	// pasta forks once ready, and quits once we delete the target namespace
-+	_, err := exec.Command(path, cmdArgs...).Output()
-+	if err != nil {
-+		return errors.Wrapf(err, "failed to start pasta: %s",
-+				    err.(*exec.ExitError).Stderr)
-+	}
-+
-+	return nil
-+}
-diff --git a/pkg/namespaces/namespaces.go b/pkg/namespaces/namespaces.go
-index 6dd576ea5..85903e7a9 100644
---- a/pkg/namespaces/namespaces.go
-+++ b/pkg/namespaces/namespaces.go
-@@ -19,6 +19,7 @@ const (
- 	privateType   = "private"
- 	shareableType = "shareable"
- 	slirpType     = "slirp4netns"
-+	pastaType     = "pasta"
- )
- 
- // KeepIDUserNsOptions defines how to keepIDmatically create a user namespace.
-@@ -440,6 +441,11 @@ func (n NetworkMode) IsSlirp4netns() bool {
- 	return n == slirpType || strings.HasPrefix(string(n), slirpType+":")
- }
- 
-+// IsPasta indicates if we are running a rootless network stack using pasta
-+func (n NetworkMode) IsPasta() bool {
-+	return n == pastaType || strings.HasPrefix(string(n), pastaType + ":")
-+}
-+
- // IsNS indicates a network namespace passed in by path (ns:<path>)
- func (n NetworkMode) IsNS() bool {
- 	return strings.HasPrefix(string(n), nsType)
-diff --git a/pkg/specgen/generate/namespaces.go b/pkg/specgen/generate/namespaces.go
-index 9497894f3..11a30e545 100644
---- a/pkg/specgen/generate/namespaces.go
-+++ b/pkg/specgen/generate/namespaces.go
-@@ -300,6 +300,16 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod.
- 			val = fmt.Sprintf("slirp4netns:%s", s.NetNS.Value)
- 		}
- 		toReturn = append(toReturn, libpod.WithNetNS(portMappings, expose, postConfigureNetNS, val, nil))
-+	case specgen.Pasta:
-+		portMappings, expose, err := createPortMappings(s, imageData)
-+		if err != nil {
-+			return nil, err
-+		}
-+		val := "pasta"
-+		if s.NetNS.Value != "" {
-+			val = fmt.Sprintf("pasta:%s", s.NetNS.Value)
-+		}
-+		toReturn = append(toReturn, libpod.WithNetNS(portMappings, expose, postConfigureNetNS, val, nil))
- 	case specgen.Bridge, specgen.Private, specgen.Default:
- 		portMappings, expose, err := createPortMappings(s, imageData)
- 		if err != nil {
-diff --git a/pkg/specgen/generate/pod_create.go b/pkg/specgen/generate/pod_create.go
-index 14d390e49..ad91353cb 100644
---- a/pkg/specgen/generate/pod_create.go
-+++ b/pkg/specgen/generate/pod_create.go
-@@ -195,6 +195,12 @@ func MapSpec(p *specgen.PodSpecGenerator) (*specgen.SpecGenerator, error) {
- 			p.InfraContainerSpec.NetworkOptions = p.NetworkOptions
- 			p.InfraContainerSpec.NetNS.NSMode = specgen.Slirp
- 		}
-+	case specgen.Pasta:
-+		logrus.Debugf("Pod will use pasta")
-+		if p.InfraContainerSpec.NetNS.NSMode != "host" {
-+			p.InfraContainerSpec.NetworkOptions = p.NetworkOptions
-+			p.InfraContainerSpec.NetNS.NSMode = specgen.NamespaceMode("pasta")
-+		}
- 	case specgen.NoNetwork:
- 		logrus.Debugf("Pod will not use networking")
- 		if len(p.InfraContainerSpec.PortMappings) > 0 ||
-diff --git a/pkg/specgen/namespaces.go b/pkg/specgen/namespaces.go
-index b6bbee868..5c4cb97c2 100644
---- a/pkg/specgen/namespaces.go
-+++ b/pkg/specgen/namespaces.go
-@@ -52,6 +52,9 @@ const (
- 	// be used.
- 	// Only used with the network namespace, invalid otherwise.
- 	Slirp NamespaceMode = "slirp4netns"
-+	// Pasta indicates that a pasta network stack should be used.
-+	// Only used with the network namespace, invalid otherwise.
-+	Pasta NamespaceMode = "pasta"
- 	// KeepId indicates a user namespace to keep the owner uid inside
- 	// of the namespace itself.
- 	// Only used with the user namespace, invalid otherwise.
-@@ -154,7 +157,7 @@ func validateNetNS(n *Namespace) error {
- 		return nil
- 	}
- 	switch n.NSMode {
--	case Slirp:
-+	case Slirp, Pasta:
- 		break
- 	case "", Default, Host, Path, FromContainer, FromPod, Private, NoNetwork, Bridge:
- 		break
-@@ -197,7 +200,7 @@ func (n *Namespace) validate() error {
- 	switch n.NSMode {
- 	case "", Default, Host, Path, FromContainer, FromPod, Private:
- 		// Valid, do nothing
--	case NoNetwork, Bridge, Slirp:
-+	case NoNetwork, Bridge, Slirp, Pasta:
- 		return errors.New("cannot use network modes with non-network namespace")
- 	default:
- 		return fmt.Errorf("invalid namespace type %s specified", n.NSMode)
-@@ -349,6 +352,13 @@ func ParseNetworkFlag(networks []string) (Namespace, map[string]types.PerNetwork
- 			networkOptions[parts[0]] = strings.Split(parts[1], ",")
- 		}
- 		toReturn.NSMode = Slirp
-+	case ns == string(Pasta), strings.HasPrefix(ns, string(Pasta) + ":"):
-+		parts := strings.SplitN(ns, ":", 2)
-+		if len(parts) > 1 {
-+			networkOptions = make(map[string][]string)
-+			networkOptions[parts[0]] = strings.Split(parts[1], ",")
-+		}
-+		toReturn.NSMode = Pasta
- 	case ns == string(FromPod):
- 		toReturn.NSMode = FromPod
- 	case ns == "" || ns == string(Default) || ns == string(Private):
-@@ -419,7 +429,7 @@ func ParseNetworkFlag(networks []string) (Namespace, map[string]types.PerNetwork
- 			if parts[0] == "" {
- 				return toReturn, nil, nil, fmt.Errorf("network name cannot be empty: %w", define.ErrInvalidArg)
- 			}
--			if cutil.StringInSlice(parts[0], []string{string(Bridge), string(Slirp), string(FromPod), string(NoNetwork),
-+			if cutil.StringInSlice(parts[0], []string{string(Bridge), string(Slirp), string(Pasta), string(FromPod), string(NoNetwork),
- 				string(Default), string(Private), string(Path), string(FromContainer), string(Host)}) {
- 				return toReturn, nil, nil, fmt.Errorf("can only set extra network names, selected mode %s conflicts with bridge: %w", parts[0], define.ErrInvalidArg)
- 			}
-diff --git a/pkg/specgen/podspecgen.go b/pkg/specgen/podspecgen.go
-index 64a79f4ee..faeeb2ed6 100644
---- a/pkg/specgen/podspecgen.go
-+++ b/pkg/specgen/podspecgen.go
-@@ -98,7 +98,7 @@ type PodNetworkConfig struct {
- 	// PortMappings is a set of ports to map into the infra container.
- 	// As, by default, containers share their network with the infra
- 	// container, this will forward the ports to the entire pod.
--	// Only available if NetNS is set to Bridge or Slirp.
-+	// Only available if NetNS is set to Bridge, Slirp, or Pasta.
- 	// Optional.
- 	PortMappings []types.PortMapping `json:"portmappings,omitempty"`
- 	// Map of networks names to ids the container should join to.
--- 
-2.35.1
-
diff -Nru passt-0.0~git20230227.c538ee8/contrib/qemu/0001-net-Allow-also-UNIX-domain-sockets-to-be-used-as-net.patch passt-0.0~git20230309.7c7625d/contrib/qemu/0001-net-Allow-also-UNIX-domain-sockets-to-be-used-as-net.patch
--- passt-0.0~git20230227.c538ee8/contrib/qemu/0001-net-Allow-also-UNIX-domain-sockets-to-be-used-as-net.patch	2023-02-27 18:56:37.000000000 +0100
+++ passt-0.0~git20230309.7c7625d/contrib/qemu/0001-net-Allow-also-UNIX-domain-sockets-to-be-used-as-net.patch	1970-01-01 01:00:00.000000000 +0100
@@ -1,171 +0,0 @@
-From 83c3f76b8fe6b4a6bb45dcf5cfad65ec6f98a10e Mon Sep 17 00:00:00 2001
-From: Stefano Brivio <sbrivio@redhat.com>
-Date: Wed, 26 Jan 2022 16:45:15 +0100
-Subject: [PATCH 1/2] net: Allow also UNIX domain sockets to be used as -netdev
- socket
-
-It has lower overhead compared to TCP, doesn't need a free port
-and the adaptation is trivial.
-
-Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
----
-SPDX-FileCopyrightText: 2020-2022 Red Hat GmbH <sbrivio@redhat.com>
-SPDX-License-Identifier: AGPL-3.0-or-later
-
- net/socket.c | 106 ++++++++++++++++++++++++++++++++++++++++++---------
- 1 file changed, 87 insertions(+), 19 deletions(-)
-
-diff --git a/net/socket.c b/net/socket.c
-index 2e5f3ac923..b901e22836 100644
---- a/net/socket.c
-+++ b/net/socket.c
-@@ -511,26 +511,59 @@ static int net_socket_listen_init(NetClientState *peer,
- {
-     NetClientState *nc;
-     NetSocketState *s;
--    struct sockaddr_in saddr;
--    int fd, ret;
-+    struct sockaddr_storage saddr;
-+    struct sockaddr_in *saddr_in = (struct sockaddr_in *)&saddr;
-+    struct sockaddr_un *saddr_un = (struct sockaddr_un *)&saddr;
-+    size_t saddr_size;
-+    int fd, ret, pf;
-+
-+#ifndef WIN32
-+    if (strchr(host_str, ':')) {
-+#endif
-+        if (parse_host_port(saddr_in, host_str, errp) < 0)
-+            return -1;
- 
--    if (parse_host_port(&saddr, host_str, errp) < 0) {
--        return -1;
--    }
-+        pf = PF_INET;
-+        saddr_size = sizeof(*saddr_in);
-+#ifndef WIN32
-+    } else {
-+        struct stat sb;
-+
-+        if (stat(host_str, &sb) == -1) {
-+            error_setg_errno(errp, errno, "can't stat socket path");
-+            return -1;
-+        }
-+
-+        if ((sb.st_mode & S_IFMT) != S_IFSOCK) {
-+            error_setg_errno(errp, errno, "path provided is not a socket");
-+            return -1;
-+        }
- 
--    fd = qemu_socket(PF_INET, SOCK_STREAM, 0);
-+        saddr_un->sun_family = PF_UNIX;
-+        strncpy(saddr_un->sun_path, host_str, sizeof(saddr_un->sun_path));
-+
-+        pf = PF_UNIX;
-+        saddr_size = sizeof(*saddr_un);
-+    }
-+#endif /* !WIN32 */
-+    fd = qemu_socket(pf, SOCK_STREAM, 0);
-     if (fd < 0) {
-         error_setg_errno(errp, errno, "can't create stream socket");
-         return -1;
-     }
-     qemu_set_nonblock(fd);
- 
--    socket_set_fast_reuse(fd);
-+    if (pf == PF_INET)
-+        socket_set_fast_reuse(fd);
- 
--    ret = bind(fd, (struct sockaddr *)&saddr, sizeof(saddr));
-+    ret = bind(fd, (struct sockaddr *)&saddr, saddr_size);
-     if (ret < 0) {
--        error_setg_errno(errp, errno, "can't bind ip=%s to socket",
--                         inet_ntoa(saddr.sin_addr));
-+        if (pf == PF_INET)
-+            error_setg_errno(errp, errno, "can't bind ip=%s to socket",
-+                             inet_ntoa(saddr_in->sin_addr));
-+        else if (pf == PF_UNIX)
-+            error_setg_errno(errp, errno, "can't create socket with path: %s",
-+                             host_str);
-         closesocket(fd);
-         return -1;
-     }
-@@ -559,14 +592,43 @@ static int net_socket_connect_init(NetClientState *peer,
-                                    Error **errp)
- {
-     NetSocketState *s;
--    int fd, connected, ret;
--    struct sockaddr_in saddr;
-+    int fd, connected, ret, pf;
-+    struct sockaddr_storage saddr;
-+    size_t saddr_size;
-+    struct sockaddr_in *saddr_in = (struct sockaddr_in *)&saddr;
-+#ifndef WIN32
-+    struct sockaddr_un *saddr_un = (struct sockaddr_un *)&saddr;
-+
-+    if (strchr(host_str, ':')) {
-+#endif
-+        if (parse_host_port(saddr_in, host_str, errp) < 0)
-+            return -1;
- 
--    if (parse_host_port(&saddr, host_str, errp) < 0) {
--        return -1;
-+        pf = PF_INET;
-+        saddr_size = sizeof(*saddr_in);
-+#ifndef WIN32
-+    } else {
-+        struct stat sb;
-+
-+        if (stat(host_str, &sb) == -1) {
-+            error_setg_errno(errp, errno, "can't stat socket path");
-+            return -1;
-+        }
-+
-+        if ((sb.st_mode & S_IFMT) != S_IFSOCK) {
-+            error_setg_errno(errp, errno, "provided path is not a socket");
-+            return -1;
-+        }
-+
-+        saddr_un->sun_family = PF_UNIX;
-+        strncpy(saddr_un->sun_path, host_str, sizeof(saddr_un->sun_path));
-+
-+        pf = PF_UNIX;
-+        saddr_size = sizeof(*saddr_un);
-     }
-+#endif /* !WIN32 */
- 
--    fd = qemu_socket(PF_INET, SOCK_STREAM, 0);
-+    fd = qemu_socket(pf, SOCK_STREAM, 0);
-     if (fd < 0) {
-         error_setg_errno(errp, errno, "can't create stream socket");
-         return -1;
-@@ -575,7 +637,7 @@ static int net_socket_connect_init(NetClientState *peer,
- 
-     connected = 0;
-     for(;;) {
--        ret = connect(fd, (struct sockaddr *)&saddr, sizeof(saddr));
-+        ret = connect(fd, (struct sockaddr *)&saddr, saddr_size);
-         if (ret < 0) {
-             if (errno == EINTR || errno == EWOULDBLOCK) {
-                 /* continue */
-@@ -597,9 +659,15 @@ static int net_socket_connect_init(NetClientState *peer,
-         return -1;
-     }
- 
--    snprintf(s->nc.info_str, sizeof(s->nc.info_str),
--             "socket: connect to %s:%d",
--             inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
-+    if (pf == PF_INET) {
-+        snprintf(s->nc.info_str, sizeof(s->nc.info_str),
-+                 "socket: connect to %s:%d",
-+                 inet_ntoa(saddr_in->sin_addr), ntohs(saddr_in->sin_port));
-+    } else if (pf == PF_UNIX) {
-+        snprintf(s->nc.info_str, sizeof(s->nc.info_str),
-+                 "socket: connect to %s", saddr_un->sun_path);
-+    }
-+
-     return 0;
- }
- 
--- 
-2.28.0
-
diff -Nru passt-0.0~git20230227.c538ee8/contrib/qemu/0002-net-Don-t-ignore-EINVAL-on-netdev-socket-connection.patch passt-0.0~git20230309.7c7625d/contrib/qemu/0002-net-Don-t-ignore-EINVAL-on-netdev-socket-connection.patch
--- passt-0.0~git20230227.c538ee8/contrib/qemu/0002-net-Don-t-ignore-EINVAL-on-netdev-socket-connection.patch	2023-02-27 18:56:37.000000000 +0100
+++ passt-0.0~git20230309.7c7625d/contrib/qemu/0002-net-Don-t-ignore-EINVAL-on-netdev-socket-connection.patch	1970-01-01 01:00:00.000000000 +0100
@@ -1,37 +0,0 @@
-From a6d475147682de1fe3b14eb325f4247e013e8440 Mon Sep 17 00:00:00 2001
-Message-Id: <a6d475147682de1fe3b14eb325f4247e013e8440.1619091389.git.sbrivio@redhat.com>
-In-Reply-To: <ba51349d353f11e05c6341a7e065f2ade3874c68.1619091389.git.sbrivio@redhat.com>
-References: <ba51349d353f11e05c6341a7e065f2ade3874c68.1619091389.git.sbrivio@redhat.com>
-From: Stefano Brivio <sbrivio@redhat.com>
-Date: Wed, 21 Apr 2021 18:52:16 +0200
-Subject: [PATCH 2/2] net: Don't ignore EINVAL on netdev socket connection
-
-Other errors are treated as failure by net_socket_connect_init(),
-but if connect() returns EINVAL, we'll fail silently. Remove the
-related exception.
-
-Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
----
-SPDX-FileCopyrightText: 2020-2021 Red Hat GmbH <sbrivio@redhat.com>
-SPDX-License-Identifier: AGPL-3.0-or-later
-
- net/socket.c | 3 +--
- 1 file changed, 1 insertion(+), 2 deletions(-)
-
-diff --git a/net/socket.c b/net/socket.c
-index aadd11dae2b3..d3293ac12e82 100644
---- a/net/socket.c
-+++ b/net/socket.c
-@@ -644,8 +644,7 @@ static int net_socket_connect_init(NetClientState *peer,
-             if (errno == EINTR || errno == EWOULDBLOCK) {
-                 /* continue */
-             } else if (errno == EINPROGRESS ||
--                       errno == EALREADY ||
--                       errno == EINVAL) {
-+                       errno == EALREADY) {
-                 break;
-             } else {
-                 error_setg_errno(errp, errno, "can't connect socket");
--- 
-2.29.2
-
diff -Nru passt-0.0~git20230227.c538ee8/contrib/selinux/passt.if passt-0.0~git20230309.7c7625d/contrib/selinux/passt.if
--- passt-0.0~git20230227.c538ee8/contrib/selinux/passt.if	2023-02-27 18:56:37.000000000 +0100
+++ passt-0.0~git20230309.7c7625d/contrib/selinux/passt.if	2023-03-09 03:44:21.000000000 +0100
@@ -30,8 +30,32 @@
 		type passt_t;
 	')
 
-	allow $1 user_tmp_t:sock_file write;
+	allow $1 $2:sock_file write;
 	allow $1 passt_t:unix_stream_socket connectto;
+
+	allow passt_t $2:sock_file { create read write unlink };
+')
+
+interface(`passt_logfile',`
+	gen_require(`
+		type passt_t;
+	')
+
+	logging_log_file($1);
+	allow passt_t $1:dir { search write add_name };
+	allow passt_t $1:file { create open read write };
+')
+
+interface(`passt_pidfile',`
+	gen_require(`
+		type passt_t;
+	')
+
+	allow $1 $2:file { open read unlink };
+
+	files_pid_file($2);
+	allow passt_t $2:dir { search write add_name };
+	allow passt_t $2:file { create open write };
 ')
 
 interface(`passt_kill',`
diff -Nru passt-0.0~git20230227.c538ee8/contrib/selinux/passt.te passt-0.0~git20230309.7c7625d/contrib/selinux/passt.te
--- passt-0.0~git20230227.c538ee8/contrib/selinux/passt.te	2023-02-27 18:56:37.000000000 +0100
+++ passt-0.0~git20230309.7c7625d/contrib/selinux/passt.te	2023-03-09 03:44:21.000000000 +0100
@@ -57,15 +57,11 @@
 domain_type(passt_t);
 type passt_exec_t;
 files_type(passt_exec_t);
-init_daemon_domain(passt_t, passt_exec_t)
 type passt_log_t;
 logging_log_file(passt_log_t);
 type passt_etc_t;
 files_config_file(passt_etc_t);
 
-type passt_port_t;
-typeattribute passt_port_t port_type;
-
 role unconfined_r types passt_t;
 
 allow passt_t passt_exec_t : file { ioctl read getattr lock execute execute_no_trans entrypoint open } ;
@@ -73,6 +69,7 @@
 allow unconfined_t passt_t : process transition ;
 
 init_daemon_domain(passt_t, passt_exec_t)
+term_use_all_inherited_terms(passt_t)
 
 allow passt_t bin_t:file { execute execute_no_trans map };
 allow passt_t user_home_dir_t:dir { search add_name write };
@@ -100,16 +97,22 @@
 allow passt_t tmp_t:sock_file { create unlink write };
 allow passt_t self:netlink_route_socket { bind create nlmsg_read read write setopt };
 
-allow passt_t self:tcp_socket create_stream_socket_perms;
-corenet_tcp_sendrecv_generic_node(passt_t)
-corenet_tcp_bind_generic_node(passt_t)
-allow passt_t passt_port_t:tcp_socket { name_bind name_connect };
-allow passt_t http_port_t:tcp_socket { name_bind name_connect };
-allow passt_t self:udp_socket create_stream_socket_perms;
-corenet_udp_sendrecv_generic_node(passt_t)
-corenet_udp_bind_generic_node(passt_t)
-allow passt_t passt_port_t:udp_socket { name_bind };
+corenet_tcp_bind_all_nodes(passt_t)
+corenet_udp_bind_all_nodes(passt_t)
+
+corenet_tcp_bind_all_ports(passt_t)
+corenet_udp_bind_all_ports(passt_t)
+
+corenet_tcp_connect_all_ports(passt_t)
+
+corenet_tcp_sendrecv_all_ports(passt_t)
+corenet_udp_sendrecv_all_ports(passt_t)
+
 allow passt_t node_t:icmp_socket { name_bind node_bind };
+allow passt_t port_t:icmp_socket name_bind;
+
+allow passt_t self:tcp_socket { create getopt setopt connect bind listen accept shutdown read write };
+allow passt_t self:udp_socket { create getopt setopt connect bind read write };
 allow passt_t self:icmp_socket { bind create setopt read write };
 
 allow passt_t user_tmp_t:dir { add_name write };
diff -Nru passt-0.0~git20230227.c538ee8/debian/changelog passt-0.0~git20230309.7c7625d/debian/changelog
--- passt-0.0~git20230227.c538ee8/debian/changelog	2023-02-27 23:29:43.000000000 +0100
+++ passt-0.0~git20230309.7c7625d/debian/changelog	2023-03-09 09:29:19.000000000 +0100
@@ -1,4 +1,20 @@
-passt (0.0~git20230227.c538ee8-1) UNRELEASED; urgency=medium
+passt (0.0~git20230309.7c7625d-1) unstable; urgency=medium
+
+  * New upstream version 0.0~git20230309.7c7625d
+  * Fix cross-build and armhf build reproducibility by using the
+    target declared by the compiler to build seccomp profiles, not
+    the current machine type
+  * Fix build on sh4 by correcting the AUDIT_ARCH target name
+  * Fix signedness warning reported on 32-bit architectures
+  * Terminate gracefully if open files quota is reached
+  * Fix inbound TCP Maximum Segment Size used to queue data to tap
+    interface, in pasta mode
+  * Fix accidental gap in slirp4netns compatibility by introducing
+    trivial options to actually set outbound interfaces and addresses
+
+ -- Stefano Brivio <sbrivio@redhat.com>  Thu, 09 Mar 2023 09:29:19 +0100
+
+passt (0.0~git20230227.c538ee8-1) unstable; urgency=medium
 
   * New upstream version 0.0~git20230227.c538ee8
   * Most rules from the AppArmor profile are now divided into two
diff -Nru passt-0.0~git20230227.c538ee8/debian/passt_skip_irrelevant.sh passt-0.0~git20230309.7c7625d/debian/passt_skip_irrelevant.sh
--- passt-0.0~git20230227.c538ee8/debian/passt_skip_irrelevant.sh	1970-01-01 01:00:00.000000000 +0100
+++ passt-0.0~git20230309.7c7625d/debian/passt_skip_irrelevant.sh	2023-03-09 09:29:19.000000000 +0100
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+uupdate --find $@
diff -Nru passt-0.0~git20230227.c538ee8/debian/rules passt-0.0~git20230309.7c7625d/debian/rules
--- passt-0.0~git20230227.c538ee8/debian/rules	2022-11-16 23:14:37.000000000 +0100
+++ passt-0.0~git20230309.7c7625d/debian/rules	2023-03-09 09:29:19.000000000 +0100
@@ -3,7 +3,7 @@
 export DEB_BUILD_MAINT_OPTIONS = hardening=+all
 
 override_dh_auto_install:
-	dh_auto_install -- prefix=/usr
+	dh_auto_install -- prefix=/usr TARGET=$(DEB_BUILD_ARCH)
 	dh_apparmor --profile-name=usr.bin.passt -ppasst
 
 ifneq (,$(findstring amd64,$(DEB_BUILD_ARCH)))
diff -Nru passt-0.0~git20230227.c538ee8/debian/watch passt-0.0~git20230309.7c7625d/debian/watch
--- passt-0.0~git20230227.c538ee8/debian/watch	2022-11-04 17:40:21.000000000 +0100
+++ passt-0.0~git20230309.7c7625d/debian/watch	2023-03-09 09:29:19.000000000 +0100
@@ -1,3 +1,4 @@
 version=4
+script=debian/passt_skip_irrelevant.sh \
 options="uversionmangle=s/_//g; s/^/0.0~git/" \
 https://passt.top/passt/ /passt/snapshot/@PACKAGE@-([0-9]{4}_[0-9]{2}_[0-9]{2}.[0-9a-f]{7})@ARCHIVE_EXT@
diff -Nru passt-0.0~git20230227.c538ee8/icmp.c passt-0.0~git20230309.7c7625d/icmp.c
--- passt-0.0~git20230227.c538ee8/icmp.c	2023-02-27 18:56:37.000000000 +0100
+++ passt-0.0~git20230309.7c7625d/icmp.c	2023-03-09 03:44:21.000000000 +0100
@@ -170,8 +170,16 @@
 		iref.icmp.id = id = ntohs(ih->un.echo.id);
 
 		if ((s = icmp_id_map[V4][id].sock) <= 0) {
-			s = sock_l4(c, AF_INET, IPPROTO_ICMP, NULL, NULL, id,
-				    iref.u32);
+			const struct in_addr *bind_addr = NULL;
+			const char *bind_if;
+
+			bind_if = *c->ip4.ifname_out ? c->ip4.ifname_out : NULL;
+
+			if (!IN4_IS_ADDR_UNSPECIFIED(&c->ip4.addr_out))
+				bind_addr = &c->ip4.addr_out;
+
+			s = sock_l4(c, AF_INET, IPPROTO_ICMP, bind_addr,
+				    bind_if, id, iref.u32);
 			if (s < 0)
 				goto fail_sock;
 			if (s > SOCKET_MAX) {
@@ -216,8 +224,16 @@
 
 		iref.icmp.id = id = ntohs(ih->icmp6_identifier);
 		if ((s = icmp_id_map[V6][id].sock) <= 0) {
-			s = sock_l4(c, AF_INET6, IPPROTO_ICMPV6, NULL, NULL, id,
-				    iref.u32);
+			const struct in6_addr *bind_addr = NULL;
+			const char *bind_if;
+
+			bind_if = *c->ip6.ifname_out ? c->ip6.ifname_out : NULL;
+
+			if (!IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addr_out))
+				bind_addr = &c->ip6.addr_out;
+
+			s = sock_l4(c, AF_INET6, IPPROTO_ICMPV6, bind_addr,
+				    bind_if, id, iref.u32);
 			if (s < 0)
 				goto fail_sock;
 			if (s > SOCKET_MAX) {
diff -Nru passt-0.0~git20230227.c538ee8/isolation.c passt-0.0~git20230309.7c7625d/isolation.c
--- passt-0.0~git20230227.c538ee8/isolation.c	2023-02-27 18:56:37.000000000 +0100
+++ passt-0.0~git20230309.7c7625d/isolation.c	2023-03-09 03:44:21.000000000 +0100
@@ -65,6 +65,7 @@
 #include <stdbool.h>
 #include <stddef.h>
 #include <stdlib.h>
+#include <stdio.h>
 #include <string.h>
 #include <time.h>
 #include <unistd.h>
diff -Nru passt-0.0~git20230227.c538ee8/Makefile passt-0.0~git20230309.7c7625d/Makefile
--- passt-0.0~git20230227.c538ee8/Makefile	2023-02-27 18:56:37.000000000 +0100
+++ passt-0.0~git20230309.7c7625d/Makefile	2023-03-09 03:44:21.000000000 +0100
@@ -31,6 +31,7 @@
 AUDIT_ARCH := $(shell echo $(AUDIT_ARCH) | sed 's/PPCLE/PPC64LE/')
 AUDIT_ARCH := $(shell echo $(AUDIT_ARCH) | sed 's/MIPS64EL/MIPSEL64/')
 AUDIT_ARCH := $(shell echo $(AUDIT_ARCH) | sed 's/HPPA/PARISC/')
+AUDIT_ARCH := $(shell echo $(AUDIT_ARCH) | sed 's/SH4/SH/')
 
 FLAGS := -Wall -Wextra -pedantic -std=c99 -D_XOPEN_SOURCE=700 -D_GNU_SOURCE
 FLAGS += -D_FORTIFY_SOURCE=2 -O2 -pie -fPIE
@@ -105,7 +106,7 @@
 static: clean all
 
 seccomp.h: seccomp.sh $(PASST_SRCS) $(PASST_HEADERS)
-	@ EXTRA_SYSCALLS="$(EXTRA_SYSCALLS)" ./seccomp.sh $(PASST_SRCS) $(PASST_HEADERS)
+	@ EXTRA_SYSCALLS="$(EXTRA_SYSCALLS)" ARCH="$(TARGET_ARCH)" CC="$(CC)" ./seccomp.sh $(PASST_SRCS) $(PASST_HEADERS)
 
 passt: $(PASST_SRCS) $(HEADERS)
 	$(CC) $(FLAGS) $(CFLAGS) $(CPPFLAGS) $(PASST_SRCS) -o passt $(LDFLAGS)
diff -Nru passt-0.0~git20230227.c538ee8/netlink.c passt-0.0~git20230309.7c7625d/netlink.c
--- passt-0.0~git20230227.c538ee8/netlink.c	2023-02-27 18:56:37.000000000 +0100
+++ passt-0.0~git20230309.7c7625d/netlink.c	2023-03-09 03:44:21.000000000 +0100
@@ -18,6 +18,7 @@
 #include <errno.h>
 #include <sys/types.h>
 #include <limits.h>
+#include <signal.h>
 #include <stdlib.h>
 #include <stdbool.h>
 #include <stdint.h>
@@ -34,6 +35,8 @@
 #include "log.h"
 #include "netlink.h"
 
+#define NLBUFSIZ	(8192 * sizeof(struct nlmsghdr)) /* See netlink(7) */
+
 /* Socket in init, in target namespace, sequence (just needs to be monotonic) */
 static int nl_sock	= -1;
 static int nl_sock_ns	= -1;
@@ -105,7 +108,7 @@
 static int nl_req(int ns, char *buf, const void *req, ssize_t len)
 {
 	int s = ns ? nl_sock_ns : nl_sock, done = 0;
-	char flush[BUFSIZ];
+	char flush[NLBUFSIZ];
 	ssize_t n;
 
 	while (!done && (n = recv(s, flush, sizeof(flush), MSG_DONTWAIT)) > 0) {
@@ -121,7 +124,8 @@
 		}
 	}
 
-	if ((send(s, req, len, 0) < len) || (len = recv(s, buf, BUFSIZ, 0)) < 0)
+	if ((send(s, req, len, 0) < len) ||
+	    (len = recv(s, buf, NLBUFSIZ, 0)) < 0)
 		return -errno;
 
 	return len;
@@ -149,7 +153,7 @@
 	};
 	struct nlmsghdr *nh;
 	struct rtattr *rta;
-	char buf[BUFSIZ];
+	char buf[NLBUFSIZ];
 	ssize_t n;
 	size_t na;
 
@@ -227,7 +231,7 @@
 	struct nlmsghdr *nh;
 	struct rtattr *rta;
 	struct rtmsg *rtm;
-	char buf[BUFSIZ];
+	char buf[NLBUFSIZ];
 	ssize_t n;
 	size_t na;
 
@@ -336,7 +340,7 @@
 	struct ifaddrmsg *ifa;
 	struct nlmsghdr *nh;
 	struct rtattr *rta;
-	char buf[BUFSIZ];
+	char buf[NLBUFSIZ];
 	ssize_t n;
 	size_t na;
 
@@ -446,7 +450,7 @@
 	struct ifinfomsg *ifm;
 	struct nlmsghdr *nh;
 	struct rtattr *rta;
-	char buf[BUFSIZ];
+	char buf[NLBUFSIZ];
 	ssize_t n;
 	size_t na;
 
diff -Nru passt-0.0~git20230227.c538ee8/passt.1 passt-0.0~git20230309.7c7625d/passt.1
--- passt-0.0~git20230227.c538ee8/passt.1	2023-02-27 18:56:37.000000000 +0100
+++ passt-0.0~git20230309.7c7625d/passt.1	2023-03-09 03:44:21.000000000 +0100
@@ -180,8 +180,33 @@
 .TP
 .BR \-i ", " \-\-interface " " \fIname
 Use host interface \fIname\fR to derive addresses and routes.
-Default is to use the interfaces with the first default routes for each IP
-version.
+Default is to use the interfaces specified by \fB--outbound-if4\fR and
+\fB--outbound-if6\fR, for IPv4 and IPv6 addresses and routes, respectively. If
+no interfaces are given, the interface with the first default routes for each IP
+version is selected.
+
+.TP
+.BR \-o ", " \-\-outbound " " \fIaddr
+Use an IPv4 \fIaddr\fR as source address for IPv4 outbound TCP connections, UDP
+flows, ICMP requests, or an IPv6 \fIaddr\fR for IPv6 ones, by binding outbound
+sockets to it.
+This option can be specified zero (for defaults) to two times (once for IPv4,
+once for IPv6).
+By default, the source address is selected by the routing tables.
+
+.TP
+.BR \-\-outbound-if4 " " \fIname
+Bind IPv4 outbound sockets to host interface \fIname\fR, and, unless another
+interface is specified via \fB-i\fR, \fB--interface\fR, use this interface to
+derive IPv4 addresses and routes.
+By default, the interface given by the default route is selected.
+
+.TP
+.BR \-\-outbound-if6 " " \fIname
+Bind IPv6 outbound sockets to host interface \fIname\fR, and, unless another
+interface is specified via \fB-i\fR, \fB--interface\fR, use this interface to
+derive IPv6 addresses and routes.
+By default, the interface given by the default route is selected.
 
 .TP
 .BR \-D ", " \-\-dns " " \fIaddr
diff -Nru passt-0.0~git20230227.c538ee8/passt.c passt-0.0~git20230309.7c7625d/passt.c
--- passt-0.0~git20230227.c538ee8/passt.c	2023-02-27 18:56:37.000000000 +0100
+++ passt-0.0~git20230309.7c7625d/passt.c	2023-03-09 03:44:21.000000000 +0100
@@ -27,6 +27,8 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <netdb.h>
+#include <signal.h>
+#include <stdio.h>
 #include <string.h>
 #include <errno.h>
 #include <time.h>
@@ -241,7 +243,7 @@
 	conf(&c, argc, argv);
 	trace_init(c.trace);
 
-	if (c.stderr || isatty(fileno(stdout)))
+	if (c.force_stderr || isatty(fileno(stdout)))
 		__openlog(log_name, LOG_PERROR, LOG_DAEMON);
 
 	quit_fd = pasta_netns_quit_init(&c);
diff -Nru passt-0.0~git20230227.c538ee8/passt.h passt-0.0~git20230309.7c7625d/passt.h
--- passt-0.0~git20230227.c538ee8/passt.h	2023-02-27 18:56:37.000000000 +0100
+++ passt-0.0~git20230309.7c7625d/passt.h	2023-03-09 03:44:21.000000000 +0100
@@ -106,6 +106,8 @@
  * @dns:		DNS addresses for DHCP, zero-terminated, network order
  * @dns_match:		Forward DNS query if sent to this address, network order
  * @dns_host:		Use this DNS on the host for forwarding, network order
+ * @addr_out:		Optional source address for outbound traffic
+ * @ifname_out:		Optional interface name to bind outbound sockets to
  */
 struct ip4_ctx {
 	struct in_addr addr;
@@ -115,6 +117,9 @@
 	struct in_addr dns[MAXNS + 1];
 	struct in_addr dns_match;
 	struct in_addr dns_host;
+
+	struct in_addr addr_out;
+	char ifname_out[IFNAMSIZ];
 };
 
 /**
@@ -127,6 +132,8 @@
  * @dns:		DNS addresses for DHCPv6 and NDP, zero-terminated
  * @dns_match:		Forward DNS query if sent to this address
  * @dns_host:		Use this DNS on the host for forwarding
+ * @addr_out:		Optional source address for outbound traffic
+ * @ifname_out:		Optional interface name to bind outbound sockets to
  */
 struct ip6_ctx {
 	struct in6_addr addr;
@@ -137,6 +144,9 @@
 	struct in6_addr dns[MAXNS + 1];
 	struct in6_addr dns_match;
 	struct in6_addr dns_host;
+
+	struct in6_addr addr_out;
+	char ifname_out[IFNAMSIZ];
 };
 
 #include <netinet/if_ether.h>
@@ -148,7 +158,7 @@
  * @trace:		Enable tracing (extra debug) mode
  * @quiet:		Don't print informational messages
  * @foreground:		Run in foreground, don't log to stderr by default
- * @stderr:		Force logging to stderr
+ * @force_stderr:	Force logging to stderr
  * @nofile:		Maximum number of open files (ulimit -n)
  * @sock_path:		Path for UNIX domain socket
  * @pcap:		Path for packet capture file
@@ -164,10 +174,10 @@
  * @fd_tap:		AF_UNIX socket, tuntap device, or pre-opened socket
  * @mac:		Host MAC address
  * @mac_guest:		MAC address of guest or namespace, seen or configured
- * @ifi4:		Index of routable interface for IPv4, 0 if IPv4 disabled
+ * @ifi4:		Index of template interface for IPv4, 0 if IPv4 disabled
  * @ip:			IPv4 configuration
  * @dns_search:		DNS search list
- * @ifi6:		Index of routable interface for IPv6, 0 if IPv6 disabled
+ * @ifi6:		Index of template interface for IPv6, 0 if IPv6 disabled
  * @ip6:		IPv6 configuration
  * @pasta_ifn:		Name of namespace interface for pasta
  * @pasta_ifn:		Index of namespace interface for pasta
@@ -197,7 +207,7 @@
 	int trace;
 	int quiet;
 	int foreground;
-	int stderr;
+	int force_stderr;
 	int nofile;
 	char sock_path[UNIX_PATH_MAX];
 	char pcap[PATH_MAX];
diff -Nru passt-0.0~git20230227.c538ee8/README.md passt-0.0~git20230309.7c7625d/README.md
--- passt-0.0~git20230227.c538ee8/README.md	2023-02-27 18:56:37.000000000 +0100
+++ passt-0.0~git20230309.7c7625d/README.md	2023-03-09 03:44:21.000000000 +0100
@@ -274,8 +274,10 @@
     * ✅ starting from 4.18 kernel version
     * ✅ starting from 3.13 kernel version
 * ✅ run-time selection of AVX2 build
-* ⌚ [_musl_](https://bugs.passt.top/show_bug.cgi?id=4) and
-  [_uClibc-ng_](https://bugs.passt.top/show_bug.cgi?id=5)
+* C libraries:
+    * ✅ glibc
+    * ✅ [_musl_](https://bugs.passt.top/show_bug.cgi?id=4)
+    * ⌚ [_uClibc-ng_](https://bugs.passt.top/show_bug.cgi?id=5)
 * ⌚ [FreeBSD](https://bugs.passt.top/show_bug.cgi?id=6),
   [Darwin](https://bugs.passt.top/show_bug.cgi?id=6)
 * ⌚ [NetBSD](https://bugs.passt.top/show_bug.cgi?id=7),
@@ -306,8 +308,8 @@
 * ✅ optional NAT, not required
 * ✅ all protocols
 * ✅ _pasta_: auto-detection of bound ports
-* 🛠 run-time configuration of port ranges without autodetection
-* 🛠 configuration of port ranges for autodetection
+* ⌚ run-time configuration of port ranges without autodetection
+* ⌚ configuration of port ranges for autodetection
 * 💡 [add](https://lists.passt.top/) [your](https://bugs.passt.top/)
   [ideas](https://chat.passt.top)
 
@@ -327,33 +329,37 @@
 * ⌚ eBPF support (might not improve performance over vhost-user)
 
 ### Interfaces
-* ✅ qemu, libvirt support with [`qrap` wrapper](/passt/tree/qrap.c)
-* ✅ out-of-tree patches for [qemu](/passt/tree/contrib/qemu) and
-  [libvirt](/passt/tree/contrib/libvirt) available
+* ✅ native [qemu](https://bugs.passt.top/show_bug.cgi?id=11) support (_passt_)
+* ✅ native [libvirt](https://bugs.passt.top/show_bug.cgi?id=12) support
+  (_passt_)
 * ✅ Podman [integration](https://github.com/containers/podman/pull/16141)
+  (_pasta_)
 * ✅ bug-to-bug compatible
   [_slirp4netns_ replacement](/passt/tree/slirp4netns.sh)
 * ✅ out-of-tree patch for
   [Kata Containers](/passt/tree/contrib/kata-containers) available
-* 🛠 native [qemu](https://bugs.passt.top/show_bug.cgi?id=11),
-  [libvirt](https://bugs.passt.top/show_bug.cgi?id=12) support
 * ⌚ drop-in replacement for VPNKit (rootless Docker)
 
 ### Availability
+* ✅ official [packages](https://gitlab.com/redhat/centos-stream/rpms/passt) for
+  CentOS Stream
 * ✅ official [packages](https://tracker.debian.org/pkg/passt) for Debian
 * ✅ official [packages](https://src.fedoraproject.org/rpms/passt) for Fedora
+* ✅ official [packages](https://packages.ubuntu.com/lunar/passt) for Ubuntu
+* ✅ unofficial [packages](https://aur.archlinux.org/packages/passt-git) for
+  Arch Linux
 * ✅ unofficial
-  [packages](https://copr.fedorainfracloud.org/coprs/sbrivio/passt/) for CentOS
-  Stream, EPEL, Mageia
+  [packages](https://copr.fedorainfracloud.org/coprs/sbrivio/passt/) for EPEL,
+  Mageia
+* 🛠 official
+  [packages](https://build.opensuse.org/package/show/Virtualization:containers/passt)
+  for openSUSE
 * ✅ unofficial [packages](https://passt.top/builds/latest/x86_64/) from x86_64
   static builds for other RPM-based distributions
 * ✅ unofficial [packages](https://passt.top/builds/latest/x86_64/) from x86_64
-  static builds for Debian-based distributions
+  static builds for other Debian-based distributions
 * ✅ testing on non-x86_64 architectures (aarch64, armv7l, i386, ppc64, ppc64le,
   s390x)
-* 🛠 official
-  [openSUSE packages](https://build.opensuse.org/package/show/home:dfaggioli:devel/passt)
-* ⌚ official packages for Arch Linux
 
 ### Services
 * ✅ built-in [ARP proxy](/passt/tree/arp.c)
@@ -534,16 +540,7 @@
         cd passt
         make
 
-    * alternatively, install one of the available packages:
-
-        * [Debian](https://tracker.debian.org/pkg/passt) (official)
-        * [Fedora](https://src.fedoraproject.org/rpms/passt) (official)
-        * [CentOS Stream](https://copr.fedorainfracloud.org/coprs/sbrivio/passt/) (unofficial)
-        * [EPEL](https://copr.fedorainfracloud.org/coprs/sbrivio/passt/) (unofficial)
-        * [Mageia](https://copr.fedorainfracloud.org/coprs/sbrivio/passt/) (unofficial)
-        * [openSUSE](https://build.opensuse.org/package/show/home:dfaggioli:devel/passt) (unofficial)
-        * [Debian-based](/builds/latest/x86_64/) (unofficial, from static x86_64 builds)
-        * [Other RPM-based distributions](/builds/latest/x86_64/) (unofficial, from static x86_64 builds)
+    * alternatively, install one of the [available packages](#availability)
 
         Static binaries and packages are simply built with:
 
@@ -558,17 +555,9 @@
 
         doc/demo.sh
 
-* alternatively, you can use libvirt, with
-  [this patch](/passt/tree/libvirt/0001-conf-Introduce-support-for-UNIX-domain-socket-as-qem.patch),
-  to start qemu (with [these patches](/passt/tree/contrib/qemu)), and 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>
+* alternatively, you can use
+  [libvirt](https://libvirt.org/formatdomain.html#userspace-slirp-or-passt-connection)
+  to start QEMU
 
 * and that's it, you should now have TCP connections, UDP, and ICMP/ICMPv6
   echo working from/to the guest for IPv4 and IPv6
@@ -584,16 +573,7 @@
         cd passt
         make
 
-    * alternatively, install one of the available packages:
-
-        * [Debian](https://tracker.debian.org/pkg/passt) (official)
-        * [Fedora](https://src.fedoraproject.org/rpms/passt) (official)
-        * [CentOS Stream](https://copr.fedorainfracloud.org/coprs/sbrivio/passt/) (unofficial)
-        * [EPEL](https://copr.fedorainfracloud.org/coprs/sbrivio/passt/) (unofficial)
-        * [Mageia](https://copr.fedorainfracloud.org/coprs/sbrivio/passt/) (unofficial)
-        * [openSUSE](https://build.opensuse.org/package/show/home:dfaggioli:devel/passt) (unofficial)
-        * [Debian-based](/builds/latest/x86_64/) (unofficial, from static x86_64 builds)
-        * [Other RPM-based distributions](/builds/latest/x86_64/) (unofficial, from static x86_64 builds)
+    * alternatively, install one of the [available packages](#availability)
 
         Static binaries and packages are simply built with:
 
@@ -655,7 +635,8 @@
 * ...or [file a bug](https://bugs.passt.top/enter_bug.cgi)
 
 ### [Chat](/passt/chat)
-* Somebody might be available on [IRC](https://irc.passt.top)
+* Somebody might be available on [IRC](https://irc.passt.top) on `#passt` at
+  [Libera.Chat](https://libera.chat/)
 
 ### Weekly development [meeting](https://pad.passt.top/p/weekly)
 * Open to everybody! Feel free to join and propose a different time directly on
diff -Nru passt-0.0~git20230227.c538ee8/seccomp.sh passt-0.0~git20230309.7c7625d/seccomp.sh
--- passt-0.0~git20230227.c538ee8/seccomp.sh	2023-02-27 18:56:37.000000000 +0100
+++ passt-0.0~git20230309.7c7625d/seccomp.sh	2023-03-09 03:44:21.000000000 +0100
@@ -17,6 +17,9 @@
 IN="$@"
 OUT="seccomp.h"
 
+[ -z "${ARCH}" ] && ARCH="$(uname -m)"
+[ -z "${CC}" ] && CC="cc"
+
 HEADER="/* This file was automatically generated by $(basename ${0}) */
 
 #ifndef AUDIT_ARCH_PPC64LE
@@ -110,7 +113,7 @@
 # $1:	Name of syscall
 syscall_nr() {
 	__in="$(printf "#include <asm-generic/unistd.h>\n#include <sys/syscall.h>\n__NR_%s" ${1})"
-	__out="$(echo "${__in}" | cc -E -xc - -o - | tail -1)"
+	__out="$(echo "${__in}" | ${CC} -E -xc - -o - | tail -1)"
 	[ "${__out}" = "__NR_$1" ] && return 1
 
 	# Output might be in the form "(x + y)" (seen on armv6l, armv7l)
@@ -125,7 +128,7 @@
 		case ${__c} in
 		*:*)
 			case ${__c} in
-			$(uname -m):*)
+			${ARCH}:*)
 				__arch_match=1
 				__c=${__c##*:}
 				;;
diff -Nru passt-0.0~git20230227.c538ee8/tap.c passt-0.0~git20230309.7c7625d/tap.c
--- passt-0.0~git20230227.c538ee8/tap.c	2023-02-27 18:56:37.000000000 +0100
+++ passt-0.0~git20230309.7c7625d/tap.c	2023-03-09 03:44:21.000000000 +0100
@@ -14,6 +14,7 @@
  */
 
 #include <sched.h>
+#include <signal.h>
 #include <stdio.h>
 #include <errno.h>
 #include <limits.h>
diff -Nru passt-0.0~git20230227.c538ee8/tcp.c passt-0.0~git20230309.7c7625d/tcp.c
--- passt-0.0~git20230227.c538ee8/tcp.c	2023-02-27 18:56:37.000000000 +0100
+++ passt-0.0~git20230309.7c7625d/tcp.c	2023-03-09 03:44:21.000000000 +0100
@@ -267,6 +267,7 @@
 #include <sched.h>
 #include <fcntl.h>
 #include <stdio.h>
+#include <signal.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <limits.h>
@@ -460,7 +461,7 @@
 	struct iphdr iph;	/* 44				28 */
 	struct tcphdr th;	/* 64				48 */
 	uint8_t data[MSS4];	/* 84				68 */
-				/* 65541			65525 */
+				/* 65536			65532 */
 #ifdef __AVX2__
 } __attribute__ ((packed, aligned(32)))
 #else
@@ -488,7 +489,7 @@
 	struct ipv6hdr ip6h;	/* 32				20 */
 	struct tcphdr th;	/* 72				60 */
 	uint8_t data[MSS6];	/* 92				80 */
-				/* 65639			65627 */
+				/* 65536			65532 */
 #ifdef __AVX2__
 } __attribute__ ((packed, aligned(32)))
 #else
@@ -1916,15 +1917,13 @@
 
 /**
  * tcp_conn_tap_mss() - Get MSS value advertised by tap/guest
- * @c:		Execution context
  * @conn:	Connection pointer
  * @opts:	Pointer to start of TCP options
  * @optlen:	Bytes in options: caller MUST ensure available length
  *
  * Return: clamped MSS value
  */
-static uint16_t tcp_conn_tap_mss(const struct ctx *c,
-				 const struct tcp_tap_conn *conn,
+static uint16_t tcp_conn_tap_mss(const struct tcp_tap_conn *conn,
 				 const char *opts, size_t optlen)
 {
 	unsigned int mss;
@@ -1935,18 +1934,70 @@
 	else
 		mss = ret;
 
-	/* Don't upset qemu */
-	if (c->mode == MODE_PASST) {
-		if (CONN_V4(conn))
-			mss = MIN(MSS4, mss);
-		else
-			mss = MIN(MSS6, mss);
-	}
+	if (CONN_V4(conn))
+		mss = MIN(MSS4, mss);
+	else
+		mss = MIN(MSS6, mss);
 
 	return MIN(mss, USHRT_MAX);
 }
 
 /**
+ * tcp_bind_outbound() - Bind socket to outbound address and interface if given
+ * @c:		Execution context
+ * @s:		Outbound TCP socket
+ * @af:		Address family
+ */
+static void tcp_bind_outbound(const struct ctx *c, int s, sa_family_t af)
+{
+	if (af == AF_INET) {
+		if (!IN4_IS_ADDR_UNSPECIFIED(&c->ip4.addr_out)) {
+			struct sockaddr_in addr4 = {
+				.sin_family = AF_INET,
+				.sin_port = 0,
+				.sin_addr = c->ip4.addr_out,
+			};
+
+			if (bind(s, (struct sockaddr *)&addr4, sizeof(addr4))) {
+				debug("Can't bind IPv4 TCP socket address: %s",
+				      strerror(errno));
+			}
+		}
+
+		if (*c->ip4.ifname_out) {
+			if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE,
+				       c->ip4.ifname_out,
+				       strlen(c->ip4.ifname_out))) {
+				debug("Can't bind IPv4 TCP socket to interface:"
+				      " %s", strerror(errno));
+			}
+		}
+	} else if (af == AF_INET6) {
+		if (!IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addr_out)) {
+			struct sockaddr_in6 addr6 = {
+				.sin6_family = AF_INET6,
+				.sin6_port = 0,
+				.sin6_addr = c->ip6.addr_out,
+			};
+
+			if (bind(s, (struct sockaddr *)&addr6, sizeof(addr6))) {
+				debug("Can't bind IPv6 TCP socket address: %s",
+				      strerror(errno));
+			}
+		}
+
+		if (*c->ip6.ifname_out) {
+			if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE,
+				       c->ip6.ifname_out,
+				       strlen(c->ip6.ifname_out))) {
+				debug("Can't bind IPv6 TCP socket to interface:"
+				      " %s", strerror(errno));
+			}
+		}
+	}
+}
+
+/**
  * tcp_conn_from_tap() - Handle connection request (SYN segment) from tap
  * @c:		Execution context
  * @af:		Address family, AF_INET or AF_INET6
@@ -2010,7 +2061,7 @@
 
 	conn->wnd_to_tap = WINDOW_DEFAULT;
 
-	mss = tcp_conn_tap_mss(c, conn, opts, optlen);
+	mss = tcp_conn_tap_mss(conn, opts, optlen);
 	if (setsockopt(s, SOL_TCP, TCP_MAXSEG, &mss, sizeof(mss)))
 		trace("TCP: failed to set TCP_MAXSEG on socket %i", s);
 	MSS_SET(conn, mss);
@@ -2052,6 +2103,11 @@
 	if (errno != EADDRNOTAVAIL && errno != EACCES)
 		conn_flag(c, conn, LOCAL);
 
+	if ((af == AF_INET &&  !IN4_IS_ADDR_LOOPBACK(&addr4.sin_addr)) ||
+	    (af == AF_INET6 && !IN6_IS_ADDR_LOOPBACK(&addr6.sin6_addr) &&
+			       !IN6_IS_ADDR_LINKLOCAL(&addr6.sin6_addr)))
+		tcp_bind_outbound(c, s, af);
+
 	if (connect(s, sa, sl)) {
 		if (errno != EINPROGRESS) {
 			tcp_rst(c, conn);
@@ -2472,7 +2528,7 @@
 	if (!(conn->wnd_from_tap >>= conn->ws_from_tap))
 		conn->wnd_from_tap = 1;
 
-	MSS_SET(conn, tcp_conn_tap_mss(c, conn, opts, optlen));
+	MSS_SET(conn, tcp_conn_tap_mss(conn, opts, optlen));
 
 	conn->seq_init_from_tap = ntohl(th->seq) + 1;
 	conn->seq_from_tap = conn->seq_init_from_tap;
@@ -2894,7 +2950,7 @@
  * @addr:	Pointer to address for binding, NULL if not configured
  * @ifname:	Name of interface to bind to, NULL if not configured
  *
- * Return: fd for the new listening socket, or -1 on failure
+ * Return: fd for the new listening socket, negative error code on failure
  */
 static int tcp_sock_init_af(const struct ctx *c, int af, in_port_t port,
 			    const struct in_addr *addr, const char *ifname)
@@ -2907,13 +2963,13 @@
 
 	if (c->tcp.fwd_in.mode == FWD_AUTO) {
 		if (af == AF_INET  || af == AF_UNSPEC)
-			tcp_sock_init_ext[port][V4] = s;
+			tcp_sock_init_ext[port][V4] = s < 0 ? -1 : s;
 		if (af == AF_INET6 || af == AF_UNSPEC)
-			tcp_sock_init_ext[port][V6] = s;
+			tcp_sock_init_ext[port][V6] = s < 0 ? -1 : s;
 	}
 
 	if (s < 0)
-		return -1;
+		return s;
 
 	tcp_sock_set_bufsize(c, s);
 	return s;
@@ -2927,12 +2983,12 @@
  * @ifname:	Name of interface to bind to, NULL if not configured
  * @port:	Port, host order
  *
- * Return: 0 on (partial) success, -1 on (complete) failure
+ * Return: 0 on (partial) success, negative error code on (complete) failure
  */
 int tcp_sock_init(const struct ctx *c, sa_family_t af, const void *addr,
 		  const char *ifname, in_port_t port)
 {
-	int ret = 0;
+	int r4 = SOCKET_MAX + 1, r6 = SOCKET_MAX + 1;
 
 	if (af == AF_UNSPEC && c->ifi4 && c->ifi6)
 		/* Attempt to get a dual stack socket */
@@ -2940,17 +2996,16 @@
 			return 0;
 
 	/* Otherwise create a socket per IP version */
-	if ((af == AF_INET  || af == AF_UNSPEC) && c->ifi4) {
-		if (tcp_sock_init_af(c, AF_INET, port, addr, ifname) < 0)
-			ret = -1;
-	}
+	if ((af == AF_INET  || af == AF_UNSPEC) && c->ifi4)
+		r4 = tcp_sock_init_af(c, AF_INET, port, addr, ifname);
 
-	if ((af == AF_INET6 || af == AF_UNSPEC) && c->ifi6) {
-		if (tcp_sock_init_af(c, AF_INET6, port, addr, ifname) < 0)
-			ret = -1;
-	}
+	if ((af == AF_INET6 || af == AF_UNSPEC) && c->ifi6)
+		r6 = tcp_sock_init_af(c, AF_INET6, port, addr, ifname);
 
-	return ret;
+	if (IN_INTERVAL(0, SOCKET_MAX, r4) || IN_INTERVAL(0, SOCKET_MAX, r6))
+		return 0;
+
+	return r4 < 0 ? r4 : r6;
 }
 
 /**
diff -Nru passt-0.0~git20230227.c538ee8/tcp_splice.c passt-0.0~git20230309.7c7625d/tcp_splice.c
--- passt-0.0~git20230227.c538ee8/tcp_splice.c	2023-02-27 18:56:37.000000000 +0100
+++ passt-0.0~git20230309.7c7625d/tcp_splice.c	2023-03-09 03:44:21.000000000 +0100
@@ -32,6 +32,7 @@
  */
 
 #include <sched.h>
+#include <signal.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <limits.h>
diff -Nru passt-0.0~git20230227.c538ee8/udp.c passt-0.0~git20230309.7c7625d/udp.c
--- passt-0.0~git20230227.c538ee8/udp.c	2023-02-27 18:56:37.000000000 +0100
+++ passt-0.0~git20230309.7c7625d/udp.c	2023-03-09 03:44:21.000000000 +0100
@@ -91,6 +91,7 @@
  */
 
 #include <sched.h>
+#include <signal.h>
 #include <stdio.h>
 #include <errno.h>
 #include <limits.h>
@@ -754,7 +755,7 @@
 	in_port_t dstport = ref.r.p.udp.udp.port;
 	bool v6 = ref.r.p.udp.udp.v6;
 	struct mmsghdr *mmh_recv;
-	unsigned int i, m;
+	int i, m;
 
 	if (!(events & EPOLLIN))
 		return;
@@ -843,20 +844,6 @@
 		sa = (struct sockaddr *)&s_in;
 		sl = sizeof(s_in);
 
-		if (!(s = udp_tap_map[V4][src].sock)) {
-			union udp_epoll_ref uref = { .udp.port = src };
-
-			s = sock_l4(c, AF_INET, IPPROTO_UDP, NULL, NULL, src,
-				    uref.u32);
-			if (s < 0)
-				return p->count;
-
-			udp_tap_map[V4][src].sock = s;
-			bitmap_set(udp_act[V4][UDP_ACT_TAP], src);
-		}
-
-		udp_tap_map[V4][src].ts = now->tv_sec;
-
 		if (IN4_ARE_ADDR_EQUAL(&s_in.sin_addr, &c->ip4.dns_match) &&
 		    ntohs(s_in.sin_port) == 53) {
 			s_in.sin_addr = c->ip4.dns_host;
@@ -868,13 +855,37 @@
 			else
 				s_in.sin_addr = c->ip4.addr_seen;
 		}
+
+		if (!(s = udp_tap_map[V4][src].sock)) {
+			union udp_epoll_ref uref = { .udp.port = src };
+			in_addr_t bind_addr = { 0 };
+			const char *bind_if = NULL;
+
+			if (!IN6_IS_ADDR_LOOPBACK(&s_in.sin_addr) &&
+			    *c->ip6.ifname_out)
+				bind_if = c->ip6.ifname_out;
+
+			if (!IN4_IS_ADDR_UNSPECIFIED(&c->ip4.addr_out) &&
+			    !IN4_IS_ADDR_LOOPBACK(&s_in.sin_addr))
+				bind_addr = c->ip4.addr_out.s_addr;
+
+			s = sock_l4(c, AF_INET, IPPROTO_UDP, &bind_addr,
+				    bind_if, src, uref.u32);
+			if (s < 0)
+				return p->count;
+
+			udp_tap_map[V4][src].sock = s;
+			bitmap_set(udp_act[V4][UDP_ACT_TAP], src);
+		}
+
+		udp_tap_map[V4][src].ts = now->tv_sec;
 	} else {
 		s_in6 = (struct sockaddr_in6) {
 			.sin6_family = AF_INET6,
 			.sin6_port = uh->dest,
 			.sin6_addr = *(struct in6_addr *)addr,
 		};
-		const void *bind_addr = &in6addr_any;
+		const struct in6_addr *bind_addr = &in6addr_any;
 
 		sa = (struct sockaddr *)&s_in6;
 		sl = sizeof(s_in6);
@@ -898,9 +909,19 @@
 		if (!(s = udp_tap_map[V6][src].sock)) {
 			union udp_epoll_ref uref = { .udp.v6 = 1,
 						     .udp.port = src };
+			const char *bind_if = NULL;
+
+			if (!IN6_IS_ADDR_LOOPBACK(&s_in6.sin6_addr) &&
+			    *c->ip6.ifname_out)
+				bind_if = c->ip6.ifname_out;
+
+			if (!IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addr_out) &&
+			    !IN6_IS_ADDR_LOOPBACK(&s_in6.sin6_addr) &&
+			    !IN6_IS_ADDR_LINKLOCAL(&s_in6.sin6_addr))
+				bind_addr = &c->ip6.addr_out;
 
-			s = sock_l4(c, AF_INET6, IPPROTO_UDP, bind_addr, NULL,
-				    src, uref.u32);
+			s = sock_l4(c, AF_INET6, IPPROTO_UDP, bind_addr,
+				    bind_if, src, uref.u32);
 			if (s < 0)
 				return p->count;
 
@@ -956,13 +977,13 @@
  * @ifname:	Name of interface to bind to, NULL if not configured
  * @port:	Port, host order
  *
- * Return: 0 on (partial) success, -1 on (complete) failure
+ * Return: 0 on (partial) success, negative error code on (complete) failure
  */
 int udp_sock_init(const struct ctx *c, int ns, sa_family_t af,
 		  const void *addr, const char *ifname, in_port_t port)
 {
 	union udp_epoll_ref uref = { .u32 = 0 };
-	int s, ret = 0;
+	int s, r4 = SOCKET_MAX + 1, r6 = SOCKET_MAX + 1;
 
 	if (ns) {
 		uref.udp.port = (in_port_t)(port +
@@ -978,22 +999,19 @@
 		uref.udp.orig = true;
 
 		if (!ns) {
-			s = sock_l4(c, AF_INET, IPPROTO_UDP, addr, ifname,
-				    port, uref.u32);
+			r4 = s = sock_l4(c, AF_INET, IPPROTO_UDP, addr,
+					 ifname, port, uref.u32);
 
-			udp_tap_map[V4][uref.udp.port].sock = s;
-			udp_splice_init[V4][port].sock = s;
+			udp_tap_map[V4][uref.udp.port].sock = s < 0 ? -1 : s;
+			udp_splice_init[V4][port].sock = s < 0 ? -1 : s;
 		} else {
 			struct in_addr loopback = { htonl(INADDR_LOOPBACK) };
 			uref.udp.ns = true;
 
-			s = sock_l4(c, AF_INET, IPPROTO_UDP, &loopback,
-				    ifname, port, uref.u32);
-			udp_splice_ns[V4][port].sock = s;
+			r4 = s = sock_l4(c, AF_INET, IPPROTO_UDP, &loopback,
+					 ifname, port, uref.u32);
+			udp_splice_ns[V4][port].sock = s < 0 ? -1 : s;
 		}
-
-		if (s < 0)
-			ret = -1;
 	}
 
 	if ((af == AF_INET6 || af == AF_UNSPEC) && c->ifi6) {
@@ -1002,24 +1020,25 @@
 		uref.udp.orig = true;
 
 		if (!ns) {
-			s = sock_l4(c, AF_INET6, IPPROTO_UDP, addr, ifname,
-				    port, uref.u32);
+			r6 = s = sock_l4(c, AF_INET6, IPPROTO_UDP, addr,
+					 ifname, port, uref.u32);
 
-			udp_tap_map[V6][uref.udp.port].sock = s;
-			udp_splice_init[V6][port].sock = s;
+			udp_tap_map[V6][uref.udp.port].sock = s < 0 ? -1 : s;
+			udp_splice_init[V6][port].sock = s < 0 ? -1 : s;
 		} else {
 			uref.udp.ns = true;
 
-			s = sock_l4(c, AF_INET6, IPPROTO_UDP, &in6addr_loopback,
-				    ifname, port, uref.u32);
-			udp_splice_ns[V6][port].sock = s;
+			r6 = s = sock_l4(c, AF_INET6, IPPROTO_UDP,
+					 &in6addr_loopback,
+					 ifname, port, uref.u32);
+			udp_splice_ns[V6][port].sock = s < 0 ? -1 : s;
 		}
-
-		if (s < 0)
-			ret = -1;
 	}
 
-	return ret;
+	if (IN_INTERVAL(0, SOCKET_MAX, r4) || IN_INTERVAL(0, SOCKET_MAX, r6))
+		return 0;
+
+	return r4 < 0 ? r4 : r6;
 }
 
 /**
diff -Nru passt-0.0~git20230227.c538ee8/util.c passt-0.0~git20230309.7c7625d/util.c
--- passt-0.0~git20230227.c538ee8/util.c	2023-02-27 18:56:37.000000000 +0100
+++ passt-0.0~git20230309.7c7625d/util.c	2023-03-09 03:44:21.000000000 +0100
@@ -13,6 +13,7 @@
  */
 
 #include <sched.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <arpa/inet.h>
@@ -95,7 +96,7 @@
  * @port:	Port, host order
  * @data:	epoll reference portion for protocol handlers
  *
- * Return: newly created socket, -1 on error
+ * Return: newly created socket, negative error code on failure
  */
 int sock_l4(const struct ctx *c, int af, uint8_t proto,
 	    const void *bind_addr, const char *ifname, uint16_t port,
@@ -114,16 +115,16 @@
 	};
 	const struct sockaddr *sa;
 	bool dual_stack = false;
+	int fd, sl, y = 1, ret;
 	struct epoll_event ev;
-	int fd, sl, y = 1;
 
 	if (proto != IPPROTO_TCP && proto != IPPROTO_UDP &&
 	    proto != IPPROTO_ICMP && proto != IPPROTO_ICMPV6)
-		return -1;	/* Not implemented. */
+		return -EPFNOSUPPORT;	/* Not implemented. */
 
 	if (af == AF_UNSPEC) {
 		if (!DUAL_STACK_SOCKETS || bind_addr)
-			return -1;
+			return -EINVAL;
 		dual_stack = true;
 		af = AF_INET6;
 	}
@@ -133,14 +134,15 @@
 	else
 		fd = socket(af, SOCK_DGRAM | SOCK_NONBLOCK, proto);
 
+	ret = -errno;
 	if (fd < 0) {
-		warn("L4 socket: %s", strerror(errno));
-		return -1;
+		warn("L4 socket: %s", strerror(-ret));
+		return ret;
 	}
 
 	if (fd > SOCKET_MAX) {
 		close(fd);
-		return -1;
+		return -EBADF;
 	}
 
 	ref.r.s = fd;
@@ -185,10 +187,11 @@
 		 */
 		if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,
 			       ifname, strlen(ifname))) {
+			ret = -errno;
 			warn("Can't bind socket for %s port %u to %s, closing",
 			     ip_proto_str[proto], port, ifname);
 			close(fd);
-			return -1;
+			return ret;
 		}
 	}
 
@@ -199,22 +202,25 @@
 		 * broken SELinux policy, see icmp_tap_handler().
 		 */
 		if (proto != IPPROTO_ICMP && proto != IPPROTO_ICMPV6) {
+			ret = -errno;
 			close(fd);
-			return -1;
+			return ret;
 		}
 	}
 
 	if (proto == IPPROTO_TCP && listen(fd, 128) < 0) {
-		warn("TCP socket listen: %s", strerror(errno));
+		ret = -errno;
+		warn("TCP socket listen: %s", strerror(-ret));
 		close(fd);
-		return -1;
+		return ret;
 	}
 
 	ev.events = EPOLLIN;
 	ev.data.u64 = ref.u64;
 	if (epoll_ctl(c->epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) {
-		warn("L4 epoll_ctl: %s", strerror(errno));
-		return -1;
+		ret = -errno;
+		warn("L4 epoll_ctl: %s", strerror(-ret));
+		return ret;
 	}
 
 	return fd;
diff -Nru passt-0.0~git20230227.c538ee8/util.h passt-0.0~git20230309.7c7625d/util.h
--- passt-0.0~git20230227.c538ee8/util.h	2023-02-27 18:56:37.000000000 +0100
+++ passt-0.0~git20230309.7c7625d/util.h	2023-03-09 03:44:21.000000000 +0100
@@ -88,6 +88,17 @@
 #define MAC_ZERO		((uint8_t [ETH_ALEN]){ 0 })
 #define MAC_IS_ZERO(addr)	(!memcmp((addr), MAC_ZERO, ETH_ALEN))
 
+#ifndef __bswap_constant_16
+#define __bswap_constant_16(x)						\
+	((uint16_t) ((((x) >> 8) & 0xff) | (((x) & 0xff) << 8)))
+#endif
+
+#ifndef __bswap_constant_32
+#define __bswap_constant_32(x)						\
+	((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >>  8) |	\
+	 (((x) & 0x0000ff00) <<  8) | (((x) & 0x000000ff) << 24))
+#endif
+
 #if __BYTE_ORDER == __BIG_ENDIAN
 #define	htons_constant(x)	(x)
 #define	htonl_constant(x)	(x)

--- End Message ---
--- Begin Message ---
Hi,

On 14-03-2023 22:44, Stefano Brivio wrote:
Sorry, I think I misinterpreted the freeze policy (I'm a new
maintainer), I thought the hard freeze would block migrations of
uploads done *after* the start of the freeze, and not affect
migrations already in the migration delay phase, so I didn't plan
accordingly.

Please learn from this and don't do it again :).

unblocked

Paul

Attachment: OpenPGP_signature
Description: OpenPGP digital signature


--- End Message ---

Reply to: