summaryrefslogtreecommitdiff
path: root/src/openvpn/pool.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/openvpn/pool.c')
-rw-r--r--src/openvpn/pool.c401
1 files changed, 306 insertions, 95 deletions
diff --git a/src/openvpn/pool.c b/src/openvpn/pool.c
index da28bc0..b3f0bcd 100644
--- a/src/openvpn/pool.c
+++ b/src/openvpn/pool.c
@@ -5,7 +5,7 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2002-2021 OpenVPN Inc <sales@openvpn.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
@@ -147,61 +147,144 @@ ifconfig_pool_verify_range(const int msglevel, const in_addr_t start, const in_a
}
struct ifconfig_pool *
-ifconfig_pool_init(int type, in_addr_t start, in_addr_t end,
- const bool duplicate_cn,
+ifconfig_pool_init(const bool ipv4_pool, enum pool_type type, in_addr_t start,
+ in_addr_t end, const bool duplicate_cn,
const bool ipv6_pool, const struct in6_addr ipv6_base,
const int ipv6_netbits )
{
struct gc_arena gc = gc_new();
struct ifconfig_pool *pool = NULL;
+ int pool_ipv4_size = -1, pool_ipv6_size = -1;
ASSERT(start <= end && end - start < IFCONFIG_POOL_MAX);
ALLOC_OBJ_CLEAR(pool, struct ifconfig_pool);
- pool->type = type;
pool->duplicate_cn = duplicate_cn;
- switch (type)
+ pool->ipv4.enabled = ipv4_pool;
+
+ if (pool->ipv4.enabled)
{
- case IFCONFIG_POOL_30NET:
- pool->base = start & ~3;
- pool->size = (((end | 3) + 1) - pool->base) >> 2;
- break;
+ pool->ipv4.type = type;
+ switch (pool->ipv4.type)
+ {
+ case IFCONFIG_POOL_30NET:
+ pool->ipv4.base = start & ~3;
+ pool_ipv4_size = (((end | 3) + 1) - pool->ipv4.base) >> 2;
+ break;
- case IFCONFIG_POOL_INDIV:
- pool->base = start;
- pool->size = end - start + 1;
- break;
+ case IFCONFIG_POOL_INDIV:
+ pool->ipv4.base = start;
+ pool_ipv4_size = end - start + 1;
+ break;
- default:
- ASSERT(0);
+ default:
+ ASSERT(0);
+ }
+
+ if (pool_ipv4_size < 2)
+ {
+ msg(M_FATAL, "IPv4 pool size is too small (%d), must be at least 2",
+ pool_ipv4_size);
+ }
+
+ msg(D_IFCONFIG_POOL, "IFCONFIG POOL IPv4: base=%s size=%d",
+ print_in_addr_t(pool->ipv4.base, 0, &gc), pool_ipv4_size);
+
+ pool->size = pool_ipv4_size;
}
/* IPv6 pools are always "INDIV" type */
- pool->ipv6 = ipv6_pool;
+ pool->ipv6.enabled = ipv6_pool;
- if (pool->ipv6)
+ if (pool->ipv6.enabled)
{
- pool->base_ipv6 = ipv6_base;
- pool->size_ipv6 = ipv6_netbits>96 ? ( 1<<(128-ipv6_netbits) )
+ /* the host portion of the address will always be contained in the last
+ * 4 bytes, therefore we can just extract that and use it as base in
+ * integer form
+ */
+ uint32_t base = (ipv6_base.s6_addr[12] << 24)
+ | (ipv6_base.s6_addr[13] << 16)
+ | (ipv6_base.s6_addr[14] << 8)
+ | ipv6_base.s6_addr[15];
+ /* some bits of the last 4 bytes may still be part of the network
+ * portion of the address, therefore we need to set them to 0
+ */
+ if ((128 - ipv6_netbits) < 32)
+ {
+ /* extract only the bits that are really part of the host portion of
+ * the address.
+ *
+ * Example: if we have netbits=31, the first bit has to be zero'd,
+ * the following operation first computes mask=0x3fffff and then
+ * uses mask to extract the wanted bits from base
+ */
+ uint32_t mask = (1 << (128 - ipv6_netbits) ) - 1;
+ base &= mask;
+ }
+
+ pool->ipv6.base = ipv6_base;
+
+ /* if a pool starts at a base address that has all-zero in the
+ * host part, that first IPv6 address must not be assigned to
+ * clients because it is not usable (subnet anycast address).
+ * Start with 1, then.
+ *
+ * NOTE: this will also (mis-)fire for something like
+ * ifconfig-ipv6-pool 2001:db8:0:1:1234::0/64
+ * as we only check the rightmost 32 bits of the host part. So be it.
+ */
+ if (base == 0)
+ {
+ msg(D_IFCONFIG_POOL, "IFCONFIG POOL IPv6: incrementing pool start "
+ "to avoid ::0 assignment");
+ base++;
+ pool->ipv6.base.s6_addr[15]++;
+ }
+
+ pool_ipv6_size = ipv6_netbits >= 112
+ ? (1 << (128 - ipv6_netbits)) - base
: IFCONFIG_POOL_MAX;
- msg( D_IFCONFIG_POOL, "IFCONFIG POOL IPv6: (IPv4) size=%d, size_ipv6=%d, netbits=%d, base_ipv6=%s",
- pool->size, pool->size_ipv6, ipv6_netbits,
- print_in6_addr( pool->base_ipv6, 0, &gc ));
+ if (pool_ipv6_size < 2)
+ {
+ msg(M_FATAL, "IPv6 pool size is too small (%d), must be at least 2",
+ pool_ipv6_size);
+ }
+
+ msg(D_IFCONFIG_POOL, "IFCONFIG POOL IPv6: base=%s size=%d netbits=%d",
+ print_in6_addr(pool->ipv6.base, 0, &gc), pool_ipv6_size,
+ ipv6_netbits);
- /* the current code is very simple and assumes that the IPv6
- * pool is at least as big as the IPv4 pool, and we don't need
- * to do separate math etc. for IPv6
+ /* if there is no v4 pool, or the v6 pool is smaller, use
+ * v6 pool size as "unified pool size"
*/
- ASSERT( pool->size < pool->size_ipv6 );
+ if (pool->size <= 0 || pool_ipv6_size < pool->size)
+ {
+ pool->size = pool_ipv6_size;
+ }
}
- ALLOC_ARRAY_CLEAR(pool->list, struct ifconfig_pool_entry, pool->size);
+ if (pool->ipv4.enabled && pool->ipv6.enabled)
+ {
+ if (pool_ipv4_size < pool_ipv6_size)
+ {
+ msg(M_INFO, "NOTE: IPv4 pool size is %d, IPv6 pool size is %d. "
+ "IPv4 pool size limits the number of clients that can be "
+ "served from the pool", pool_ipv4_size, pool_ipv6_size);
+ }
+ else if (pool_ipv4_size > pool_ipv6_size)
+ {
+ msg(M_WARN, "WARNING: IPv4 pool size is %d, IPv6 pool size is %d. "
+ "IPv6 pool size limits the number of clients that can be "
+ "served from the pool. This is likely a MISTAKE - please check "
+ "your configuration", pool_ipv4_size, pool_ipv6_size);
+ }
+ }
+
+ ASSERT(pool->size > 0);
- msg(D_IFCONFIG_POOL, "IFCONFIG POOL: base=%s size=%d, ipv6=%d",
- print_in_addr_t(pool->base, 0, &gc),
- pool->size, pool->ipv6 );
+ ALLOC_ARRAY_CLEAR(pool->list, struct ifconfig_pool_entry, pool->size);
gc_free(&gc);
return pool;
@@ -213,6 +296,7 @@ ifconfig_pool_free(struct ifconfig_pool *pool)
if (pool)
{
int i;
+
for (i = 0; i < pool->size; ++i)
{
ifconfig_pool_entry_free(&pool->list[i], true);
@@ -239,32 +323,35 @@ ifconfig_pool_acquire(struct ifconfig_pool *pool, in_addr_t *local, in_addr_t *r
ipe->common_name = string_alloc(common_name, NULL);
}
- switch (pool->type)
+ if (pool->ipv4.enabled && local && remote)
{
- case IFCONFIG_POOL_30NET:
+ switch (pool->ipv4.type)
{
- in_addr_t b = pool->base + (i << 2);
- *local = b + 1;
- *remote = b + 2;
- break;
- }
+ case IFCONFIG_POOL_30NET:
+ {
+ in_addr_t b = pool->ipv4.base + (i << 2);
+ *local = b + 1;
+ *remote = b + 2;
+ break;
+ }
- case IFCONFIG_POOL_INDIV:
- {
- in_addr_t b = pool->base + i;
- *local = 0;
- *remote = b;
- break;
- }
+ case IFCONFIG_POOL_INDIV:
+ {
+ in_addr_t b = pool->ipv4.base + i;
+ *local = 0;
+ *remote = b;
+ break;
+ }
- default:
- ASSERT(0);
+ default:
+ ASSERT(0);
+ }
}
/* IPv6 pools are always INDIV (--linear) */
- if (pool->ipv6 && remote_ipv6)
+ if (pool->ipv6.enabled && remote_ipv6)
{
- *remote_ipv6 = add_in6_addr( pool->base_ipv6, i );
+ *remote_ipv6 = add_in6_addr(pool->ipv6.base, i);
}
}
return i;
@@ -274,6 +361,7 @@ bool
ifconfig_pool_release(struct ifconfig_pool *pool, ifconfig_pool_handle hand, const bool hard)
{
bool ret = false;
+
if (pool && hand >= 0 && hand < pool->size)
{
ifconfig_pool_entry_free(&pool->list[hand], hard);
@@ -286,22 +374,23 @@ ifconfig_pool_release(struct ifconfig_pool *pool, ifconfig_pool_handle hand, con
* private access functions
*/
+/* currently handling IPv4 logic only */
static ifconfig_pool_handle
ifconfig_pool_ip_base_to_handle(const struct ifconfig_pool *pool, const in_addr_t addr)
{
ifconfig_pool_handle ret = -1;
- switch (pool->type)
+ switch (pool->ipv4.type)
{
case IFCONFIG_POOL_30NET:
{
- ret = (addr - pool->base) >> 2;
+ ret = (addr - pool->ipv4.base) >> 2;
break;
}
case IFCONFIG_POOL_INDIV:
{
- ret = (addr - pool->base);
+ ret = (addr - pool->ipv4.base);
break;
}
@@ -317,24 +406,64 @@ ifconfig_pool_ip_base_to_handle(const struct ifconfig_pool *pool, const in_addr_
return ret;
}
+static ifconfig_pool_handle
+ifconfig_pool_ipv6_base_to_handle(const struct ifconfig_pool *pool,
+ const struct in6_addr *in_addr)
+{
+ ifconfig_pool_handle ret;
+ uint32_t base, addr;
+
+ /* IPv6 pool is always IFCONFIG_POOL_INDIV.
+ *
+ * We assume the offset can't be larger than 2^32-1, therefore we compute
+ * the difference only among the last 4 bytes like if they were two 32bit
+ * long integers. The rest of the address must match.
+ */
+ for (int i = 0; i < (12); i++)
+ {
+ if (pool->ipv6.base.s6_addr[i] != in_addr->s6_addr[i])
+ {
+ return -1;
+ }
+ }
+
+ base = (pool->ipv6.base.s6_addr[12] << 24)
+ | (pool->ipv6.base.s6_addr[13] << 16)
+ | (pool->ipv6.base.s6_addr[14] << 8)
+ | pool->ipv6.base.s6_addr[15];
+
+ addr = (in_addr->s6_addr[12] << 24)
+ | (in_addr->s6_addr[13] << 16)
+ | (in_addr->s6_addr[14] << 8)
+ | in_addr->s6_addr[15];
+
+ ret = addr - base;
+ if (ret < 0 || ret >= pool->size)
+ {
+ ret = -1;
+ }
+
+ return ret;
+}
+
static in_addr_t
ifconfig_pool_handle_to_ip_base(const struct ifconfig_pool *pool, ifconfig_pool_handle hand)
{
in_addr_t ret = 0;
- if (hand >= 0 && hand < pool->size)
+ if (pool->ipv4.enabled && hand >= 0 && hand < pool->size)
{
- switch (pool->type)
+ switch (pool->ipv4.type)
{
case IFCONFIG_POOL_30NET:
{
- ret = pool->base + (hand << 2);
+ ret = pool->ipv4.base + (hand << 2);
break;
}
case IFCONFIG_POOL_INDIV:
{
- ret = pool->base + hand;
+ ret = pool->ipv4.base + hand;
break;
}
@@ -349,29 +478,26 @@ ifconfig_pool_handle_to_ip_base(const struct ifconfig_pool *pool, ifconfig_pool_
static struct in6_addr
ifconfig_pool_handle_to_ipv6_base(const struct ifconfig_pool *pool, ifconfig_pool_handle hand)
{
- struct in6_addr ret = in6addr_any;
+ struct in6_addr ret = IN6ADDR_ANY_INIT;
/* IPv6 pools are always INDIV (--linear) */
- if (hand >= 0 && hand < pool->size_ipv6)
+ if (pool->ipv6.enabled && hand >= 0 && hand < pool->size)
{
- ret = add_in6_addr( pool->base_ipv6, hand );
+ ret = add_in6_addr( pool->ipv6.base, hand );
}
return ret;
}
static void
-ifconfig_pool_set(struct ifconfig_pool *pool, const char *cn, const in_addr_t addr, const bool fixed)
+ifconfig_pool_set(struct ifconfig_pool *pool, const char *cn,
+ ifconfig_pool_handle h, const bool fixed)
{
- ifconfig_pool_handle h = ifconfig_pool_ip_base_to_handle(pool, addr);
- if (h >= 0)
- {
- struct ifconfig_pool_entry *e = &pool->list[h];
- ifconfig_pool_entry_free(e, true);
- e->in_use = false;
- e->common_name = string_alloc(cn, NULL);
- e->last_release = now;
- e->fixed = fixed;
- }
+ struct ifconfig_pool_entry *e = &pool->list[h];
+ ifconfig_pool_entry_free(e, true);
+ e->in_use = false;
+ e->common_name = string_alloc(cn, NULL);
+ e->last_release = now;
+ e->fixed = fixed;
}
static void
@@ -385,23 +511,26 @@ ifconfig_pool_list(const struct ifconfig_pool *pool, struct status_output *out)
for (i = 0; i < pool->size; ++i)
{
const struct ifconfig_pool_entry *e = &pool->list[i];
+ struct in6_addr ip6;
+ in_addr_t ip;
+ const char *ip6_str = "";
+ const char *ip_str = "";
+
if (e->common_name)
{
- const in_addr_t ip = ifconfig_pool_handle_to_ip_base(pool, i);
- if (pool->ipv6)
+ if (pool->ipv4.enabled)
{
- struct in6_addr ip6 = ifconfig_pool_handle_to_ipv6_base(pool, i);
- status_printf(out, "%s,%s,%s",
- e->common_name,
- print_in_addr_t(ip, 0, &gc),
- print_in6_addr(ip6, 0, &gc));
+ ip = ifconfig_pool_handle_to_ip_base(pool, i);
+ ip_str = print_in_addr_t(ip, 0, &gc);
}
- else
+
+ if (pool->ipv6.enabled)
{
- status_printf(out, "%s,%s",
- e->common_name,
- print_in_addr_t(ip, 0, &gc));
+ ip6 = ifconfig_pool_handle_to_ipv6_base(pool, i);
+ ip6_str = print_in6_addr(ip6, 0, &gc);
}
+
+ status_printf(out, "%s,%s,%s", e->common_name, ip_str, ip6_str);
}
}
gc_free(&gc);
@@ -475,16 +604,17 @@ ifconfig_pool_read(struct ifconfig_pool_persist *persist, struct ifconfig_pool *
const int buf_size = 128;
update_time();
+
if (persist && persist->file && pool)
{
struct gc_arena gc = gc_new();
struct buffer in = alloc_buf_gc(256, &gc);
- char *cn_buf;
- char *ip_buf;
+ char *cn_buf, *ip_buf, *ip6_buf;
int line = 0;
ALLOC_ARRAY_CLEAR_GC(cn_buf, char, buf_size, &gc);
ALLOC_ARRAY_CLEAR_GC(ip_buf, char, buf_size, &gc);
+ ALLOC_ARRAY_CLEAR_GC(ip6_buf, char, buf_size, &gc);
while (true)
{
@@ -494,28 +624,109 @@ ifconfig_pool_read(struct ifconfig_pool_persist *persist, struct ifconfig_pool *
break;
}
++line;
- if (BLEN(&in))
+ if (!BLEN(&in))
{
- int c = *BSTR(&in);
- if (c == '#' || c == ';')
+ continue;
+ }
+
+ int c = *BSTR(&in);
+ if (c == '#' || c == ';')
+ {
+ continue;
+ }
+
+ msg(M_INFO, "ifconfig_pool_read(), in='%s'", BSTR(&in));
+
+ /* The expected format of a line is: "CN,IP4,IP6".
+ *
+ * IP4 or IP6 may be empty when respectively no v4 or v6 pool
+ * was previously specified.
+ *
+ * This means that accepted strings can be:
+ * - CN,IP4,IP6
+ * - CN,IP4
+ * - CN,,IP6
+ */
+ if (!buf_parse(&in, ',', cn_buf, buf_size)
+ || !buf_parse(&in, ',', ip_buf, buf_size))
+ {
+ continue;
+ }
+
+ ifconfig_pool_handle h = -1, h6 = -1;
+
+ if (strlen(ip_buf) > 0)
+ {
+ bool v4_ok = true;
+ in_addr_t addr = getaddr(GETADDR_HOST_ORDER, ip_buf, 0, &v4_ok,
+ NULL);
+
+ if (!v4_ok)
{
- continue;
+ msg(M_WARN, "pool: invalid IPv4 (%s) for CN=%s", ip_buf,
+ cn_buf);
}
- msg( M_INFO, "ifconfig_pool_read(), in='%s', TODO: IPv6",
- BSTR(&in) );
+ else
+ {
+ h = ifconfig_pool_ip_base_to_handle(pool, addr);
+ if (h < 0)
+ {
+ msg(M_WARN,
+ "pool: IPv4 (%s) out of pool range for CN=%s",
+ ip_buf, cn_buf);
+ }
+ }
+ }
+
+ if (buf_parse(&in, ',', ip6_buf, buf_size) && strlen(ip6_buf) > 0)
+ {
+ struct in6_addr addr6;
- if (buf_parse(&in, ',', cn_buf, buf_size)
- && buf_parse(&in, ',', ip_buf, buf_size))
+ if (!get_ipv6_addr(ip6_buf, &addr6, NULL, M_WARN))
{
- bool succeeded;
- const in_addr_t addr = getaddr(GETADDR_HOST_ORDER, ip_buf, 0, &succeeded, NULL);
- if (succeeded)
+ msg(M_WARN, "pool: invalid IPv6 (%s) for CN=%s", ip6_buf,
+ cn_buf);
+ }
+ else
+ {
+ h6 = ifconfig_pool_ipv6_base_to_handle(pool, &addr6);
+ if (h6 < 0)
{
- msg( M_INFO, "succeeded -> ifconfig_pool_set()");
- ifconfig_pool_set(pool, cn_buf, addr, persist->fixed);
+ msg(M_WARN,
+ "pool: IPv6 (%s) out of pool range for CN=%s",
+ ip6_buf, cn_buf);
+ }
+
+ /* Rely on IPv6 if no IPv4 was provided or the one provided
+ * was not valid
+ */
+ if (h < 0)
+ {
+ h = h6;
}
}
}
+
+ /* 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.
+ */
+ 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)
+ {
+ 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);