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