chiark / gitweb /
sd-network: fixup api
[elogind.git] / src / network / networkd-address-pool.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2014 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <arpa/inet.h>
23
24 #include "networkd.h"
25
26 int address_pool_new(
27                 Manager *m,
28                 AddressPool **ret,
29                 unsigned family,
30                 const union in_addr_union *u,
31                 unsigned prefixlen) {
32
33         AddressPool *p;
34
35         assert(m);
36         assert(ret);
37         assert(u);
38
39         p = new0(AddressPool, 1);
40         if (!p)
41                 return -ENOMEM;
42
43         p->manager = m;
44         p->family = family;
45         p->prefixlen = prefixlen;
46         p->in_addr = *u;
47
48         LIST_PREPEND(address_pools, m->address_pools, p);
49
50         *ret = p;
51         return 0;
52 }
53
54 int address_pool_new_from_string(
55                 Manager *m,
56                 AddressPool **ret,
57                 unsigned family,
58                 const char *p,
59                 unsigned prefixlen) {
60
61         union in_addr_union u;
62         int r;
63
64         assert(m);
65         assert(ret);
66         assert(p);
67
68         r = in_addr_from_string(family, p, &u);
69         if (r < 0)
70                 return r;
71
72         return address_pool_new(m, ret, family, &u, prefixlen);
73 }
74
75 void address_pool_free(AddressPool *p) {
76
77         if (!p)
78                 return;
79
80         if (p->manager)
81                 LIST_REMOVE(address_pools, p->manager->address_pools, p);
82
83         free(p);
84 }
85
86 static bool address_pool_prefix_is_taken(
87                 AddressPool *p,
88                 const union in_addr_union *u,
89                 unsigned prefixlen) {
90
91         Iterator i;
92         Link *l;
93         Network *n;
94
95         assert(p);
96         assert(u);
97
98         HASHMAP_FOREACH(l, p->manager->links, i) {
99                 Address *a;
100
101                 /* Don't clash with assigned addresses */
102                 LIST_FOREACH(addresses, a, l->addresses) {
103                         if (a->family != p->family)
104                                 continue;
105
106                         if (in_addr_prefix_intersect(p->family, u, prefixlen, &a->in_addr, a->prefixlen))
107                                 return true;
108                 }
109
110                 /* Don't clash with addresses already pulled from the pool, but not assigned yet */
111                 LIST_FOREACH(addresses, a, l->pool_addresses) {
112                         if (a->family != p->family)
113                                 continue;
114
115                         if (in_addr_prefix_intersect(p->family, u, prefixlen, &a->in_addr, a->prefixlen))
116                                 return true;
117                 }
118         }
119
120         /* And don't clash with configured but un-assigned addresses either */
121         LIST_FOREACH(networks, n, p->manager->networks) {
122                 Address *a;
123
124                 LIST_FOREACH(addresses, a, n->static_addresses) {
125                         if (a->family != p->family)
126                                 continue;
127
128                         if (in_addr_prefix_intersect(p->family, u, prefixlen, &a->in_addr, a->prefixlen))
129                                 return true;
130                 }
131         }
132
133         return false;
134 }
135
136 int address_pool_acquire(AddressPool *p, unsigned prefixlen, union in_addr_union *found) {
137         union in_addr_union u;
138
139         assert(p);
140         assert(prefixlen > 0);
141         assert(found);
142
143         if (p->prefixlen > prefixlen)
144                 return 0;
145
146         u = p->in_addr;
147         for (;;) {
148                 if (!address_pool_prefix_is_taken(p, &u, prefixlen)) {
149                         _cleanup_free_ char *s = NULL;
150
151                         in_addr_to_string(p->family, &u, &s);
152                         log_debug("Found range %s/%u", strna(s), prefixlen);
153
154                         *found = u;
155                         return 1;
156                 }
157
158                 if (!in_addr_prefix_next(p->family, &u, prefixlen))
159                         return 0;
160
161                 if (!in_addr_prefix_intersect(p->family, &p->in_addr, p->prefixlen, &u, prefixlen))
162                         return 0;
163         }
164
165         return 0;
166 }