mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-22 13:45:38 +00:00
Support IPv6 in port allocator
Also try to bind on IPv6 to check if the port is occupied. Change the mocked bind in the test to return EADDRINUSE for some ports only for the IPv4/IPv6 socket if we're testing on a host with IPv6 compiled in. Also mock socket() to make it fail with EAFNOTSUPPORTED if LIBVIRT_TEST_IPV4ONLY is set in the environment, to simulate a host without IPv6 support in the kernel. The tests are repeated again with this variable set. https://bugzilla.redhat.com/show_bug.cgi?id=1025407
This commit is contained in:
parent
531bc0bbd0
commit
0ee9081215
@ -100,21 +100,45 @@ virPortAllocatorPtr virPortAllocatorNew(const char *name,
|
||||
}
|
||||
|
||||
static int virPortAllocatorBindToPort(bool *used,
|
||||
unsigned short port)
|
||||
unsigned short port,
|
||||
int family)
|
||||
{
|
||||
struct sockaddr_in addr = {
|
||||
struct sockaddr_in6 addr6 = {
|
||||
.sin6_family = AF_INET6,
|
||||
.sin6_port = htons(port),
|
||||
.sin6_addr = IN6ADDR_ANY_INIT,
|
||||
};
|
||||
struct sockaddr_in addr4 = {
|
||||
.sin_family = AF_INET,
|
||||
.sin_port = htons(port),
|
||||
.sin_addr.s_addr = htonl(INADDR_ANY)
|
||||
};
|
||||
struct sockaddr* addr;
|
||||
size_t addrlen;
|
||||
int v6only = 1;
|
||||
int reuse = 1;
|
||||
int ret = -1;
|
||||
int fd = -1;
|
||||
bool ipv6 = false;
|
||||
|
||||
if (family == AF_INET6) {
|
||||
addr = (struct sockaddr*)&addr6;
|
||||
addrlen = sizeof(addr6);
|
||||
ipv6 = true;
|
||||
} else if (family == AF_INET) {
|
||||
addr = (struct sockaddr*)&addr4;
|
||||
addrlen = sizeof(addr4);
|
||||
} else {
|
||||
virReportError(VIR_ERR_INTERNAL_ERROR, _("Unknown family %d"), family);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*used = false;
|
||||
|
||||
fd = socket(PF_INET, SOCK_STREAM, 0);
|
||||
fd = socket(family, SOCK_STREAM, 0);
|
||||
if (fd < 0) {
|
||||
if (errno == EAFNOSUPPORT)
|
||||
return 0;
|
||||
virReportSystemError(errno, "%s", _("Unable to open test socket"));
|
||||
goto cleanup;
|
||||
}
|
||||
@ -126,7 +150,14 @@ static int virPortAllocatorBindToPort(bool *used,
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
|
||||
if (ipv6 && setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&v6only,
|
||||
sizeof(v6only)) < 0) {
|
||||
virReportSystemError(errno, "%s",
|
||||
_("Unable to set IPV6_V6ONLY flag"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (bind(fd, addr, addrlen) < 0) {
|
||||
if (errno == EADDRINUSE) {
|
||||
*used = true;
|
||||
ret = 0;
|
||||
@ -152,7 +183,7 @@ int virPortAllocatorAcquire(virPortAllocatorPtr pa,
|
||||
virObjectLock(pa);
|
||||
|
||||
for (i = pa->start; i <= pa->end && !*port; i++) {
|
||||
bool used = false;
|
||||
bool used = false, v6used = false;
|
||||
|
||||
if (virBitmapGetBit(pa->bitmap,
|
||||
i - pa->start, &used) < 0) {
|
||||
@ -164,10 +195,11 @@ int virPortAllocatorAcquire(virPortAllocatorPtr pa,
|
||||
if (used)
|
||||
continue;
|
||||
|
||||
if (virPortAllocatorBindToPort(&used, i) < 0)
|
||||
if (virPortAllocatorBindToPort(&v6used, i, AF_INET6) < 0 ||
|
||||
virPortAllocatorBindToPort(&used, i, AF_INET) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (!used) {
|
||||
if (!used && !v6used) {
|
||||
/* Add port to bitmap of reserved ports */
|
||||
if (virBitmapSetBit(pa->bitmap,
|
||||
i - pa->start) < 0) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Red Hat, Inc.
|
||||
* Copyright (C) 2013-2014 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
@ -19,6 +19,8 @@
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <stdlib.h>
|
||||
#include "virfile.h"
|
||||
|
||||
#ifdef MOCK_HELPER
|
||||
# include "internal.h"
|
||||
@ -26,6 +28,47 @@
|
||||
# include <errno.h>
|
||||
# include <arpa/inet.h>
|
||||
# include <netinet/in.h>
|
||||
# include <stdio.h>
|
||||
# include <dlfcn.h>
|
||||
|
||||
static bool host_has_ipv6 = false;
|
||||
static int (*realsocket)(int domain, int type, int protocol);
|
||||
|
||||
static void init_syms(void)
|
||||
{
|
||||
int fd;
|
||||
|
||||
if (realsocket)
|
||||
return;
|
||||
|
||||
realsocket = dlsym(RTLD_NEXT, "socket");
|
||||
|
||||
if (!realsocket) {
|
||||
fprintf(stderr, "Unable to find 'socket' symbol\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
fd = realsocket(AF_INET6, SOCK_STREAM, 0);
|
||||
if (fd < 0)
|
||||
return;
|
||||
|
||||
host_has_ipv6 = true;
|
||||
VIR_FORCE_CLOSE(fd);
|
||||
}
|
||||
|
||||
int socket(int domain,
|
||||
int type,
|
||||
int protocol)
|
||||
{
|
||||
init_syms();
|
||||
|
||||
if (getenv("LIBVIRT_TEST_IPV4ONLY") && domain == AF_INET6) {
|
||||
errno = EAFNOSUPPORT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return realsocket(domain, type, protocol);
|
||||
}
|
||||
|
||||
int bind(int sockfd ATTRIBUTE_UNUSED,
|
||||
const struct sockaddr *addr,
|
||||
@ -35,6 +78,19 @@ int bind(int sockfd ATTRIBUTE_UNUSED,
|
||||
|
||||
memcpy(&saddr, addr, sizeof(saddr));
|
||||
|
||||
if (host_has_ipv6 && !getenv("LIBVIRT_TEST_IPV4ONLY")) {
|
||||
if (saddr.sin_port == htons(5900) ||
|
||||
(saddr.sin_family == AF_INET &&
|
||||
saddr.sin_port == htons(5904)) ||
|
||||
(saddr.sin_family == AF_INET6 &&
|
||||
(saddr.sin_port == htons(5905) ||
|
||||
saddr.sin_port == htons(5906)))) {
|
||||
errno = EADDRINUSE;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (saddr.sin_port == htons(5900) ||
|
||||
saddr.sin_port == htons(5904) ||
|
||||
saddr.sin_port == htons(5905) ||
|
||||
@ -47,13 +103,11 @@ int bind(int sockfd ATTRIBUTE_UNUSED,
|
||||
}
|
||||
|
||||
#else
|
||||
# include <stdlib.h>
|
||||
|
||||
# include "testutils.h"
|
||||
# include "virutil.h"
|
||||
# include "virerror.h"
|
||||
# include "viralloc.h"
|
||||
# include "virfile.h"
|
||||
# include "virlog.h"
|
||||
# include "virportallocator.h"
|
||||
# include "virstring.h"
|
||||
@ -195,6 +249,14 @@ mymain(void)
|
||||
if (virtTestRun("Test alloc reuse", testAllocReuse, NULL) < 0)
|
||||
ret = -1;
|
||||
|
||||
setenv("LIBVIRT_TEST_IPV4ONLY", "really", 1);
|
||||
|
||||
if (virtTestRun("Test IPv4-only alloc all", testAllocAll, NULL) < 0)
|
||||
ret = -1;
|
||||
|
||||
if (virtTestRun("Test IPv4-only alloc reuse", testAllocReuse, NULL) < 0)
|
||||
ret = -1;
|
||||
|
||||
return ret==0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user