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=Lfkt8fQo; 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 D42CE5A0626 for ; Mon, 13 Apr 2026 02:53:37 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1776041616; 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=RWhVOyBJhgEUqN5HMedvKwlics8s49s6Nwad2ft3l8k=; b=Lfkt8fQodVyW6iKTxCRRdCytFOMnBg8z6Pfa9BEuHmWewLgpruoix3CfQQh9TpigWWPSiv lS/iCWrrYxaxev9GpnNXDn/KhYH99Ir3vT4+syCQuub7wmoD7x71CzmaJIskxM/+UFdy+7 B+hJml1GaExXNfBSmYdK4aOkePtPtRo= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-160-7sXk1-1FPV-fqdJc_9FC1Q-1; Sun, 12 Apr 2026 20:53:33 -0400 X-MC-Unique: 7sXk1-1FPV-fqdJc_9FC1Q-1 X-Mimecast-MFC-AGG-ID: 7sXk1-1FPV-fqdJc_9FC1Q_1776041612 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (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-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id CDC2E195608F; Mon, 13 Apr 2026 00:53:32 +0000 (UTC) Received: from jmaloy-thinkpadp16vgen1.rmtcaqc.csb (unknown [10.22.64.70]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 1AD0330001A1; Mon, 13 Apr 2026 00:53:31 +0000 (UTC) From: Jon Maloy To: sbrivio@redhat.com, david@gibson.dropbear.id.au, jmaloy@redhat.com, passt-dev@passt.top Subject: [PATCH v7 10/13] migrate: Update protocol to v3 for multi-address support Date: Sun, 12 Apr 2026 20:53:16 -0400 Message-ID: <20260413005319.3295910-11-jmaloy@redhat.com> In-Reply-To: <20260413005319.3295910-1-jmaloy@redhat.com> References: <20260413005319.3295910-1-jmaloy@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: gaoGmIerFuE9EDQaPdwnTpTdmorvY-bLPM9cbNNAmHc_1776041612 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: 8bit content-type: text/plain; charset="US-ASCII"; x-default=true Message-ID-Hash: T5WPQ4PAGDMOFOIJYF2TRZA6Q7UNI7GW X-Message-ID-Hash: T5WPQ4PAGDMOFOIJYF2TRZA6Q7UNI7GW 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 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 he wants to apply. Signed-off-by: Jon Maloy --- 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. --- migrate.c | 179 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) diff --git a/migrate.c b/migrate.c index 2dc4dd9..93f67ae 100644 --- a/migrate.c +++ b/migrate.c @@ -44,6 +44,71 @@ 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 + */ +#define MIGRATE_ADDR_USER BIT(0) +#define MIGRATE_ADDR_HOST BIT(1) +#define MIGRATE_ADDR_LINKLOCAL BIT(2) +#define MIGRATE_ADDR_OBSERVED BIT(3) + +/** + * struct migrate_addr_v3 - Migration format for a single address entry + * @addr: IPv6 or IPv4-mapped address (16 bytes) + * @prefix_len: Prefix length + * @flags: MIGRATE_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 MIGRATE_ADDR_* flags + */ +static uint8_t flags_to_migration(uint8_t flags) +{ + uint8_t migration = 0; + + if (flags & CONF_ADDR_USER) + migration |= MIGRATE_ADDR_USER; + if (flags & CONF_ADDR_HOST) + migration |= MIGRATE_ADDR_HOST; + if (flags & CONF_ADDR_LINKLOCAL) + migration |= MIGRATE_ADDR_LINKLOCAL; + if (flags & CONF_ADDR_OBSERVED) + migration |= MIGRATE_ADDR_OBSERVED; + + return migration; +} + +/** + * flags_from_migration() - Convert migration format flags to internal format + * @migration: Migration format MIGRATE_ADDR_* flags + * + * Return: Internal CONF_ADDR_* flags + */ +static uint8_t flags_from_migration(uint8_t migration) +{ + uint8_t flags = 0; + + if (migration & MIGRATE_ADDR_USER) + flags |= CONF_ADDR_USER; + if (migration & MIGRATE_ADDR_HOST) + flags |= CONF_ADDR_HOST; + if (migration & MIGRATE_ADDR_LINKLOCAL) + flags |= CONF_ADDR_LINKLOCAL; + if (migration & MIGRATE_ADDR_OBSERVED) + flags |= CONF_ADDR_OBSERVED; + + return flags; +} + /** * seen_addrs_source_v2() - Copy and send guest observed addresses from source * @c: Execution context @@ -126,6 +191,99 @@ 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) +{ + uint8_t addr_count = c->addr_count; + const struct guest_addr *a; + + (void)stage; + + /* Send count first */ + if (write_all_buf(fd, &addr_count, sizeof(addr_count))) + return errno; + + /* Send each address in stable migration format */ + for_each_addr(a, c->addrs, c->addr_count, 0) { + struct migrate_addr_v3 migration = { + .addr = a->addr.a6, + .prefix_len = htonl(a->prefix_len), + .flags = htonl(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) +{ + uint8_t addr_count, i; + + (void)stage; + + if (read_all_buf(fd, &addr_count, sizeof(addr_count))) + return errno; + + if (addr_count > MAX_GUEST_ADDRS) + 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(ntohl(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[] = { { @@ -146,8 +304,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