public inbox for passt-dev@passt.top
 help / color / mirror / code / Atom feed
From: Paul Holzinger <pholzing@redhat.com>
To: Yumei Huang <yuhuang@redhat.com>,
	passt-dev@passt.top, sbrivio@redhat.com
Cc: david@gibson.dropbear.id.au
Subject: Re: [PATCH] conf, pasta: Add --no-tap option
Date: Mon, 5 Jan 2026 14:48:15 +0100	[thread overview]
Message-ID: <e6ea3c46-08d7-49ea-b78a-40d6bbb302bd@redhat.com> (raw)
In-Reply-To: <20251229095558.918055-1-yuhuang@redhat.com>

Sorry I was out for a while so I didn't had time to clarify on the bug 
before.

On 29/12/2025 10:55, Yumei Huang wrote:
> This patch introduces a mode where we only forward loopback connections
> and traffic between two namespaces (via the loopback interface, 'lo'),
> without a tap device.
>
> With this, podman can support forwarding ::1 in custom networks when using
> rootlesskit for forwarding ports.

I guess I didn't really communicate my requirements well. When we use 
rootlessport (rootlesskit) today for custom networks we only do so as 
rootless user and it forwards ::1 (by possibly mapping this to v4 inside 
the container) fine.

My main point for this feature was using as root (requires further 
changes to allow pasta running as root). Because as root podman does 
port forwarding via DNAT firewall rules (i.e. custom nftables rules we 
add). The kernel however never added support for DNAT on ::1 meaning 
clients trying to access that are not getting forwarded. The only way to 
support this is using a user space helper. Right now this doesn't work 
and we do not use rootlessport for this either so I was just thinking 
ahead because we do have these users requests who want ::1 to work as root.

For the current rootlessport use case we also must bind all ports as 
given (i.e. also addresses 0.0.0.0 bind address), just forwarding 
loopback to loopback is not what we want or do for security reasons, see 
CVE-2021-20199. And logically it would not really work to have another 
process bind 0.0.0.0 and this pasta helper bind lo on the same port at 
the same time.

The way I am thinking is bind ports as normal, add the no-tap option and 
add two options to give the v4 and v6 namespace (container) side connect 
addresses so we never actually connect to lo. Then we also should have a 
dynamic way to update the connect addresses at runtime which is required 
for podman network connect/disconnect to work which changes the 
addresses inside the namespace, see 
https://github.com/containers/podman/commit/e88d8dbeae2aebd2d816f16a21891764163afcd4.

Overall none of this is a blocker for removing rootlessport. I think our 
plan was and still is to use the dynamic port forwarding logic David is 
working on to replace the rootless custom network port forwarding case 
with that.

>
> In --no-tap mode, --host-lo-to-ns-lo, --no-icmp and --no-ra is automatically
> enabled. Options requiring a tap device (--ns-ifname, --ns-mac-addr,
> --config-net, --outbound-if4/6) are rejected.
>
> Link: https://bugs.passt.top/show_bug.cgi?id=149
> Signed-off-by: Yumei Huang <yuhuang@redhat.com>
> ---
>   conf.c  | 56 +++++++++++++++++++++++++++++++++++++++++---------------
>   fwd.c   |  3 +++
>   passt.1 |  5 +++++
>   passt.h |  2 ++
>   pasta.c |  3 +++
>   tap.c   | 11 +++++++----
>   6 files changed, 61 insertions(+), 19 deletions(-)
>
> diff --git a/conf.c b/conf.c
> index 84ae12b..353d0a5 100644
> --- a/conf.c
> +++ b/conf.c
> @@ -1049,7 +1049,8 @@ pasta_opts:
>   		"  --no-copy-addrs	DEPRECATED:\n"
>   		"			Don't copy all addresses to namespace\n"
>   		"  --ns-mac-addr ADDR	Set MAC address on tap interface\n"
> -		"  --no-splice		Disable inbound socket splicing\n");
> +		"  --no-splice		Disable inbound socket splicing\n"
> +		"  --no-tap		Don't create tap device\n");
>   
>   	passt_exit(status);
>   }
> @@ -1451,6 +1452,7 @@ void conf(struct ctx *c, int argc, char **argv)
>   		{"no-ndp",	no_argument,		&c->no_ndp,	1 },
>   		{"no-ra",	no_argument,		&c->no_ra,	1 },
>   		{"no-splice",	no_argument,		&c->no_splice,	1 },
> +		{"no-tap",	no_argument,		&c->no_tap,	1 },
>   		{"freebind",	no_argument,		&c->freebind,	1 },
>   		{"no-map-gw",	no_argument,		&no_map_gw,	1 },
>   		{"ipv4-only",	no_argument,		NULL,		'4' },
> @@ -1947,8 +1949,11 @@ void conf(struct ctx *c, int argc, char **argv)
>   		}
>   	} while (name != -1);
>   
> -	if (c->mode != MODE_PASTA)
> +	if (c->mode != MODE_PASTA) {
>   		c->no_splice = 1;
> +		if (c->no_tap)
> +			die("--no-tap is for pasta mode only");
> +	}
>   
>   	if (c->mode == MODE_PASTA && !c->pasta_conf_ns) {
>   		if (copy_routes_opt)
> @@ -1957,6 +1962,25 @@ void conf(struct ctx *c, int argc, char **argv)
>   			die("--no-copy-addrs needs --config-net");
>   	}
>   
> +	if (c->mode == MODE_PASTA && c->no_tap) {
> +		if (c->no_splice)
> +			die("--no-tap is incompatible with --no-splice");
> +		if (*c->ip4.ifname_out || *c->ip6.ifname_out)
> +			die("--no-tap is incompatible with --outbound-if4/6");
> +		if (*c->pasta_ifn)
> +			die("--no-tap is incompatible with --ns-ifname");
> +		if (*c->guest_mac)
> +			die("--no-tap is incompatible with --ns-mac-addr");
> +		if (c->pasta_conf_ns)
> +			die("--no-tap is incompatible with --config-net");
> +
> +		c->host_lo_to_ns_lo = 1;
> +		c->no_icmp = 1;
> +		c->no_ra = 1;
> +		c->no_dns = 1;
> +		c->no_dns_search = 1;
> +	}
> +
>   	if (!ifi4 && *c->ip4.ifname_out)
>   		ifi4 = if_nametoindex(c->ip4.ifname_out);
>   
> @@ -1980,9 +2004,9 @@ void conf(struct ctx *c, int argc, char **argv)
>   	log_conf_parsed = true;		/* Stop printing everything */
>   
>   	nl_sock_init(c, false);
> -	if (!v6_only)
> +	if (!v6_only && !c->no_tap)
>   		c->ifi4 = conf_ip4(ifi4, &c->ip4);
> -	if (!v4_only)
> +	if (!v4_only && !c->no_tap)
>   		c->ifi6 = conf_ip6(ifi6, &c->ip6);
>   
>   	if (c->ifi4 && c->mtu < IPV4_MIN_MTU) {
> @@ -1998,30 +2022,32 @@ void conf(struct ctx *c, int argc, char **argv)
>   	    (*c->ip6.ifname_out && !c->ifi6))
>   		die("External interface not usable");
>   
> -	if (!c->ifi4 && !c->ifi6 && !*c->pasta_ifn) {
> +	if (!c->ifi4 && !c->ifi6 && !*c->pasta_ifn && !c->no_tap) {
>   		strncpy(c->pasta_ifn, pasta_default_ifn,
>   			sizeof(c->pasta_ifn) - 1);
>   	}
>   
>   	if (!c->ifi4 && !v6_only) {
> -		info("IPv4: no external interface as template, use local mode");
> -
> -		conf_ip4_local(&c->ip4);
> +		if (!c->no_tap) {
> +			info("IPv4: no external interface as template, use local mode");
> +			conf_ip4_local(&c->ip4);
> +		}
>   		c->ifi4 = -1;
>   	}
>   
>   	if (!c->ifi6 && !v4_only) {
> -		info("IPv6: no external interface as template, use local mode");
> -
> -		conf_ip6_local(&c->ip6);
> +		if (!c->no_tap) {
> +			info("IPv6: no external interface as template, use local mode");
> +			conf_ip6_local(&c->ip6);
> +		}
>   		c->ifi6 = -1;
>   	}
>   
> -	if (c->ifi4 && !no_map_gw &&
> +	if (c->ifi4 > 0 && !no_map_gw &&
>   	    IN4_IS_ADDR_UNSPECIFIED(&c->ip4.map_host_loopback))
>   		c->ip4.map_host_loopback = c->ip4.guest_gw;
>   
> -	if (c->ifi6 && !no_map_gw &&
> +	if (c->ifi6 > 0 && !no_map_gw &&
>   	    IN6_IS_ADDR_UNSPECIFIED(&c->ip6.map_host_loopback))
>   		c->ip6.map_host_loopback = c->ip6.guest_gw;
>   
> @@ -2116,10 +2142,10 @@ void conf(struct ctx *c, int argc, char **argv)
>   			conf_ports(c, name, optarg, &c->udp.fwd_out);
>   	} while (name != -1);
>   
> -	if (!c->ifi4)
> +	if (c->ifi4 <= 0)
>   		c->no_dhcp = 1;
>   
> -	if (!c->ifi6) {
> +	if (c->ifi6 <= 0) {
>   		c->no_ndp = 1;
>   		c->no_dhcpv6 = 1;
>   	} else if (IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addr)) {
> diff --git a/fwd.c b/fwd.c
> index 44a0e10..2f4a89a 100644
> --- a/fwd.c
> +++ b/fwd.c
> @@ -780,6 +780,9 @@ uint8_t fwd_nat_from_host(const struct ctx *c, uint8_t proto,
>   		return PIF_SPLICE;
>   	}
>   
> +	if (c->no_tap)
> +		return PIF_NONE;
> +
>   	if (!nat_inbound(c, &ini->eaddr, &tgt->oaddr)) {
>   		if (inany_v4(&ini->eaddr)) {
>   			if (IN4_IS_ADDR_UNSPECIFIED(&c->ip4.our_tap_addr))
> diff --git a/passt.1 b/passt.1
> index db0d662..2d643f7 100644
> --- a/passt.1
> +++ b/passt.1
> @@ -755,6 +755,11 @@ Default is to let the tap driver build a pseudorandom hardware address.
>   Disable the bypass path for inbound, local traffic. See the section \fBHandling
>   of local traffic in pasta\fR in the \fBNOTES\fR for more details.
>   
> +.TP
> +.BR \-\-no-tap
> +Do not create a tap device in the namespace. In this mode, only local loopback
> +traffic between namespaces is forwarded using splice.
> +
>   .SH EXAMPLES
>   
>   .SS \fBpasta
> diff --git a/passt.h b/passt.h
> index 79d01dd..0c1ec4c 100644
> --- a/passt.h
> +++ b/passt.h
> @@ -200,6 +200,7 @@ struct ip6_ctx {
>    * @no_ndp:		Disable NDP handler altogether
>    * @no_ra:		Disable router advertisements
>    * @no_splice:		Disable socket splicing for inbound traffic
> + * @no_tap:		Do not create tap device
>    * @host_lo_to_ns_lo:	Map host loopback addresses to ns loopback addresses
>    * @freebind:		Allow binding of non-local addresses for forwarding
>    * @low_wmem:		Low probed net.core.wmem_max
> @@ -277,6 +278,7 @@ struct ctx {
>   	int no_ndp;
>   	int no_ra;
>   	int no_splice;
> +	int no_tap;
>   	int host_lo_to_ns_lo;
>   	int freebind;
>   
> diff --git a/pasta.c b/pasta.c
> index 0ddd6b0..3510ec5 100644
> --- a/pasta.c
> +++ b/pasta.c
> @@ -316,6 +316,9 @@ void pasta_ns_conf(struct ctx *c)
>   		die("Couldn't bring up loopback interface in namespace: %s",
>   		    strerror_(-rc));
>   
> +	if (c->no_tap)
> +		return;
> +
>   	/* Get or set MAC in target namespace */
>   	if (MAC_IS_ZERO(c->guest_mac))
>   		nl_link_get_mac(nl_sock_ns, c->pasta_ifi, c->guest_mac);
> diff --git a/tap.c b/tap.c
> index 9d1344b..9b4eedc 100644
> --- a/tap.c
> +++ b/tap.c
> @@ -1491,13 +1491,16 @@ static int tap_ns_tun(void *arg)
>    */
>   static void tap_sock_tun_init(struct ctx *c)
>   {
> -	NS_CALL(tap_ns_tun, c);
> -	if (c->fd_tap == -1)
> -		die("Failed to set up tap device in namespace");
> +	if (!c->no_tap) {
> +		NS_CALL(tap_ns_tun, c);
> +		if (c->fd_tap == -1)
> +			die("Failed to set up tap device in namespace");
> +	}
>   
>   	pasta_ns_conf(c);
>   
> -	tap_start_connection(c);
> +	if (!c->no_tap)
> +		tap_start_connection(c);
>   }
>   
>   /**

-- 
Paul Holzinger


  parent reply	other threads:[~2026-01-05 13:48 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-12-29  9:55 Yumei Huang
2025-12-31 15:07 ` Stefano Brivio
2026-01-05  4:18 ` David Gibson
2026-01-05  8:53   ` Yumei Huang
2026-01-05 13:48 ` Paul Holzinger [this message]
2026-01-05 21:10   ` Stefano Brivio
2026-01-07 15:20     ` Paul Holzinger

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=e6ea3c46-08d7-49ea-b78a-40d6bbb302bd@redhat.com \
    --to=pholzing@redhat.com \
    --cc=david@gibson.dropbear.id.au \
    --cc=passt-dev@passt.top \
    --cc=sbrivio@redhat.com \
    --cc=yuhuang@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://passt.top/passt

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for IMAP folder(s).