summaryrefslogtreecommitdiffstats
path: root/src/network/networkd-address-pool.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/networkd-address-pool.c')
-rw-r--r--src/network/networkd-address-pool.c175
1 files changed, 175 insertions, 0 deletions
diff --git a/src/network/networkd-address-pool.c b/src/network/networkd-address-pool.c
new file mode 100644
index 0000000..51f092a
--- /dev/null
+++ b/src/network/networkd-address-pool.c
@@ -0,0 +1,175 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "alloc-util.h"
+#include "networkd-address-pool.h"
+#include "networkd-address.h"
+#include "networkd-manager.h"
+#include "set.h"
+#include "string-util.h"
+
+#define RANDOM_PREFIX_TRIAL_MAX 1024
+
+static int address_pool_new(
+ Manager *m,
+ int family,
+ const union in_addr_union *u,
+ unsigned prefixlen) {
+
+ _cleanup_free_ AddressPool *p = NULL;
+ int r;
+
+ assert(m);
+ assert(u);
+
+ p = new(AddressPool, 1);
+ if (!p)
+ return -ENOMEM;
+
+ *p = (AddressPool) {
+ .manager = m,
+ .family = family,
+ .prefixlen = prefixlen,
+ .in_addr = *u,
+ };
+
+ r = ordered_set_ensure_put(&m->address_pools, NULL, p);
+ if (r < 0)
+ return r;
+
+ TAKE_PTR(p);
+ return 0;
+}
+
+static int address_pool_new_from_string(
+ Manager *m,
+ int family,
+ const char *p,
+ unsigned prefixlen) {
+
+ union in_addr_union u;
+ int r;
+
+ assert(m);
+ assert(p);
+
+ r = in_addr_from_string(family, p, &u);
+ if (r < 0)
+ return r;
+
+ return address_pool_new(m, family, &u, prefixlen);
+}
+
+int address_pool_setup_default(Manager *m) {
+ int r;
+
+ assert(m);
+
+ /* Add in the well-known private address ranges. */
+ r = address_pool_new_from_string(m, AF_INET6, "fd00::", 8);
+ if (r < 0)
+ return r;
+
+ r = address_pool_new_from_string(m, AF_INET, "192.168.0.0", 16);
+ if (r < 0)
+ return r;
+
+ r = address_pool_new_from_string(m, AF_INET, "172.16.0.0", 12);
+ if (r < 0)
+ return r;
+
+ r = address_pool_new_from_string(m, AF_INET, "10.0.0.0", 8);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static bool address_pool_prefix_is_taken(
+ AddressPool *p,
+ const union in_addr_union *u,
+ unsigned prefixlen) {
+
+ Link *l;
+ Network *n;
+
+ assert(p);
+ assert(u);
+
+ HASHMAP_FOREACH(l, p->manager->links_by_index) {
+ Address *a;
+
+ /* Don't clash with assigned addresses */
+ SET_FOREACH(a, l->addresses) {
+ if (a->family != p->family)
+ continue;
+
+ if (in_addr_prefix_intersect(p->family, u, prefixlen, &a->in_addr, a->prefixlen))
+ return true;
+ }
+ }
+
+ /* And don't clash with configured but un-assigned addresses either */
+ ORDERED_HASHMAP_FOREACH(n, p->manager->networks) {
+ Address *a;
+
+ ORDERED_HASHMAP_FOREACH(a, n->addresses_by_section) {
+ if (a->family != p->family)
+ continue;
+
+ if (in_addr_prefix_intersect(p->family, u, prefixlen, &a->in_addr, a->prefixlen))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static int address_pool_acquire_one(AddressPool *p, int family, unsigned prefixlen, union in_addr_union *found) {
+ union in_addr_union u;
+ int r;
+
+ assert(p);
+ assert(prefixlen > 0);
+ assert(found);
+
+ if (p->family != family)
+ return 0;
+
+ if (p->prefixlen >= prefixlen)
+ return 0;
+
+ u = p->in_addr;
+
+ for (unsigned i = 0; i < RANDOM_PREFIX_TRIAL_MAX; i++) {
+ r = in_addr_random_prefix(p->family, &u, p->prefixlen, prefixlen);
+ if (r <= 0)
+ return r;
+
+ if (!address_pool_prefix_is_taken(p, &u, prefixlen)) {
+ log_debug("Found range %s", IN_ADDR_PREFIX_TO_STRING(p->family, &u, prefixlen));
+
+ *found = u;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+int address_pool_acquire(Manager *m, int family, unsigned prefixlen, union in_addr_union *found) {
+ AddressPool *p;
+ int r;
+
+ assert(m);
+ assert(IN_SET(family, AF_INET, AF_INET6));
+ assert(prefixlen > 0);
+ assert(found);
+
+ ORDERED_SET_FOREACH(p, m->address_pools) {
+ r = address_pool_acquire_one(p, family, prefixlen, found);
+ if (r != 0)
+ return r;
+ }
+
+ return 0;
+}