public inbox for passt-dev@passt.top
 help / color / mirror / code / Atom feed
From: Jon Maloy <jmaloy@redhat.com>
To: sbrivio@redhat.com, david@gibson.dropbear.id.au,
	jmaloy@redhat.com, passt-dev@passt.top
Subject: [PATCH v8 10/14] conf, pasta: Track observed guest IPv6 addresses in unified address array
Date: Thu, 25 Jun 2026 22:45:15 -0400	[thread overview]
Message-ID: <20260626024519.3701556-11-jmaloy@redhat.com> (raw)
In-Reply-To: <20260626024519.3701556-1-jmaloy@redhat.com>

We remove the addr_seen and addr_ll_seen fields in struct ip6_ctx
and replace them by setting CONF_ADDR_OBSERVED and CONF_ADDR_LINKLOCAL
flags in the corresponding entry in the unified address array.

The observed IPv6 address is always added/moved to position 0
in the array, improving chances for fast lookup.

The separate check against addr_seen in fwd_guest_accessible() can now
be removed because the observed address is now in the unified array,
and the existing for_each_addr() loop already checks against all
addresses, including this one.

This completes the unification of address storage for both IPv4 and
IPv6, enabling future support for multiple guest addresses per family.

Signed-off-by: Jon Maloy <jmaloy@redhat.com>

---
v5: - Made to use same algorithm and function as IPv4 for inserting
      observed into the array.

v6: - Re-introduced code that by accident had been moved to the
      previous commit.
    - Some fixes based on feedback from David G.

v7: - Added a commit at the beginning of the series addressing
      Stefanos's concern about DHCPv6 reply addresses.
    - Some other updates based on feedback from David and Stefano.

v8: - Adapted to previous changes in this series.
    - A couple of minor fixes in migrate.c (David)
    - Refactored parts of fwd_nat_from_host() to get v6 LINK_LOCAL
      selection criteria right. (David)
---
 conf.c    |  4 ----
 dhcpv6.c  |  4 +---
 fwd.c     | 58 +++++++++++++++++++++++++++++++++----------------------
 inany.h   |  3 +++
 migrate.c | 37 +++++++++++++++++++++++++++--------
 passt.h   |  4 ----
 pasta.c   | 11 +++++++----
 tap.c     | 19 +++++-------------
 8 files changed, 80 insertions(+), 60 deletions(-)

diff --git a/conf.c b/conf.c
index 32734188..69c1c439 100644
--- a/conf.c
+++ b/conf.c
@@ -473,7 +473,6 @@ static unsigned int conf_ip6(struct ctx *c, unsigned int ifi)
 			      strerror_(-rc));
 			return 0;
 		}
-		a = fwd_get_addr(c, AF_INET6, CONF_ADDR_HOST, 0);
 	} else {
 		rc = nl_addr_get_ll(nl_sock, ifi, &ip6->our_tap_ll);
 		if (rc < 0) {
@@ -482,9 +481,6 @@ static unsigned int conf_ip6(struct ctx *c, unsigned int ifi)
 		}
 	}
 
-	if (a)
-		ip6->addr_seen = a->addr.a6;
-
 	if (IN6_IS_ADDR_LINKLOCAL(&ip6->guest_gw))
 		ip6->our_tap_ll = ip6->guest_gw;
 
diff --git a/dhcpv6.c b/dhcpv6.c
index f64cbc24..f5de90cd 100644
--- a/dhcpv6.c
+++ b/dhcpv6.c
@@ -567,8 +567,8 @@ int dhcpv6(struct ctx *c, struct iov_tail *data,
 	struct opt_hdr client_id_storage;
 	/* cppcheck-suppress [variableScope,unmatchedSuppression] */
 	struct opt_ia_na ia_storage;
-	const struct guest_addr *a;
 	const struct in6_addr *src;
+	const struct guest_addr *a;
 	struct msg_hdr mh_storage;
 	const struct msg_hdr *mh;
 	struct udphdr uh_storage;
@@ -685,8 +685,6 @@ int dhcpv6(struct ctx *c, struct iov_tail *data,
 	resp.hdr.xid = mh->xid;
 
 	tap_udp6_send(c, src, 547, saddr, 546, mh->xid, &resp, n);
-	if (a)
-		c->ip6.addr_seen = a->addr.a6;
 
 	return 1;
 }
diff --git a/fwd.c b/fwd.c
index 10ed6034..e4090f4d 100644
--- a/fwd.c
+++ b/fwd.c
@@ -949,14 +949,6 @@ static bool fwd_guest_accessible(const struct ctx *c,
 			return false;
 	}
 
-	/* For IPv6, addr_seen starts unspecified, because we don't know what LL
-	 * address the guest will take until we see it.  Only check against it
-	 * if it has been set to a real address.
-	 */
-	if (!IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addr_seen) &&
-	    inany_equals6(addr, &c->ip6.addr_seen))
-		return false;
-
 	return true;
 }
 
@@ -1128,6 +1120,8 @@ uint8_t fwd_nat_from_host(const struct ctx *c,
 			  const struct fwd_rule *rule, uint8_t proto,
 			  const struct flowside *ini, struct flowside *tgt)
 {
+	const struct guest_addr *a = NULL;
+
 	/* Common for spliced and non-spliced cases */
 	tgt->eport = rule->to + (ini->oport - rule->first);
 
@@ -1148,8 +1142,6 @@ uint8_t fwd_nat_from_host(const struct ctx *c,
 			if (c->host_lo_to_ns_lo) {
 				tgt->eaddr = inany_loopback4;
 			} else {
-				const struct guest_addr *a;
-
 				a = fwd_select_addr(c, AF_INET,
 						    CONF_ADDR_OBSERVED,
 						    CONF_ADDR_USER |
@@ -1161,10 +1153,18 @@ uint8_t fwd_nat_from_host(const struct ctx *c,
 			}
 			tgt->oaddr = inany_any4;
 		} else {
-			if (c->host_lo_to_ns_lo)
+			if (c->host_lo_to_ns_lo) {
 				tgt->eaddr = inany_loopback6;
-			else
-				tgt->eaddr.a6 = c->ip6.addr_seen;
+			} else {
+				a = fwd_select_addr(c, AF_INET6,
+						    CONF_ADDR_OBSERVED,
+						    CONF_ADDR_USER |
+						    CONF_ADDR_HOST,
+						    CONF_ADDR_LINKLOCAL);
+				if (!a)
+					return PIF_NONE;
+				tgt->eaddr = a->addr;
+			}
 			tgt->oaddr = inany_any6;
 		}
 
@@ -1193,20 +1193,32 @@ uint8_t fwd_nat_from_host(const struct ctx *c,
 	tgt->oport = ini->eport;
 
 	if (inany_v4(&tgt->oaddr)) {
-		const struct guest_addr *a;
-
 		a = fwd_select_addr(c, AF_INET, CONF_ADDR_OBSERVED,
 				    CONF_ADDR_USER | CONF_ADDR_HOST, 0);
-		if (!a)
-			return PIF_NONE;
-
-		tgt->eaddr = a->addr;
+	} else if (!inany_is_linklocal6(&tgt->oaddr)) {
+		a = fwd_select_addr(c, AF_INET6, CONF_ADDR_OBSERVED,
+				    CONF_ADDR_USER | CONF_ADDR_HOST,
+				    CONF_ADDR_LINKLOCAL);
 	} else {
-		if (inany_is_linklocal6(&tgt->oaddr))
-			tgt->eaddr.a6 = c->ip6.addr_ll_seen;
-		else
-			tgt->eaddr.a6 = c->ip6.addr_seen;
+		const struct guest_addr *tmp;
+
+		/* Preferably, we want an entry with both flags set */
+		for_each_addr(tmp, c->addrs, c->addr_count, AF_INET6) {
+			if (!(tmp->flags & CONF_ADDR_OBSERVED))
+				continue;
+			if (!(tmp->flags & CONF_ADDR_LINKLOCAL))
+				continue;
+			a = tmp;
+			break;
+		}
+		/* If not, LINK_LOCAL only will do */
+		if (!a)
+			a = fwd_get_addr(c, AF_INET6, CONF_ADDR_LINKLOCAL, 0);
 	}
 
+	if (!a)
+		return PIF_NONE;
+
+	tgt->eaddr = a->addr;
 	return PIF_TAP;
 }
diff --git a/inany.h b/inany.h
index 95756eb8..14390c29 100644
--- a/inany.h
+++ b/inany.h
@@ -60,6 +60,9 @@ extern const union inany_addr inany_any4;
 #define inany_from_v4(a4)	\
 	((union inany_addr)INANY_INIT4((a4)))
 
+#define inany_from_v6(v6)	\
+	((union inany_addr){ .a6 = (v6) })
+
 /** union sockaddr_inany - Either a sockaddr_in or a sockaddr_in6
  * @sa_family:	Address family, AF_INET or AF_INET6
  * @sa:		Plain struct sockaddr (useful to avoid casts)
diff --git a/migrate.c b/migrate.c
index 795f2818..3ca97bf6 100644
--- a/migrate.c
+++ b/migrate.c
@@ -57,10 +57,7 @@ struct migrate_seen_addrs_v2 {
 static int seen_addrs_source_v2(struct ctx *c,
 				const struct migrate_stage *stage, int fd)
 {
-	struct migrate_seen_addrs_v2 addrs = {
-		.addr6 = c->ip6.addr_seen,
-		.addr6_ll = c->ip6.addr_ll_seen,
-	};
+	struct migrate_seen_addrs_v2 addrs = { 0 };
 	const struct guest_addr *a;
 
 	(void)stage;
@@ -71,6 +68,18 @@ static int seen_addrs_source_v2(struct ctx *c,
 	if (a)
 		addrs.addr4 = *inany_v4(&a->addr);
 
+	/* IPv6 observed address, with fallback to any other non-LL address */
+	a = fwd_select_addr(c, AF_INET6, CONF_ADDR_OBSERVED,
+			    CONF_ADDR_USER | CONF_ADDR_HOST,
+			    CONF_ADDR_LINKLOCAL);
+	if (a)
+		addrs.addr6 = a->addr.a6;
+
+	/* IPv6 link-local address */
+	a = fwd_get_addr(c, AF_INET6, CONF_ADDR_LINKLOCAL, 0);
+	if (a)
+		addrs.addr6_ll = a->addr.a6;
+
 	memcpy(addrs.mac, c->guest_mac, sizeof(addrs.mac));
 
 	if (write_all_buf(fd, &addrs, sizeof(addrs)))
@@ -91,17 +100,29 @@ static int seen_addrs_target_v2(struct ctx *c,
 				const struct migrate_stage *stage, int fd)
 {
 	struct migrate_seen_addrs_v2 addrs;
+	struct in6_addr addr6, addr6_ll;
+	struct in_addr addr4;
 
 	(void)stage;
 
 	if (read_all_buf(fd, &addrs, sizeof(addrs)))
 		return errno;
 
-	c->ip6.addr_seen = addrs.addr6;
-	c->ip6.addr_ll_seen = addrs.addr6_ll;
+	addr6 = addrs.addr6;
+	if (!IN6_IS_ADDR_UNSPECIFIED(&addr6)) {
+		fwd_set_addr(c, &inany_from_v6(addr6),
+			     CONF_ADDR_OBSERVED, 0);
+	}
+
+	addr6_ll = addrs.addr6_ll;
+	if (!IN6_IS_ADDR_UNSPECIFIED(&addr6_ll)) {
+		fwd_set_addr(c, &inany_from_v6(addr6_ll),
+			     CONF_ADDR_OBSERVED | CONF_ADDR_LINKLOCAL, 0);
+	}
 
-	if (!IN4_IS_ADDR_UNSPECIFIED(&addrs.addr4)) {
-		fwd_set_addr(c, &inany_from_v4(addrs.addr4),
+	addr4 = addrs.addr4;
+	if (!IN4_IS_ADDR_UNSPECIFIED(&addr4)) {
+		fwd_set_addr(c, &inany_from_v4(addr4),
 			     CONF_ADDR_OBSERVED, 0);
 	}
 	memcpy(c->guest_mac, addrs.mac, sizeof(c->guest_mac));
diff --git a/passt.h b/passt.h
index 20ad7022..005f7631 100644
--- a/passt.h
+++ b/passt.h
@@ -123,8 +123,6 @@ struct ip4_ctx {
 
 /**
  * struct ip6_ctx - IPv6 execution context
- * @addr_seen:		Latest IPv6 global/site address seen as source from tap
- * @addr_ll_seen:	Latest IPv6 link-local address seen as source from tap
  * @guest_gw:		IPv6 gateway as seen by the guest
  * @map_host_loopback:	Outbound connections to this address are NATted to the
  *                      host's [::1]
@@ -141,8 +139,6 @@ struct ip4_ctx {
  */
 struct ip6_ctx {
 	/* PIF_TAP addresses */
-	struct in6_addr addr_seen;
-	struct in6_addr addr_ll_seen;
 	struct in6_addr guest_gw;
 	struct in6_addr map_host_loopback;
 	struct in6_addr map_guest_addr;
diff --git a/pasta.c b/pasta.c
index 56df2fdc..575b9315 100644
--- a/pasta.c
+++ b/pasta.c
@@ -366,6 +366,7 @@ static int pasta_conf_routes(struct ctx *c, sa_family_t af, int ifi,
 void pasta_ns_conf(struct ctx *c)
 {
 	unsigned int flags = IFF_UP;
+	struct in6_addr addr_ll;
 	int rc;
 
 	rc = nl_link_set_flags(nl_sock_ns, 1 /* lo */, IFF_UP, IFF_UP);
@@ -415,12 +416,14 @@ void pasta_ns_conf(struct ctx *c)
 	if (!c->ifi6)
 		return;
 
-	rc = nl_addr_get_ll(nl_sock_ns, c->pasta_ifi,
-			    &c->ip6.addr_ll_seen);
-	if (rc < 0)
+	rc = nl_addr_get_ll(nl_sock_ns, c->pasta_ifi, &addr_ll);
+	if (rc < 0) {
 		warn("Can't get LL address from namespace: %s",
 		     strerror_(-rc));
-
+	} else {
+		fwd_set_addr(c, &inany_from_v6(addr_ll),
+			     CONF_ADDR_LINKLOCAL | CONF_ADDR_OBSERVED, 0);
+	}
 	rc = nl_addr_set_ll_nodad(nl_sock_ns, c->pasta_ifi);
 	if (rc < 0)
 		warn("Can't set nodad for LL in namespace: %s",
diff --git a/tap.c b/tap.c
index 93db2f86..e7d37082 100644
--- a/tap.c
+++ b/tap.c
@@ -1000,22 +1000,13 @@ resume:
 			continue;
 		}
 
-		if (IN6_IS_ADDR_LINKLOCAL(saddr)) {
-			c->ip6.addr_ll_seen = *saddr;
+		if (!IN6_IS_ADDR_UNSPECIFIED(saddr)) {
+			uint8_t flags = CONF_ADDR_OBSERVED;
 
-			if (!fwd_get_addr(c, AF_INET6, CONF_ADDR_USER, 0) &&
-			    IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addr_seen)) {
-				c->ip6.addr_seen = *saddr;
-			}
-
-			if (!fwd_get_addr(c, AF_INET6, CONF_ADDR_USER, 0)) {
-				union inany_addr addr = { .a6 = *saddr };
+			if (IN6_IS_ADDR_LINKLOCAL(saddr))
+				flags |= CONF_ADDR_LINKLOCAL;
 
-				fwd_set_addr(c, &addr, CONF_ADDR_LINKLOCAL, 64);
-			}
-		} else if (!fwd_get_addr(c, AF_INET6, CONF_ADDR_USER, 0) &&
-			   !IN6_IS_ADDR_UNSPECIFIED(saddr)) {
-			c->ip6.addr_seen = *saddr;
+			fwd_set_addr(c, &inany_from_v6(*saddr), flags, 0);
 		}
 
 		if (proto == IPPROTO_ICMPV6) {
-- 
2.52.0


  parent reply	other threads:[~2026-06-26  2:45 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-26  2:45 [PATCH v8 00/14] Introduce multiple addresses Jon Maloy
2026-06-26  2:45 ` [PATCH v8 01/14] dhcpv6: Fix reply destination to match client's source address Jon Maloy
2026-06-26  2:45 ` [PATCH v8 02/14] passt, pasta: Introduce unified multi-address data structures Jon Maloy
2026-06-26  2:45 ` [PATCH v8 03/14] tap, conf: Replace addr_fixed with CONF_ADDR_USER flag check Jon Maloy
2026-06-26  2:45 ` [PATCH v8 04/14] fwd: Unify guest accessibility checks with unified address array Jon Maloy
2026-06-26  2:45 ` [PATCH v8 05/14] arp: Check all configured addresses in ARP filtering Jon Maloy
2026-06-26  2:45 ` [PATCH v8 06/14] conf: Allow multiple -a/--address options per address family Jon Maloy
2026-06-26  2:45 ` [PATCH v8 07/14] netlink, conf: Read all addresses from template interface at startup Jon Maloy
2026-06-26  2:45 ` [PATCH v8 08/14] netlink, pasta: refactor function pasta_ns_conf() Jon Maloy
2026-06-26  2:45 ` [PATCH v8 09/14] conf, pasta: Track observed guest IPv4 addresses in unified address array Jon Maloy
2026-06-26  2:45 ` Jon Maloy [this message]
2026-06-26  2:45 ` [PATCH v8 11/14] dhcp: Select address for DHCP distribution Jon Maloy
2026-06-26  2:45 ` [PATCH v8 12/14] dhcpv6: Select addresses for DHCPv6 distribution Jon Maloy
2026-06-26  2:45 ` [PATCH v8 13/14] ndp: Support advertising multiple prefixes in Router Advertisements Jon Maloy
2026-06-26  2:45 ` [PATCH v8 14/14] migrate: Update protocol to v3 for multi-address support Jon Maloy

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