2 * Copyright (C) 2010 Tobias Brunner
3 * Copyright (C) 2008 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
20 #include <utils/hashtable.h>
21 #include <threading/rwlock.h>
23 #define POOL_LIMIT (sizeof(uintptr_t)*8)
25 typedef struct private_mem_pool_t private_mem_pool_t
;
28 * private data of mem_pool_t
30 struct private_mem_pool_t
{
42 * base address of the pool
57 * hashtable [identity => offset], for online leases
62 * hashtable [identity => offset], for offline leases
67 * hashtable [identity => identity], handles identity references
72 * lock to safely access the pool
78 * hashtable hash function for identities
80 static u_int
id_hash(identification_t
*id
)
82 return chunk_hash(id
->get_encoding(id
));
86 * hashtable equals function for identities
88 static bool id_equals(identification_t
*a
, identification_t
*b
)
90 return a
->equals(a
, b
);
94 * convert a pool offset to an address
96 static host_t
* offset2host(private_mem_pool_t
*pool
, int offset
)
103 if (offset
> pool
->size
)
108 addr
= chunk_clone(pool
->base
->get_address(pool
->base
));
109 if (pool
->base
->get_family(pool
->base
) == AF_INET6
)
111 pos
= (u_int32_t
*)(addr
.ptr
+ 12);
115 pos
= (u_int32_t
*)addr
.ptr
;
117 *pos
= htonl(offset
+ ntohl(*pos
));
118 host
= host_create_from_chunk(pool
->base
->get_family(pool
->base
), addr
, 0);
124 * convert a host to a pool offset
126 static int host2offset(private_mem_pool_t
*pool
, host_t
*addr
)
129 u_int32_t hosti
, basei
;
131 if (addr
->get_family(addr
) != pool
->base
->get_family(pool
->base
))
135 host
= addr
->get_address(addr
);
136 base
= pool
->base
->get_address(pool
->base
);
137 if (addr
->get_family(addr
) == AF_INET6
)
139 /* only look at last /32 block */
140 if (!memeq(host
.ptr
, base
.ptr
, 12))
144 host
= chunk_skip(host
, 12);
145 base
= chunk_skip(base
, 12);
147 hosti
= ntohl(*(u_int32_t
*)(host
.ptr
));
148 basei
= ntohl(*(u_int32_t
*)(base
.ptr
));
149 if (hosti
> basei
+ pool
->size
)
153 return hosti
- basei
+ 1;
156 METHOD(mem_pool_t
, get_name
, const char*,
157 private_mem_pool_t
*this)
162 METHOD(mem_pool_t
, get_size
, u_int
,
163 private_mem_pool_t
*this)
168 METHOD(mem_pool_t
, get_online
, u_int
,
169 private_mem_pool_t
*this)
172 this->lock
->read_lock(this->lock
);
173 count
= this->online
->get_count(this->online
);
174 this->lock
->unlock(this->lock
);
178 METHOD(mem_pool_t
, get_offline
, u_int
,
179 private_mem_pool_t
*this)
182 this->lock
->read_lock(this->lock
);
183 count
= this->offline
->get_count(this->offline
);
184 this->lock
->unlock(this->lock
);
188 METHOD(mem_pool_t
, acquire_address
, host_t
*,
189 private_mem_pool_t
*this, identification_t
*id
, host_t
*requested
)
191 uintptr_t offset
= 0;
192 enumerator_t
*enumerator
;
193 identification_t
*old_id
;
195 /* if the pool is empty (e.g. in the %config case) we simply return the
196 * requested address */
199 return requested
->clone(requested
);
202 if (!requested
->is_anyaddr(requested
) &&
203 requested
->get_family(requested
) !=
204 this->base
->get_family(this->base
))
206 DBG1(DBG_CFG
, "IP pool address family mismatch");
210 this->lock
->write_lock(this->lock
);
213 /* check for a valid offline lease, refresh */
214 offset
= (uintptr_t)this->offline
->remove(this->offline
, id
);
217 id
= this->ids
->get(this->ids
, id
);
220 DBG1(DBG_CFG
, "reassigning offline lease to '%Y'", id
);
221 this->online
->put(this->online
, id
, (void*)offset
);
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
))
230 DBG1(DBG_CFG
, "reassigning online lease to '%Y'", id
);
234 if (this->unused
< this->size
)
236 /* assigning offset, starting by 1. Handling 0 in hashtable
238 offset
= ++this->unused
;
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
);
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
))
250 offset
= (uintptr_t)this->offline
->remove(this->offline
, old_id
);
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
);
259 old_id
->destroy(old_id
);
262 this->ids
->put(this->ids
, id
, id
);
263 this->online
->put(this->online
, id
, (void*)offset
);
264 enumerator
->destroy(enumerator
);
268 enumerator
->destroy(enumerator
);
270 DBG1(DBG_CFG
, "pool '%s' is full, unable to assign address",
274 this->lock
->unlock(this->lock
);
278 return offset2host(this, offset
);
283 METHOD(mem_pool_t
, release_address
, bool,
284 private_mem_pool_t
*this, host_t
*address
, identification_t
*id
)
290 this->lock
->write_lock(this->lock
);
291 offset
= (uintptr_t)this->online
->remove(this->online
, id
);
294 id
= this->ids
->get(this->ids
, id
);
297 DBG1(DBG_CFG
, "lease %H by '%Y' went offline", address
, id
);
298 this->offline
->put(this->offline
, id
, (void*)offset
);
302 this->lock
->unlock(this->lock
);
311 /** implemented enumerator interface */
313 /** inner hash-table enumerator */
315 /** enumerated pool */
316 private_mem_pool_t
*pool
;
317 /** currently enumerated lease address */
319 } lease_enumerator_t
;
321 METHOD(enumerator_t
, lease_enumerate
, bool,
322 lease_enumerator_t
*this, identification_t
**id_out
, host_t
**addr_out
,
325 identification_t
*id
;
328 DESTROY_IF(this->current
);
329 this->current
= NULL
;
331 if (this->inner
->enumerate(this->inner
, &id
, NULL
))
333 offset
= (uintptr_t)this->pool
->online
->get(this->pool
->online
, id
);
337 *addr_out
= this->current
= offset2host(this->pool
, offset
);
341 offset
= (uintptr_t)this->pool
->offline
->get(this->pool
->offline
, id
);
345 *addr_out
= this->current
= offset2host(this->pool
, offset
);
353 METHOD(enumerator_t
, lease_enumerator_destroy
, void,
354 lease_enumerator_t
*this)
356 DESTROY_IF(this->current
);
357 this->inner
->destroy(this->inner
);
358 this->pool
->lock
->unlock(this->pool
->lock
);
362 METHOD(mem_pool_t
, create_lease_enumerator
, enumerator_t
*,
363 private_mem_pool_t
*this)
365 lease_enumerator_t
*enumerator
;
366 this->lock
->read_lock(this->lock
);
369 .enumerate
= (void*)_lease_enumerate
,
370 .destroy
= (void*)_lease_enumerator_destroy
,
373 .inner
= this->ids
->create_enumerator(this->ids
),
375 return &enumerator
->public;
378 METHOD(mem_pool_t
, destroy
, void,
379 private_mem_pool_t
*this)
381 enumerator_t
*enumerator
;
382 identification_t
*id
;
384 enumerator
= this->ids
->create_enumerator(this->ids
);
385 while (enumerator
->enumerate(enumerator
, &id
, NULL
))
389 enumerator
->destroy(enumerator
);
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
);
401 * Described in header
403 mem_pool_t
*mem_pool_create(char *name
, host_t
*base
, int bits
)
405 private_mem_pool_t
*this;
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
,
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
),
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
)
436 DBG1(DBG_CFG
, "virtual IP pool too large, limiting to %H/%d",
437 base
, addr_bits
- bits
);
439 this->size
= 1 << (bits
);
442 { /* do not use first and last addresses of a block */
446 this->base
= base
->clone(base
);
449 return &this->public;