* [PATCH 0/2] Allow users to give source address with --gateway also for IPv6
@ 2026-07-22 23:26 Stefano Brivio
2026-07-22 23:26 ` [PATCH 1/2] conf: Honour --address, --gateway, --netmask in local mode as well Stefano Brivio
2026-07-22 23:26 ` [PATCH 2/2] conf, fwd: Prefer same-scope address as inbound source address from host Stefano Brivio
0 siblings, 2 replies; 6+ messages in thread
From: Stefano Brivio @ 2026-07-22 23:26 UTC (permalink / raw)
To: passt-dev; +Cc: Jan Rodák, Paul Holzinger, David Gibson
...just like it happens with IPv4. This offers a solution for
https://bugs.passt.top/show_bug.cgi?id=217, as Podman can specify
--address and --gateway for IPv6 local mode, and provide a unique
local address, without the need to introduce other options.
Further options might be more appropriate, strictly speaking, but
as the future perspective is to eventually generalise NAT tables,
it would be nice to avoid them right now.
Stefano Brivio (2):
conf: Honour --address, --gateway, --netmask in local mode as well
conf, fwd: Prefer same-scope address as inbound source address from
host
conf.c | 23 +++++++++++++++++++----
fwd.c | 6 +++++-
passt.h | 2 ++
3 files changed, 26 insertions(+), 5 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH 1/2] conf: Honour --address, --gateway, --netmask in local mode as well 2026-07-22 23:26 [PATCH 0/2] Allow users to give source address with --gateway also for IPv6 Stefano Brivio @ 2026-07-22 23:26 ` Stefano Brivio 2026-07-23 3:26 ` David Gibson 2026-07-22 23:26 ` [PATCH 2/2] conf, fwd: Prefer same-scope address as inbound source address from host Stefano Brivio 1 sibling, 1 reply; 6+ messages in thread From: Stefano Brivio @ 2026-07-22 23:26 UTC (permalink / raw) To: passt-dev; +Cc: Jan Rodák, Paul Holzinger, David Gibson When I implemented local mode in 14b84a7f077e ("treewide: Introduce 'local mode' for disconnected setups"), I didn't consider the possibility that, also in that case, the user might want to override addresses, default gateway or netmask, even though I expressly mentioned this in the man page: In this case, **unless configured otherwise**, they will assign the IPv4 link-local address 169.254.2.1 to the guest or target namespace, and no IPv6 address. Fix this by checking if an address, gateway, or netmask length was explicitly set by the user, before overriding them with the default parameters for local mode. This might lead to invalid configurations where we won't be able to set the default gateway passed by the user, but we print a warning message, and we assume users know what they're doing in that case. Link: https://bugs.passt.top/show_bug.cgi?id=217 Fixes: 14b84a7f077e ("treewide: Introduce 'local mode' for disconnected setups") Signed-off-by: Stefano Brivio <sbrivio@redhat.com> --- conf.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/conf.c b/conf.c index 0fcba5c..7908123 100644 --- a/conf.c +++ b/conf.c @@ -435,9 +435,16 @@ static unsigned int conf_ip4(unsigned int ifi, struct ip4_ctx *ip4) */ static void conf_ip4_local(struct ip4_ctx *ip4) { - ip4->addr_seen = ip4->addr = IP4_LL_GUEST_ADDR; - ip4->our_tap_addr = ip4->guest_gw = IP4_LL_GUEST_GW; - ip4->prefix_len = IP4_LL_PREFIX_LEN; + if (IN4_IS_ADDR_UNSPECIFIED(&ip4->addr)) + ip4->addr = IP4_LL_GUEST_ADDR; + ip4->addr_seen = ip4->addr; + + if (IN4_IS_ADDR_UNSPECIFIED(&ip4->guest_gw)) + ip4->guest_gw = IP4_LL_GUEST_GW; + ip4->our_tap_addr = ip4->guest_gw; + + if (!ip4->prefix_len) + ip4->prefix_len = IP4_LL_PREFIX_LEN; ip4->no_copy_addrs = ip4->no_copy_routes = true; } @@ -497,7 +504,13 @@ static unsigned int conf_ip6(unsigned int ifi, struct ip6_ctx *ip6) */ static void conf_ip6_local(struct ip6_ctx *ip6) { - ip6->our_tap_ll = ip6->guest_gw = IP6_LL_GUEST_GW; + if (IN6_IS_ADDR_UNSPECIFIED(&ip6->guest_gw)) + ip6->guest_gw = IP6_LL_GUEST_GW; + + if (IN6_IS_ADDR_LINKLOCAL(&ip6->guest_gw)) + ip6->our_tap_ll = ip6->guest_gw; + else + ip6->our_tap_ll = IP6_LL_GUEST_GW; ip6->no_copy_addrs = ip6->no_copy_routes = true; } -- 2.43.0 ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 1/2] conf: Honour --address, --gateway, --netmask in local mode as well 2026-07-22 23:26 ` [PATCH 1/2] conf: Honour --address, --gateway, --netmask in local mode as well Stefano Brivio @ 2026-07-23 3:26 ` David Gibson 0 siblings, 0 replies; 6+ messages in thread From: David Gibson @ 2026-07-23 3:26 UTC (permalink / raw) To: Stefano Brivio; +Cc: passt-dev, Jan Rodák, Paul Holzinger [-- Attachment #1: Type: text/plain, Size: 2827 bytes --] On Thu, Jul 23, 2026 at 01:26:38AM +0200, Stefano Brivio wrote: > When I implemented local mode in 14b84a7f077e ("treewide: Introduce > 'local mode' for disconnected setups"), I didn't consider the > possibility that, also in that case, the user might want to override > addresses, default gateway or netmask, even though I expressly > mentioned this in the man page: > > In this case, **unless configured otherwise**, they will assign the > IPv4 link-local address 169.254.2.1 to the guest or target > namespace, and no IPv6 address. > > Fix this by checking if an address, gateway, or netmask length was > explicitly set by the user, before overriding them with the default > parameters for local mode. > > This might lead to invalid configurations where we won't be able to > set the default gateway passed by the user, but we print a warning > message, and we assume users know what they're doing in that case. > > Link: https://bugs.passt.top/show_bug.cgi?id=217 > Fixes: 14b84a7f077e ("treewide: Introduce 'local mode' for disconnected setups") > Signed-off-by: Stefano Brivio <sbrivio@redhat.com> Reviewed-by: David Gibson <david@gibson.dropbear.id.au> > --- > conf.c | 21 +++++++++++++++++---- > 1 file changed, 17 insertions(+), 4 deletions(-) > > diff --git a/conf.c b/conf.c > index 0fcba5c..7908123 100644 > --- a/conf.c > +++ b/conf.c > @@ -435,9 +435,16 @@ static unsigned int conf_ip4(unsigned int ifi, struct ip4_ctx *ip4) > */ > static void conf_ip4_local(struct ip4_ctx *ip4) > { > - ip4->addr_seen = ip4->addr = IP4_LL_GUEST_ADDR; > - ip4->our_tap_addr = ip4->guest_gw = IP4_LL_GUEST_GW; > - ip4->prefix_len = IP4_LL_PREFIX_LEN; > + if (IN4_IS_ADDR_UNSPECIFIED(&ip4->addr)) > + ip4->addr = IP4_LL_GUEST_ADDR; > + ip4->addr_seen = ip4->addr; > + > + if (IN4_IS_ADDR_UNSPECIFIED(&ip4->guest_gw)) > + ip4->guest_gw = IP4_LL_GUEST_GW; > + ip4->our_tap_addr = ip4->guest_gw; > + > + if (!ip4->prefix_len) > + ip4->prefix_len = IP4_LL_PREFIX_LEN; > > ip4->no_copy_addrs = ip4->no_copy_routes = true; > } > @@ -497,7 +504,13 @@ static unsigned int conf_ip6(unsigned int ifi, struct ip6_ctx *ip6) > */ > static void conf_ip6_local(struct ip6_ctx *ip6) > { > - ip6->our_tap_ll = ip6->guest_gw = IP6_LL_GUEST_GW; > + if (IN6_IS_ADDR_UNSPECIFIED(&ip6->guest_gw)) > + ip6->guest_gw = IP6_LL_GUEST_GW; > + > + if (IN6_IS_ADDR_LINKLOCAL(&ip6->guest_gw)) > + ip6->our_tap_ll = ip6->guest_gw; > + else > + ip6->our_tap_ll = IP6_LL_GUEST_GW; > > ip6->no_copy_addrs = ip6->no_copy_routes = true; > } > -- > 2.43.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 [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 833 bytes --] ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 2/2] conf, fwd: Prefer same-scope address as inbound source address from host 2026-07-22 23:26 [PATCH 0/2] Allow users to give source address with --gateway also for IPv6 Stefano Brivio 2026-07-22 23:26 ` [PATCH 1/2] conf: Honour --address, --gateway, --netmask in local mode as well Stefano Brivio @ 2026-07-22 23:26 ` Stefano Brivio 2026-07-23 3:36 ` David Gibson 1 sibling, 1 reply; 6+ messages in thread From: Stefano Brivio @ 2026-07-22 23:26 UTC (permalink / raw) To: passt-dev; +Cc: Jan Rodák, Paul Holzinger, David Gibson We might have situations, such as the one described in https://bugs.passt.top/show_bug.cgi?id=217, where using a link-local address as source in a given namespace doesn't guarantee that we can reach the intended destination, because, for instance, the inbound traffic we forward is in turn forwarded to a different interface, such as a bridge. In that case, the assumption from 9618d247006a ("ndp, dhcpv6, tcp, udp: Always use link-local as source if gateway isn't") isn't a safe one: the user might have specified a valid gateway address, matching the scope of the destination address, but we won't use it as address of last resort, and prefer a link-local address with a mismatch in scope instead. This should only be an issue in IPv6 local mode, because, otherwise, we source address and default gateway address (presumably compatible) from the host. So, in local mode, if the user specifies a given default gateway address for IPv6, note that as 'our_tap_addr', like we would do with with IPv4, and stick to Rule 2 of RFC 6724, Section 5, when selecting a source address, by preferring an address with the same scope, if available. Reported-by: Paul Holzinger <pholzing@redhat.com> Link: https://bugs.passt.top/show_bug.cgi?id=217 Signed-off-by: Stefano Brivio <sbrivio@redhat.com> --- conf.c | 6 ++++-- fwd.c | 6 +++++- passt.h | 2 ++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/conf.c b/conf.c index 7908123..1998004 100644 --- a/conf.c +++ b/conf.c @@ -507,10 +507,12 @@ static void conf_ip6_local(struct ip6_ctx *ip6) if (IN6_IS_ADDR_UNSPECIFIED(&ip6->guest_gw)) ip6->guest_gw = IP6_LL_GUEST_GW; - if (IN6_IS_ADDR_LINKLOCAL(&ip6->guest_gw)) + if (IN6_IS_ADDR_LINKLOCAL(&ip6->guest_gw)) { ip6->our_tap_ll = ip6->guest_gw; - else + } else { + ip6->our_tap_addr = ip6->guest_gw; ip6->our_tap_ll = IP6_LL_GUEST_GW; + } ip6->no_copy_addrs = ip6->no_copy_routes = true; } diff --git a/fwd.c b/fwd.c index 7152169..4ba0af3 100644 --- a/fwd.c +++ b/fwd.c @@ -1090,7 +1090,11 @@ uint8_t fwd_nat_from_host(const struct ctx *c, return PIF_NONE; tgt->oaddr = inany_from_v4(c->ip4.our_tap_addr); } else { - tgt->oaddr.a6 = c->ip6.our_tap_ll; + if (inany_is_linklocal6(&tgt->eaddr) || + IN6_IS_ADDR_UNSPECIFIED(&c->ip6.our_tap_addr)) + tgt->oaddr.a6 = c->ip6.our_tap_ll; + else + tgt->oaddr.a6 = c->ip6.our_tap_addr; } } tgt->oport = ini->eport; diff --git a/passt.h b/passt.h index a61baca..51ccd4f 100644 --- a/passt.h +++ b/passt.h @@ -121,6 +121,7 @@ struct ip4_ctx { * @dns: DNS addresses for DHCPv6 and NDP * @dns_match: Forward DNS query if sent to this address * @our_tap_ll: Link-local IPv6 address for passt's use on tap + * @our_tap_addr: Non-LL IPv6 address for passt's use on tap (if any) * @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 @@ -140,6 +141,7 @@ struct ip6_ctx { struct in6_addr dns[MAXNS]; struct in6_addr dns_match; struct in6_addr our_tap_ll; + struct in6_addr our_tap_addr; /* PIF_HOST addresses */ struct in6_addr dns_host; -- 2.43.0 ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 2/2] conf, fwd: Prefer same-scope address as inbound source address from host 2026-07-22 23:26 ` [PATCH 2/2] conf, fwd: Prefer same-scope address as inbound source address from host Stefano Brivio @ 2026-07-23 3:36 ` David Gibson 2026-07-23 9:42 ` Stefano Brivio 0 siblings, 1 reply; 6+ messages in thread From: David Gibson @ 2026-07-23 3:36 UTC (permalink / raw) To: Stefano Brivio; +Cc: passt-dev, Jan Rodák, Paul Holzinger [-- Attachment #1: Type: text/plain, Size: 3792 bytes --] On Thu, Jul 23, 2026 at 01:26:39AM +0200, Stefano Brivio wrote: > We might have situations, such as the one described in > https://bugs.passt.top/show_bug.cgi?id=217, where using a link-local > address as source in a given namespace doesn't guarantee that we can > reach the intended destination, because, for instance, the inbound > traffic we forward is in turn forwarded to a different interface, such > as a bridge. > > In that case, the assumption from 9618d247006a ("ndp, dhcpv6, tcp, > udp: Always use link-local as source if gateway isn't") isn't a safe > one: the user might have specified a valid gateway address, matching > the scope of the destination address, but we won't use it as address > of last resort, and prefer a link-local address with a mismatch in > scope instead. > > This should only be an issue in IPv6 local mode, because, otherwise, > we source address and default gateway address (presumably compatible) > from the host. > > So, in local mode, if the user specifies a given default gateway > address for IPv6, note that as 'our_tap_addr', like we would do with > with IPv4, and stick to Rule 2 of RFC 6724, Section 5, when selecting > a source address, by preferring an address with the same scope, if > available. > > Reported-by: Paul Holzinger <pholzing@redhat.com> > Link: https://bugs.passt.top/show_bug.cgi?id=217 > Signed-off-by: Stefano Brivio <sbrivio@redhat.com> Reviewed-by: David Gibson <david@gibson.dropbear.id.au> [snip] > diff --git a/fwd.c b/fwd.c > index 7152169..4ba0af3 100644 > --- a/fwd.c > +++ b/fwd.c > @@ -1090,7 +1090,11 @@ uint8_t fwd_nat_from_host(const struct ctx *c, > return PIF_NONE; > tgt->oaddr = inany_from_v4(c->ip4.our_tap_addr); > } else { > - tgt->oaddr.a6 = c->ip6.our_tap_ll; > + if (inany_is_linklocal6(&tgt->eaddr) || Just to make sure you're aware: this will only trigger if tgt->eaddr has been set at this point, which is not always the case. In fact it will usually only be the case when the rule specifies a target address. In other cases we pick tgt->oaddr first then pick tgt->eaddr's scope to try to match it. I think the fact that the order in which we pick eaddr and oaddr varies is pretty confusing, but I haven't so far seen a way to avoid it without breaking something worse. I still think this change is correct: not previously having ip6.our_tap_addr was only possible because we did this odd dance to pick eaddr based on oaddr's scope rather than the other way around. > + IN6_IS_ADDR_UNSPECIFIED(&c->ip6.our_tap_addr)) > + tgt->oaddr.a6 = c->ip6.our_tap_ll; > + else > + tgt->oaddr.a6 = c->ip6.our_tap_addr; > } > } > tgt->oport = ini->eport; > diff --git a/passt.h b/passt.h > index a61baca..51ccd4f 100644 > --- a/passt.h > +++ b/passt.h > @@ -121,6 +121,7 @@ struct ip4_ctx { > * @dns: DNS addresses for DHCPv6 and NDP > * @dns_match: Forward DNS query if sent to this address > * @our_tap_ll: Link-local IPv6 address for passt's use on tap > + * @our_tap_addr: Non-LL IPv6 address for passt's use on tap (if any) > * @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 > @@ -140,6 +141,7 @@ struct ip6_ctx { > struct in6_addr dns[MAXNS]; > struct in6_addr dns_match; > struct in6_addr our_tap_ll; > + struct in6_addr our_tap_addr; > > /* PIF_HOST addresses */ > struct in6_addr dns_host; > -- > 2.43.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 [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 833 bytes --] ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 2/2] conf, fwd: Prefer same-scope address as inbound source address from host 2026-07-23 3:36 ` David Gibson @ 2026-07-23 9:42 ` Stefano Brivio 0 siblings, 0 replies; 6+ messages in thread From: Stefano Brivio @ 2026-07-23 9:42 UTC (permalink / raw) To: David Gibson; +Cc: passt-dev, Jan Rodák, Paul Holzinger On Thu, 23 Jul 2026 13:36:58 +1000 David Gibson <david@gibson.dropbear.id.au> wrote: > On Thu, Jul 23, 2026 at 01:26:39AM +0200, Stefano Brivio wrote: > > We might have situations, such as the one described in > > https://bugs.passt.top/show_bug.cgi?id=217, where using a link-local > > address as source in a given namespace doesn't guarantee that we can > > reach the intended destination, because, for instance, the inbound > > traffic we forward is in turn forwarded to a different interface, such > > as a bridge. > > > > In that case, the assumption from 9618d247006a ("ndp, dhcpv6, tcp, > > udp: Always use link-local as source if gateway isn't") isn't a safe > > one: the user might have specified a valid gateway address, matching > > the scope of the destination address, but we won't use it as address > > of last resort, and prefer a link-local address with a mismatch in > > scope instead. > > > > This should only be an issue in IPv6 local mode, because, otherwise, > > we source address and default gateway address (presumably compatible) > > from the host. > > > > So, in local mode, if the user specifies a given default gateway > > address for IPv6, note that as 'our_tap_addr', like we would do with > > with IPv4, and stick to Rule 2 of RFC 6724, Section 5, when selecting > > a source address, by preferring an address with the same scope, if > > available. > > > > Reported-by: Paul Holzinger <pholzing@redhat.com> > > Link: https://bugs.passt.top/show_bug.cgi?id=217 > > Signed-off-by: Stefano Brivio <sbrivio@redhat.com> > > Reviewed-by: David Gibson <david@gibson.dropbear.id.au> > > [snip] > > diff --git a/fwd.c b/fwd.c > > index 7152169..4ba0af3 100644 > > --- a/fwd.c > > +++ b/fwd.c > > @@ -1090,7 +1090,11 @@ uint8_t fwd_nat_from_host(const struct ctx *c, > > return PIF_NONE; > > tgt->oaddr = inany_from_v4(c->ip4.our_tap_addr); > > } else { > > - tgt->oaddr.a6 = c->ip6.our_tap_ll; > > + if (inany_is_linklocal6(&tgt->eaddr) || > > Just to make sure you're aware: this will only trigger if tgt->eaddr > has been set at this point, which is not always the case. In fact it > will usually only be the case when the rule specifies a target > address. In other cases we pick tgt->oaddr first then pick > tgt->eaddr's scope to try to match it. Right, yes, I had half a mind to try and change this slightly (see below) but then I realised that luckily it wasn't needed for this minimal fix, as Podman will specify an explicit destination address, so I preferred to avoid the topic altogether for the moment (including avoiding comments that risk ignoring some corner cases) because: > I think the fact that the order in which we pick eaddr and oaddr > varies is pretty confusing, but I haven't so far seen a way to avoid > it without breaking something worse. ...I think that, at least in the !nat_inbound() case, we should avoid a strict ordering in the selection of source and destination address (we should look into both at the same time) because in general we know upfront if we can match the scope between source and destination, and we should always try to do that (same here, RFC 6724 Section 5 Rule 2). And, if there are multiple ways to match the scopes, we should prefer the most specific / smaller common scope (same rule as above, Section 3.1 helps in the interpretation). But this would be much simpler to implement once we have Jon's changes generalising address storage, because at that point we could have a lookup function for the smallest scope of usable address, or even a joint lookup function altogether. In detail, I think we should do this (again, under the !nat_inbound() condition): 1. if there's a possible link-local source address and a possible destination source address, pick both 2. if there's a possible unicast source address and a possible unicast destination address, pick both 3. if there's a possible link-local source address, pick it, and then pick any (mismatching) destination address 4. otherwise, pick any available source address, and any available destination address ...and once we have that series, we can probably avoid implementing these as distinct steps. I would defer all this to that point, and meanwhile just fix whatever critical case might come up (like the current one with Podman). > I still think this change is correct: not previously having > ip6.our_tap_addr was only possible because we did this odd dance to > pick eaddr based on oaddr's scope rather than the other way around. > > > + IN6_IS_ADDR_UNSPECIFIED(&c->ip6.our_tap_addr)) > > + tgt->oaddr.a6 = c->ip6.our_tap_ll; > > + else > > + tgt->oaddr.a6 = c->ip6.our_tap_addr; > > } > > } > > tgt->oport = ini->eport; > > diff --git a/passt.h b/passt.h > > index a61baca..51ccd4f 100644 > > --- a/passt.h > > +++ b/passt.h > > @@ -121,6 +121,7 @@ struct ip4_ctx { > > * @dns: DNS addresses for DHCPv6 and NDP > > * @dns_match: Forward DNS query if sent to this address > > * @our_tap_ll: Link-local IPv6 address for passt's use on tap > > + * @our_tap_addr: Non-LL IPv6 address for passt's use on tap (if any) > > * @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 > > @@ -140,6 +141,7 @@ struct ip6_ctx { > > struct in6_addr dns[MAXNS]; > > struct in6_addr dns_match; > > struct in6_addr our_tap_ll; > > + struct in6_addr our_tap_addr; > > > > /* PIF_HOST addresses */ > > struct in6_addr dns_host; > > -- > > 2.43.0 -- Stefano ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-07-23 9:42 UTC | newest] Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-07-22 23:26 [PATCH 0/2] Allow users to give source address with --gateway also for IPv6 Stefano Brivio 2026-07-22 23:26 ` [PATCH 1/2] conf: Honour --address, --gateway, --netmask in local mode as well Stefano Brivio 2026-07-23 3:26 ` David Gibson 2026-07-22 23:26 ` [PATCH 2/2] conf, fwd: Prefer same-scope address as inbound source address from host Stefano Brivio 2026-07-23 3:36 ` David Gibson 2026-07-23 9:42 ` Stefano Brivio
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).