Allow migration over IPv6 by listening on [::] instead of 0.0.0.0 when QEMU supports it (QEMU_CAPS_IPV6_MIGRATION) and there is at least one v6 address configured on the system. Use virURIParse in qemuMigrationPrepareDirect to allow parsing IPv6 addresses, which would cause an 'incorrect :port' error message before. Move setting of migrateFrom from qemuMigrationPrepare{Direct,Tunnel} after domain XML parsing, since we need the QEMU binary path from it to get its capabilities. Bug: https://bugzilla.redhat.com/show_bug.cgi?id=846013 --- diff to v4: Always listen on IPv6 if it's available. Don't add a migration flag. v4: https://www.redhat.com/archives/libvir-list/2013-March/msg01213.html discussion: https://www.redhat.com/archives/libvir-list/2013-March/msg00515.html src/qemu/qemu_capabilities.c | 6 +++ src/qemu/qemu_capabilities.h | 1 + src/qemu/qemu_migration.c | 104 +++++++++++++++++++++++++++++++++---------- tests/qemuhelptest.c | 9 ++-- 4 files changed, 93 insertions(+), 27 deletions(-) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 3840b41..1e1da4d 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -213,6 +213,8 @@ VIR_ENUM_IMPL(virQEMUCaps, QEMU_CAPS_LAST, "virtio-ccw", "dtb", "megasas", + + "ipv6-migration", /* 135 */ ); struct _virQEMUCaps { @@ -1181,6 +1183,9 @@ virQEMUCapsComputeCmdFlags(const char *help, if (version >= 11000) virQEMUCapsSet(qemuCaps, QEMU_CAPS_CPU_HOST); + if (version >= 1001000) + virQEMUCapsSet(qemuCaps, QEMU_CAPS_IPV6_MIGRATION); + if (version >= 1002000) virQEMUCapsSet(qemuCaps, QEMU_CAPS_DEVICE_VIDEO_PRIMARY); return 0; @@ -2317,6 +2322,7 @@ virQEMUCapsInitQMPBasic(virQEMUCapsPtr qemuCaps) virQEMUCapsSet(qemuCaps, QEMU_CAPS_SECCOMP_SANDBOX); virQEMUCapsSet(qemuCaps, QEMU_CAPS_NO_KVM_PIT); virQEMUCapsSet(qemuCaps, QEMU_CAPS_DTB); + virQEMUCapsSet(qemuCaps, QEMU_CAPS_IPV6_MIGRATION); } diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index 7101f67..2ccc7c2 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -174,6 +174,7 @@ enum virQEMUCapsFlags { QEMU_CAPS_VIRTIO_CCW = 132, /* -device virtio-*-ccw */ QEMU_CAPS_DTB = 133, /* -dtb file */ QEMU_CAPS_SCSI_MEGASAS = 134, /* -device megasas */ + QEMU_CAPS_IPV6_MIGRATION = 135, /* -incoming [::] */ QEMU_CAPS_LAST, /* this must always be the last item */ }; diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 537b834..867c7f1 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -22,6 +22,8 @@ #include <config.h> +#include <netdb.h> +#include <sys/socket.h> #include <sys/time.h> #ifdef WITH_GNUTLS # include <gnutls/gnutls.h> @@ -1104,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; @@ -1980,8 +1982,8 @@ qemuMigrationPrepareAny(virQEMUDriverPtr driver, int *cookieoutlen, const char *dname, const char *dom_xml, - const char *migrateFrom, virStreamPtr st, + unsigned int port, unsigned long flags) { virDomainDefPtr def = NULL; @@ -1997,6 +1999,8 @@ qemuMigrationPrepareAny(virQEMUDriverPtr driver, char *xmlout = NULL; unsigned int cookieFlags; virCapsPtr caps = NULL; + const char *listenAddr = NULL; + char *migrateFrom = NULL; if (virTimeMillisNow(&now) < 0) return -1; @@ -2084,6 +2088,45 @@ qemuMigrationPrepareAny(virQEMUDriverPtr driver, } } + if (tunnel) { + /* QEMU will be started with -incoming stdio + * (which qemu_command might convert to exec:cat or fd:n) + */ + if (!(migrateFrom = strdup("stdio"))) { + virReportOOMError(); + goto cleanup; + } + } else { + virQEMUCapsPtr qemuCaps = NULL; + struct addrinfo *info = NULL; + struct addrinfo hints = { .ai_flags = AI_ADDRCONFIG, + .ai_socktype = SOCK_STREAM }; + + if (!(qemuCaps = virQEMUCapsCacheLookupCopy(driver->qemuCapsCache, + def->emulator))) + goto cleanup; + + /* Listen on :: instead of 0.0.0.0 if QEMU understands it + * and there is at least one IPv6 address configured + */ + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_IPV6_MIGRATION) && + getaddrinfo("::", NULL, &hints, &info) == 0) { + freeaddrinfo(info); + listenAddr = "[::]"; + } else { + listenAddr = "0.0.0.0"; + } + virObjectUnref(qemuCaps); + + /* QEMU will be started with -incoming [::]:port + * or -incoming 0.0.0.0:port + */ + if (virAsprintf(&migrateFrom, "tcp:%s:%d", listenAddr, port) < 0) { + virReportOOMError(); + goto cleanup; + } + } + if (!(vm = virDomainObjListAdd(driver->domains, driver->xmlconf, def, @@ -2172,7 +2215,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; } @@ -2213,6 +2256,7 @@ done: ret = 0; cleanup: + VIR_FREE(migrateFrom); VIR_FREE(origname); VIR_FREE(xmlout); virDomainDefFree(def); @@ -2270,12 +2314,9 @@ qemuMigrationPrepareTunnel(virQEMUDriverPtr driver, driver, dconn, NULLSTR(cookiein), cookieinlen, cookieout, cookieoutlen, st, NULLSTR(dname), dom_xml, flags); - /* QEMU will be started with -incoming stdio (which qemu_command might - * convert to exec:cat or fd:n) - */ ret = qemuMigrationPrepareAny(driver, dconn, cookiein, cookieinlen, cookieout, cookieoutlen, dname, dom_xml, - "stdio", st, flags); + st, 0, flags); return ret; } @@ -2296,9 +2337,10 @@ qemuMigrationPrepareDirect(virQEMUDriverPtr driver, static int port = 0; int this_port; char *hostname = NULL; - char migrateFrom [64]; const char *p; + char *uri_str = NULL; int ret = -1; + virURIPtr uri; VIR_DEBUG("driver=%p, dconn=%p, cookiein=%s, cookieinlen=%d, " "cookieout=%p, cookieoutlen=%p, uri_in=%s, uri_out=%p, " @@ -2347,16 +2389,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) @@ -2369,25 +2434,16 @@ 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 (*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); - ret = qemuMigrationPrepareAny(driver, dconn, cookiein, cookieinlen, cookieout, cookieoutlen, dname, dom_xml, - migrateFrom, NULL, flags); + NULL, this_port, flags); cleanup: VIR_FREE(hostname); if (ret != 0) diff --git a/tests/qemuhelptest.c b/tests/qemuhelptest.c index a28109a..05bb8a6 100644 --- a/tests/qemuhelptest.c +++ b/tests/qemuhelptest.c @@ -812,7 +812,8 @@ mymain(void) QEMU_CAPS_DEVICE_VMWARE_SVGA, QEMU_CAPS_DEVICE_USB_SERIAL, QEMU_CAPS_DEVICE_USB_NET, - QEMU_CAPS_DTB); + QEMU_CAPS_DTB, + QEMU_CAPS_IPV6_MIGRATION); DO_TEST("qemu-1.2.0", 1002000, 0, 0, QEMU_CAPS_VNC_COLON, QEMU_CAPS_NO_REBOOT, @@ -913,7 +914,8 @@ mymain(void) QEMU_CAPS_DEVICE_USB_SERIAL, QEMU_CAPS_DEVICE_USB_NET, QEMU_CAPS_DTB, - QEMU_CAPS_SCSI_MEGASAS); + QEMU_CAPS_SCSI_MEGASAS, + QEMU_CAPS_IPV6_MIGRATION); DO_TEST("qemu-kvm-1.2.0", 1002000, 1, 0, QEMU_CAPS_VNC_COLON, QEMU_CAPS_NO_REBOOT, @@ -1019,7 +1021,8 @@ mymain(void) QEMU_CAPS_DEVICE_USB_SERIAL, QEMU_CAPS_DEVICE_USB_NET, QEMU_CAPS_DTB, - QEMU_CAPS_SCSI_MEGASAS); + QEMU_CAPS_SCSI_MEGASAS, + QEMU_CAPS_IPV6_MIGRATION); return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } -- 1.8.1.5 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list