Attention is currently required from: plaisthos.

Hello plaisthos,

I'd like you to do a code review.
Please visit

    http://gerrit.openvpn.net/c/openvpn/+/1790?usp=email

to review the following change.


Change subject: pool, multi: persist subnet pools via --ifconfig-pool-persist
......................................................................

pool, multi: persist subnet pools via --ifconfig-pool-persist

Extend --ifconfig-pool-persist so its single file covers the global pool
and every --subnet-pool. ifconfig_pool_read()/ifconfig_pool_write() now
operate on the set of pools: each persisted entry is routed to the pool
whose address range owns it, and an entry matching no configured pool is
ignored on load (e.g. a --subnet-pool that was removed). When persist is
enabled the subnet pools are created eagerly at startup so their saved
allocations can be reloaded before the first client connects.

Change-Id: I0bff8d0a549a65b7da01ea0e785b7ec8bea17f05
GitHub: closes openvpn/OpenVPN#987
Signed-off-by: Antonio Quartulli <[email protected]>
---
M CMakeLists.txt
M doc/man-sections/server-options.rst
M src/openvpn/multi.c
M src/openvpn/options.c
M src/openvpn/pool.c
M src/openvpn/pool.h
M tests/unit_tests/openvpn/Makefile.am
A tests/unit_tests/openvpn/test_pool.c
8 files changed, 393 insertions(+), 39 deletions(-)



  git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/90/1790/1

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3463b5a..0259234 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -668,6 +668,7 @@
         "test_options_parse"
         "test_packet_id"
         "test_pkt"
+        "test_pool"
         "test_provider"
         "test_socket"
         "test_ssl"
@@ -835,6 +836,15 @@
         src/openvpn/options_util.c
         )

+    target_sources(test_pool PRIVATE
+        tests/unit_tests/openvpn/mock_get_random.c
+        src/openvpn/pool.c
+        src/openvpn/status.c
+        src/openvpn/otime.c
+        src/openvpn/fdmisc.c
+        src/openvpn/interval.c
+        )
+
     target_sources(test_ncp PRIVATE
         src/openvpn/crypto_epoch.c
         src/openvpn/crypto_mbedtls.c
diff --git a/doc/man-sections/server-options.rst 
b/doc/man-sections/server-options.rst
index 0146594..910a414 100644
--- a/doc/man-sections/server-options.rst
+++ b/doc/man-sections/server-options.rst
@@ -275,6 +275,11 @@
   ``file`` is a comma-delimited ASCII file, formatted as
   :code:`<Common-Name>,<IP-address>`.

+  A single ``file`` covers the global pool and every ``--subnet-pool``: each
+  entry is associated with a pool by its address, and on load an entry whose
+  address falls in no configured pool (for example a ``--subnet-pool`` that is
+  no longer defined) is ignored.
+
   If ``seconds`` = :code:`0`, ``file`` will be treated as read-only. This
   is useful if you would like to treat ``file`` as a configuration file.

diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c
index 1cd84b6..a281764 100644
--- a/src/openvpn/multi.c
+++ b/src/openvpn/multi.c
@@ -65,6 +65,41 @@
     struct ifconfig_pool *pool;
 };

+static struct ifconfig_pool *multi_get_group_pool(struct multi_context *m,
+                                                  const struct options *o, 
const char *tag);
+
+/*
+ * Collect every address pool (the global one plus each --subnet-pool group)
+ * into a gc-allocated array, for the single combined --ifconfig-pool-persist
+ * file.  Returns the number of pools.
+ */
+static int
+multi_all_pools(struct multi_context *m, struct gc_arena *gc, struct 
ifconfig_pool ***pools)
+{
+    struct subnet_pool *sp;
+    int n = m->ifconfig_pool ? 1 : 0;
+
+    for (sp = m->subnet_pools; sp; sp = sp->next)
+    {
+        n++;
+    }
+
+    struct ifconfig_pool **arr = gc_malloc(n * sizeof(struct ifconfig_pool *), 
true, gc);
+    int i = 0;
+
+    if (m->ifconfig_pool)
+    {
+        arr[i++] = m->ifconfig_pool;
+    }
+    for (sp = m->subnet_pools; sp; sp = sp->next)
+    {
+        arr[i++] = sp->pool;
+    }
+
+    *pools = arr;
+    return n;
+}
+
 /*#define MULTI_DEBUG_EVENT_LOOP*/

 #ifdef MULTI_DEBUG_EVENT_LOOP
@@ -161,11 +196,16 @@
 void
 multi_ifconfig_pool_persist(struct multi_context *m, bool force)
 {
-    /* write pool data to file */
-    if (m->ifconfig_pool && m->top.c1.ifconfig_pool_persist
+    /* write the global pool and every --subnet-pool to the one persist file */
+    if (m->top.c1.ifconfig_pool_persist
         && (force || 
ifconfig_pool_write_trigger(m->top.c1.ifconfig_pool_persist)))
     {
-        ifconfig_pool_write(m->top.c1.ifconfig_pool_persist, m->ifconfig_pool);
+        struct gc_arena gc = gc_new();
+        struct ifconfig_pool **pools;
+        int n = multi_all_pools(m, &gc, &pools);
+
+        ifconfig_pool_write(m->top.c1.ifconfig_pool_persist, pools, n);
+        gc_free(&gc);
     }
 }

@@ -368,12 +408,29 @@
             t->options.ifconfig_pool_end, t->options.duplicate_cn,
             t->options.ifconfig_ipv6_pool_defined, 
t->options.ifconfig_ipv6_pool_base,
             t->options.ifconfig_ipv6_pool_netbits);
+    }

-        /* reload pool data from file */
-        if (t->c1.ifconfig_pool_persist)
+    /* With --ifconfig-pool-persist the single file covers the global pool and
+     * every configured --subnet-pool, so eagerly create the subnet pools and
+     * reload them all in one pass (entries for unknown pools are ignored). */
+    if (t->c1.ifconfig_pool_persist)
+    {
+        struct gc_arena gc = gc_new();
+        struct ifconfig_pool **pools;
+        int n;
+
+        for (const struct subnet_pool_def *d = t->options.subnet_pools; d; d = 
d->next)
         {
-            ifconfig_pool_read(t->c1.ifconfig_pool_persist, m->ifconfig_pool);
+            multi_get_group_pool(m, &t->options, d->tag);
         }
+        for (const struct subnet_pool6_def *d = t->options.subnet_pools_ipv6; 
d; d = d->next)
+        {
+            multi_get_group_pool(m, &t->options, d->tag);
+        }
+
+        n = multi_all_pools(m, &gc, &pools);
+        ifconfig_pool_read(t->c1.ifconfig_pool_persist, pools, n);
+        gc_free(&gc);
     }

     /*
@@ -1425,7 +1482,7 @@
  * on first use from the matching --subnet-pool definition.
  */
 static struct ifconfig_pool *
-multi_get_group_pool(struct multi_context *m, const char *tag)
+multi_get_group_pool(struct multi_context *m, const struct options *o, const 
char *tag)
 {
     struct subnet_pool *sp;

@@ -1437,14 +1494,13 @@
         }
     }

-    const struct subnet_pool_def *d4 = 
subnet_pool_by_tag(m->top.options.subnet_pools, tag);
-    const struct subnet_pool6_def *d6 = 
subnet_pool6_by_tag(m->top.options.subnet_pools_ipv6, tag);
+    const struct subnet_pool_def *d4 = subnet_pool_by_tag(o->subnet_pools, 
tag);
+    const struct subnet_pool6_def *d6 = 
subnet_pool6_by_tag(o->subnet_pools_ipv6, tag);
     ALLOC_OBJ_CLEAR(sp, struct subnet_pool);
     sp->tag = d4 ? d4->tag : d6->tag;
     sp->pool = ifconfig_pool_init(d4 != NULL, IFCONFIG_POOL_INDIV, d4 ? 
d4->network + 2 : 0,
-                                  d4 ? (d4->network | ~d4->netmask) - 1 : 0,
-                                  m->top.options.duplicate_cn, d6 != NULL,
-                                  d6 ? add_in6_addr(d6->network, 2) : 
in6addr_any,
+                                  d4 ? (d4->network | ~d4->netmask) - 1 : 0, 
o->duplicate_cn,
+                                  d6 != NULL, d6 ? add_in6_addr(d6->network, 
2) : in6addr_any,
                                   d6 ? (int)d6->netbits : 0);
     sp->next = m->subnet_pools;
     m->subnet_pools = sp;
@@ -1549,7 +1605,8 @@
                 cn = tls_common_name(mi->context.c2.tls_multi, true);
             }

-            mi->vaddr_pool = multi_get_group_pool(m, 
mi->context.options.subnet_pool_tag);
+            mi->vaddr_pool =
+                multi_get_group_pool(m, &m->top.options, 
mi->context.options.subnet_pool_tag);
             mi->vaddr_handle =
                 ifconfig_pool_acquire(mi->vaddr_pool, &local, &remote, 
&remote_ipv6, cn);
             if (mi->vaddr_handle < 0)
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index b4485ca..40a195d 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -441,6 +441,7 @@
     "--ifconfig-pool-persist file [seconds] : Persist/unpersist 
ifconfig-pool\n"
     "                  data to file, at seconds intervals (default=600).\n"
     "                  If seconds=0, file will be treated as read-only.\n"
+    "                  Covers the global pool and every --subnet-pool.\n"
     "--ifconfig-ipv6-pool base-IP/bits : set aside an IPv6 network block\n"
     "                  to be dynamically allocated to connecting clients.\n"
     "--subnet-pool tag network netmask [gateway] : Define a named pool of\n"
@@ -2566,10 +2567,11 @@
         MUST_BE_UNDEF(route_delay_defined, "route-delay");
         MUST_BE_UNDEF(up_delay, "up-delay");
         if (!options->ifconfig_pool_defined && 
!options->ifconfig_ipv6_pool_defined
+            && !options->subnet_pools && !options->subnet_pools_ipv6
             && options->ifconfig_pool_persist_filename)
         {
-            msg(M_USAGE,
-                "--ifconfig-pool-persist must be used with --ifconfig-pool or 
--ifconfig-ipv6-pool");
+            msg(M_USAGE, "--ifconfig-pool-persist must be used with 
--ifconfig-pool, "
+                         "--ifconfig-ipv6-pool or --subnet-pool");
         }
         if (options->ifconfig_ipv6_pool_defined && 
!options->ifconfig_ipv6_local)
         {
diff --git a/src/openvpn/pool.c b/src/openvpn/pool.c
index ff74e7c..b260cc8 100644
--- a/src/openvpn/pool.c
+++ b/src/openvpn/pool.c
@@ -583,13 +583,14 @@
 }

 void
-ifconfig_pool_read(struct ifconfig_pool_persist *persist, struct ifconfig_pool 
*pool)
+ifconfig_pool_read(struct ifconfig_pool_persist *persist, struct ifconfig_pool 
**pools,
+                   int n_pools)
 {
     const int buf_size = 128;

     update_time();

-    if (persist && persist->file && pool)
+    if (persist && persist->file && n_pools > 0)
     {
         struct gc_arena gc = gc_new();
         struct buffer in = alloc_buf_gc(256, &gc);
@@ -634,6 +635,9 @@
                 continue;
             }

+            /* the address selects which pool this entry belongs to; an entry
+             * matching none of the configured pools is ignored */
+            struct ifconfig_pool *pool = NULL;
             ifconfig_pool_handle h = -1, h6 = -1;

             if (strlen(ip_buf) > 0)
@@ -647,10 +651,13 @@
                 }
                 else
                 {
-                    h = ifconfig_pool_ip_base_to_handle(pool, addr);
-                    if (h < 0)
+                    for (int i = 0; i < n_pools && !pool; ++i)
                     {
-                        msg(M_WARN, "pool: IPv4 (%s) out of pool range for 
CN=%s", ip_buf, cn_buf);
+                        h = ifconfig_pool_ip_base_to_handle(pools[i], addr);
+                        if (h >= 0)
+                        {
+                            pool = pools[i];
+                        }
                     }
                 }
             }
@@ -663,12 +670,20 @@
                 {
                     msg(M_WARN, "pool: invalid IPv6 (%s) for CN=%s", ip6_buf, 
cn_buf);
                 }
+                else if (pool)
+                {
+                    /* IPv4 already picked the pool; v4 and v6 share the 
handle */
+                    h6 = ifconfig_pool_ipv6_base_to_handle(pool, &addr6);
+                }
                 else
                 {
-                    h6 = ifconfig_pool_ipv6_base_to_handle(pool, &addr6);
-                    if (h6 < 0)
+                    for (int i = 0; i < n_pools && !pool; ++i)
                     {
-                        msg(M_WARN, "pool: IPv6 (%s) out of pool range for 
CN=%s", ip6_buf, cn_buf);
+                        h6 = ifconfig_pool_ipv6_base_to_handle(pools[i], 
&addr6);
+                        if (h6 >= 0)
+                        {
+                            pool = pools[i];
+                        }
                     }

                     /* Rely on IPv6 if no IPv4 was provided or the one provided
@@ -681,40 +696,44 @@
                 }
             }

-            /* at the moment IPv4 and IPv6 share the same pool, therefore 
offsets
-             * have to match for the same client.
-             *
-             * If offsets differ we use the IPv4, therefore warn the user 
about this.
-             */
+            /* IPv4 and IPv6 share the same pool handle, so their offsets have
+             * to match; if they differ we use the IPv4 and warn */
             if ((h6 >= 0) && (h != h6))
             {
                 msg(M_WARN, "pool: IPv4 (%s) and IPv6 (%s) have different 
offsets! Relying on IPv4",
                     ip_buf, ip6_buf);
             }

-            /* if at least one among v4 and v6 was properly parsed, attempt
-             * setting an handle for this client
-             */
-            if (h >= 0)
+            /* set the handle if the entry belongs to one of the pools */
+            if (pool && h >= 0)
             {
-                msg(M_INFO, "succeeded -> ifconfig_pool_set(hand=%d)", h);
                 ifconfig_pool_set(pool, cn_buf, h, persist->fixed);
             }
         }

-        ifconfig_pool_msg(pool, D_IFCONFIG_POOL);
+        for (int i = 0; i < n_pools; ++i)
+        {
+            ifconfig_pool_msg(pools[i], D_IFCONFIG_POOL);
+        }

         gc_free(&gc);
     }
 }

 void
-ifconfig_pool_write(struct ifconfig_pool_persist *persist, const struct 
ifconfig_pool *pool)
+ifconfig_pool_write(struct ifconfig_pool_persist *persist, struct 
ifconfig_pool *const *pools,
+                    int n_pools)
 {
-    if (persist && persist->file && (status_rw_flags(persist->file) & 
STATUS_OUTPUT_WRITE) && pool)
+    if (persist && persist->file && (status_rw_flags(persist->file) & 
STATUS_OUTPUT_WRITE))
     {
         status_reset(persist->file);
-        ifconfig_pool_list(pool, persist->file);
+        for (int i = 0; i < n_pools; ++i)
+        {
+            if (pools[i])
+            {
+                ifconfig_pool_list(pools[i], persist->file);
+            }
+        }
         status_flush(persist->file);
     }
 }
diff --git a/src/openvpn/pool.h b/src/openvpn/pool.h
index e2d3f82..79f3ac5 100644
--- a/src/openvpn/pool.h
+++ b/src/openvpn/pool.h
@@ -92,9 +92,15 @@

 bool ifconfig_pool_write_trigger(struct ifconfig_pool_persist *persist);

-void ifconfig_pool_read(struct ifconfig_pool_persist *persist, struct 
ifconfig_pool *pool);
+/* Read a persist file into a set of pools, routing each entry to the pool
+ * whose address range owns it.  Entries matching no pool (e.g. a --subnet-pool
+ * that is no longer configured) are ignored. */
+void ifconfig_pool_read(struct ifconfig_pool_persist *persist, struct 
ifconfig_pool **pools,
+                        int n_pools);

-void ifconfig_pool_write(struct ifconfig_pool_persist *persist, const struct 
ifconfig_pool *pool);
+/* Write a set of pools to a single persist file. */
+void ifconfig_pool_write(struct ifconfig_pool_persist *persist,
+                         struct ifconfig_pool *const *pools, int n_pools);

 #ifdef IFCONFIG_POOL_TEST
 void ifconfig_pool_test(in_addr_t start, in_addr_t end);
diff --git a/tests/unit_tests/openvpn/Makefile.am 
b/tests/unit_tests/openvpn/Makefile.am
index bfb7284..d1d9574 100644
--- a/tests/unit_tests/openvpn/Makefile.am
+++ b/tests/unit_tests/openvpn/Makefile.am
@@ -17,6 +17,7 @@
        options_parse_testdriver \
        packet_id_testdriver \
        pkt_testdriver \
+       pool_testdriver \
        provider_testdriver \
        push_update_msg_testdriver \
        socket_testdriver \
@@ -362,6 +363,23 @@
        $(top_srcdir)/src/openvpn/platform.c \
        $(top_srcdir)/src/openvpn/mbuf.c

+pool_testdriver_CFLAGS  = \
+       -I$(top_srcdir)/include -I$(top_srcdir)/src/compat 
-I$(top_srcdir)/src/openvpn \
+       @TEST_CFLAGS@
+
+pool_testdriver_LDFLAGS = @TEST_LDFLAGS@
+
+pool_testdriver_SOURCES = test_pool.c \
+       mock_msg.c test_common.h \
+       mock_get_random.c \
+       $(top_srcdir)/src/openvpn/pool.c \
+       $(top_srcdir)/src/openvpn/status.c \
+       $(top_srcdir)/src/openvpn/buffer.c \
+       $(top_srcdir)/src/openvpn/otime.c \
+       $(top_srcdir)/src/openvpn/platform.c \
+       $(top_srcdir)/src/openvpn/fdmisc.c \
+       $(top_srcdir)/src/openvpn/interval.c
+
 subnet_pool_testdriver_CFLAGS  = \
        -I$(top_srcdir)/include -I$(top_srcdir)/src/compat 
-I$(top_srcdir)/src/openvpn \
        @TEST_CFLAGS@
diff --git a/tests/unit_tests/openvpn/test_pool.c 
b/tests/unit_tests/openvpn/test_pool.c
new file mode 100644
index 0000000..daadb32
--- /dev/null
+++ b/tests/unit_tests/openvpn/test_pool.c
@@ -0,0 +1,237 @@
+/*
+ *  OpenVPN -- An application to securely tunnel IP networks
+ *             over a single UDP port, with support for SSL/TLS-based
+ *             session authentication and key exchange,
+ *             packet encryption, packet authentication, and
+ *             packet compression.
+ *
+ *  Copyright (C) 2026 OpenVPN Inc <[email protected]>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation.
+ *
+ *  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.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "syshead.h"
+
+#include <stdlib.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include "pool.h"
+#include "buffer.h"
+#include "test_common.h"
+#include "mock_msg.h"
+
+/*
+ * pool.c pulls in a few socket.c/socket_util.c helpers for address parsing and
+ * formatting.  The pool logic under test does not need the full socket stack,
+ * so provide small IPv4 implementations here (the IPv6 ones are not reached by
+ * these IPv4-only pools, but must exist to link).
+ */
+struct signal_info;
+
+in_addr_t
+getaddr(unsigned int flags, const char *hostname, int resolve_retry_seconds, 
bool *succeeded,
+        struct signal_info *sig_info)
+{
+    struct in_addr a;
+    int ok = inet_pton(AF_INET, hostname, &a) == 1;
+
+    if (succeeded)
+    {
+        *succeeded = ok;
+    }
+    return ok ? ntohl(a.s_addr) : 0;
+}
+
+const char *
+print_in_addr_t(in_addr_t addr, unsigned int flags, struct gc_arena *gc)
+{
+    struct in_addr a = { .s_addr = htonl(addr) };
+    char buf[INET_ADDRSTRLEN] = { 0 };
+
+    inet_ntop(AF_INET, &a, buf, sizeof(buf));
+    return string_alloc(buf, gc);
+}
+
+bool
+get_ipv6_addr(const char *hostname, struct in6_addr *network, unsigned int 
*netbits,
+              msglvl_t msglevel)
+{
+    return false;
+}
+
+const char *
+print_in6_addr(struct in6_addr a6, unsigned int flags, struct gc_arena *gc)
+{
+    return "";
+}
+
+struct in6_addr
+add_in6_addr(struct in6_addr base, uint32_t add)
+{
+    return base;
+}
+
+#define IP(a, b, c, d) (((in_addr_t)(a) << 24) | ((b) << 16) | ((c) << 8) | 
(d))
+
+/* three non-overlapping /24 pools: the global one and two --subnet-pool 
groups */
+#define GLOBAL_START IP(10, 1, 1, 2)
+#define GLOBAL_END   IP(10, 1, 1, 254)
+#define A_START      IP(10, 2, 1, 2)
+#define A_END        IP(10, 2, 1, 254)
+#define B_START      IP(10, 2, 2, 2)
+#define B_END        IP(10, 2, 2, 254)
+
+static struct ifconfig_pool *
+make_pool(in_addr_t start, in_addr_t end)
+{
+    struct in6_addr any = { 0 };
+
+    return ifconfig_pool_init(true, IFCONFIG_POOL_INDIV, start, end, false, 
false, any, 0);
+}
+
+static in_addr_t
+acquire(struct ifconfig_pool *pool, const char *cn)
+{
+    in_addr_t local = 0, remote = 0;
+
+    ifconfig_pool_acquire(pool, &local, &remote, NULL, cn);
+    return remote;
+}
+
+/* whether any entry of the pool is reserved for common name cn */
+static bool
+pool_has_cn(const struct ifconfig_pool *pool, const char *cn)
+{
+    for (int i = 0; i < pool->size; ++i)
+    {
+        if (pool->list[i].common_name && !strcmp(pool->list[i].common_name, 
cn))
+        {
+            return true;
+        }
+    }
+    return false;
+}
+
+static const char *persist_file;
+
+/* Write {global, A, B} to one persist file, reload into a fresh set, and check
+ * that every entry was routed back to the pool that owns its address. */
+static void
+test_persist_roundtrip(void **state)
+{
+    struct ifconfig_pool *g = make_pool(GLOBAL_START, GLOBAL_END);
+    struct ifconfig_pool *a = make_pool(A_START, A_END);
+    struct ifconfig_pool *b = make_pool(B_START, B_END);
+
+    assert_int_equal(acquire(g, "alice"), GLOBAL_START);
+    assert_int_equal(acquire(a, "bob"), A_START);
+    assert_int_equal(acquire(b, "carol"), B_START);
+
+    struct ifconfig_pool *pools[] = { g, a, b };
+    struct ifconfig_pool_persist *wp = 
ifconfig_pool_persist_init(persist_file, 5);
+    ifconfig_pool_write(wp, pools, 3);
+    ifconfig_pool_persist_close(wp);
+
+    struct ifconfig_pool *g2 = make_pool(GLOBAL_START, GLOBAL_END);
+    struct ifconfig_pool *a2 = make_pool(A_START, A_END);
+    struct ifconfig_pool *b2 = make_pool(B_START, B_END);
+    struct ifconfig_pool *pools2[] = { g2, a2, b2 };
+    struct ifconfig_pool_persist *rp = 
ifconfig_pool_persist_init(persist_file, 0);
+    ifconfig_pool_read(rp, pools2, 3);
+    ifconfig_pool_persist_close(rp);
+
+    /* each client was routed to its own pool (handle 0 == the pool's base) */
+    assert_string_equal(g2->list[0].common_name, "alice");
+    assert_string_equal(a2->list[0].common_name, "bob");
+    assert_string_equal(b2->list[0].common_name, "carol");
+
+    /* and nowhere else */
+    assert_false(pool_has_cn(g2, "bob"));
+    assert_false(pool_has_cn(g2, "carol"));
+    assert_false(pool_has_cn(a2, "alice"));
+
+    /* the reservation is honoured: the same CN gets its persisted address 
back */
+    assert_int_equal(acquire(a2, "bob"), A_START);
+
+    ifconfig_pool_free(g);
+    ifconfig_pool_free(a);
+    ifconfig_pool_free(b);
+    ifconfig_pool_free(g2);
+    ifconfig_pool_free(a2);
+    ifconfig_pool_free(b2);
+}
+
+/* An entry whose address matches no configured pool (here: B's, when B is not
+ * in the set) is ignored on load and does not disturb the other pools. */
+static void
+test_persist_ignore_unknown(void **state)
+{
+    struct ifconfig_pool *g = make_pool(GLOBAL_START, GLOBAL_END);
+    struct ifconfig_pool *a = make_pool(A_START, A_END);
+    struct ifconfig_pool *b = make_pool(B_START, B_END);
+
+    acquire(g, "alice");
+    acquire(a, "bob");
+    acquire(b, "carol");
+
+    struct ifconfig_pool *pools[] = { g, a, b };
+    struct ifconfig_pool_persist *wp = 
ifconfig_pool_persist_init(persist_file, 5);
+    ifconfig_pool_write(wp, pools, 3);
+    ifconfig_pool_persist_close(wp);
+
+    /* reload into a set WITHOUT pool B: carol's entry now matches no pool */
+    struct ifconfig_pool *g2 = make_pool(GLOBAL_START, GLOBAL_END);
+    struct ifconfig_pool *a2 = make_pool(A_START, A_END);
+    struct ifconfig_pool *pools2[] = { g2, a2 };
+    struct ifconfig_pool_persist *rp = 
ifconfig_pool_persist_init(persist_file, 0);
+    ifconfig_pool_read(rp, pools2, 2);
+    ifconfig_pool_persist_close(rp);
+
+    assert_string_equal(g2->list[0].common_name, "alice");
+    assert_string_equal(a2->list[0].common_name, "bob");
+    assert_false(pool_has_cn(g2, "carol"));
+    assert_false(pool_has_cn(a2, "carol"));
+
+    ifconfig_pool_free(g);
+    ifconfig_pool_free(a);
+    ifconfig_pool_free(b);
+    ifconfig_pool_free(g2);
+    ifconfig_pool_free(a2);
+}
+
+const struct CMUnitTest pool_tests[] = {
+    cmocka_unit_test(test_persist_roundtrip),
+    cmocka_unit_test(test_persist_ignore_unknown),
+};
+
+int
+main(void)
+{
+    char template[] = "/tmp/ovpn_pool_persist_ut.XXXXXX";
+    int fd = mkstemp(template);
+
+    assert_true(fd >= 0);
+    close(fd);
+    persist_file = template;
+
+    openvpn_unit_test_setup();
+    int ret = cmocka_run_group_tests(pool_tests, NULL, NULL);
+
+    unlink(template);
+    return ret;
+}

--
To view, visit http://gerrit.openvpn.net/c/openvpn/+/1790?usp=email
To unsubscribe, or for help writing mail filters, visit 
http://gerrit.openvpn.net/settings?usp=email

Gerrit-MessageType: newchange
Gerrit-Project: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: I0bff8d0a549a65b7da01ea0e785b7ec8bea17f05
Gerrit-Change-Number: 1790
Gerrit-PatchSet: 1
Gerrit-Owner: ordex <[email protected]>
Gerrit-Reviewer: plaisthos <[email protected]>
Gerrit-CC: openvpn-devel <[email protected]>
Gerrit-Attention: plaisthos <[email protected]>
_______________________________________________
Openvpn-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/openvpn-devel

Reply via email to