ha: Fix assignment of IP addresses if multiple pools are defined
[strongswan.git] / src / libcharon / plugins / ha / ha_attribute.c
1 /*
2 * Copyright (C) 2010 Martin Willi
3 * Copyright (C) 2010 revosec AG
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "ha_attribute.h"
17
18 #include <collections/linked_list.h>
19 #include <threading/mutex.h>
20
21 typedef struct private_ha_attribute_t private_ha_attribute_t;
22
23 /**
24 * Private data of an ha_attribute_t object.
25 */
26 struct private_ha_attribute_t {
27
28 /**
29 * Public ha_attribute_t interface.
30 */
31 ha_attribute_t public;
32
33 /**
34 * List of pools, pool_t
35 */
36 linked_list_t *pools;
37
38 /**
39 * Mutex to lock mask
40 */
41 mutex_t *mutex;
42
43 /**
44 * Kernel helper
45 */
46 ha_kernel_t *kernel;
47
48 /**
49 * Segment responsibility
50 */
51 ha_segments_t *segments;
52 };
53
54 /**
55 * In-memory pool.
56 */
57 typedef struct {
58 /** name of the pool */
59 char *name;
60 /** base address of pool */
61 host_t *base;
62 /** total number of addresses in this pool */
63 int size;
64 /** bitmask for address usage */
65 u_char *mask;
66 } pool_t;
67
68 /**
69 * Clean up a pool entry
70 */
71 static void pool_destroy(pool_t *pool)
72 {
73 pool->base->destroy(pool->base);
74 free(pool->name);
75 free(pool->mask);
76 free(pool);
77 }
78
79 /**
80 * convert a pool offset to an address
81 */
82 static host_t* offset2host(pool_t *pool, int offset)
83 {
84 chunk_t addr;
85 host_t *host;
86 uint32_t *pos;
87
88 if (offset > pool->size)
89 {
90 return NULL;
91 }
92
93 addr = chunk_clone(pool->base->get_address(pool->base));
94 if (pool->base->get_family(pool->base) == AF_INET6)
95 {
96 pos = (uint32_t*)(addr.ptr + 12);
97 }
98 else
99 {
100 pos = (uint32_t*)addr.ptr;
101 }
102 *pos = htonl(offset + ntohl(*pos));
103 host = host_create_from_chunk(pool->base->get_family(pool->base), addr, 0);
104 free(addr.ptr);
105 return host;
106 }
107
108 /**
109 * convert a host to a pool offset
110 */
111 static int host2offset(pool_t *pool, host_t *addr)
112 {
113 chunk_t host, base;
114 uint32_t hosti, basei;
115
116 if (addr->get_family(addr) != pool->base->get_family(pool->base))
117 {
118 return -1;
119 }
120 host = addr->get_address(addr);
121 base = pool->base->get_address(pool->base);
122 if (addr->get_family(addr) == AF_INET6)
123 {
124 /* only look at last /32 block */
125 if (!memeq(host.ptr, base.ptr, 12))
126 {
127 return -1;
128 }
129 host = chunk_skip(host, 12);
130 base = chunk_skip(base, 12);
131 }
132 hosti = ntohl(*(uint32_t*)(host.ptr));
133 basei = ntohl(*(uint32_t*)(base.ptr));
134 if (hosti > basei + pool->size)
135 {
136 return -1;
137 }
138 return hosti - basei;
139 }
140
141 /**
142 * Find a pool by its name
143 */
144 static pool_t* get_pool(private_ha_attribute_t *this, char *name)
145 {
146 enumerator_t *enumerator;
147 pool_t *pool, *found = NULL;
148
149 enumerator = this->pools->create_enumerator(this->pools);
150 while (enumerator->enumerate(enumerator, &pool))
151 {
152 if (streq(name, pool->name))
153 {
154 found = pool;
155 }
156 }
157 enumerator->destroy(enumerator);
158 return found;
159 }
160
161 /**
162 * Check if we are responsible for a bit in our bitmask
163 */
164 static bool responsible_for(private_ha_attribute_t *this, int bit)
165 {
166 u_int segment;
167
168 segment = this->kernel->get_segment_int(this->kernel, bit);
169 return this->segments->is_active(this->segments, segment);
170 }
171
172 METHOD(attribute_provider_t, acquire_address, host_t*,
173 private_ha_attribute_t *this, linked_list_t *pools, ike_sa_t *ike_sa,
174 host_t *requested)
175 {
176 enumerator_t *enumerator;
177 pool_t *pool = NULL;
178 int offset = -1, byte, bit;
179 host_t *address;
180 char *name;
181
182 enumerator = pools->create_enumerator(pools);
183 this->mutex->lock(this->mutex);
184 while (enumerator->enumerate(enumerator, &name))
185 {
186 pool = get_pool(this, name);
187 if (!pool)
188 {
189 continue;
190 }
191 if (pool->base->get_family(pool->base) !=
192 requested->get_family(requested))
193 {
194 continue;
195 }
196 for (byte = 0; byte < pool->size / 8; byte++)
197 {
198 if (pool->mask[byte] != 0xFF)
199 {
200 for (bit = 0; bit < 8; bit++)
201 {
202 if (!(pool->mask[byte] & 1 << bit) &&
203 responsible_for(this, bit))
204 {
205 offset = byte * 8 + bit;
206 pool->mask[byte] |= 1 << bit;
207 break;
208 }
209 }
210 }
211 if (offset != -1)
212 {
213 break;
214 }
215 }
216 if (offset == -1)
217 {
218 DBG1(DBG_CFG, "no address belonging to a responsible segment left "
219 "in HA pool '%s'", name);
220 }
221 else
222 {
223 break;
224 }
225 }
226 this->mutex->unlock(this->mutex);
227 enumerator->destroy(enumerator);
228
229 if (offset != -1)
230 {
231 address = offset2host(pool, offset);
232 DBG1(DBG_CFG, "acquired address %H from HA pool '%s'", address, name);
233 return address;
234 }
235 return NULL;
236 }
237
238 METHOD(attribute_provider_t, release_address, bool,
239 private_ha_attribute_t *this, linked_list_t *pools, host_t *address,
240 ike_sa_t *ike_sa)
241 {
242 enumerator_t *enumerator;
243 pool_t *pool;
244 int offset;
245 char *name;
246 bool found = FALSE;
247
248 enumerator = pools->create_enumerator(pools);
249 this->mutex->lock(this->mutex);
250 while (enumerator->enumerate(enumerator, &name))
251 {
252 pool = get_pool(this, name);
253 if (!pool)
254 {
255 continue;
256 }
257 if (pool->base->get_family(pool->base) != address->get_family(address))
258 {
259 continue;
260 }
261 offset = host2offset(pool, address);
262 if (offset > 0 && offset < pool->size)
263 {
264 pool->mask[offset / 8] &= ~(1 << (offset % 8));
265 DBG1(DBG_CFG, "released address %H to HA pool '%s'", address, name);
266 found = TRUE;
267 break;
268 }
269 }
270 this->mutex->unlock(this->mutex);
271 enumerator->destroy(enumerator);
272
273 return found;
274 }
275
276 METHOD(ha_attribute_t, reserve, void,
277 private_ha_attribute_t *this, char *name, host_t *address)
278 {
279 pool_t *pool;
280 int offset;
281
282 this->mutex->lock(this->mutex);
283 pool = get_pool(this, name);
284 if (pool)
285 {
286 offset = host2offset(pool, address);
287 if (offset > 0 && offset < pool->size)
288 {
289 pool->mask[offset / 8] |= 1 << (offset % 8);
290 DBG1(DBG_CFG, "reserved address %H in HA pool '%s'", address, name);
291 }
292 }
293 this->mutex->unlock(this->mutex);
294 }
295
296 METHOD(ha_attribute_t, destroy, void,
297 private_ha_attribute_t *this)
298 {
299 this->pools->destroy_function(this->pools, (void*)pool_destroy);
300 this->mutex->destroy(this->mutex);
301 free(this);
302 }
303
304 /**
305 * Load the configured pools.
306 */
307 static void load_pools(private_ha_attribute_t *this)
308 {
309 enumerator_t *enumerator;
310 char *name, *net, *bits;
311 host_t *base;
312 int mask, maxbits;
313 pool_t *pool;
314
315 enumerator = lib->settings->create_key_value_enumerator(lib->settings,
316 "%s.plugins.ha.pools", lib->ns);
317 while (enumerator->enumerate(enumerator, &name, &net))
318 {
319 net = strdup(net);
320 bits = strchr(net, '/');
321 if (!bits)
322 {
323 DBG1(DBG_CFG, "invalid HA pool '%s' subnet, skipped", name);
324 free(net);
325 continue;
326 }
327 *bits++ = '\0';
328
329 base = host_create_from_string(net, 0);
330 mask = atoi(bits);
331 free(net);
332 if (!base || !mask)
333 {
334 DESTROY_IF(base);
335 DBG1(DBG_CFG, "invalid HA pool '%s', skipped", name);
336 continue;
337 }
338 maxbits = base->get_family(base) == AF_INET ? 32 : 128;
339 mask = maxbits - mask;
340 if (mask > 24)
341 {
342 mask = 24;
343 DBG1(DBG_CFG, "size of HA pool '%s' limited to /%d",
344 name, maxbits - mask);
345 }
346 if (mask < 3)
347 {
348 DBG1(DBG_CFG, "HA pool '%s' too small, skipped", name);
349 base->destroy(base);
350 continue;
351 }
352
353 INIT(pool,
354 .name = strdup(name),
355 .base = base,
356 .size = (1 << mask),
357 );
358 pool->mask = calloc(pool->size / 8, 1);
359 /* do not use first/last address of pool */
360 pool->mask[0] |= 0x01;
361 pool->mask[pool->size / 8 - 1] |= 0x80;
362
363 DBG1(DBG_CFG, "loaded HA pool '%s' %H/%d (%d addresses)",
364 pool->name, pool->base, maxbits - mask, pool->size - 2);
365 this->pools->insert_last(this->pools, pool);
366 }
367 enumerator->destroy(enumerator);
368 }
369
370 /**
371 * See header
372 */
373 ha_attribute_t *ha_attribute_create(ha_kernel_t *kernel, ha_segments_t *segments)
374 {
375 private_ha_attribute_t *this;
376
377 INIT(this,
378 .public = {
379 .provider = {
380 .acquire_address = _acquire_address,
381 .release_address = _release_address,
382 .create_attribute_enumerator = enumerator_create_empty,
383 },
384 .reserve = _reserve,
385 .destroy = _destroy,
386 },
387 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
388 .pools = linked_list_create(),
389 .kernel = kernel,
390 .segments = segments,
391 );
392
393 load_pools(this);
394
395 return &this->public;
396 }