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