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

[libvirt] [PATCH] qemu: enable direct migration over IPv6



Use virURIParse in qemuMigrationPrepareDirect to allow parsing
IPv6 addresses, which would cause an 'incorrect :port' error message
before.

To be able to migrate over IPv6, QEMU needs to listen on [::] instead
of 0.0.0.0. This patch adds a call to getaddrinfo and sets the listen
address based on the result.

It also uses the same listen address for the NBD server.

This will break migration if a hostname does not resolve to the same
address family on both sides.

Bug: https://bugzilla.redhat.com/show_bug.cgi?id=846013
---

Diff to V1:
* initialize uri_str
* reuse STRSKIP("tcp:") result instead of doing strlen on it
* print a warning instead of failing when the hostname can't be resolved

Diff to V2:
* freeaddrinfo
* separate the listen address to allow reuse in qemuMigrationStartNBDServer

 src/qemu/qemu_migration.c | 77 ++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 59 insertions(+), 18 deletions(-)

diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
index cae58fa..ff9b959 100644
--- a/src/qemu/qemu_migration.c
+++ b/src/qemu/qemu_migration.c
@@ -22,7 +22,10 @@
 
 #include <config.h>
 
+#include <netdb.h>
+#include <sys/socket.h>
 #include <sys/time.h>
+#include <sys/types.h>
 #ifdef WITH_GNUTLS
 # include <gnutls/gnutls.h>
 # include <gnutls/x509.h>
@@ -1103,12 +1106,12 @@ error:
  */
 static int
 qemuMigrationStartNBDServer(virQEMUDriverPtr driver,
-                            virDomainObjPtr vm)
+                            virDomainObjPtr vm,
+                            const char *listenAddr)
 {
     int ret = -1;
     qemuDomainObjPrivatePtr priv = vm->privateData;
     unsigned short port = 0;
-    const char *listenAddr = "0.0.0.0";
     char *diskAlias = NULL;
     size_t i;
 
@@ -1981,6 +1984,7 @@ qemuMigrationPrepareAny(virQEMUDriverPtr driver,
                         const char *dom_xml,
                         const char *migrateFrom,
                         virStreamPtr st,
+                        const char *listenAddr,
                         unsigned long flags)
 {
     virDomainDefPtr def = NULL;
@@ -2168,7 +2172,7 @@ done:
         if (flags & VIR_MIGRATE_TUNNELLED)
             VIR_DEBUG("NBD in tunnelled migration is currently not supported");
         else {
-            if (qemuMigrationStartNBDServer(driver, vm) < 0) {
+            if (qemuMigrationStartNBDServer(driver, vm, listenAddr) < 0) {
                 /* error already reported */
                 goto endjob;
             }
@@ -2271,7 +2275,7 @@ qemuMigrationPrepareTunnel(virQEMUDriverPtr driver,
      */
     ret = qemuMigrationPrepareAny(driver, dconn, cookiein, cookieinlen,
                                   cookieout, cookieoutlen, dname, dom_xml,
-                                  "stdio", st, flags);
+                                  "stdio", st, NULL, flags);
     return ret;
 }
 
@@ -2292,9 +2296,14 @@ qemuMigrationPrepareDirect(virQEMUDriverPtr driver,
     static int port = 0;
     int this_port;
     char *hostname = NULL;
+    char listenAddr[8];
     char migrateFrom [64];
     const char *p;
+    char *uri_str = NULL;
     int ret = -1;
+    bool ipv6 = false;
+    struct addrinfo *info;
+    virURIPtr uri;
 
     VIR_DEBUG("driver=%p, dconn=%p, cookiein=%s, cookieinlen=%d, "
               "cookieout=%p, cookieoutlen=%p, uri_in=%s, uri_out=%p, "
@@ -2343,16 +2352,39 @@ qemuMigrationPrepareDirect(virQEMUDriverPtr driver,
          * URI when passing it to the qemu monitor, so bad
          * characters in hostname part don't matter.
          */
-        if (!STRPREFIX(uri_in, "tcp:")) {
+        if (!(p = STRSKIP(uri_in, "tcp:"))) {
             virReportError(VIR_ERR_INVALID_ARG, "%s",
                            _("only tcp URIs are supported for KVM/QEMU"
                              " migrations"));
             goto cleanup;
         }
 
-        /* Get the port number. */
-        p = strrchr(uri_in, ':');
-        if (p == strchr(uri_in, ':')) {
+        /* Convert uri_in to well-formed URI with // after tcp: */
+        if (!(STRPREFIX(uri_in, "tcp://"))) {
+            if (virAsprintf(&uri_str, "tcp://%s", p) < 0) {
+                virReportOOMError();
+                goto cleanup;
+            }
+        }
+
+        uri = virURIParse(uri_str ? uri_str : uri_in);
+        VIR_FREE(uri_str);
+
+        if (uri == NULL) {
+            virReportError(VIR_ERR_INVALID_ARG, _("unable to parse URI: %s"),
+                           uri_in);
+            goto cleanup;
+        }
+
+        if (uri->server == NULL) {
+            virReportError(VIR_ERR_INVALID_ARG, _("missing host in migration"
+                                                  " URI: %s"), uri_in);
+            goto cleanup;
+        } else {
+            hostname = uri->server;
+        }
+
+        if (uri->port == 0) {
             /* Generate a port */
             this_port = QEMUD_MIGRATION_FIRST_PORT + port++;
             if (port == QEMUD_MIGRATION_NUM_PORTS)
@@ -2365,25 +2397,34 @@ qemuMigrationPrepareDirect(virQEMUDriverPtr driver,
             }
 
         } else {
-            p++; /* definitely has a ':' in it, see above */
-            this_port = virParseNumber(&p);
-            if (this_port == -1 || p-uri_in != strlen(uri_in)) {
-                virReportError(VIR_ERR_INVALID_ARG,
-                               "%s", _("URI ended with incorrect ':port'"));
-                goto cleanup;
-            }
+            this_port = uri->port;
         }
     }
 
+    if (getaddrinfo(hostname, NULL, NULL, &info)) {
+        VIR_WARN("unable to get address info for %s, defaulting to IPv4",
+                 hostname);
+    } else {
+        ipv6 = info->ai_family == AF_INET6;
+        freeaddrinfo(info);
+    }
+
     if (*uri_out)
         VIR_DEBUG("Generated uri_out=%s", *uri_out);
 
-    /* QEMU will be started with -incoming tcp:0.0.0.0:port */
-    snprintf(migrateFrom, sizeof(migrateFrom), "tcp:0.0.0.0:%d", this_port);
+    /* QEMU will be started with -incoming tcp:0.0.0.0:port
+     * or -incoming tcp:[::]:port for IPv6 */
+    if (ipv6)
+        snprintf(listenAddr, sizeof(listenAddr), "[::]");
+    else
+        snprintf(listenAddr, sizeof(listenAddr), "0.0.0.0");
+
+    snprintf(migrateFrom, sizeof(migrateFrom),
+             "tcp:%s:%d", listenAddr, this_port);
 
     ret = qemuMigrationPrepareAny(driver, dconn, cookiein, cookieinlen,
                                   cookieout, cookieoutlen, dname, dom_xml,
-                                  migrateFrom, NULL, flags);
+                                  migrateFrom, NULL, listenAddr, flags);
 cleanup:
     VIR_FREE(hostname);
     if (ret != 0)
-- 
1.7.12.4


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