public inbox for passt-dev@passt.top
 help / color / mirror / code / Atom feed
From: Stefano Brivio <sbrivio@redhat.com>
To: Yumei Huang <yuhuang@redhat.com>
Cc: David Gibson <david@gibson.dropbear.id.au>, passt-dev@passt.top
Subject: Re: [PATCH] conf, pasta: Add --no-tap option
Date: Sat, 10 Jan 2026 19:12:19 +0100	[thread overview]
Message-ID: <20260110191219.29498050@elisabeth> (raw)
In-Reply-To: <CANsz47=ZN3=gZEbYt8RbV9k2Sc0T4knMQxGvZr69YPr8dcP1cQ@mail.gmail.com>

On Mon, 5 Jan 2026 16:53:49 +0800
Yumei Huang <yuhuang@redhat.com> wrote:

> On Mon, Jan 5, 2026 at 12:18 PM David Gibson
> <david@gibson.dropbear.id.au> wrote:
> >
> > On Mon, Dec 29, 2025 at 05:55:58PM +0800, 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.
> > >
> > > 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>  
> >
> > Nice work.  There are some things that need polish, but overall this
> > looks pretty good to me.  Like Stefano, I'm pleasantly surprised at
> > how simple it turned out to be.
> >  
> > > ---
> > >  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");  
> >
> > I feel like this description can be improved, but I'm not exactly sure
> > how, yet.

A few possible alternatives:

- "Only enable loopback forwarding"

- "Loopback only from/to namespace"

- call it --splice-only, and use one of the descriptions above

- call it --loopback-only, and use one of the descriptions above

> >  
> > >
> > >       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");  
> >
> > These all make sense.  It might also make sense to exclude the -i
> > option - setting a template interface also makes no sense in --no-tap
> > mode.  
> 
> Sure, I can add an if condition with if4 (as if4=if6 in that case).
> >  
> > > +             if (c->pasta_conf_ns)
> > > +                     die("--no-tap is incompatible with --config-net");  
> >
> > I don't think this is right.  We still can and should bring up 'lo' in
> > the --no-tap case.  
> 
> I see your point, but seems c->pasta_conf_ns is only used for tap as
> https://passt.top/passt/tree/pasta.c#n328, 'lo' is configured before
> that line.

Right, and the reason is that there are basic bits of functionality
(probing pipe sizes if I recall correctly, or anyway probing for some
kind of capability) that need the loopback interface to be up.

On the other hand, checks we're adding here are kind of fragile because
we'll add other options in the future and probably forget to check
which ones are incompatible, so I would try a slightly different
approach: only check the options that are *obviously* conflicting with
--no-tap.

That is, the main thing "--config-net" does is to "Configure networking
in the namespace", which we still do with "--no-tap".

Now, I see that making sure c->pasta_conf_ns is false saves you checks
elsewhere in the implementation, which is, I think, a good reason to
have this check here.

But in general we don't need to exclude all the possible options that
make no sense with --no-tap. We don't really confuse users if we allow
them (or, at least, some of them).

> > > +             c->host_lo_to_ns_lo = 1;
> > > +             c->no_icmp = 1;
> > > +             c->no_ra = 1;
> > > +             c->no_dns = 1;
> > > +             c->no_dns_search = 1;  
> >
> > The reasoning for the last two items is a bit unclear to me.  IIUC,
> > no_dns and no_dns_search aren't so much about "support" for DNS itself
> > but for advertising DNS settings via DHCP.  Since DHCP will be
> > unsupported, so are these as a consequence.  Is that right?  
> 
> Yeah, I think so. Actually I added c->no_dhcp, c->no_ndp here as well,
> then removed them as they are set in later changes(conditions about
> c->ifi4/c->ifi6), though they turn out to be not quite right :'\

Do we care about them, though? That code won't be reachable anyway,
unless I'm missing something. Or is it to make the output of
conf_print() nicer? In that case I guess it makes sense to go and
disable things.

> > > +     }
> > > +
> > >       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 &&  
> >
> > This isn't quite right.  ifi4 == -1 now occurs in two cases: local
> > mode, and --no-tap mode.  Not setting map_host_loopback makes sense
> > for --no-tap mode, but it's still needed for local mode.  
> 
> I'm a bit confused by map_host_loopback. I don't quite understand the
> use scenario. IIUC, either in --no-tap mode or local mode, guest can
> only communicate with host.

That's not the case for local mode, the guest can communicate with any
other host. Local mode is just about addresses and routes, and the fact
that, when pasta started, there was no template interface.

> Then why do we need to set map_host_loopback? What's the benefit?

Example: guest has 169.254.2.1 (default in local mode), and wants to use
192.0.2.1 to refer to the host, via loopback interface.

> > >           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 &&  
> >
> > Same here.
> >  
> > >           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;  
> >
> > And here.  Local mode can still use NDP and DHCP, even though --no-tap
> > mode can't.  It might be simpler to force no_ndp, no_dhcp etc. along
> > with no_ra and the rest above.  
> 
> Sure, I will add them.
> >  
> > >       } 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.  
> >
> > This probably wants some work, because I'm not sure "tap device" and
> > "splice" are sufficiently clear in this context.  
> 
> Yeah, I will think about that. Thanks.
> >
> >  
> > > +
> > >  .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);
> > >  }
> > >
> > >  /**
> > > --
> > > 2.49.0
> > >  
> >
> > --
> > David Gibson (he or they)       | I'll have my music baroque, and my code
> > david AT gibson.dropbear.id.au  | minimalist, thank you, not the other way
> >                                 | around.
> > http://www.ozlabs.org/~dgibson  
> 
> 
> 

-- 
Stefano


  reply	other threads:[~2026-01-10 18:12 UTC|newest]

Thread overview: 16+ 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-10 18:12     ` Stefano Brivio [this message]
2026-01-12  4:26       ` David Gibson
2026-01-13  0:12         ` Stefano Brivio
2026-01-13  2:39           ` David Gibson
2026-01-13  9:57       ` Yumei Huang
2026-01-05 13:48 ` Paul Holzinger
2026-01-05 21:10   ` Stefano Brivio
2026-01-07 15:20     ` Paul Holzinger
2026-01-10 18:12       ` Stefano Brivio
2026-01-12  8:20         ` Yumei Huang
2026-01-10 18:12 ` Stefano Brivio
2026-01-13 11:20   ` Yumei Huang

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=20260110191219.29498050@elisabeth \
    --to=sbrivio@redhat.com \
    --cc=david@gibson.dropbear.id.au \
    --cc=passt-dev@passt.top \
    --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).