All, Attached is a *very* rough first draft of the secure migration code I'm working on. This is in no way ready for merge. That being said, this demonstrates the basic idea that I'm pursuing, and I've actually been able to perform a KVM secure live migrate using this. Before I go and finish polishing it up, though, I wanted to make sure there wasn't anything fundamentally wrong with the approach. So, in that vein, comments are appreciated. Thanks, -- Chris Lalancette
diff --git a/docs/apibuild.py b/docs/apibuild.py index bb309f3..bf45f51 100755 --- a/docs/apibuild.py +++ b/docs/apibuild.py @@ -39,6 +39,7 @@ ignored_functions = { "virDomainMigratePrepare": "private function for migration", "virDomainMigratePrepare2": "private function for migration", "virDrvSupportsFeature": "private function for remote access", + "virConnectSecureMigrationData": "private function for secure migration", } def escape(raw): diff --git a/include/libvirt/libvirt.h b/include/libvirt/libvirt.h index 779ea72..05831d6 100644 --- a/include/libvirt/libvirt.h +++ b/include/libvirt/libvirt.h @@ -319,6 +319,7 @@ typedef virDomainInterfaceStatsStruct *virDomainInterfaceStatsPtr; /* Domain migration flags. */ typedef enum { VIR_MIGRATE_LIVE = 1, /* live migration */ + VIR_MIGRATE_SECURE = 2, /* secure migration */ } virDomainMigrateFlags; /* Domain migration. */ diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index a028b21..18995b5 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -319,6 +319,7 @@ typedef virDomainInterfaceStatsStruct *virDomainInterfaceStatsPtr; /* Domain migration flags. */ typedef enum { VIR_MIGRATE_LIVE = 1, /* live migration */ + VIR_MIGRATE_SECURE = 2, /* secure migration */ } virDomainMigrateFlags; /* Domain migration. */ diff --git a/qemud/remote.c b/qemud/remote.c index 8eaa7d6..01d740f 100644 --- a/qemud/remote.c +++ b/qemud/remote.c @@ -708,6 +708,40 @@ remoteDispatchNodeGetFreeMemory (struct qemud_server *server ATTRIBUTE_UNUSED, return 0; } +static int +remoteDispatchSecureMigrationData (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_error *rerr, + remote_secure_migration_data_args *args, + void *ret ATTRIBUTE_UNUSED) +{ + int r; + + if (args->cookie.cookie_len > REMOTE_CONNECT_SECURE_MIGRATION_COOKIE_MAX) { + remoteDispatchFormatError (rerr, "%s", + _("cookie_len > REMOTE_CONNECT_SECURE_MIGRATION_COOKIE_MAX")); + return -1; + } + + if (args->buffer.buffer_len > REMOTE_CONNECT_SECURE_MIGRATION_DATA_MAX) { + remoteDispatchFormatError (rerr, "%s", + _("buffer_len > REMOTE_CONNECT_SECURE_MIGRATION_DATA_MAX")); + return -1; + } + + r = virConnectSecureMigrationData(conn, args->cookie.cookie_val, + args->cookie.cookie_len, + args->buffer.buffer_val, + args->buffer.buffer_len); + + if (r < 0) { + remoteDispatchConnError(rerr, conn); + return -1; + } + + return 0; +} static int remoteDispatchDomainGetSchedulerType (struct qemud_server *server ATTRIBUTE_UNUSED, diff --git a/qemud/remote_dispatch_args.h b/qemud/remote_dispatch_args.h index 6573729..bb10e12 100644 --- a/qemud/remote_dispatch_args.h +++ b/qemud/remote_dispatch_args.h @@ -103,3 +103,4 @@ remote_node_device_re_attach_args val_remote_node_device_re_attach_args; remote_node_device_reset_args val_remote_node_device_reset_args; remote_domain_get_security_label_args val_remote_domain_get_security_label_args; + remote_secure_migration_data_args val_remote_secure_migration_data_args; diff --git a/qemud/remote_dispatch_prototypes.h b/qemud/remote_dispatch_prototypes.h index c44e5ce..efa28c8 100644 --- a/qemud/remote_dispatch_prototypes.h +++ b/qemud/remote_dispatch_prototypes.h @@ -674,6 +674,13 @@ static int remoteDispatchOpen( remote_error *err, remote_open_args *args, void *ret); +static int remoteDispatchSecureMigrationData( + struct qemud_server *server, + struct qemud_client *client, + virConnectPtr conn, + remote_error *err, + remote_secure_migration_data_args *args, + void *ret); static int remoteDispatchStoragePoolBuild( struct qemud_server *server, struct qemud_client *client, diff --git a/qemud/remote_dispatch_table.h b/qemud/remote_dispatch_table.h index 75f184b..f8a14fe 100644 --- a/qemud/remote_dispatch_table.h +++ b/qemud/remote_dispatch_table.h @@ -607,13 +607,18 @@ .args_filter = (xdrproc_t) xdr_remote_node_device_reset_args, .ret_filter = (xdrproc_t) xdr_void, }, -{ /* DomainGetSecurityLabel => 118 */ +{ /* DomainGetSecurityLabel => 121 */ .fn = (dispatch_fn) remoteDispatchDomainGetSecurityLabel, .args_filter = (xdrproc_t) xdr_remote_domain_get_security_label_args, .ret_filter = (xdrproc_t) xdr_remote_domain_get_security_label_ret, }, -{ /* NodeGetSecurityModel => 119 */ +{ /* NodeGetSecurityModel => 122 */ .fn = (dispatch_fn) remoteDispatchNodeGetSecurityModel, .args_filter = (xdrproc_t) xdr_void, .ret_filter = (xdrproc_t) xdr_remote_node_get_security_model_ret, }, +{ /* SecureMigrationData => 123 */ + .fn = (dispatch_fn) remoteDispatchSecureMigrationData, + .args_filter = (xdrproc_t) xdr_remote_secure_migration_data_args, + .ret_filter = (xdrproc_t) xdr_void, +}, diff --git a/qemud/remote_protocol.c b/qemud/remote_protocol.c index 7f7c628..c06f183 100644 --- a/qemud/remote_protocol.c +++ b/qemud/remote_protocol.c @@ -467,6 +467,19 @@ xdr_remote_domain_get_scheduler_type_ret (XDR *xdrs, remote_domain_get_scheduler } bool_t +xdr_remote_secure_migration_data_args (XDR *xdrs, remote_secure_migration_data_args *objp) +{ + char **objp_cpp0 = (char **) (void *) &objp->cookie.cookie_val; + char **objp_cpp1 = (char **) (void *) &objp->buffer.buffer_val; + + if (!xdr_bytes (xdrs, objp_cpp0, (u_int *) &objp->cookie.cookie_len, REMOTE_CONNECT_SECURE_MIGRATION_COOKIE_MAX)) + return FALSE; + if (!xdr_bytes (xdrs, objp_cpp1, (u_int *) &objp->buffer.buffer_len, REMOTE_CONNECT_SECURE_MIGRATION_DATA_MAX)) + return FALSE; + return TRUE; +} + +bool_t xdr_remote_domain_get_scheduler_parameters_args (XDR *xdrs, remote_domain_get_scheduler_parameters_args *objp) { diff --git a/qemud/remote_protocol.h b/qemud/remote_protocol.h index 75def5e..d97a18b 100644 --- a/qemud/remote_protocol.h +++ b/qemud/remote_protocol.h @@ -41,6 +41,8 @@ typedef remote_nonnull_string *remote_string; #define REMOTE_SECURITY_MODEL_MAX VIR_SECURITY_MODEL_BUFLEN #define REMOTE_SECURITY_LABEL_MAX VIR_SECURITY_LABEL_BUFLEN #define REMOTE_SECURITY_DOI_MAX VIR_SECURITY_DOI_BUFLEN +#define REMOTE_CONNECT_SECURE_MIGRATION_DATA_MAX 65536 +#define REMOTE_CONNECT_SECURE_MIGRATION_COOKIE_MAX 65536 typedef char remote_uuid[VIR_UUID_BUFLEN]; @@ -227,6 +229,18 @@ struct remote_domain_get_scheduler_type_ret { }; typedef struct remote_domain_get_scheduler_type_ret remote_domain_get_scheduler_type_ret; +struct remote_secure_migration_data_args { + struct { + u_int cookie_len; + char *cookie_val; + } cookie; + struct { + u_int buffer_len; + char *buffer_val; + } buffer; +}; +typedef struct remote_secure_migration_data_args remote_secure_migration_data_args; + struct remote_domain_get_scheduler_parameters_args { remote_nonnull_domain dom; int nparams; @@ -1397,6 +1411,7 @@ enum remote_procedure { REMOTE_PROC_NODE_DEVICE_RESET = 120, REMOTE_PROC_DOMAIN_GET_SECURITY_LABEL = 121, REMOTE_PROC_NODE_GET_SECURITY_MODEL = 122, + REMOTE_PROC_SECURE_MIGRATION_DATA = 123, }; typedef enum remote_procedure remote_procedure; @@ -1461,6 +1476,7 @@ extern bool_t xdr_remote_node_get_cells_free_memory_ret (XDR *, remote_node_get extern bool_t xdr_remote_node_get_free_memory_ret (XDR *, remote_node_get_free_memory_ret*); extern bool_t xdr_remote_domain_get_scheduler_type_args (XDR *, remote_domain_get_scheduler_type_args*); extern bool_t xdr_remote_domain_get_scheduler_type_ret (XDR *, remote_domain_get_scheduler_type_ret*); +extern bool_t xdr_remote_secure_migration_data_args (XDR *, remote_secure_migration_data_args*); extern bool_t xdr_remote_domain_get_scheduler_parameters_args (XDR *, remote_domain_get_scheduler_parameters_args*); extern bool_t xdr_remote_domain_get_scheduler_parameters_ret (XDR *, remote_domain_get_scheduler_parameters_ret*); extern bool_t xdr_remote_domain_set_scheduler_parameters_args (XDR *, remote_domain_set_scheduler_parameters_args*); @@ -1672,6 +1688,7 @@ extern bool_t xdr_remote_node_get_cells_free_memory_ret (); extern bool_t xdr_remote_node_get_free_memory_ret (); extern bool_t xdr_remote_domain_get_scheduler_type_args (); extern bool_t xdr_remote_domain_get_scheduler_type_ret (); +extern bool_t xdr_remote_secure_migration_data_args (); extern bool_t xdr_remote_domain_get_scheduler_parameters_args (); extern bool_t xdr_remote_domain_get_scheduler_parameters_ret (); extern bool_t xdr_remote_domain_set_scheduler_parameters_args (); diff --git a/qemud/remote_protocol.x b/qemud/remote_protocol.x index 2d8e6a2..8cf6b50 100644 --- a/qemud/remote_protocol.x +++ b/qemud/remote_protocol.x @@ -130,6 +130,9 @@ const REMOTE_SECURITY_LABEL_MAX = VIR_SECURITY_LABEL_BUFLEN; */ const REMOTE_SECURITY_DOI_MAX = VIR_SECURITY_DOI_BUFLEN; +const REMOTE_CONNECT_SECURE_MIGRATION_DATA_MAX = 65536; +const REMOTE_CONNECT_SECURE_MIGRATION_COOKIE_MAX = 65536; + /* UUID. VIR_UUID_BUFLEN definition comes from libvirt.h */ typedef opaque remote_uuid[VIR_UUID_BUFLEN]; @@ -323,6 +326,11 @@ struct remote_domain_get_scheduler_type_ret { int nparams; }; +struct remote_secure_migration_data_args { + opaque cookie<REMOTE_CONNECT_SECURE_MIGRATION_COOKIE_MAX>; + opaque buffer<REMOTE_CONNECT_SECURE_MIGRATION_DATA_MAX>; +}; + struct remote_domain_get_scheduler_parameters_args { remote_nonnull_domain dom; int nparams; @@ -1270,7 +1278,8 @@ enum remote_procedure { REMOTE_PROC_NODE_DEVICE_RESET = 120, REMOTE_PROC_DOMAIN_GET_SECURITY_LABEL = 121, - REMOTE_PROC_NODE_GET_SECURITY_MODEL = 122 + REMOTE_PROC_NODE_GET_SECURITY_MODEL = 122, + REMOTE_PROC_SECURE_MIGRATION_DATA = 123 }; /* Custom RPC structure. */ diff --git a/src/driver.h b/src/driver.h index 62d6fbc..be04d1d 100644 --- a/src/driver.h +++ b/src/driver.h @@ -330,6 +330,15 @@ typedef int (*virDrvNodeDeviceReset) (virNodeDevicePtr dev); + +typedef int + (*virDrvSecureMigrationData) + (virConnectPtr domain, + char *cookie, + int cookielen, + char *data, + int datalen); + /** * _virDriver: * @@ -409,6 +418,7 @@ struct _virDriver { virDrvNodeDeviceDettach nodeDeviceDettach; virDrvNodeDeviceReAttach nodeDeviceReAttach; virDrvNodeDeviceReset nodeDeviceReset; + virDrvSecureMigrationData secureMigrationData; }; typedef int diff --git a/src/libvirt.c b/src/libvirt.c index bf3453a..2aae0e5 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -2649,6 +2649,7 @@ error: * * Flags may be one of more of the following: * VIR_MIGRATE_LIVE Attempt a live migration. + * VIR_MIGRATE_SECURE Attempt to do a secure migration * * If a hypervisor supports renaming domains during migration, * then you may set the dname parameter to the new name (otherwise @@ -2722,6 +2723,13 @@ virDomainMigrate (virDomainPtr domain, goto error; } + if ((flags & VIR_MIGRATE_SECURE) && uri == NULL) { + /* if you are doing a secure migration, you *must* also pass a uri */ + virLibConnError(conn, VIR_ERR_INVALID_ARG, + _("requested SECURE migration, but no URI passed")); + goto error; + } + /* Check that migration is supported by both drivers. */ if (VIR_DRV_SUPPORTS_FEATURE (conn->driver, conn, VIR_DRV_FEATURE_MIGRATION_V1) && @@ -2839,13 +2847,13 @@ error: */ int virDomainMigratePrepare (virConnectPtr dconn, - char **cookie, - int *cookielen, - const char *uri_in, - char **uri_out, - unsigned long flags, - const char *dname, - unsigned long bandwidth) + char **cookie, + int *cookielen, + const char *uri_in, + char **uri_out, + unsigned long flags, + const char *dname, + unsigned long bandwidth) { VIR_DEBUG("dconn=%p, cookie=%p, cookielen=%p, uri_in=%s, uri_out=%p, " "flags=%lu, dname=%s, bandwidth=%lu", dconn, cookie, cookielen, @@ -2883,6 +2891,39 @@ error: /* * Not for public use. This function is part of the internal + * implementation of secure migration in the remote case. + */ +int virConnectSecureMigrationData(virConnectPtr conn, + char *cookie, + int cookielen, + char *data, + int datalen) +{ + virResetLastError(); + + if (conn->flags & VIR_CONNECT_RO) { + virLibConnError(conn, VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + if (conn->driver->secureMigrationData) { + int ret; + ret = conn->driver->secureMigrationData (conn, cookie, cookielen, data, datalen); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + /* Copy to connection error object for back compatability */ + virSetConnError(conn); + return -1; +} + +/* + * Not for public use. This function is part of the internal * implementation of migration in the remote case. */ int diff --git a/src/libvirt_internal.h b/src/libvirt_internal.h index 44145d4..6677c00 100644 --- a/src/libvirt_internal.h +++ b/src/libvirt_internal.h @@ -72,5 +72,10 @@ virDomainPtr virDomainMigrateFinish2 (virConnectPtr dconn, unsigned long flags, int retcode); +int virConnectSecureMigrationData(virConnectPtr conn, + char *cookie, + int cookielen, + char *data, + int datalen); #endif diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index f0d8afa..8f8e945 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -179,6 +179,7 @@ virDomainMigratePerform; virDomainMigrateFinish; virDomainMigratePrepare2; virDomainMigrateFinish2; +virConnectSecureMigrationData; virRegisterDriver; virRegisterNetworkDriver; virRegisterStateDriver; diff --git a/src/qemu_driver.c b/src/qemu_driver.c index 9a0d526..441e540 100644 --- a/src/qemu_driver.c +++ b/src/qemu_driver.c @@ -4436,6 +4436,12 @@ static void qemuDomainEventQueue(struct qemud_driver *driver, } /* Migration support. */ +struct secure_mig { + int socket; + int port; + char *cookie; + int cookielen; +}; /* Prepare is the first step, and it runs on the destination host. * @@ -4443,11 +4449,11 @@ static void qemuDomainEventQueue(struct qemud_driver *driver, */ static int qemudDomainMigratePrepare2 (virConnectPtr dconn, - char **cookie ATTRIBUTE_UNUSED, - int *cookielen ATTRIBUTE_UNUSED, + char **cookie, + int *cookielen, const char *uri_in, char **uri_out, - unsigned long flags ATTRIBUTE_UNUSED, + unsigned long flags, const char *dname, unsigned long resource ATTRIBUTE_UNUSED, const char *dom_xml) @@ -4461,7 +4467,9 @@ qemudDomainMigratePrepare2 (virConnectPtr dconn, char migrateFrom [64]; const char *p; virDomainEventPtr event = NULL; - int ret = -1;; + int ret = -1; + struct secure_mig *secureMigData; + struct sockaddr_in a; *uri_out = NULL; @@ -4503,20 +4511,27 @@ qemudDomainMigratePrepare2 (virConnectPtr dconn, * URI when passing it to the qemu monitor, so bad * characters in hostname part don't matter. */ - if (!STREQLEN (uri_in, "tcp:", 6)) { - qemudReportError (dconn, NULL, NULL, VIR_ERR_INVALID_ARG, - "%s", _("only tcp URIs are supported for KVM migrations")); - goto cleanup; - } + if (!(flags & VIR_MIGRATE_SECURE)) { + if (!STREQLEN (uri_in, "tcp:", 6)) { + qemudReportError (dconn, NULL, NULL, VIR_ERR_INVALID_ARG, + "%s", _("only tcp URIs are supported for Qemu migrations")); + goto cleanup; + } - /* Get the port number. */ - p = strrchr (uri_in, ':'); - p++; /* definitely has a ':' in it, see above */ - this_port = virParseNumber (&p); - if (this_port == -1 || p-uri_in != strlen (uri_in)) { - qemudReportError (dconn, NULL, NULL, VIR_ERR_INVALID_ARG, - "%s", _("URI did not have ':port' at the end")); - goto cleanup; + /* Get the port number. */ + p = strrchr (uri_in, ':'); + p++; /* definitely has a ':' in it, see above */ + this_port = virParseNumber (&p); + if (this_port == -1 || p-uri_in != strlen (uri_in)) { + qemudReportError (dconn, NULL, NULL, VIR_ERR_INVALID_ARG, + "%s", _("URI did not have ':port' at the end")); + goto cleanup; + } + } + else { + /* Secure migration requested; find a free port */ + this_port = QEMUD_MIGRATION_FIRST_PORT + port++; + if (port == QEMUD_MIGRATION_NUM_PORTS) port = 0; } } @@ -4571,7 +4586,11 @@ qemudDomainMigratePrepare2 (virConnectPtr dconn, /* Start the QEMU daemon, with the same command-line arguments plus * -incoming tcp:0.0.0.0:port */ - snprintf (migrateFrom, sizeof (migrateFrom), "tcp:0.0.0.0:%d", this_port); + if (!(flags & VIR_MIGRATE_SECURE)) + snprintf (migrateFrom, sizeof (migrateFrom), "tcp:0.0.0.0:%d", this_port); + else + snprintf (migrateFrom, sizeof (migrateFrom), "tcp:127.0.0.1:%d", this_port); + if (qemudStartVMDaemon (dconn, driver, vm, migrateFrom, -1) < 0) { qemudReportError (dconn, NULL, NULL, VIR_ERR_OPERATION_FAILED, "%s", _("failed to start listening VM")); @@ -4582,6 +4601,39 @@ qemudDomainMigratePrepare2 (virConnectPtr dconn, goto cleanup; } + if (flags & VIR_MIGRATE_SECURE) { + if (VIR_ALLOC(secureMigData) < 0) { + virReportOOMError (dconn); + goto cleanup; + } + + secureMigData->socket = socket(AF_INET, SOCK_STREAM, 0); + if (secureMigData->socket < 0) { + virReportSystemError(dconn, errno, + "%s", _("cannot open socket")); + goto cleanup; + } + + memset(&a, 0, sizeof(a)); + a.sin_port = htons(this_port); + a.sin_family = AF_INET; + a.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + if ((connect(secureMigData->socket, (struct sockaddr *)&a, sizeof(a))) < 0) { + virReportSystemError(dconn, errno, + "%s", _("cannot connect to qemu")); + close(secureMigData->socket); + goto cleanup; + } + + secureMigData->port = this_port; + /* FIXME: generate a real cookie here */ + secureMigData->cookie = strdup("hello"); + secureMigData->cookielen = 6; + *cookie = (char *)secureMigData; + *cookielen = sizeof(*secureMigData); + } + event = virDomainEventNewFromObj(vm, VIR_DOMAIN_EVENT_STARTED, VIR_DOMAIN_EVENT_STARTED_MIGRATED); @@ -4591,6 +4643,11 @@ cleanup: virDomainDefFree(def); if (ret != 0) { VIR_FREE(*uri_out); + + /* there might have been an error after we started the incoming qemu + * process, so be sure to kill it before we leave + */ + qemudShutdownVMDaemon(NULL, driver, vm); } if (vm) virDomainObjUnlock(vm); @@ -4600,11 +4657,166 @@ cleanup: return ret; } +static int qemu_listen(int port) +{ + int qemu_sock, optval; + struct sockaddr_in sa_qemu; + + /* NOTE: on error, we just cleanup after ourselves and return -1; the + * higher layer will report the error for us + */ + qemu_sock = socket(AF_INET, SOCK_STREAM, 0); + if (qemu_sock < 0) { + return -1; + } + + optval = 1; + if (setsockopt(qemu_sock, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, + sizeof(optval)) < 0) + goto close_qemu_sock; + + memset(&sa_qemu, 0, sizeof(sa_qemu)); + sa_qemu.sin_port = htons(port); + sa_qemu.sin_family = AF_INET; + sa_qemu.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + if (bind(qemu_sock, (struct sockaddr *)&sa_qemu, sizeof(sa_qemu)) < 0) + goto close_qemu_sock; + if (listen(qemu_sock, 1) < 0) + goto close_qemu_sock; + + return qemu_sock; + +close_qemu_sock: + close(qemu_sock); + + return -1; +} + +int virConnectSecureMigrationData(virConnectPtr conn, + char *cookie, + int cookielen, + char *data, + int datalen); + +static int doSecureMigrate(virDomainPtr dom, + virDomainObjPtr vm, + const char *cookie, + int cookielen, + const char *uri) +{ +/* FIXME: we know 65536 is enough because that is the limit from RPC. Is + * there a nicer way to reflect that here? + */ +#define MAX_BUFFER 65536 + /* FIXME: make port dynamic */ + int port = 1234; + int client_sock, qemu_sock; + struct sockaddr_in sa_client; + socklen_t addrlen; + virConnectPtr dconn; + char cmd[HOST_NAME_MAX+50]; + char *info = NULL; + int retval = -1; + ssize_t bytes; + char buffer[MAX_BUFFER]; + int ret; + char *safe_uri; + + qemu_sock = qemu_listen(port); + if (qemu_sock < 0) { + qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, + _("Could not open secure listening socket: %s"), + strerror(errno)); + return -1; + } + + /* Do the migrate command, but let it return immediately. Then we + * will accept data below + */ + snprintf (cmd, sizeof cmd, "migrate -d tcp:127.0.0.1:%d", port); + + if (qemudMonitorCommand(vm, cmd, &info) < 0) { + qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, + "%s", _("migrate operation failed")); + goto close_qemu_sock; + } + + /* Now check for "fail" in the output string */ + if (strstr(info, "fail") != NULL) { + qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, + _("secure migrate failed: %s"), info); + goto close_qemu_sock; + } + + addrlen = sizeof(sa_client); + while ((client_sock = accept(qemu_sock, (struct sockaddr *)&sa_client, &addrlen)) < 0) { + if (errno == EAGAIN || errno == EINTR) + continue; + qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, + _("Failed accepting from the qemu socket: %s"), + strerror(errno)); + goto qemu_cancel_migration; + } + + safe_uri = qemudEscapeMonitorArg (uri); + if (!safe_uri) { + virReportOOMError (dom->conn); + goto close_client_sock; + } + + dconn = virConnectOpen(uri); + VIR_FREE (safe_uri); + if (dconn == NULL) { + qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, + _("Failed to connect to remote libvirtd")); + goto close_client_sock; + } + + for (;;) { + bytes = saferead(client_sock, buffer, MAX_BUFFER); + if (bytes < 0) { + qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, + _("Failed to read from qemu: %s"), + strerror(errno)); + goto close_dconn; + } + else if (bytes == 0) + /* EOF; get out of here */ + break; + + ret = virConnectSecureMigrationData(dom->conn, cookie, cookielen, + buffer, bytes); + if (ret < 0) { + qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, + _("Failed to write migration data to remote libvirtd")); + goto close_dconn; + } + } + + retval = 0; + +close_dconn: + virConnectClose(dconn); + +close_client_sock: + close(client_sock); + +qemu_cancel_migration: + if (retval != 0) + qemudMonitorCommand(vm, "migrate_cancel", &info); + VIR_FREE(info); + +close_qemu_sock: + close(qemu_sock); + + return retval; +} + /* Perform is the second step, and it runs on the source host. */ static int qemudDomainMigratePerform (virDomainPtr dom, - const char *cookie ATTRIBUTE_UNUSED, - int cookielen ATTRIBUTE_UNUSED, + const char *cookie, + int cookielen, const char *uri, unsigned long flags ATTRIBUTE_UNUSED, const char *dname ATTRIBUTE_UNUSED, @@ -4661,28 +4873,36 @@ qemudDomainMigratePerform (virDomainPtr dom, VIR_FREE (info); } - /* Issue the migrate command. */ - safe_uri = qemudEscapeMonitorArg (uri); - if (!safe_uri) { - virReportOOMError (dom->conn); - goto cleanup; - } - snprintf (cmd, sizeof cmd, "migrate \"%s\"", safe_uri); - VIR_FREE (safe_uri); + if (!(flags & VIR_MIGRATE_SECURE)) { + /* Issue the migrate command. */ + safe_uri = qemudEscapeMonitorArg (uri); + if (!safe_uri) { + virReportOOMError (dom->conn); + goto cleanup; + } + snprintf (cmd, sizeof cmd, "migrate \"%s\"", safe_uri); + VIR_FREE (safe_uri); - if (qemudMonitorCommand (vm, cmd, &info) < 0) { - qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, - "%s", _("migrate operation failed")); - goto cleanup; - } + if (qemudMonitorCommand (vm, cmd, &info) < 0) { + qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, + "%s", _("migrate operation failed")); + goto cleanup; + } - DEBUG ("migrate reply: %s", info); + DEBUG ("migrate reply: %s", info); - /* Now check for "fail" in the output string */ - if (strstr(info, "fail") != NULL) { - qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, - _("migrate failed: %s"), info); - goto cleanup; + /* Now check for "fail" in the output string */ + if (strstr(info, "fail") != NULL) { + qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, + _("migrate failed: %s"), info); + goto cleanup; + } + } + else { + if (doSecureMigrate(dom, vm, cookie, cookielen, uri) < 0) { + /* doSecureMigrate already set the error, so just get out */ + goto cleanup; + } } /* Clean up the source domain. */ @@ -4901,6 +5121,34 @@ out: return ret; } +static int +qemudSecureMigrationData (virConnectPtr conn, char *cookie, int cookielen, + char *data, int datalen) +{ + struct secure_mig *migdata; + + /* FIXME: do we need to lock the qemu driver here? */ + + if (cookielen != sizeof(struct secure_mig)) { + qemudReportError (conn, NULL, NULL, VIR_ERR_INVALID_ARG, + _("Bad size for secure migration cookie")); + return -1; + } + + migdata = (struct secure_mig *)cookie; + + /* FIXME: check that the cookie data is what we expect */ + + if (safewrite(migdata->socket, data, datalen) != datalen) { + qemudReportError (conn, NULL, NULL, VIR_ERR_OPERATION_FAILED, + _("Could not write data to migration socket: %s"), + strerror(errno)); + return -1; + } + + return 0; +} + static virDriver qemuDriver = { .no = VIR_DRV_QEMU, .name = "QEMU", @@ -4964,6 +5212,7 @@ static virDriver qemuDriver = { .nodeDeviceDettach = qemudNodeDeviceDettach, .nodeDeviceReAttach = qemudNodeDeviceReAttach, .nodeDeviceReset = qemudNodeDeviceReset, + .secureMigrationData = qemudSecureMigrationData, }; diff --git a/src/remote_internal.c b/src/remote_internal.c index 4c43f53..acb7ae6 100644 --- a/src/remote_internal.c +++ b/src/remote_internal.c @@ -948,7 +948,7 @@ remoteOpen (virConnectPtr conn, struct private_data *priv; int ret, rflags = 0; - if (inside_daemon) + if (inside_daemon && (!conn->uri || (conn->uri && !conn->uri->server))) return VIR_DRV_OPEN_DECLINED; if (!(priv = remoteAllocPrivateData(conn))) @@ -2508,6 +2508,36 @@ done: return ddom; } +static int remoteSecureMigrationData(virConnectPtr conn, + char *cookie, + int cookielen, + char *data, + int datalen) +{ + int rv = -1; + remote_secure_migration_data_args args; + struct private_data *priv = conn->privateData; + + remoteDriverLock(priv); + + args.cookie.cookie_len = cookielen; + args.cookie.cookie_val = cookie; + args.buffer.buffer_len = datalen; + args.buffer.buffer_val = data; + + if (call (conn, priv, 0, REMOTE_PROC_SECURE_MIGRATION_DATA, + (xdrproc_t) xdr_remote_secure_migration_data_args, (char *) &args, + (xdrproc_t) xdr_void, (char *) NULL) == -1) + goto done; + + rv = 0; + +done: + remoteDriverUnlock(priv); + + return rv; +} + static int remoteDomainMigratePrepare2 (virConnectPtr dconn, char **cookie, int *cookielen, @@ -6892,6 +6922,7 @@ static virDriver driver = { .nodeDeviceDettach = remoteNodeDeviceDettach, .nodeDeviceReAttach = remoteNodeDeviceReAttach, .nodeDeviceReset = remoteNodeDeviceReset, + .secureMigrationData = remoteSecureMigrationData, }; static virNetworkDriver network_driver = { diff --git a/src/virsh.c b/src/virsh.c index 9a7b0ed..eee98aa 100644 --- a/src/virsh.c +++ b/src/virsh.c @@ -2277,6 +2277,7 @@ static const vshCmdInfo info_migrate[] = { static const vshCmdOptDef opts_migrate[] = { {"live", VSH_OT_BOOL, 0, gettext_noop("live migration")}, + {"secure", VSH_OT_BOOL, 0, gettext_noop("secure migration")}, {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("domain name, id or uuid")}, {"desturi", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("connection URI of the destination host")}, {"migrateuri", VSH_OT_DATA, 0, gettext_noop("migration URI, usually can be omitted")}, @@ -2314,6 +2315,9 @@ cmdMigrate (vshControl *ctl, const vshCmd *cmd) if (vshCommandOptBool (cmd, "live")) flags |= VIR_MIGRATE_LIVE; + if (vshCommandOptBool (cmd, "secure")) + flags |= VIR_MIGRATE_SECURE; + /* Temporarily connect to the destination host. */ dconn = virConnectOpen (desturi); if (!dconn) goto done;
-- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list