]> git.ipfire.org Git - thirdparty/dhcp.git/blob - server/mdb6.c
Fixed the code that checks if an address the server is planning
[thirdparty/dhcp.git] / server / mdb6.c
1 /*
2 * Copyright (C) 2007-2012 by Internet Systems Consortium, Inc. ("ISC")
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14 * PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 /* TODO: assert() */
18 /* TODO: simplify functions, as pool is now in iaaddr */
19
20 #include "config.h"
21
22 #include <sys/types.h>
23 #include <time.h>
24 #include <netinet/in.h>
25
26 #include <stdarg.h>
27 #include "dhcpd.h"
28 #include "omapip/omapip.h"
29 #include "omapip/hash.h"
30 #include <isc/md5.h>
31
32 HASH_FUNCTIONS(ia, unsigned char *, struct ia_xx, ia_hash_t,
33 ia_reference, ia_dereference, do_string_hash)
34
35 ia_hash_t *ia_na_active;
36 ia_hash_t *ia_ta_active;
37 ia_hash_t *ia_pd_active;
38
39 HASH_FUNCTIONS(iasubopt, struct in6_addr *, struct iasubopt, iasubopt_hash_t,
40 iasubopt_reference, iasubopt_dereference, do_string_hash)
41
42 struct ipv6_pool **pools;
43 int num_pools;
44
45 /*
46 * Create a new IAADDR/PREFIX structure.
47 *
48 * - iasubopt must be a pointer to a (struct iasubopt *) pointer previously
49 * initialized to NULL
50 */
51 isc_result_t
52 iasubopt_allocate(struct iasubopt **iasubopt, const char *file, int line) {
53 struct iasubopt *tmp;
54
55 if (iasubopt == NULL) {
56 log_error("%s(%d): NULL pointer reference", file, line);
57 return DHCP_R_INVALIDARG;
58 }
59 if (*iasubopt != NULL) {
60 log_error("%s(%d): non-NULL pointer", file, line);
61 return DHCP_R_INVALIDARG;
62 }
63
64 tmp = dmalloc(sizeof(*tmp), file, line);
65 if (tmp == NULL) {
66 return ISC_R_NOMEMORY;
67 }
68
69 tmp->refcnt = 1;
70 tmp->state = FTS_FREE;
71 tmp->heap_index = -1;
72 tmp->plen = 255;
73
74 *iasubopt = tmp;
75 return ISC_R_SUCCESS;
76 }
77
78 /*
79 * Reference an IAADDR/PREFIX structure.
80 *
81 * - iasubopt must be a pointer to a (struct iasubopt *) pointer previously
82 * initialized to NULL
83 */
84 isc_result_t
85 iasubopt_reference(struct iasubopt **iasubopt, struct iasubopt *src,
86 const char *file, int line) {
87 if (iasubopt == NULL) {
88 log_error("%s(%d): NULL pointer reference", file, line);
89 return DHCP_R_INVALIDARG;
90 }
91 if (*iasubopt != NULL) {
92 log_error("%s(%d): non-NULL pointer", file, line);
93 return DHCP_R_INVALIDARG;
94 }
95 if (src == NULL) {
96 log_error("%s(%d): NULL pointer reference", file, line);
97 return DHCP_R_INVALIDARG;
98 }
99 *iasubopt = src;
100 src->refcnt++;
101 return ISC_R_SUCCESS;
102 }
103
104
105 /*
106 * Dereference an IAADDR/PREFIX structure.
107 *
108 * If it is the last reference, then the memory for the
109 * structure is freed.
110 */
111 isc_result_t
112 iasubopt_dereference(struct iasubopt **iasubopt, const char *file, int line) {
113 struct iasubopt *tmp;
114
115 if ((iasubopt == NULL) || (*iasubopt == NULL)) {
116 log_error("%s(%d): NULL pointer", file, line);
117 return DHCP_R_INVALIDARG;
118 }
119
120 tmp = *iasubopt;
121 *iasubopt = NULL;
122
123 tmp->refcnt--;
124 if (tmp->refcnt < 0) {
125 log_error("%s(%d): negative refcnt", file, line);
126 tmp->refcnt = 0;
127 }
128 if (tmp->refcnt == 0) {
129 if (tmp->ia != NULL) {
130 ia_dereference(&(tmp->ia), file, line);
131 }
132 if (tmp->ipv6_pool != NULL) {
133 ipv6_pool_dereference(&(tmp->ipv6_pool), file, line);
134 }
135 if (tmp->scope != NULL) {
136 binding_scope_dereference(&tmp->scope, file, line);
137 }
138 dfree(tmp, file, line);
139 }
140
141 return ISC_R_SUCCESS;
142 }
143
144 /*
145 * Make the key that we use for IA.
146 */
147 isc_result_t
148 ia_make_key(struct data_string *key, u_int32_t iaid,
149 const char *duid, unsigned int duid_len,
150 const char *file, int line) {
151
152 memset(key, 0, sizeof(*key));
153 key->len = duid_len + sizeof(iaid);
154 if (!buffer_allocate(&(key->buffer), key->len, file, line)) {
155 return ISC_R_NOMEMORY;
156 }
157 key->data = key->buffer->data;
158 memcpy((char *)key->data, &iaid, sizeof(iaid));
159 memcpy((char *)key->data + sizeof(iaid), duid, duid_len);
160
161 return ISC_R_SUCCESS;
162 }
163
164 /*
165 * Create a new IA structure.
166 *
167 * - ia must be a pointer to a (struct ia_xx *) pointer previously
168 * initialized to NULL
169 * - iaid and duid are values from the client
170 *
171 * XXXsk: we don't concern ourself with the byte order of the IAID,
172 * which might be a problem if we transfer this structure
173 * between machines of different byte order
174 */
175 isc_result_t
176 ia_allocate(struct ia_xx **ia, u_int32_t iaid,
177 const char *duid, unsigned int duid_len,
178 const char *file, int line) {
179 struct ia_xx *tmp;
180
181 if (ia == NULL) {
182 log_error("%s(%d): NULL pointer reference", file, line);
183 return DHCP_R_INVALIDARG;
184 }
185 if (*ia != NULL) {
186 log_error("%s(%d): non-NULL pointer", file, line);
187 return DHCP_R_INVALIDARG;
188 }
189
190 tmp = dmalloc(sizeof(*tmp), file, line);
191 if (tmp == NULL) {
192 return ISC_R_NOMEMORY;
193 }
194
195 if (ia_make_key(&tmp->iaid_duid, iaid,
196 duid, duid_len, file, line) != ISC_R_SUCCESS) {
197 dfree(tmp, file, line);
198 return ISC_R_NOMEMORY;
199 }
200
201 tmp->refcnt = 1;
202
203 *ia = tmp;
204 return ISC_R_SUCCESS;
205 }
206
207 /*
208 * Reference an IA structure.
209 *
210 * - ia must be a pointer to a (struct ia_xx *) pointer previously
211 * initialized to NULL
212 */
213 isc_result_t
214 ia_reference(struct ia_xx **ia, struct ia_xx *src,
215 const char *file, int line) {
216 if (ia == NULL) {
217 log_error("%s(%d): NULL pointer reference", file, line);
218 return DHCP_R_INVALIDARG;
219 }
220 if (*ia != NULL) {
221 log_error("%s(%d): non-NULL pointer", file, line);
222 return DHCP_R_INVALIDARG;
223 }
224 if (src == NULL) {
225 log_error("%s(%d): NULL pointer reference", file, line);
226 return DHCP_R_INVALIDARG;
227 }
228 *ia = src;
229 src->refcnt++;
230 return ISC_R_SUCCESS;
231 }
232
233 /*
234 * Dereference an IA structure.
235 *
236 * If it is the last reference, then the memory for the
237 * structure is freed.
238 */
239 isc_result_t
240 ia_dereference(struct ia_xx **ia, const char *file, int line) {
241 struct ia_xx *tmp;
242 int i;
243
244 if ((ia == NULL) || (*ia == NULL)) {
245 log_error("%s(%d): NULL pointer", file, line);
246 return DHCP_R_INVALIDARG;
247 }
248
249 tmp = *ia;
250 *ia = NULL;
251
252 tmp->refcnt--;
253 if (tmp->refcnt < 0) {
254 log_error("%s(%d): negative refcnt", file, line);
255 tmp->refcnt = 0;
256 }
257 if (tmp->refcnt == 0) {
258 if (tmp->iasubopt != NULL) {
259 for (i=0; i<tmp->num_iasubopt; i++) {
260 iasubopt_dereference(&(tmp->iasubopt[i]),
261 file, line);
262 }
263 dfree(tmp->iasubopt, file, line);
264 }
265 data_string_forget(&(tmp->iaid_duid), file, line);
266 dfree(tmp, file, line);
267 }
268 return ISC_R_SUCCESS;
269 }
270
271
272 /*
273 * Add an IAADDR/PREFIX entry to an IA structure.
274 */
275 isc_result_t
276 ia_add_iasubopt(struct ia_xx *ia, struct iasubopt *iasubopt,
277 const char *file, int line) {
278 int max;
279 struct iasubopt **new;
280
281 /*
282 * Grow our array if we need to.
283 *
284 * Note: we pick 4 as the increment, as that seems a reasonable
285 * guess as to how many addresses/prefixes we might expect
286 * on an interface.
287 */
288 if (ia->max_iasubopt <= ia->num_iasubopt) {
289 max = ia->max_iasubopt + 4;
290 new = dmalloc(max * sizeof(struct iasubopt *), file, line);
291 if (new == NULL) {
292 return ISC_R_NOMEMORY;
293 }
294 memcpy(new, ia->iasubopt,
295 ia->num_iasubopt * sizeof(struct iasubopt *));
296 ia->iasubopt = new;
297 ia->max_iasubopt = max;
298 }
299
300 iasubopt_reference(&(ia->iasubopt[ia->num_iasubopt]), iasubopt,
301 file, line);
302 ia->num_iasubopt++;
303
304 return ISC_R_SUCCESS;
305 }
306
307 /*
308 * Remove an IAADDR/PREFIX entry to an IA structure.
309 *
310 * Note: if a suboption appears more than once, then only ONE will be removed.
311 */
312 void
313 ia_remove_iasubopt(struct ia_xx *ia, struct iasubopt *iasubopt,
314 const char *file, int line) {
315 int i, j;
316
317 for (i=0; i<ia->num_iasubopt; i++) {
318 if (ia->iasubopt[i] == iasubopt) {
319 /* remove this sub option */
320 iasubopt_dereference(&(ia->iasubopt[i]), file, line);
321 /* move remaining suboption pointers down one */
322 for (j=i+1; j < ia->num_iasubopt; j++) {
323 ia->iasubopt[j-1] = ia->iasubopt[j];
324 }
325 /* decrease our total count */
326 /* remove the back-reference in the suboption itself */
327 ia_dereference(&iasubopt->ia, file, line);
328 ia->num_iasubopt--;
329 return;
330 }
331 }
332 log_error("%s(%d): IAADDR/PREFIX not in IA", file, line);
333 }
334
335 /*
336 * Remove all addresses/prefixes from an IA.
337 */
338 void
339 ia_remove_all_lease(struct ia_xx *ia, const char *file, int line) {
340 int i;
341
342 for (i=0; i<ia->num_iasubopt; i++) {
343 ia_dereference(&(ia->iasubopt[i]->ia), file, line);
344 iasubopt_dereference(&(ia->iasubopt[i]), file, line);
345 }
346 ia->num_iasubopt = 0;
347 }
348
349 /*
350 * Compare two IA.
351 */
352 isc_boolean_t
353 ia_equal(const struct ia_xx *a, const struct ia_xx *b)
354 {
355 isc_boolean_t found;
356 int i, j;
357
358 /*
359 * Handle cases where one or both of the inputs is NULL.
360 */
361 if (a == NULL) {
362 if (b == NULL) {
363 return ISC_TRUE;
364 } else {
365 return ISC_FALSE;
366 }
367 }
368
369 /*
370 * Check the type is the same.
371 */
372 if (a->ia_type != b->ia_type) {
373 return ISC_FALSE;
374 }
375
376 /*
377 * Check the DUID is the same.
378 */
379 if (a->iaid_duid.len != b->iaid_duid.len) {
380 return ISC_FALSE;
381 }
382 if (memcmp(a->iaid_duid.data,
383 b->iaid_duid.data, a->iaid_duid.len) != 0) {
384 return ISC_FALSE;
385 }
386
387 /*
388 * Make sure we have the same number of addresses/prefixes in each.
389 */
390 if (a->num_iasubopt != b->num_iasubopt) {
391 return ISC_FALSE;
392 }
393
394 /*
395 * Check that each address/prefix is present in both.
396 */
397 for (i=0; i<a->num_iasubopt; i++) {
398 found = ISC_FALSE;
399 for (j=0; j<a->num_iasubopt; j++) {
400 if (a->iasubopt[i]->plen != b->iasubopt[i]->plen)
401 continue;
402 if (memcmp(&(a->iasubopt[i]->addr),
403 &(b->iasubopt[j]->addr),
404 sizeof(struct in6_addr)) == 0) {
405 found = ISC_TRUE;
406 break;
407 }
408 }
409 if (!found) {
410 return ISC_FALSE;
411 }
412 }
413
414 /*
415 * These are the same in every way we care about.
416 */
417 return ISC_TRUE;
418 }
419
420 /*
421 * Helper function for lease heaps.
422 * Makes the top of the heap the oldest lease.
423 */
424 static isc_boolean_t
425 lease_older(void *a, void *b) {
426 struct iasubopt *la = (struct iasubopt *)a;
427 struct iasubopt *lb = (struct iasubopt *)b;
428
429 if (la->hard_lifetime_end_time == lb->hard_lifetime_end_time) {
430 return difftime(la->soft_lifetime_end_time,
431 lb->soft_lifetime_end_time) < 0;
432 } else {
433 return difftime(la->hard_lifetime_end_time,
434 lb->hard_lifetime_end_time) < 0;
435 }
436 }
437
438 /*
439 * Helper function for lease address/prefix heaps.
440 * Callback when an address's position in the heap changes.
441 */
442 static void
443 lease_index_changed(void *iasubopt, unsigned int new_heap_index) {
444 ((struct iasubopt *)iasubopt)-> heap_index = new_heap_index;
445 }
446
447
448 /*
449 * Create a new IPv6 lease pool structure.
450 *
451 * - pool must be a pointer to a (struct ipv6_pool *) pointer previously
452 * initialized to NULL
453 */
454 isc_result_t
455 ipv6_pool_allocate(struct ipv6_pool **pool, u_int16_t type,
456 const struct in6_addr *start_addr, int bits,
457 int units, const char *file, int line) {
458 struct ipv6_pool *tmp;
459
460 if (pool == NULL) {
461 log_error("%s(%d): NULL pointer reference", file, line);
462 return DHCP_R_INVALIDARG;
463 }
464 if (*pool != NULL) {
465 log_error("%s(%d): non-NULL pointer", file, line);
466 return DHCP_R_INVALIDARG;
467 }
468
469 tmp = dmalloc(sizeof(*tmp), file, line);
470 if (tmp == NULL) {
471 return ISC_R_NOMEMORY;
472 }
473
474 tmp->refcnt = 1;
475 tmp->pool_type = type;
476 tmp->start_addr = *start_addr;
477 tmp->bits = bits;
478 tmp->units = units;
479 if (!iasubopt_new_hash(&tmp->leases, DEFAULT_HASH_SIZE, file, line)) {
480 dfree(tmp, file, line);
481 return ISC_R_NOMEMORY;
482 }
483 if (isc_heap_create(dhcp_gbl_ctx.mctx, lease_older, lease_index_changed,
484 0, &(tmp->active_timeouts)) != ISC_R_SUCCESS) {
485 iasubopt_free_hash_table(&(tmp->leases), file, line);
486 dfree(tmp, file, line);
487 return ISC_R_NOMEMORY;
488 }
489 if (isc_heap_create(dhcp_gbl_ctx.mctx, lease_older, lease_index_changed,
490 0, &(tmp->inactive_timeouts)) != ISC_R_SUCCESS) {
491 isc_heap_destroy(&(tmp->active_timeouts));
492 iasubopt_free_hash_table(&(tmp->leases), file, line);
493 dfree(tmp, file, line);
494 return ISC_R_NOMEMORY;
495 }
496
497 *pool = tmp;
498 return ISC_R_SUCCESS;
499 }
500
501 /*
502 * Reference an IPv6 pool structure.
503 *
504 * - pool must be a pointer to a (struct pool *) pointer previously
505 * initialized to NULL
506 */
507 isc_result_t
508 ipv6_pool_reference(struct ipv6_pool **pool, struct ipv6_pool *src,
509 const char *file, int line) {
510 if (pool == NULL) {
511 log_error("%s(%d): NULL pointer reference", file, line);
512 return DHCP_R_INVALIDARG;
513 }
514 if (*pool != NULL) {
515 log_error("%s(%d): non-NULL pointer", file, line);
516 return DHCP_R_INVALIDARG;
517 }
518 if (src == NULL) {
519 log_error("%s(%d): NULL pointer reference", file, line);
520 return DHCP_R_INVALIDARG;
521 }
522 *pool = src;
523 src->refcnt++;
524 return ISC_R_SUCCESS;
525 }
526
527 /*
528 * Note: Each IAADDR/PREFIX in a pool is referenced by the pool. This is needed
529 * to prevent the lease from being garbage collected out from under the
530 * pool.
531 *
532 * The references are made from the hash and from the heap. The following
533 * helper functions dereference these when a pool is destroyed.
534 */
535
536 /*
537 * Helper function for pool cleanup.
538 * Dereference each of the hash entries in a pool.
539 */
540 static isc_result_t
541 dereference_hash_entry(const void *name, unsigned len, void *value) {
542 struct iasubopt *iasubopt = (struct iasubopt *)value;
543
544 iasubopt_dereference(&iasubopt, MDL);
545 return ISC_R_SUCCESS;
546 }
547
548 /*
549 * Helper function for pool cleanup.
550 * Dereference each of the heap entries in a pool.
551 */
552 static void
553 dereference_heap_entry(void *value, void *dummy) {
554 struct iasubopt *iasubopt = (struct iasubopt *)value;
555
556 iasubopt_dereference(&iasubopt, MDL);
557 }
558
559
560 /*
561 * Dereference an IPv6 pool structure.
562 *
563 * If it is the last reference, then the memory for the
564 * structure is freed.
565 */
566 isc_result_t
567 ipv6_pool_dereference(struct ipv6_pool **pool, const char *file, int line) {
568 struct ipv6_pool *tmp;
569
570 if ((pool == NULL) || (*pool == NULL)) {
571 log_error("%s(%d): NULL pointer", file, line);
572 return DHCP_R_INVALIDARG;
573 }
574
575 tmp = *pool;
576 *pool = NULL;
577
578 tmp->refcnt--;
579 if (tmp->refcnt < 0) {
580 log_error("%s(%d): negative refcnt", file, line);
581 tmp->refcnt = 0;
582 }
583 if (tmp->refcnt == 0) {
584 iasubopt_hash_foreach(tmp->leases, dereference_hash_entry);
585 iasubopt_free_hash_table(&(tmp->leases), file, line);
586 isc_heap_foreach(tmp->active_timeouts,
587 dereference_heap_entry, NULL);
588 isc_heap_destroy(&(tmp->active_timeouts));
589 isc_heap_foreach(tmp->inactive_timeouts,
590 dereference_heap_entry, NULL);
591 isc_heap_destroy(&(tmp->inactive_timeouts));
592 dfree(tmp, file, line);
593 }
594
595 return ISC_R_SUCCESS;
596 }
597
598 /*
599 * Create an address by hashing the input, and using that for
600 * the non-network part.
601 */
602 static void
603 build_address6(struct in6_addr *addr,
604 const struct in6_addr *net_start_addr, int net_bits,
605 const struct data_string *input) {
606 isc_md5_t ctx;
607 int net_bytes;
608 int i;
609 char *str;
610 const char *net_str;
611
612 /*
613 * Use MD5 to get a nice 128 bit hash of the input.
614 * Yes, we know MD5 isn't cryptographically sound.
615 * No, we don't care.
616 */
617 isc_md5_init(&ctx);
618 isc_md5_update(&ctx, input->data, input->len);
619 isc_md5_final(&ctx, (unsigned char *)addr);
620
621 /*
622 * Copy the [0..128] network bits over.
623 */
624 str = (char *)addr;
625 net_str = (const char *)net_start_addr;
626 net_bytes = net_bits / 8;
627 for (i = 0; i < net_bytes; i++) {
628 str[i] = net_str[i];
629 }
630 switch (net_bits % 8) {
631 case 1: str[i] = (str[i] & 0x7F) | (net_str[i] & 0x80); break;
632 case 2: str[i] = (str[i] & 0x3F) | (net_str[i] & 0xC0); break;
633 case 3: str[i] = (str[i] & 0x1F) | (net_str[i] & 0xE0); break;
634 case 4: str[i] = (str[i] & 0x0F) | (net_str[i] & 0xF0); break;
635 case 5: str[i] = (str[i] & 0x07) | (net_str[i] & 0xF8); break;
636 case 6: str[i] = (str[i] & 0x03) | (net_str[i] & 0xFC); break;
637 case 7: str[i] = (str[i] & 0x01) | (net_str[i] & 0xFE); break;
638 }
639
640 /*
641 * Set the universal/local bit ("u bit") to zero for /64s. The
642 * individual/group bit ("g bit") is unchanged, because the g-bit
643 * has no meaning when the u-bit is cleared.
644 */
645 if (net_bits == 64)
646 str[8] &= ~0x02;
647 }
648
649 /*
650 * Create a temporary address by a variant of RFC 4941 algo.
651 * Note: this should not be used for prefixes shorter than 64 bits.
652 */
653 static void
654 build_temporary6(struct in6_addr *addr,
655 const struct in6_addr *net_start_addr, int net_bits,
656 const struct data_string *input) {
657 static u_int32_t history[2];
658 static u_int32_t counter = 0;
659 isc_md5_t ctx;
660 unsigned char md[16];
661
662 /*
663 * First time/time to reseed.
664 * Please use a good pseudo-random generator here!
665 */
666 if (counter == 0) {
667 isc_random_get(&history[0]);
668 isc_random_get(&history[1]);
669 }
670
671 /*
672 * Use MD5 as recommended by RFC 4941.
673 */
674 isc_md5_init(&ctx);
675 isc_md5_update(&ctx, (unsigned char *)&history[0], 8UL);
676 isc_md5_update(&ctx, input->data, input->len);
677 isc_md5_final(&ctx, md);
678
679 /*
680 * Build the address.
681 */
682 if (net_bits == 64) {
683 memcpy(&addr->s6_addr[0], &net_start_addr->s6_addr[0], 8);
684 memcpy(&addr->s6_addr[8], md, 8);
685 addr->s6_addr[8] &= ~0x02;
686 } else {
687 int net_bytes;
688 int i;
689 char *str;
690 const char *net_str;
691
692 /*
693 * Copy the [0..128] network bits over.
694 */
695 str = (char *)addr;
696 net_str = (const char *)net_start_addr;
697 net_bytes = net_bits / 8;
698 for (i = 0; i < net_bytes; i++) {
699 str[i] = net_str[i];
700 }
701 memcpy(str + net_bytes, md, 16 - net_bytes);
702 switch (net_bits % 8) {
703 case 1: str[i] = (str[i] & 0x7F) | (net_str[i] & 0x80); break;
704 case 2: str[i] = (str[i] & 0x3F) | (net_str[i] & 0xC0); break;
705 case 3: str[i] = (str[i] & 0x1F) | (net_str[i] & 0xE0); break;
706 case 4: str[i] = (str[i] & 0x0F) | (net_str[i] & 0xF0); break;
707 case 5: str[i] = (str[i] & 0x07) | (net_str[i] & 0xF8); break;
708 case 6: str[i] = (str[i] & 0x03) | (net_str[i] & 0xFC); break;
709 case 7: str[i] = (str[i] & 0x01) | (net_str[i] & 0xFE); break;
710 }
711 }
712
713
714 /*
715 * Save history for the next call.
716 */
717 memcpy((unsigned char *)&history[0], md + 8, 8);
718 counter++;
719 }
720
721 /* Reserved Subnet Router Anycast ::0:0:0:0. */
722 static struct in6_addr rtany;
723 /* Reserved Subnet Anycasts ::fdff:ffff:ffff:ff80-::fdff:ffff:ffff:ffff. */
724 static struct in6_addr resany;
725
726 /*
727 * Create a lease for the given address and client duid.
728 *
729 * - pool must be a pointer to a (struct pool *) pointer previously
730 * initialized to NULL
731 *
732 * Right now we simply hash the DUID, and if we get a collision, we hash
733 * again until we find a free address. We try this a fixed number of times,
734 * to avoid getting stuck in a loop (this is important on small pools
735 * where we can run out of space).
736 *
737 * We return the number of attempts that it took to find an available
738 * lease. This tells callers when a pool is are filling up, as
739 * well as an indication of how full the pool is; statistically the
740 * more full a pool is the more attempts must be made before finding
741 * a free lease. Realistically this will only happen in very full
742 * pools.
743 *
744 * We probably want different algorithms depending on the network size, in
745 * the long term.
746 */
747 isc_result_t
748 create_lease6(struct ipv6_pool *pool, struct iasubopt **addr,
749 unsigned int *attempts,
750 const struct data_string *uid, time_t soft_lifetime_end_time) {
751 struct data_string ds;
752 struct in6_addr tmp;
753 struct iasubopt *test_iaaddr;
754 struct data_string new_ds;
755 struct iasubopt *iaaddr;
756 isc_result_t result;
757 isc_boolean_t reserved_iid;
758 static isc_boolean_t init_resiid = ISC_FALSE;
759
760 /*
761 * Fill the reserved IIDs.
762 */
763 if (!init_resiid) {
764 memset(&rtany, 0, 16);
765 memset(&resany, 0, 8);
766 resany.s6_addr[8] = 0xfd;
767 memset(&resany.s6_addr[9], 0xff, 6);
768 init_resiid = ISC_TRUE;
769 }
770
771 /*
772 * Use the UID as our initial seed for the hash
773 */
774 memset(&ds, 0, sizeof(ds));
775 data_string_copy(&ds, (struct data_string *)uid, MDL);
776
777 *attempts = 0;
778 for (;;) {
779 /*
780 * Give up at some point.
781 */
782 if (++(*attempts) > 100) {
783 data_string_forget(&ds, MDL);
784 return ISC_R_NORESOURCES;
785 }
786
787 /*
788 * Build a resource.
789 */
790 switch (pool->pool_type) {
791 case D6O_IA_NA:
792 /* address */
793 build_address6(&tmp, &pool->start_addr,
794 pool->bits, &ds);
795 break;
796 case D6O_IA_TA:
797 /* temporary address */
798 build_temporary6(&tmp, &pool->start_addr,
799 pool->bits, &ds);
800 break;
801 case D6O_IA_PD:
802 /* prefix */
803 log_error("create_lease6: prefix pool.");
804 return DHCP_R_INVALIDARG;
805 default:
806 log_error("create_lease6: untyped pool.");
807 return DHCP_R_INVALIDARG;
808 }
809
810 /*
811 * Avoid reserved interface IDs. (cf. RFC 5453)
812 */
813 reserved_iid = ISC_FALSE;
814 if (memcmp(&tmp.s6_addr[8], &rtany.s6_addr[8], 8) == 0) {
815 reserved_iid = ISC_TRUE;
816 }
817 if (!reserved_iid &&
818 (memcmp(&tmp.s6_addr[8], &resany.s6_addr[8], 7) == 0) &&
819 ((tmp.s6_addr[15] & 0x80) == 0x80)) {
820 reserved_iid = ISC_TRUE;
821 }
822
823 /*
824 * If this address is not in use, we're happy with it
825 */
826 test_iaaddr = NULL;
827 if (!reserved_iid &&
828 (iasubopt_hash_lookup(&test_iaaddr, pool->leases,
829 &tmp, sizeof(tmp), MDL) == 0)) {
830 break;
831 }
832 if (test_iaaddr != NULL)
833 iasubopt_dereference(&test_iaaddr, MDL);
834
835 /*
836 * Otherwise, we create a new input, adding the address
837 */
838 memset(&new_ds, 0, sizeof(new_ds));
839 new_ds.len = ds.len + sizeof(tmp);
840 if (!buffer_allocate(&new_ds.buffer, new_ds.len, MDL)) {
841 data_string_forget(&ds, MDL);
842 return ISC_R_NOMEMORY;
843 }
844 new_ds.data = new_ds.buffer->data;
845 memcpy(new_ds.buffer->data, ds.data, ds.len);
846 memcpy(new_ds.buffer->data + ds.len, &tmp, sizeof(tmp));
847 data_string_forget(&ds, MDL);
848 data_string_copy(&ds, &new_ds, MDL);
849 data_string_forget(&new_ds, MDL);
850 }
851
852 data_string_forget(&ds, MDL);
853
854 /*
855 * We're happy with the address, create an IAADDR
856 * to hold it.
857 */
858 iaaddr = NULL;
859 result = iasubopt_allocate(&iaaddr, MDL);
860 if (result != ISC_R_SUCCESS) {
861 return result;
862 }
863 iaaddr->plen = 0;
864 memcpy(&iaaddr->addr, &tmp, sizeof(iaaddr->addr));
865
866 /*
867 * Add the lease to the pool (note state is free, not active?!).
868 */
869 result = add_lease6(pool, iaaddr, soft_lifetime_end_time);
870 if (result == ISC_R_SUCCESS) {
871 iasubopt_reference(addr, iaaddr, MDL);
872 }
873 iasubopt_dereference(&iaaddr, MDL);
874 return result;
875 }
876
877 /*
878 * Put a lease in the pool directly. This is intended to be used when
879 * loading leases from the file.
880 */
881 isc_result_t
882 add_lease6(struct ipv6_pool *pool, struct iasubopt *lease,
883 time_t valid_lifetime_end_time) {
884 isc_result_t insert_result;
885 struct iasubopt *test_iasubopt;
886 struct iasubopt *tmp_iasubopt;
887
888 /* If a state was not assigned by the caller, assume active. */
889 if (lease->state == 0)
890 lease->state = FTS_ACTIVE;
891
892 ipv6_pool_reference(&lease->ipv6_pool, pool, MDL);
893
894 /*
895 * If this IAADDR/PREFIX is already in our structures, remove the
896 * old one.
897 */
898 test_iasubopt = NULL;
899 if (iasubopt_hash_lookup(&test_iasubopt, pool->leases,
900 &lease->addr, sizeof(lease->addr), MDL)) {
901 /* XXX: we should probably ask the lease what heap it is on
902 * (as a consistency check).
903 * XXX: we should probably have one function to "put this lease
904 * on its heap" rather than doing these if's everywhere. If
905 * you add more states to this list, don't.
906 */
907 if ((test_iasubopt->state == FTS_ACTIVE) ||
908 (test_iasubopt->state == FTS_ABANDONED)) {
909 isc_heap_delete(pool->active_timeouts,
910 test_iasubopt->heap_index);
911 pool->num_active--;
912 } else {
913 isc_heap_delete(pool->inactive_timeouts,
914 test_iasubopt->heap_index);
915 pool->num_inactive--;
916 }
917
918 iasubopt_hash_delete(pool->leases, &test_iasubopt->addr,
919 sizeof(test_iasubopt->addr), MDL);
920
921 /*
922 * We're going to do a bit of evil trickery here.
923 *
924 * We need to dereference the entry once to remove our
925 * current reference (in test_iasubopt), and then one
926 * more time to remove the reference left when the
927 * address was added to the pool before.
928 */
929 tmp_iasubopt = test_iasubopt;
930 iasubopt_dereference(&test_iasubopt, MDL);
931 iasubopt_dereference(&tmp_iasubopt, MDL);
932 }
933
934 /*
935 * Add IAADDR/PREFIX to our structures.
936 */
937 tmp_iasubopt = NULL;
938 iasubopt_reference(&tmp_iasubopt, lease, MDL);
939 if ((tmp_iasubopt->state == FTS_ACTIVE) ||
940 (tmp_iasubopt->state == FTS_ABANDONED)) {
941 tmp_iasubopt->hard_lifetime_end_time = valid_lifetime_end_time;
942 iasubopt_hash_add(pool->leases, &tmp_iasubopt->addr,
943 sizeof(tmp_iasubopt->addr), lease, MDL);
944 insert_result = isc_heap_insert(pool->active_timeouts,
945 tmp_iasubopt);
946 if (insert_result == ISC_R_SUCCESS)
947 pool->num_active++;
948 } else {
949 tmp_iasubopt->soft_lifetime_end_time = valid_lifetime_end_time;
950 insert_result = isc_heap_insert(pool->inactive_timeouts,
951 tmp_iasubopt);
952 if (insert_result == ISC_R_SUCCESS)
953 pool->num_inactive++;
954 }
955 if (insert_result != ISC_R_SUCCESS) {
956 iasubopt_hash_delete(pool->leases, &lease->addr,
957 sizeof(lease->addr), MDL);
958 iasubopt_dereference(&tmp_iasubopt, MDL);
959 return insert_result;
960 }
961
962 /*
963 * Note: we intentionally leave tmp_iasubopt referenced; there
964 * is a reference in the heap/hash, after all.
965 */
966
967 return ISC_R_SUCCESS;
968 }
969
970 /*
971 * Determine if an address is present in a pool or not.
972 */
973 isc_boolean_t
974 lease6_exists(const struct ipv6_pool *pool, const struct in6_addr *addr) {
975 struct iasubopt *test_iaaddr;
976
977 test_iaaddr = NULL;
978 if (iasubopt_hash_lookup(&test_iaaddr, pool->leases,
979 (void *)addr, sizeof(*addr), MDL)) {
980 iasubopt_dereference(&test_iaaddr, MDL);
981 return ISC_TRUE;
982 } else {
983 return ISC_FALSE;
984 }
985 }
986
987 /*
988 * Put the lease on our active pool.
989 */
990 static isc_result_t
991 move_lease_to_active(struct ipv6_pool *pool, struct iasubopt *lease) {
992 isc_result_t insert_result;
993 int old_heap_index;
994
995 old_heap_index = lease->heap_index;
996 insert_result = isc_heap_insert(pool->active_timeouts, lease);
997 if (insert_result == ISC_R_SUCCESS) {
998 iasubopt_hash_add(pool->leases, &lease->addr,
999 sizeof(lease->addr), lease, MDL);
1000 isc_heap_delete(pool->inactive_timeouts, old_heap_index);
1001 pool->num_active++;
1002 pool->num_inactive--;
1003 lease->state = FTS_ACTIVE;
1004 }
1005 return insert_result;
1006 }
1007
1008 /*
1009 * Renew an lease in the pool.
1010 *
1011 * To do this, first set the new hard_lifetime_end_time for the resource,
1012 * and then invoke renew_lease6() on it.
1013 *
1014 * WARNING: lease times must only be extended, never reduced!!!
1015 */
1016 isc_result_t
1017 renew_lease6(struct ipv6_pool *pool, struct iasubopt *lease) {
1018 /*
1019 * If we're already active, then we can just move our expiration
1020 * time down the heap.
1021 *
1022 * If we're abandoned then we are already on the active list
1023 * but we need to retag the lease and move our expiration
1024 * from infinite to the current value
1025 *
1026 * Otherwise, we have to move from the inactive heap to the
1027 * active heap.
1028 */
1029 if (lease->state == FTS_ACTIVE) {
1030 isc_heap_decreased(pool->active_timeouts, lease->heap_index);
1031 return ISC_R_SUCCESS;
1032 } else if (lease->state == FTS_ABANDONED) {
1033 char tmp_addr[INET6_ADDRSTRLEN];
1034 lease->state = FTS_ACTIVE;
1035 isc_heap_increased(pool->active_timeouts, lease->heap_index);
1036 log_info("Reclaiming previously abandoned address %s",
1037 inet_ntop(AF_INET6, &(lease->addr), tmp_addr,
1038 sizeof(tmp_addr)));
1039 return ISC_R_SUCCESS;
1040 } else {
1041 return move_lease_to_active(pool, lease);
1042 }
1043 }
1044
1045 /*
1046 * Put the lease on our inactive pool, with the specified state.
1047 */
1048 static isc_result_t
1049 move_lease_to_inactive(struct ipv6_pool *pool, struct iasubopt *lease,
1050 binding_state_t state) {
1051 isc_result_t insert_result;
1052 int old_heap_index;
1053
1054 old_heap_index = lease->heap_index;
1055 insert_result = isc_heap_insert(pool->inactive_timeouts, lease);
1056 if (insert_result == ISC_R_SUCCESS) {
1057 #if defined (NSUPDATE)
1058 /* Process events upon expiration. */
1059 if (pool->pool_type != D6O_IA_PD) {
1060 ddns_removals(NULL, lease, NULL, ISC_FALSE);
1061 }
1062 #endif
1063
1064 /* Binding scopes are no longer valid after expiry or
1065 * release.
1066 */
1067 if (lease->scope != NULL) {
1068 binding_scope_dereference(&lease->scope, MDL);
1069 }
1070
1071 iasubopt_hash_delete(pool->leases,
1072 &lease->addr, sizeof(lease->addr), MDL);
1073 isc_heap_delete(pool->active_timeouts, old_heap_index);
1074 lease->state = state;
1075 pool->num_active--;
1076 pool->num_inactive++;
1077 }
1078 return insert_result;
1079 }
1080
1081 /*
1082 * Expire the oldest lease if it's lifetime_end_time is
1083 * older than the given time.
1084 *
1085 * - leasep must be a pointer to a (struct iasubopt *) pointer previously
1086 * initialized to NULL
1087 *
1088 * On return leasep has a reference to the removed entry. It is left
1089 * pointing to NULL if the oldest lease has not expired.
1090 */
1091 isc_result_t
1092 expire_lease6(struct iasubopt **leasep, struct ipv6_pool *pool, time_t now) {
1093 struct iasubopt *tmp;
1094 isc_result_t result;
1095
1096 if (leasep == NULL) {
1097 log_error("%s(%d): NULL pointer reference", MDL);
1098 return DHCP_R_INVALIDARG;
1099 }
1100 if (*leasep != NULL) {
1101 log_error("%s(%d): non-NULL pointer", MDL);
1102 return DHCP_R_INVALIDARG;
1103 }
1104
1105 if (pool->num_active > 0) {
1106 tmp = (struct iasubopt *)
1107 isc_heap_element(pool->active_timeouts, 1);
1108 if (now > tmp->hard_lifetime_end_time) {
1109 result = move_lease_to_inactive(pool, tmp,
1110 FTS_EXPIRED);
1111 if (result == ISC_R_SUCCESS) {
1112 iasubopt_reference(leasep, tmp, MDL);
1113 }
1114 return result;
1115 }
1116 }
1117 return ISC_R_SUCCESS;
1118 }
1119
1120
1121 /*
1122 * For a declined lease, leave it on the "active" pool, but mark
1123 * it as declined. Give it an infinite (well, really long) life.
1124 */
1125 isc_result_t
1126 decline_lease6(struct ipv6_pool *pool, struct iasubopt *lease) {
1127 isc_result_t result;
1128
1129 if ((lease->state != FTS_ACTIVE) &&
1130 (lease->state != FTS_ABANDONED)) {
1131 result = move_lease_to_active(pool, lease);
1132 if (result != ISC_R_SUCCESS) {
1133 return result;
1134 }
1135 }
1136 lease->state = FTS_ABANDONED;
1137 lease->hard_lifetime_end_time = MAX_TIME;
1138 isc_heap_decreased(pool->active_timeouts, lease->heap_index);
1139 return ISC_R_SUCCESS;
1140 }
1141
1142 /*
1143 * Put the returned lease on our inactive pool.
1144 */
1145 isc_result_t
1146 release_lease6(struct ipv6_pool *pool, struct iasubopt *lease) {
1147 if (lease->state == FTS_ACTIVE) {
1148 return move_lease_to_inactive(pool, lease, FTS_RELEASED);
1149 } else {
1150 return ISC_R_SUCCESS;
1151 }
1152 }
1153
1154 /*
1155 * Create a prefix by hashing the input, and using that for
1156 * the part subject to allocation.
1157 */
1158 static void
1159 build_prefix6(struct in6_addr *pref,
1160 const struct in6_addr *net_start_pref,
1161 int pool_bits, int pref_bits,
1162 const struct data_string *input) {
1163 isc_md5_t ctx;
1164 int net_bytes;
1165 int i;
1166 char *str;
1167 const char *net_str;
1168
1169 /*
1170 * Use MD5 to get a nice 128 bit hash of the input.
1171 * Yes, we know MD5 isn't cryptographically sound.
1172 * No, we don't care.
1173 */
1174 isc_md5_init(&ctx);
1175 isc_md5_update(&ctx, input->data, input->len);
1176 isc_md5_final(&ctx, (unsigned char *)pref);
1177
1178 /*
1179 * Copy the network bits over.
1180 */
1181 str = (char *)pref;
1182 net_str = (const char *)net_start_pref;
1183 net_bytes = pool_bits / 8;
1184 for (i = 0; i < net_bytes; i++) {
1185 str[i] = net_str[i];
1186 }
1187 i = net_bytes;
1188 switch (pool_bits % 8) {
1189 case 1: str[i] = (str[i] & 0x7F) | (net_str[i] & 0x80); break;
1190 case 2: str[i] = (str[i] & 0x3F) | (net_str[i] & 0xC0); break;
1191 case 3: str[i] = (str[i] & 0x1F) | (net_str[i] & 0xE0); break;
1192 case 4: str[i] = (str[i] & 0x0F) | (net_str[i] & 0xF0); break;
1193 case 5: str[i] = (str[i] & 0x07) | (net_str[i] & 0xF8); break;
1194 case 6: str[i] = (str[i] & 0x03) | (net_str[i] & 0xFC); break;
1195 case 7: str[i] = (str[i] & 0x01) | (net_str[i] & 0xFE); break;
1196 }
1197 /*
1198 * Zero the remaining bits.
1199 */
1200 net_bytes = pref_bits / 8;
1201 for (i=net_bytes+1; i<16; i++) {
1202 str[i] = 0;
1203 }
1204 i = net_bytes;
1205 switch (pref_bits % 8) {
1206 case 0: str[i] &= 0; break;
1207 case 1: str[i] &= 0x80; break;
1208 case 2: str[i] &= 0xC0; break;
1209 case 3: str[i] &= 0xE0; break;
1210 case 4: str[i] &= 0xF0; break;
1211 case 5: str[i] &= 0xF8; break;
1212 case 6: str[i] &= 0xFC; break;
1213 case 7: str[i] &= 0xFE; break;
1214 }
1215 }
1216
1217 /*
1218 * Create a lease for the given prefix and client duid.
1219 *
1220 * - pool must be a pointer to a (struct pool *) pointer previously
1221 * initialized to NULL
1222 *
1223 * Right now we simply hash the DUID, and if we get a collision, we hash
1224 * again until we find a free prefix. We try this a fixed number of times,
1225 * to avoid getting stuck in a loop (this is important on small pools
1226 * where we can run out of space).
1227 *
1228 * We return the number of attempts that it took to find an available
1229 * prefix. This tells callers when a pool is are filling up, as
1230 * well as an indication of how full the pool is; statistically the
1231 * more full a pool is the more attempts must be made before finding
1232 * a free prefix. Realistically this will only happen in very full
1233 * pools.
1234 *
1235 * We probably want different algorithms depending on the network size, in
1236 * the long term.
1237 */
1238 isc_result_t
1239 create_prefix6(struct ipv6_pool *pool, struct iasubopt **pref,
1240 unsigned int *attempts,
1241 const struct data_string *uid,
1242 time_t soft_lifetime_end_time) {
1243 struct data_string ds;
1244 struct in6_addr tmp;
1245 struct iasubopt *test_iapref;
1246 struct data_string new_ds;
1247 struct iasubopt *iapref;
1248 isc_result_t result;
1249
1250 /*
1251 * Use the UID as our initial seed for the hash
1252 */
1253 memset(&ds, 0, sizeof(ds));
1254 data_string_copy(&ds, (struct data_string *)uid, MDL);
1255
1256 *attempts = 0;
1257 for (;;) {
1258 /*
1259 * Give up at some point.
1260 */
1261 if (++(*attempts) > 10) {
1262 data_string_forget(&ds, MDL);
1263 return ISC_R_NORESOURCES;
1264 }
1265
1266 /*
1267 * Build a prefix
1268 */
1269 build_prefix6(&tmp, &pool->start_addr,
1270 pool->bits, pool->units, &ds);
1271
1272 /*
1273 * If this prefix is not in use, we're happy with it
1274 */
1275 test_iapref = NULL;
1276 if (iasubopt_hash_lookup(&test_iapref, pool->leases,
1277 &tmp, sizeof(tmp), MDL) == 0) {
1278 break;
1279 }
1280 iasubopt_dereference(&test_iapref, MDL);
1281
1282 /*
1283 * Otherwise, we create a new input, adding the prefix
1284 */
1285 memset(&new_ds, 0, sizeof(new_ds));
1286 new_ds.len = ds.len + sizeof(tmp);
1287 if (!buffer_allocate(&new_ds.buffer, new_ds.len, MDL)) {
1288 data_string_forget(&ds, MDL);
1289 return ISC_R_NOMEMORY;
1290 }
1291 new_ds.data = new_ds.buffer->data;
1292 memcpy(new_ds.buffer->data, ds.data, ds.len);
1293 memcpy(new_ds.buffer->data + ds.len, &tmp, sizeof(tmp));
1294 data_string_forget(&ds, MDL);
1295 data_string_copy(&ds, &new_ds, MDL);
1296 data_string_forget(&new_ds, MDL);
1297 }
1298
1299 data_string_forget(&ds, MDL);
1300
1301 /*
1302 * We're happy with the prefix, create an IAPREFIX
1303 * to hold it.
1304 */
1305 iapref = NULL;
1306 result = iasubopt_allocate(&iapref, MDL);
1307 if (result != ISC_R_SUCCESS) {
1308 return result;
1309 }
1310 iapref->plen = (u_int8_t)pool->units;
1311 memcpy(&iapref->addr, &tmp, sizeof(iapref->addr));
1312
1313 /*
1314 * Add the prefix to the pool (note state is free, not active?!).
1315 */
1316 result = add_lease6(pool, iapref, soft_lifetime_end_time);
1317 if (result == ISC_R_SUCCESS) {
1318 iasubopt_reference(pref, iapref, MDL);
1319 }
1320 iasubopt_dereference(&iapref, MDL);
1321 return result;
1322 }
1323
1324 /*
1325 * Determine if a prefix is present in a pool or not.
1326 */
1327 isc_boolean_t
1328 prefix6_exists(const struct ipv6_pool *pool,
1329 const struct in6_addr *pref, u_int8_t plen) {
1330 struct iasubopt *test_iapref;
1331
1332 if ((int)plen != pool->units)
1333 return ISC_FALSE;
1334
1335 test_iapref = NULL;
1336 if (iasubopt_hash_lookup(&test_iapref, pool->leases,
1337 (void *)pref, sizeof(*pref), MDL)) {
1338 iasubopt_dereference(&test_iapref, MDL);
1339 return ISC_TRUE;
1340 } else {
1341 return ISC_FALSE;
1342 }
1343 }
1344
1345 /*
1346 * Mark an IPv6 address/prefix as unavailable from a pool.
1347 *
1348 * This is used for host entries and the addresses of the server itself.
1349 */
1350 isc_result_t
1351 mark_lease_unavailable(struct ipv6_pool *pool, const struct in6_addr *addr) {
1352 struct iasubopt *dummy_iasubopt;
1353 isc_result_t result;
1354
1355 dummy_iasubopt = NULL;
1356 result = iasubopt_allocate(&dummy_iasubopt, MDL);
1357 if (result == ISC_R_SUCCESS) {
1358 dummy_iasubopt->addr = *addr;
1359 iasubopt_hash_add(pool->leases, &dummy_iasubopt->addr,
1360 sizeof(*addr), dummy_iasubopt, MDL);
1361 }
1362 return result;
1363 }
1364
1365 /*
1366 * Add a pool.
1367 */
1368 isc_result_t
1369 add_ipv6_pool(struct ipv6_pool *pool) {
1370 struct ipv6_pool **new_pools;
1371
1372 new_pools = dmalloc(sizeof(struct ipv6_pool *) * (num_pools+1), MDL);
1373 if (new_pools == NULL) {
1374 return ISC_R_NOMEMORY;
1375 }
1376
1377 if (num_pools > 0) {
1378 memcpy(new_pools, pools,
1379 sizeof(struct ipv6_pool *) * num_pools);
1380 dfree(pools, MDL);
1381 }
1382 pools = new_pools;
1383
1384 pools[num_pools] = NULL;
1385 ipv6_pool_reference(&pools[num_pools], pool, MDL);
1386 num_pools++;
1387 return ISC_R_SUCCESS;
1388 }
1389
1390 static void
1391 cleanup_old_expired(struct ipv6_pool *pool) {
1392 struct iasubopt *tmp;
1393 struct ia_xx *ia;
1394 struct ia_xx *ia_active;
1395 unsigned char *tmpd;
1396 time_t timeout;
1397
1398 while (pool->num_inactive > 0) {
1399 tmp = (struct iasubopt *)
1400 isc_heap_element(pool->inactive_timeouts, 1);
1401 if (tmp->hard_lifetime_end_time != 0) {
1402 timeout = tmp->hard_lifetime_end_time;
1403 timeout += EXPIRED_IPV6_CLEANUP_TIME;
1404 } else {
1405 timeout = tmp->soft_lifetime_end_time;
1406 }
1407 if (cur_time < timeout) {
1408 break;
1409 }
1410
1411 isc_heap_delete(pool->inactive_timeouts, tmp->heap_index);
1412 pool->num_inactive--;
1413
1414 if (tmp->ia != NULL) {
1415 /*
1416 * Check to see if this IA is in an active list,
1417 * but has no remaining resources. If so, remove it
1418 * from the active list.
1419 */
1420 ia = NULL;
1421 ia_reference(&ia, tmp->ia, MDL);
1422 ia_remove_iasubopt(ia, tmp, MDL);
1423 ia_active = NULL;
1424 tmpd = (unsigned char *)ia->iaid_duid.data;
1425 if ((ia->ia_type == D6O_IA_NA) &&
1426 (ia->num_iasubopt <= 0) &&
1427 (ia_hash_lookup(&ia_active, ia_na_active, tmpd,
1428 ia->iaid_duid.len, MDL) == 0) &&
1429 (ia_active == ia)) {
1430 ia_hash_delete(ia_na_active, tmpd,
1431 ia->iaid_duid.len, MDL);
1432 }
1433 if ((ia->ia_type == D6O_IA_TA) &&
1434 (ia->num_iasubopt <= 0) &&
1435 (ia_hash_lookup(&ia_active, ia_ta_active, tmpd,
1436 ia->iaid_duid.len, MDL) == 0) &&
1437 (ia_active == ia)) {
1438 ia_hash_delete(ia_ta_active, tmpd,
1439 ia->iaid_duid.len, MDL);
1440 }
1441 if ((ia->ia_type == D6O_IA_PD) &&
1442 (ia->num_iasubopt <= 0) &&
1443 (ia_hash_lookup(&ia_active, ia_pd_active, tmpd,
1444 ia->iaid_duid.len, MDL) == 0) &&
1445 (ia_active == ia)) {
1446 ia_hash_delete(ia_pd_active, tmpd,
1447 ia->iaid_duid.len, MDL);
1448 }
1449 ia_dereference(&ia, MDL);
1450 }
1451 iasubopt_dereference(&tmp, MDL);
1452 }
1453 }
1454
1455 static void
1456 lease_timeout_support(void *vpool) {
1457 struct ipv6_pool *pool;
1458 struct iasubopt *lease;
1459
1460 pool = (struct ipv6_pool *)vpool;
1461 for (;;) {
1462 /*
1463 * Get the next lease scheduled to expire.
1464 *
1465 * Note that if there are no leases in the pool,
1466 * expire_lease6() will return ISC_R_SUCCESS with
1467 * a NULL lease.
1468 *
1469 * expire_lease6() will call move_lease_to_inactive() which
1470 * calls ddns_removals() do we want that on the standard
1471 * expiration timer or a special 'depref' timer? Original
1472 * query from DH, moved here by SAR.
1473 */
1474 lease = NULL;
1475 if (expire_lease6(&lease, pool, cur_time) != ISC_R_SUCCESS) {
1476 break;
1477 }
1478 if (lease == NULL) {
1479 break;
1480 }
1481
1482 write_ia(lease->ia);
1483
1484 iasubopt_dereference(&lease, MDL);
1485 }
1486
1487 /*
1488 * Do some cleanup of our expired leases.
1489 */
1490 cleanup_old_expired(pool);
1491
1492 /*
1493 * Schedule next round of expirations.
1494 */
1495 schedule_lease_timeout(pool);
1496 }
1497
1498 /*
1499 * For a given pool, add a timer that will remove the next
1500 * lease to expire.
1501 */
1502 void
1503 schedule_lease_timeout(struct ipv6_pool *pool) {
1504 struct iasubopt *tmp;
1505 time_t timeout;
1506 time_t next_timeout;
1507 struct timeval tv;
1508
1509 next_timeout = MAX_TIME;
1510
1511 if (pool->num_active > 0) {
1512 tmp = (struct iasubopt *)
1513 isc_heap_element(pool->active_timeouts, 1);
1514 if (tmp->hard_lifetime_end_time < next_timeout) {
1515 next_timeout = tmp->hard_lifetime_end_time + 1;
1516 }
1517 }
1518
1519 if (pool->num_inactive > 0) {
1520 tmp = (struct iasubopt *)
1521 isc_heap_element(pool->inactive_timeouts, 1);
1522 if (tmp->hard_lifetime_end_time != 0) {
1523 timeout = tmp->hard_lifetime_end_time;
1524 timeout += EXPIRED_IPV6_CLEANUP_TIME;
1525 } else {
1526 timeout = tmp->soft_lifetime_end_time + 1;
1527 }
1528 if (timeout < next_timeout) {
1529 next_timeout = timeout;
1530 }
1531 }
1532
1533 if (next_timeout < MAX_TIME) {
1534 tv.tv_sec = next_timeout;
1535 tv.tv_usec = 0;
1536 add_timeout(&tv, lease_timeout_support, pool,
1537 (tvref_t)ipv6_pool_reference,
1538 (tvunref_t)ipv6_pool_dereference);
1539 }
1540 }
1541
1542 /*
1543 * Schedule timeouts across all pools.
1544 */
1545 void
1546 schedule_all_ipv6_lease_timeouts(void) {
1547 int i;
1548
1549 for (i=0; i<num_pools; i++) {
1550 schedule_lease_timeout(pools[i]);
1551 }
1552 }
1553
1554 /*
1555 * Given an address and the length of the network mask, return
1556 * only the network portion.
1557 *
1558 * Examples:
1559 *
1560 * "fe80::216:6fff:fe49:7d9b", length 64 = "fe80::"
1561 * "2001:888:1936:2:216:6fff:fe49:7d9b", length 48 = "2001:888:1936::"
1562 */
1563 static void
1564 ipv6_network_portion(struct in6_addr *result,
1565 const struct in6_addr *addr, int bits) {
1566 unsigned char *addrp;
1567 int mask_bits;
1568 int bytes;
1569 int extra_bits;
1570 int i;
1571
1572 static const unsigned char bitmasks[] = {
1573 0x00, 0xFE, 0xFC, 0xF8,
1574 0xF0, 0xE0, 0xC0, 0x80,
1575 };
1576
1577 /*
1578 * Sanity check our bits. ;)
1579 */
1580 if ((bits < 0) || (bits > 128)) {
1581 log_fatal("ipv6_network_portion: bits %d not between 0 and 128",
1582 bits);
1583 }
1584
1585 /*
1586 * Copy our address portion.
1587 */
1588 *result = *addr;
1589 addrp = ((unsigned char *)result) + 15;
1590
1591 /*
1592 * Zero out masked portion.
1593 */
1594 mask_bits = 128 - bits;
1595 bytes = mask_bits / 8;
1596 extra_bits = mask_bits % 8;
1597
1598 for (i=0; i<bytes; i++) {
1599 *addrp = 0;
1600 addrp--;
1601 }
1602 if (extra_bits) {
1603 *addrp &= bitmasks[extra_bits];
1604 }
1605 }
1606
1607 /*
1608 * Determine if the given address/prefix is in the pool.
1609 */
1610 isc_boolean_t
1611 ipv6_in_pool(const struct in6_addr *addr, const struct ipv6_pool *pool) {
1612 struct in6_addr tmp;
1613
1614 ipv6_network_portion(&tmp, addr, pool->bits);
1615 if (memcmp(&tmp, &pool->start_addr, sizeof(tmp)) == 0) {
1616 return ISC_TRUE;
1617 } else {
1618 return ISC_FALSE;
1619 }
1620 }
1621
1622 /*
1623 * Find the pool that contains the given address.
1624 *
1625 * - pool must be a pointer to a (struct ipv6_pool *) pointer previously
1626 * initialized to NULL
1627 */
1628 isc_result_t
1629 find_ipv6_pool(struct ipv6_pool **pool, u_int16_t type,
1630 const struct in6_addr *addr) {
1631 int i;
1632
1633 if (pool == NULL) {
1634 log_error("%s(%d): NULL pointer reference", MDL);
1635 return DHCP_R_INVALIDARG;
1636 }
1637 if (*pool != NULL) {
1638 log_error("%s(%d): non-NULL pointer", MDL);
1639 return DHCP_R_INVALIDARG;
1640 }
1641
1642 for (i=0; i<num_pools; i++) {
1643 if (pools[i]->pool_type != type)
1644 continue;
1645 if (ipv6_in_pool(addr, pools[i])) {
1646 ipv6_pool_reference(pool, pools[i], MDL);
1647 return ISC_R_SUCCESS;
1648 }
1649 }
1650 return ISC_R_NOTFOUND;
1651 }
1652
1653 /*
1654 * Helper function for the various functions that act across all
1655 * pools.
1656 */
1657 static isc_result_t
1658 change_leases(struct ia_xx *ia,
1659 isc_result_t (*change_func)(struct ipv6_pool *,
1660 struct iasubopt *)) {
1661 isc_result_t retval;
1662 isc_result_t renew_retval;
1663 struct ipv6_pool *pool;
1664 struct in6_addr *addr;
1665 int i;
1666
1667 retval = ISC_R_SUCCESS;
1668 for (i=0; i<ia->num_iasubopt; i++) {
1669 pool = NULL;
1670 addr = &ia->iasubopt[i]->addr;
1671 if (find_ipv6_pool(&pool, ia->ia_type,
1672 addr) == ISC_R_SUCCESS) {
1673 renew_retval = change_func(pool, ia->iasubopt[i]);
1674 if (renew_retval != ISC_R_SUCCESS) {
1675 retval = renew_retval;
1676 }
1677 }
1678 /* XXXsk: should we warn if we don't find a pool? */
1679 }
1680 return retval;
1681 }
1682
1683 /*
1684 * Renew all leases in an IA from all pools.
1685 *
1686 * The new hard_lifetime_end_time should be updated for the addresses/prefixes.
1687 *
1688 * WARNING: lease times must only be extended, never reduced!!!
1689 */
1690 isc_result_t
1691 renew_leases(struct ia_xx *ia) {
1692 return change_leases(ia, renew_lease6);
1693 }
1694
1695 /*
1696 * Release all leases in an IA from all pools.
1697 */
1698 isc_result_t
1699 release_leases(struct ia_xx *ia) {
1700 return change_leases(ia, release_lease6);
1701 }
1702
1703 /*
1704 * Decline all leases in an IA from all pools.
1705 */
1706 isc_result_t
1707 decline_leases(struct ia_xx *ia) {
1708 return change_leases(ia, decline_lease6);
1709 }
1710
1711 #ifdef DHCPv6
1712 /*
1713 * Helper function to output leases.
1714 */
1715 static int write_error;
1716
1717 static isc_result_t
1718 write_ia_leases(const void *name, unsigned len, void *value) {
1719 struct ia_xx *ia = (struct ia_xx *)value;
1720
1721 if (!write_error) {
1722 if (!write_ia(ia)) {
1723 write_error = 1;
1724 }
1725 }
1726 return ISC_R_SUCCESS;
1727 }
1728
1729 /*
1730 * Write all DHCPv6 information.
1731 */
1732 int
1733 write_leases6(void) {
1734 write_error = 0;
1735 write_server_duid();
1736 ia_hash_foreach(ia_na_active, write_ia_leases);
1737 if (write_error) {
1738 return 0;
1739 }
1740 ia_hash_foreach(ia_ta_active, write_ia_leases);
1741 if (write_error) {
1742 return 0;
1743 }
1744 ia_hash_foreach(ia_pd_active, write_ia_leases);
1745 if (write_error) {
1746 return 0;
1747 }
1748 return 1;
1749 }
1750 #endif /* DHCPv6 */
1751
1752 static isc_result_t
1753 mark_hosts_unavailable_support(const void *name, unsigned len, void *value) {
1754 struct host_decl *h;
1755 struct data_string fixed_addr;
1756 struct in6_addr addr;
1757 struct ipv6_pool *p;
1758
1759 h = (struct host_decl *)value;
1760
1761 /*
1762 * If the host has no address, we don't need to mark anything.
1763 */
1764 if (h->fixed_addr == NULL) {
1765 return ISC_R_SUCCESS;
1766 }
1767
1768 /*
1769 * Evaluate the fixed address.
1770 */
1771 memset(&fixed_addr, 0, sizeof(fixed_addr));
1772 if (!evaluate_option_cache(&fixed_addr, NULL, NULL, NULL, NULL, NULL,
1773 &global_scope, h->fixed_addr, MDL)) {
1774 log_error("mark_hosts_unavailable: "
1775 "error evaluating host address.");
1776 return ISC_R_SUCCESS;
1777 }
1778 if (fixed_addr.len != 16) {
1779 log_error("mark_hosts_unavailable: "
1780 "host address is not 128 bits.");
1781 return ISC_R_SUCCESS;
1782 }
1783 memcpy(&addr, fixed_addr.data, 16);
1784 data_string_forget(&fixed_addr, MDL);
1785
1786 /*
1787 * Find the pool holding this host, and mark the address.
1788 * (I suppose it is arguably valid to have a host that does not
1789 * sit in any pool.)
1790 */
1791 p = NULL;
1792 if (find_ipv6_pool(&p, D6O_IA_NA, &addr) == ISC_R_SUCCESS) {
1793 mark_lease_unavailable(p, &addr);
1794 ipv6_pool_dereference(&p, MDL);
1795 }
1796 if (find_ipv6_pool(&p, D6O_IA_TA, &addr) == ISC_R_SUCCESS) {
1797 mark_lease_unavailable(p, &addr);
1798 ipv6_pool_dereference(&p, MDL);
1799 }
1800
1801 return ISC_R_SUCCESS;
1802 }
1803
1804 void
1805 mark_hosts_unavailable(void) {
1806 hash_foreach(host_name_hash, mark_hosts_unavailable_support);
1807 }
1808
1809 static isc_result_t
1810 mark_phosts_unavailable_support(const void *name, unsigned len, void *value) {
1811 struct host_decl *h;
1812 struct iaddrcidrnetlist *l;
1813 struct in6_addr pref;
1814 struct ipv6_pool *p;
1815
1816 h = (struct host_decl *)value;
1817
1818 /*
1819 * If the host has no prefix, we don't need to mark anything.
1820 */
1821 if (h->fixed_prefix == NULL) {
1822 return ISC_R_SUCCESS;
1823 }
1824
1825 /*
1826 * Get the fixed prefixes.
1827 */
1828 for (l = h->fixed_prefix; l != NULL; l = l->next) {
1829 if (l->cidrnet.lo_addr.len != 16) {
1830 continue;
1831 }
1832 memcpy(&pref, l->cidrnet.lo_addr.iabuf, 16);
1833
1834 /*
1835 * Find the pool holding this host, and mark the prefix.
1836 * (I suppose it is arguably valid to have a host that does not
1837 * sit in any pool.)
1838 */
1839 p = NULL;
1840 if (find_ipv6_pool(&p, D6O_IA_PD, &pref) != ISC_R_SUCCESS) {
1841 continue;
1842 }
1843 if (l->cidrnet.bits != p->units) {
1844 ipv6_pool_dereference(&p, MDL);
1845 continue;
1846 }
1847 mark_lease_unavailable(p, &pref);
1848 ipv6_pool_dereference(&p, MDL);
1849 }
1850
1851 return ISC_R_SUCCESS;
1852 }
1853
1854 void
1855 mark_phosts_unavailable(void) {
1856 hash_foreach(host_name_hash, mark_phosts_unavailable_support);
1857 }
1858
1859 void
1860 mark_interfaces_unavailable(void) {
1861 struct interface_info *ip;
1862 int i;
1863 struct ipv6_pool *p;
1864
1865 ip = interfaces;
1866 while (ip != NULL) {
1867 for (i=0; i<ip->v6address_count; i++) {
1868 p = NULL;
1869 if (find_ipv6_pool(&p, D6O_IA_NA, &ip->v6addresses[i])
1870 == ISC_R_SUCCESS) {
1871 mark_lease_unavailable(p,
1872 &ip->v6addresses[i]);
1873 ipv6_pool_dereference(&p, MDL);
1874 }
1875 if (find_ipv6_pool(&p, D6O_IA_TA, &ip->v6addresses[i])
1876 == ISC_R_SUCCESS) {
1877 mark_lease_unavailable(p,
1878 &ip->v6addresses[i]);
1879 ipv6_pool_dereference(&p, MDL);
1880 }
1881 }
1882 ip = ip->next;
1883 }
1884 }
1885
1886
1887 #ifdef UNIT_TEST
1888 #include <stdlib.h>
1889
1890 int
1891 main(int argc, char *argv[]) {
1892 struct iasubopt *iaaddr;
1893 struct iasubopt *iaaddr_copy;
1894 u_int32_t iaid;
1895 struct ia_xx *ia_na;
1896 struct ia_xx *ia_na_copy;
1897 int i;
1898 struct in6_addr addr;
1899 struct ipv6_pool *pool;
1900 struct ipv6_pool *pool_copy;
1901 char addr_buf[INET6_ADDRSTRLEN];
1902 char *uid;
1903 struct data_string ds;
1904 struct iasubopt *expired_iaaddr;
1905 unsigned int attempts;
1906
1907 /*
1908 * Test 0: Basic iaaddr manipulation.
1909 */
1910 iaaddr = NULL;
1911 if (iasubopt_allocate(&iaaddr, MDL) != ISC_R_SUCCESS) {
1912 printf("ERROR: iasubopt_allocate() %s:%d\n", MDL);
1913 return 1;
1914 }
1915 if (iaaddr->state != FTS_FREE) {
1916 printf("ERROR: bad state %s:%d\n", MDL);
1917 return 1;
1918 }
1919 if (iaaddr->heap_index != -1) {
1920 printf("ERROR: bad heap_index %s:%d\n", MDL);
1921 return 1;
1922 }
1923 iaaddr_copy = NULL;
1924 if (iasubopt_reference(&iaaddr_copy, iaaddr, MDL) != ISC_R_SUCCESS) {
1925 printf("ERROR: iasubopt_reference() %s:%d\n", MDL);
1926 return 1;
1927 }
1928 if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
1929 printf("ERROR: iasubopt_reference() %s:%d\n", MDL);
1930 return 1;
1931 }
1932 if (iasubopt_dereference(&iaaddr_copy, MDL) != ISC_R_SUCCESS) {
1933 printf("ERROR: iasubopt_reference() %s:%d\n", MDL);
1934 return 1;
1935 }
1936
1937 /*
1938 * Test 1: Error iaaddr manipulation.
1939 */
1940 /* bogus allocate arguments */
1941 if (iasubopt_allocate(NULL, MDL) != DHCP_R_INVALIDARG) {
1942 printf("ERROR: iasubopt_allocate() %s:%d\n", MDL);
1943 return 1;
1944 }
1945 iaaddr = (struct iasubopt *)1;
1946 if (iasubopt_allocate(&iaaddr, MDL) != DHCP_R_INVALIDARG) {
1947 printf("ERROR: iasubopt_allocate() %s:%d\n", MDL);
1948 return 1;
1949 }
1950
1951 /* bogus reference arguments */
1952 iaaddr = NULL;
1953 if (iasubopt_allocate(&iaaddr, MDL) != ISC_R_SUCCESS) {
1954 printf("ERROR: iasubopt_allocate() %s:%d\n", MDL);
1955 return 1;
1956 }
1957 if (iasubopt_reference(NULL, iaaddr, MDL) != DHCP_R_INVALIDARG) {
1958 printf("ERROR: iasubopt_reference() %s:%d\n", MDL);
1959 return 1;
1960 }
1961 iaaddr_copy = (struct iasubopt *)1;
1962 if (iasubopt_reference(&iaaddr_copy, iaaddr,
1963 MDL) != DHCP_R_INVALIDARG) {
1964 printf("ERROR: iasubopt_reference() %s:%d\n", MDL);
1965 return 1;
1966 }
1967 iaaddr_copy = NULL;
1968 if (iasubopt_reference(&iaaddr_copy, NULL, MDL) != DHCP_R_INVALIDARG) {
1969 printf("ERROR: iasubopt_reference() %s:%d\n", MDL);
1970 return 1;
1971 }
1972 if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
1973 printf("ERROR: iasubopt_reference() %s:%d\n", MDL);
1974 return 1;
1975 }
1976
1977 /* bogus dereference arguments */
1978 if (iasubopt_dereference(NULL, MDL) != DHCP_R_INVALIDARG) {
1979 printf("ERROR: iasubopt_dereference() %s:%d\n", MDL);
1980 return 1;
1981 }
1982 iaaddr = NULL;
1983 if (iasubopt_dereference(&iaaddr, MDL) != DHCP_R_INVALIDARG) {
1984 printf("ERROR: iasubopt_dereference() %s:%d\n", MDL);
1985 return 1;
1986 }
1987
1988 /*
1989 * Test 2: Basic ia_na manipulation.
1990 */
1991 iaid = 666;
1992 ia_na = NULL;
1993 if (ia_allocate(&ia_na, iaid, "TestDUID", 8, MDL) != ISC_R_SUCCESS) {
1994 printf("ERROR: ia_allocate() %s:%d\n", MDL);
1995 return 1;
1996 }
1997 if (memcmp(ia_na->iaid_duid.data, &iaid, sizeof(iaid)) != 0) {
1998 printf("ERROR: bad IAID_DUID %s:%d\n", MDL);
1999 return 1;
2000 }
2001 if (memcmp(ia_na->iaid_duid.data+sizeof(iaid), "TestDUID", 8) != 0) {
2002 printf("ERROR: bad IAID_DUID %s:%d\n", MDL);
2003 return 1;
2004 }
2005 if (ia_na->num_iasubopt != 0) {
2006 printf("ERROR: bad num_iasubopt %s:%d\n", MDL);
2007 return 1;
2008 }
2009 ia_na_copy = NULL;
2010 if (ia_reference(&ia_na_copy, ia_na, MDL) != ISC_R_SUCCESS) {
2011 printf("ERROR: ia_reference() %s:%d\n", MDL);
2012 return 1;
2013 }
2014 iaaddr = NULL;
2015 if (iasubopt_allocate(&iaaddr, MDL) != ISC_R_SUCCESS) {
2016 printf("ERROR: iasubopt_allocate() %s:%d\n", MDL);
2017 return 1;
2018 }
2019 if (ia_add_iasubopt(ia_na, iaaddr, MDL) != ISC_R_SUCCESS) {
2020 printf("ERROR: ia_add_iasubopt() %s:%d\n", MDL);
2021 return 1;
2022 }
2023 ia_remove_iasubopt(ia_na, iaaddr, MDL);
2024 if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
2025 printf("ERROR: iasubopt_reference() %s:%d\n", MDL);
2026 return 1;
2027 }
2028 if (ia_dereference(&ia_na, MDL) != ISC_R_SUCCESS) {
2029 printf("ERROR: ia_dereference() %s:%d\n", MDL);
2030 return 1;
2031 }
2032 if (ia_dereference(&ia_na_copy, MDL) != ISC_R_SUCCESS) {
2033 printf("ERROR: ia_dereference() %s:%d\n", MDL);
2034 return 1;
2035 }
2036
2037 /*
2038 * Test 3: lots of iaaddr in our ia_na
2039 */
2040
2041 /* lots of iaaddr that we delete */
2042 iaid = 666;
2043 ia_na = NULL;
2044 if (ia_allocate(&ia_na, iaid, "TestDUID", 8, MDL) != ISC_R_SUCCESS) {
2045 printf("ERROR: ia_allocate() %s:%d\n", MDL);
2046 return 1;
2047 }
2048 for (i=0; i<100; i++) {
2049 iaaddr = NULL;
2050 if (iasubopt_allocate(&iaaddr, MDL) != ISC_R_SUCCESS) {
2051 printf("ERROR: iasubopt_allocate() %s:%d\n", MDL);
2052 return 1;
2053 }
2054 if (ia_add_iasubopt(ia_na, iaaddr, MDL) != ISC_R_SUCCESS) {
2055 printf("ERROR: ia_add_iasubopt() %s:%d\n", MDL);
2056 return 1;
2057 }
2058 if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
2059 printf("ERROR: iasubopt_reference() %s:%d\n", MDL);
2060 return 1;
2061 }
2062 }
2063 for (i=0; i<100; i++) {
2064 iaaddr = ia_na->iasubopt[random() % ia_na->num_iasubopt];
2065 ia_remove_iasubopt(ia_na, iaaddr, MDL);
2066 }
2067 if (ia_dereference(&ia_na, MDL) != ISC_R_SUCCESS) {
2068 printf("ERROR: ia_dereference() %s:%d\n", MDL);
2069 return 1;
2070 }
2071
2072 /* lots of iaaddr, let dereference cleanup */
2073 iaid = 666;
2074 ia_na = NULL;
2075 if (ia_allocate(&ia_na, iaid, "TestDUID", 8, MDL) != ISC_R_SUCCESS) {
2076 printf("ERROR: ia_allocate() %s:%d\n", MDL);
2077 return 1;
2078 }
2079 for (i=0; i<100; i++) {
2080 iaaddr = NULL;
2081 if (iasubopt_allocate(&iaaddr, MDL) != ISC_R_SUCCESS) {
2082 printf("ERROR: iasubopt_allocate() %s:%d\n", MDL);
2083 return 1;
2084 }
2085 if (ia_add_iasubopt(ia_na, iaaddr, MDL) != ISC_R_SUCCESS) {
2086 printf("ERROR: ia_add_iasubopt() %s:%d\n", MDL);
2087 return 1;
2088 }
2089 if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
2090 printf("ERROR: iasubopt_reference() %s:%d\n", MDL);
2091 return 1;
2092 }
2093 }
2094 if (ia_dereference(&ia_na, MDL) != ISC_R_SUCCESS) {
2095 printf("ERROR: ia_dereference() %s:%d\n", MDL);
2096 return 1;
2097 }
2098
2099 /*
2100 * Test 4: Errors in ia_na.
2101 */
2102 /* bogus allocate arguments */
2103 if (ia_allocate(NULL, 123, "", 0, MDL) != DHCP_R_INVALIDARG) {
2104 printf("ERROR: ia_allocate() %s:%d\n", MDL);
2105 return 1;
2106 }
2107 ia_na = (struct ia_na *)1;
2108 if (ia_allocate(&ia_na, 456, "", 0, MDL) != DHCP_R_INVALIDARG) {
2109 printf("ERROR: ia_allocate() %s:%d\n", MDL);
2110 return 1;
2111 }
2112
2113 /* bogus reference arguments */
2114 iaid = 666;
2115 ia_na = NULL;
2116 if (ia_allocate(&ia_na, iaid, "TestDUID", 8, MDL) != ISC_R_SUCCESS) {
2117 printf("ERROR: ia_allocate() %s:%d\n", MDL);
2118 return 1;
2119 }
2120 if (ia_reference(NULL, ia_na, MDL) != DHCP_R_INVALIDARG) {
2121 printf("ERROR: ia_reference() %s:%d\n", MDL);
2122 return 1;
2123 }
2124 ia_na_copy = (struct ia_na *)1;
2125 if (ia_reference(&ia_na_copy, ia_na, MDL) != DHCP_R_INVALIDARG) {
2126 printf("ERROR: ia_reference() %s:%d\n", MDL);
2127 return 1;
2128 }
2129 ia_na_copy = NULL;
2130 if (ia_reference(&ia_na_copy, NULL, MDL) != DHCP_R_INVALIDARG) {
2131 printf("ERROR: ia_reference() %s:%d\n", MDL);
2132 return 1;
2133 }
2134 if (ia_dereference(&ia_na, MDL) != ISC_R_SUCCESS) {
2135 printf("ERROR: ia_dereference() %s:%d\n", MDL);
2136 return 1;
2137 }
2138
2139 /* bogus dereference arguments */
2140 if (ia_dereference(NULL, MDL) != DHCP_R_INVALIDARG) {
2141 printf("ERROR: ia_dereference() %s:%d\n", MDL);
2142 return 1;
2143 }
2144
2145 /* bogus remove */
2146 iaid = 666;
2147 ia_na = NULL;
2148 if (ia_allocate(&ia_na, iaid, "TestDUID", 8, MDL) != ISC_R_SUCCESS) {
2149 printf("ERROR: ia_allocate() %s:%d\n", MDL);
2150 return 1;
2151 }
2152 ia_remove_iasubopt(ia_na, NULL, MDL);
2153 if (ia_dereference(&ia_na, MDL) != ISC_R_SUCCESS) {
2154 printf("ERROR: ia_dereference() %s:%d\n", MDL);
2155 return 1;
2156 }
2157
2158 /*
2159 * Test 5: Basic ipv6_pool manipulation.
2160 */
2161
2162 /* allocate, reference */
2163 inet_pton(AF_INET6, "1:2:3:4::", &addr);
2164 pool = NULL;
2165 if (ipv6_pool_allocate(&pool, 0, &addr, 64, 128, MDL) != ISC_R_SUCCESS) {
2166 printf("ERROR: ipv6_pool_allocate() %s:%d\n", MDL);
2167 return 1;
2168 }
2169 if (pool->num_active != 0) {
2170 printf("ERROR: bad num_active %s:%d\n", MDL);
2171 return 1;
2172 }
2173 if (pool->bits != 64) {
2174 printf("ERROR: bad bits %s:%d\n", MDL);
2175 return 1;
2176 }
2177 inet_ntop(AF_INET6, &pool->start_addr, addr_buf, sizeof(addr_buf));
2178 if (strcmp(inet_ntop(AF_INET6, &pool->start_addr, addr_buf,
2179 sizeof(addr_buf)), "1:2:3:4::") != 0) {
2180 printf("ERROR: bad start_addr %s:%d\n", MDL);
2181 return 1;
2182 }
2183 pool_copy = NULL;
2184 if (ipv6_pool_reference(&pool_copy, pool, MDL) != ISC_R_SUCCESS) {
2185 printf("ERROR: ipv6_pool_reference() %s:%d\n", MDL);
2186 return 1;
2187 }
2188
2189 /* create_lease6, renew_lease6, expire_lease6 */
2190 uid = "client0";
2191 memset(&ds, 0, sizeof(ds));
2192 ds.len = strlen(uid);
2193 if (!buffer_allocate(&ds.buffer, ds.len, MDL)) {
2194 printf("Out of memory\n");
2195 return 1;
2196 }
2197 ds.data = ds.buffer->data;
2198 memcpy((char *)ds.data, uid, ds.len);
2199 if (create_lease6(pool, &iaaddr,
2200 &attempts, &ds, 1) != ISC_R_SUCCESS) {
2201 printf("ERROR: create_lease6() %s:%d\n", MDL);
2202 return 1;
2203 }
2204 if (pool->num_inactive != 1) {
2205 printf("ERROR: bad num_inactive %s:%d\n", MDL);
2206 return 1;
2207 }
2208 if (renew_lease6(pool, iaaddr) != ISC_R_SUCCESS) {
2209 printf("ERROR: renew_lease6() %s:%d\n", MDL);
2210 return 1;
2211 }
2212 if (pool->num_active != 1) {
2213 printf("ERROR: bad num_active %s:%d\n", MDL);
2214 return 1;
2215 }
2216 expired_iaaddr = NULL;
2217 if (expire_lease6(&expired_iaaddr, pool, 0) != ISC_R_SUCCESS) {
2218 printf("ERROR: expire_lease6() %s:%d\n", MDL);
2219 return 1;
2220 }
2221 if (expired_iaaddr != NULL) {
2222 printf("ERROR: should not have expired a lease %s:%d\n", MDL);
2223 return 1;
2224 }
2225 if (pool->num_active != 1) {
2226 printf("ERROR: bad num_active %s:%d\n", MDL);
2227 return 1;
2228 }
2229 if (expire_lease6(&expired_iaaddr, pool, 1000) != ISC_R_SUCCESS) {
2230 printf("ERROR: expire_lease6() %s:%d\n", MDL);
2231 return 1;
2232 }
2233 if (expired_iaaddr == NULL) {
2234 printf("ERROR: should have expired a lease %s:%d\n", MDL);
2235 return 1;
2236 }
2237 if (iasubopt_dereference(&expired_iaaddr, MDL) != ISC_R_SUCCESS) {
2238 printf("ERROR: iasubopt_dereference() %s:%d\n", MDL);
2239 return 1;
2240 }
2241 if (pool->num_active != 0) {
2242 printf("ERROR: bad num_active %s:%d\n", MDL);
2243 return 1;
2244 }
2245 if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
2246 printf("ERROR: iasubopt_dereference() %s:%d\n", MDL);
2247 return 1;
2248 }
2249
2250 /* release_lease6, decline_lease6 */
2251 if (create_lease6(pool, &iaaddr, &attempts,
2252 &ds, 1) != ISC_R_SUCCESS) {
2253 printf("ERROR: create_lease6() %s:%d\n", MDL);
2254 return 1;
2255 }
2256 if (renew_lease6(pool, iaaddr) != ISC_R_SUCCESS) {
2257 printf("ERROR: renew_lease6() %s:%d\n", MDL);
2258 return 1;
2259 }
2260 if (pool->num_active != 1) {
2261 printf("ERROR: bad num_active %s:%d\n", MDL);
2262 return 1;
2263 }
2264 if (release_lease6(pool, iaaddr) != ISC_R_SUCCESS) {
2265 printf("ERROR: decline_lease6() %s:%d\n", MDL);
2266 return 1;
2267 }
2268 if (pool->num_active != 0) {
2269 printf("ERROR: bad num_active %s:%d\n", MDL);
2270 return 1;
2271 }
2272 if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
2273 printf("ERROR: iasubopt_dereference() %s:%d\n", MDL);
2274 return 1;
2275 }
2276 if (create_lease6(pool, &iaaddr, &attempts,
2277 &ds, 1) != ISC_R_SUCCESS) {
2278 printf("ERROR: create_lease6() %s:%d\n", MDL);
2279 return 1;
2280 }
2281 if (renew_lease6(pool, iaaddr) != ISC_R_SUCCESS) {
2282 printf("ERROR: renew_lease6() %s:%d\n", MDL);
2283 return 1;
2284 }
2285 if (pool->num_active != 1) {
2286 printf("ERROR: bad num_active %s:%d\n", MDL);
2287 return 1;
2288 }
2289 if (decline_lease6(pool, iaaddr) != ISC_R_SUCCESS) {
2290 printf("ERROR: decline_lease6() %s:%d\n", MDL);
2291 return 1;
2292 }
2293 if (pool->num_active != 1) {
2294 printf("ERROR: bad num_active %s:%d\n", MDL);
2295 return 1;
2296 }
2297 if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
2298 printf("ERROR: iasubopt_dereference() %s:%d\n", MDL);
2299 return 1;
2300 }
2301
2302 /* dereference */
2303 if (ipv6_pool_dereference(&pool, MDL) != ISC_R_SUCCESS) {
2304 printf("ERROR: ipv6_pool_reference() %s:%d\n", MDL);
2305 return 1;
2306 }
2307 if (ipv6_pool_dereference(&pool_copy, MDL) != ISC_R_SUCCESS) {
2308 printf("ERROR: ipv6_pool_reference() %s:%d\n", MDL);
2309 return 1;
2310 }
2311
2312 /*
2313 * Test 6: Error ipv6_pool manipulation
2314 */
2315 if (ipv6_pool_allocate(NULL, 0, &addr,
2316 64, 128, MDL) != DHCP_R_INVALIDARG) {
2317 printf("ERROR: ipv6_pool_allocate() %s:%d\n", MDL);
2318 return 1;
2319 }
2320 pool = (struct ipv6_pool *)1;
2321 if (ipv6_pool_allocate(&pool, 0, &addr,
2322 64, 128, MDL) != DHCP_R_INVALIDARG) {
2323 printf("ERROR: ipv6_pool_allocate() %s:%d\n", MDL);
2324 return 1;
2325 }
2326 if (ipv6_pool_reference(NULL, pool, MDL) != DHCP_R_INVALIDARG) {
2327 printf("ERROR: ipv6_pool_reference() %s:%d\n", MDL);
2328 return 1;
2329 }
2330 pool_copy = (struct ipv6_pool *)1;
2331 if (ipv6_pool_reference(&pool_copy, pool, MDL) != DHCP_R_INVALIDARG) {
2332 printf("ERROR: ipv6_pool_reference() %s:%d\n", MDL);
2333 return 1;
2334 }
2335 pool_copy = NULL;
2336 if (ipv6_pool_reference(&pool_copy, NULL, MDL) != DHCP_R_INVALIDARG) {
2337 printf("ERROR: ipv6_pool_reference() %s:%d\n", MDL);
2338 return 1;
2339 }
2340 if (ipv6_pool_dereference(NULL, MDL) != DHCP_R_INVALIDARG) {
2341 printf("ERROR: ipv6_pool_dereference() %s:%d\n", MDL);
2342 return 1;
2343 }
2344 if (ipv6_pool_dereference(&pool_copy, MDL) != DHCP_R_INVALIDARG) {
2345 printf("ERROR: ipv6_pool_dereference() %s:%d\n", MDL);
2346 return 1;
2347 }
2348
2349 /*
2350 * Test 7: order of expiration
2351 */
2352 pool = NULL;
2353 if (ipv6_pool_allocate(&pool, 0, &addr, 64, 128, MDL) != ISC_R_SUCCESS) {
2354 printf("ERROR: ipv6_pool_allocate() %s:%d\n", MDL);
2355 return 1;
2356 }
2357 for (i=10; i<100; i+=10) {
2358 if (create_lease6(pool, &iaaddr, &attempts,
2359 &ds, i) != ISC_R_SUCCESS) {
2360 printf("ERROR: create_lease6() %s:%d\n", MDL);
2361 return 1;
2362 }
2363 if (renew_lease6(pool, iaaddr) != ISC_R_SUCCESS) {
2364 printf("ERROR: renew_lease6() %s:%d\n", MDL);
2365 return 1;
2366 }
2367 if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
2368 printf("ERROR: iasubopt_dereference() %s:%d\n", MDL);
2369 return 1;
2370 }
2371 if (pool->num_active != (i / 10)) {
2372 printf("ERROR: bad num_active %s:%d\n", MDL);
2373 return 1;
2374 }
2375 }
2376 if (pool->num_active != 9) {
2377 printf("ERROR: bad num_active %s:%d\n", MDL);
2378 return 1;
2379 }
2380 for (i=10; i<100; i+=10) {
2381 if (expire_lease6(&expired_iaaddr,
2382 pool, 1000) != ISC_R_SUCCESS) {
2383 printf("ERROR: expire_lease6() %s:%d\n", MDL);
2384 return 1;
2385 }
2386 if (expired_iaaddr == NULL) {
2387 printf("ERROR: should have expired a lease %s:%d\n",
2388 MDL);
2389 return 1;
2390 }
2391 if (pool->num_active != (9 - (i / 10))) {
2392 printf("ERROR: bad num_active %s:%d\n", MDL);
2393 return 1;
2394 }
2395 if (expired_iaaddr->hard_lifetime_end_time != i) {
2396 printf("ERROR: bad hard_lifetime_end_time %s:%d\n",
2397 MDL);
2398 return 1;
2399 }
2400 if (iasubopt_dereference(&expired_iaaddr, MDL) !=
2401 ISC_R_SUCCESS) {
2402 printf("ERROR: iasubopt_dereference() %s:%d\n", MDL);
2403 return 1;
2404 }
2405 }
2406 if (pool->num_active != 0) {
2407 printf("ERROR: bad num_active %s:%d\n", MDL);
2408 return 1;
2409 }
2410 expired_iaaddr = NULL;
2411 if (expire_lease6(&expired_iaaddr, pool, 1000) != ISC_R_SUCCESS) {
2412 printf("ERROR: expire_lease6() %s:%d\n", MDL);
2413 return 1;
2414 }
2415 if (ipv6_pool_dereference(&pool, MDL) != ISC_R_SUCCESS) {
2416 printf("ERROR: ipv6_pool_dereference() %s:%d\n", MDL);
2417 return 1;
2418 }
2419
2420 /*
2421 * Test 8: small pool
2422 */
2423 pool = NULL;
2424 addr.s6_addr[14] = 0x81;
2425 if (ipv6_pool_allocate(&pool, 0, &addr, 127, 128, MDL) != ISC_R_SUCCESS) {
2426 printf("ERROR: ipv6_pool_allocate() %s:%d\n", MDL);
2427 return 1;
2428 }
2429 if (create_lease6(pool, &iaaddr, &attempts,
2430 &ds, 42) != ISC_R_SUCCESS) {
2431 printf("ERROR: create_lease6() %s:%d\n", MDL);
2432 return 1;
2433 }
2434 if (renew_lease6(pool, iaaddr) != ISC_R_SUCCESS) {
2435 printf("ERROR: renew_lease6() %s:%d\n", MDL);
2436 return 1;
2437 }
2438 if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
2439 printf("ERROR: iasubopt_dereference() %s:%d\n", MDL);
2440 return 1;
2441 }
2442 if (create_lease6(pool, &iaaddr, &attempts,
2443 &ds, 11) != ISC_R_SUCCESS) {
2444 printf("ERROR: create_lease6() %s:%d\n", MDL);
2445 return 1;
2446 }
2447 if (renew_lease6(pool, iaaddr) != ISC_R_SUCCESS) {
2448 printf("ERROR: renew_lease6() %s:%d\n", MDL);
2449 return 1;
2450 }
2451 if (iasubopt_dereference(&iaaddr, MDL) != ISC_R_SUCCESS) {
2452 printf("ERROR: iasubopt_dereference() %s:%d\n", MDL);
2453 return 1;
2454 }
2455 if (create_lease6(pool, &iaaddr, &attempts,
2456 &ds, 11) != ISC_R_NORESOURCES) {
2457 printf("ERROR: create_lease6() %s:%d\n", MDL);
2458 return 1;
2459 }
2460 if (ipv6_pool_dereference(&pool, MDL) != ISC_R_SUCCESS) {
2461 printf("ERROR: ipv6_pool_dereference() %s:%d\n", MDL);
2462 return 1;
2463 }
2464 addr.s6_addr[14] = 0;
2465
2466 /*
2467 * Test 9: functions across all pools
2468 */
2469 pool = NULL;
2470 if (ipv6_pool_allocate(&pool, 0, &addr, 64, 128, MDL) != ISC_R_SUCCESS) {
2471 printf("ERROR: ipv6_pool_allocate() %s:%d\n", MDL);
2472 return 1;
2473 }
2474 if (add_ipv6_pool(pool) != ISC_R_SUCCESS) {
2475 printf("ERROR: add_ipv6_pool() %s:%d\n", MDL);
2476 return 1;
2477 }
2478 if (ipv6_pool_dereference(&pool, MDL) != ISC_R_SUCCESS) {
2479 printf("ERROR: ipv6_pool_dereference() %s:%d\n", MDL);
2480 return 1;
2481 }
2482 pool = NULL;
2483 if (find_ipv6_pool(&pool, 0, &addr) != ISC_R_SUCCESS) {
2484 printf("ERROR: find_ipv6_pool() %s:%d\n", MDL);
2485 return 1;
2486 }
2487 if (ipv6_pool_dereference(&pool, MDL) != ISC_R_SUCCESS) {
2488 printf("ERROR: ipv6_pool_dereference() %s:%d\n", MDL);
2489 return 1;
2490 }
2491 inet_pton(AF_INET6, "1:2:3:4:ffff:ffff:ffff:ffff", &addr);
2492 pool = NULL;
2493 if (find_ipv6_pool(&pool, 0, &addr) != ISC_R_SUCCESS) {
2494 printf("ERROR: find_ipv6_pool() %s:%d\n", MDL);
2495 return 1;
2496 }
2497 if (ipv6_pool_dereference(&pool, MDL) != ISC_R_SUCCESS) {
2498 printf("ERROR: ipv6_pool_dereference() %s:%d\n", MDL);
2499 return 1;
2500 }
2501 inet_pton(AF_INET6, "1:2:3:5::", &addr);
2502 pool = NULL;
2503 if (find_ipv6_pool(&pool, 0, &addr) != ISC_R_NOTFOUND) {
2504 printf("ERROR: find_ipv6_pool() %s:%d\n", MDL);
2505 return 1;
2506 }
2507 inet_pton(AF_INET6, "1:2:3:3:ffff:ffff:ffff:ffff", &addr);
2508 pool = NULL;
2509 if (find_ipv6_pool(&pool, 0, &addr) != ISC_R_NOTFOUND) {
2510 printf("ERROR: find_ipv6_pool() %s:%d\n", MDL);
2511 return 1;
2512 }
2513
2514 /* iaid = 666;
2515 ia_na = NULL;
2516 if (ia_allocate(&ia_na, iaid, "TestDUID", 8, MDL) != ISC_R_SUCCESS) {
2517 printf("ERROR: ia_allocate() %s:%d\n", MDL);
2518 return 1;
2519 }*/
2520
2521 {
2522 struct in6_addr r;
2523 struct data_string ds;
2524 u_char data[16];
2525 char buf[64];
2526 int i, j;
2527
2528 memset(&ds, 0, sizeof(ds));
2529 memset(data, 0xaa, sizeof(data));
2530 ds.len = 16;
2531 ds.data = data;
2532
2533 inet_pton(AF_INET6, "3ffe:501:ffff:100::", &addr);
2534 for (i = 32; i < 42; i++)
2535 for (j = i + 1; j < 49; j++) {
2536 memset(&r, 0, sizeof(r));
2537 memset(buf, 0, 64);
2538 build_prefix6(&r, &addr, i, j, &ds);
2539 inet_ntop(AF_INET6, &r, buf, 64);
2540 printf("%d,%d-> %s/%d\n", i, j, buf, j);
2541 }
2542 }
2543
2544 printf("SUCCESS: all tests passed (ignore any warning messages)\n");
2545 return 0;
2546 }
2547 #endif