Attention is currently required from: Arne Schwabe.

Hello Arne Schwabe, 

I'd like you to reexamine a change. Please visit

    https://gerrit.openvpn.net/c/openvpn/+/1786?usp=email

to look at the new patch set (#5).


Change subject: multi: assign IPv6 addresses from a tagged subnet-pool
......................................................................

multi: assign IPv6 addresses from a tagged subnet-pool

Add the IPv6 counterpart of --subnet-pool:

  subnet-pool-ipv6 <tag> <network>/<bits> [<gateway>]   (main config)

selected by the same per-client --subnet-pool-tag. A client's tag is
resolved against the IPv6 pool list too and, when it matches, an IPv6
address is handed out from the group's pool alongside any IPv4 one (a
single dual-stack ifconfig_pool per tag). The pool gateway is pushed as
the remote end of the client's ifconfig-ipv6, as the server's own
address is off-link for it. A tag matching neither family is still
declined with AUTH_FAILED.

As for IPv4 the pools are checked when the config is loaded (network
between /64 and /124 without host bits, gateway a host address inside
it, unique tags, no overlap with another pool or the --server-ipv6
network), created at startup with their gateway reserved, and routed
into the server's tun device.

Change-Id: I64e0850d48647a2c0e3d385c2f20a9f445931727
GitHub: closes openvpn/OpenVPN#987
Signed-off-by: Antonio Quartulli <[email protected]>
---
M doc/man-sections/server-options.rst
M src/openvpn/multi.c
M src/openvpn/options.c
M src/openvpn/options.h
M src/openvpn/options_util.c
M src/openvpn/options_util.h
M src/openvpn/pool.c
M src/openvpn/pool.h
M tests/unit_tests/openvpn/test_subnet_pool.c
9 files changed, 389 insertions(+), 29 deletions(-)


  git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/86/1786/5

diff --git a/doc/man-sections/server-options.rst 
b/doc/man-sections/server-options.rst
index d868d09..b3e687f 100644
--- a/doc/man-sections/server-options.rst
+++ b/doc/man-sections/server-options.rst
@@ -359,11 +359,32 @@
   and FreeBSD the IPv4 route is installed on-link, without a gateway, so its
   ``route_gateway_{n}`` is not set for ``--route-up`` scripts.

+--subnet-pool-ipv6 args
+  The IPv6 counterpart of ``--subnet-pool``. Repeatable main-config directive.
+
+  Valid syntax:
+  ::
+
+     subnet-pool-ipv6 tag network/bits [gateway]
+
+  ``bits`` must be between 64 and 124; ``gateway`` defaults to the first
+  address of the network, is never assigned to a client, and is pushed to the
+  pool's clients as the remote end of their ``ifconfig-ipv6``. A tag may name
+  an IPv4 pool, an IPv6 pool, or both; a client referencing it with
+  ``--subnet-pool-tag`` is then assigned an address from each family the tag
+  defines.
+
+  Requires ``--server-ipv6`` with ``--dev tun``. ``network`` must have no host
+  bits set, ``gateway`` must be a host address inside it, each ``tag`` may be
+  defined only once and no pool may overlap another one or the
+  ``--server-ipv6`` network. As for ``--subnet-pool``, the server routes each
+  pool's network into the tun device.
+
 --subnet-pool-tag tag
-  Assign this client to the ``--subnet-pool`` named ``tag``. Must be
-  associated with a specific client instance via ``--client-config-dir`` or
-  ``--client-connect``. Several clients may reference the same tag and are
-  then served from the same shared pool.
+  Assign this client to the ``--subnet-pool`` / ``--subnet-pool-ipv6`` named
+  ``tag``. Must be associated with a specific client instance via
+  ``--client-config-dir`` or ``--client-connect``. Several clients may
+  reference the same tag and are then served from the same shared pool(s).

 --ifconfig-ipv6-push args
   for ``--client-config-dir`` per-client static IPv6 interface
diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c
index 86fd9c0..ac3962d 100644
--- a/src/openvpn/multi.c
+++ b/src/openvpn/multi.c
@@ -392,6 +392,10 @@
     {
         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);
+    }

     /*
      * Help us keep track of routing table.
@@ -1469,14 +1473,24 @@
     }

     const struct subnet_pool_def *d4 = subnet_pool_by_tag(o->subnet_pools, 
tag);
-    ASSERT(d4);
+    const struct subnet_pool6_def *d6 = 
subnet_pool6_by_tag(o->subnet_pools_ipv6, tag);
+    ASSERT(d4 || d6);
     ALLOC_OBJ_CLEAR(sp, struct subnet_pool);
-    sp->tag = d4->tag;
-    /* every host address of the subnet, minus the gateway reserved below */
-    sp->pool = ifconfig_pool_init(true, IFCONFIG_POOL_INDIV, d4->network + 1,
-                                  (d4->network | ~d4->netmask) - 1, 
o->duplicate_cn, false,
-                                  in6addr_any, 0);
-    ifconfig_pool_reserve_ip(sp->pool, d4->gateway);
+    sp->tag = d4 ? d4->tag : d6->tag;
+    /* every host address of each subnet, minus the gateways reserved below
+     * (the IPv4 and the IPv6 address of a client share one pool handle) */
+    sp->pool = ifconfig_pool_init(d4 != NULL, IFCONFIG_POOL_INDIV, d4 ? 
d4->network + 1 : 0,
+                                  d4 ? (d4->network | ~d4->netmask) - 1 : 0, 
o->duplicate_cn,
+                                  d6 != NULL, d6 ? add_in6_addr(d6->network, 
1) : in6addr_any,
+                                  d6 ? (int)d6->netbits : 0);
+    if (d4)
+    {
+        ifconfig_pool_reserve_ip(sp->pool, d4->gateway);
+    }
+    if (d6)
+    {
+        ifconfig_pool_reserve_ipv6(sp->pool, &d6->gateway);
+    }
     sp->next = m->subnet_pools;
     m->subnet_pools = sp;
     return sp->pool;
@@ -1510,6 +1524,19 @@
             mi->context.options.subnet_pool_gateway = d->gateway;
         }
     }
+    if (mi->context.options.subnet_pool_tag)
+    {
+        const struct subnet_pool6_def *d = subnet_pool6_by_tag(
+            m->top.options.subnet_pools_ipv6, 
mi->context.options.subnet_pool_tag);
+
+        mi->context.options.subnet_pool_ipv6_defined = d != NULL;
+        if (d)
+        {
+            mi->context.options.subnet_pool_ipv6_network = d->network;
+            mi->context.options.subnet_pool_ipv6_netbits = d->netbits;
+            mi->context.options.subnet_pool_ipv6_gateway = d->gateway;
+        }
+    }

     /*
      * If ifconfig addresses were set by dynamic config file,
@@ -1544,15 +1571,16 @@
                 "MULTI_sva: WARNING: if --ifconfig-push is used for IPv4, 
automatic IPv6 assignment from --ifconfig-ipv6-pool does not work.  Use 
--ifconfig-ipv6-push for IPv6 then.");
         }
     }
-    else if (mi->context.options.subnet_pool_defined)
+    else if (mi->context.options.subnet_pool_defined
+             || mi->context.options.subnet_pool_ipv6_defined)
     {
-        /* dynamic address from this client's --subnet-pool group */
+        /* dynamic address(es) from this client's --subnet-pool group */
         struct ifconfig_pool *group =
             multi_get_group_pool(m, &m->top.options, 
mi->context.options.subnet_pool_tag);

         /* the address may have been acquired before the tag was known (from
          * the global pool) or for a tag that a later handler overrode: drop
-         * it so we serve this client from its current group */
+         * it so we serve this client from its current group only */
         if (mi->vaddr_handle >= 0 && mi->vaddr_pool != group)
         {
             ifconfig_pool_release(mi->vaddr_pool ? mi->vaddr_pool : 
m->ifconfig_pool,
@@ -1560,32 +1588,50 @@
             mi->vaddr_handle = -1;
             mi->vaddr_pool = NULL;
             mi->context.c2.push_ifconfig_defined = false;
+            mi->context.c2.push_ifconfig_ipv6_defined = false;
         }

         if (mi->vaddr_handle < 0)
         {
             in_addr_t local = 0, remote = 0;
+            struct in6_addr remote_ipv6;
             const char *cn = NULL;

+            CLEAR(remote_ipv6);
             if (!mi->context.options.duplicate_cn)
             {
                 cn = tls_common_name(mi->context.c2.tls_multi, true);
             }

             mi->vaddr_pool = group;
-            mi->vaddr_handle = ifconfig_pool_acquire(mi->vaddr_pool, &local, 
&remote, NULL, cn);
-            if (mi->vaddr_handle >= 0)
-            {
-                mi->context.c2.push_ifconfig_local = remote;
-                mi->context.c2.push_ifconfig_remote_netmask =
-                    mi->context.options.subnet_pool_netmask;
-                mi->context.c2.push_ifconfig_defined = true;
-            }
-            else
+            mi->vaddr_handle =
+                ifconfig_pool_acquire(mi->vaddr_pool, &local, &remote, 
&remote_ipv6, cn);
+            if (mi->vaddr_handle < 0)
             {
                 msg(D_MULTI_ERRORS, "MULTI: no free --subnet-pool addresses 
are available for %s",
                     multi_instance_string(mi, false, &gc));
             }
+            else
+            {
+                if (mi->context.options.subnet_pool_defined)
+                {
+                    mi->context.c2.push_ifconfig_local = remote;
+                    mi->context.c2.push_ifconfig_remote_netmask =
+                        mi->context.options.subnet_pool_netmask;
+                    mi->context.c2.push_ifconfig_defined = true;
+                }
+                if (mi->context.options.subnet_pool_ipv6_defined)
+                {
+                    /* the server's own address is off-link for this client:
+                     * hand out the pool gateway as the remote end instead */
+                    mi->context.c2.push_ifconfig_ipv6_local = remote_ipv6;
+                    mi->context.c2.push_ifconfig_ipv6_remote =
+                        mi->context.options.subnet_pool_ipv6_gateway;
+                    mi->context.c2.push_ifconfig_ipv6_netbits =
+                        mi->context.options.subnet_pool_ipv6_netbits;
+                    mi->context.c2.push_ifconfig_ipv6_defined = true;
+                }
+            }
         }
     }
     else if (m->ifconfig_pool && mi->vaddr_handle < 0) /* otherwise, choose a 
pool address */
@@ -2926,7 +2972,9 @@
     }

     if (mi->context.options.subnet_pool_tag
-        && !subnet_pool_by_tag(m->top.options.subnet_pools, 
mi->context.options.subnet_pool_tag))
+        && !subnet_pool_by_tag(m->top.options.subnet_pools, 
mi->context.options.subnet_pool_tag)
+        && !subnet_pool6_by_tag(m->top.options.subnet_pools_ipv6,
+                                mi->context.options.subnet_pool_tag))
     {
         msg(D_MULTI_ERRORS, "MULTI: client has been rejected due to unknown 
--subnet-pool-tag '%s'",
             mi->context.options.subnet_pool_tag);
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index d5666b8..414680c 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -455,6 +455,8 @@
     "--subnet-pool tag network netmask [gateway] : Define a named pool of\n"
     "                  addresses outside the --server network, selected per\n"
     "                  client with --subnet-pool-tag.\n"
+    "--subnet-pool-ipv6 tag network/bits [gateway] : IPv6 counterpart of\n"
+    "                  --subnet-pool.\n"
     "--subnet-pool-tag tag : Assign this client to the --subnet-pool named 
tag.\n"
     "                  Only valid in a client-specific config file.\n"
     "--ifconfig-push local remote-netmask : Push an ifconfig option to 
remote,\n"
@@ -1944,6 +1946,20 @@
             subnet_pools_check(options->subnet_pools, options->server_network,
                                options->server_netmask, M_USAGE);
         }
+        if (options->subnet_pools_ipv6)
+        {
+            if (!options->server_ipv6_defined)
+            {
+                msg(M_USAGE, "--subnet-pool-ipv6 requires --server-ipv6");
+            }
+            if (dev_type_enum(options->dev, options->dev_type) != DEV_TYPE_TUN
+                || options->topology != TOP_SUBNET)
+            {
+                msg(M_USAGE, "--subnet-pool-ipv6 requires --dev tun and 
--topology subnet");
+            }
+            subnet_pools6_check(options->subnet_pools_ipv6, 
&options->server_network_ipv6,
+                                options->server_netbits_ipv6, M_USAGE);
+        }
         MUST_BE_UNDEF(allow_recursive_routing, "allow-recursive-routing");
         if (options->auth_user_pass_file)
         {
@@ -2011,6 +2027,7 @@
         MUST_BE_UNDEF(ifconfig_pool_persist_filename, "ifconfig-pool-persist");
         MUST_BE_UNDEF(ifconfig_ipv6_pool_defined, "ifconfig-ipv6-pool");
         MUST_BE_UNDEF(subnet_pools, "subnet-pool");
+        MUST_BE_UNDEF(subnet_pools_ipv6, "subnet-pool-ipv6");
         MUST_BE_UNDEF(real_hash_size, "hash-size");
         MUST_BE_UNDEF(virtual_hash_size, "hash-size");
         MUST_BE_UNDEF(learn_address_script, "learn-address");
@@ -3144,8 +3161,9 @@
 }

 /*
- * The server's tun device is only configured with the --server network, so
- * route each --subnet-pool network into it as well: without this the server
+ * The server's tun device is only configured with the --server and
+ * --server-ipv6 networks, so route each --subnet-pool and --subnet-pool-ipv6
+ * network into it as well: without this the server
  * could not reach its subnet-pool clients when the data channel runs in
  * userspace.  Under DCO on Linux and FreeBSD the routes are installed on-link,
  * see subnet_pool_routes_onlink().
@@ -3163,6 +3181,16 @@
                                       o->route_default_table_id);
         ro->on_link = subnet_pool_routes_onlink(o);
     }
+
+    for (const struct subnet_pool6_def *d = o->subnet_pools_ipv6; d; d = 
d->next)
+    {
+        struct buffer prefix = alloc_buf_gc(64, &o->gc);
+
+        buf_printf(&prefix, "%s/%u", print_in6_addr(d->network, 0, &o->gc), 
d->netbits);
+        rol6_check_alloc(o);
+        add_route_ipv6_to_option_list(o->routes_ipv6, BSTR(&prefix), NULL, 
NULL,
+                                      o->route_default_table_id);
+    }
 }

 static void
@@ -6325,6 +6353,37 @@
         sp->next = options->subnet_pools;
         options->subnet_pools = sp;
     }
+    else if (streq(p[0], "subnet-pool-ipv6") && p[1] && p[2] && !p[4])
+    {
+        struct subnet_pool6_def *sp;
+        struct in6_addr network, gateway;
+        unsigned int netbits = 0;
+
+        VERIFY_PERMISSION(OPT_P_GENERAL);
+        if (!get_ipv6_addr(p[2], &network, &netbits, msglevel))
+        {
+            msg(msglevel, "cannot parse --subnet-pool-ipv6 network");
+            goto err;
+        }
+        if (netbits < 64 || netbits > 124)
+        {
+            msg(msglevel, "--subnet-pool-ipv6 network must be between /64 and 
/124 (not /%d)",
+                netbits);
+            goto err;
+        }
+        if (p[3] && !get_ipv6_addr(p[3], &gateway, NULL, msglevel))
+        {
+            msg(msglevel, "cannot parse --subnet-pool-ipv6 gateway");
+            goto err;
+        }
+        ALLOC_OBJ_GC(sp, struct subnet_pool6_def, &options->gc);
+        sp->tag = p[1];
+        sp->network = network;
+        sp->netbits = netbits;
+        sp->gateway = p[3] ? gateway : add_in6_addr(network, 1);
+        sp->next = options->subnet_pools_ipv6;
+        options->subnet_pools_ipv6 = sp;
+    }
     else if (streq(p[0], "subnet-pool-tag") && p[1] && !p[2])
     {
         VERIFY_PERMISSION(OPT_P_INSTANCE);
diff --git a/src/openvpn/options.h b/src/openvpn/options.h
index ff26066..c758943 100644
--- a/src/openvpn/options.h
+++ b/src/openvpn/options.h
@@ -263,6 +263,16 @@
     in_addr_t gateway;
 };

+/* The IPv6 counterpart of struct subnet_pool_def (--subnet-pool-ipv6). */
+struct subnet_pool6_def
+{
+    struct subnet_pool6_def *next;
+    const char *tag;
+    struct in6_addr network;
+    unsigned int netbits;
+    struct in6_addr gateway;
+};
+
 /* Command line options */
 struct options
 {
@@ -529,12 +539,17 @@
     bool push_ifconfig_constraint_defined;
     in_addr_t push_ifconfig_constraint_network;
     in_addr_t push_ifconfig_constraint_netmask;
-    struct subnet_pool_def *subnet_pools; /* named pools (server config) */
-    const char *subnet_pool_tag;          /* which pool this client uses (CCD) 
*/
-    bool subnet_pool_defined;             /* resolved from the tag at connect 
*/
+    struct subnet_pool_def *subnet_pools;       /* named IPv4 pools (server 
config) */
+    struct subnet_pool6_def *subnet_pools_ipv6; /* named IPv6 pools (server 
config) */
+    const char *subnet_pool_tag;                /* which pool this client uses 
(CCD) */
+    bool subnet_pool_defined;                   /* resolved from the tag at 
connect */
     in_addr_t subnet_pool_network;
     in_addr_t subnet_pool_netmask;
     in_addr_t subnet_pool_gateway;
+    bool subnet_pool_ipv6_defined; /* resolved from the tag at connect */
+    struct in6_addr subnet_pool_ipv6_network;
+    unsigned int subnet_pool_ipv6_netbits;
+    struct in6_addr subnet_pool_ipv6_gateway;
     bool push_ifconfig_ipv4_blocked;           /* IPv4 */
     bool push_ifconfig_ipv6_defined;           /* IPv6 */
     struct in6_addr push_ifconfig_ipv6_local;  /* IPv6 */
diff --git a/src/openvpn/options_util.c b/src/openvpn/options_util.c
index 744ad4c..fb38cfa 100644
--- a/src/openvpn/options_util.c
+++ b/src/openvpn/options_util.c
@@ -116,6 +116,108 @@
     return true;
 }

+/* value of bit <i> (0 = most significant) of an IPv6 address */
+static bool
+in6_bit(const struct in6_addr *a, unsigned int i)
+{
+    return a->s6_addr[i / 8] & (0x80 >> (i % 8));
+}
+
+/* true if the first <bits> bits of the two addresses are equal */
+static bool
+in6_prefix_equal(const struct in6_addr *a, const struct in6_addr *b, unsigned 
int bits)
+{
+    for (unsigned int i = 0; i < bits; ++i)
+    {
+        if (in6_bit(a, i) != in6_bit(b, i))
+        {
+            return false;
+        }
+    }
+    return true;
+}
+
+/* true if every bit of <a> from bit <from> on is zero */
+static bool
+in6_suffix_zero(const struct in6_addr *a, unsigned int from)
+{
+    for (unsigned int i = from; i < 128; ++i)
+    {
+        if (in6_bit(a, i))
+        {
+            return false;
+        }
+    }
+    return true;
+}
+
+bool
+subnet_pools6_check(const struct subnet_pool6_def *pools, const struct 
in6_addr *server_network,
+                    unsigned int server_netbits, msglvl_t msglevel)
+{
+    for (const struct subnet_pool6_def *d = pools; d; d = d->next)
+    {
+        if (d->netbits < 64 || d->netbits > 124)
+        {
+            msg(msglevel, "--subnet-pool-ipv6 %s: network must be between /64 
and /124 (not /%u)",
+                d->tag, d->netbits);
+            return false;
+        }
+
+        if (!in6_suffix_zero(&d->network, d->netbits))
+        {
+            msg(msglevel, "--subnet-pool-ipv6 %s: network has host bits set", 
d->tag);
+            return false;
+        }
+
+        if (!in6_prefix_equal(&d->gateway, &d->network, d->netbits)
+            || in6_suffix_zero(&d->gateway, d->netbits))
+        {
+            msg(msglevel,
+                "--subnet-pool-ipv6 %s: gateway must be a host address inside 
the network",
+                d->tag);
+            return false;
+        }
+
+        if (server_netbits
+            && in6_prefix_equal(&d->network, server_network, 
min_uint(d->netbits, server_netbits)))
+        {
+            msg(msglevel, "--subnet-pool-ipv6 %s: network overlaps the 
--server-ipv6 network",
+                d->tag);
+            return false;
+        }
+
+        for (const struct subnet_pool6_def *e = d->next; e; e = e->next)
+        {
+            if (!strcmp(d->tag, e->tag))
+            {
+                msg(msglevel, "--subnet-pool-ipv6 %s: tag defined more than 
once", d->tag);
+                return false;
+            }
+            if (in6_prefix_equal(&d->network, &e->network, 
min_uint(d->netbits, e->netbits)))
+            {
+                msg(msglevel, "--subnet-pool-ipv6 %s and %s: networks 
overlap", d->tag, e->tag);
+                return false;
+            }
+        }
+    }
+
+    return true;
+}
+
+const struct subnet_pool6_def *
+subnet_pool6_by_tag(const struct subnet_pool6_def *pools, const char *tag)
+{
+    for (const struct subnet_pool6_def *d = pools; d; d = d->next)
+    {
+        if (!strcmp(d->tag, tag))
+        {
+            return d;
+        }
+    }
+    return NULL;
+}
+
 const char *
 parse_auth_failed_temp(struct options *o, const char *reason)
 {
diff --git a/src/openvpn/options_util.h b/src/openvpn/options_util.h
index d60759a..bffcddb 100644
--- a/src/openvpn/options_util.h
+++ b/src/openvpn/options_util.h
@@ -53,6 +53,25 @@
 bool subnet_pools_check(const struct subnet_pool_def *pools, in_addr_t 
server_network,
                         in_addr_t server_netmask, msglvl_t msglevel);

+/**
+ * IPv6 counterpart of subnet_pools_check(): network between /64 and /124
+ * without host bits, gateway a host address inside it, unique tags, and no
+ * network overlapping another pool or the --server-ipv6 network.
+ *
+ * @param pools           head of the --subnet-pool-ipv6 list (may be NULL)
+ * @param server_network  the --server-ipv6 network
+ * @param server_netbits  the --server-ipv6 prefix length, or 0 if there is 
none
+ * @param msglevel        level of the message logged on the first error
+ * @return                true if every definition is valid
+ */
+bool subnet_pools6_check(const struct subnet_pool6_def *pools,
+                         const struct in6_addr *server_network, unsigned int 
server_netbits,
+                         msglvl_t msglevel);
+
+/** IPv6 counterpart of subnet_pool_by_tag(). */
+const struct subnet_pool6_def *subnet_pool6_by_tag(const struct 
subnet_pool6_def *pools,
+                                                   const char *tag);
+

 /** Checks if the string is a valid integer by checking if it can be
  *  converted to an integer */
diff --git a/src/openvpn/pool.c b/src/openvpn/pool.c
index fa04d9c..8f899d3 100644
--- a/src/openvpn/pool.c
+++ b/src/openvpn/pool.c
@@ -445,6 +445,22 @@
     return true;
 }

+bool
+ifconfig_pool_reserve_ipv6(struct ifconfig_pool *pool, const struct in6_addr 
*addr)
+{
+    const ifconfig_pool_handle h = ifconfig_pool_ipv6_base_to_handle(pool, 
addr);
+
+    if (h < 0)
+    {
+        return false;
+    }
+
+    ifconfig_pool_entry_free(&pool->list[h], true);
+    pool->list[h].in_use = true;
+    pool->list[h].reserved = true;
+    return true;
+}
+
 static in_addr_t
 ifconfig_pool_handle_to_ip_base(const struct ifconfig_pool *pool, 
ifconfig_pool_handle hand)
 {
diff --git a/src/openvpn/pool.h b/src/openvpn/pool.h
index 497dcda..fda87ed 100644
--- a/src/openvpn/pool.h
+++ b/src/openvpn/pool.h
@@ -98,6 +98,9 @@
  */
 bool ifconfig_pool_reserve_ip(struct ifconfig_pool *pool, in_addr_t addr);

+/** IPv6 counterpart of ifconfig_pool_reserve_ip(). */
+bool ifconfig_pool_reserve_ipv6(struct ifconfig_pool *pool, const struct 
in6_addr *addr);
+
 struct ifconfig_pool_persist *ifconfig_pool_persist_init(const char *filename, 
int refresh_freq);

 void ifconfig_pool_persist_close(struct ifconfig_pool_persist *persist);
diff --git a/tests/unit_tests/openvpn/test_subnet_pool.c 
b/tests/unit_tests/openvpn/test_subnet_pool.c
index 1bb9e4c..f3d58cb 100644
--- a/tests/unit_tests/openvpn/test_subnet_pool.c
+++ b/tests/unit_tests/openvpn/test_subnet_pool.c
@@ -131,11 +131,88 @@
     assert_false(subnet_pools_check(&a, 0, 0, M_WARN));
 }

+/* the IPv6 counterpart resolves independently: a tag may name a v4 pool, a
+ * v6 pool, or both. */
+static void
+test_subnet_pool6_by_tag(void **state)
+{
+    struct subnet_pool6_def c = { .next = NULL, .tag = "grpC", .netbits = 64 };
+    struct subnet_pool6_def b = { .next = &c, .tag = "grpB", .netbits = 96 };
+    struct subnet_pool6_def a = { .next = &b, .tag = "grpA", .netbits = 64 };
+
+    assert_ptr_equal(subnet_pool6_by_tag(&a, "grpA"), &a);
+    assert_ptr_equal(subnet_pool6_by_tag(&a, "grpB"), &b);
+    assert_ptr_equal(subnet_pool6_by_tag(&a, "grpC"), &c);
+    assert_int_equal(subnet_pool6_by_tag(&a, "grpB")->netbits, 96);
+    assert_null(subnet_pool6_by_tag(&a, "nope"));
+    assert_null(subnet_pool6_by_tag(NULL, "grpA"));
+}
+
+/* <prefix>::<host> as an in6_addr */
+static struct in6_addr
+in6(const char *addr)
+{
+    struct in6_addr a;
+
+    assert_int_equal(inet_pton(AF_INET6, addr, &a), 1);
+    return a;
+}
+
+/* a single IPv6 pool, checked against the fd00:8::/64 --server-ipv6 network */
+static bool
+check6_one(const char *net, unsigned int bits, const char *gw)
+{
+    const struct in6_addr server = in6("fd00:8::");
+    struct subnet_pool6_def d = { .next = NULL, .tag = "grp", .network = 
in6(net), .netbits = bits, .gateway = in6(gw) };
+
+    return subnet_pools6_check(&d, &server, 64, M_WARN);
+}
+
+static void
+test_subnet_pools6_check(void **state)
+{
+    /* /64 .. /124, any host address of the network as gateway */
+    assert_true(check6_one("fd00:9::", 64, "fd00:9::1"));
+    assert_true(check6_one("fd00:9::", 124, "fd00:9::1"));
+    assert_true(check6_one("fd00:9::", 112, "fd00:9::ffff"));
+    assert_false(check6_one("fd00:9::", 63, "fd00:9::1"));
+    assert_false(check6_one("fd00:9::", 125, "fd00:9::1"));
+
+    /* host bits in the network, gateway = network or outside it */
+    assert_false(check6_one("fd00:9::5", 64, "fd00:9::1"));
+    assert_false(check6_one("fd00:9::", 64, "fd00:9::"));
+    assert_false(check6_one("fd00:9::", 64, "fd00:a::1"));
+
+    /* overlapping the --server-ipv6 network, inside or covering it */
+    assert_false(check6_one("fd00:8::", 96, "fd00:8::1"));
+
+    /* two pools: disjoint, overlapping, same tag */
+    const struct in6_addr server = in6("fd00:8::");
+    struct subnet_pool6_def b = { .next = NULL, .tag = "grpB", .network = 
in6("fd00:a::"), .netbits = 64, .gateway = in6("fd00:a::1") };
+    struct subnet_pool6_def a = { .next = &b, .tag = "grpA", .network = 
in6("fd00:9::"), .netbits = 64, .gateway = in6("fd00:9::1") };
+
+    assert_true(subnet_pools6_check(&a, &server, 64, M_WARN));
+    assert_true(subnet_pools6_check(&a, &server, 0, M_WARN));
+
+    b.network = in6("fd00:9::100");
+    b.netbits = 120;
+    b.gateway = in6("fd00:9::101");
+    assert_false(subnet_pools6_check(&a, &server, 64, M_WARN));
+
+    b.network = in6("fd00:a::");
+    b.netbits = 64;
+    b.gateway = in6("fd00:a::1");
+    b.tag = "grpA";
+    assert_false(subnet_pools6_check(&a, &server, 64, M_WARN));
+}
+
 const struct CMUnitTest subnet_pool_tests[] = {
     cmocka_unit_test(test_subnet_pool_by_tag),
     cmocka_unit_test(test_subnet_pools_check_netmask),
     cmocka_unit_test(test_subnet_pools_check_gateway),
     cmocka_unit_test(test_subnet_pools_check_overlap),
+    cmocka_unit_test(test_subnet_pool6_by_tag),
+    cmocka_unit_test(test_subnet_pools6_check),
 };

 int

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

Gerrit-MessageType: newpatchset
Gerrit-Project: openvpn
Gerrit-Branch: master
Gerrit-Change-Id: I64e0850d48647a2c0e3d385c2f20a9f445931727
Gerrit-Change-Number: 1786
Gerrit-PatchSet: 5
Gerrit-Owner: Antonio Quartulli <[email protected]>
Gerrit-Reviewer: Arne Schwabe <[email protected]>
Gerrit-CC: openvpn-devel <[email protected]>
Gerrit-Attention: Arne Schwabe <[email protected]>
_______________________________________________
Openvpn-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/openvpn-devel

Reply via email to