ikev1 hybrid authentication does not need client certificates
[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 int 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;
298 int offset = 0;
299
300 enumerator = this->leases->create_enumerator(this->leases);
301 while (enumerator->enumerate(enumerator, NULL, &entry))
302 {
303 if (entry->offline->remove_first(entry->offline,
304 (void**)&current) == SUCCESS)
305 {
306 offset = current;
307 DBG1(DBG_CFG, "reassigning existing offline lease by '%Y'"
308 " to '%Y'", entry->id, id);
309 break;
310 }
311 }
312 enumerator->destroy(enumerator);
313
314 if (offset)
315 {
316 INIT(entry,
317 .id = id->clone(id),
318 .online = linked_list_create(),
319 .offline = linked_list_create(),
320 );
321 entry->online->insert_last(entry->online, (void*)offset);
322 this->leases->put(this->leases, entry->id, entry);
323 }
324 return offset;
325 }
326
327 METHOD(mem_pool_t, acquire_address, host_t*,
328 private_mem_pool_t *this, identification_t *id, host_t *requested,
329 mem_pool_op_t operation)
330 {
331 int offset = 0;
332
333 /* if the pool is empty (e.g. in the %config case) we simply return the
334 * requested address */
335 if (this->size == 0)
336 {
337 return requested->clone(requested);
338 }
339
340 if (requested->get_family(requested) !=
341 this->base->get_family(this->base))
342 {
343 return NULL;
344 }
345
346 this->mutex->lock(this->mutex);
347 switch (operation)
348 {
349 case MEM_POOL_EXISTING:
350 offset = get_existing(this, id, requested);
351 break;
352 case MEM_POOL_NEW:
353 offset = get_new(this, id);
354 break;
355 case MEM_POOL_REASSIGN:
356 offset = get_reassigned(this, id);
357 if (!offset)
358 {
359 DBG1(DBG_CFG, "pool '%s' is full, unable to assign address",
360 this->name);
361 }
362 break;
363 default:
364 break;
365 }
366 this->mutex->unlock(this->mutex);
367
368 if (offset)
369 {
370 return offset2host(this, offset);
371 }
372 return NULL;
373 }
374
375 METHOD(mem_pool_t, release_address, bool,
376 private_mem_pool_t *this, host_t *address, identification_t *id)
377 {
378 bool found = FALSE;
379 entry_t *entry;
380 uintptr_t offset;
381
382 if (this->size != 0)
383 {
384 this->mutex->lock(this->mutex);
385 entry = this->leases->get(this->leases, id);
386 if (entry)
387 {
388 offset = host2offset(this, address);
389 if (entry->online->remove(entry->online, (void*)offset, NULL) > 0)
390 {
391 DBG1(DBG_CFG, "lease %H by '%Y' went offline", address, id);
392 entry->offline->insert_last(entry->offline, (void*)offset);
393 found = TRUE;
394 }
395 }
396 this->mutex->unlock(this->mutex);
397 }
398 return found;
399 }
400
401 /**
402 * lease enumerator
403 */
404 typedef struct {
405 /** implemented enumerator interface */
406 enumerator_t public;
407 /** hash-table enumerator */
408 enumerator_t *entries;
409 /** online enumerator */
410 enumerator_t *online;
411 /** offline enumerator */
412 enumerator_t *offline;
413 /** enumerated pool */
414 private_mem_pool_t *pool;
415 /** currently enumerated entry */
416 entry_t *entry;
417 /** currently enumerated lease address */
418 host_t *addr;
419 } lease_enumerator_t;
420
421 METHOD(enumerator_t, lease_enumerate, bool,
422 lease_enumerator_t *this, identification_t **id, host_t **addr, bool *online)
423 {
424 uintptr_t offset;
425
426 DESTROY_IF(this->addr);
427 this->addr = NULL;
428
429 while (TRUE)
430 {
431 if (this->entry)
432 {
433 if (this->online->enumerate(this->online, (void**)&offset))
434 {
435 *id = this->entry->id;
436 *addr = this->addr = offset2host(this->pool, offset);
437 *online = TRUE;
438 return TRUE;
439 }
440 if (this->offline->enumerate(this->offline, (void**)&offset))
441 {
442 *id = this->entry->id;
443 *addr = this->addr = offset2host(this->pool, offset);
444 *online = FALSE;
445 return TRUE;
446 }
447 this->online->destroy(this->online);
448 this->offline->destroy(this->offline);
449 this->online = this->offline = NULL;
450 }
451 if (!this->entries->enumerate(this->entries, NULL, &this->entry))
452 {
453 return FALSE;
454 }
455 this->online = this->entry->online->create_enumerator(
456 this->entry->online);
457 this->offline = this->entry->offline->create_enumerator(
458 this->entry->offline);
459 }
460 }
461
462 METHOD(enumerator_t, lease_enumerator_destroy, void,
463 lease_enumerator_t *this)
464 {
465 DESTROY_IF(this->addr);
466 DESTROY_IF(this->online);
467 DESTROY_IF(this->offline);
468 this->entries->destroy(this->entries);
469 this->pool->mutex->unlock(this->pool->mutex);
470 free(this);
471 }
472
473 METHOD(mem_pool_t, create_lease_enumerator, enumerator_t*,
474 private_mem_pool_t *this)
475 {
476 lease_enumerator_t *enumerator;
477
478 this->mutex->lock(this->mutex);
479 INIT(enumerator,
480 .public = {
481 .enumerate = (void*)_lease_enumerate,
482 .destroy = _lease_enumerator_destroy,
483 },
484 .pool = this,
485 .entries = this->leases->create_enumerator(this->leases),
486 );
487 return &enumerator->public;
488 }
489
490 METHOD(mem_pool_t, destroy, void,
491 private_mem_pool_t *this)
492 {
493 enumerator_t *enumerator;
494 entry_t *entry;
495
496 enumerator = this->leases->create_enumerator(this->leases);
497 while (enumerator->enumerate(enumerator, NULL, &entry))
498 {
499 entry->id->destroy(entry->id);
500 entry->online->destroy(entry->online);
501 entry->offline->destroy(entry->offline);
502 free(entry);
503 }
504 enumerator->destroy(enumerator);
505
506 this->leases->destroy(this->leases);
507 this->mutex->destroy(this->mutex);
508 DESTROY_IF(this->base);
509 free(this->name);
510 free(this);
511 }
512
513 /**
514 * Described in header
515 */
516 mem_pool_t *mem_pool_create(char *name, host_t *base, int bits)
517 {
518 private_mem_pool_t *this;
519 int addr_bits;
520
521 INIT(this,
522 .public = {
523 .get_name = _get_name,
524 .get_base = _get_base,
525 .get_size = _get_size,
526 .get_online = _get_online,
527 .get_offline = _get_offline,
528 .acquire_address = _acquire_address,
529 .release_address = _release_address,
530 .create_lease_enumerator = _create_lease_enumerator,
531 .destroy = _destroy,
532 },
533 .name = strdup(name),
534 .leases = hashtable_create((hashtable_hash_t)id_hash,
535 (hashtable_equals_t)id_equals, 16),
536 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
537 );
538
539 if (base)
540 {
541 addr_bits = base->get_family(base) == AF_INET ? 32 : 128;
542 bits = max(0, min(bits, base->get_family(base) == AF_INET ? 32 : 128));
543 /* net bits -> host bits */
544 bits = addr_bits - bits;
545 if (bits > POOL_LIMIT)
546 {
547 bits = POOL_LIMIT;
548 DBG1(DBG_CFG, "virtual IP pool too large, limiting to %H/%d",
549 base, addr_bits - bits);
550 }
551 this->size = 1 << (bits);
552
553 if (this->size > 2)
554 { /* do not use first and last addresses of a block */
555 this->unused++;
556 this->size -= 2;
557 }
558 this->base = base->clone(base);
559 }
560
561 return &this->public;
562 }
563