From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: passt.top; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: passt.top; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=dWXVV4Ej; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by passt.top (Postfix) with ESMTPS id EC95E5A0269 for ; Sun, 22 Mar 2026 01:43:52 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1774140231; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=czx3bBOQxLEVfIkmIAl5KQZupjZ7qVUzFRVXTOX68MA=; b=dWXVV4Ejhh7goFTeQH1YnFOdjm3g6z14dAIZcsHeqYQ5VuG/Mgb0xl2QqX57oCv85VIzt5 e1qagb3Dfb1ypGmetPuAM+mjb4UGCLAoRN0OZfAQxu9ugB9OdaGu1gImlPDHBKM49tLZjG vx+G2cFhPGhHme4aT3vLi9XUAwQyX74= Received: from mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-574-i9J67QQSPdeyiUUZg2yGgA-1; Sat, 21 Mar 2026 20:43:48 -0400 X-MC-Unique: i9J67QQSPdeyiUUZg2yGgA-1 X-Mimecast-MFC-AGG-ID: i9J67QQSPdeyiUUZg2yGgA_1774140227 Received: from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 362D3180047F; Sun, 22 Mar 2026 00:43:47 +0000 (UTC) Received: from jmaloy-thinkpadp16vgen1.rmtcaqc.csb (unknown [10.22.64.183]) by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 5816318001FE; Sun, 22 Mar 2026 00:43:46 +0000 (UTC) From: Jon Maloy To: sbrivio@redhat.com, dgibson@redhat.com, david@gibson.dropbear.id.au, jmaloy@redhat.com, passt-dev@passt.top Subject: [PATCH v6 09/13] ip: Track observed guest IPv6 addresses in unified address array Date: Sat, 21 Mar 2026 20:43:29 -0400 Message-ID: <20260322004333.365713-10-jmaloy@redhat.com> In-Reply-To: <20260322004333.365713-1-jmaloy@redhat.com> References: <20260322004333.365713-1-jmaloy@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.93 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: NIFaOTLR_0susreHueS25VTJWZzxZBqxFNHtGhngmsY_1774140227 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: 8bit content-type: text/plain; charset="US-ASCII"; x-default=true Message-ID-Hash: R4AIIEHXVML6UQBSQ7JCWGKLPZFWNKF7 X-Message-ID-Hash: R4AIIEHXVML6UQBSQ7JCWGKLPZFWNKF7 X-MailFrom: jmaloy@redhat.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.8 Precedence: list List-Id: Development discussion and patches for passt Archived-At: Archived-At: List-Archive: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: 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. 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 --- 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. --- conf.c | 6 ------ dhcpv6.c | 20 +++++++++++-------- dhcpv6.h | 2 +- fwd.c | 38 ++++++++++++++++++++++-------------- inany.h | 3 +++ migrate.c | 33 +++++++++++++++++++++++-------- passt.h | 4 ---- pasta.c | 7 +++++-- tap.c | 58 ++++++++++++++++++++++++++++++++++++------------------- 9 files changed, 107 insertions(+), 64 deletions(-) diff --git a/conf.c b/conf.c index 1c9f07c..320a9e4 100644 --- a/conf.c +++ b/conf.c @@ -767,8 +767,6 @@ static unsigned int conf_ip4(struct ctx *c, unsigned int ifi) } if (!rc || !fwd_get_addr(c, AF_INET, 0, 0)) return 0; - - a = fwd_get_addr(c, AF_INET, CONF_ADDR_HOST, 0); } ip4->our_tap_addr = ip4->guest_gw; @@ -829,7 +827,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) { @@ -838,9 +835,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 3a007bf..313c243 100644 --- a/dhcpv6.c +++ b/dhcpv6.c @@ -382,8 +382,12 @@ static void dhcpv6_send_ia_notonlink(struct ctx *c, { const struct in6_addr *src = &c->ip6.our_tap_ll; struct opt_hdr *ia = (struct opt_hdr *)resp_not_on_link.var; + const struct in6_addr *dst = tap_ip6_daddr(c, src); size_t n; + if (!dst) + return; + info("DHCPv6: received CONFIRM with inappropriate IA," " sending NotOnLink status in REPLY"); @@ -405,7 +409,7 @@ static void dhcpv6_send_ia_notonlink(struct ctx *c, resp_not_on_link.hdr.xid = xid; - tap_udp6_send(c, src, 547, tap_ip6_daddr(c, src), 546, + tap_udp6_send(c, src, 547, dst, 546, xid, &resp_not_on_link, n); } @@ -549,7 +553,7 @@ static size_t dhcpv6_client_fqdn_fill(const struct iov_tail *data, * Return: 0 if it's not a DHCPv6 message, 1 if handled, -1 on failure */ int dhcpv6(struct ctx *c, struct iov_tail *data, - const struct in6_addr *saddr, const struct in6_addr *daddr) + const struct in6_addr *daddr) { const struct opt_server_id *server_id = NULL; const struct opt_hdr *client_id = NULL; @@ -565,9 +569,9 @@ int dhcpv6(struct ctx *c, struct iov_tail *data, /* cppcheck-suppress [variableScope,unmatchedSuppression] */ struct opt_hdr client_id_storage; /* cppcheck-suppress [variableScope,unmatchedSuppression] */ + const struct in6_addr *src, *dst; struct opt_ia_na ia_storage; const struct guest_addr *a; - const struct in6_addr *src; struct msg_hdr mh_storage; const struct msg_hdr *mh; struct udphdr uh_storage; @@ -593,9 +597,12 @@ int dhcpv6(struct ctx *c, struct iov_tail *data, if (mlen + sizeof(*uh) != ntohs(uh->len) || mlen < sizeof(*mh)) return -1; - c->ip6.addr_ll_seen = *saddr; + /* Guest LL address already recorded by tap_check_src_addr6() */ src = &c->ip6.our_tap_ll; + dst = tap_ip6_daddr(c, src); + if (!dst) + return -1; mh = IOV_REMOVE_HEADER(data, mh_storage); if (!mh) @@ -683,10 +690,7 @@ int dhcpv6(struct ctx *c, struct iov_tail *data, resp.hdr.xid = mh->xid; - tap_udp6_send(c, src, 547, tap_ip6_daddr(c, src), 546, - mh->xid, &resp, n); - if (a) - c->ip6.addr_seen = a->addr.a6; + tap_udp6_send(c, src, 547, dst, 546, mh->xid, &resp, n); return 1; } diff --git a/dhcpv6.h b/dhcpv6.h index c706dfd..eda133f 100644 --- a/dhcpv6.h +++ b/dhcpv6.h @@ -7,7 +7,7 @@ #define DHCPV6_H int dhcpv6(struct ctx *c, struct iov_tail *data, - struct in6_addr *saddr, struct in6_addr *daddr); + const struct in6_addr *daddr); void dhcpv6_init(const struct ctx *c); #endif /* DHCPV6_H */ diff --git a/fwd.c b/fwd.c index 28a721e..b3f5dc0 100644 --- a/fwd.c +++ b/fwd.c @@ -1095,14 +1095,6 @@ static bool fwd_guest_accessible(const struct ctx *c, if (inany_equals(addr, &a->addr)) 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; } @@ -1305,10 +1297,20 @@ 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 { + const struct guest_addr *a; + + 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; } @@ -1346,10 +1348,16 @@ uint8_t fwd_nat_from_host(const struct ctx *c, tgt->eaddr = a->addr; } else { - if (inany_is_linklocal6(&tgt->oaddr)) - tgt->eaddr.a6 = c->ip6.addr_ll_seen; - else - tgt->eaddr.a6 = c->ip6.addr_seen; + bool linklocal = inany_is_linklocal6(&tgt->oaddr); + int excl = linklocal ? 0 : CONF_ADDR_LINKLOCAL; + const struct guest_addr *a; + + a = fwd_select_addr(c, AF_INET6, CONF_ADDR_OBSERVED, + CONF_ADDR_USER | CONF_ADDR_HOST, excl); + if (!a) + return PIF_NONE; + + tgt->eaddr = a->addr; } return PIF_TAP; diff --git a/inany.h b/inany.h index 7b23cb0..82dd102 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 1e02720..a92301b 100644 --- a/migrate.c +++ b/migrate.c @@ -56,21 +56,30 @@ 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; - /* IPv4 observed address, with fallback to configured address */ + /* IPv4 observed address, with fallback to any other non-LL address */ a = fwd_select_addr(c, AF_INET, CONF_ADDR_OBSERVED, CONF_ADDR_USER | CONF_ADDR_HOST, CONF_ADDR_LINKLOCAL); 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,19 +100,27 @@ 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; (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; - if (addrs.addr4.s_addr) fwd_set_addr(c, &inany_from_v4(addrs.addr4), CONF_ADDR_OBSERVED, 0); + 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); + memcpy(c->guest_mac, addrs.mac, sizeof(c->guest_mac)); return 0; diff --git a/passt.h b/passt.h index 5452225..db2f10d 100644 --- a/passt.h +++ b/passt.h @@ -124,8 +124,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 { * @no_copy_addrs: Don't copy all addresses when configuring namespace */ struct ip6_ctx { - 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 b8d7cf4..fcb169a 100644 --- a/pasta.c +++ b/pasta.c @@ -370,6 +370,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); @@ -417,11 +418,13 @@ void pasta_ns_conf(struct ctx *c) if (!c->ifi6) goto done; - rc = nl_addr_get_ll(nl_sock_ns, c->pasta_ifi, - &c->ip6.addr_ll_seen); + 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_HOST, 0); rc = nl_addr_set_ll_nodad(nl_sock_ns, c->pasta_ifi); if (rc < 0) diff --git a/tap.c b/tap.c index c75a4df..07f92bb 100644 --- a/tap.c +++ b/tap.c @@ -173,19 +173,51 @@ static void tap_check_src_addr4(struct ctx *c, const struct in_addr *addr) fwd_set_addr(c, &inany_from_v4(*addr), CONF_ADDR_OBSERVED, 0); } +/** + * tap_check_src_addr6() - Note an IPv6 address seen in guest traffic + * @c: Execution context + * @addr: IPv6 address seen as source from guest + */ +static void tap_check_src_addr6(struct ctx *c, const struct in6_addr *addr) +{ + uint8_t flags = CONF_ADDR_OBSERVED; + + if (IN6_IS_ADDR_UNSPECIFIED(addr)) + return; + + if (IN6_IS_ADDR_LINKLOCAL(addr)) + flags |= CONF_ADDR_LINKLOCAL; + + fwd_set_addr(c, &inany_from_v6(*addr), flags, 0); +} + /** * tap_ip6_daddr() - Normal IPv6 destination address for inbound packets * @c: Execution context * @src: Source address * - * Return: pointer to IPv6 address + * Return: pointer to IPv6 address, NULL if no suitable address found */ const struct in6_addr *tap_ip6_daddr(const struct ctx *c, const struct in6_addr *src) { - if (IN6_IS_ADDR_LINKLOCAL(src)) - return &c->ip6.addr_ll_seen; - return &c->ip6.addr_seen; + const struct guest_addr *a; + + if (IN6_IS_ADDR_LINKLOCAL(src)) { + /* Link-local: first LL address in array */ + a = fwd_get_addr(c, AF_INET6, CONF_ADDR_LINKLOCAL, 0); + } else { + /* Global: observed non-LL first, then any non-LL */ + a = fwd_select_addr(c, AF_INET6, CONF_ADDR_OBSERVED, + CONF_ADDR_USER | CONF_ADDR_HOST, + CONF_ADDR_LINKLOCAL); + } + + if (a) + return &a->addr.a6; + + debug("No suitable IPv6 guest address found"); + return NULL; } /** @@ -958,21 +990,7 @@ resume: continue; } - if (IN6_IS_ADDR_LINKLOCAL(saddr)) { - c->ip6.addr_ll_seen = *saddr; - - if (IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addr_seen)) { - c->ip6.addr_seen = *saddr; - } - - if (!fwd_get_addr(c, AF_INET6, 0, 0)) { - union inany_addr addr = { .a6 = *saddr }; - - fwd_set_addr(c, &addr, CONF_ADDR_LINKLOCAL, 64); - } - } else if (!IN6_IS_ADDR_UNSPECIFIED(saddr)){ - c->ip6.addr_seen = *saddr; - } + tap_check_src_addr6(c, saddr); if (proto == IPPROTO_ICMPV6) { struct iov_tail ndp_data; @@ -1003,7 +1021,7 @@ resume: if (proto == IPPROTO_UDP) { struct iov_tail uh_data = data; - if (dhcpv6(c, &uh_data, saddr, daddr)) + if (dhcpv6(c, &uh_data, daddr)) continue; } -- 2.52.0