Moved all kernel plugins to libhydra.
[strongswan.git] / src / libhydra / attributes / mem_pool.c
1 /*
2 * Copyright (C) 2010 Tobias Brunner
3 * Copyright (C) 2008-2010 Martin Willi
4 * Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #include "mem_pool.h"
18
19 #include <debug.h>
20 #include <utils/hashtable.h>
21 #include <utils/linked_list.h>
22 #include <threading/mutex.h>
23
24 #define POOL_LIMIT (sizeof(uintptr_t)*8)
25
26 typedef struct private_mem_pool_t private_mem_pool_t;
27
28 /**
29 * private data of mem_pool_t
30 */
31 struct private_mem_pool_t {
32 /**
33 * public interface
34 */
35 mem_pool_t public;
36
37 /**
38 * name of the pool
39 */
40 char *name;
41
42 /**
43 * base address of the pool
44 */
45 host_t *base;
46
47 /**
48 * size of the pool
49 */
50 u_int size;
51
52 /**
53 * next unused address
54 */
55 u_int unused;
56
57 /**
58 * lease hashtable [identity => entry]
59 */
60 hashtable_t *leases;
61
62 /**
63 * lock to safely access the pool
64 */
65 mutex_t *mutex;
66 };
67
68 /**
69 * Lease entry.
70 */
71 typedef struct {
72 /* identitiy reference */
73 identification_t *id;
74 /* list of online leases, as offset */
75 linked_list_t *online;
76 /* list of offline leases, as offset */
77 linked_list_t *offline;
78 } entry_t;
79
80 /**
81 * hashtable hash function for identities
82 */
83 static u_int id_hash(identification_t *id)
84 {
85 return chunk_hash(id->get_encoding(id));
86 }
87
88 /**
89 * hashtable equals function for identities
90 */
91 static bool id_equals(identification_t *a, identification_t *b)
92 {
93 return a->equals(a, b);
94 }
95
96 /**
97 * convert a pool offset to an address
98 */
99 static host_t* offset2host(private_mem_pool_t *pool, int offset)
100 {
101 chunk_t addr;
102 host_t *host;
103 u_int32_t *pos;
104
105 offset--;
106 if (offset > pool->size)
107 {
108 return NULL;
109 }
110
111 addr = chunk_clone(pool->base->get_address(pool->base));
112 if (pool->base->get_family(pool->base) == AF_INET6)
113 {
114 pos = (u_int32_t*)(addr.ptr + 12);
115 }
116 else
117 {
118 pos = (u_int32_t*)addr.ptr;
119 }
120 *pos = htonl(offset + ntohl(*pos));
121 host = host_create_from_chunk(pool->base->get_family(pool->base), addr, 0);
122 free(addr.ptr);
123 return host;
124 }
125
126 /**
127 * convert a host to a pool offset
128 */
129 static int host2offset(private_mem_pool_t *pool, host_t *addr)
130 {
131 chunk_t host, base;
132 u_int32_t hosti, basei;
133
134 if (addr->get_family(addr) != pool->base->get_family(pool->base))
135 {
136 return -1;
137 }
138 host = addr->get_address(addr);
139 base = pool->base->get_address(pool->base);
140 if (addr->get_family(addr) == AF_INET6)
141 {
142 /* only look at last /32 block */
143 if (!memeq(host.ptr, base.ptr, 12))
144 {
145 return -1;
146 }
147 host = chunk_skip(host, 12);
148 base = chunk_skip(base, 12);
149 }
150 hosti = ntohl(*(u_int32_t*)(host.ptr));
151 basei = ntohl(*(u_int32_t*)(base.ptr));
152 if (hosti > basei + pool->size)
153 {
154 return -1;
155 }
156 return hosti - basei + 1;
157 }
158
159 METHOD(mem_pool_t, get_name, const char*,
160 private_mem_pool_t *this)
161 {
162 return this->name;
163 }
164
165 METHOD(mem_pool_t, get_size, u_int,
166 private_mem_pool_t *this)
167 {
168 return this->size;
169 }
170
171 METHOD(mem_pool_t, get_online, u_int,
172 private_mem_pool_t *this)
173 {
174 enumerator_t *enumerator;
175 entry_t *entry;
176 u_int count = 0;
177
178 this->mutex->lock(this->mutex);
179 enumerator = this->leases->create_enumerator(this->leases);
180 while (enumerator->enumerate(enumerator, NULL, &entry))
181 {
182 count += entry->online->get_count(entry->online);
183 }
184 enumerator->destroy(enumerator);
185 this->mutex->unlock(this->mutex);
186
187 return count;
188 }
189
190 METHOD(mem_pool_t, get_offline, u_int,
191 private_mem_pool_t *this)
192 {
193 enumerator_t *enumerator;
194 entry_t *entry;
195 u_int count = 0;
196
197 this->mutex->lock(this->mutex);
198 enumerator = this->leases->create_enumerator(this->leases);
199 while (enumerator->enumerate(enumerator, NULL, &entry))
200 {
201 count += entry->offline->get_count(entry->offline);
202 }
203 enumerator->destroy(enumerator);
204 this->mutex->unlock(this->mutex);
205
206 return count;
207 }
208
209 METHOD(mem_pool_t, acquire_address, host_t*,
210 private_mem_pool_t *this, identification_t *id, host_t *requested)
211 {
212 uintptr_t offset = 0, current;
213 enumerator_t *enumerator;
214 entry_t *entry, *old;
215
216 /* if the pool is empty (e.g. in the %config case) we simply return the
217 * requested address */
218 if (this->size == 0)
219 {
220 return requested->clone(requested);
221 }
222
223 if (!requested->is_anyaddr(requested) &&
224 requested->get_family(requested) !=
225 this->base->get_family(this->base))
226 {
227 DBG1(DBG_CFG, "IP pool address family mismatch");
228 return NULL;
229 }
230
231 this->mutex->lock(this->mutex);
232 while (TRUE)
233 {
234 entry = this->leases->get(this->leases, id);
235 if (entry)
236 {
237 /* check for a valid offline lease, refresh */
238 enumerator = entry->offline->create_enumerator(entry->offline);
239 if (enumerator->enumerate(enumerator, &current))
240 {
241 entry->offline->remove_at(entry->offline, enumerator);
242 entry->online->insert_last(entry->online, (void*)current);
243 offset = current;
244 }
245 enumerator->destroy(enumerator);
246 if (offset)
247 {
248 DBG1(DBG_CFG, "reassigning offline lease to '%Y'", id);
249 break;
250 }
251 /* check for a valid online lease to reassign */
252 enumerator = entry->online->create_enumerator(entry->online);
253 while (enumerator->enumerate(enumerator, &current))
254 {
255 if (current == host2offset(this, requested))
256 {
257 offset = current;
258 break;
259 }
260 }
261 enumerator->destroy(enumerator);
262 if (offset)
263 {
264 DBG1(DBG_CFG, "reassigning online lease to '%Y'", id);
265 break;
266 }
267 }
268 else
269 {
270 INIT(entry,
271 .id = id->clone(id),
272 .online = linked_list_create(),
273 .offline = linked_list_create(),
274 );
275 this->leases->put(this->leases, entry->id, entry);
276 }
277 if (this->unused < this->size)
278 {
279 /* assigning offset, starting by 1 */
280 offset = ++this->unused;
281 entry->online->insert_last(entry->online, (void*)offset);
282 DBG1(DBG_CFG, "assigning new lease to '%Y'", id);
283 break;
284 }
285
286 /* no more addresses, replace the first found offline lease */
287 enumerator = this->leases->create_enumerator(this->leases);
288 while (enumerator->enumerate(enumerator, NULL, &old))
289 {
290 if (old->offline->remove_first(old->offline,
291 (void**)&current) == SUCCESS)
292 {
293 offset = current;
294 entry->online->insert_last(entry->online, (void*)offset);
295 DBG1(DBG_CFG, "reassigning existing offline lease by '%Y'"
296 " to '%Y'", old->id, id);
297 break;
298 }
299 }
300 enumerator->destroy(enumerator);
301 break;
302 }
303 this->mutex->unlock(this->mutex);
304
305 if (offset)
306 {
307 return offset2host(this, offset);
308 }
309 else
310 {
311 DBG1(DBG_CFG, "pool '%s' is full, unable to assign address",
312 this->name);
313 }
314 return NULL;
315 }
316
317 METHOD(mem_pool_t, release_address, bool,
318 private_mem_pool_t *this, host_t *address, identification_t *id)
319 {
320 bool found = FALSE;
321 entry_t *entry;
322 uintptr_t offset;
323
324 if (this->size != 0)
325 {
326 this->mutex->lock(this->mutex);
327 entry = this->leases->get(this->leases, id);
328 if (entry)
329 {
330 offset = host2offset(this, address);
331 if (entry->online->remove(entry->online, (void*)offset, NULL) > 0)
332 {
333 DBG1(DBG_CFG, "lease %H by '%Y' went offline", address, id);
334 entry->offline->insert_last(entry->offline, (void*)offset);
335 found = TRUE;
336 }
337 }
338 this->mutex->unlock(this->mutex);
339 }
340 return found;
341 }
342
343 /**
344 * lease enumerator
345 */
346 typedef struct {
347 /** implemented enumerator interface */
348 enumerator_t public;
349 /** hash-table enumerator */
350 enumerator_t *entries;
351 /** online enumerator */
352 enumerator_t *online;
353 /** offline enumerator */
354 enumerator_t *offline;
355 /** enumerated pool */
356 private_mem_pool_t *pool;
357 /** currently enumerated entry */
358 entry_t *entry;
359 /** currently enumerated lease address */
360 host_t *addr;
361 } lease_enumerator_t;
362
363 METHOD(enumerator_t, lease_enumerate, bool,
364 lease_enumerator_t *this, identification_t **id, host_t **addr, bool *online)
365 {
366 uintptr_t offset;
367
368 DESTROY_IF(this->addr);
369 this->addr = NULL;
370
371 while (TRUE)
372 {
373 if (this->entry)
374 {
375 if (this->online->enumerate(this->online, (void**)&offset))
376 {
377 *id = this->entry->id;
378 *addr = this->addr = offset2host(this->pool, offset);
379 *online = TRUE;
380 return TRUE;
381 }
382 if (this->offline->enumerate(this->offline, (void**)&offset))
383 {
384 *id = this->entry->id;
385 *addr = this->addr = offset2host(this->pool, offset);
386 *online = FALSE;
387 return TRUE;
388 }
389 this->online->destroy(this->online);
390 this->offline->destroy(this->offline);
391 this->online = this->offline = NULL;
392 }
393 if (!this->entries->enumerate(this->entries, NULL, &this->entry))
394 {
395 return FALSE;
396 }
397 this->online = this->entry->online->create_enumerator(
398 this->entry->online);
399 this->offline = this->entry->offline->create_enumerator(
400 this->entry->offline);
401 }
402 }
403
404 METHOD(enumerator_t, lease_enumerator_destroy, void,
405 lease_enumerator_t *this)
406 {
407 DESTROY_IF(this->addr);
408 DESTROY_IF(this->online);
409 DESTROY_IF(this->offline);
410 this->entries->destroy(this->entries);
411 this->pool->mutex->unlock(this->pool->mutex);
412 free(this);
413 }
414
415 METHOD(mem_pool_t, create_lease_enumerator, enumerator_t*,
416 private_mem_pool_t *this)
417 {
418 lease_enumerator_t *enumerator;
419
420 this->mutex->lock(this->mutex);
421 INIT(enumerator,
422 .public = {
423 .enumerate = (void*)_lease_enumerate,
424 .destroy = _lease_enumerator_destroy,
425 },
426 .pool = this,
427 .entries = this->leases->create_enumerator(this->leases),
428 );
429 return &enumerator->public;
430 }
431
432 METHOD(mem_pool_t, destroy, void,
433 private_mem_pool_t *this)
434 {
435 enumerator_t *enumerator;
436 entry_t *entry;
437
438 enumerator = this->leases->create_enumerator(this->leases);
439 while (enumerator->enumerate(enumerator, NULL, &entry))
440 {
441 entry->id->destroy(entry->id);
442 entry->online->destroy(entry->online);
443 entry->offline->destroy(entry->offline);
444 free(entry);
445 }
446 enumerator->destroy(enumerator);
447
448 this->leases->destroy(this->leases);
449 this->mutex->destroy(this->mutex);
450 DESTROY_IF(this->base);
451 free(this->name);
452 free(this);
453 }
454
455 /**
456 * Described in header
457 */
458 mem_pool_t *mem_pool_create(char *name, host_t *base, int bits)
459 {
460 private_mem_pool_t *this;
461 int addr_bits;
462
463 INIT(this,
464 .public = {
465 .get_name = _get_name,
466 .get_size = _get_size,
467 .get_online = _get_online,
468 .get_offline = _get_offline,
469 .acquire_address = _acquire_address,
470 .release_address = _release_address,
471 .create_lease_enumerator = _create_lease_enumerator,
472 .destroy = _destroy,
473 },
474 .name = strdup(name),
475 .leases = hashtable_create((hashtable_hash_t)id_hash,
476 (hashtable_equals_t)id_equals, 16),
477 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
478 );
479
480 if (base)
481 {
482 addr_bits = base->get_family(base) == AF_INET ? 32 : 128;
483 /* net bits -> host bits */
484 bits = addr_bits - bits;
485 if (bits > POOL_LIMIT)
486 {
487 bits = POOL_LIMIT;
488 DBG1(DBG_CFG, "virtual IP pool too large, limiting to %H/%d",
489 base, addr_bits - bits);
490 }
491 this->size = 1 << (bits);
492
493 if (this->size > 2)
494 { /* do not use first and last addresses of a block */
495 this->unused++;
496 this->size--;
497 }
498 this->base = base->clone(base);
499 }
500
501 return &this->public;
502 }
503