From: "Daniel P. Berrange" <berrange(a)redhat.com>
Introduce a virPortAllocator for managing TCP port allocations.
---
.gitignore | 1 +
src/Makefile.am | 1 +
src/libvirt_private.syms | 6 ++
src/util/virportallocator.c | 188 +++++++++++++++++++++++++++++++++++++++++
src/util/virportallocator.h | 40 +++++++++
tests/Makefile.am | 17 +++-
tests/virportallocatortest.c | 195 +++++++++++++++++++++++++++++++++++++++++++
7 files changed, 447 insertions(+), 1 deletion(-)
create mode 100644 src/util/virportallocator.c
create mode 100644 src/util/virportallocator.h
create mode 100644 tests/virportallocatortest.c
diff --git a/.gitignore b/.gitignore
index 882ae4c..e5d09fa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -175,6 +175,7 @@
/tests/virkeyfiletest
/tests/virlockspacetest
/tests/virnet*test
+/tests/virportallocatortest
/tests/virshtest
/tests/virstringtest
/tests/virtimetest
diff --git a/src/Makefile.am b/src/Makefile.am
index da571c7..df58933 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -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 \
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index cbdbb32..a4e818b 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1796,6 +1796,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
index 0000000..2c27711
--- /dev/null
+++ b/src/util/virportallocator.c
@@ -0,0 +1,188 @@
+/*
+ * 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 int start;
+ unsigned int 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))) {
+ 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 && fd == -1; 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;
+ }
+ }
+ }
+
+ 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
index 0000000..a5e68f7
--- /dev/null
+++ b/src/util/virportallocator.h
@@ -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__ */
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 9c7c6fb..d2f27fa 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -97,6 +97,7 @@ test_programs = virshtest sockettest \
virbitmaptest \
virlockspacetest \
virstringtest \
+ virportallocatortest \
sysinfotest \
$(NULL)
@@ -231,7 +232,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
@@ -561,6 +564,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
index 0000000..e448be6
--- /dev/null
+++ b/tests/virportallocatortest.c
@@ -0,0 +1,195 @@
+/*
+ * 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(a)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 <signal.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
--
1.8.0.1