Annotation of embedaddon/strongswan/src/libcharon/attributes/mem_pool.c, revision 1.1.1.2
1.1 misho 1: /*
2: * Copyright (C) 2010 Tobias Brunner
3: * Copyright (C) 2008-2010 Martin Willi
4: * HSR 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 <library.h>
20: #include <utils/debug.h>
21: #include <collections/hashtable.h>
22: #include <collections/array.h>
23: #include <threading/mutex.h>
24:
25: #define POOL_LIMIT (sizeof(u_int)*8 - 1)
26:
27: typedef struct private_mem_pool_t private_mem_pool_t;
28:
29: /**
30: * private data of mem_pool_t
31: */
32: struct private_mem_pool_t {
33: /**
34: * public interface
35: */
36: mem_pool_t public;
37:
38: /**
39: * name of the pool
40: */
41: char *name;
42:
43: /**
44: * base address of the pool
45: */
46: host_t *base;
47:
48: /**
49: * whether base is the network id of the subnet on which the pool is based
50: */
51: bool base_is_network_id;
52:
53: /**
54: * size of the pool
55: */
56: u_int size;
57:
58: /**
59: * next unused address
60: */
61: u_int unused;
62:
63: /**
64: * lease hashtable [identity => entry]
65: */
66: hashtable_t *leases;
67:
68: /**
69: * lock to safely access the pool
70: */
71: mutex_t *mutex;
72: };
73:
74: /**
75: * A unique lease address offset, with a hash of the peer host address
76: */
77: typedef struct {
78: /** lease, as offset */
79: u_int offset;
80: /** hash of remote address, to allow duplicates */
81: u_int hash;
82: } unique_lease_t;
83:
84: /**
85: * Lease entry.
86: */
87: typedef struct {
88: /* identity reference */
89: identification_t *id;
90: /* array of online leases, as unique_lease_t */
91: array_t *online;
92: /* array of offline leases, as u_int offset */
93: array_t *offline;
94: } entry_t;
95:
96: /**
97: * Create a new entry
98: */
99: static entry_t* entry_create(identification_t *id)
100: {
101: entry_t *entry;
102:
103: INIT(entry,
104: .id = id->clone(id),
105: .online = array_create(sizeof(unique_lease_t), 0),
106: .offline = array_create(sizeof(u_int), 0),
107: );
108: return entry;
109: }
110:
111: /**
112: * Destroy an entry
113: */
114: static void entry_destroy(entry_t *this)
115: {
116: this->id->destroy(this->id);
117: array_destroy(this->online);
118: array_destroy(this->offline);
119: free(this);
120: }
121:
122: /**
123: * hashtable hash function for identities
124: */
125: static u_int id_hash(identification_t *id)
126: {
127: return chunk_hash(id->get_encoding(id));
128: }
129:
130: /**
131: * hashtable equals function for identities
132: */
133: static bool id_equals(identification_t *a, identification_t *b)
134: {
135: return a->equals(a, b);
136: }
137:
138: /**
139: * convert a pool offset to an address
140: */
141: static host_t* offset2host(private_mem_pool_t *pool, int offset)
142: {
143: chunk_t addr;
144: host_t *host;
145: uint32_t *pos;
146:
147: offset--;
148: if (offset > pool->size)
149: {
150: return NULL;
151: }
152:
153: addr = chunk_clone(pool->base->get_address(pool->base));
154: if (pool->base->get_family(pool->base) == AF_INET6)
155: {
156: pos = (uint32_t*)(addr.ptr + 12);
157: }
158: else
159: {
160: pos = (uint32_t*)addr.ptr;
161: }
162: *pos = htonl(offset + ntohl(*pos));
163: host = host_create_from_chunk(pool->base->get_family(pool->base), addr, 0);
164: free(addr.ptr);
165: return host;
166: }
167:
168: /**
169: * convert a host to a pool offset
170: */
171: static int host2offset(private_mem_pool_t *pool, host_t *addr)
172: {
173: chunk_t host, base;
174: uint32_t hosti, basei;
175:
176: if (addr->get_family(addr) != pool->base->get_family(pool->base))
177: {
178: return -1;
179: }
180: host = addr->get_address(addr);
181: base = pool->base->get_address(pool->base);
182: if (addr->get_family(addr) == AF_INET6)
183: {
184: /* only look at last /32 block */
185: if (!memeq(host.ptr, base.ptr, 12))
186: {
187: return -1;
188: }
189: host = chunk_skip(host, 12);
190: base = chunk_skip(base, 12);
191: }
192: hosti = ntohl(*(uint32_t*)(host.ptr));
193: basei = ntohl(*(uint32_t*)(base.ptr));
194: if (hosti > basei + pool->size)
195: {
196: return -1;
197: }
198: return hosti - basei + 1;
199: }
200:
201: METHOD(mem_pool_t, get_name, const char*,
202: private_mem_pool_t *this)
203: {
204: return this->name;
205: }
206:
207: METHOD(mem_pool_t, get_base, host_t*,
208: private_mem_pool_t *this)
209: {
210: return this->base;
211: }
212:
213: METHOD(mem_pool_t, get_size, u_int,
214: private_mem_pool_t *this)
215: {
216: return this->size;
217: }
218:
219: METHOD(mem_pool_t, get_online, u_int,
220: private_mem_pool_t *this)
221: {
222: enumerator_t *enumerator;
223: entry_t *entry;
224: u_int count = 0;
225:
226: this->mutex->lock(this->mutex);
227: enumerator = this->leases->create_enumerator(this->leases);
228: while (enumerator->enumerate(enumerator, NULL, &entry))
229: {
230: count += array_count(entry->online);
231: }
232: enumerator->destroy(enumerator);
233: this->mutex->unlock(this->mutex);
234:
235: return count;
236: }
237:
238: METHOD(mem_pool_t, get_offline, u_int,
239: private_mem_pool_t *this)
240: {
241: enumerator_t *enumerator;
242: entry_t *entry;
243: u_int count = 0;
244:
245: this->mutex->lock(this->mutex);
246: enumerator = this->leases->create_enumerator(this->leases);
247: while (enumerator->enumerate(enumerator, NULL, &entry))
248: {
249: count += array_count(entry->offline);
250: }
251: enumerator->destroy(enumerator);
252: this->mutex->unlock(this->mutex);
253:
254: return count;
255: }
256:
257: /**
258: * Create a unique hash for a remote address
259: */
260: static u_int hash_addr(host_t *addr)
261: {
262: if (addr)
263: {
264: return chunk_hash_inc(addr->get_address(addr), addr->get_port(addr));
265: }
266: return 0;
267: }
268:
269: /**
270: * Get an existing lease for id
271: */
272: static int get_existing(private_mem_pool_t *this, identification_t *id,
273: host_t *requested, host_t *peer)
274: {
275: enumerator_t *enumerator;
276: unique_lease_t *lease, reassign;
277: u_int *current;
278: entry_t *entry;
279: int offset = 0;
280:
281: entry = this->leases->get(this->leases, id);
282: if (!entry)
283: {
284: return 0;
285: }
286:
287: /* check for a valid offline lease, refresh */
288: enumerator = array_create_enumerator(entry->offline);
289: if (enumerator->enumerate(enumerator, ¤t))
290: {
291: reassign.offset = offset = *current;
292: reassign.hash = hash_addr(peer);
293: array_insert(entry->online, ARRAY_TAIL, &reassign);
294: array_remove_at(entry->offline, enumerator);
295: }
296: enumerator->destroy(enumerator);
297: if (offset)
298: {
299: DBG1(DBG_CFG, "reassigning offline lease to '%Y'", id);
300: return offset;
301: }
302: if (!peer)
303: {
304: return 0;
305: }
306: /* check for a valid online lease to reassign */
307: enumerator = array_create_enumerator(entry->online);
308: while (enumerator->enumerate(enumerator, &lease))
309: {
1.1.1.2 ! misho 310: if (lease->hash == hash_addr(peer) &&
! 311: (requested->is_anyaddr(requested) ||
! 312: lease->offset == host2offset(this, requested)))
1.1 misho 313: {
314: offset = lease->offset;
315: /* add an additional "online" entry */
316: array_insert(entry->online, ARRAY_TAIL, lease);
317: break;
318: }
319: }
320: enumerator->destroy(enumerator);
321: if (offset)
322: {
323: DBG1(DBG_CFG, "reassigning online lease to '%Y'", id);
324: }
325: return offset;
326: }
327:
328: /**
329: * Get a new lease for id
330: */
331: static int get_new(private_mem_pool_t *this, identification_t *id, host_t *peer)
332: {
333: entry_t *entry;
334: unique_lease_t lease = {};
335:
336: if (this->unused < this->size)
337: {
338: entry = this->leases->get(this->leases, id);
339: if (!entry)
340: {
341: entry = entry_create(id);
342: this->leases->put(this->leases, entry->id, entry);
343: }
344: /* assigning offset, starting by 1 */
345: lease.offset = ++this->unused + (this->base_is_network_id ? 1 : 0);
346: lease.hash = hash_addr(peer);
347: array_insert(entry->online, ARRAY_TAIL, &lease);
348: DBG1(DBG_CFG, "assigning new lease to '%Y'", id);
349: }
350: return lease.offset;
351: }
352:
353: /**
354: * Get a reassigned lease for id in case the pool is full
355: */
356: static int get_reassigned(private_mem_pool_t *this, identification_t *id,
357: host_t *peer)
358: {
359: enumerator_t *enumerator;
360: entry_t *entry;
361: u_int current;
362: unique_lease_t lease = {};
363:
364: enumerator = this->leases->create_enumerator(this->leases);
365: while (enumerator->enumerate(enumerator, NULL, &entry))
366: {
367: if (array_remove(entry->offline, ARRAY_HEAD, ¤t))
368: {
369: lease.offset = current;
370: DBG1(DBG_CFG, "reassigning existing offline lease by '%Y' "
371: "to '%Y'", entry->id, id);
372: }
373: if (!array_count(entry->online) && !array_count(entry->offline))
374: {
375: this->leases->remove_at(this->leases, enumerator);
376: entry_destroy(entry);
377: }
378: if (lease.offset)
379: {
380: break;
381: }
382: }
383: enumerator->destroy(enumerator);
384:
385: if (lease.offset)
386: {
387: entry = this->leases->get(this->leases, id);
388: if (!entry)
389: {
390: entry = entry_create(id);
391: this->leases->put(this->leases, entry->id, entry);
392: }
393: lease.hash = hash_addr(peer);
394: array_insert(entry->online, ARRAY_TAIL, &lease);
395: }
396: return lease.offset;
397: }
398:
399: METHOD(mem_pool_t, acquire_address, host_t*,
400: private_mem_pool_t *this, identification_t *id, host_t *requested,
401: mem_pool_op_t operation, host_t *peer)
402: {
403: int offset = 0;
404:
405: /* if the pool is empty (e.g. in the %config case) we simply return the
406: * requested address */
407: if (this->size == 0)
408: {
409: return requested->clone(requested);
410: }
411:
412: if (requested->get_family(requested) !=
413: this->base->get_family(this->base))
414: {
415: return NULL;
416: }
417:
418: this->mutex->lock(this->mutex);
419: switch (operation)
420: {
421: case MEM_POOL_EXISTING:
422: offset = get_existing(this, id, requested, peer);
423: break;
424: case MEM_POOL_NEW:
425: offset = get_new(this, id, peer);
426: break;
427: case MEM_POOL_REASSIGN:
428: offset = get_reassigned(this, id, peer);
429: if (!offset)
430: {
431: DBG1(DBG_CFG, "pool '%s' is full, unable to assign address",
432: this->name);
433: }
434: break;
435: default:
436: break;
437: }
438: this->mutex->unlock(this->mutex);
439:
440: if (offset)
441: {
442: return offset2host(this, offset);
443: }
444: return NULL;
445: }
446:
447: METHOD(mem_pool_t, release_address, bool,
448: private_mem_pool_t *this, host_t *address, identification_t *id)
449: {
450: enumerator_t *enumerator;
451: bool found = FALSE, more = FALSE;
452: entry_t *entry;
453: u_int offset;
454: unique_lease_t *current;
455:
456: if (this->size != 0)
457: {
458: this->mutex->lock(this->mutex);
459: entry = this->leases->get(this->leases, id);
460: if (entry)
461: {
462: offset = host2offset(this, address);
463:
464: enumerator = array_create_enumerator(entry->online);
465: while (enumerator->enumerate(enumerator, ¤t))
466: {
467: if (current->offset == offset)
468: {
469: if (!found)
470: { /* remove the first entry only */
471: array_remove_at(entry->online, enumerator);
472: found = TRUE;
473: }
474: else
475: { /* but check for more entries */
476: more = TRUE;
477: break;
478: }
479: }
480: }
481: enumerator->destroy(enumerator);
482:
483: if (found && !more)
484: {
485: /* no tunnels are online anymore for this lease, make offline */
486: array_insert(entry->offline, ARRAY_TAIL, &offset);
487: DBG1(DBG_CFG, "lease %H by '%Y' went offline", address, id);
488: }
489: }
490: this->mutex->unlock(this->mutex);
491: }
492: return found;
493: }
494:
495: /**
496: * lease enumerator
497: */
498: typedef struct {
499: /** implemented enumerator interface */
500: enumerator_t public;
501: /** hash-table enumerator */
502: enumerator_t *entries;
503: /** online enumerator */
504: enumerator_t *online;
505: /** offline enumerator */
506: enumerator_t *offline;
507: /** enumerated pool */
508: private_mem_pool_t *pool;
509: /** currently enumerated entry */
510: entry_t *entry;
511: /** currently enumerated lease address */
512: host_t *addr;
513: } lease_enumerator_t;
514:
515: METHOD(enumerator_t, lease_enumerate, bool,
516: lease_enumerator_t *this, va_list args)
517: {
518: identification_t **id;
519: unique_lease_t *lease;
520: host_t **addr;
521: u_int *offset;
522: bool *online;
523:
524: VA_ARGS_VGET(args, id, addr, online);
525:
526: DESTROY_IF(this->addr);
527: this->addr = NULL;
528:
529: while (TRUE)
530: {
531: if (this->entry)
532: {
533: if (this->online->enumerate(this->online, &lease))
534: {
535: *id = this->entry->id;
536: *addr = this->addr = offset2host(this->pool, lease->offset);
537: *online = TRUE;
538: return TRUE;
539: }
540: if (this->offline->enumerate(this->offline, &offset))
541: {
542: *id = this->entry->id;
543: *addr = this->addr = offset2host(this->pool, *offset);
544: *online = FALSE;
545: return TRUE;
546: }
547: this->online->destroy(this->online);
548: this->offline->destroy(this->offline);
549: this->online = this->offline = NULL;
550: }
551: if (!this->entries->enumerate(this->entries, NULL, &this->entry))
552: {
553: return FALSE;
554: }
555: this->online = array_create_enumerator(this->entry->online);
556: this->offline = array_create_enumerator(this->entry->offline);
557: }
558: }
559:
560: METHOD(enumerator_t, lease_enumerator_destroy, void,
561: lease_enumerator_t *this)
562: {
563: DESTROY_IF(this->addr);
564: DESTROY_IF(this->online);
565: DESTROY_IF(this->offline);
566: this->entries->destroy(this->entries);
567: this->pool->mutex->unlock(this->pool->mutex);
568: free(this);
569: }
570:
571: METHOD(mem_pool_t, create_lease_enumerator, enumerator_t*,
572: private_mem_pool_t *this)
573: {
574: lease_enumerator_t *enumerator;
575:
576: this->mutex->lock(this->mutex);
577: INIT(enumerator,
578: .public = {
579: .enumerate = enumerator_enumerate_default,
580: .venumerate = _lease_enumerate,
581: .destroy = _lease_enumerator_destroy,
582: },
583: .pool = this,
584: .entries = this->leases->create_enumerator(this->leases),
585: );
586: return &enumerator->public;
587: }
588:
589: METHOD(mem_pool_t, destroy, void,
590: private_mem_pool_t *this)
591: {
592: enumerator_t *enumerator;
593: entry_t *entry;
594:
595: enumerator = this->leases->create_enumerator(this->leases);
596: while (enumerator->enumerate(enumerator, NULL, &entry))
597: {
598: entry_destroy(entry);
599: }
600: enumerator->destroy(enumerator);
601:
602: this->leases->destroy(this->leases);
603: this->mutex->destroy(this->mutex);
604: DESTROY_IF(this->base);
605: free(this->name);
606: free(this);
607: }
608:
609: /**
610: * Generic constructor
611: */
612: static private_mem_pool_t *create_generic(char *name)
613: {
614: private_mem_pool_t *this;
615:
616: INIT(this,
617: .public = {
618: .get_name = _get_name,
619: .get_base = _get_base,
620: .get_size = _get_size,
621: .get_online = _get_online,
622: .get_offline = _get_offline,
623: .acquire_address = _acquire_address,
624: .release_address = _release_address,
625: .create_lease_enumerator = _create_lease_enumerator,
626: .destroy = _destroy,
627: },
628: .name = strdup(name),
629: .leases = hashtable_create((hashtable_hash_t)id_hash,
630: (hashtable_equals_t)id_equals, 16),
631: .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
632: );
633:
634: return this;
635: }
636:
637: /**
638: * Check if the given host is the network ID of a subnet, that is, if hostbits
639: * are zero. Since we limit pools to 2^31 addresses we only have to check the
640: * last 4 bytes.
641: */
642: static u_int network_id_diff(host_t *host, int hostbits)
643: {
644: uint32_t last;
645: chunk_t addr;
646:
647: if (!hostbits)
648: {
649: return 0;
650: }
651: addr = host->get_address(host);
652: last = untoh32(addr.ptr + addr.len - sizeof(last));
653: hostbits = sizeof(last) * 8 - hostbits;
654: return (last << hostbits) >> hostbits;
655: }
656:
657: /**
658: * Described in header
659: */
660: mem_pool_t *mem_pool_create(char *name, host_t *base, int bits)
661: {
662: private_mem_pool_t *this;
663: u_int diff;
664: int addr_bits;
665:
666: this = create_generic(name);
667: if (base)
668: {
669: addr_bits = base->get_family(base) == AF_INET ? 32 : 128;
670: bits = max(0, min(bits, addr_bits));
671: /* net bits -> host bits */
672: bits = addr_bits - bits;
673: if (bits > POOL_LIMIT)
674: {
675: bits = POOL_LIMIT;
676: DBG1(DBG_CFG, "virtual IP pool too large, limiting to %H/%d",
677: base, addr_bits - bits);
678: }
679: this->size = 1 << bits;
680: this->base = base->clone(base);
681:
682: if (this->size > 2)
683: {
684: /* if base is the network id we later skip the first address,
685: * otherwise adjust the size to represent the actual number
686: * of assignable addresses */
687: diff = network_id_diff(base, bits);
688: if (!diff)
689: {
690: this->base_is_network_id = TRUE;
691: this->size--;
692: }
693: else
694: {
695: this->size -= diff;
696: }
697: /* skip the last address (broadcast) of the subnet */
698: this->size--;
699: }
700: else if (network_id_diff(base, bits))
701: { /* only serve the second address of the subnet */
702: this->size--;
703: }
704: }
705: return &this->public;
706: }
707:
708: /**
709: * Described in header
710: */
711: mem_pool_t *mem_pool_create_range(char *name, host_t *from, host_t *to)
712: {
713: private_mem_pool_t *this;
714: chunk_t fromaddr, toaddr;
715: uint32_t diff;
716:
717: fromaddr = from->get_address(from);
718: toaddr = to->get_address(to);
719:
720: if (from->get_family(from) != to->get_family(to) ||
721: fromaddr.len != toaddr.len || fromaddr.len < sizeof(diff) ||
722: memcmp(fromaddr.ptr, toaddr.ptr, toaddr.len) > 0)
723: {
724: DBG1(DBG_CFG, "invalid IP address range: %H-%H", from, to);
725: return NULL;
726: }
727: if (fromaddr.len > sizeof(diff) &&
728: !chunk_equals(chunk_create(fromaddr.ptr, fromaddr.len - sizeof(diff)),
729: chunk_create(toaddr.ptr, toaddr.len - sizeof(diff))))
730: {
731: DBG1(DBG_CFG, "IP address range too large: %H-%H", from, to);
732: return NULL;
733: }
734: this = create_generic(name);
735: this->base = from->clone(from);
736: diff = untoh32(toaddr.ptr + toaddr.len - sizeof(diff)) -
737: untoh32(fromaddr.ptr + fromaddr.len - sizeof(diff));
738: this->size = diff + 1;
739:
740: return &this->public;
741: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>