]>
Commit | Line | Data |
---|---|---|
ac5fb545 TB |
1 | /* |
2 | * Copyright (C) 2010 Tobias Brunner | |
e82186fb | 3 | * Copyright (C) 2008-2010 Martin Willi |
1b671669 | 4 | * HSR Hochschule fuer Technik Rapperswil |
ac5fb545 TB |
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 | ||
7612a6e4 | 19 | #include <library.h> |
f05b4272 | 20 | #include <utils/debug.h> |
12642a68 | 21 | #include <collections/hashtable.h> |
c5d2d867 | 22 | #include <collections/array.h> |
e82186fb | 23 | #include <threading/mutex.h> |
ac5fb545 | 24 | |
292ee515 | 25 | #define POOL_LIMIT (sizeof(u_int)*8 - 1) |
ac5fb545 TB |
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 | ||
9d2a3554 TB |
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 | ||
ac5fb545 TB |
53 | /** |
54 | * size of the pool | |
55 | */ | |
56 | u_int size; | |
57 | ||
58 | /** | |
59 | * next unused address | |
60 | */ | |
61 | u_int unused; | |
62 | ||
63 | /** | |
e82186fb | 64 | * lease hashtable [identity => entry] |
ac5fb545 | 65 | */ |
e82186fb | 66 | hashtable_t *leases; |
fb111e55 TB |
67 | |
68 | /** | |
69 | * lock to safely access the pool | |
70 | */ | |
e82186fb | 71 | mutex_t *mutex; |
ac5fb545 TB |
72 | }; |
73 | ||
22e6a06b MW |
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 | ||
e82186fb MW |
84 | /** |
85 | * Lease entry. | |
86 | */ | |
87 | typedef struct { | |
b3ab7a48 | 88 | /* identity reference */ |
e82186fb | 89 | identification_t *id; |
22e6a06b | 90 | /* array of online leases, as unique_lease_t */ |
c5d2d867 MW |
91 | array_t *online; |
92 | /* array of offline leases, as u_int offset */ | |
93 | array_t *offline; | |
e82186fb MW |
94 | } entry_t; |
95 | ||
c5d2d867 MW |
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), | |
22e6a06b | 105 | .online = array_create(sizeof(unique_lease_t), 0), |
c5d2d867 MW |
106 | .offline = array_create(sizeof(u_int), 0), |
107 | ); | |
108 | return entry; | |
109 | } | |
110 | ||
7d02f8db TB |
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 | ||
ac5fb545 TB |
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; | |
b12c53ce | 145 | uint32_t *pos; |
ac5fb545 TB |
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 | { | |
b12c53ce | 156 | pos = (uint32_t*)(addr.ptr + 12); |
ac5fb545 TB |
157 | } |
158 | else | |
159 | { | |
b12c53ce | 160 | pos = (uint32_t*)addr.ptr; |
ac5fb545 TB |
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; | |
b12c53ce | 174 | uint32_t hosti, basei; |
ac5fb545 TB |
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 | } | |
b12c53ce AS |
192 | hosti = ntohl(*(uint32_t*)(host.ptr)); |
193 | basei = ntohl(*(uint32_t*)(base.ptr)); | |
ac5fb545 TB |
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*, | |
e82186fb | 202 | private_mem_pool_t *this) |
ac5fb545 TB |
203 | { |
204 | return this->name; | |
205 | } | |
206 | ||
d8eec395 MW |
207 | METHOD(mem_pool_t, get_base, host_t*, |
208 | private_mem_pool_t *this) | |
209 | { | |
210 | return this->base; | |
211 | } | |
212 | ||
ac5fb545 | 213 | METHOD(mem_pool_t, get_size, u_int, |
e82186fb | 214 | private_mem_pool_t *this) |
ac5fb545 TB |
215 | { |
216 | return this->size; | |
217 | } | |
218 | ||
219 | METHOD(mem_pool_t, get_online, u_int, | |
e82186fb | 220 | private_mem_pool_t *this) |
ac5fb545 | 221 | { |
e82186fb MW |
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 | { | |
c5d2d867 | 230 | count += array_count(entry->online); |
e82186fb MW |
231 | } |
232 | enumerator->destroy(enumerator); | |
233 | this->mutex->unlock(this->mutex); | |
234 | ||
fb111e55 | 235 | return count; |
ac5fb545 TB |
236 | } |
237 | ||
238 | METHOD(mem_pool_t, get_offline, u_int, | |
e82186fb | 239 | private_mem_pool_t *this) |
ac5fb545 | 240 | { |
e82186fb MW |
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 | { | |
c5d2d867 | 249 | count += array_count(entry->offline); |
e82186fb MW |
250 | } |
251 | enumerator->destroy(enumerator); | |
252 | this->mutex->unlock(this->mutex); | |
253 | ||
fb111e55 | 254 | return count; |
ac5fb545 TB |
255 | } |
256 | ||
22e6a06b MW |
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 | ||
1e04488f MW |
269 | /** |
270 | * Get an existing lease for id | |
271 | */ | |
272 | static int get_existing(private_mem_pool_t *this, identification_t *id, | |
22e6a06b | 273 | host_t *requested, host_t *peer) |
ac5fb545 | 274 | { |
ac5fb545 | 275 | enumerator_t *enumerator; |
22e6a06b | 276 | unique_lease_t *lease, reassign; |
c5d2d867 | 277 | u_int *current; |
1e04488f MW |
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 */ | |
c5d2d867 | 288 | enumerator = array_create_enumerator(entry->offline); |
1e04488f MW |
289 | if (enumerator->enumerate(enumerator, ¤t)) |
290 | { | |
22e6a06b MW |
291 | reassign.offset = offset = *current; |
292 | reassign.hash = hash_addr(peer); | |
293 | array_insert(entry->online, ARRAY_TAIL, &reassign); | |
c5d2d867 | 294 | array_remove_at(entry->offline, enumerator); |
1e04488f MW |
295 | } |
296 | enumerator->destroy(enumerator); | |
297 | if (offset) | |
298 | { | |
299 | DBG1(DBG_CFG, "reassigning offline lease to '%Y'", id); | |
300 | return offset; | |
301 | } | |
22e6a06b | 302 | if (!peer) |
7612a6e4 MW |
303 | { |
304 | return 0; | |
305 | } | |
1e04488f | 306 | /* check for a valid online lease to reassign */ |
c5d2d867 | 307 | enumerator = array_create_enumerator(entry->online); |
22e6a06b | 308 | while (enumerator->enumerate(enumerator, &lease)) |
1e04488f | 309 | { |
22e6a06b MW |
310 | if (lease->offset == host2offset(this, requested) && |
311 | lease->hash == hash_addr(peer)) | |
1e04488f | 312 | { |
22e6a06b | 313 | offset = lease->offset; |
d882880e | 314 | /* add an additional "online" entry */ |
22e6a06b | 315 | array_insert(entry->online, ARRAY_TAIL, lease); |
1e04488f MW |
316 | break; |
317 | } | |
318 | } | |
319 | enumerator->destroy(enumerator); | |
320 | if (offset) | |
321 | { | |
322 | DBG1(DBG_CFG, "reassigning online lease to '%Y'", id); | |
323 | } | |
324 | return offset; | |
325 | } | |
326 | ||
327 | /** | |
328 | * Get a new lease for id | |
329 | */ | |
22e6a06b | 330 | static int get_new(private_mem_pool_t *this, identification_t *id, host_t *peer) |
1e04488f MW |
331 | { |
332 | entry_t *entry; | |
22e6a06b | 333 | unique_lease_t lease = {}; |
1e04488f MW |
334 | |
335 | if (this->unused < this->size) | |
336 | { | |
f0a2fef8 MW |
337 | entry = this->leases->get(this->leases, id); |
338 | if (!entry) | |
339 | { | |
c5d2d867 | 340 | entry = entry_create(id); |
f0a2fef8 MW |
341 | this->leases->put(this->leases, entry->id, entry); |
342 | } | |
1e04488f | 343 | /* assigning offset, starting by 1 */ |
22e6a06b MW |
344 | lease.offset = ++this->unused + (this->base_is_network_id ? 1 : 0); |
345 | lease.hash = hash_addr(peer); | |
346 | array_insert(entry->online, ARRAY_TAIL, &lease); | |
1e04488f MW |
347 | DBG1(DBG_CFG, "assigning new lease to '%Y'", id); |
348 | } | |
22e6a06b | 349 | return lease.offset; |
1e04488f MW |
350 | } |
351 | ||
352 | /** | |
353 | * Get a reassigned lease for id in case the pool is full | |
354 | */ | |
22e6a06b MW |
355 | static int get_reassigned(private_mem_pool_t *this, identification_t *id, |
356 | host_t *peer) | |
1e04488f MW |
357 | { |
358 | enumerator_t *enumerator; | |
359 | entry_t *entry; | |
22e6a06b MW |
360 | u_int current; |
361 | unique_lease_t lease = {}; | |
1e04488f MW |
362 | |
363 | enumerator = this->leases->create_enumerator(this->leases); | |
364 | while (enumerator->enumerate(enumerator, NULL, &entry)) | |
365 | { | |
c5d2d867 | 366 | if (array_remove(entry->offline, ARRAY_HEAD, ¤t)) |
1e04488f | 367 | { |
22e6a06b | 368 | lease.offset = current; |
7d02f8db TB |
369 | DBG1(DBG_CFG, "reassigning existing offline lease by '%Y' " |
370 | "to '%Y'", entry->id, id); | |
371 | } | |
372 | if (!array_count(entry->online) && !array_count(entry->offline)) | |
373 | { | |
374 | this->leases->remove_at(this->leases, enumerator); | |
375 | entry_destroy(entry); | |
376 | } | |
377 | if (lease.offset) | |
378 | { | |
1e04488f MW |
379 | break; |
380 | } | |
381 | } | |
382 | enumerator->destroy(enumerator); | |
383 | ||
22e6a06b | 384 | if (lease.offset) |
1e04488f | 385 | { |
8ab1b29d TB |
386 | entry = this->leases->get(this->leases, id); |
387 | if (!entry) | |
388 | { | |
389 | entry = entry_create(id); | |
390 | this->leases->put(this->leases, entry->id, entry); | |
391 | } | |
22e6a06b MW |
392 | lease.hash = hash_addr(peer); |
393 | array_insert(entry->online, ARRAY_TAIL, &lease); | |
1e04488f | 394 | } |
22e6a06b | 395 | return lease.offset; |
1e04488f MW |
396 | } |
397 | ||
398 | METHOD(mem_pool_t, acquire_address, host_t*, | |
399 | private_mem_pool_t *this, identification_t *id, host_t *requested, | |
22e6a06b | 400 | mem_pool_op_t operation, host_t *peer) |
1e04488f MW |
401 | { |
402 | int offset = 0; | |
ac5fb545 TB |
403 | |
404 | /* if the pool is empty (e.g. in the %config case) we simply return the | |
405 | * requested address */ | |
406 | if (this->size == 0) | |
407 | { | |
408 | return requested->clone(requested); | |
409 | } | |
410 | ||
40e90898 | 411 | if (requested->get_family(requested) != |
fb111e55 | 412 | this->base->get_family(this->base)) |
ac5fb545 | 413 | { |
fb111e55 TB |
414 | return NULL; |
415 | } | |
ac5fb545 | 416 | |
e82186fb | 417 | this->mutex->lock(this->mutex); |
1e04488f | 418 | switch (operation) |
fb111e55 | 419 | { |
1e04488f | 420 | case MEM_POOL_EXISTING: |
22e6a06b | 421 | offset = get_existing(this, id, requested, peer); |
ac5fb545 | 422 | break; |
1e04488f | 423 | case MEM_POOL_NEW: |
22e6a06b | 424 | offset = get_new(this, id, peer); |
1e04488f MW |
425 | break; |
426 | case MEM_POOL_REASSIGN: | |
22e6a06b | 427 | offset = get_reassigned(this, id, peer); |
1e04488f | 428 | if (!offset) |
ac5fb545 | 429 | { |
1e04488f MW |
430 | DBG1(DBG_CFG, "pool '%s' is full, unable to assign address", |
431 | this->name); | |
ac5fb545 | 432 | } |
1e04488f MW |
433 | break; |
434 | default: | |
435 | break; | |
ac5fb545 | 436 | } |
e82186fb | 437 | this->mutex->unlock(this->mutex); |
ac5fb545 TB |
438 | |
439 | if (offset) | |
440 | { | |
441 | return offset2host(this, offset); | |
442 | } | |
443 | return NULL; | |
444 | } | |
445 | ||
446 | METHOD(mem_pool_t, release_address, bool, | |
e82186fb | 447 | private_mem_pool_t *this, host_t *address, identification_t *id) |
ac5fb545 | 448 | { |
d882880e MW |
449 | enumerator_t *enumerator; |
450 | bool found = FALSE, more = FALSE; | |
e82186fb | 451 | entry_t *entry; |
22e6a06b MW |
452 | u_int offset; |
453 | unique_lease_t *current; | |
e82186fb | 454 | |
ac5fb545 TB |
455 | if (this->size != 0) |
456 | { | |
e82186fb MW |
457 | this->mutex->lock(this->mutex); |
458 | entry = this->leases->get(this->leases, id); | |
459 | if (entry) | |
ac5fb545 | 460 | { |
e82186fb | 461 | offset = host2offset(this, address); |
d882880e | 462 | |
c5d2d867 | 463 | enumerator = array_create_enumerator(entry->online); |
d882880e | 464 | while (enumerator->enumerate(enumerator, ¤t)) |
ac5fb545 | 465 | { |
22e6a06b | 466 | if (current->offset == offset) |
d882880e MW |
467 | { |
468 | if (!found) | |
469 | { /* remove the first entry only */ | |
c5d2d867 | 470 | array_remove_at(entry->online, enumerator); |
d882880e MW |
471 | found = TRUE; |
472 | } | |
473 | else | |
474 | { /* but check for more entries */ | |
475 | more = TRUE; | |
476 | break; | |
477 | } | |
478 | } | |
479 | } | |
480 | enumerator->destroy(enumerator); | |
481 | ||
482 | if (found && !more) | |
483 | { | |
484 | /* no tunnels are online anymore for this lease, make offline */ | |
c5d2d867 | 485 | array_insert(entry->offline, ARRAY_TAIL, &offset); |
d882880e | 486 | DBG1(DBG_CFG, "lease %H by '%Y' went offline", address, id); |
ac5fb545 TB |
487 | } |
488 | } | |
e82186fb | 489 | this->mutex->unlock(this->mutex); |
ac5fb545 | 490 | } |
fb111e55 | 491 | return found; |
ac5fb545 TB |
492 | } |
493 | ||
494 | /** | |
495 | * lease enumerator | |
496 | */ | |
497 | typedef struct { | |
498 | /** implemented enumerator interface */ | |
499 | enumerator_t public; | |
e82186fb MW |
500 | /** hash-table enumerator */ |
501 | enumerator_t *entries; | |
502 | /** online enumerator */ | |
503 | enumerator_t *online; | |
504 | /** offline enumerator */ | |
505 | enumerator_t *offline; | |
ac5fb545 TB |
506 | /** enumerated pool */ |
507 | private_mem_pool_t *pool; | |
e82186fb MW |
508 | /** currently enumerated entry */ |
509 | entry_t *entry; | |
ac5fb545 | 510 | /** currently enumerated lease address */ |
e82186fb | 511 | host_t *addr; |
ac5fb545 TB |
512 | } lease_enumerator_t; |
513 | ||
514 | METHOD(enumerator_t, lease_enumerate, bool, | |
95a63bf2 | 515 | lease_enumerator_t *this, va_list args) |
ac5fb545 | 516 | { |
95a63bf2 | 517 | identification_t **id; |
22e6a06b | 518 | unique_lease_t *lease; |
95a63bf2 TB |
519 | host_t **addr; |
520 | u_int *offset; | |
521 | bool *online; | |
522 | ||
523 | VA_ARGS_VGET(args, id, addr, online); | |
ac5fb545 | 524 | |
e82186fb MW |
525 | DESTROY_IF(this->addr); |
526 | this->addr = NULL; | |
ac5fb545 | 527 | |
e82186fb | 528 | while (TRUE) |
ac5fb545 | 529 | { |
e82186fb | 530 | if (this->entry) |
ac5fb545 | 531 | { |
22e6a06b | 532 | if (this->online->enumerate(this->online, &lease)) |
e82186fb MW |
533 | { |
534 | *id = this->entry->id; | |
22e6a06b | 535 | *addr = this->addr = offset2host(this->pool, lease->offset); |
e82186fb MW |
536 | *online = TRUE; |
537 | return TRUE; | |
538 | } | |
c5d2d867 | 539 | if (this->offline->enumerate(this->offline, &offset)) |
e82186fb MW |
540 | { |
541 | *id = this->entry->id; | |
c5d2d867 | 542 | *addr = this->addr = offset2host(this->pool, *offset); |
e82186fb MW |
543 | *online = FALSE; |
544 | return TRUE; | |
545 | } | |
546 | this->online->destroy(this->online); | |
547 | this->offline->destroy(this->offline); | |
548 | this->online = this->offline = NULL; | |
ac5fb545 | 549 | } |
e82186fb | 550 | if (!this->entries->enumerate(this->entries, NULL, &this->entry)) |
ac5fb545 | 551 | { |
e82186fb | 552 | return FALSE; |
ac5fb545 | 553 | } |
c5d2d867 MW |
554 | this->online = array_create_enumerator(this->entry->online); |
555 | this->offline = array_create_enumerator(this->entry->offline); | |
ac5fb545 | 556 | } |
ac5fb545 TB |
557 | } |
558 | ||
559 | METHOD(enumerator_t, lease_enumerator_destroy, void, | |
e82186fb | 560 | lease_enumerator_t *this) |
ac5fb545 | 561 | { |
e82186fb MW |
562 | DESTROY_IF(this->addr); |
563 | DESTROY_IF(this->online); | |
564 | DESTROY_IF(this->offline); | |
565 | this->entries->destroy(this->entries); | |
566 | this->pool->mutex->unlock(this->pool->mutex); | |
ac5fb545 TB |
567 | free(this); |
568 | } | |
569 | ||
570 | METHOD(mem_pool_t, create_lease_enumerator, enumerator_t*, | |
571 | private_mem_pool_t *this) | |
572 | { | |
573 | lease_enumerator_t *enumerator; | |
e82186fb MW |
574 | |
575 | this->mutex->lock(this->mutex); | |
ac5fb545 TB |
576 | INIT(enumerator, |
577 | .public = { | |
95a63bf2 TB |
578 | .enumerate = enumerator_enumerate_default, |
579 | .venumerate = _lease_enumerate, | |
e82186fb | 580 | .destroy = _lease_enumerator_destroy, |
ac5fb545 TB |
581 | }, |
582 | .pool = this, | |
e82186fb | 583 | .entries = this->leases->create_enumerator(this->leases), |
ac5fb545 | 584 | ); |
ac5fb545 TB |
585 | return &enumerator->public; |
586 | } | |
587 | ||
588 | METHOD(mem_pool_t, destroy, void, | |
e82186fb | 589 | private_mem_pool_t *this) |
ac5fb545 TB |
590 | { |
591 | enumerator_t *enumerator; | |
e82186fb | 592 | entry_t *entry; |
ac5fb545 | 593 | |
e82186fb MW |
594 | enumerator = this->leases->create_enumerator(this->leases); |
595 | while (enumerator->enumerate(enumerator, NULL, &entry)) | |
ac5fb545 | 596 | { |
7d02f8db | 597 | entry_destroy(entry); |
ac5fb545 TB |
598 | } |
599 | enumerator->destroy(enumerator); | |
600 | ||
e82186fb MW |
601 | this->leases->destroy(this->leases); |
602 | this->mutex->destroy(this->mutex); | |
ac5fb545 TB |
603 | DESTROY_IF(this->base); |
604 | free(this->name); | |
605 | free(this); | |
606 | } | |
607 | ||
608 | /** | |
0897cda3 | 609 | * Generic constructor |
ac5fb545 | 610 | */ |
0897cda3 | 611 | static private_mem_pool_t *create_generic(char *name) |
ac5fb545 TB |
612 | { |
613 | private_mem_pool_t *this; | |
614 | ||
615 | INIT(this, | |
616 | .public = { | |
617 | .get_name = _get_name, | |
d8eec395 | 618 | .get_base = _get_base, |
ac5fb545 TB |
619 | .get_size = _get_size, |
620 | .get_online = _get_online, | |
621 | .get_offline = _get_offline, | |
622 | .acquire_address = _acquire_address, | |
623 | .release_address = _release_address, | |
624 | .create_lease_enumerator = _create_lease_enumerator, | |
625 | .destroy = _destroy, | |
626 | }, | |
627 | .name = strdup(name), | |
e82186fb | 628 | .leases = hashtable_create((hashtable_hash_t)id_hash, |
ac5fb545 | 629 | (hashtable_equals_t)id_equals, 16), |
e82186fb | 630 | .mutex = mutex_create(MUTEX_TYPE_DEFAULT), |
ac5fb545 TB |
631 | ); |
632 | ||
0897cda3 MW |
633 | return this; |
634 | } | |
635 | ||
9d2a3554 TB |
636 | /** |
637 | * Check if the given host is the network ID of a subnet, that is, if hostbits | |
638 | * are zero. Since we limit pools to 2^31 addresses we only have to check the | |
639 | * last 4 bytes. | |
640 | */ | |
641 | static u_int network_id_diff(host_t *host, int hostbits) | |
642 | { | |
b12c53ce | 643 | uint32_t last; |
9d2a3554 TB |
644 | chunk_t addr; |
645 | ||
646 | if (!hostbits) | |
647 | { | |
648 | return 0; | |
649 | } | |
650 | addr = host->get_address(host); | |
651 | last = untoh32(addr.ptr + addr.len - sizeof(last)); | |
652 | hostbits = sizeof(last) * 8 - hostbits; | |
653 | return (last << hostbits) >> hostbits; | |
654 | } | |
655 | ||
0897cda3 MW |
656 | /** |
657 | * Described in header | |
658 | */ | |
659 | mem_pool_t *mem_pool_create(char *name, host_t *base, int bits) | |
660 | { | |
661 | private_mem_pool_t *this; | |
9d2a3554 | 662 | u_int diff; |
0897cda3 MW |
663 | int addr_bits; |
664 | ||
665 | this = create_generic(name); | |
ac5fb545 TB |
666 | if (base) |
667 | { | |
e82186fb | 668 | addr_bits = base->get_family(base) == AF_INET ? 32 : 128; |
9d2a3554 | 669 | bits = max(0, min(bits, addr_bits)); |
ac5fb545 TB |
670 | /* net bits -> host bits */ |
671 | bits = addr_bits - bits; | |
672 | if (bits > POOL_LIMIT) | |
673 | { | |
674 | bits = POOL_LIMIT; | |
894936ce | 675 | DBG1(DBG_CFG, "virtual IP pool too large, limiting to %H/%d", |
ac5fb545 TB |
676 | base, addr_bits - bits); |
677 | } | |
292ee515 | 678 | this->size = 1 << bits; |
9d2a3554 | 679 | this->base = base->clone(base); |
ac5fb545 TB |
680 | |
681 | if (this->size > 2) | |
9d2a3554 TB |
682 | { |
683 | /* if base is the network id we later skip the first address, | |
684 | * otherwise adjust the size to represent the actual number | |
685 | * of assignable addresses */ | |
686 | diff = network_id_diff(base, bits); | |
687 | if (!diff) | |
688 | { | |
689 | this->base_is_network_id = TRUE; | |
690 | this->size--; | |
691 | } | |
692 | else | |
693 | { | |
694 | this->size -= diff; | |
695 | } | |
696 | /* skip the last address (broadcast) of the subnet */ | |
697 | this->size--; | |
698 | } | |
699 | else if (network_id_diff(base, bits)) | |
700 | { /* only serve the second address of the subnet */ | |
701 | this->size--; | |
ac5fb545 | 702 | } |
ac5fb545 | 703 | } |
ac5fb545 TB |
704 | return &this->public; |
705 | } | |
706 | ||
0897cda3 MW |
707 | /** |
708 | * Described in header | |
709 | */ | |
710 | mem_pool_t *mem_pool_create_range(char *name, host_t *from, host_t *to) | |
711 | { | |
712 | private_mem_pool_t *this; | |
713 | chunk_t fromaddr, toaddr; | |
b12c53ce | 714 | uint32_t diff; |
0897cda3 MW |
715 | |
716 | fromaddr = from->get_address(from); | |
717 | toaddr = to->get_address(to); | |
718 | ||
719 | if (from->get_family(from) != to->get_family(to) || | |
720 | fromaddr.len != toaddr.len || fromaddr.len < sizeof(diff) || | |
721 | memcmp(fromaddr.ptr, toaddr.ptr, toaddr.len) > 0) | |
722 | { | |
723 | DBG1(DBG_CFG, "invalid IP address range: %H-%H", from, to); | |
724 | return NULL; | |
725 | } | |
726 | if (fromaddr.len > sizeof(diff) && | |
727 | !chunk_equals(chunk_create(fromaddr.ptr, fromaddr.len - sizeof(diff)), | |
728 | chunk_create(toaddr.ptr, toaddr.len - sizeof(diff)))) | |
729 | { | |
730 | DBG1(DBG_CFG, "IP address range too large: %H-%H", from, to); | |
731 | return NULL; | |
732 | } | |
733 | this = create_generic(name); | |
734 | this->base = from->clone(from); | |
735 | diff = untoh32(toaddr.ptr + toaddr.len - sizeof(diff)) - | |
736 | untoh32(fromaddr.ptr + fromaddr.len - sizeof(diff)); | |
737 | this->size = diff + 1; | |
738 | ||
739 | return &this->public; | |
740 | } |