aboutgitcodebugslistschat
path: root/migrate.c
diff options
context:
space:
mode:
Diffstat (limited to 'migrate.c')
-rw-r--r--migrate.c300
1 files changed, 300 insertions, 0 deletions
diff --git a/migrate.c b/migrate.c
new file mode 100644
index 0000000..0fca77b
--- /dev/null
+++ b/migrate.c
@@ -0,0 +1,300 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/* PASST - Plug A Simple Socket Transport
+ * for qemu/UNIX domain socket mode
+ *
+ * PASTA - Pack A Subtle Tap Abstraction
+ * for network namespace/tap device mode
+ *
+ * migrate.c - Migration sections, layout, and routines
+ *
+ * Copyright (c) 2025 Red Hat GmbH
+ * Author: Stefano Brivio <sbrivio@redhat.com>
+ */
+
+#include <errno.h>
+#include <sys/uio.h>
+
+#include "util.h"
+#include "ip.h"
+#include "passt.h"
+#include "inany.h"
+#include "flow.h"
+#include "flow_table.h"
+
+#include "migrate.h"
+#include "repair.h"
+
+/* Magic identifier for migration data */
+#define MIGRATE_MAGIC 0xB1BB1D1B0BB1D1B0
+
+/**
+ * struct migrate_seen_addrs_v1 - Migratable guest addresses for v1 state stream
+ * @addr6: Observed guest IPv6 address
+ * @addr6_ll: Observed guest IPv6 link-local address
+ * @addr4: Observed guest IPv4 address
+ * @mac: Observed guest MAC address
+ */
+struct migrate_seen_addrs_v1 {
+ struct in6_addr addr6;
+ struct in6_addr addr6_ll;
+ struct in_addr addr4;
+ unsigned char mac[ETH_ALEN];
+} __attribute__((packed));
+
+/**
+ * seen_addrs_source_v1() - Copy and send guest observed addresses from source
+ * @c: Execution context
+ * @stage: Migration stage, unused
+ * @fd: File descriptor for state transfer
+ *
+ * Return: 0 on success, positive error code on failure
+ */
+/* cppcheck-suppress [constParameterCallback, unmatchedSuppression] */
+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,
+ .addr4 = c->ip4.addr_seen,
+ };
+
+ (void)stage;
+
+ memcpy(addrs.mac, c->guest_mac, sizeof(addrs.mac));
+
+ if (write_all_buf(fd, &addrs, sizeof(addrs)))
+ return errno;
+
+ return 0;
+}
+
+/**
+ * seen_addrs_target_v1() - Receive and use guest observed addresses on target
+ * @c: Execution context
+ * @stage: Migration stage, unused
+ * @fd: File descriptor for state transfer
+ *
+ * Return: 0 on success, positive error code on failure
+ */
+static int seen_addrs_target_v1(struct ctx *c,
+ const struct migrate_stage *stage, int fd)
+{
+ struct migrate_seen_addrs_v1 addrs;
+
+ (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;
+ c->ip4.addr_seen = addrs.addr4;
+ memcpy(c->guest_mac, addrs.mac, sizeof(c->guest_mac));
+
+ return 0;
+}
+
+/* Stages for version 1 */
+static const struct migrate_stage stages_v1[] = {
+ {
+ .name = "observed addresses",
+ .source = seen_addrs_source_v1,
+ .target = seen_addrs_target_v1,
+ },
+ {
+ .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[] = {
+ { 1, stages_v1, },
+ { 0 },
+};
+
+/* Current encoding version */
+#define CURRENT_VERSION (&versions[0])
+
+/**
+ * migrate_source() - Migration as source, send state to hypervisor
+ * @c: Execution context
+ * @fd: File descriptor for state transfer
+ *
+ * Return: 0 on success, positive error code on failure
+ */
+static int migrate_source(struct ctx *c, int fd)
+{
+ const struct migrate_version *v = CURRENT_VERSION;
+ const struct migrate_header header = {
+ .magic = htonll_constant(MIGRATE_MAGIC),
+ .version = htonl(v->id),
+ .compat_version = htonl(v->id),
+ };
+ const struct migrate_stage *s;
+ int ret;
+
+ if (write_all_buf(fd, &header, sizeof(header))) {
+ ret = errno;
+ err("Can't send migration header: %s, abort", strerror_(ret));
+ return ret;
+ }
+
+ for (s = v->s; s->name; s++) {
+ if (!s->source)
+ continue;
+
+ debug("Source side migration stage: %s", s->name);
+
+ if ((ret = s->source(c, s, fd))) {
+ err("Source migration stage: %s: %s, abort", s->name,
+ strerror_(ret));
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * migrate_target_read_header() - Read header in target
+ * @fd: Descriptor for state transfer
+ *
+ * Return: version structure on success, NULL on failure with errno set
+ */
+static const struct migrate_version *migrate_target_read_header(int fd)
+{
+ const struct migrate_version *v;
+ struct migrate_header h;
+ uint32_t id, compat_id;
+
+ if (read_all_buf(fd, &h, sizeof(h)))
+ return NULL;
+
+ id = ntohl(h.version);
+ compat_id = ntohl(h.compat_version);
+
+ debug("Source magic: 0x%016" PRIx64 ", version: %u, compat: %u",
+ ntohll(h.magic), id, compat_id);
+
+ if (ntohll(h.magic) != MIGRATE_MAGIC || !id || !compat_id) {
+ err("Invalid incoming device state");
+ errno = EINVAL;
+ return NULL;
+ }
+
+ for (v = versions; v->id; v++)
+ if (v->id <= id && v->id >= compat_id)
+ return v;
+
+ errno = ENOTSUP;
+ err("Unsupported device state version: %u", id);
+ return NULL;
+}
+
+/**
+ * migrate_target() - Migration as target, receive state from hypervisor
+ * @c: Execution context
+ * @fd: File descriptor for state transfer
+ *
+ * Return: 0 on success, positive error code on failure
+ */
+static int migrate_target(struct ctx *c, int fd)
+{
+ const struct migrate_version *v;
+ const struct migrate_stage *s;
+ int ret;
+
+ if (!(v = migrate_target_read_header(fd)))
+ return errno;
+
+ for (s = v->s; s->name; s++) {
+ if (!s->target)
+ continue;
+
+ debug("Target side migration stage: %s", s->name);
+
+ if ((ret = s->target(c, s, fd))) {
+ err("Target migration stage: %s: %s, abort", s->name,
+ strerror_(ret));
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * migrate_init() - Set up things necessary for migration
+ * @c: Execution context
+ */
+void migrate_init(struct ctx *c)
+{
+ c->device_state_result = -1;
+}
+
+/**
+ * migrate_close() - Close migration channel and connection to passt-repair
+ * @c: Execution context
+ */
+void migrate_close(struct ctx *c)
+{
+ if (c->device_state_fd != -1) {
+ debug("Closing migration channel, fd: %d", c->device_state_fd);
+ close(c->device_state_fd);
+ c->device_state_fd = -1;
+ c->device_state_result = -1;
+ }
+
+ repair_close(c);
+}
+
+/**
+ * migrate_request() - Request a migration of device state
+ * @c: Execution context
+ * @fd: fd to transfer state
+ * @target: Are we the target of the migration?
+ */
+void migrate_request(struct ctx *c, int fd, bool target)
+{
+ debug("Migration requested, fd: %d (was %d)", fd, c->device_state_fd);
+
+ if (c->device_state_fd != -1)
+ migrate_close(c);
+
+ c->device_state_fd = fd;
+ c->migrate_target = target;
+}
+
+/**
+ * migrate_handler() - Send/receive passt internal state to/from hypervisor
+ * @c: Execution context
+ */
+void migrate_handler(struct ctx *c)
+{
+ int rc;
+
+ if (c->device_state_fd < 0)
+ return;
+
+ debug("Handling migration request from fd: %d, target: %d",
+ c->device_state_fd, c->migrate_target);
+
+ if (c->migrate_target)
+ rc = migrate_target(c, c->device_state_fd);
+ else
+ rc = migrate_source(c, c->device_state_fd);
+
+ migrate_close(c);
+
+ c->device_state_result = rc;
+}