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 v4 10/12] fwd: Unify fwd_set_observed_ip4() and fwd_set_observed_ip6()
Date: Tue, 17 Feb 2026 17:18:12 -0500 [thread overview]
Message-ID: <20260217221814.4053583-11-jmaloy@redhat.com> (raw)
In-Reply-To: <20260217221814.4053583-1-jmaloy@redhat.com>
Create a common fwd_set_observed() function that handles both IPv4 and
IPv6 observed addresses. The function determines the address family from
the input and uses the appropriate reserved position (0 for IPv4, 1 for
IPv6) for O(1) lookup.
Call sites are updated to use fwd_set_observed() directly with
union inany_addr.
This reduces code duplication and ensures consistent behavior between
IPv4 and IPv6 address tracking.
Signed-off-by: Jon Maloy <jmaloy@redhat.com>
---
fwd.c | 129 ++++++++++++++++++------------------------------------
fwd.h | 3 +-
inany.h | 3 ++
migrate.c | 6 +--
pasta.c | 2 +-
tap.c | 4 +-
6 files changed, 53 insertions(+), 94 deletions(-)
diff --git a/fwd.c b/fwd.c
index 8598fff..d5f4932 100644
--- a/fwd.c
+++ b/fwd.c
@@ -518,119 +518,76 @@ const union inany_addr *fwd_guest_addr(const struct ctx *c, sa_family_t af,
}
/**
- * fwd_set_observed_ip4() - Set observed IPv4 guest address
+ * fwd_set_observed() - Set observed guest address (IPv4 or IPv6)
* @c: Execution context
- * @addr: IPv4 address observed in guest traffic
+ * @addr: Address observed in guest traffic
*
- * Mark @addr as the observed guest address. The observed address is always
- * kept at position 0 for O(1) lookup. Only one address can have the OBSERVED
- * flag at a time.
+ * Mark @addr as the observed guest address. Observed addresses are kept at
+ * reserved positions for O(1) lookup: position 0 for IPv4, position 1 for
+ * IPv6. Only one address per family can have the OBSERVED flag at a time.
+ * Link-local IPv6 addresses also get the LINKLOCAL flag.
*/
-void fwd_set_observed_ip4(struct ctx *c, const struct in_addr *addr)
+void fwd_set_observed(struct ctx *c, const union inany_addr *addr)
{
- struct inany_addr_entry *e = &c->addrs[0];
- int i;
-
- if (!addr->s_addr)
- return;
-
- /* Fast path: check if already observed at position 0 */
- if (c->addr_count > 0 && (e->flags & CONF_ADDR_OBSERVED) &&
- inany_equals4(&e->addr, addr))
- return;
-
- /* Slow path: new observed address - insert at position 0 */
- if (c->addr_count >= INANY_MAX_ADDRS) {
- debug("Address table full, can't add observed IPv4");
- return;
- }
-
- /* Make room and insert at position 0 */
- memmove(&c->addrs[1], e, c->addr_count * sizeof(*e));
- c->addr_count++;
- inany_from_af(&e->addr, AF_INET, addr);
- e->prefix_len = 0;
- e->flags = CONF_ADDR_OBSERVED;
-
- /* Handle old observed IPv4 address, if any */
- for (i = 1; i < c->addr_count; i++) {
- e = &c->addrs[i];
-
- if (!inany_v4(&e->addr) || !(e->flags & CONF_ADDR_OBSERVED))
- continue;
-
- e->flags &= ~CONF_ADDR_OBSERVED;
-
- /* Remove if no other flags, or if addr is duplicate */
- if (!e->flags || inany_equals4(&e->addr, addr)) {
- memmove(&c->addrs[i], &c->addrs[i + 1],
- (c->addr_count - i - 1) * sizeof(*e));
- c->addr_count--;
- }
- break;
- }
-}
-
-/**
- * fwd_set_observed_ip6() - Set observed IPv6 guest address
- * @c: Execution context
- * @addr: IPv6 address observed in guest traffic
- *
- * Mark @addr as the observed guest address. The observed address is always
- * kept at position 1 for O(1) lookup. Only one IPv6 address can have the
- * OBSERVED flag at a time. Link-local addresses also get LINKLOCAL flag.
- */
-void fwd_set_observed_ip6(struct ctx *c, const struct in6_addr *addr)
-{
- struct inany_addr_entry *e;
uint8_t flags = CONF_ADDR_OBSERVED;
+ bool is_v4 = !!inany_v4(addr);
+ struct inany_addr_entry *e;
+ int pos = is_v4 ? 0 : 1;
int i;
- if (IN6_IS_ADDR_UNSPECIFIED(addr))
- return;
-
- if (IN6_IS_ADDR_LINKLOCAL(addr))
- flags |= CONF_ADDR_LINKLOCAL;
+ /* Check for unspecified address */
+ if (is_v4) {
+ if (inany_is_unspecified4(addr))
+ return;
+ } else {
+ if (IN6_IS_ADDR_UNSPECIFIED(&addr->a6))
+ return;
+ if (IN6_IS_ADDR_LINKLOCAL(&addr->a6))
+ flags |= CONF_ADDR_LINKLOCAL;
+ }
- /* Fast path: check if already observed at position 1 */
- if (c->addr_count > 1) {
- e = &c->addrs[1];
- if ((e->flags & CONF_ADDR_OBSERVED) && !inany_v4(&e->addr) &&
- IN6_ARE_ADDR_EQUAL(&e->addr.a6, addr))
+ /* Fast path: check if already observed at reserved position */
+ if (c->addr_count > pos) {
+ e = &c->addrs[pos];
+ if ((e->flags & CONF_ADDR_OBSERVED) &&
+ (is_v4 == !!inany_v4(&e->addr)) &&
+ inany_equals(&e->addr, addr))
return;
}
- /* Slow path: new observed address - insert at position 1 */
+ /* Slow path: new observed address */
if (c->addr_count >= INANY_MAX_ADDRS) {
- debug("Address table full, can't add observed IPv6");
+ debug("Address table full, can't add observed %s",
+ is_v4 ? "IPv4" : "IPv6");
return;
}
- /* Make room at position 1 */
- if (c->addr_count > 1) {
- e = &c->addrs[1];
- memmove(&c->addrs[2], e, (c->addr_count - 1) * sizeof(*e));
+ /* Make room at reserved position */
+ if (c->addr_count > pos) {
+ e = &c->addrs[pos];
+ memmove(&c->addrs[pos + 1], e,
+ (c->addr_count - pos) * sizeof(*e));
}
c->addr_count++;
- /* Insert new observed address at position 1 */
- e = &c->addrs[1];
- e->addr.a6 = *addr;
- e->prefix_len = 64;
+ /* Insert new observed address */
+ e = &c->addrs[pos];
+ e->addr = *addr;
+ e->prefix_len = is_v4 ? 0 : 64;
e->flags = flags;
- /* Clean up: find and handle old observed IPv6 address */
- for (i = 2; i < c->addr_count; i++) {
+ /* Clean up: find and handle old observed address of same family */
+ for (i = pos + 1; i < c->addr_count; i++) {
e = &c->addrs[i];
- if (inany_v4(&e->addr) || !(e->flags & CONF_ADDR_OBSERVED))
+ if ((is_v4 != !!inany_v4(&e->addr)) ||
+ !(e->flags & CONF_ADDR_OBSERVED))
continue;
- /* Found old observed - clear flag */
e->flags &= ~CONF_ADDR_OBSERVED;
/* Remove if no other flags, or if addr is duplicate */
- if (!e->flags || IN6_ARE_ADDR_EQUAL(&e->addr.a6, addr)) {
+ if (!e->flags || inany_equals(&e->addr, addr)) {
memmove(&c->addrs[i], &c->addrs[i + 1],
(c->addr_count - i - 1) * sizeof(*e));
c->addr_count--;
diff --git a/fwd.h b/fwd.h
index 4de3890..7bdf462 100644
--- a/fwd.h
+++ b/fwd.h
@@ -17,8 +17,7 @@ void fwd_probe_ephemeral(void);
bool fwd_port_is_ephemeral(in_port_t port);
const union inany_addr *fwd_guest_addr(const struct ctx *c, sa_family_t af,
uint8_t incl, uint8_t excl);
-void fwd_set_observed_ip4(struct ctx *c, const struct in_addr *addr);
-void fwd_set_observed_ip6(struct ctx *c, const struct in6_addr *addr);
+void fwd_set_observed(struct ctx *c, const union inany_addr *addr);
enum fwd_ports_mode {
FWD_UNSET = 0,
diff --git a/inany.h b/inany.h
index c7d6027..8b7caa5 100644
--- a/inany.h
+++ b/inany.h
@@ -54,6 +54,9 @@ extern const union inany_addr inany_any4;
#define inany_from_v4(a4) \
((union inany_addr)INANY_INIT4((a4)))
+#define inany_from_v6(addr) \
+ ((union inany_addr){ .a6 = (addr) })
+
/** 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 6577334..2dc5c8a 100644
--- a/migrate.c
+++ b/migrate.c
@@ -111,13 +111,13 @@ static int seen_addrs_target_v1(struct ctx *c,
addr6 = addrs.addr6;
addr6_ll = addrs.addr6_ll;
- fwd_set_observed_ip4(c, &addr4);
+ fwd_set_observed(c, &inany_from_v4(addr4));
/* Prefer global over link-local if both present (only one slot) */
if (!IN6_IS_ADDR_UNSPECIFIED(&addr6))
- fwd_set_observed_ip6(c, &addr6);
+ fwd_set_observed(c, &inany_from_v6(addr6));
else
- fwd_set_observed_ip6(c, &addr6_ll);
+ fwd_set_observed(c, &inany_from_v6(addr6_ll));
memcpy(c->guest_mac, addrs.mac, sizeof(c->guest_mac));
diff --git a/pasta.c b/pasta.c
index f16d508..a134af8 100644
--- a/pasta.c
+++ b/pasta.c
@@ -362,7 +362,7 @@ static void pasta_ns_conf_ip6(struct ctx *c)
warn("Can't get LL address from namespace: %s",
strerror_(-rc));
else
- fwd_set_observed_ip6(c, &addr_ll);
+ fwd_set_observed(c, &inany_from_v6(addr_ll));
rc = nl_addr_set_ll_nodad(nl_sock_ns, c->pasta_ifi);
if (rc < 0) {
diff --git a/tap.c b/tap.c
index be698eb..643c139 100644
--- a/tap.c
+++ b/tap.c
@@ -170,7 +170,7 @@ void tap_send_single(const struct ctx *c, const void *data, size_t l2len)
*/
static void tap_check_src_addr4(struct ctx *c, const struct in_addr *addr)
{
- fwd_set_observed_ip4(c, addr);
+ fwd_set_observed(c, &inany_from_v4(*addr));
}
/**
@@ -180,7 +180,7 @@ static void tap_check_src_addr4(struct ctx *c, const struct in_addr *addr)
*/
static void tap_check_src_addr6(struct ctx *c, const struct in6_addr *addr)
{
- fwd_set_observed_ip6(c, addr);
+ fwd_set_observed(c, &inany_from_v6(*addr));
}
/**
--
2.52.0
next prev parent reply other threads:[~2026-02-17 22:18 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-02-17 22:18 [PATCH v4 00/12] Introduce multiple addresses Jon Maloy
2026-02-17 22:18 ` [PATCH v4 01/12] ip: Introduce unified multi-address data structures Jon Maloy
2026-02-17 22:18 ` [PATCH v4 02/12] ip: Introduce for_each_addr() macro for address iteration Jon Maloy
2026-02-17 22:18 ` [PATCH v4 03/12] fwd: Unify guest accessibility checks with unified address array Jon Maloy
2026-02-17 22:18 ` [PATCH v4 04/12] arp: Check all configured addresses in ARP filtering Jon Maloy
2026-02-17 22:18 ` [PATCH v4 05/12] pasta: Extract pasta_ns_conf_ip4/6() to reduce nesting Jon Maloy
2026-02-17 22:18 ` [PATCH v4 06/12] netlink: Return prefix length for IPv6 addresses in nl_addr_get() Jon Maloy
2026-02-17 22:18 ` [PATCH v4 07/12] conf: Allow multiple -a/--address options per address family Jon Maloy
2026-02-17 22:18 ` [PATCH v4 08/12] ip: Track observed guest IPv4 addresses in unified address array Jon Maloy
2026-02-18 14:14 ` Jon Maloy
2026-02-17 22:18 ` [PATCH v4 09/12] ip: Track observed guest IPv6 " Jon Maloy
2026-02-17 22:18 ` Jon Maloy [this message]
2026-02-17 22:18 ` [PATCH v4 11/12] migrate: Rename v1 address functions to v2 for clarity Jon Maloy
2026-02-17 22:18 ` [PATCH v4 12/12] 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=20260217221814.4053583-11-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).