Currently, a listen address for a SPICE server can be specified. Later,
when the domain is migrated, we need to relocate the graphics which
involves telling new destination to the SPICE server. However, we can't
just assume the listen address is the new location, because the listen
address can be ANYCAST (0.0.0.0 for IPv4, :: for IPv6). In which case,
we want to pass the remote hostname. But there are some troubles with
ANYCAST. In both IPv4 and IPv6 it has many ways for specifying such
address. For instance, in IPv4: 0, 0.0, 0.0.0, 0.0.0.0. The number of
variations gets bigger in IPv6 world. Hence, in order to check for
ANYCAST address sanely, we should take the provided listen address,
parse it and format back in it's full form. Which is exactly what this
patch does.
---
src/qemu/qemu_migration.c | 18 +++++++++++++++++-
tests/sockettest.c | 24 ++++++++++++++++++++++++
2 files changed, 41 insertions(+), 1 deletion(-)
diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
index 7aa0476..3ca3f32 100644
--- a/src/qemu/qemu_migration.c
+++ b/src/qemu/qemu_migration.c
@@ -1735,42 +1735,58 @@ static int
qemuDomainMigrateGraphicsRelocate(virQEMUDriverPtr driver,
virDomainObjPtr vm,
qemuMigrationCookiePtr cookie)
{
qemuDomainObjPrivatePtr priv = vm->privateData;
int ret;
char *listenAddress;
+ virSocketAddr addr;
+ bool reformatted = false;
if (!cookie)
return 0;
if (!cookie->graphics)
return 0;
/* QEMU doesn't support VNC relocation yet, so
* skip it to avoid generating an error
*/
if (cookie->graphics->type != VIR_DOMAIN_GRAPHICS_TYPE_SPICE)
return 0;
listenAddress = cookie->graphics->listen;
+
+ /* Okay, here's the magic: some mgmt applications set bare '0' as listen
+ * address. On the other hand, it's a valid IPv4 address. This means, we
+ * need to reformat the address so the if statement below can check just
+ * for two ANYCAST addresses and not all their variants. */
+ if (listenAddress &&
+ virSocketAddrParse(&addr, listenAddress, AF_UNSPEC) > 0) {
+ listenAddress = virSocketAddrFormat(&addr);
+ reformatted = true;
+ }
+
if (!listenAddress ||
STREQ(listenAddress, "0.0.0.0") ||
- STREQ(listenAddress, "::"))
+ STREQ(listenAddress, "::")) {
+ if (reformatted)
+ VIR_FREE(listenAddress);
listenAddress = cookie->remoteHostname;
+ }
ret = qemuDomainObjEnterMonitorAsync(driver, vm,
QEMU_ASYNC_JOB_MIGRATION_OUT);
if (ret == 0) {
ret = qemuMonitorGraphicsRelocate(priv->mon,
cookie->graphics->type,
listenAddress,
cookie->graphics->port,
cookie->graphics->tlsPort,
cookie->graphics->tlsSubject);
qemuDomainObjExitMonitor(driver, vm);
}
return ret;
}
diff --git a/tests/sockettest.c b/tests/sockettest.c
index 156ef45..5b36a6c 100644
--- a/tests/sockettest.c
+++ b/tests/sockettest.c
@@ -162,96 +162,120 @@ static int
mymain(void)
{
int ret = 0;
/* Some of our tests deliberately test failure cases, so
* register a handler to stop error messages cluttering
* up display
*/
if (!virTestGetDebug())
virSetErrorFunc(NULL, testQuietError);
#define DO_TEST_PARSE(addrstr, family, pass) \
do { \
virSocketAddr addr; \
struct testParseData data = { &addr, addrstr, family, pass }; \
memset(&addr, 0, sizeof(addr)); \
if (virtTestRun("Test parse " addrstr, \
1, testParseHelper, &data) < 0) \
ret = -1; \
} while (0)
#define DO_TEST_PARSE_AND_FORMAT(addrstr, family, pass) \
do { \
virSocketAddr addr; \
struct testParseData data = { &addr, addrstr, family, pass }; \
memset(&addr, 0, sizeof(addr)); \
if (virtTestRun("Test parse " addrstr " family " #family,
\
1, testParseHelper, &data) < 0) \
ret = -1; \
struct testFormatData data2 = { &addr, addrstr, pass }; \
if (virtTestRun("Test format " addrstr " family " #family,
\
1, testFormatHelper, &data2) < 0) \
ret = -1; \
} while (0)
+#define DO_TEST_PARSE_AND_CHECK_FORMAT(addrstr, addrformated, family, pass) \
+ do { \
+ virSocketAddr addr; \
+ struct testParseData data = { &addr, addrstr, family, true}; \
+ memset(&addr, 0, sizeof(addr)); \
+ if (virtTestRun("Test parse " addrstr " family " #family,
\
+ 1, testParseHelper, &data) < 0) \
+ ret = -1; \
+ struct testFormatData data2 = { &addr, addrformated, pass }; \
+ if (virtTestRun("Test format " addrstr " family " #family,
\
+ 1, testFormatHelper, &data2) < 0) \
+ ret = -1; \
+ } while (0)
+
#define DO_TEST_RANGE(saddr, eaddr, size, pass) \
do { \
struct testRangeData data = { saddr, eaddr, size, pass }; \
if (virtTestRun("Test range " saddr " -> " eaddr "
size " #size, \
1, testRangeHelper, &data) < 0) \
ret = -1; \
} while (0)
#define DO_TEST_NETMASK(addr1, addr2, netmask, pass) \
do { \
struct testNetmaskData data = { addr1, addr2, netmask, pass }; \
if (virtTestRun("Test netmask " addr1 " + " addr2 " in
" netmask, \
1, testNetmaskHelper, &data) < 0) \
ret = -1; \
} while (0)
DO_TEST_PARSE_AND_FORMAT("127.0.0.1", AF_UNSPEC, true);
DO_TEST_PARSE_AND_FORMAT("127.0.0.1", AF_INET, true);
DO_TEST_PARSE_AND_FORMAT("127.0.0.1", AF_INET6, false);
DO_TEST_PARSE_AND_FORMAT("127.0.0.1", AF_UNIX, false);
DO_TEST_PARSE_AND_FORMAT("127.0.0.256", AF_UNSPEC, false);
+ DO_TEST_PARSE_AND_CHECK_FORMAT("127.0.0.2", "127.0.0.2", AF_INET,
true);
+ DO_TEST_PARSE_AND_CHECK_FORMAT("127.0.0.2", "127.0.0.3", AF_INET,
false);
+ DO_TEST_PARSE_AND_CHECK_FORMAT("0", "0.0.0.0", AF_INET, true);
+ DO_TEST_PARSE_AND_CHECK_FORMAT("127", "0.0.0.127", AF_INET,
true);
+ DO_TEST_PARSE_AND_CHECK_FORMAT("127", "127.0.0.0", AF_INET,
false);
+ DO_TEST_PARSE_AND_CHECK_FORMAT("127.2", "127.0.0.2", AF_INET,
true);
+ DO_TEST_PARSE_AND_CHECK_FORMAT("127.2", "127.2.0.0", AF_INET,
false);
+ DO_TEST_PARSE_AND_CHECK_FORMAT("1.2.3", "1.2.0.3", AF_INET,
true);
+ DO_TEST_PARSE_AND_CHECK_FORMAT("1.2.3", "1.2.3.0", AF_INET,
false);
+
DO_TEST_PARSE_AND_FORMAT("::1", AF_UNSPEC, true);
DO_TEST_PARSE_AND_FORMAT("::1", AF_INET, false);
DO_TEST_PARSE_AND_FORMAT("::1", AF_INET6, true);
DO_TEST_PARSE_AND_FORMAT("::1", AF_UNIX, false);
DO_TEST_PARSE_AND_FORMAT("::ffff", AF_UNSPEC, true);
DO_TEST_RANGE("192.168.122.1", "192.168.122.1", 1, true);
DO_TEST_RANGE("192.168.122.1", "192.168.122.20", 20, true);
DO_TEST_RANGE("192.168.122.0", "192.168.122.255", 256, true);
DO_TEST_RANGE("192.168.122.20", "192.168.122.1", -1, false);
DO_TEST_RANGE("10.0.0.1", "192.168.122.20", -1, false);
DO_TEST_RANGE("192.168.122.20", "10.0.0.1", -1, false);
DO_TEST_RANGE("2000::1", "2000::1", 1, true);
DO_TEST_RANGE("2000::1", "2000::2", 2, true);
DO_TEST_RANGE("2000::2", "2000::1", -1, false);
DO_TEST_RANGE("2000::1", "9001::1", -1, false);
DO_TEST_NETMASK("192.168.122.1", "192.168.122.2",
"255.255.255.0", true);
DO_TEST_NETMASK("192.168.122.1", "192.168.122.4",
"255.255.255.248", true);
DO_TEST_NETMASK("192.168.122.1", "192.168.123.2",
"255.255.255.0", false);
DO_TEST_NETMASK("192.168.122.1", "192.168.123.2",
"255.255.0.0", true);
DO_TEST_NETMASK("2000::1:1", "2000::1:1",
"ffff:ffff:ffff:ffff:ffff:ffff:ffff:0", true);
DO_TEST_NETMASK("2000::1:1", "2000::2:1",
"ffff:ffff:ffff:ffff:ffff:ffff:ffff:0", false);
DO_TEST_NETMASK("2000::1:1", "2000::2:1",
"ffff:ffff:ffff:ffff:ffff:ffff:fff8:0", true);
DO_TEST_NETMASK("2000::1:1", "9000::1:1",
"ffff:ffff:ffff:ffff:ffff:ffff:ffff:0", false);
return ret==0 ? EXIT_SUCCESS : EXIT_FAILURE;
}
--
1.8.2.1