[PATCH 0/3] esx: Form URLs containing IPv6 addresses correctly
https://issues.redhat.com/browse/RHEL-138300 This bug requires a further fix to allow libvirt to access ESXi servers over IPv6. The way that we constructed the URL (for fetching things from the SDK with curl) was wrong. We formed URLs like: https://1234:56:0:789a:bcde:72ff:fe0a:7baa:443/sdk but with IPv6 we need to put [...] around the IPv6 address. The first patch just adds some debugging so we can see what URLs we are passing to curl. THe second patch is a bit of abstraction so that we're only creating URLs in one place (but two functions). The third detects if the server name is an IPv6 address and adds the square brackets as appropriate. Tested by me, by connecting to an IPv6 VMware server and listing all the domains, and getting the libvirt XML of a single domain. I didn't test all possible operations in depth. The bug was reported by Ming Xie. Rich.
Signed-off-by: Richard W.M. Jones <rjones@redhat.com> --- src/esx/esx_vi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/esx/esx_vi.c b/src/esx/esx_vi.c index 3264afc13a..c5ecb22bc4 100644 --- a/src/esx/esx_vi.c +++ b/src/esx/esx_vi.c @@ -383,6 +383,7 @@ esxVI_CURL_Download(esxVI_CURL *curl, const char *url, char **content, } VIR_WITH_MUTEX_LOCK_GUARD(&curl->lock) { + VIR_DEBUG("URL: %s", url); curl_easy_setopt(curl->handle, CURLOPT_URL, url); curl_easy_setopt(curl->handle, CURLOPT_RANGE, range); curl_easy_setopt(curl->handle, CURLOPT_WRITEDATA, &buffer); @@ -423,6 +424,7 @@ esxVI_CURL_Upload(esxVI_CURL *curl, const char *url, const char *content) } VIR_WITH_MUTEX_LOCK_GUARD(&curl->lock) { + VIR_DEBUG("URL: %s", url); curl_easy_setopt(curl->handle, CURLOPT_URL, url); curl_easy_setopt(curl->handle, CURLOPT_RANGE, NULL); curl_easy_setopt(curl->handle, CURLOPT_READDATA, &content); @@ -1220,6 +1222,7 @@ esxVI_Context_Execute(esxVI_Context *ctx, const char *methodName, return -1; VIR_WITH_MUTEX_LOCK_GUARD(&ctx->curl->lock) { + VIR_DEBUG("URL: %s", ctx->url); curl_easy_setopt(ctx->curl->handle, CURLOPT_URL, ctx->url); curl_easy_setopt(ctx->curl->handle, CURLOPT_RANGE, NULL); curl_easy_setopt(ctx->curl->handle, CURLOPT_WRITEDATA, &buffer); -- 2.52.0
On 1/26/26 12:05, Richard W.M. Jones wrote:
Signed-off-by: Richard W.M. Jones <rjones@redhat.com> --- src/esx/esx_vi.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/src/esx/esx_vi.c b/src/esx/esx_vi.c index 3264afc13a..c5ecb22bc4 100644 --- a/src/esx/esx_vi.c +++ b/src/esx/esx_vi.c @@ -383,6 +383,7 @@ esxVI_CURL_Download(esxVI_CURL *curl, const char *url, char **content, }
VIR_WITH_MUTEX_LOCK_GUARD(&curl->lock) { + VIR_DEBUG("URL: %s", url); curl_easy_setopt(curl->handle, CURLOPT_URL, url); curl_easy_setopt(curl->handle, CURLOPT_RANGE, range); curl_easy_setopt(curl->handle, CURLOPT_WRITEDATA, &buffer); @@ -423,6 +424,7 @@ esxVI_CURL_Upload(esxVI_CURL *curl, const char *url, const char *content) }
VIR_WITH_MUTEX_LOCK_GUARD(&curl->lock) { + VIR_DEBUG("URL: %s", url); curl_easy_setopt(curl->handle, CURLOPT_URL, url); curl_easy_setopt(curl->handle, CURLOPT_RANGE, NULL); curl_easy_setopt(curl->handle, CURLOPT_READDATA, &content); @@ -1220,6 +1222,7 @@ esxVI_Context_Execute(esxVI_Context *ctx, const char *methodName, return -1;
VIR_WITH_MUTEX_LOCK_GUARD(&ctx->curl->lock) { + VIR_DEBUG("URL: %s", ctx->url); curl_easy_setopt(ctx->curl->handle, CURLOPT_URL, ctx->url); curl_easy_setopt(ctx->curl->handle, CURLOPT_RANGE, NULL); curl_easy_setopt(ctx->curl->handle, CURLOPT_WRITEDATA, &buffer);
Alternatively, just put one VIR_DEBUG() into esxVI_CURL_Perform() Michal
On Mon, Jan 26, 2026 at 01:58:37PM +0100, Michal Prívozník wrote:
On 1/26/26 12:05, Richard W.M. Jones wrote:
Signed-off-by: Richard W.M. Jones <rjones@redhat.com> --- src/esx/esx_vi.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/src/esx/esx_vi.c b/src/esx/esx_vi.c index 3264afc13a..c5ecb22bc4 100644 --- a/src/esx/esx_vi.c +++ b/src/esx/esx_vi.c @@ -383,6 +383,7 @@ esxVI_CURL_Download(esxVI_CURL *curl, const char *url, char **content, }
VIR_WITH_MUTEX_LOCK_GUARD(&curl->lock) { + VIR_DEBUG("URL: %s", url); curl_easy_setopt(curl->handle, CURLOPT_URL, url); curl_easy_setopt(curl->handle, CURLOPT_RANGE, range); curl_easy_setopt(curl->handle, CURLOPT_WRITEDATA, &buffer); @@ -423,6 +424,7 @@ esxVI_CURL_Upload(esxVI_CURL *curl, const char *url, const char *content) }
VIR_WITH_MUTEX_LOCK_GUARD(&curl->lock) { + VIR_DEBUG("URL: %s", url); curl_easy_setopt(curl->handle, CURLOPT_URL, url); curl_easy_setopt(curl->handle, CURLOPT_RANGE, NULL); curl_easy_setopt(curl->handle, CURLOPT_READDATA, &content); @@ -1220,6 +1222,7 @@ esxVI_Context_Execute(esxVI_Context *ctx, const char *methodName, return -1;
VIR_WITH_MUTEX_LOCK_GUARD(&ctx->curl->lock) { + VIR_DEBUG("URL: %s", ctx->url); curl_easy_setopt(ctx->curl->handle, CURLOPT_URL, ctx->url); curl_easy_setopt(ctx->curl->handle, CURLOPT_RANGE, NULL); curl_easy_setopt(ctx->curl->handle, CURLOPT_WRITEDATA, &buffer);
Alternatively, just put one VIR_DEBUG() into esxVI_CURL_Perform()
I'm going to do a V2 which fixes this and one other cosmetic thing. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming and virtualization blog: http://rwmj.wordpress.com Fedora Windows cross-compiler. Compile Windows programs, test, and build Windows installers. Over 100 libraries supported. http://fedoraproject.org/wiki/MinGW
Abstract the places where we create URLs into one place (but actually into two functions because of how URLs are used). Signed-off-by: Richard W.M. Jones <rjones@redhat.com> --- src/esx/esx_driver.c | 55 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 10 deletions(-) diff --git a/src/esx/esx_driver.c b/src/esx/esx_driver.c index 02f30c2b19..8d7c58c88f 100644 --- a/src/esx/esx_driver.c +++ b/src/esx/esx_driver.c @@ -582,7 +582,39 @@ esxCapsInit(esxPrivate *priv) return NULL; } +static char * +esxCreateURL(const char *transport, + const char *server, + int port, + const char *path) +{ + char *url; + url = g_strdup_printf("%s://%s:%d/%s", + transport, + server, + port, + path); + return url; +} + +static void +esxCreateURLBuffer(virBuffer *buffer, + const char *transport, + const char *server, + int port, + const char *path) +{ + /* Same as above, but add it to a buffer because the calling code + * will append query strings etc. + */ + virBufferAsprintf(buffer, + "%s://%s:%d/%s", + transport, + server, + port, + path); +} static int esxConnectToHost(esxPrivate *priv, @@ -619,8 +651,8 @@ esxConnectToHost(esxPrivate *priv, conn->uri->server))) goto cleanup; - url = g_strdup_printf("%s://%s:%d/sdk", priv->parsedUri->transport, - conn->uri->server, conn->uri->port); + url = esxCreateURL(priv->parsedUri->transport, + conn->uri->server, conn->uri->port, "sdk"); if (esxVI_Context_Alloc(&priv->host) < 0 || esxVI_Context_Connect(priv->host, url, ipAddress, username, password, @@ -706,8 +738,8 @@ esxConnectToVCenter(esxPrivate *priv, if (!(password = virAuthGetPassword(conn, auth, "esx", username, hostname))) return -1; - url = g_strdup_printf("%s://%s:%d/sdk", priv->parsedUri->transport, hostname, - conn->uri->port); + url = esxCreateURL(priv->parsedUri->transport, hostname, + conn->uri->port, "sdk"); if (esxVI_Context_Alloc(&priv->vCenter) < 0 || esxVI_Context_Connect(priv->vCenter, url, ipAddress, username, @@ -2357,8 +2389,9 @@ esxDomainScreenshot(virDomainPtr domain, virStreamPtr stream, } /* Build URL */ - virBufferAsprintf(&buffer, "%s://%s:%d/screen?id=", priv->parsedUri->transport, - domain->conn->uri->server, domain->conn->uri->port); + esxCreateURLBuffer(&buffer, priv->parsedUri->transport, + domain->conn->uri->server, domain->conn->uri->port, + "screen?id="); virBufferURIEncodeString(&buffer, virtualMachine->obj->value); url = virBufferContentAndReset(&buffer); @@ -2563,8 +2596,9 @@ esxDomainGetXMLDesc(virDomainPtr domain, unsigned int flags) goto cleanup; } - virBufferAsprintf(&buffer, "%s://%s:%d/folder/", priv->parsedUri->transport, - domain->conn->uri->server, domain->conn->uri->port); + esxCreateURLBuffer(&buffer, priv->parsedUri->transport, + domain->conn->uri->server, domain->conn->uri->port, + "folder/"); virBufferURIEncodeString(&buffer, directoryAndFileName); virBufferAddLit(&buffer, "?dcPath="); esxUtil_EscapeInventoryObject(&buffer, priv->primary->datacenterPath); @@ -2987,8 +3021,9 @@ esxDomainDefineXMLFlags(virConnectPtr conn, const char *xml, unsigned int flags) goto cleanup; } - virBufferAsprintf(&buffer, "%s://%s:%d/folder/", priv->parsedUri->transport, - conn->uri->server, conn->uri->port); + esxCreateURLBuffer(&buffer, priv->parsedUri->transport, + conn->uri->server, conn->uri->port, + "folder/"); if (directoryName) { virBufferURIEncodeString(&buffer, directoryName); -- 2.52.0
When creating a URL to pass to curl, if the server name is an IPv6 address, enclose it in '[...]', for example forming a URL like: https://[1234:56:0:789a:bcde:72ff:fe0a:7baa]:443/sdk Fixes: https://issues.redhat.com/browse/RHEL-138300 Updates: commit 845210011a9ffd9d17e30c51cbc81ba67c5d3166 Signed-off-by: Richard W.M. Jones <rjones@redhat.com> --- src/esx/esx_driver.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/esx/esx_driver.c b/src/esx/esx_driver.c index 8d7c58c88f..208077eeda 100644 --- a/src/esx/esx_driver.c +++ b/src/esx/esx_driver.c @@ -582,6 +582,19 @@ esxCapsInit(esxPrivate *priv) return NULL; } +static bool +esxServerIsIPv6(const char *server) +{ + size_t i; + const size_t n = strlen(server); + + for (i = 0; i < n; ++i) { + if (!g_ascii_isxdigit(server[i]) && server[i] != ':') + return false; + } + return true; +} + static char * esxCreateURL(const char *transport, const char *server, @@ -589,10 +602,13 @@ esxCreateURL(const char *transport, const char *path) { char *url; + const bool is_ipv6 = esxServerIsIPv6(server); - url = g_strdup_printf("%s://%s:%d/%s", + url = g_strdup_printf("%s://%s%s%s:%d/%s", transport, + is_ipv6 ? "[" : "", server, + is_ipv6 ? "]" : "", port, path); return url; @@ -605,13 +621,17 @@ esxCreateURLBuffer(virBuffer *buffer, int port, const char *path) { + const bool is_ipv6 = esxServerIsIPv6(server); + /* Same as above, but add it to a buffer because the calling code * will append query strings etc. */ virBufferAsprintf(buffer, - "%s://%s:%d/%s", + "%s://%s%s%s:%d/%s", transport, + is_ipv6 ? "[" : "", server, + is_ipv6 ? "]" : "", port, path); } -- 2.52.0
On 1/26/26 12:04, Richard W.M. Jones wrote:
https://issues.redhat.com/browse/RHEL-138300
This bug requires a further fix to allow libvirt to access ESXi servers over IPv6. The way that we constructed the URL (for fetching things from the SDK with curl) was wrong. We formed URLs like:
https://1234:56:0:789a:bcde:72ff:fe0a:7baa:443/sdk
but with IPv6 we need to put [...] around the IPv6 address.
The first patch just adds some debugging so we can see what URLs we are passing to curl. THe second patch is a bit of abstraction so that we're only creating URLs in one place (but two functions). The third detects if the server name is an IPv6 address and adds the square brackets as appropriate.
Tested by me, by connecting to an IPv6 VMware server and listing all the domains, and getting the libvirt XML of a single domain. I didn't test all possible operations in depth.
The bug was reported by Ming Xie.
Rich.
Reviewed-by: Michal Privoznik <mprivozn@redhat.com> Michal
participants (2)
-
Michal Prívozník -
Richard W.M. Jones