]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
Add a port allocator class
authorDaniel P. Berrange <berrange@redhat.com>
Wed, 9 Jan 2013 15:11:50 +0000 (15:11 +0000)
committerDaniel P. Berrange <berrange@redhat.com>
Wed, 16 Jan 2013 11:02:58 +0000 (11:02 +0000)
Introduce a virPortAllocator for managing TCP port allocations.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
.gitignore
po/POTFILES.in
src/Makefile.am
src/libvirt_private.syms
src/util/virportallocator.c [new file with mode: 0644]
src/util/virportallocator.h [new file with mode: 0644]
tests/Makefile.am
tests/virportallocatortest.c [new file with mode: 0644]

index be83e28f91422014828f1e5c03d1bec03da21b01..291d8347e67c7ee42b51c932fb9b8a889b3de517 100644 (file)
 /tests/virkeyfiletest
 /tests/virlockspacetest
 /tests/virnet*test
+/tests/virportallocatortest
 /tests/virshtest
 /tests/virstringtest
 /tests/virtimetest
index fb4312094bd300bb4b66e67b04733d9629b24ffc..bd2c02e4c1e5de50e515a6450cd62d9d95f5a36a 100644 (file)
@@ -167,6 +167,7 @@ src/util/virnodesuspend.c
 src/util/virobject.c
 src/util/virpci.c
 src/util/virpidfile.c
+src/util/virportallocator.c
 src/util/virprocess.c
 src/util/virrandom.c
 src/util/virsexpr.c
index b1318c55a83a31c99f1a0773f764a59cda8b7e83..070a089e921633dc51b6acbe0ea124b675b9336d 100644 (file)
@@ -97,6 +97,7 @@ UTIL_SOURCES =                                                        \
                util/virobject.c util/virobject.h               \
                util/virpci.c util/virpci.h                     \
                util/virpidfile.c util/virpidfile.h             \
+               util/virportallocator.c util/virportallocator.h \
                util/virprocess.c util/virprocess.h             \
                util/virrandom.h util/virrandom.c               \
                util/virsexpr.c util/virsexpr.h                 \
index 0f2950a94bc772060d79118dcdb054cd02872383..521f8e045cb45ceff1055443507de2e08b7a2e56 100644 (file)
@@ -1798,6 +1798,12 @@ virPidFileWrite;
 virPidFileWritePath;
 
 
+# virportallocator.h
+virPortAllocatorAcquire;
+virPortAllocatorNew;
+virPortAllocatorRelease;
+
+
 # virprocess.h
 virProcessAbort;
 virProcessGetAffinity;
diff --git a/src/util/virportallocator.c b/src/util/virportallocator.c
new file mode 100644 (file)
index 0000000..033aee4
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * virportallocator.c: Allocate & track TCP port allocations
+ *
+ * Copyright (C) 2013 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
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <config.h>
+
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include "viralloc.h"
+#include "virbitmap.h"
+#include "virportallocator.h"
+#include "virthread.h"
+#include "virerror.h"
+#include "virfile.h"
+
+#define VIR_FROM_THIS VIR_FROM_NONE
+
+struct _virPortAllocator {
+    virObjectLockable parent;
+    virBitmapPtr bitmap;
+
+    unsigned short start;
+    unsigned short end;
+};
+
+static virClassPtr virPortAllocatorClass;
+
+static void
+virPortAllocatorDispose(void *obj)
+{
+    virPortAllocatorPtr pa = obj;
+
+    virBitmapFree(pa->bitmap);
+}
+
+static int virPortAllocatorOnceInit(void)
+{
+    if (!(virPortAllocatorClass = virClassNew(virClassForObjectLockable(),
+                                              "virPortAllocator",
+                                              sizeof(virPortAllocator),
+                                              virPortAllocatorDispose)))
+        return -1;
+
+    return 0;
+}
+
+VIR_ONCE_GLOBAL_INIT(virPortAllocator)
+
+virPortAllocatorPtr virPortAllocatorNew(unsigned short start,
+                                        unsigned short end)
+{
+    virPortAllocatorPtr pa;
+
+    if (start >= end) {
+        virReportInvalidArg(start, "start port %d must be less than end port %d",
+                            start, end);
+        return NULL;
+    }
+
+    if (virPortAllocatorInitialize() < 0)
+        return NULL;
+
+    if (!(pa = virObjectLockableNew(virPortAllocatorClass)))
+        return NULL;
+
+    pa->start = start;
+    pa->end = end;
+
+    if (!(pa->bitmap = virBitmapNew(end-start))) {
+        virReportOOMError();
+        virObjectUnref(pa);
+        return NULL;
+    }
+
+    return pa;
+}
+
+int virPortAllocatorAcquire(virPortAllocatorPtr pa,
+                            unsigned short *port)
+{
+    int ret = -1;
+    unsigned short i;
+    int fd = -1;
+
+    *port = 0;
+    virObjectLock(pa);
+
+    for (i = pa->start ; i < pa->end && !*port; i++) {
+        int reuse = 1;
+        struct sockaddr_in addr;
+        bool used = false;
+
+        if (virBitmapGetBit(pa->bitmap,
+                            i - pa->start, &used) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("Failed to query port %d"), i);
+            goto cleanup;
+        }
+
+        if (used)
+            continue;
+
+        addr.sin_family = AF_INET;
+        addr.sin_port = htons(i);
+        addr.sin_addr.s_addr = htonl(INADDR_ANY);
+        fd = socket(PF_INET, SOCK_STREAM, 0);
+        if (fd < 0) {
+            virReportSystemError(errno, "%s",
+                                 _("Unable to open test socket"));
+            goto cleanup;
+        }
+
+        if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void*)&reuse, sizeof(reuse)) < 0) {
+            virReportSystemError(errno, "%s",
+                                 _("Unable to set socket reuse addr flag"));
+            goto cleanup;
+        }
+
+        if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
+            if (errno != EADDRINUSE) {
+                virReportSystemError(errno,
+                                     _("Unable to bind to port %d"), i);
+                goto cleanup;
+            }
+            /* In use, try next */
+            VIR_FORCE_CLOSE(fd);
+        } else {
+            /* Add port to bitmap of reserved ports */
+            if (virBitmapSetBit(pa->bitmap,
+                                i - pa->start) < 0) {
+                virReportError(VIR_ERR_INTERNAL_ERROR,
+                               _("Failed to reserve port %d"), i);
+                goto cleanup;
+            }
+            *port = i;
+        }
+    }
+
+    ret = 0;
+cleanup:
+    virObjectUnlock(pa);
+    VIR_FORCE_CLOSE(fd);
+    return ret;
+}
+
+int virPortAllocatorRelease(virPortAllocatorPtr pa,
+                            unsigned short port)
+{
+    int ret = -1;
+    virObjectLock(pa);
+
+    if (port < pa->start ||
+        port >= pa->end) {
+        virReportInvalidArg(port, "port %d must be in range (%d, %d)",
+                            port, pa->start, pa->end);
+        goto cleanup;
+    }
+
+    if (virBitmapClearBit(pa->bitmap,
+                          port - pa->start) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Failed to release port %d"),
+                       port);
+        goto cleanup;
+    }
+
+    ret = 0;
+cleanup:
+    virObjectUnlock(pa);
+    return ret;
+}
diff --git a/src/util/virportallocator.h b/src/util/virportallocator.h
new file mode 100644 (file)
index 0000000..a5e68f7
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * virportallocator.h: Allocate & track TCP port allocations
+ *
+ * Copyright (C) 2013 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
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __VIR_PORT_ALLOCATOR_H__
+# define __VIR_PORT_ALLOCATOR_H__
+
+# include "internal.h"
+# include "virobject.h"
+
+typedef struct _virPortAllocator virPortAllocator;
+typedef virPortAllocator *virPortAllocatorPtr;
+
+virPortAllocatorPtr virPortAllocatorNew(unsigned short start,
+                                        unsigned short end);
+
+int virPortAllocatorAcquire(virPortAllocatorPtr pa,
+                            unsigned short *port);
+
+int virPortAllocatorRelease(virPortAllocatorPtr pa,
+                            unsigned short port);
+
+#endif /* __VIR_PORT_ALLOCATOR_H__ */
index 61b0a0c2ff2947f9bc2178cd4a57a0210afee6f7..9da0b4a23fa94e27ea84b407cf79ad9ed71fc6ee 100644 (file)
@@ -98,6 +98,7 @@ test_programs = virshtest sockettest \
        virbitmaptest \
        virlockspacetest \
        virstringtest \
+        virportallocatortest \
        sysinfotest \
        $(NULL)
 
@@ -235,7 +236,9 @@ endif
 
 EXTRA_DIST += $(test_scripts)
 
-test_libraries = libshunload.la
+test_libraries = libshunload.la \
+               libvirportallocatormock.la \
+               $(NULL)
 if WITH_QEMU
 test_libraries += libqemumonitortestutils.la
 endif
@@ -565,6 +568,18 @@ virlockspacetest_SOURCES = \
 virlockspacetest_CFLAGS = -Dabs_builddir="\"$(abs_builddir)\"" $(AM_CFLAGS)
 virlockspacetest_LDADD = $(LDADDS)
 
+virportallocatortest_SOURCES = \
+       virportallocatortest.c testutils.h testutils.c
+virportallocatortest_CFLAGS = -Dabs_builddir="\"$(abs_builddir)\"" $(AM_CFLAGS)
+virportallocatortest_LDADD = $(LDADDS)
+
+libvirportallocatormock_la_SOURCES = \
+       virportallocatortest.c
+libvirportallocatormock_la_CFLAGS = $(AM_CFLAGS) -DMOCK_HELPER=1
+libvirportallocatormock_la_LDFLAGS = -module -avoid-version \
+        -rpath /evil/libtool/hack/to/force/shared/lib/creation
+
+
 viruritest_SOURCES = \
        viruritest.c testutils.h testutils.c
 viruritest_CFLAGS = -Dabs_builddir="\"$(abs_builddir)\"" $(AM_CFLAGS)
diff --git a/tests/virportallocatortest.c b/tests/virportallocatortest.c
new file mode 100644 (file)
index 0000000..93577d7
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2013 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
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Author: Daniel P. Berrange <berrange@redhat.com>
+ */
+
+#include <config.h>
+
+#ifdef MOCK_HELPER
+# include "internal.h"
+# include <sys/socket.h>
+# include <errno.h>
+# include <arpa/inet.h>
+
+int bind(int sockfd ATTRIBUTE_UNUSED,
+         const struct sockaddr *addr,
+         socklen_t addrlen ATTRIBUTE_UNUSED)
+{
+    struct sockaddr_in *saddr = (struct sockaddr_in *)addr;
+
+    if (saddr->sin_port == htons(5900) ||
+        saddr->sin_port == htons(5904) ||
+        saddr->sin_port == htons(5905) ||
+        saddr->sin_port == htons(5906)) {
+        errno = EADDRINUSE;
+        return -1;
+    }
+
+    return 0;
+}
+
+#else
+# include <stdlib.h>
+
+# include "testutils.h"
+# include "virutil.h"
+# include "virerror.h"
+# include "viralloc.h"
+# include "virlog.h"
+
+# include "virportallocator.h"
+
+# define VIR_FROM_THIS VIR_FROM_RPC
+
+
+static int testAllocAll(const void *args ATTRIBUTE_UNUSED)
+{
+    virPortAllocatorPtr alloc = virPortAllocatorNew(5900, 5910);
+    int ret = -1;
+    unsigned short p1, p2, p3, p4, p5, p6, p7;
+
+    if (virPortAllocatorAcquire(alloc, &p1) < 0)
+        goto cleanup;
+    if (p1 != 5901) {
+        if (virTestGetDebug())
+            fprintf(stderr, "Expected 5901, got %d", p1);
+        goto cleanup;
+    }
+
+    if (virPortAllocatorAcquire(alloc, &p2) < 0)
+        goto cleanup;
+    if (p2 != 5902) {
+        if (virTestGetDebug())
+            fprintf(stderr, "Expected 5902, got %d", p2);
+        goto cleanup;
+    }
+
+    if (virPortAllocatorAcquire(alloc, &p3) < 0)
+        goto cleanup;
+    if (p3 != 5903) {
+        if (virTestGetDebug())
+            fprintf(stderr, "Expected 5903, got %d", p3);
+        goto cleanup;
+    }
+
+    if (virPortAllocatorAcquire(alloc, &p4) < 0)
+        goto cleanup;
+    if (p4 != 5907) {
+        if (virTestGetDebug())
+            fprintf(stderr, "Expected 5907, got %d", p4);
+        goto cleanup;
+    }
+
+    if (virPortAllocatorAcquire(alloc, &p5) < 0)
+        goto cleanup;
+    if (p5 != 5908) {
+        if (virTestGetDebug())
+            fprintf(stderr, "Expected 5908, got %d", p5);
+        goto cleanup;
+    }
+
+    if (virPortAllocatorAcquire(alloc, &p6) < 0)
+        goto cleanup;
+    if (p6 != 5909) {
+        if (virTestGetDebug())
+            fprintf(stderr, "Expected 5909, got %d", p6);
+        goto cleanup;
+    }
+
+    if (virPortAllocatorAcquire(alloc, &p7) < 0)
+        goto cleanup;
+    if (p7 != 0) {
+        if (virTestGetDebug())
+            fprintf(stderr, "Expected 0, got %d", p7);
+        goto cleanup;
+    }
+
+    ret = 0;
+cleanup:
+    virObjectUnref(alloc);
+    return ret;
+}
+
+
+
+static int testAllocReuse(const void *args ATTRIBUTE_UNUSED)
+{
+    virPortAllocatorPtr alloc = virPortAllocatorNew(5900, 5910);
+    int ret = -1;
+    unsigned short p1, p2, p3, p4;
+
+    if (virPortAllocatorAcquire(alloc, &p1) < 0)
+        goto cleanup;
+    if (p1 != 5901) {
+        if (virTestGetDebug())
+            fprintf(stderr, "Expected 5901, got %d", p1);
+        goto cleanup;
+    }
+
+    if (virPortAllocatorAcquire(alloc, &p2) < 0)
+        goto cleanup;
+    if (p2 != 5902) {
+        if (virTestGetDebug())
+            fprintf(stderr, "Expected 5902, got %d", p2);
+        goto cleanup;
+    }
+
+    if (virPortAllocatorAcquire(alloc, &p3) < 0)
+        goto cleanup;
+    if (p3 != 5903) {
+        if (virTestGetDebug())
+            fprintf(stderr, "Expected 5903, got %d", p3);
+        goto cleanup;
+    }
+
+
+    if (virPortAllocatorRelease(alloc, p2) < 0)
+        goto cleanup;
+
+    if (virPortAllocatorAcquire(alloc, &p4) < 0)
+        goto cleanup;
+    if (p4 != 5902) {
+        if (virTestGetDebug())
+            fprintf(stderr, "Expected 5902, got %d", p4);
+        goto cleanup;
+    }
+
+    ret = 0;
+cleanup:
+    virObjectUnref(alloc);
+    return ret;
+}
+
+
+static int
+mymain(void)
+{
+    int ret = 0;
+
+    if (virtTestRun("Test alloc all", 1, testAllocAll, NULL) < 0)
+        ret = -1;
+
+    if (virtTestRun("Test alloc reuse", 1, testAllocReuse, NULL) < 0)
+        ret = -1;
+
+    return ret==0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+VIRT_TEST_MAIN_PRELOAD(mymain, abs_builddir "/.libs/libvirportallocatormock.so")
+#endif