[libvirt] [PATCHv2] 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. This will break migration if a hostname that can only be resolved on the source machine is passed in the migration URI, or if it 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 src/qemu/qemu_migration.c | 64 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 51 insertions(+), 13 deletions(-) diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 36e55d2..62e9260 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> @@ -1836,7 +1839,11 @@ qemuMigrationPrepareDirect(virQEMUDriverPtr driver, char *hostname = NULL; 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, " @@ -1885,16 +1892,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) @@ -1907,21 +1937,29 @@ 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; + } + 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(migrateFrom, sizeof(migrateFrom), + "tcp:[::]:%d", this_port); + } else { + snprintf(migrateFrom, sizeof(migrateFrom), + "tcp:0.0.0.0:%d", this_port); + } ret = qemuMigrationPrepareAny(driver, dconn, cookiein, cookieinlen, cookieout, cookieoutlen, dname, dom_xml, -- 1.7.12.4

On 02/18/2013 04:30 PM, Ján Tomko wrote:
+ 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; + } + getaddrinfo can return a list of IPv4 and IPv6 addresses for a hostname. If you want to give preference to the IPv6 address you will need to iterate the return info list. Alternatively you can pass in a hints addrinfo with ai_family set to AF_INET6.
In both cases you will have to call freeaddrinfo in the cleanup section to avoid a memory leak. -- Mit freundlichen Grüßen/Kind Regards Viktor Mihajlovski IBM Deutschland Research & Development GmbH Vorsitzender des Aufsichtsrats: Martin Jetter Geschäftsführung: Dirk Wittkopp Sitz der Gesellschaft: Böblingen Registergericht: Amtsgericht Stuttgart, HRB 243294

On 02/18/13 17:13, Viktor Mihajlovski wrote:
On 02/18/2013 04:30 PM, Ján Tomko wrote:
+ 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; + } + getaddrinfo can return a list of IPv4 and IPv6 addresses for a hostname. If you want to give preference to the IPv6 address you will need to iterate the return info list. Alternatively you can pass in a hints addrinfo with ai_family set to AF_INET6.
I only wanted to check the first result, which is what QEMU on the source will do (until http://git.qemu.org/?p=qemu.git;h=233aa5c in 1.3.0). This would break migration if the family of the first address is different on the source and destination. I assume this is less likely than getaddrinfo with INET6 hint returning an IPv6 address, even though there is no IPv6 connectivity between them.
In both cases you will have to call freeaddrinfo in the cleanup section to avoid a memory leak.
Thanks for catching that. Jan

On 02/19/2013 02:17 PM, Ján Tomko wrote:
I only wanted to check the first result, which is what QEMU on the source will do (until http://git.qemu.org/?p=qemu.git;h=233aa5c in 1.3.0). This would break migration if the family of the first address is different on the source and destination. I assume this is less likely than getaddrinfo with INET6 hint returning an IPv6 address, even though there is no IPv6 connectivity between them.
Well, since there are other reasons that the direct migration connection cannot be established and your approach will probably not cause a regression, I withdraw my comment. -- Mit freundlichen Grüßen/Kind Regards Viktor Mihajlovski IBM Deutschland Research & Development GmbH Vorsitzender des Aufsichtsrats: Martin Jetter Geschäftsführung: Dirk Wittkopp Sitz der Gesellschaft: Böblingen Registergericht: Amtsgericht Stuttgart, HRB 243294
participants (2)
-
Ján Tomko
-
Viktor Mihajlovski