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