Merge branch 'mem-pool-range'
authorTobias Brunner <tobias@strongswan.org>
Thu, 30 Oct 2014 14:04:31 +0000 (15:04 +0100)
committerTobias Brunner <tobias@strongswan.org>
Thu, 30 Oct 2014 14:08:05 +0000 (15:08 +0100)
Adds support to configure address pools as ranges (from-to) in
ipsec.conf and swanctl.conf.

The first and last addresses in subnet based pools are now skipped
properly and the pools' sizes are adjusted accordingly.  Which is also
the case if pools are configured with an offset, e.g. 192.168.0.100/24,
which reduces the number of available addresses from 254 to 155, and
assignment now starts at .100 not .101, i.e. .100-.254 are assignable
to clients.

References #744.

16 files changed:
configure.ac
man/ipsec.conf.5.in
src/libcharon/config/ike_cfg.c
src/libcharon/plugins/stroke/stroke_config.c
src/libcharon/plugins/vici/vici_attribute.c
src/libhydra/Makefile.am
src/libhydra/attributes/mem_pool.c
src/libhydra/tests/.gitignore [new file with mode: 0644]
src/libhydra/tests/Makefile.am [new file with mode: 0644]
src/libhydra/tests/hydra_tests.c [new file with mode: 0644]
src/libhydra/tests/hydra_tests.h [new file with mode: 0644]
src/libhydra/tests/suites/test_mem_pool.c [new file with mode: 0644]
src/libstrongswan/networking/host.c
src/libstrongswan/networking/host.h
src/libstrongswan/tests/suites/test_host.c
src/swanctl/swanctl.opt

index 7a3c328..7e561ed 100644 (file)
@@ -1650,6 +1650,7 @@ AC_CONFIG_FILES([
        src/libhydra/plugins/kernel_pfkey/Makefile
        src/libhydra/plugins/kernel_pfroute/Makefile
        src/libhydra/plugins/resolve/Makefile
+       src/libhydra/tests/Makefile
        src/libipsec/Makefile
        src/libsimaka/Makefile
        src/libtls/Makefile
index 32d0b9a..1c5ac00 100644 (file)
@@ -853,13 +853,15 @@ an address of the given address family will be requested explicitly.
 If an IP address is configured, it will be requested from the responder,
 which is free to respond with a different address.
 .TP
-.BR rightsourceip " = %config | <network>/<netmask> | %poolname"
+.BR rightsourceip " = %config | <network>/<netmask> | <from>-<to> | %poolname"
 Comma separated list of internal source IPs to use in a tunnel for the remote
 peer. If the value is
 .B %config
 on the responder side, the initiator must propose an address which is then
 echoed back. Also supported are address pools expressed as
 \fInetwork\fB/\fInetmask\fR
+and
+\fIfrom\fB-\fIto\fR
 or the use of an external IP address pool using %\fIpoolname\fR,
 where \fIpoolname\fR is the name of the IP address pool used for the lookup.
 .TP
index 42a3e90..9464ceb 100644 (file)
@@ -459,25 +459,10 @@ static traffic_selector_t* make_range(char *str)
 {
        traffic_selector_t *ts;
        ts_type_t type;
-       char *pos;
        host_t *from, *to;
 
-       pos = strchr(str, '-');
-       if (!pos)
-       {
-               return NULL;
-       }
-       to = host_create_from_string(pos + 1, 0);
-       if (!to)
-       {
-               return NULL;
-       }
-       str = strndup(str, pos - str);
-       from = host_create_from_string_and_family(str, to->get_family(to), 0);
-       free(str);
-       if (!from)
+       if (!host_create_from_range(str, &from, &to))
        {
-               to->destroy(to);
                return NULL;
        }
        if (to->get_family(to) == AF_INET)
index 62967b0..3e40a78 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012-2014 Tobias Brunner
  * Copyright (C) 2008 Martin Willi
  * Hochschule fuer Technik Rapperswil
  *
@@ -667,6 +667,24 @@ static auth_cfg_t *build_auth_cfg(private_stroke_config_t *this,
 }
 
 /**
+ * build a mem_pool_t from an address range
+ */
+static mem_pool_t *create_pool_range(char *str)
+{
+       mem_pool_t *pool;
+       host_t *from, *to;
+
+       if (!host_create_from_range(str, &from, &to))
+       {
+               return NULL;
+       }
+       pool = mem_pool_create_range(str, from, to);
+       from->destroy(from);
+       to->destroy(to);
+       return pool;
+}
+
+/**
  * build a peer_cfg from a stroke msg
  */
 static peer_cfg_t *build_peer_cfg(private_stroke_config_t *this,
@@ -789,17 +807,25 @@ static peer_cfg_t *build_peer_cfg(private_stroke_config_t *this,
                        }
                        else
                        {
-                               /* in-memory pool, named using CIDR notation */
+                               /* in-memory pool, using range or CIDR notation */
+                               mem_pool_t *pool;
                                host_t *base;
                                int bits;
 
-                               base = host_create_from_subnet(token, &bits);
-                               if (base)
+                               pool = create_pool_range(token);
+                               if (!pool)
+                               {
+                                       base = host_create_from_subnet(token, &bits);
+                                       if (base)
+                                       {
+                                               pool = mem_pool_create(token, base, bits);
+                                               base->destroy(base);
+                                       }
+                               }
+                               if (pool)
                                {
-                                       this->attributes->add_pool(this->attributes,
-                                                                               mem_pool_create(token, base, bits));
+                                       this->attributes->add_pool(this->attributes, pool);
                                        peer_cfg->add_pool(peer_cfg, token);
-                                       base->destroy(base);
                                }
                                else
                                {
index 2178116..c0ac573 100644 (file)
@@ -1,4 +1,7 @@
 /*
+ * Copyright (C) 2014 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
  * Copyright (C) 2014 Martin Willi
  * Copyright (C) 2014 revosec AG
  *
@@ -355,6 +358,24 @@ static vici_message_t* create_reply(char *fmt, ...)
 }
 
 /**
+ * Parse a range definition of an address pool
+ */
+static mem_pool_t *create_pool_range(char *name, char *buf)
+{
+       mem_pool_t *pool;
+       host_t *from, *to;
+
+       if (!host_create_from_range(buf, &from, &to))
+       {
+               return NULL;
+       }
+       pool = mem_pool_create_range(name, from, to);
+       from->destroy(from);
+       to->destroy(to);
+       return pool;
+}
+
+/**
  * Parse callback data, passed to each callback
  */
 typedef struct {
@@ -490,7 +511,8 @@ CALLBACK(pool_kv, bool,
        if (streq(name, "addrs"))
        {
                char buf[128];
-               host_t *base;
+               mem_pool_t *pool;
+               host_t *base = NULL;
                int bits;
 
                if (data->pool->vips)
@@ -503,14 +525,22 @@ CALLBACK(pool_kv, bool,
                        data->request->reply = create_reply("invalid addrs value");
                        return FALSE;
                }
-               base = host_create_from_subnet(buf, &bits);
-               if (!base)
+               pool = create_pool_range(data->name, buf);
+               if (!pool)
+               {
+                       base = host_create_from_subnet(buf, &bits);
+                       if (base)
+                       {
+                               pool = mem_pool_create(data->name, base, bits);
+                               base->destroy(base);
+                       }
+               }
+               if (!pool)
                {
                        data->request->reply = create_reply("invalid addrs value: %s", buf);
                        return FALSE;
                }
-               data->pool->vips = mem_pool_create(data->name, base, bits);
-               base->destroy(base);
+               data->pool->vips = pool;
                return TRUE;
        }
        data->request->reply = create_reply("invalid attribute: %s", name);
index 510f2a1..7334cdc 100644 (file)
@@ -78,3 +78,8 @@ if MONOLITHIC
   libhydra_la_LIBADD += plugins/resolve/libstrongswan-resolve.la
 endif
 endif
+
+if MONOLITHIC
+  SUBDIRS += .
+endif
+SUBDIRS += tests
index cc45e56..225abe3 100644 (file)
@@ -47,6 +47,11 @@ struct private_mem_pool_t {
        host_t *base;
 
        /**
+        * whether base is the network id of the subnet on which the pool is based
+        */
+       bool base_is_network_id;
+
+       /**
         * size of the pool
         */
        u_int size;
@@ -306,7 +311,7 @@ static int get_new(private_mem_pool_t *this, identification_t *id)
                        this->leases->put(this->leases, entry->id, entry);
                }
                /* assigning offset, starting by 1 */
-               offset = ++this->unused;
+               offset = ++this->unused + (this->base_is_network_id ? 1 : 0);
                array_insert(entry->online, ARRAY_TAIL, &offset);
                DBG1(DBG_CFG, "assigning new lease to '%Y'", id);
        }
@@ -580,18 +585,39 @@ static private_mem_pool_t *create_generic(char *name)
 }
 
 /**
+ * Check if the given host is the network ID of a subnet, that is, if hostbits
+ * are zero.  Since we limit pools to 2^31 addresses we only have to check the
+ * last 4 bytes.
+ */
+static u_int network_id_diff(host_t *host, int hostbits)
+{
+       u_int32_t last;
+       chunk_t addr;
+
+       if (!hostbits)
+       {
+               return 0;
+       }
+       addr = host->get_address(host);
+       last = untoh32(addr.ptr + addr.len - sizeof(last));
+       hostbits = sizeof(last) * 8 - hostbits;
+       return (last << hostbits) >> hostbits;
+}
+
+/**
  * Described in header
  */
 mem_pool_t *mem_pool_create(char *name, host_t *base, int bits)
 {
        private_mem_pool_t *this;
+       u_int diff;
        int addr_bits;
 
        this = create_generic(name);
        if (base)
        {
                addr_bits = base->get_family(base) == AF_INET ? 32 : 128;
-               bits = max(0, min(bits, base->get_family(base) == AF_INET ? 32 : 128));
+               bits = max(0, min(bits, addr_bits));
                /* net bits -> host bits */
                bits = addr_bits - bits;
                if (bits > POOL_LIMIT)
@@ -601,15 +627,31 @@ mem_pool_t *mem_pool_create(char *name, host_t *base, int bits)
                                 base, addr_bits - bits);
                }
                this->size = 1 << bits;
+               this->base = base->clone(base);
 
                if (this->size > 2)
-               {       /* do not use first and last addresses of a block */
-                       this->unused++;
-                       this->size -= 2;
+               {
+                       /* if base is the network id we later skip the first address,
+                        * otherwise adjust the size to represent the actual number
+                        * of assignable addresses */
+                       diff = network_id_diff(base, bits);
+                       if (!diff)
+                       {
+                               this->base_is_network_id = TRUE;
+                               this->size--;
+                       }
+                       else
+                       {
+                               this->size -= diff;
+                       }
+                       /* skip the last address (broadcast) of the subnet */
+                       this->size--;
+               }
+               else if (network_id_diff(base, bits))
+               {       /* only serve the second address of the subnet */
+                       this->size--;
                }
-               this->base = base->clone(base);
        }
-
        return &this->public;
 }
 
diff --git a/src/libhydra/tests/.gitignore b/src/libhydra/tests/.gitignore
new file mode 100644 (file)
index 0000000..6870ba5
--- /dev/null
@@ -0,0 +1 @@
+hydra_tests
diff --git a/src/libhydra/tests/Makefile.am b/src/libhydra/tests/Makefile.am
new file mode 100644 (file)
index 0000000..c2377c1
--- /dev/null
@@ -0,0 +1,19 @@
+TESTS = hydra_tests
+
+check_PROGRAMS = $(TESTS)
+
+hydra_tests_SOURCES = \
+  suites/test_mem_pool.c \
+  hydra_tests.h hydra_tests.c
+
+hydra_tests_CFLAGS = \
+  -I$(top_srcdir)/src/libhydra \
+  -I$(top_srcdir)/src/libstrongswan \
+  -I$(top_srcdir)/src/libstrongswan/tests \
+  @COVERAGE_CFLAGS@
+
+hydra_tests_LDFLAGS = @COVERAGE_LDFLAGS@
+hydra_tests_LDADD = \
+  $(top_builddir)/src/libhydra/libhydra.la \
+  $(top_builddir)/src/libstrongswan/libstrongswan.la \
+  $(top_builddir)/src/libstrongswan/tests/libtest.la
diff --git a/src/libhydra/tests/hydra_tests.c b/src/libhydra/tests/hydra_tests.c
new file mode 100644 (file)
index 0000000..90abd83
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2014 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program 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 General Public License
+ * for more details.
+ */
+
+#include <test_runner.h>
+#include <hydra.h>
+
+/* declare test suite constructors */
+#define TEST_SUITE(x) test_suite_t* x();
+#define TEST_SUITE_DEPEND(x, ...) TEST_SUITE(x)
+#include "hydra_tests.h"
+#undef TEST_SUITE
+#undef TEST_SUITE_DEPEND
+
+static test_configuration_t tests[] = {
+#define TEST_SUITE(x) \
+       { .suite = x, },
+#define TEST_SUITE_DEPEND(x, type, args) \
+       { .suite = x, .feature = PLUGIN_DEPENDS(type, args) },
+#include "hydra_tests.h"
+       { .suite = NULL, }
+};
+
+static bool test_runner_init(bool init)
+{
+       if (init)
+       {
+               libhydra_init();
+       }
+       else
+       {
+               lib->processor->set_threads(lib->processor, 0);
+               lib->processor->cancel(lib->processor);
+               libhydra_deinit();
+       }
+       return TRUE;
+}
+
+int main(int argc, char *argv[])
+{
+       return test_runner_run("libhydra", tests, test_runner_init);
+}
diff --git a/src/libhydra/tests/hydra_tests.h b/src/libhydra/tests/hydra_tests.h
new file mode 100644 (file)
index 0000000..a73b78d
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) 2014 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program 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 General Public License
+ * for more details.
+ */
+
+TEST_SUITE(mem_pool_suite_create)
diff --git a/src/libhydra/tests/suites/test_mem_pool.c b/src/libhydra/tests/suites/test_mem_pool.c
new file mode 100644 (file)
index 0000000..c47a92f
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2014 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program 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 General Public License
+ * for more details.
+ */
+
+#include "test_suite.h"
+
+#include <attributes/mem_pool.h>
+
+static void assert_host(char *expected, host_t *host)
+{
+       if (!expected)
+       {
+               ck_assert_msg(!host, "not epxecting IP != %+H", host);
+       }
+       else
+       {
+               host_t *verifier;
+               verifier = host_create_from_string(expected, 0);
+               ck_assert_msg(host, "expected IP %+H != NULL", verifier);
+               ck_assert_msg(verifier->ip_equals(verifier, host), "expected IP %+H != "
+                                         "%+H", verifier, host);;
+               verifier->destroy(verifier);
+       }
+}
+
+static void assert_acquire(mem_pool_t *pool, char *requested, char *expected,
+                                                  mem_pool_op_t operation)
+{
+       identification_t *id;
+       host_t *req, *acquired;
+
+       id = identification_create_from_string("tester");
+       req = host_create_from_string(requested, 0);
+
+       acquired = pool->acquire_address(pool, id, req, operation);
+       assert_host(expected, acquired);
+       DESTROY_IF(acquired);
+
+       req->destroy(req);
+       id->destroy(id);
+}
+
+static void assert_acquires_new(mem_pool_t *pool, char *pattern, int first)
+{
+       char expected[16];
+       int i;
+
+       for (i = 0; i < pool->get_size(pool); i++)
+       {
+               snprintf(expected, sizeof(expected), pattern, first + i);
+               assert_acquire(pool, "0.0.0.0", expected, MEM_POOL_NEW);
+               ck_assert_int_eq(i + 1, pool->get_online(pool));
+       }
+       assert_acquire(pool, "0.0.0.0", NULL, MEM_POOL_NEW);
+}
+
+START_TEST(test_config)
+{
+       mem_pool_t *pool;
+
+       pool = mem_pool_create("test", NULL, 0);
+       ck_assert_int_eq(0, pool->get_size(pool));
+       assert_acquire(pool, "192.168.0.1", "192.168.0.1", MEM_POOL_NEW);
+       assert_acquire(pool, "10.0.1.1", "10.0.1.1", MEM_POOL_NEW);
+       assert_acquire(pool, "0.0.0.0", "0.0.0.0", MEM_POOL_NEW);
+       assert_acquire(pool, "255.255.255.255", "255.255.255.255", MEM_POOL_NEW);
+       ck_assert_int_eq(0, pool->get_online(pool));
+       pool->destroy(pool);
+}
+END_TEST
+
+START_TEST(test_cidr)
+{
+       mem_pool_t *pool;
+       host_t *base;
+
+       base = host_create_from_string("192.168.0.0", 0);
+
+       pool = mem_pool_create("test", base, 32);
+       ck_assert_int_eq(1, pool->get_size(pool));
+       assert_acquires_new(pool, "192.168.0.%d", 0);
+       pool->destroy(pool);
+
+       pool = mem_pool_create("test", base, 31);
+       ck_assert_int_eq(2, pool->get_size(pool));
+       assert_acquires_new(pool, "192.168.0.%d", 0);
+       pool->destroy(pool);
+
+       pool = mem_pool_create("test", base, 30);
+       ck_assert_int_eq(2, pool->get_size(pool));
+       assert_acquires_new(pool, "192.168.0.%d", 1);
+       pool->destroy(pool);
+
+       pool = mem_pool_create("test", base, 29);
+       ck_assert_int_eq(6, pool->get_size(pool));
+       assert_acquires_new(pool, "192.168.0.%d", 1);
+       pool->destroy(pool);
+
+       pool = mem_pool_create("test", base, 24);
+       ck_assert_int_eq(254, pool->get_size(pool));
+       assert_acquires_new(pool, "192.168.0.%d", 1);
+       pool->destroy(pool);
+
+       base->destroy(base);
+}
+END_TEST
+
+START_TEST(test_cidr_offset)
+{
+       mem_pool_t *pool;
+       host_t *base;
+
+       base = host_create_from_string("192.168.0.1", 0);
+       pool = mem_pool_create("test", base, 31);
+       ck_assert_int_eq(1, pool->get_size(pool));
+       assert_acquires_new(pool, "192.168.0.%d", 1);
+       pool->destroy(pool);
+
+       pool = mem_pool_create("test", base, 30);
+       ck_assert_int_eq(2, pool->get_size(pool));
+       assert_acquires_new(pool, "192.168.0.%d", 1);
+       pool->destroy(pool);
+       base->destroy(base);
+
+       base = host_create_from_string("192.168.0.2", 0);
+       pool = mem_pool_create("test", base, 30);
+       ck_assert_int_eq(1, pool->get_size(pool));
+       assert_acquires_new(pool, "192.168.0.%d", 2);
+       pool->destroy(pool);
+
+       pool = mem_pool_create("test", base, 24);
+       ck_assert_int_eq(253, pool->get_size(pool));
+       assert_acquires_new(pool, "192.168.0.%d", 2);
+       pool->destroy(pool);
+       base->destroy(base);
+
+       base = host_create_from_string("192.168.0.254", 0);
+       pool = mem_pool_create("test", base, 24);
+       ck_assert_int_eq(1, pool->get_size(pool));
+       assert_acquires_new(pool, "192.168.0.%d", 254);
+       pool->destroy(pool);
+       base->destroy(base);
+
+       /* due to size == 0 we get the requested IP back */
+       base = host_create_from_string("192.168.0.255", 0);
+       pool = mem_pool_create("test", base, 24);
+       ck_assert_int_eq(0, pool->get_size(pool));
+       assert_acquire(pool, "192.168.0.1", "192.168.0.1", MEM_POOL_NEW);
+       pool->destroy(pool);
+
+       base->destroy(base);
+}
+END_TEST
+
+START_TEST(test_range)
+{
+       mem_pool_t *pool;
+       host_t *from, *to;
+
+       from = host_create_from_string("192.168.0.0", 0);
+       to = host_create_from_string("192.168.0.0", 0);
+       pool = mem_pool_create_range("test", from, to);
+       ck_assert_int_eq(1, pool->get_size(pool));
+       assert_acquires_new(pool, "192.168.0.%d", 0);
+       pool->destroy(pool);
+
+       to->destroy(to);
+       to = host_create_from_string("192.168.0.1", 0);
+       pool = mem_pool_create_range("test", from, to);
+       ck_assert_int_eq(2, pool->get_size(pool));
+       assert_acquires_new(pool, "192.168.0.%d", 0);
+       pool->destroy(pool);
+
+       from->destroy(from);
+       from = host_create_from_string("192.168.0.10", 0);
+       pool = mem_pool_create_range("test", from, to);
+       ck_assert(!pool);
+
+       to->destroy(to);
+       to = host_create_from_string("192.168.0.20", 0);
+       pool = mem_pool_create_range("test", from, to);
+       ck_assert_int_eq(11, pool->get_size(pool));
+       assert_acquires_new(pool, "192.168.0.%d", 10);
+       pool->destroy(pool);
+
+       from->destroy(from);
+       from = host_create_from_string("fec::1", 0);
+       to->destroy(to);
+       to = host_create_from_string("fed::1", 0);
+       pool = mem_pool_create_range("test", from, to);
+       ck_assert(!pool);
+
+       from->destroy(from);
+       to->destroy(to);
+}
+END_TEST
+
+Suite *mem_pool_suite_create()
+{
+       Suite *s;
+       TCase *tc;
+
+       s = suite_create("mem_pool");
+
+       tc = tcase_create("%config-like pool");
+       tcase_add_test(tc, test_config);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("cidr constructor");
+       tcase_add_test(tc, test_cidr);
+       tcase_add_test(tc, test_cidr_offset);
+       suite_add_tcase(s, tc);
+
+       tc = tcase_create("range constructor");
+       tcase_add_test(tc, test_range);
+       suite_add_tcase(s, tc);
+
+       return s;
+}
index 8d04a4e..07da3ef 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2012 Tobias Brunner
+ * Copyright (C) 2006-2014 Tobias Brunner
  * Copyright (C) 2006 Daniel Roethlisberger
  * Copyright (C) 2005-2006 Martin Willi
  * Copyright (C) 2005 Jan Hutter
@@ -528,6 +528,42 @@ host_t *host_create_from_chunk(int family, chunk_t address, u_int16_t port)
 /*
  * Described in header.
  */
+bool host_create_from_range(char *string, host_t **from, host_t **to)
+{
+       char *sep, *pos;
+
+       sep = strchr(string, '-');
+       if (!sep)
+       {
+               return FALSE;
+       }
+       for (pos = sep+1; *pos && *pos == ' '; pos++)
+       {
+               /* trim spaces before to address*/
+       }
+       *to = host_create_from_string(pos, 0);
+       if (!*to)
+       {
+               return FALSE;
+       }
+       for (pos = sep-1; pos > string && *pos == ' '; pos--)
+       {
+               /* trim spaces behind from address */
+       }
+       pos = strndup(string, pos - string + 1);
+       *from = host_create_from_string_and_family(pos, (*to)->get_family(*to), 0);
+       free(pos);
+       if (!*from)
+       {
+               (*to)->destroy(*to);
+               return FALSE;
+       }
+       return TRUE;
+}
+
+/*
+ * Described in header.
+ */
 host_t *host_create_from_subnet(char *string, int *bits)
 {
        char *pos, buf[64];
index 9c9b503..26f23bc 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2009 Tobias Brunner
+ * Copyright (C) 2006-2014 Tobias Brunner
  * Copyright (C) 2006 Daniel Roethlisberger
  * Copyright (C) 2005-2008 Martin Willi
  * Copyright (C) 2005 Jan Hutter
@@ -181,6 +181,19 @@ host_t *host_create_from_chunk(int family, chunk_t address, u_int16_t port);
 host_t *host_create_from_sockaddr(sockaddr_t *sockaddr);
 
 /**
+ * Parse a range definition (1.2.3.0-1.2.3.5), return the two hosts.
+ *
+ * The two hosts are not ordered, from is simply the first, to is the second,
+ * from is not necessarily smaller.
+ *
+ * @param string               string to parse
+ * @param from                 returns the first address (out)
+ * @param to                   returns the second address (out)
+ * @return                             TRUE if parsed successfully, FALSE otherwise
+ */
+bool host_create_from_range(char *string, host_t **from, host_t **to);
+
+/**
  * Create a host from a CIDR subnet definition (1.2.3.0/24), return bits.
  *
  * @param string               string to parse
index 6344208..ec3c83f 100644 (file)
@@ -400,6 +400,90 @@ START_TEST(test_create_from_subnet_v6)
 END_TEST
 
 /*******************************************************************************
+ * host_create_from_range
+ */
+
+static const chunk_t addr_v4_to = chunk_from_chars(0xc0, 0xa8, 0x00, 0x05);
+static const chunk_t addr_v6_to = chunk_from_chars(0xfe, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                                                                                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05);
+
+static void verify_range(char *str, int family, chunk_t from_addr,
+                                                chunk_t to_addr)
+{
+       host_t *from, *to;
+
+       if (!family)
+       {
+               ck_assert(!host_create_from_range(str, &from, &to));
+       }
+       else
+       {
+               ck_assert(host_create_from_range(str, &from, &to));
+               verify_address(from, from_addr, family, 0);
+               verify_address(to, to_addr, family, 0);
+               from->destroy(from);
+               to->destroy(to);
+       }
+}
+
+START_TEST(test_create_from_range_v4)
+{
+       host_t *from, *to;
+
+       ck_assert(host_create_from_range("0.0.0.0-0.0.0.0", &from, &to));
+       verify_any(from, AF_INET, 0);
+       verify_any(to, AF_INET, 0);
+       from->destroy(from);
+       to->destroy(to);
+
+       verify_range("192.168.0.1-192.168.0.1", AF_INET, addr_v4, addr_v4);
+       verify_range("192.168.0.1-192.168.0.5", AF_INET, addr_v4, addr_v4_to);
+       verify_range("192.168.0.1- 192.168.0.5", AF_INET, addr_v4, addr_v4_to);
+       verify_range("192.168.0.1 -192.168.0.5", AF_INET, addr_v4, addr_v4_to);
+       verify_range("192.168.0.1 - 192.168.0.5", AF_INET, addr_v4, addr_v4_to);
+       verify_range("192.168.0.5-192.168.0.1", AF_INET, addr_v4_to, addr_v4);
+
+       verify_range("192.168.0.1", 0, chunk_empty, chunk_empty);
+       verify_range("192.168.0.1-", 0, chunk_empty, chunk_empty);
+       verify_range("-192.168.0.1", 0, chunk_empty, chunk_empty);
+       verify_range("192.168.0.1-192", 0, chunk_empty, chunk_empty);
+       verify_range("192.168.0.1-192.168", 0, chunk_empty, chunk_empty);
+       verify_range("192.168.0.1-192.168.0", 0, chunk_empty, chunk_empty);
+       verify_range("foo.b.a.r", 0, chunk_empty, chunk_empty);
+       verify_range("foo.b.a.r-b.a.r.f", 0, chunk_empty, chunk_empty);
+}
+END_TEST
+
+START_TEST(test_create_from_range_v6)
+{
+       host_t *from, *to;
+
+       ck_assert(host_create_from_range("::-::", &from, &to));
+       verify_any(from, AF_INET6, 0);
+       verify_any(to, AF_INET6, 0);
+       from->destroy(from);
+       to->destroy(to);
+
+       verify_range("fec1::1-fec1::1", AF_INET6, addr_v6, addr_v6);
+       verify_range("fec1::1-fec1::5", AF_INET6, addr_v6, addr_v6_to);
+       verify_range("fec1::1- fec1::5", AF_INET6, addr_v6, addr_v6_to);
+       verify_range("fec1::1 -fec1::5", AF_INET6, addr_v6, addr_v6_to);
+       verify_range("fec1::1 - fec1::5", AF_INET6, addr_v6, addr_v6_to);
+       verify_range("fec1::5-fec1::1", AF_INET6, addr_v6_to, addr_v6);
+
+       verify_range("fec1::1", 0, chunk_empty, chunk_empty);
+       verify_range("fec1::1-", 0, chunk_empty, chunk_empty);
+       verify_range("-fec1::1", 0, chunk_empty, chunk_empty);
+       verify_range("fec1::1-fec1", 0, chunk_empty, chunk_empty);
+       verify_range("foo::bar", 0, chunk_empty, chunk_empty);
+       verify_range("foo::bar-bar::foo", 0, chunk_empty, chunk_empty);
+
+       verify_range("fec1::1-192.168.0.1", 0, chunk_empty, chunk_empty);
+       verify_range("192.168.0.1-fec1::1", 0, chunk_empty, chunk_empty);
+}
+END_TEST
+
+/*******************************************************************************
  * host_create_netmask
  */
 
@@ -627,6 +711,11 @@ Suite *host_suite_create()
        tcase_add_test(tc, test_create_from_subnet_v6);
        suite_add_tcase(s, tc);
 
+       tc = tcase_create("host_create_from_range");
+       tcase_add_test(tc, test_create_from_range_v4);
+       tcase_add_test(tc, test_create_from_range_v6);
+       suite_add_tcase(s, tc);
+
        tc = tcase_create("host_create_netmask");
        tcase_add_test(tc, test_create_netmask_v4);
        tcase_add_test(tc, test_create_netmask_v6);
index 5783555..5880045 100644 (file)
@@ -782,11 +782,11 @@ pools.<name> { # }
        Section defining a single pool with a unique name.
 
 pools.<name>.addrs =
-       Subnet defining addresses allocated in pool.
+       Addresses allocated in pool.
 
-       Subnet defining addresses allocated in pool. Accepts a single CIDR subnet
-       defining the pool to allocate addresses from. Pools must be unique and
-       non-overlapping.
+       Subnet or range defining addresses allocated in pool. Accepts a single CIDR
+       subnet defining the pool to allocate addresses from, or an address range
+       (<from>-<to>).  Pools must be unique and non-overlapping.
 
 pools.<name>.<attr> =
        Comma separated list of additional attributes from type <attr>.