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 (#7).
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, 388 insertions(+), 29 deletions(-)
git pull ssh://gerrit.openvpn.net:29418/openvpn refs/changes/86/1786/7
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 12b7442..e3e48f5 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,25 @@
}
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);
+ const char *pool_tag = d4 ? d4->tag : (d6 ? d6->tag : NULL);
+ ASSERT(pool_tag);
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 = pool_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 +1525,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 +1572,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,
@@ -1567,27 +1596,44 @@
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 */
@@ -2928,7 +2974,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..61b52f2 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 /%u)",
+ 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 5ce4c76..fbb8da9 100644
--- a/tests/unit_tests/openvpn/test_subnet_pool.c
+++ b/tests/unit_tests/openvpn/test_subnet_pool.c
@@ -128,11 +128,87 @@
assert_false(subnet_pools_check(&adup, 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));
+
+ /* a /120 grpB inside grpA */
+ struct subnet_pool6_def bin = { .next = NULL, .tag = "grpB", .network =
in6("fd00:9::100"), .netbits = 120, .gateway = in6("fd00:9::101") };
+ struct subnet_pool6_def ain = { .next = &bin, .tag = "grpA", .network =
in6("fd00:9::"), .netbits = 64, .gateway = in6("fd00:9::1") };
+ assert_false(subnet_pools6_check(&ain, &server, 64, M_WARN));
+
+ /* the same tag defined twice */
+ struct subnet_pool6_def bdup = { .next = NULL, .tag = "grpA", .network =
in6("fd00:a::"), .netbits = 64, .gateway = in6("fd00:a::1") };
+ struct subnet_pool6_def adup = { .next = &bdup, .tag = "grpA", .network =
in6("fd00:9::"), .netbits = 64, .gateway = in6("fd00:9::1") };
+ assert_false(subnet_pools6_check(&adup, &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: 7
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