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