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
---
 src/util/virportallocator.c  | 46 +++++++++++++++++++++++++-----
 tests/virportallocatortest.c | 68 ++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 104 insertions(+), 10 deletions(-)

diff --git a/src/util/virportallocator.c b/src/util/virportallocator.c
index 5d51434..06174b0 100644
--- a/src/util/virportallocator.c
+++ b/src/util/virportallocator.c
@@ -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) {
diff --git a/tests/virportallocatortest.c b/tests/virportallocatortest.c
index 721356e..7fe18df 100644
--- a/tests/virportallocatortest.c
+++ b/tests/virportallocatortest.c
@@ -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;
 }
 
-- 
1.8.3.2

--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list

Reply via email to