1
0
mirror of https://passt.top/passt synced 2025-02-22 19:02:20 +00:00
passt/migrate.c

301 lines
6.7 KiB
C
Raw Permalink Normal View History

// 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;
}