[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[libvirt] [PATCH]: Secure migration support for KVM



All,
     Attached is the secure migration patch for libvirt.  What this patch
implements is a new remote RPC call for secure migration.  On the source of the
migration, we do a migration from the qemu process to the libvirtd on localhost.
 As each read() in libvirtd completes, it issues an RPC message to the remote
libvirtd, using the standard libvirt RPC mechanisms.  On the destination, we do
essentially the mirror; the libvirtd accepts the data from RPC, and then writes
it to a qemu container process listening on localhost.

In order to actually use this, the command-line is pretty complex.  If you want
to use standard live migration, the command-line looks something like:

# virsh -c qemu+tls://source.example.org/system migrate --live guest
qemu+tls://dest.example.org/system

This says to a live migration of "guest" from "source.example.org" to
"dest.example.org", connecting to each of the remote libvirtd via TLS.  Note
that in this model, the virsh process connects to the remote libvirtd's via the
-c argument (source) and the destination argument (dest).

To do secure live migration, this becomes:

# virsh -c qemu+tls://source.example.org/system migrate --live --secure guest
qemu+tls://dest.example.org/system qemu+tls://dest.example.org/system

This says to do a secure live migration of "guest" from "source.example.org" to
"dest.example.org".  The virsh process figures out how to connect to the remote
libvirtd's via the -c argument (source) and the destination argument (dest).
The second "qemu+tls://dest.example.org/system" is the secure channel from the
source libvirtd to the destination libvirtd.

Signed-off-by: Chris Lalancette <clalance redhat com>

 docs/apibuild.py                   |    1
 include/libvirt/libvirt.h          |    1
 include/libvirt/libvirt.h.in       |    1
 qemud/remote.c                     |   34 +++
 qemud/remote_dispatch_args.h       |    1
 qemud/remote_dispatch_prototypes.h |    7
 qemud/remote_dispatch_table.h      |    5
 qemud/remote_protocol.c            |   13 +
 qemud/remote_protocol.h            |   17 +
 qemud/remote_protocol.x            |   12 +
 src/driver.h                       |   10 +
 src/libvirt.c                      |   55 ++++-
 src/libvirt_internal.h             |    5
 src/libvirt_private.syms           |    1
 src/lxc_driver.c                   |    1
 src/openvz_driver.c                |    1
 src/qemu_driver.c                  |  367 ++++++++++++++++++++++++++++++++-----
 src/remote_internal.c              |   33 +++
 src/test.c                         |    1
 src/uml_driver.c                   |    1
 src/virsh.c                        |    4
 src/xen_unified.c                  |    1
 22 files changed, 518 insertions(+), 54 deletions(-)
diff --git a/docs/apibuild.py b/docs/apibuild.py
index 6fec049..b2c918e 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",
   "DllMain": "specific function fo Win32",
 }
 
diff --git a/include/libvirt/libvirt.h b/include/libvirt/libvirt.h
index 30f559d..b9362d5 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 2f7076f..94e3995 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 8d24a3a..5228550 100644
--- a/qemud/remote.c
+++ b/qemud/remote.c
@@ -710,6 +710,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 58eccf5..8ba3a3a 100644
--- a/qemud/remote_dispatch_args.h
+++ b/qemud/remote_dispatch_args.h
@@ -105,3 +105,4 @@
     remote_domain_get_security_label_args val_remote_domain_get_security_label_args;
     remote_node_device_create_xml_args val_remote_node_device_create_xml_args;
     remote_node_device_destroy_args val_remote_node_device_destroy_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 96dcb2a..f54ec21 100644
--- a/qemud/remote_dispatch_prototypes.h
+++ b/qemud/remote_dispatch_prototypes.h
@@ -688,6 +688,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 ac7f9b9..cd62290 100644
--- a/qemud/remote_dispatch_table.h
+++ b/qemud/remote_dispatch_table.h
@@ -627,3 +627,8 @@
     .args_filter = (xdrproc_t) xdr_remote_node_device_destroy_args,
     .ret_filter = (xdrproc_t) xdr_void,
 },
+{   /* SecureMigrationData => 125 */
+    .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 af3c792..87d9dcf 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 9d67e58..e5e3d8b 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;
@@ -1415,6 +1429,7 @@ enum remote_procedure {
         REMOTE_PROC_NODE_GET_SECURITY_MODEL = 122,
         REMOTE_PROC_NODE_DEVICE_CREATE_XML = 123,
         REMOTE_PROC_NODE_DEVICE_DESTROY = 124,
+        REMOTE_PROC_SECURE_MIGRATION_DATA = 125,
 };
 typedef enum remote_procedure remote_procedure;
 
@@ -1479,6 +1494,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*);
@@ -1693,6 +1709,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 2c79949..6ba03da 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;
@@ -1286,7 +1294,9 @@ enum remote_procedure {
     REMOTE_PROC_NODE_GET_SECURITY_MODEL = 122,
 
     REMOTE_PROC_NODE_DEVICE_CREATE_XML = 123,
-    REMOTE_PROC_NODE_DEVICE_DESTROY = 124
+    REMOTE_PROC_NODE_DEVICE_DESTROY = 124,
+
+    REMOTE_PROC_SECURE_MIGRATION_DATA = 125
 };
 
 /* Custom RPC structure. */
diff --git a/src/driver.h b/src/driver.h
index c357b76..7a90201 100644
--- a/src/driver.h
+++ b/src/driver.h
@@ -331,6 +331,15 @@ typedef int
     (*virDrvNodeDeviceReset)
                     (virNodeDevicePtr dev);
 
+
+typedef int
+    (*virDrvSecureMigrationData)
+                    (virConnectPtr domain,
+                     const 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 ded18a7..79b4fc7 100644
--- a/src/libvirt.c
+++ b/src/libvirt.c
@@ -2661,6 +2661,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
@@ -2734,6 +2735,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) &&
@@ -2851,13 +2859,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,
@@ -2895,6 +2903,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,
+                                  const 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..61ef508 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,
+                                  const char *cookie,
+                                  int cookielen,
+                                  char *data,
+                                  int datalen);
 
 #endif
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index ce63869..6c01566 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -182,6 +182,7 @@ virDomainMigratePerform;
 virDomainMigrateFinish;
 virDomainMigratePrepare2;
 virDomainMigrateFinish2;
+virConnectSecureMigrationData;
 virRegisterDriver;
 virRegisterNetworkDriver;
 virRegisterStateDriver;
diff --git a/src/lxc_driver.c b/src/lxc_driver.c
index 5392af1..5aa9cea 100644
--- a/src/lxc_driver.c
+++ b/src/lxc_driver.c
@@ -1485,6 +1485,7 @@ static virDriver lxcDriver = {
     NULL, /* nodeDeviceDettach */
     NULL, /* nodeDeviceReAttach */
     NULL, /* nodeDeviceReset */
+    NULL, /* secureMigrationData */
 };
 
 static virStateDriver lxcStateDriver = {
diff --git a/src/openvz_driver.c b/src/openvz_driver.c
index 755e449..db46143 100644
--- a/src/openvz_driver.c
+++ b/src/openvz_driver.c
@@ -1372,6 +1372,7 @@ static virDriver openvzDriver = {
     NULL, /* nodeDeviceDettach */
     NULL, /* nodeDeviceReAttach */
     NULL, /* nodeDeviceReset */
+    NULL, /* secureMigrationData */
 };
 
 int openvzRegister(void) {
diff --git a/src/qemu_driver.c b/src/qemu_driver.c
index bd60b29..f608e36 100644
--- a/src/qemu_driver.c
+++ b/src/qemu_driver.c
@@ -4723,6 +4723,9 @@ static void qemuDomainEventQueue(struct qemud_driver *driver,
 }
 
 /* Migration support. */
+struct secure_mig {
+    int socket;
+};
 
 /* Prepare is the first step, and it runs on the destination host.
  *
@@ -4730,11 +4733,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)
@@ -4748,7 +4751,9 @@ qemudDomainMigratePrepare2 (virConnectPtr dconn,
     char migrateFrom [64];
     const char *p;
     virDomainEventPtr event = NULL;
-    int ret = -1;;
+    int ret = -1;
+    struct secure_mig *secureMigData = NULL;
+    struct sockaddr_in a;
 
     *uri_out = NULL;
 
@@ -4768,6 +4773,8 @@ qemudDomainMigratePrepare2 (virConnectPtr dconn,
      * If the URI passed in is not NULL then we try to parse out the
      * port number and use that (note that the hostname is assumed
      * to be a correct hostname which refers to the target machine).
+     *
+     * Note that for secure migration, uri_in is guaranteed to be non-NULL
      */
     if (uri_in == NULL) {
         this_port = QEMUD_MIGRATION_FIRST_PORT + port++;
@@ -4786,24 +4793,37 @@ qemudDomainMigratePrepare2 (virConnectPtr dconn,
             goto cleanup;
         }
     } else {
-        /* Check the URI starts with "tcp:".  We will escape the
-         * URI when passing it to the qemu monitor, so bad
-         * characters in hostname part don't matter.
-         */
-        if (!STRPREFIX (uri_in, "tcp:")) {
-            qemudReportError (dconn, NULL, NULL, VIR_ERR_INVALID_ARG,
-                  "%s", _("only tcp URIs are supported for KVM migrations"));
-            goto cleanup;
+        if (!(flags & VIR_MIGRATE_SECURE)) {
+            /* Check the URI starts with "tcp:".  We will escape the
+             * URI when passing it to the qemu monitor, so bad
+             * characters in hostname part don't matter.
+             */
+            if (!STRPREFIX (uri_in, "tcp:")) {
+                qemudReportError (dconn, NULL, NULL, VIR_ERR_INVALID_ARG,
+                                  "%s", _("only tcp URIs are supported for KVM 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;
+            }
         }
+        else {
+            /* Secure migration requested; find a free port */
+            this_port = QEMUD_MIGRATION_FIRST_PORT + port++;
+            if (port == QEMUD_MIGRATION_NUM_PORTS) port = 0;
 
-        /* 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;
+            /* for secure migration, uri_out is more or less meaningless (since
+             * we are migrating to localhost anyway).  Dup uri_in to maintain
+             * our contract
+             */
+            *uri_out = strdup(uri_in);
         }
     }
 
@@ -4858,8 +4878,14 @@ 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"));
         if (!vm->persistent) {
             virDomainRemoveInactive(&driver->domains, vm);
             vm = NULL;
@@ -4867,6 +4893,38 @@ qemudDomainMigratePrepare2 (virConnectPtr dconn,
         goto cleanup;
     }
 
+    if (flags & VIR_MIGRATE_SECURE) {
+        /* this memory (and the socket) will be cleaned up during the
+         * finish step
+         */
+        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;
+        }
+
+        *cookie = (char *)secureMigData;
+        *cookielen = sizeof(*secureMigData);
+    }
+
     event = virDomainEventNewFromObj(vm,
                                      VIR_DOMAIN_EVENT_STARTED,
                                      VIR_DOMAIN_EVENT_STARTED_MIGRATED);
@@ -4876,6 +4934,13 @@ cleanup:
     virDomainDefFree(def);
     if (ret != 0) {
         VIR_FREE(*uri_out);
+        VIR_FREE(secureMigData);
+
+        /* there might have been an error after we started the incoming qemu
+         * process, so be sure to kill it before we leave
+         */
+        if (vm)
+            qemudShutdownVMDaemon(NULL, driver, vm);
     }
     if (vm)
         virDomainObjUnlock(vm);
@@ -4885,11 +4950,174 @@ 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,
+                                  const 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
+    int port;
+    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;
+    char ebuf[1024];
+
+    /* cycle through the localhost ports 9000 to 10000, looking for a socket
+     * we can listen on.  Once we've found one, break out of here
+     */
+    qemu_sock = -1;
+    for (port = 9000; port < 10000; port++) {
+        qemu_sock = qemu_listen(port);
+        if (qemu_sock >= 0)
+            break;
+    }
+
+    if (qemu_sock < 0) {
+        qemudReportError (dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
+                          _("Could not open secure listening socket: %s"),
+                          virStrerror(errno, ebuf, sizeof ebuf));
+        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;
+    }
+
+    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"),
+                          virStrerror(errno, ebuf, sizeof ebuf));
+        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"),
+                              virStrerror(errno, ebuf, sizeof ebuf));
+            goto close_dconn;
+        }
+        else if (bytes == 0)
+            /* EOF; get out of here */
+            break;
+
+        ret = virConnectSecureMigrationData(dconn, 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,
@@ -4948,28 +5176,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 ("%s: migrate reply: %s", vm->def->name, info);
+        DEBUG ("%s: migrate reply: %s", vm->def->name, 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. */
@@ -5023,10 +5259,10 @@ cleanup:
 static virDomainPtr
 qemudDomainMigrateFinish2 (virConnectPtr dconn,
                            const char *dname,
-                           const char *cookie ATTRIBUTE_UNUSED,
-                           int cookielen ATTRIBUTE_UNUSED,
+                           const char *cookie,
+                           int cookielen,
                            const char *uri ATTRIBUTE_UNUSED,
-                           unsigned long flags ATTRIBUTE_UNUSED,
+                           unsigned long flags,
                            int retcode)
 {
     struct qemud_driver *driver = dconn->privateData;
@@ -5034,6 +5270,7 @@ qemudDomainMigrateFinish2 (virConnectPtr dconn,
     virDomainPtr dom = NULL;
     virDomainEventPtr event = NULL;
     char *info = NULL;
+    struct secure_mig *secureMigData;
 
     qemuDriverLock(driver);
     vm = virDomainFindByName(&driver->domains, dname);
@@ -5043,6 +5280,20 @@ qemudDomainMigrateFinish2 (virConnectPtr dconn,
         goto cleanup;
     }
 
+    if (flags & VIR_MIGRATE_SECURE) {
+        /* in this case, we need to close out the cookie resources */
+        if (cookie == NULL || cookielen != sizeof(struct secure_mig)) {
+            qemudReportError (dconn, NULL, NULL, VIR_ERR_INVALID_ARG,
+                              _("Bad size for secure migration cookie"));
+            goto cleanup;
+        }
+
+        secureMigData = (struct secure_mig *)cookie;
+
+        close(secureMigData->socket);
+        VIR_FREE(secureMigData);
+    }
+
     /* Did the migration go as planned?  If yes, return the domain
      * object, but if no, clean up the empty qemu process.
      */
@@ -5188,6 +5439,31 @@ out:
     return ret;
 }
 
+static int
+qemudSecureMigrationData (virConnectPtr conn, const char *cookie, int cookielen,
+                          char *data, int datalen)
+{
+    struct secure_mig *migdata;
+    char ebuf[1024];
+
+    if (cookie == NULL || 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;
+
+    if (safewrite(migdata->socket, data, datalen) != datalen) {
+        qemudReportError (conn, NULL, NULL, VIR_ERR_OPERATION_FAILED,
+                          _("Could not write data to migration socket: %s"),
+                          virStrerror(errno, ebuf, sizeof ebuf));
+        return -1;
+    }
+
+    return 0;
+}
+
 static virDriver qemuDriver = {
     VIR_DRV_QEMU,
     "QEMU",
@@ -5264,6 +5540,7 @@ static virDriver qemuDriver = {
     qemudNodeDeviceDettach, /* nodeDeviceDettach */
     qemudNodeDeviceReAttach, /* nodeDeviceReAttach */
     qemudNodeDeviceReset, /* nodeDeviceReset */
+    qemudSecureMigrationData, /* secureMigrationData */
 };
 
 
diff --git a/src/remote_internal.c b/src/remote_internal.c
index 64c24f4..0c9b7ff 100644
--- a/src/remote_internal.c
+++ b/src/remote_internal.c
@@ -944,7 +944,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)))
@@ -2510,6 +2510,36 @@ done:
     return ddom;
 }
 
+static int remoteSecureMigrationData(virConnectPtr conn,
+                                     const 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 = (char *)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,
@@ -6964,6 +6994,7 @@ static virDriver driver = {
     remoteNodeDeviceDettach, /* nodeDeviceDettach */
     remoteNodeDeviceReAttach, /* nodeDeviceReAttach */
     remoteNodeDeviceReset, /* nodeDeviceReset */
+    remoteSecureMigrationData, /* secureMigrationData */
 };
 
 static virNetworkDriver network_driver = {
diff --git a/src/test.c b/src/test.c
index 1915b57..b93d8e7 100644
--- a/src/test.c
+++ b/src/test.c
@@ -3528,6 +3528,7 @@ static virDriver testDriver = {
     NULL, /* nodeDeviceDettach */
     NULL, /* nodeDeviceReAttach */
     NULL, /* nodeDeviceReset */
+    NULL, /* secureMigrationData */
 };
 
 static virNetworkDriver testNetworkDriver = {
diff --git a/src/uml_driver.c b/src/uml_driver.c
index 0e2c4dd..88140bb 100644
--- a/src/uml_driver.c
+++ b/src/uml_driver.c
@@ -1887,6 +1887,7 @@ static virDriver umlDriver = {
     NULL, /* nodeDeviceDettach */
     NULL, /* nodeDeviceReAttach */
     NULL, /* nodeDeviceReset */
+    NULL, /* secureMigrationData */
 };
 
 
diff --git a/src/virsh.c b/src/virsh.c
index cb32ede..08cbe4d 100644
--- a/src/virsh.c
+++ b/src/virsh.c
@@ -2320,6 +2320,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")},
@@ -2357,6 +2358,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 = virConnectOpenAuth (desturi, virConnectAuthPtrDefault, 0);
     if (!dconn) goto done;
diff --git a/src/xen_unified.c b/src/xen_unified.c
index e708980..2f6fcfe 100644
--- a/src/xen_unified.c
+++ b/src/xen_unified.c
@@ -1608,6 +1608,7 @@ static virDriver xenUnifiedDriver = {
     xenUnifiedNodeDeviceDettach, /* nodeDeviceDettach */
     xenUnifiedNodeDeviceReAttach, /* nodeDeviceReAttach */
     xenUnifiedNodeDeviceReset, /* nodeDeviceReset */
+    NULL, /* secureMigrationData */
 };
 
 /**

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]