We remove the addr_seen and addr_ll_seen fields in struct ip6_ctx
and replace them by setting INANY_ADDR_OBSERVED and a new
INANY_ADDR_LINKLOCAL flag in the corresponding entry in the
address array. If the seen address is not present in the array
we add it first.
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
---
conf.c | 2 --
dhcpv6.c | 6 ++---
dhcpv6.h | 2 +-
fwd.c | 70 +++++++++++++++++++++++++++++++++++++------------
inany.h | 3 ++-
migrate.c | 67 ++++++++++++++++++++++++++++++++++++++++-------
passt.h | 7 ++---
pasta.c | 16 ++++++++++--
tap.c | 78 +++++++++++++++++++++++++++++++++++++++++++++----------
9 files changed, 198 insertions(+), 53 deletions(-)
diff --git a/conf.c b/conf.c
index 8f0091d..d670bb7 100644
--- a/conf.c
+++ b/conf.c
@@ -862,8 +862,6 @@ static unsigned int conf_ip6(unsigned int ifi, struct ip6_ctx *ip6)
}
}
- ip6->addr_seen = ip6->addrs[0].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 f45dece..027b4be 100644
--- a/dhcpv6.c
+++ b/dhcpv6.c
@@ -546,10 +546,11 @@ 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;
+
/* The _storage variables can't be local to the blocks they're used in,
* because IOV_*_HEADER() may return pointers to them which are
* dereferenced afterwards. Since we don't have Rust-like lifetime
@@ -587,8 +588,6 @@ 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;
-
src = &c->ip6.our_tap_ll;
mh = IOV_REMOVE_HEADER(data, mh_storage);
@@ -679,7 +678,6 @@ int dhcpv6(struct ctx *c, struct iov_tail *data,
tap_udp6_send(c, src, 547, tap_ip6_daddr(c, src), 546,
mh->xid, &resp, n);
- c->ip6.addr_seen = c->ip6.addrs[0].addr.a6;
return 1;
}
diff --git a/dhcpv6.h b/dhcpv6.h
index c706dfd..8cbc769 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);
+ struct in6_addr *daddr);
void dhcpv6_init(const struct ctx *c);
#endif /* DHCPV6_H */
diff --git a/fwd.c b/fwd.c
index 08b0b83..dfdb078 100644
--- a/fwd.c
+++ b/fwd.c
@@ -514,6 +514,44 @@ static const struct in_addr *fwd_guest_addr4(const struct ctx *c)
return NULL;
}
+/**
+ * fwd_guest_addr6() - Get first observed IPv6 guest address
+ * @c: Execution context
+ * @linklocal: If true, find link-local address; if false, find global
+ *
+ * Return: pointer to first observed IPv6 address of requested scope,
+ * or first address of that scope if none observed, or NULL
+ */
+static const struct in6_addr *fwd_guest_addr6(const struct ctx *c,
+ bool linklocal)
+{
+ uint16_t want = INANY_ADDR_OBSERVED;
+ int i;
+
+ if (linklocal)
+ want |= INANY_ADDR_LINKLOCAL;
+
+ /* Find first observed address of matching scope */
+ for (i = 0; i < c->ip6.addr_count; i++) {
+ bool is_ll = !!(c->ip6.addrs[i].flags & INANY_ADDR_LINKLOCAL);
+
+ if (is_ll != linklocal)
+ continue;
+ if (c->ip6.addrs[i].flags & INANY_ADDR_OBSERVED)
+ return &c->ip6.addrs[i].addr.a6;
+ }
+
+ /* Fallback to first address of matching scope */
+ for (i = 0; i < c->ip6.addr_count; i++) {
+ bool is_ll = IN6_IS_ADDR_LINKLOCAL(&c->ip6.addrs[i].addr.a6);
+
+ if (is_ll == linklocal)
+ return &c->ip6.addrs[i].addr.a6;
+ }
+
+ return NULL;
+}
+
/**
* fwd_guest_accessible4() - Is IPv4 address guest-accessible
* @c: Execution context
@@ -562,19 +600,11 @@ static bool fwd_guest_accessible6(const struct ctx *c,
if (IN6_IS_ADDR_LOOPBACK(addr))
return false;
- /* Check against all configured guest addresses */
+ /* Check against all guest addresses (configured, host, or observed) */
for (i = 0; i < c->ip6.addr_count; i++)
if (IN6_ARE_ADDR_EQUAL(addr, &c->ip6.addrs[i].addr.a6))
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) &&
- IN6_ARE_ADDR_EQUAL(addr, &c->ip6.addr_seen))
- return false;
-
return true;
}
@@ -797,10 +827,16 @@ uint8_t fwd_nat_from_host(const struct ctx *c, uint8_t proto,
}
tgt->oaddr = inany_any4;
} else {
- if (c->host_lo_to_ns_lo)
+ const struct in6_addr *guest_addr6;
+
+ if (c->host_lo_to_ns_lo) {
tgt->eaddr = inany_loopback6;
- else
- tgt->eaddr.a6 = c->ip6.addr_seen;
+ } else {
+ guest_addr6 = fwd_guest_addr6(c, false);
+ if (!guest_addr6)
+ return PIF_NONE;
+ tgt->eaddr.a6 = *guest_addr6;
+ }
tgt->oaddr = inany_any6;
}
@@ -832,10 +868,12 @@ uint8_t fwd_nat_from_host(const struct ctx *c, uint8_t proto,
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;
- else
- tgt->eaddr.a6 = c->ip6.addr_seen;
+ bool linklocal = inany_is_linklocal6(&tgt->oaddr);
+ const struct in6_addr *guest_addr6 = fwd_guest_addr6(c, linklocal);
+
+ if (!guest_addr6)
+ return PIF_NONE;
+ tgt->eaddr.a6 = *guest_addr6;
}
return PIF_TAP;
diff --git a/inany.h b/inany.h
index a334da9..ae310e3 100644
--- a/inany.h
+++ b/inany.h
@@ -299,8 +299,9 @@ 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_HOST (1 << 1) /* From host interface */
#define INANY_ADDR_OBSERVED (1 << 2) /* Seen in guest traffic */
+#define INANY_ADDR_LINKLOCAL (1 << 3) /* Link-local address */
/**
* struct inany_addr_entry - Unified IPv4/IPv6 address entry
diff --git a/migrate.c b/migrate.c
index 01be6f1..52f0cdf 100644
--- a/migrate.c
+++ b/migrate.c
@@ -54,12 +54,11 @@ struct migrate_seen_addrs_v1 {
static int seen_addrs_source_v1(struct ctx *c,
const struct migrate_stage *stage, int fd)
{
- struct migrate_seen_addrs_v1 addrs = {
- .addr6 = c->ip6.addr_seen,
- .addr6_ll = c->ip6.addr_ll_seen,
- };
+ struct migrate_seen_addrs_v1 addrs = { 0 };
int i;
+ (void)stage;
+
/* Find first observed IPv4 address */
for (i = 0; i < c->ip4.addr_count; i++) {
if (c->ip4.addrs[i].flags & INANY_ADDR_OBSERVED) {
@@ -68,7 +67,22 @@ static int seen_addrs_source_v1(struct ctx *c,
}
}
- (void)stage;
+ /* Find first observed IPv6 addresses (global and link-local) */
+ for (i = 0; i < c->ip6.addr_count; i++) {
+ struct in6_addr tmp;
+
+ if (!(c->ip6.addrs[i].flags & INANY_ADDR_OBSERVED))
+ continue;
+ if (c->ip6.addrs[i].flags & INANY_ADDR_LINKLOCAL) {
+ tmp = addrs.addr6_ll;
+ if (IN6_IS_ADDR_UNSPECIFIED(&tmp))
+ addrs.addr6_ll = c->ip6.addrs[i].addr.a6;
+ } else {
+ tmp = addrs.addr6;
+ if (IN6_IS_ADDR_UNSPECIFIED(&tmp))
+ addrs.addr6 = c->ip6.addrs[i].addr.a6;
+ }
+ }
memcpy(addrs.mac, c->guest_mac, sizeof(addrs.mac));
@@ -105,6 +119,38 @@ static void migrate_observed_addr4(struct ctx *c, const struct in_addr *addr)
}
}
+/**
+ * migrate_observed_addr6() - Add observed IPv6 address to ip6_ctx address array
+ * @c: Execution context
+ * @addr: IPv6 address to add
+ */
+static void migrate_observed_addr6(struct ctx *c, const struct in6_addr *addr)
+{
+ uint16_t flags = INANY_ADDR_OBSERVED;
+ int i;
+
+ if (IN6_IS_ADDR_UNSPECIFIED(addr))
+ return;
+
+ if (IN6_IS_ADDR_LINKLOCAL(addr))
+ flags |= INANY_ADDR_LINKLOCAL;
+
+ for (i = 0; i < c->ip6.addr_count; i++) {
+ if (IN6_ARE_ADDR_EQUAL(addr, &c->ip6.addrs[i].addr.a6)) {
+ c->ip6.addrs[i].flags |= flags;
+ return;
+ }
+ }
+
+ if (c->ip6.addr_count < IP6_MAX_ADDRS) {
+ c->ip6.addrs[c->ip6.addr_count].addr.a6 = *addr;
+ c->ip6.addrs[c->ip6.addr_count].prefix_len = 0;
+ c->ip6.addrs[c->ip6.addr_count].flags = flags;
+ debug("added new entry at index %d", c->ip6.addr_count);
+ c->ip6.addr_count++;
+ }
+}
+
/**
* seen_addrs_target_v1() - Receive and use guest observed addresses on target
* @c: Execution context
@@ -117,6 +163,7 @@ static int seen_addrs_target_v1(struct ctx *c,
const struct migrate_stage *stage, int fd)
{
struct migrate_seen_addrs_v1 addrs;
+ struct in6_addr addr6, addr6_ll;
struct in_addr addr4;
(void)stage;
@@ -124,12 +171,14 @@ static int seen_addrs_target_v1(struct ctx *c,
if (read_all_buf(fd, &addrs, sizeof(addrs)))
return errno;
- c->ip6.addr_seen = addrs.addr6;
- c->ip6.addr_ll_seen = addrs.addr6_ll;
-
- /* Avoid alignment warning */
+ /* Copy from packed struct to avoid alignment issues */
addr4 = addrs.addr4;
+ addr6 = addrs.addr6;
+ addr6_ll = addrs.addr6_ll;
+
migrate_observed_addr4(c, &addr4);
+ migrate_observed_addr6(c, &addr6);
+ migrate_observed_addr6(c, &addr6_ll);
memcpy(c->guest_mac, addrs.mac, sizeof(c->guest_mac));
diff --git a/passt.h b/passt.h
index db09da5..665ce2b 100644
--- a/passt.h
+++ b/passt.h
@@ -104,10 +104,8 @@ struct ip4_ctx {
/**
* struct ip6_ctx - IPv6 execution context
- * @addrs: IPv6 addresses assigned to guest
+ * @addrs: IPv6 addresses assigned to guest (with flags)
* @addr_count: Number of addresses in addrs[] array
- * @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]
@@ -125,8 +123,7 @@ 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;
struct in6_addr map_host_loopback;
struct in6_addr map_guest_addr;
diff --git a/pasta.c b/pasta.c
index faf1a73..aee1fb4 100644
--- a/pasta.c
+++ b/pasta.c
@@ -369,11 +369,19 @@ void pasta_ns_conf(struct ctx *c)
}
if (c->ifi6) {
- rc = nl_addr_get_ll(nl_sock_ns, c->pasta_ifi,
- &c->ip6.addr_ll_seen);
+ struct in6_addr addr_ll;
+
+ 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 if (c->ip6.addr_count < IP6_MAX_ADDRS) {
+ int idx = c->ip6.addr_count++;
+
+ c->ip6.addrs[idx].addr.a6 = addr_ll;
+ c->ip6.addrs[idx].prefix_len = 64;
+ c->ip6.addrs[idx].flags =
+ INANY_ADDR_OBSERVED | INANY_ADDR_LINKLOCAL;
}
rc = nl_addr_set_ll_nodad(nl_sock_ns, c->pasta_ifi);
@@ -391,6 +399,10 @@ void pasta_ns_conf(struct ctx *c)
int i;
for (i = 0; i < c->ip6.addr_count; i++) {
+ /* Skip link-local - kernel auto-configures */
+ if (c->ip6.addrs[i].flags & INANY_ADDR_LINKLOCAL)
+ continue;
+
a = &c->ip6.addrs[i].addr.a6;
if (IN6_IS_ADDR_UNSPECIFIED(a))
continue;
diff --git a/tap.c b/tap.c
index 1a52adb..f18bfd1 100644
--- a/tap.c
+++ b/tap.c
@@ -192,6 +192,42 @@ static void tap_check_src_addr4(struct ctx *c, const struct in_addr *addr)
}
}
+/**
+ * tap_check_src_addr6() - Note an IPv6 address seen in guest traffic
+ * @c: Execution context
+ * @addr: IPv6 address seen as source from guest
+ *
+ * Add the address to ip6.addrs[] with OBSERVED flag if not already present.
+ * Link-local addresses are also marked with LINKLOCAL flag.
+ */
+static void tap_check_src_addr6(struct ctx *c, const struct in6_addr *addr)
+{
+ uint16_t flags = INANY_ADDR_OBSERVED;
+ int i;
+
+ if (IN6_IS_ADDR_LINKLOCAL(addr))
+ flags |= INANY_ADDR_LINKLOCAL;
+
+ /* Check if already in array */
+ for (i = 0; i < c->ip6.addr_count; i++) {
+ if (IN6_ARE_ADDR_EQUAL(addr, &c->ip6.addrs[i].addr.a6)) {
+ c->ip6.addrs[i].flags |= flags;
+ return;
+ }
+ }
+
+ /* Add new entry if space available */
+ if (c->ip6.addr_count < IP6_MAX_ADDRS) {
+ c->ip6.addrs[c->ip6.addr_count].addr.a6 = *addr;
+ c->ip6.addrs[c->ip6.addr_count].prefix_len = 0;
+ c->ip6.addrs[c->ip6.addr_count].flags = flags;
+ debug("added new IPv6 address at index %d", c->ip6.addr_count);
+ c->ip6.addr_count++;
+ } else {
+ warn("IPv6 address table full, can't add address");
+ }
+}
+
/**
* tap_ip6_daddr() - Normal IPv6 destination address for inbound packets
* @c: Execution context
@@ -202,9 +238,32 @@ static void tap_check_src_addr4(struct ctx *c, const struct in_addr *addr)
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;
+ bool want_ll = IN6_IS_ADDR_LINKLOCAL(src);
+ int i;
+
+ /* Find first observed address of matching scope */
+ for (i = 0; i < c->ip6.addr_count; i++) {
+ bool is_ll = !!(c->ip6.addrs[i].flags & INANY_ADDR_LINKLOCAL);
+
+ if (is_ll != want_ll)
+ continue;
+ if (c->ip6.addrs[i].flags & INANY_ADDR_OBSERVED)
+ return &c->ip6.addrs[i].addr.a6;
+ }
+
+ /* Fallback to first address of matching scope */
+ for (i = 0; i < c->ip6.addr_count; i++) {
+ bool is_ll = IN6_IS_ADDR_LINKLOCAL(&c->ip6.addrs[i].addr.a6);
+
+ if (is_ll == want_ll)
+ return &c->ip6.addrs[i].addr.a6;
+ }
+
+ /* Last resort: return first address */
+ if (c->ip6.addr_count > 0)
+ return &c->ip6.addrs[0].addr.a6;
+
+ return &in6addr_any;
}
/**
@@ -975,15 +1034,8 @@ 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;
- }
- } else if (!IN6_IS_ADDR_UNSPECIFIED(saddr)){
- c->ip6.addr_seen = *saddr;
- }
+ if (!IN6_IS_ADDR_UNSPECIFIED(saddr))
+ tap_check_src_addr6(c, saddr);
if (proto == IPPROTO_ICMPV6) {
struct iov_tail ndp_data;
@@ -1014,7 +1066,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