Replaced some DBG_LIB with more specific groups.
[strongswan.git] / src / libhydra / attributes / mem_pool.c
1 /*
2 * Copyright (C) 2010 Tobias Brunner
3 * Copyright (C) 2008 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 <threading/rwlock.h>
22
23 #define POOL_LIMIT (sizeof(uintptr_t)*8)
24
25 typedef struct private_mem_pool_t private_mem_pool_t;
26
27 /**
28 * private data of mem_pool_t
29 */
30 struct private_mem_pool_t {
31 /**
32 * public interface
33 */
34 mem_pool_t public;
35
36 /**
37 * name of the pool
38 */
39 char *name;
40
41 /**
42 * base address of the pool
43 */
44 host_t *base;
45
46 /**
47 * size of the pool
48 */
49 u_int size;
50
51 /**
52 * next unused address
53 */
54 u_int unused;
55
56 /**
57 * hashtable [identity => offset], for online leases
58 */
59 hashtable_t *online;
60
61 /**
62 * hashtable [identity => offset], for offline leases
63 */
64 hashtable_t *offline;
65
66 /**
67 * hashtable [identity => identity], handles identity references
68 */
69 hashtable_t *ids;
70
71 /**
72 * lock to safely access the pool
73 */
74 rwlock_t *lock;
75 };
76
77 /**
78 * hashtable hash function for identities
79 */
80 static u_int id_hash(identification_t *id)
81 {
82 return chunk_hash(id->get_encoding(id));
83 }
84
85 /**
86 * hashtable equals function for identities
87 */
88 static bool id_equals(identification_t *a, identification_t *b)
89 {
90 return a->equals(a, b);
91 }
92
93 /**
94 * convert a pool offset to an address
95 */
96 static host_t* offset2host(private_mem_pool_t *pool, int offset)
97 {
98 chunk_t addr;
99 host_t *host;
100 u_int32_t *pos;
101
102 offset--;
103 if (offset > pool->size)
104 {
105 return NULL;
106 }
107
108 addr = chunk_clone(pool->base->get_address(pool->base));
109 if (pool->base->get_family(pool->base) == AF_INET6)
110 {
111 pos = (u_int32_t*)(addr.ptr + 12);
112 }
113 else
114 {
115 pos = (u_int32_t*)addr.ptr;
116 }
117 *pos = htonl(offset + ntohl(*pos));
118 host = host_create_from_chunk(pool->base->get_family(pool->base), addr, 0);
119 free(addr.ptr);
120 return host;
121 }
122
123 /**
124 * convert a host to a pool offset
125 */
126 static int host2offset(private_mem_pool_t *pool, host_t *addr)
127 {
128 chunk_t host, base;
129 u_int32_t hosti, basei;
130
131 if (addr->get_family(addr) != pool->base->get_family(pool->base))
132 {
133 return -1;
134 }
135 host = addr->get_address(addr);
136 base = pool->base->get_address(pool->base);
137 if (addr->get_family(addr) == AF_INET6)
138 {
139 /* only look at last /32 block */
140 if (!memeq(host.ptr, base.ptr, 12))
141 {
142 return -1;
143 }
144 host = chunk_skip(host, 12);
145 base = chunk_skip(base, 12);
146 }
147 hosti = ntohl(*(u_int32_t*)(host.ptr));
148 basei = ntohl(*(u_int32_t*)(base.ptr));
149 if (hosti > basei + pool->size)
150 {
151 return -1;
152 }
153 return hosti - basei + 1;
154 }
155
156 METHOD(mem_pool_t, get_name, const char*,
157 private_mem_pool_t *this)
158 {
159 return this->name;
160 }
161
162 METHOD(mem_pool_t, get_size, u_int,
163 private_mem_pool_t *this)
164 {
165 return this->size;
166 }
167
168 METHOD(mem_pool_t, get_online, u_int,
169 private_mem_pool_t *this)
170 {
171 u_int count;
172 this->lock->read_lock(this->lock);
173 count = this->online->get_count(this->online);
174 this->lock->unlock(this->lock);
175 return count;
176 }
177
178 METHOD(mem_pool_t, get_offline, u_int,
179 private_mem_pool_t *this)
180 {
181 u_int count;
182 this->lock->read_lock(this->lock);
183 count = this->offline->get_count(this->offline);
184 this->lock->unlock(this->lock);
185 return count;
186 }
187
188 METHOD(mem_pool_t, acquire_address, host_t*,
189 private_mem_pool_t *this, identification_t *id, host_t *requested)
190 {
191 uintptr_t offset = 0;
192 enumerator_t *enumerator;
193 identification_t *old_id;
194
195 /* if the pool is empty (e.g. in the %config case) we simply return the
196 * requested address */
197 if (this->size == 0)
198 {
199 return requested->clone(requested);
200 }
201
202 if (!requested->is_anyaddr(requested) &&
203 requested->get_family(requested) !=
204 this->base->get_family(this->base))
205 {
206 DBG1(DBG_CFG, "IP pool address family mismatch");
207 return NULL;
208 }
209
210 this->lock->write_lock(this->lock);
211 while (TRUE)
212 {
213 /* check for a valid offline lease, refresh */
214 offset = (uintptr_t)this->offline->remove(this->offline, id);
215 if (offset)
216 {
217 id = this->ids->get(this->ids, id);
218 if (id)
219 {
220 DBG1(DBG_CFG, "reassigning offline lease to '%Y'", id);
221 this->online->put(this->online, id, (void*)offset);
222 break;
223 }
224 }
225
226 /* check for a valid online lease, reassign */
227 offset = (uintptr_t)this->online->get(this->online, id);
228 if (offset && offset == host2offset(this, requested))
229 {
230 DBG1(DBG_CFG, "reassigning online lease to '%Y'", id);
231 break;
232 }
233
234 if (this->unused < this->size)
235 {
236 /* assigning offset, starting by 1. Handling 0 in hashtable
237 * is difficult. */
238 offset = ++this->unused;
239 id = id->clone(id);
240 this->ids->put(this->ids, id, id);
241 this->online->put(this->online, id, (void*)offset);
242 DBG1(DBG_CFG, "assigning new lease to '%Y'", id);
243 break;
244 }
245
246 /* no more addresses, replace the first found offline lease */
247 enumerator = this->offline->create_enumerator(this->offline);
248 if (enumerator->enumerate(enumerator, &old_id, &offset))
249 {
250 offset = (uintptr_t)this->offline->remove(this->offline, old_id);
251 if (offset)
252 {
253 /* destroy reference to old ID */
254 old_id = this->ids->remove(this->ids, old_id);
255 DBG1(DBG_CFG, "reassigning existing offline lease by '%Y'"
256 " to '%Y'", old_id, id);
257 if (old_id)
258 {
259 old_id->destroy(old_id);
260 }
261 id = id->clone(id);
262 this->ids->put(this->ids, id, id);
263 this->online->put(this->online, id, (void*)offset);
264 enumerator->destroy(enumerator);
265 break;
266 }
267 }
268 enumerator->destroy(enumerator);
269
270 DBG1(DBG_CFG, "pool '%s' is full, unable to assign address",
271 this->name);
272 break;
273 }
274 this->lock->unlock(this->lock);
275
276 if (offset)
277 {
278 return offset2host(this, offset);
279 }
280 return NULL;
281 }
282
283 METHOD(mem_pool_t, release_address, bool,
284 private_mem_pool_t *this, host_t *address, identification_t *id)
285 {
286 bool found = FALSE;
287 if (this->size != 0)
288 {
289 uintptr_t offset;
290 this->lock->write_lock(this->lock);
291 offset = (uintptr_t)this->online->remove(this->online, id);
292 if (offset)
293 {
294 id = this->ids->get(this->ids, id);
295 if (id)
296 {
297 DBG1(DBG_CFG, "lease %H by '%Y' went offline", address, id);
298 this->offline->put(this->offline, id, (void*)offset);
299 found = TRUE;
300 }
301 }
302 this->lock->unlock(this->lock);
303 }
304 return found;
305 }
306
307 /**
308 * lease enumerator
309 */
310 typedef struct {
311 /** implemented enumerator interface */
312 enumerator_t public;
313 /** inner hash-table enumerator */
314 enumerator_t *inner;
315 /** enumerated pool */
316 private_mem_pool_t *pool;
317 /** currently enumerated lease address */
318 host_t *current;
319 } lease_enumerator_t;
320
321 METHOD(enumerator_t, lease_enumerate, bool,
322 lease_enumerator_t *this, identification_t **id_out, host_t **addr_out,
323 bool *online)
324 {
325 identification_t *id;
326 uintptr_t offset;
327
328 DESTROY_IF(this->current);
329 this->current = NULL;
330
331 if (this->inner->enumerate(this->inner, &id, NULL))
332 {
333 offset = (uintptr_t)this->pool->online->get(this->pool->online, id);
334 if (offset)
335 {
336 *id_out = id;
337 *addr_out = this->current = offset2host(this->pool, offset);
338 *online = TRUE;
339 return TRUE;
340 }
341 offset = (uintptr_t)this->pool->offline->get(this->pool->offline, id);
342 if (offset)
343 {
344 *id_out = id;
345 *addr_out = this->current = offset2host(this->pool, offset);
346 *online = FALSE;
347 return TRUE;
348 }
349 }
350 return FALSE;
351 }
352
353 METHOD(enumerator_t, lease_enumerator_destroy, void,
354 lease_enumerator_t *this)
355 {
356 DESTROY_IF(this->current);
357 this->inner->destroy(this->inner);
358 this->pool->lock->unlock(this->pool->lock);
359 free(this);
360 }
361
362 METHOD(mem_pool_t, create_lease_enumerator, enumerator_t*,
363 private_mem_pool_t *this)
364 {
365 lease_enumerator_t *enumerator;
366 this->lock->read_lock(this->lock);
367 INIT(enumerator,
368 .public = {
369 .enumerate = (void*)_lease_enumerate,
370 .destroy = (void*)_lease_enumerator_destroy,
371 },
372 .pool = this,
373 .inner = this->ids->create_enumerator(this->ids),
374 );
375 return &enumerator->public;
376 }
377
378 METHOD(mem_pool_t, destroy, void,
379 private_mem_pool_t *this)
380 {
381 enumerator_t *enumerator;
382 identification_t *id;
383
384 enumerator = this->ids->create_enumerator(this->ids);
385 while (enumerator->enumerate(enumerator, &id, NULL))
386 {
387 id->destroy(id);
388 }
389 enumerator->destroy(enumerator);
390
391 this->ids->destroy(this->ids);
392 this->online->destroy(this->online);
393 this->offline->destroy(this->offline);
394 this->lock->destroy(this->lock);
395 DESTROY_IF(this->base);
396 free(this->name);
397 free(this);
398 }
399
400 /**
401 * Described in header
402 */
403 mem_pool_t *mem_pool_create(char *name, host_t *base, int bits)
404 {
405 private_mem_pool_t *this;
406
407 INIT(this,
408 .public = {
409 .get_name = _get_name,
410 .get_size = _get_size,
411 .get_online = _get_online,
412 .get_offline = _get_offline,
413 .acquire_address = _acquire_address,
414 .release_address = _release_address,
415 .create_lease_enumerator = _create_lease_enumerator,
416 .destroy = _destroy,
417 },
418 .name = strdup(name),
419 .online = hashtable_create((hashtable_hash_t)id_hash,
420 (hashtable_equals_t)id_equals, 16),
421 .offline = hashtable_create((hashtable_hash_t)id_hash,
422 (hashtable_equals_t)id_equals, 16),
423 .ids = hashtable_create((hashtable_hash_t)id_hash,
424 (hashtable_equals_t)id_equals, 16),
425 .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
426 );
427
428 if (base)
429 {
430 int addr_bits = base->get_family(base) == AF_INET ? 32 : 128;
431 /* net bits -> host bits */
432 bits = addr_bits - bits;
433 if (bits > POOL_LIMIT)
434 {
435 bits = POOL_LIMIT;
436 DBG1(DBG_CFG, "virtual IP pool too large, limiting to %H/%d",
437 base, addr_bits - bits);
438 }
439 this->size = 1 << (bits);
440
441 if (this->size > 2)
442 { /* do not use first and last addresses of a block */
443 this->unused++;
444 this->size--;
445 }
446 this->base = base->clone(base);
447 }
448
449 return &this->public;
450 }
451