Use uintptr_t in mem pool to avoid compiler warning if sizeof(void*) != sizeof(int)
[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_base, host_t*,
166 private_mem_pool_t *this)
167 {
168 return this->base;
169 }
170
171 METHOD(mem_pool_t, get_size, u_int,
172 private_mem_pool_t *this)
173 {
174 return this->size;
175 }
176
177 METHOD(mem_pool_t, get_online, u_int,
178 private_mem_pool_t *this)
179 {
180 enumerator_t *enumerator;
181 entry_t *entry;
182 u_int count = 0;
183
184 this->mutex->lock(this->mutex);
185 enumerator = this->leases->create_enumerator(this->leases);
186 while (enumerator->enumerate(enumerator, NULL, &entry))
187 {
188 count += entry->online->get_count(entry->online);
189 }
190 enumerator->destroy(enumerator);
191 this->mutex->unlock(this->mutex);
192
193 return count;
194 }
195
196 METHOD(mem_pool_t, get_offline, u_int,
197 private_mem_pool_t *this)
198 {
199 enumerator_t *enumerator;
200 entry_t *entry;
201 u_int count = 0;
202
203 this->mutex->lock(this->mutex);
204 enumerator = this->leases->create_enumerator(this->leases);
205 while (enumerator->enumerate(enumerator, NULL, &entry))
206 {
207 count += entry->offline->get_count(entry->offline);
208 }
209 enumerator->destroy(enumerator);
210 this->mutex->unlock(this->mutex);
211
212 return count;
213 }
214
215 /**
216 * Get an existing lease for id
217 */
218 static int get_existing(private_mem_pool_t *this, identification_t *id,
219 host_t *requested)
220 {
221 enumerator_t *enumerator;
222 uintptr_t current;
223 entry_t *entry;
224 int offset = 0;
225
226 entry = this->leases->get(this->leases, id);
227 if (!entry)
228 {
229 return 0;
230 }
231
232 /* check for a valid offline lease, refresh */
233 enumerator = entry->offline->create_enumerator(entry->offline);
234 if (enumerator->enumerate(enumerator, &current))
235 {
236 entry->offline->remove_at(entry->offline, enumerator);
237 entry->online->insert_last(entry->online, (void*)current);
238 offset = current;
239 }
240 enumerator->destroy(enumerator);
241 if (offset)
242 {
243 DBG1(DBG_CFG, "reassigning offline lease to '%Y'", id);
244 return offset;
245 }
246
247 /* check for a valid online lease to reassign */
248 enumerator = entry->online->create_enumerator(entry->online);
249 while (enumerator->enumerate(enumerator, &current))
250 {
251 if (current == host2offset(this, requested))
252 {
253 offset = current;
254 break;
255 }
256 }
257 enumerator->destroy(enumerator);
258 if (offset)
259 {
260 DBG1(DBG_CFG, "reassigning online lease to '%Y'", id);
261 }
262 return offset;
263 }
264
265 /**
266 * Get a new lease for id
267 */
268 static int get_new(private_mem_pool_t *this, identification_t *id)
269 {
270 entry_t *entry;
271 uintptr_t offset = 0;
272
273 if (this->unused < this->size)
274 {
275 INIT(entry,
276 .id = id->clone(id),
277 .online = linked_list_create(),
278 .offline = linked_list_create(),
279 );
280 this->leases->put(this->leases, entry->id, entry);
281
282 /* assigning offset, starting by 1 */
283 offset = ++this->unused;
284 entry->online->insert_last(entry->online, (void*)offset);
285 DBG1(DBG_CFG, "assigning new lease to '%Y'", id);
286 }
287 return offset;
288 }
289
290 /**
291 * Get a reassigned lease for id in case the pool is full
292 */
293 static int get_reassigned(private_mem_pool_t *this, identification_t *id)
294 {
295 enumerator_t *enumerator;
296 entry_t *entry;
297 uintptr_t current, offset = 0;
298
299 enumerator = this->leases->create_enumerator(this->leases);
300 while (enumerator->enumerate(enumerator, NULL, &entry))
301 {
302 if (entry->offline->remove_first(entry->offline,
303 (void**)&current) == SUCCESS)
304 {
305 offset = current;
306 DBG1(DBG_CFG, "reassigning existing offline lease by '%Y'"
307 " to '%Y'", entry->id, id);
308 break;
309 }
310 }
311 enumerator->destroy(enumerator);
312
313 if (offset)
314 {
315 INIT(entry,
316 .id = id->clone(id),
317 .online = linked_list_create(),
318 .offline = linked_list_create(),
319 );
320 entry->online->insert_last(entry->online, (void*)offset);
321 this->leases->put(this->leases, entry->id, entry);
322 }
323 return offset;
324 }
325
326 METHOD(mem_pool_t, acquire_address, host_t*,
327 private_mem_pool_t *this, identification_t *id, host_t *requested,
328 mem_pool_op_t operation)
329 {
330 int offset = 0;
331
332 /* if the pool is empty (e.g. in the %config case) we simply return the
333 * requested address */
334 if (this->size == 0)
335 {
336 return requested->clone(requested);
337 }
338
339 if (requested->get_family(requested) !=
340 this->base->get_family(this->base))
341 {
342 return NULL;
343 }
344
345 this->mutex->lock(this->mutex);
346 switch (operation)
347 {
348 case MEM_POOL_EXISTING:
349 offset = get_existing(this, id, requested);
350 break;
351 case MEM_POOL_NEW:
352 offset = get_new(this, id);
353 break;
354 case MEM_POOL_REASSIGN:
355 offset = get_reassigned(this, id);
356 if (!offset)
357 {
358 DBG1(DBG_CFG, "pool '%s' is full, unable to assign address",
359 this->name);
360 }
361 break;
362 default:
363 break;
364 }
365 this->mutex->unlock(this->mutex);
366
367 if (offset)
368 {
369 return offset2host(this, offset);
370 }
371 return NULL;
372 }
373
374 METHOD(mem_pool_t, release_address, bool,
375 private_mem_pool_t *this, host_t *address, identification_t *id)
376 {
377 bool found = FALSE;
378 entry_t *entry;
379 uintptr_t offset;
380
381 if (this->size != 0)
382 {
383 this->mutex->lock(this->mutex);
384 entry = this->leases->get(this->leases, id);
385 if (entry)
386 {
387 offset = host2offset(this, address);
388 if (entry->online->remove(entry->online, (void*)offset, NULL) > 0)
389 {
390 DBG1(DBG_CFG, "lease %H by '%Y' went offline", address, id);
391 entry->offline->insert_last(entry->offline, (void*)offset);
392 found = TRUE;
393 }
394 }
395 this->mutex->unlock(this->mutex);
396 }
397 return found;
398 }
399
400 /**
401 * lease enumerator
402 */
403 typedef struct {
404 /** implemented enumerator interface */
405 enumerator_t public;
406 /** hash-table enumerator */
407 enumerator_t *entries;
408 /** online enumerator */
409 enumerator_t *online;
410 /** offline enumerator */
411 enumerator_t *offline;
412 /** enumerated pool */
413 private_mem_pool_t *pool;
414 /** currently enumerated entry */
415 entry_t *entry;
416 /** currently enumerated lease address */
417 host_t *addr;
418 } lease_enumerator_t;
419
420 METHOD(enumerator_t, lease_enumerate, bool,
421 lease_enumerator_t *this, identification_t **id, host_t **addr, bool *online)
422 {
423 uintptr_t offset;
424
425 DESTROY_IF(this->addr);
426 this->addr = NULL;
427
428 while (TRUE)
429 {
430 if (this->entry)
431 {
432 if (this->online->enumerate(this->online, (void**)&offset))
433 {
434 *id = this->entry->id;
435 *addr = this->addr = offset2host(this->pool, offset);
436 *online = TRUE;
437 return TRUE;
438 }
439 if (this->offline->enumerate(this->offline, (void**)&offset))
440 {
441 *id = this->entry->id;
442 *addr = this->addr = offset2host(this->pool, offset);
443 *online = FALSE;
444 return TRUE;
445 }
446 this->online->destroy(this->online);
447 this->offline->destroy(this->offline);
448 this->online = this->offline = NULL;
449 }
450 if (!this->entries->enumerate(this->entries, NULL, &this->entry))
451 {
452 return FALSE;
453 }
454 this->online = this->entry->online->create_enumerator(
455 this->entry->online);
456 this->offline = this->entry->offline->create_enumerator(
457 this->entry->offline);
458 }
459 }
460
461 METHOD(enumerator_t, lease_enumerator_destroy, void,
462 lease_enumerator_t *this)
463 {
464 DESTROY_IF(this->addr);
465 DESTROY_IF(this->online);
466 DESTROY_IF(this->offline);
467 this->entries->destroy(this->entries);
468 this->pool->mutex->unlock(this->pool->mutex);
469 free(this);
470 }
471
472 METHOD(mem_pool_t, create_lease_enumerator, enumerator_t*,
473 private_mem_pool_t *this)
474 {
475 lease_enumerator_t *enumerator;
476
477 this->mutex->lock(this->mutex);
478 INIT(enumerator,
479 .public = {
480 .enumerate = (void*)_lease_enumerate,
481 .destroy = _lease_enumerator_destroy,
482 },
483 .pool = this,
484 .entries = this->leases->create_enumerator(this->leases),
485 );
486 return &enumerator->public;
487 }
488
489 METHOD(mem_pool_t, destroy, void,
490 private_mem_pool_t *this)
491 {
492 enumerator_t *enumerator;
493 entry_t *entry;
494
495 enumerator = this->leases->create_enumerator(this->leases);
496 while (enumerator->enumerate(enumerator, NULL, &entry))
497 {
498 entry->id->destroy(entry->id);
499 entry->online->destroy(entry->online);
500 entry->offline->destroy(entry->offline);
501 free(entry);
502 }
503 enumerator->destroy(enumerator);
504
505 this->leases->destroy(this->leases);
506 this->mutex->destroy(this->mutex);
507 DESTROY_IF(this->base);
508 free(this->name);
509 free(this);
510 }
511
512 /**
513 * Described in header
514 */
515 mem_pool_t *mem_pool_create(char *name, host_t *base, int bits)
516 {
517 private_mem_pool_t *this;
518 int addr_bits;
519
520 INIT(this,
521 .public = {
522 .get_name = _get_name,
523 .get_base = _get_base,
524 .get_size = _get_size,
525 .get_online = _get_online,
526 .get_offline = _get_offline,
527 .acquire_address = _acquire_address,
528 .release_address = _release_address,
529 .create_lease_enumerator = _create_lease_enumerator,
530 .destroy = _destroy,
531 },
532 .name = strdup(name),
533 .leases = hashtable_create((hashtable_hash_t)id_hash,
534 (hashtable_equals_t)id_equals, 16),
535 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
536 );
537
538 if (base)
539 {
540 addr_bits = base->get_family(base) == AF_INET ? 32 : 128;
541 bits = max(0, min(bits, base->get_family(base) == AF_INET ? 32 : 128));
542 /* net bits -> host bits */
543 bits = addr_bits - bits;
544 if (bits > POOL_LIMIT)
545 {
546 bits = POOL_LIMIT;
547 DBG1(DBG_CFG, "virtual IP pool too large, limiting to %H/%d",
548 base, addr_bits - bits);
549 }
550 this->size = 1 << (bits);
551
552 if (this->size > 2)
553 { /* do not use first and last addresses of a block */
554 this->unused++;
555 this->size -= 2;
556 }
557 this->base = base->clone(base);
558 }
559
560 return &this->public;
561 }
562