public inbox for passt-dev@passt.top
 help / color / mirror / code / Atom feed
From: Jon Maloy <jmaloy@redhat.com>
To: sbrivio@redhat.com, dgibson@redhat.com,
	david@gibson.dropbear.id.au, jmaloy@redhat.com,
	passt-dev@passt.top
Subject: [PATCH v2 8/9] ip: Track observed guest IPv4 addresses in unified address array
Date: Sun, 18 Jan 2026 17:16:11 -0500	[thread overview]
Message-ID: <20260118221612.2115386-9-jmaloy@redhat.com> (raw)
In-Reply-To: <20260118221612.2115386-1-jmaloy@redhat.com>

We remove the addr_seen field in struct ip4_ctx and replace it by
setting a new INANY_ADDR_OBSERVED flag in the corresponding entry in
the new address array. If the seen address is not present in the
array we add it first.

Signed-off-by: Jon Maloy <jmaloy@redhat.com>
---
 conf.c    |  3 ---
 fwd.c     | 49 ++++++++++++++++++++++++++++++++++++++-----------
 inany.h   |  1 +
 migrate.c | 44 ++++++++++++++++++++++++++++++++++++++++++--
 passt.h   |  3 ---
 tap.c     | 38 +++++++++++++++++++++++++++++++++-----
 6 files changed, 114 insertions(+), 24 deletions(-)

diff --git a/conf.c b/conf.c
index 22c2222..8f0091d 100644
--- a/conf.c
+++ b/conf.c
@@ -787,8 +787,6 @@ static unsigned int conf_ip4(unsigned int ifi, struct ip4_ctx *ip4)
 			ip4->addrs[i].prefix_len = ip4_default_prefix_len(a4);
 	}
 
-	ip4->addr_seen = *inany_v4(&ip4->addrs[0].addr);
-
 	ip4->our_tap_addr = ip4->guest_gw;
 
 	if (!ip4->addr_count)
@@ -804,7 +802,6 @@ static unsigned int conf_ip4(unsigned int ifi, struct ip4_ctx *ip4)
 static void conf_ip4_local(struct ip4_ctx *ip4)
 {
 	ip4->addrs[0].addr = inany_from_v4(IP4_LL_GUEST_ADDR);
-	ip4->addr_seen = *inany_v4(&ip4->addrs[0].addr);
 	ip4->our_tap_addr = ip4->guest_gw = IP4_LL_GUEST_GW;
 	ip4->addrs[0].prefix_len = IP4_LL_PREFIX_LEN;
 	ip4->addr_count = 1;
diff --git a/fwd.c b/fwd.c
index f1db34c..08b0b83 100644
--- a/fwd.c
+++ b/fwd.c
@@ -491,6 +491,29 @@ static bool is_dns_flow(uint8_t proto, const struct flowside *ini)
 		((ini->oport == 53) || (ini->oport == 853));
 }
 
+/**
+ * fwd_guest_addr4() - Get first observed IPv4 guest address
+ * @c:		Execution context
+ *
+ * Return: pointer to first observed IPv4 address, or first address if none
+ *         observed, or NULL if no addresses
+ */
+static const struct in_addr *fwd_guest_addr4(const struct ctx *c)
+{
+	int i;
+
+	/* Find first observed address */
+	for (i = 0; i < c->ip4.addr_count; i++)
+		if (c->ip4.addrs[i].flags & INANY_ADDR_OBSERVED)
+			return inany_v4(&c->ip4.addrs[i].addr);
+
+	/* Fallback to first address */
+	if (c->ip4.addr_count > 0)
+		return inany_v4(&c->ip4.addrs[0].addr);
+
+	return NULL;
+}
+
 /**
  * fwd_guest_accessible4() - Is IPv4 address guest-accessible
  * @c:		Execution context
@@ -515,17 +538,11 @@ static bool fwd_guest_accessible4(const struct ctx *c,
 	if (IN4_IS_ADDR_UNSPECIFIED(addr))
 		return false;
 
-	/* Check against all configured guest addresses */
+	/* Check against all guest addresses */
 	for (i = 0; i < c->ip4.addr_count; i++)
 		if (IN4_ARE_ADDR_EQUAL(addr, inany_v4(&c->ip4.addrs[i].addr)))
 			return false;
 
-	/* Also check addr_seen: it tracks the address the guest is actually
-	 * using, which may differ from configured addresses.
-	 */
-	if (IN4_ARE_ADDR_EQUAL(addr, &c->ip4.addr_seen))
-		return false;
-
 	return true;
 }
 
@@ -768,10 +785,16 @@ uint8_t fwd_nat_from_host(const struct ctx *c, uint8_t proto,
 		 * match.
 		 */
 		if (inany_v4(&ini->eaddr)) {
-			if (c->host_lo_to_ns_lo)
+			const struct in_addr *guest_addr;
+
+			if (c->host_lo_to_ns_lo) {
 				tgt->eaddr = inany_loopback4;
-			else
-				tgt->eaddr = inany_from_v4(c->ip4.addr_seen);
+			} else {
+				guest_addr = fwd_guest_addr4(c);
+				if (!guest_addr)
+					return PIF_NONE;
+				tgt->eaddr = inany_from_v4(*guest_addr);
+			}
 			tgt->oaddr = inany_any4;
 		} else {
 			if (c->host_lo_to_ns_lo)
@@ -803,7 +826,11 @@ uint8_t fwd_nat_from_host(const struct ctx *c, uint8_t proto,
 	tgt->oport = ini->eport;
 
 	if (inany_v4(&tgt->oaddr)) {
-		tgt->eaddr = inany_from_v4(c->ip4.addr_seen);
+		const struct in_addr *guest_addr = fwd_guest_addr4(c);
+
+		if (!guest_addr)
+			return PIF_NONE;
+		tgt->eaddr = inany_from_v4(*guest_addr);
 	} else {
 		if (inany_is_linklocal6(&tgt->oaddr))
 			tgt->eaddr.a6 = c->ip6.addr_ll_seen;
diff --git a/inany.h b/inany.h
index 07bfc3d..a334da9 100644
--- a/inany.h
+++ b/inany.h
@@ -300,6 +300,7 @@ int inany_prefix_pton(const char *src, union inany_addr *dst, int *prefix_len);
 /* Flags for struct inany_addr_entry */
 #define INANY_ADDR_CONFIGURED	(1 << 0)	/* User set via -a */
 #define INANY_ADDR_HOST	(1 << 1)	/* From host interface */
+#define INANY_ADDR_OBSERVED	(1 << 2)	/* Seen in guest traffic */
 
 /**
  * struct inany_addr_entry - Unified IPv4/IPv6 address entry
diff --git a/migrate.c b/migrate.c
index 48d63a0..01be6f1 100644
--- a/migrate.c
+++ b/migrate.c
@@ -57,8 +57,16 @@ static int seen_addrs_source_v1(struct ctx *c,
 	struct migrate_seen_addrs_v1 addrs = {
 		.addr6 = c->ip6.addr_seen,
 		.addr6_ll = c->ip6.addr_ll_seen,
-		.addr4 = c->ip4.addr_seen,
 	};
+	int i;
+
+	/* Find first observed IPv4 address */
+	for (i = 0; i < c->ip4.addr_count; i++) {
+		if (c->ip4.addrs[i].flags & INANY_ADDR_OBSERVED) {
+			addrs.addr4 = *inany_v4(&c->ip4.addrs[i].addr);
+			break;
+		}
+	}
 
 	(void)stage;
 
@@ -70,6 +78,33 @@ static int seen_addrs_source_v1(struct ctx *c,
 	return 0;
 }
 
+/**
+ * migrate_observed_addr4() - Add observed IPv4 address to ip4_ctx address array
+ * @c:		Execution context
+ * @addr:	IPv4 address to add
+ */
+static void migrate_observed_addr4(struct ctx *c, const struct in_addr *addr)
+{
+	int i;
+
+	if (IN4_IS_ADDR_UNSPECIFIED(addr))
+		return;
+
+	for (i = 0; i < c->ip4.addr_count; i++) {
+		if (IN4_ARE_ADDR_EQUAL(addr, inany_v4(&c->ip4.addrs[i].addr))) {
+			c->ip4.addrs[i].flags |= INANY_ADDR_OBSERVED;
+			return;
+		}
+	}
+
+	if (c->ip4.addr_count < IP4_MAX_ADDRS) {
+		c->ip4.addrs[c->ip4.addr_count].addr = inany_from_v4(*addr);
+		c->ip4.addrs[c->ip4.addr_count].prefix_len = 0;
+		c->ip4.addrs[c->ip4.addr_count].flags = INANY_ADDR_OBSERVED;
+		c->ip4.addr_count++;
+	}
+}
+
 /**
  * seen_addrs_target_v1() - Receive and use guest observed addresses on target
  * @c:		Execution context
@@ -82,6 +117,7 @@ static int seen_addrs_target_v1(struct ctx *c,
 				const struct migrate_stage *stage, int fd)
 {
 	struct migrate_seen_addrs_v1 addrs;
+	struct in_addr addr4;
 
 	(void)stage;
 
@@ -90,7 +126,11 @@ static int seen_addrs_target_v1(struct ctx *c,
 
 	c->ip6.addr_seen = addrs.addr6;
 	c->ip6.addr_ll_seen = addrs.addr6_ll;
-	c->ip4.addr_seen = addrs.addr4;
+
+	/* Avoid alignment warning */
+	addr4 = addrs.addr4;
+	migrate_observed_addr4(c, &addr4);
+
 	memcpy(c->guest_mac, addrs.mac, sizeof(c->guest_mac));
 
 	return 0;
diff --git a/passt.h b/passt.h
index 929b474..db09da5 100644
--- a/passt.h
+++ b/passt.h
@@ -68,7 +68,6 @@ enum passt_modes {
  * struct ip4_ctx - IPv4 execution context
  * @addrs:		IPv4 addresses assigned to guest
  * @addr_count:	Number of addresses in addrs[] array
- * @addr_seen:		Latest IPv4 address seen as source from tap
  * @guest_gw:		IPv4 gateway as seen by the guest
  * @map_host_loopback:	Outbound connections to this address are NATted to the
  *                      host's 127.0.0.1
@@ -87,7 +86,6 @@ struct ip4_ctx {
 	struct inany_addr_entry addrs[IP4_MAX_ADDRS];
 	int addr_count;
 
-	struct in_addr addr_seen;
 	struct in_addr guest_gw;
 	struct in_addr map_host_loopback;
 	struct in_addr map_guest_addr;
@@ -127,7 +125,6 @@ struct ip6_ctx {
 	/* PIF_TAP addresses */
 	struct inany_addr_entry addrs[IP6_MAX_ADDRS];
 	int addr_count;
-
 	struct in6_addr addr_seen;
 	struct in6_addr addr_ll_seen;
 	struct in6_addr guest_gw;
diff --git a/tap.c b/tap.c
index 7c50013..1a52adb 100644
--- a/tap.c
+++ b/tap.c
@@ -161,6 +161,37 @@ void tap_send_single(const struct ctx *c, const void *data, size_t l2len)
 	}
 }
 
+/**
+ * tap_check_src_addr4() - Note an IPv4 address seen in guest traffic
+ * @c:		Execution context
+ * @addr:	IPv4 address seen as source from guest
+ *
+ * Add the address to ip4.addrs[] with OBSERVED flag if not already present.
+ */
+static void tap_check_src_addr4(struct ctx *c, const struct in_addr *addr)
+{
+	int i;
+
+	/* Check if already in array */
+	for (i = 0; i < c->ip4.addr_count; i++) {
+		if (IN4_ARE_ADDR_EQUAL(addr, inany_v4(&c->ip4.addrs[i].addr))) {
+			c->ip4.addrs[i].flags |= INANY_ADDR_OBSERVED;
+			return;
+		}
+	}
+
+	/* Add new entry if space available */
+	if (c->ip4.addr_count < IP4_MAX_ADDRS) {
+		c->ip4.addrs[c->ip4.addr_count].addr = inany_from_v4(*addr);
+		c->ip4.addrs[c->ip4.addr_count].prefix_len = 0;
+		c->ip4.addrs[c->ip4.addr_count].flags = INANY_ADDR_OBSERVED;
+		debug("added new IPv4 address at index %d", c->ip6.addr_count);
+		c->ip4.addr_count++;
+	} else {
+		warn("IPv4 address table full, can't add new address");
+	}
+}
+
 /**
  * tap_ip6_daddr() - Normal IPv6 destination address for inbound packets
  * @c:		Execution context
@@ -771,8 +802,8 @@ resume:
 			continue;
 		}
 
-		if (iph->saddr && c->ip4.addr_seen.s_addr != iph->saddr)
-			c->ip4.addr_seen.s_addr = iph->saddr;
+		if (iph->saddr)
+			tap_check_src_addr4(c, (const struct in_addr *)&iph->saddr);
 
 		if (!iov_drop_header(&data, hlen))
 			continue;
@@ -950,9 +981,6 @@ resume:
 			if (IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addr_seen)) {
 				c->ip6.addr_seen = *saddr;
 			}
-
-			if (IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addrs[0].addr.a6))
-				c->ip6.addrs[0].addr.a6 = *saddr;
 		} else if (!IN6_IS_ADDR_UNSPECIFIED(saddr)){
 			c->ip6.addr_seen = *saddr;
 		}
-- 
2.52.0


  parent reply	other threads:[~2026-01-18 22:16 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-01-18 22:16 [PATCH v2 0/9] Introduce multiple addresses Jon Maloy
2026-01-18 22:16 ` [PATCH v2 1/9] conf: Support CIDR notation for -a/--address option Jon Maloy
2026-01-19  5:02   ` David Gibson
2026-01-21  8:15   ` Stefano Brivio
2026-01-18 22:16 ` [PATCH v2 2/9] ip: Introduce unified multi-address data structures Jon Maloy
2026-01-19  7:22   ` David Gibson
2026-01-21 13:02   ` Stefano Brivio
2026-01-18 22:16 ` [PATCH v2 3/9] conf: Refactor conf_print() for multi-address support Jon Maloy
2026-01-19  7:25   ` David Gibson
2026-01-21 13:02   ` Stefano Brivio
2026-01-18 22:16 ` [PATCH v2 4/9] fwd: Check all configured addresses in guest accessibility functions Jon Maloy
2026-01-19  7:29   ` David Gibson
2026-01-18 22:16 ` [PATCH v2 5/9] arp: Check all configured addresses in ARP filtering Jon Maloy
2026-01-19  8:28   ` David Gibson
2026-01-18 22:16 ` [PATCH v2 6/9] conf: Allow multiple -a/--address options per address family Jon Maloy
2026-01-19  8:41   ` David Gibson
2026-01-18 22:16 ` [PATCH v2 7/9] pasta: Unify address configuration paths using address array Jon Maloy
2026-01-18 22:16 ` Jon Maloy [this message]
2026-01-18 22:16 ` [PATCH v2 9/9] ip: Track observed guest IPv6 addresses in unified " 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=20260118221612.2115386-9-jmaloy@redhat.com \
    --to=jmaloy@redhat.com \
    --cc=david@gibson.dropbear.id.au \
    --cc=dgibson@redhat.com \
    --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).