summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am3
-rw-r--r--src/network/networkd-address-pool.c166
-rw-r--r--src/network/networkd-address.c66
-rw-r--r--src/network/networkd-link.c5
-rw-r--r--src/network/networkd-manager.c55
-rw-r--r--src/network/networkd.h25
6 files changed, 319 insertions, 1 deletions
diff --git a/Makefile.am b/Makefile.am
index dea0633a95..82145c6765 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4384,7 +4384,8 @@ libsystemd_networkd_core_la_SOURCES = \
src/network/networkd-network.c \
src/network/networkd-address.c \
src/network/networkd-route.c \
- src/network/networkd-manager.c
+ src/network/networkd-manager.c \
+ src/network/networkd-address-pool.c
nodist_libsystemd_networkd_core_la_SOURCES = \
src/network/networkd-network-gperf.c \
diff --git a/src/network/networkd-address-pool.c b/src/network/networkd-address-pool.c
new file mode 100644
index 0000000000..a5079add02
--- /dev/null
+++ b/src/network/networkd-address-pool.c
@@ -0,0 +1,166 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <arpa/inet.h>
+
+#include "networkd.h"
+
+int address_pool_new(
+ Manager *m,
+ AddressPool **ret,
+ unsigned family,
+ const union in_addr_union *u,
+ unsigned prefixlen) {
+
+ AddressPool *p;
+
+ assert(m);
+ assert(ret);
+ assert(u);
+
+ p = new0(AddressPool, 1);
+ if (!p)
+ return -ENOMEM;
+
+ p->manager = m;
+ p->family = family;
+ p->prefixlen = prefixlen;
+ p->in_addr = *u;
+
+ LIST_PREPEND(address_pools, m->address_pools, p);
+
+ *ret = p;
+ return 0;
+}
+
+int address_pool_new_from_string(
+ Manager *m,
+ AddressPool **ret,
+ unsigned family,
+ const char *p,
+ unsigned prefixlen) {
+
+ union in_addr_union u;
+ int r;
+
+ assert(m);
+ assert(ret);
+ assert(p);
+
+ r = in_addr_from_string(family, p, &u);
+ if (r < 0)
+ return r;
+
+ return address_pool_new(m, ret, family, &u, prefixlen);
+}
+
+void address_pool_free(AddressPool *p) {
+
+ if (!p)
+ return;
+
+ if (p->manager)
+ LIST_REMOVE(address_pools, p->manager->address_pools, p);
+
+ free(p);
+}
+
+static bool address_pool_prefix_is_taken(
+ AddressPool *p,
+ const union in_addr_union *u,
+ unsigned prefixlen) {
+
+ Iterator i;
+ Link *l;
+ Network *n;
+
+ assert(p);
+ assert(u);
+
+ HASHMAP_FOREACH(l, p->manager->links, i) {
+ Address *a;
+
+ /* Don't clash with assigned addresses */
+ LIST_FOREACH(addresses, 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;
+ }
+
+ /* Don't clash with addresses already pulled from the pool, but not assigned yet */
+ LIST_FOREACH(addresses, a, l->pool_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 */
+ LIST_FOREACH(networks, n, p->manager->networks) {
+ Address *a;
+
+ LIST_FOREACH(addresses, a, n->static_addresses) {
+ if (a->family != p->family)
+ continue;
+
+ if (in_addr_prefix_intersect(p->family, u, prefixlen, &a->in_addr, a->prefixlen))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+int address_pool_acquire(AddressPool *p, unsigned prefixlen, union in_addr_union *found) {
+ union in_addr_union u;
+
+ assert(p);
+ assert(prefixlen > 0);
+ assert(found);
+
+ if (p->prefixlen > prefixlen)
+ return 0;
+
+ u = p->in_addr;
+ for (;;) {
+ if (!address_pool_prefix_is_taken(p, &u, prefixlen)) {
+ _cleanup_free_ char *s = NULL;
+
+ in_addr_to_string(p->family, &u, &s);
+ log_debug("Found range %s/%u", strna(s), prefixlen);
+
+ *found = u;
+ return 1;
+ }
+
+ if (!in_addr_prefix_next(p->family, &u, prefixlen))
+ return 0;
+
+ if (!in_addr_prefix_intersect(p->family, &p->in_addr, p->prefixlen, &u, prefixlen))
+ return 0;
+ }
+
+ return 0;
+}
diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c
index afd36a0dfc..ce015004de 100644
--- a/src/network/networkd-address.c
+++ b/src/network/networkd-address.c
@@ -228,6 +228,68 @@ int address_update(Address *address, Link *link,
return 0;
}
+static int address_acquire(Link *link, Address *original, Address **ret) {
+ union in_addr_union in_addr = {};
+ struct in_addr broadcast = {};
+ Address *na = NULL;
+ int r;
+
+ assert(link);
+ assert(original);
+ assert(ret);
+
+ /* Something useful was configured? just use it */
+ if (in_addr_null(original->family, &original->in_addr) <= 0)
+ return 0;
+
+ /* The address is configured to be 0.0.0.0 or [::] by the user?
+ * Then let's acquire something more useful from the pool. */
+ r = manager_address_pool_acquire(link->manager, original->family, original->prefixlen, &in_addr);
+ if (r < 0) {
+ log_error_link(link, "Failed to acquire address from pool: %s", strerror(-r));
+ return r;
+ }
+ if (r == 0) {
+ log_error_link(link, "Couldn't find free address for interface, all taken.");
+ return -EBUSY;
+ }
+
+ if (original->family == AF_INET) {
+ /* Pick first address in range for ourselves ...*/
+ in_addr.in.s_addr = in_addr.in.s_addr | htobe32(1);
+
+ /* .. and use last as broadcast address */
+ broadcast.s_addr = in_addr.in.s_addr | htobe32(0xFFFFFFFFUL >> original->prefixlen);
+ } else if (original->family == AF_INET6)
+ in_addr.in6.s6_addr[15] |= 1;
+
+ r = address_new_dynamic(&na);
+ if (r < 0)
+ return r;
+
+ na->family = original->family;
+ na->prefixlen = original->prefixlen;
+ na->scope = original->scope;
+ na->cinfo = original->cinfo;
+
+ if (original->label) {
+ na->label = strdup(original->label);
+
+ if (!na->label) {
+ free(na);
+ return -ENOMEM;
+ }
+ }
+
+ na->broadcast = broadcast;
+ na->in_addr = in_addr;
+
+ LIST_PREPEND(addresses, link->pool_addresses, na);
+
+ *ret = na;
+ return 0;
+}
+
int address_configure(Address *address, Link *link,
sd_rtnl_message_handler_t callback) {
_cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
@@ -240,6 +302,10 @@ int address_configure(Address *address, Link *link,
assert(link->manager);
assert(link->manager->rtnl);
+ r = address_acquire(link, address, &address);
+ if (r < 0)
+ return r;
+
r = sd_rtnl_message_new_addr(link->manager->rtnl, &req, RTM_NEWADDR,
link->ifindex, address->family);
if (r < 0) {
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index d81a3bf630..e7753dc98c 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -112,6 +112,11 @@ static void link_free(Link *link) {
address_free(address);
}
+ while ((address = link->pool_addresses)) {
+ LIST_REMOVE(addresses, link->pool_addresses, address);
+ address_free(address);
+ }
+
sd_dhcp_client_unref(link->dhcp_client);
sd_dhcp_lease_unref(link->dhcp_lease);
diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c
index 2a0d5342da..5cc88723e5 100644
--- a/src/network/networkd-manager.c
+++ b/src/network/networkd-manager.c
@@ -75,6 +75,33 @@ static int setup_signals(Manager *m) {
return 0;
}
+static int setup_default_address_pool(Manager *m) {
+ AddressPool *p;
+ int r;
+
+ assert(m);
+
+ /* Add in the well-known private address ranges. */
+
+ r = address_pool_new_from_string(m, &p, AF_INET6, "fc00::", 7);
+ if (r < 0)
+ return r;
+
+ r = address_pool_new_from_string(m, &p, AF_INET, "192.168.0.0", 16);
+ if (r < 0)
+ return r;
+
+ r = address_pool_new_from_string(m, &p, AF_INET, "172.16.0.0", 12);
+ if (r < 0)
+ return r;
+
+ r = address_pool_new_from_string(m, &p, AF_INET, "10.0.0.0", 8);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
int manager_new(Manager **ret) {
_cleanup_manager_free_ Manager *m = NULL;
int r;
@@ -129,6 +156,10 @@ int manager_new(Manager **ret) {
LIST_HEAD_INIT(m->networks);
+ r = setup_default_address_pool(m);
+ if (r < 0)
+ return r;
+
*ret = m;
m = NULL;
@@ -139,6 +170,7 @@ void manager_free(Manager *m) {
Network *network;
NetDev *netdev;
Link *link;
+ AddressPool *pool;
if (!m)
return;
@@ -164,6 +196,9 @@ void manager_free(Manager *m) {
netdev_unref(netdev);
hashmap_free(m->netdevs);
+ while ((pool = m->address_pools))
+ address_pool_free(pool);
+
sd_rtnl_unref(m->rtnl);
free(m);
@@ -460,3 +495,23 @@ finish:
return r;
}
+
+int manager_address_pool_acquire(Manager *m, unsigned family, unsigned prefixlen, union in_addr_union *found) {
+ AddressPool *p;
+ int r;
+
+ assert(m);
+ assert(prefixlen > 0);
+ assert(found);
+
+ LIST_FOREACH(address_pools, p, m->address_pools) {
+ if (p->family != family)
+ continue;
+
+ r = address_pool_acquire(p, prefixlen, found);
+ if (r != 0)
+ return r;
+ }
+
+ return 0;
+}
diff --git a/src/network/networkd.h b/src/network/networkd.h
index 308be832e1..7069a11d4c 100644
--- a/src/network/networkd.h
+++ b/src/network/networkd.h
@@ -47,6 +47,7 @@ typedef struct Link Link;
typedef struct Address Address;
typedef struct Route Route;
typedef struct Manager Manager;
+typedef struct AddressPool AddressPool;
typedef struct netdev_enslave_callback netdev_enslave_callback;
@@ -259,9 +260,22 @@ struct Link {
uint16_t original_mtu;
sd_ipv4ll *ipv4ll;
+ LIST_HEAD(Address, pool_addresses);
+
sd_dhcp_server *dhcp_server;
};
+struct AddressPool {
+ Manager *manager;
+
+ unsigned family;
+ unsigned prefixlen;
+
+ union in_addr_union in_addr;
+
+ LIST_FIELDS(AddressPool, address_pools);
+};
+
struct Manager {
sd_rtnl *rtnl;
sd_event *event;
@@ -277,6 +291,7 @@ struct Manager {
Hashmap *links;
Hashmap *netdevs;
LIST_HEAD(Network, networks);
+ LIST_HEAD(AddressPool, address_pools);
usec_t network_dirs_ts_usec;
};
@@ -299,6 +314,8 @@ int manager_bus_listen(Manager *m);
int manager_save(Manager *m);
+int manager_address_pool_acquire(Manager *m, unsigned family, unsigned prefixlen, union in_addr_union *found);
+
DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
#define _cleanup_manager_free_ _cleanup_(manager_freep)
@@ -449,6 +466,14 @@ LinkOperationalState link_operstate_from_string(const char *s) _pure_;
DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_unref);
#define _cleanup_link_unref_ _cleanup_(link_unrefp)
+/* Address Pool */
+
+int address_pool_new(Manager *m, AddressPool **ret, unsigned family, const union in_addr_union *u, unsigned prefixlen);
+int address_pool_new_from_string(Manager *m, AddressPool **ret, unsigned family, const char *p, unsigned prefixlen);
+void address_pool_free(AddressPool *p);
+
+int address_pool_acquire(AddressPool *p, unsigned prefixlen, union in_addr_union *found);
+
/* Macros which append INTERFACE= to the message */
#define log_full_link(level, link, fmt, ...) log_meta_object(level, __FILE__, __LINE__, __func__, "INTERFACE=", link->ifname, "%-*s: " fmt, IFNAMSIZ, link->ifname, ##__VA_ARGS__)