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 14/14] migrate: Update protocol to v3 for multi-address support
Date: Thu, 25 Jun 2026 22:45:19 -0400	[thread overview]
Message-ID: <20260626024519.3701556-15-jmaloy@redhat.com> (raw)
In-Reply-To: <20260626024519.3701556-1-jmaloy@redhat.com>

We update the migration protocol to version 3 to support distributing
multiple addresses from the unified address array. The new protocol
migrates all address entries in the array, along with their prefix
lengths and flags, and leaves it to the receiver to filter which
ones it wants to apply.

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

---
v4: - Broke out as separate commit
    - Made number of transferable addresses variable

v6: - Separated internal and wire transfer format

v7: - Using uint32_t instead of uint8_t for fields in migration format
    - Replaced term "wire format" with "migration format"
    - Some other minor changes after feedback from Stefano.

v8: - Moved commit to end of series, to keep the new protocol
      version complete from the beginning.
    - Let flags_to_migration() and flags_from_migration() take uint32_t
      and return value in correct network format.
    - Expanded addr_count to uint32_t
    - Changed read/write method for scalars.
    - Internalized endianness conversions to addr_target/source_v3()
      functions.
    - All above suggested by David.
---
 migrate.c | 198 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 198 insertions(+)

diff --git a/migrate.c b/migrate.c
index 3ca97bf6..82176743 100644
--- a/migrate.c
+++ b/migrate.c
@@ -45,6 +45,89 @@ struct migrate_seen_addrs_v2 {
 	unsigned char mac[ETH_ALEN];
 } __attribute__((packed));
 
+/**
+ * Migration format flags for address migration (v3)
+ * These are stable values - do not change existing assignments
+ * Note that CONF_ADDR_GENERATED is excluded, since it is redundant
+ * and easily can be re-generated at the target
+ */
+#define MIGR_ADDR_USER		BIT(0)
+#define MIGR_ADDR_HOST		BIT(1)
+#define MIGR_ADDR_LINKLOCAL	BIT(2)
+#define MIGR_ADDR_OBSERVED	BIT(3)
+#define MIGR_ADDR_DHCPOFFER	BIT(4)
+#define MIGR_ADDR_DHCPV6OFFER	BIT(5)
+#define MIGR_ADDR_SLAAC		BIT(6)
+
+/**
+ * struct migrate_addr_v3 - Migration format for a single address entry
+ * @addr:	IPv6 or IPv4-mapped address (16 bytes)
+ * @prefix_len:	Prefix length
+ * @flags:	MIGR_ADDR_* flags (migration format)
+ */
+struct migrate_addr_v3 {
+	struct in6_addr addr;
+	uint32_t prefix_len;
+	uint32_t flags;
+} __attribute__((__packed__));
+
+/**
+ * flags_to_migration() - Convert internal flags to stable migration format
+ * @flags:	Internal CONF_ADDR_* flags
+ *
+ * Return: Migration format MIGR_ADDR_* flags
+ */
+static uint32_t flags_to_migration(uint8_t flags)
+{
+	uint32_t migration = 0;
+
+	if (flags & CONF_ADDR_USER)
+		migration |= MIGR_ADDR_USER;
+	if (flags & CONF_ADDR_HOST)
+		migration |= MIGR_ADDR_HOST;
+	if (flags & CONF_ADDR_LINKLOCAL)
+		migration |= MIGR_ADDR_LINKLOCAL;
+	if (flags & CONF_ADDR_OBSERVED)
+		migration |= MIGR_ADDR_OBSERVED;
+	if (flags & CONF_ADDR_DHCPOFFER)
+		migration |= MIGR_ADDR_DHCPOFFER;
+	if (flags & CONF_ADDR_DHCPV6OFFER)
+		migration |= MIGR_ADDR_DHCPV6OFFER;
+	if (flags & CONF_ADDR_SLAAC)
+		migration |= MIGR_ADDR_SLAAC;
+
+	return htonl(migration);
+}
+
+/**
+ * flags_from_migration() - Convert migration format flags to internal format
+ * @migration:	Migration format MIGR_ADDR_* flags
+ *
+ * Return: Internal CONF_ADDR_* flags
+ */
+static uint8_t flags_from_migration(uint32_t migr)
+{
+	uint32_t migration = ntohl(migr);
+	uint8_t flags = 0;
+
+	if (migration & MIGR_ADDR_USER)
+		flags |= CONF_ADDR_USER;
+	if (migration & MIGR_ADDR_HOST)
+		flags |= CONF_ADDR_HOST;
+	if (migration & MIGR_ADDR_LINKLOCAL)
+		flags |= CONF_ADDR_LINKLOCAL;
+	if (migration & MIGR_ADDR_OBSERVED)
+		flags |= CONF_ADDR_OBSERVED;
+	if (migration & MIGR_ADDR_DHCPOFFER)
+		flags |= CONF_ADDR_DHCPOFFER;
+	if (migration & MIGR_ADDR_DHCPV6OFFER)
+		flags |= CONF_ADDR_DHCPV6OFFER;
+	if (migration & MIGR_ADDR_SLAAC)
+		flags |= CONF_ADDR_SLAAC;
+
+	return flags;
+}
+
 /**
  * seen_addrs_source_v2() - Copy and send guest observed addresses from source
  * @c:		Execution context
@@ -130,6 +213,100 @@ static int seen_addrs_target_v2(struct ctx *c,
 	return 0;
 }
 
+/**
+ * addrs_source_v3() - Send all addresses with flags from source
+ * @c:		Execution context
+ * @stage:	Migration stage, unused
+ * @fd:		File descriptor for state transfer
+ *
+ * Send all address entries using a stable migration format. Each field is
+ * serialised explicitly to avoid coupling the migration format to internal
+ * structure layout or flag bit assignments.
+ *
+ * Return: 0 on success, positive error code on failure
+ */
+/* cppcheck-suppress [constParameterCallback, unmatchedSuppression] */
+static int addrs_source_v3(struct ctx *c,
+			   const struct migrate_stage *stage, int fd)
+{
+	uint32_t addr_count = c->addr_count;
+	const struct guest_addr *a;
+
+	(void)stage;
+
+	/* Send count first */
+	if (write_u32(fd, addr_count))
+		return errno;
+
+	/* Send each address in stable migration format */
+	for_each_addr(a, c->addrs, c->addr_count, AF_UNSPEC) {
+		struct migrate_addr_v3 migration = {
+			.addr = a->addr.a6,
+			.prefix_len = htonl(a->prefix_len),
+			.flags = flags_to_migration(a->flags),
+		};
+
+		if (write_all_buf(fd, &migration, sizeof(migration)))
+			return errno;
+	}
+
+	/* Send MAC address */
+	if (write_all_buf(fd, c->guest_mac, ETH_ALEN))
+		return errno;
+
+	return 0;
+}
+
+/**
+ * addrs_target_v3() - Receive addresses on target
+ * @c:		Execution context
+ * @stage:	Migration stage, unused
+ * @fd:		File descriptor for state transfer
+ *
+ * Receive address entries from the stable migration format and merge only
+ * observed addresses into local array. Source sends all addresses for
+ * forward compatibility, but target only applies those marked as observed.
+ *
+ * Return: 0 on success, positive error code on failure
+ */
+static int addrs_target_v3(struct ctx *c,
+			   const struct migrate_stage *stage, int fd)
+{
+	uint32_t addr_count, i;
+
+	(void)stage;
+
+	if (read_u32(fd, &addr_count))
+		return errno;
+
+	if (addr_count > MAX_GUEST_ADDRS) {
+		warn("Truncated list of received migrated addresses");
+		addr_count = MAX_GUEST_ADDRS;
+	}
+	/* Read each address from stable migration format */
+	for (i = 0; i < addr_count; i++) {
+		struct migrate_addr_v3 migration;
+		struct guest_addr addr;
+
+		if (read_all_buf(fd, &migration, sizeof(migration)))
+			return errno;
+
+		addr.addr.a6 = migration.addr;
+		addr.prefix_len = ntohl(migration.prefix_len);
+		addr.flags = flags_from_migration(migration.flags);
+
+		if (addr.flags & CONF_ADDR_OBSERVED) {
+			fwd_set_addr(c, &addr.addr, addr.flags,
+				     addr.prefix_len);
+		}
+	}
+
+	if (read_all_buf(fd, c->guest_mac, ETH_ALEN))
+		return errno;
+
+	return 0;
+}
+
 /* Stages for version 2 */
 static const struct migrate_stage stages_v2[] = {
 	{
@@ -150,8 +327,29 @@ static const struct migrate_stage stages_v2[] = {
 	{ 0 },
 };
 
+/* Stages for version 3 (all addresses, with flags) */
+static const struct migrate_stage stages_v3[] = {
+	{
+		.name = "addresses",
+		.source = addrs_source_v3,
+		.target = addrs_target_v3,
+	},
+	{
+		.name = "prepare flows",
+		.source = flow_migrate_source_pre,
+		.target = NULL,
+	},
+	{
+		.name = "transfer flows",
+		.source = flow_migrate_source,
+		.target = flow_migrate_target,
+	},
+	{ 0 },
+};
+
 /* Supported encoding versions, from latest (most preferred) to oldest */
 static const struct migrate_version versions[] = {
+	{ 3,	stages_v3, },
 	{ 2,	stages_v2, },
 	/* v1 was released, but not widely used.  It had bad endianness for the
 	 * MSS and omitted timestamps, which meant it usually wouldn't work.
-- 
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 ` [PATCH v8 10/14] conf, pasta: Track observed guest IPv6 " Jon Maloy
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 ` Jon Maloy [this message]

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-15-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).