Moved data structures to new collections subfolder
[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 u_int32_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 = (u_int32_t*)(addr.ptr + 12);
97 }
98 else
99 {
100 pos = (u_int32_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 u_int32_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(*(u_int32_t*)(host.ptr));
133 basei = ntohl(*(u_int32_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, identification_t *id,
174 host_t *requested)
175 {
176 enumerator_t *enumerator;
177 pool_t *pool;
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 left in HA pool '%s' belonging to"
219 "a responsible segment", name);
220 }
221 }
222 this->mutex->unlock(this->mutex);
223 enumerator->destroy(enumerator);
224
225 if (offset != -1)
226 {
227 address = offset2host(pool, offset);
228 DBG1(DBG_CFG, "acquired address %H from HA pool '%s'", address, name);
229 return address;
230 }
231 return NULL;
232 }
233
234 METHOD(attribute_provider_t, release_address, bool,
235 private_ha_attribute_t *this, linked_list_t *pools, host_t *address,
236 identification_t *id)
237 {
238 enumerator_t *enumerator;
239 pool_t *pool;
240 int offset;
241 char *name;
242 bool found = FALSE;
243
244 enumerator = pools->create_enumerator(pools);
245 this->mutex->lock(this->mutex);
246 while (enumerator->enumerate(enumerator, &name))
247 {
248 pool = get_pool(this, name);
249 if (!pool)
250 {
251 continue;
252 }
253 if (pool->base->get_family(pool->base) != address->get_family(address))
254 {
255 continue;
256 }
257 offset = host2offset(pool, address);
258 if (offset > 0 && offset < pool->size)
259 {
260 pool->mask[offset / 8] &= ~(1 << (offset % 8));
261 DBG1(DBG_CFG, "released address %H to HA pool '%s'", address, name);
262 found = TRUE;
263 break;
264 }
265 }
266 this->mutex->unlock(this->mutex);
267 enumerator->destroy(enumerator);
268
269 return found;
270 }
271
272 METHOD(ha_attribute_t, reserve, void,
273 private_ha_attribute_t *this, char *name, host_t *address)
274 {
275 pool_t *pool;
276 int offset;
277
278 this->mutex->lock(this->mutex);
279 pool = get_pool(this, name);
280 if (pool)
281 {
282 offset = host2offset(pool, address);
283 if (offset > 0 && offset < pool->size)
284 {
285 pool->mask[offset / 8] |= 1 << (offset % 8);
286 DBG1(DBG_CFG, "reserved address %H in HA pool '%s'", address, name);
287 }
288 }
289 this->mutex->unlock(this->mutex);
290 }
291
292 METHOD(ha_attribute_t, destroy, void,
293 private_ha_attribute_t *this)
294 {
295 this->pools->destroy_function(this->pools, (void*)pool_destroy);
296 this->mutex->destroy(this->mutex);
297 free(this);
298 }
299
300 /**
301 * Load the configured pools.
302 */
303 static void load_pools(private_ha_attribute_t *this)
304 {
305 enumerator_t *enumerator;
306 char *name, *net, *bits;
307 host_t *base;
308 int mask, maxbits;
309 pool_t *pool;
310
311 enumerator = lib->settings->create_key_value_enumerator(lib->settings,
312 "%s.plugins.ha.pools", charon->name);
313 while (enumerator->enumerate(enumerator, &name, &net))
314 {
315 net = strdup(net);
316 bits = strchr(net, '/');
317 if (!bits)
318 {
319 DBG1(DBG_CFG, "invalid HA pool '%s' subnet, skipped", name);
320 free(net);
321 continue;
322 }
323 *bits++ = '\0';
324
325 base = host_create_from_string(net, 0);
326 mask = atoi(bits);
327 free(net);
328 if (!base || !mask)
329 {
330 DESTROY_IF(base);
331 DBG1(DBG_CFG, "invalid HA pool '%s', skipped", name);
332 continue;
333 }
334 maxbits = base->get_family(base) == AF_INET ? 32 : 128;
335 mask = maxbits - mask;
336 if (mask > 24)
337 {
338 mask = 24;
339 DBG1(DBG_CFG, "size of HA pool '%s' limited to /%d",
340 name, maxbits - mask);
341 }
342 if (mask < 3)
343 {
344 DBG1(DBG_CFG, "HA pool '%s' too small, skipped", name);
345 base->destroy(base);
346 continue;
347 }
348
349 INIT(pool,
350 .name = strdup(name),
351 .base = base,
352 .size = (1 << mask),
353 );
354 pool->mask = calloc(pool->size / 8, 1);
355 /* do not use first/last address of pool */
356 pool->mask[0] |= 0x01;
357 pool->mask[pool->size / 8 - 1] |= 0x80;
358
359 DBG1(DBG_CFG, "loaded HA pool '%s' %H/%d (%d addresses)",
360 pool->name, pool->base, maxbits - mask, pool->size - 2);
361 this->pools->insert_last(this->pools, pool);
362 }
363 enumerator->destroy(enumerator);
364 }
365
366 /**
367 * See header
368 */
369 ha_attribute_t *ha_attribute_create(ha_kernel_t *kernel, ha_segments_t *segments)
370 {
371 private_ha_attribute_t *this;
372
373 INIT(this,
374 .public = {
375 .provider = {
376 .acquire_address = _acquire_address,
377 .release_address = _release_address,
378 .create_attribute_enumerator = enumerator_create_empty,
379 },
380 .reserve = _reserve,
381 .destroy = _destroy,
382 },
383 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
384 .pools = linked_list_create(),
385 .kernel = kernel,
386 .segments = segments,
387 );
388
389 load_pools(this);
390
391 return &this->public;
392 }