f18c58b6a9ba9d80abdf9ab305a3e0f31056c112
[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, char *name, identification_t *id,
174 host_t *requested)
175 {
176 pool_t *pool;
177 int offset = -1, byte, bit;
178 host_t *address;
179
180 this->mutex->lock(this->mutex);
181 pool = get_pool(this, name);
182 if (pool)
183 {
184 if (pool->base->get_family(pool->base) !=
185 requested->get_family(requested))
186 {
187 this->mutex->unlock(this->mutex);
188 return NULL;
189 }
190 for (byte = 0; byte < pool->size / 8; byte++)
191 {
192 if (pool->mask[byte] != 0xFF)
193 {
194 for (bit = 0; bit < 8; bit++)
195 {
196 if (!(pool->mask[byte] & 1 << bit) &&
197 responsible_for(this, bit))
198 {
199 offset = byte * 8 + bit;
200 pool->mask[byte] |= 1 << bit;
201 break;
202 }
203 }
204 }
205 if (offset != -1)
206 {
207 break;
208 }
209 }
210 if (offset == -1)
211 {
212 DBG1(DBG_CFG, "no address left in HA pool '%s' belonging to"
213 "a responsible segment", name);
214 }
215 }
216 this->mutex->unlock(this->mutex);
217 if (offset != -1)
218 {
219 address = offset2host(pool, offset);
220 DBG1(DBG_CFG, "acquired address %H from HA pool '%s'", address, name);
221 return address;
222 }
223 return NULL;
224 }
225
226 METHOD(attribute_provider_t, release_address, bool,
227 private_ha_attribute_t *this, char *name, host_t *address,
228 identification_t *id)
229 {
230 pool_t *pool;
231 int offset;
232 bool found = FALSE;
233
234 this->mutex->lock(this->mutex);
235 pool = get_pool(this, name);
236 if (pool)
237 {
238 offset = host2offset(pool, address);
239 if (offset > 0 && offset < pool->size)
240 {
241 pool->mask[offset / 8] &= ~(1 << (offset % 8));
242 DBG1(DBG_CFG, "released address %H to HA pool '%s'", address, name);
243 found = TRUE;
244 }
245 }
246 this->mutex->unlock(this->mutex);
247 return found;
248 }
249
250 METHOD(ha_attribute_t, reserve, void,
251 private_ha_attribute_t *this, char *name, host_t *address)
252 {
253 pool_t *pool;
254 int offset;
255
256 this->mutex->lock(this->mutex);
257 pool = get_pool(this, name);
258 if (pool)
259 {
260 offset = host2offset(pool, address);
261 if (offset > 0 && offset < pool->size)
262 {
263 pool->mask[offset / 8] |= 1 << (offset % 8);
264 DBG1(DBG_CFG, "reserved address %H in HA pool '%s'", address, name);
265 }
266 }
267 this->mutex->unlock(this->mutex);
268 }
269
270 METHOD(ha_attribute_t, destroy, void,
271 private_ha_attribute_t *this)
272 {
273 this->pools->destroy_function(this->pools, (void*)pool_destroy);
274 this->mutex->destroy(this->mutex);
275 free(this);
276 }
277
278 /**
279 * Load the configured pools.
280 */
281 static void load_pools(private_ha_attribute_t *this)
282 {
283 enumerator_t *enumerator;
284 char *name, *net, *bits;
285 host_t *base;
286 int mask, maxbits;
287 pool_t *pool;
288
289 enumerator = lib->settings->create_key_value_enumerator(lib->settings,
290 "%s.plugins.ha.pools", charon->name);
291 while (enumerator->enumerate(enumerator, &name, &net))
292 {
293 net = strdup(net);
294 bits = strchr(net, '/');
295 if (!bits)
296 {
297 DBG1(DBG_CFG, "invalid HA pool '%s' subnet, skipped", name);
298 free(net);
299 continue;
300 }
301 *bits++ = '\0';
302
303 base = host_create_from_string(net, 0);
304 mask = atoi(bits);
305 free(net);
306 if (!base || !mask)
307 {
308 DESTROY_IF(base);
309 DBG1(DBG_CFG, "invalid HA pool '%s', skipped", name);
310 continue;
311 }
312 maxbits = base->get_family(base) == AF_INET ? 32 : 128;
313 mask = maxbits - mask;
314 if (mask > 24)
315 {
316 mask = 24;
317 DBG1(DBG_CFG, "size of HA pool '%s' limited to /%d",
318 name, maxbits - mask);
319 }
320 if (mask < 3)
321 {
322 DBG1(DBG_CFG, "HA pool '%s' too small, skipped", name);
323 base->destroy(base);
324 continue;
325 }
326
327 INIT(pool,
328 .name = strdup(name),
329 .base = base,
330 .size = (1 << mask),
331 );
332 pool->mask = calloc(pool->size / 8, 1);
333 /* do not use first/last address of pool */
334 pool->mask[0] |= 0x01;
335 pool->mask[pool->size / 8 - 1] |= 0x80;
336
337 DBG1(DBG_CFG, "loaded HA pool '%s' %H/%d (%d addresses)",
338 pool->name, pool->base, maxbits - mask, pool->size - 2);
339 this->pools->insert_last(this->pools, pool);
340 }
341 enumerator->destroy(enumerator);
342 }
343
344 /**
345 * See header
346 */
347 ha_attribute_t *ha_attribute_create(ha_kernel_t *kernel, ha_segments_t *segments)
348 {
349 private_ha_attribute_t *this;
350
351 INIT(this,
352 .public = {
353 .provider = {
354 .acquire_address = _acquire_address,
355 .release_address = _release_address,
356 .create_attribute_enumerator = enumerator_create_empty,
357 },
358 .reserve = _reserve,
359 .destroy = _destroy,
360 },
361 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
362 .pools = linked_list_create(),
363 .kernel = kernel,
364 .segments = segments,
365 );
366
367 load_pools(this);
368
369 return &this->public;
370 }