]> git.ipfire.org Git - thirdparty/strongswan.git/blame - src/libcharon/attributes/mem_pool.c
mem-pool: Reject the creation of unintentionally empty pools
[thirdparty/strongswan.git] / src / libcharon / attributes / mem_pool.c
CommitLineData
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
28typedef struct private_mem_pool_t private_mem_pool_t;
29
30/**
31 * private data of mem_pool_t
32 */
33struct 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 */
78typedef 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 */
88typedef 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 */
100static 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 */
115static 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 */
126static 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 */
134static 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 */
142static 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 */
172static 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
202METHOD(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
208METHOD(mem_pool_t, get_base, host_t*,
209 private_mem_pool_t *this)
210{
211 return this->base;
212}
213
ac5fb545 214METHOD(mem_pool_t, get_size, u_int,
e82186fb 215 private_mem_pool_t *this)
ac5fb545
TB
216{
217 return this->size;
218}
219
220METHOD(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
239METHOD(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 */
261static 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 */
273static 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, &current))
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 332static 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
357static 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, &current))
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
400METHOD(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
448METHOD(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, &current))
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 */
499typedef 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
516METHOD(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
561METHOD(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
572METHOD(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
590METHOD(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 613static 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 */
643static 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 */
661mem_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 */
719mem_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}