2 * Copyright (C) 2010 Tobias Brunner
3 * Copyright (C) 2008-2010 Martin Willi
4 * Hochschule fuer Technik Rapperswil
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>.
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
21 #include <utils/debug.h>
22 #include <collections/hashtable.h>
23 #include <collections/array.h>
24 #include <threading/mutex.h>
26 #define POOL_LIMIT (sizeof(u_int)*8 - 1)
28 typedef struct private_mem_pool_t private_mem_pool_t
;
31 * private data of mem_pool_t
33 struct private_mem_pool_t
{
45 * base address of the pool
50 * whether base is the network id of the subnet on which the pool is based
52 bool base_is_network_id
;
65 * lease hashtable [identity => entry]
70 * lock to safely access the pool
75 * Do we reassign online leases to the same identity, if requested?
84 /* identitiy reference */
86 /* array of online leases, as u_int offset */
88 /* array of offline leases, as u_int offset */
95 static entry_t
* entry_create(identification_t
*id
)
101 .online
= array_create(sizeof(u_int
), 0),
102 .offline
= array_create(sizeof(u_int
), 0),
108 * hashtable hash function for identities
110 static u_int
id_hash(identification_t
*id
)
112 return chunk_hash(id
->get_encoding(id
));
116 * hashtable equals function for identities
118 static bool id_equals(identification_t
*a
, identification_t
*b
)
120 return a
->equals(a
, b
);
124 * convert a pool offset to an address
126 static host_t
* offset2host(private_mem_pool_t
*pool
, int offset
)
133 if (offset
> pool
->size
)
138 addr
= chunk_clone(pool
->base
->get_address(pool
->base
));
139 if (pool
->base
->get_family(pool
->base
) == AF_INET6
)
141 pos
= (u_int32_t
*)(addr
.ptr
+ 12);
145 pos
= (u_int32_t
*)addr
.ptr
;
147 *pos
= htonl(offset
+ ntohl(*pos
));
148 host
= host_create_from_chunk(pool
->base
->get_family(pool
->base
), addr
, 0);
154 * convert a host to a pool offset
156 static int host2offset(private_mem_pool_t
*pool
, host_t
*addr
)
159 u_int32_t hosti
, basei
;
161 if (addr
->get_family(addr
) != pool
->base
->get_family(pool
->base
))
165 host
= addr
->get_address(addr
);
166 base
= pool
->base
->get_address(pool
->base
);
167 if (addr
->get_family(addr
) == AF_INET6
)
169 /* only look at last /32 block */
170 if (!memeq(host
.ptr
, base
.ptr
, 12))
174 host
= chunk_skip(host
, 12);
175 base
= chunk_skip(base
, 12);
177 hosti
= ntohl(*(u_int32_t
*)(host
.ptr
));
178 basei
= ntohl(*(u_int32_t
*)(base
.ptr
));
179 if (hosti
> basei
+ pool
->size
)
183 return hosti
- basei
+ 1;
186 METHOD(mem_pool_t
, get_name
, const char*,
187 private_mem_pool_t
*this)
192 METHOD(mem_pool_t
, get_base
, host_t
*,
193 private_mem_pool_t
*this)
198 METHOD(mem_pool_t
, get_size
, u_int
,
199 private_mem_pool_t
*this)
204 METHOD(mem_pool_t
, get_online
, u_int
,
205 private_mem_pool_t
*this)
207 enumerator_t
*enumerator
;
211 this->mutex
->lock(this->mutex
);
212 enumerator
= this->leases
->create_enumerator(this->leases
);
213 while (enumerator
->enumerate(enumerator
, NULL
, &entry
))
215 count
+= array_count(entry
->online
);
217 enumerator
->destroy(enumerator
);
218 this->mutex
->unlock(this->mutex
);
223 METHOD(mem_pool_t
, get_offline
, u_int
,
224 private_mem_pool_t
*this)
226 enumerator_t
*enumerator
;
230 this->mutex
->lock(this->mutex
);
231 enumerator
= this->leases
->create_enumerator(this->leases
);
232 while (enumerator
->enumerate(enumerator
, NULL
, &entry
))
234 count
+= array_count(entry
->offline
);
236 enumerator
->destroy(enumerator
);
237 this->mutex
->unlock(this->mutex
);
243 * Get an existing lease for id
245 static int get_existing(private_mem_pool_t
*this, identification_t
*id
,
248 enumerator_t
*enumerator
;
253 entry
= this->leases
->get(this->leases
, id
);
259 /* check for a valid offline lease, refresh */
260 enumerator
= array_create_enumerator(entry
->offline
);
261 if (enumerator
->enumerate(enumerator
, ¤t
))
264 array_insert(entry
->online
, ARRAY_TAIL
, current
);
265 array_remove_at(entry
->offline
, enumerator
);
267 enumerator
->destroy(enumerator
);
270 DBG1(DBG_CFG
, "reassigning offline lease to '%Y'", id
);
273 if (!this->reassign_online
)
277 /* check for a valid online lease to reassign */
278 enumerator
= array_create_enumerator(entry
->online
);
279 while (enumerator
->enumerate(enumerator
, ¤t
))
281 if (*current
== host2offset(this, requested
))
284 /* add an additional "online" entry */
285 array_insert(entry
->online
, ARRAY_TAIL
, current
);
289 enumerator
->destroy(enumerator
);
292 DBG1(DBG_CFG
, "reassigning online lease to '%Y'", id
);
298 * Get a new lease for id
300 static int get_new(private_mem_pool_t
*this, identification_t
*id
)
305 if (this->unused
< this->size
)
307 entry
= this->leases
->get(this->leases
, id
);
310 entry
= entry_create(id
);
311 this->leases
->put(this->leases
, entry
->id
, entry
);
313 /* assigning offset, starting by 1 */
314 offset
= ++this->unused
+ (this->base_is_network_id
? 1 : 0);
315 array_insert(entry
->online
, ARRAY_TAIL
, &offset
);
316 DBG1(DBG_CFG
, "assigning new lease to '%Y'", id
);
322 * Get a reassigned lease for id in case the pool is full
324 static int get_reassigned(private_mem_pool_t
*this, identification_t
*id
)
326 enumerator_t
*enumerator
;
328 u_int current
, offset
= 0;
330 enumerator
= this->leases
->create_enumerator(this->leases
);
331 while (enumerator
->enumerate(enumerator
, NULL
, &entry
))
333 if (array_remove(entry
->offline
, ARRAY_HEAD
, ¤t
))
336 DBG1(DBG_CFG
, "reassigning existing offline lease by '%Y'"
337 " to '%Y'", entry
->id
, id
);
341 enumerator
->destroy(enumerator
);
345 entry
= this->leases
->get(this->leases
, id
);
348 entry
= entry_create(id
);
349 this->leases
->put(this->leases
, entry
->id
, entry
);
351 array_insert(entry
->online
, ARRAY_TAIL
, &offset
);
356 METHOD(mem_pool_t
, acquire_address
, host_t
*,
357 private_mem_pool_t
*this, identification_t
*id
, host_t
*requested
,
358 mem_pool_op_t operation
)
362 /* if the pool is empty (e.g. in the %config case) we simply return the
363 * requested address */
366 return requested
->clone(requested
);
369 if (requested
->get_family(requested
) !=
370 this->base
->get_family(this->base
))
375 this->mutex
->lock(this->mutex
);
378 case MEM_POOL_EXISTING
:
379 offset
= get_existing(this, id
, requested
);
382 offset
= get_new(this, id
);
384 case MEM_POOL_REASSIGN
:
385 offset
= get_reassigned(this, id
);
388 DBG1(DBG_CFG
, "pool '%s' is full, unable to assign address",
395 this->mutex
->unlock(this->mutex
);
399 return offset2host(this, offset
);
404 METHOD(mem_pool_t
, release_address
, bool,
405 private_mem_pool_t
*this, host_t
*address
, identification_t
*id
)
407 enumerator_t
*enumerator
;
408 bool found
= FALSE
, more
= FALSE
;
410 u_int offset
, *current
;
414 this->mutex
->lock(this->mutex
);
415 entry
= this->leases
->get(this->leases
, id
);
418 offset
= host2offset(this, address
);
420 enumerator
= array_create_enumerator(entry
->online
);
421 while (enumerator
->enumerate(enumerator
, ¤t
))
423 if (*current
== offset
)
426 { /* remove the first entry only */
427 array_remove_at(entry
->online
, enumerator
);
431 { /* but check for more entries */
437 enumerator
->destroy(enumerator
);
441 /* no tunnels are online anymore for this lease, make offline */
442 array_insert(entry
->offline
, ARRAY_TAIL
, &offset
);
443 DBG1(DBG_CFG
, "lease %H by '%Y' went offline", address
, id
);
446 this->mutex
->unlock(this->mutex
);
455 /** implemented enumerator interface */
457 /** hash-table enumerator */
458 enumerator_t
*entries
;
459 /** online enumerator */
460 enumerator_t
*online
;
461 /** offline enumerator */
462 enumerator_t
*offline
;
463 /** enumerated pool */
464 private_mem_pool_t
*pool
;
465 /** currently enumerated entry */
467 /** currently enumerated lease address */
469 } lease_enumerator_t
;
471 METHOD(enumerator_t
, lease_enumerate
, bool,
472 lease_enumerator_t
*this, identification_t
**id
, host_t
**addr
, bool *online
)
476 DESTROY_IF(this->addr
);
483 if (this->online
->enumerate(this->online
, &offset
))
485 *id
= this->entry
->id
;
486 *addr
= this->addr
= offset2host(this->pool
, *offset
);
490 if (this->offline
->enumerate(this->offline
, &offset
))
492 *id
= this->entry
->id
;
493 *addr
= this->addr
= offset2host(this->pool
, *offset
);
497 this->online
->destroy(this->online
);
498 this->offline
->destroy(this->offline
);
499 this->online
= this->offline
= NULL
;
501 if (!this->entries
->enumerate(this->entries
, NULL
, &this->entry
))
505 this->online
= array_create_enumerator(this->entry
->online
);
506 this->offline
= array_create_enumerator(this->entry
->offline
);
510 METHOD(enumerator_t
, lease_enumerator_destroy
, void,
511 lease_enumerator_t
*this)
513 DESTROY_IF(this->addr
);
514 DESTROY_IF(this->online
);
515 DESTROY_IF(this->offline
);
516 this->entries
->destroy(this->entries
);
517 this->pool
->mutex
->unlock(this->pool
->mutex
);
521 METHOD(mem_pool_t
, create_lease_enumerator
, enumerator_t
*,
522 private_mem_pool_t
*this)
524 lease_enumerator_t
*enumerator
;
526 this->mutex
->lock(this->mutex
);
529 .enumerate
= (void*)_lease_enumerate
,
530 .destroy
= _lease_enumerator_destroy
,
533 .entries
= this->leases
->create_enumerator(this->leases
),
535 return &enumerator
->public;
538 METHOD(mem_pool_t
, destroy
, void,
539 private_mem_pool_t
*this)
541 enumerator_t
*enumerator
;
544 enumerator
= this->leases
->create_enumerator(this->leases
);
545 while (enumerator
->enumerate(enumerator
, NULL
, &entry
))
547 entry
->id
->destroy(entry
->id
);
548 array_destroy(entry
->online
);
549 array_destroy(entry
->offline
);
552 enumerator
->destroy(enumerator
);
554 this->leases
->destroy(this->leases
);
555 this->mutex
->destroy(this->mutex
);
556 DESTROY_IF(this->base
);
562 * Generic constructor
564 static private_mem_pool_t
*create_generic(char *name
)
566 private_mem_pool_t
*this;
570 .get_name
= _get_name
,
571 .get_base
= _get_base
,
572 .get_size
= _get_size
,
573 .get_online
= _get_online
,
574 .get_offline
= _get_offline
,
575 .acquire_address
= _acquire_address
,
576 .release_address
= _release_address
,
577 .create_lease_enumerator
= _create_lease_enumerator
,
580 .name
= strdup(name
),
581 .leases
= hashtable_create((hashtable_hash_t
)id_hash
,
582 (hashtable_equals_t
)id_equals
, 16),
583 .mutex
= mutex_create(MUTEX_TYPE_DEFAULT
),
584 .reassign_online
= lib
->settings
->get_bool(lib
->settings
,
585 "%s.mem-pool.reassign_online", FALSE
, lib
->ns
),
592 * Check if the given host is the network ID of a subnet, that is, if hostbits
593 * are zero. Since we limit pools to 2^31 addresses we only have to check the
596 static u_int
network_id_diff(host_t
*host
, int hostbits
)
605 addr
= host
->get_address(host
);
606 last
= untoh32(addr
.ptr
+ addr
.len
- sizeof(last
));
607 hostbits
= sizeof(last
) * 8 - hostbits
;
608 return (last
<< hostbits
) >> hostbits
;
612 * Described in header
614 mem_pool_t
*mem_pool_create(char *name
, host_t
*base
, int bits
)
616 private_mem_pool_t
*this;
620 this = create_generic(name
);
623 addr_bits
= base
->get_family(base
) == AF_INET
? 32 : 128;
624 bits
= max(0, min(bits
, addr_bits
));
625 /* net bits -> host bits */
626 bits
= addr_bits
- bits
;
627 if (bits
> POOL_LIMIT
)
630 DBG1(DBG_CFG
, "virtual IP pool too large, limiting to %H/%d",
631 base
, addr_bits
- bits
);
633 this->size
= 1 << bits
;
634 this->base
= base
->clone(base
);
638 /* if base is the network id we later skip the first address,
639 * otherwise adjust the size to represent the actual number
640 * of assignable addresses */
641 diff
= network_id_diff(base
, bits
);
644 this->base_is_network_id
= TRUE
;
651 /* skip the last address (broadcast) of the subnet */
654 else if (network_id_diff(base
, bits
))
655 { /* only serve the second address of the subnet */
659 return &this->public;
663 * Described in header
665 mem_pool_t
*mem_pool_create_range(char *name
, host_t
*from
, host_t
*to
)
667 private_mem_pool_t
*this;
668 chunk_t fromaddr
, toaddr
;
671 fromaddr
= from
->get_address(from
);
672 toaddr
= to
->get_address(to
);
674 if (from
->get_family(from
) != to
->get_family(to
) ||
675 fromaddr
.len
!= toaddr
.len
|| fromaddr
.len
< sizeof(diff
) ||
676 memcmp(fromaddr
.ptr
, toaddr
.ptr
, toaddr
.len
) > 0)
678 DBG1(DBG_CFG
, "invalid IP address range: %H-%H", from
, to
);
681 if (fromaddr
.len
> sizeof(diff
) &&
682 !chunk_equals(chunk_create(fromaddr
.ptr
, fromaddr
.len
- sizeof(diff
)),
683 chunk_create(toaddr
.ptr
, toaddr
.len
- sizeof(diff
))))
685 DBG1(DBG_CFG
, "IP address range too large: %H-%H", from
, to
);
688 this = create_generic(name
);
689 this->base
= from
->clone(from
);
690 diff
= untoh32(toaddr
.ptr
+ toaddr
.len
- sizeof(diff
)) -
691 untoh32(fromaddr
.ptr
+ fromaddr
.len
- sizeof(diff
));
692 this->size
= diff
+ 1;
694 return &this->public;