Remove building with NOCRYPTO option
[minix.git] / external / bsd / dhcp / dist / server / mdb6.c
blob80efd7fe15f9c7edb79c90cd6f62b20dd8360033
1 /* $NetBSD: mdb6.c,v 1.5 2014/07/12 12:09:38 spz Exp $ */
2 /*
3 * Copyright (C) 2007-2013 by Internet Systems Consortium, Inc. ("ISC")
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
18 #include <sys/cdefs.h>
19 __RCSID("$NetBSD: mdb6.c,v 1.5 2014/07/12 12:09:38 spz Exp $");
21 /*!
22 * \todo assert()
23 * \todo simplify functions, as pool is now in iaaddr
26 /*! \file server/mdb6.c
28 * \page ipv6structures IPv6 Structures Overview
30 * A brief description of the IPv6 structures as reverse engineered.
32 * There are four major data structures in the lease configuraion.
34 * - shared_network - The shared network is the outer enclosing scope for a
35 * network region that shares a broadcast domain. It is
36 * composed of one or more subnets all of which are valid
37 * in the given region. The share network may be
38 * explicitly defined or implicitly created if there is
39 * only a subnet statement. This structrure is shared
40 * with v4. Each shared network statment or naked subnet
41 * will map to one of these structures
43 * - subnet - The subnet structure mostly specifies the address range
44 * that could be valid in a given region. This structute
45 * doesn't include the addresses that the server can delegate
46 * those are in the ipv6_pool. This structure is also shared
47 * with v4. Each subnet statement will map to one of these
48 * structures.
50 * - ipv6_pond - The pond structure is a grouping of the address and prefix
51 * information via the pointers to the ipv6_pool and the
52 * allowability of this pool for given clinets via the permit
53 * lists and the valid TIMEs. This is equivilent to the v4
54 * pool structure and would have been named ip6_pool except
55 * that the name was already in use. Generally each pool6
56 * statement will map to one of these structures. In addition
57 * there may be one or for each group of naked range6 and
58 * prefix6 statements within a shared network that share
59 * the same group of statements.
61 * - ipv6_pool - this contains information about a pool of addresses or prefixes
62 * that the server is using. This includes a hash table that
63 * tracks the active items and a pair of heap tables one for
64 * active items and one for non-active items. The heap tables
65 * are used to determine the next items to be modified due to
66 * timing events (expire mostly).
68 * The linkages then look like this:
69 * \verbatim
70 *+--------------+ +-------------+
71 *|Shared Network| | ipv6_pond |
72 *| group | | group |
73 *| | | permit info |
74 *| | | next ---->
75 *| ponds ---->| |
76 *| |<---- shared |
77 *| Subnets | | pools |
78 *+-----|--------+ +------|------+
79 * | ^ | ^
80 * | | v |
81 * | | +-----------|-+
82 * | | | ipv6_pool | |
83 * | | | type | |
84 * | | | ipv6_pond |
85 * | | | |
86 * | | | next ---->
87 * | | | |
88 * | | | subnet |
89 * | | +-----|-------+
90 * | | |
91 * | | v
92 * | | +-------------+
93 * | | | subnet |
94 * | +---------- shared |
95 * +----------->| |
96 * | group |
97 * +-------------+
99 * The shared network contains a list of all the subnets that are on a broadcast
100 * doamin. These can be used to determine if an address makes sense in a given
101 * domain, but the subnets do not contain the addresses the server can delegate.
102 * Those are stored in the ponds and pools.
104 * In the simple case to find an acceptable address the server would first find
105 * the shared network the client is on based on either the interface used to
106 * receive the request or the relay agent's information. From the shared
107 * network the server will walk through it's list of ponds. For each pond it
108 * will evaluate the permit information against the (already done) classification.
109 * If it finds an acceptable pond it will then walk through the pools for that
110 * pond. The server first checks the type of the pool (NA, TA and PD) agaisnt the
111 * request and if they match it attemps to find an address within that pool. On
112 * success the address is used, on failure the server steps to the next pool and
113 * if necessary to the next pond.
115 * When the server is successful in finding an address it will execute any
116 * statements assocaited with the pond, then the subnet, then the shared
117 * network the group field is for in the above picture).
119 * In configurations that don't include either a shared network or a pool6
120 * statement (or both) the missing pieces are created.
123 * There are three major data structuress involved in the lease database:
125 * - ipv6_pool - see above
126 * - ia_xx - this contains information about a single IA from a request
127 * normally it will contain one pointer to a lease for the client
128 * but it may contain more in some circumstances. There are 3
129 * hash tables to aid in accessing these one each for NA, TA and PD.
130 * - iasubopt - the v6 lease structure. These are created dynamically when
131 * a client asks for something and will eventually be destroyed
132 * if the client doesn't re-ask for that item. A lease has space
133 * for backpointers to the IA and to the pool to which it belongs.
134 * The pool backpointer is always filled, the IA pointer may not be.
136 * In normal use we then have something like this:
138 * \verbatim
139 * ia hash tables
140 * ia_na_active +----------------+
141 * ia_ta_active +------------+ | pool |
142 * ia_pd_active | iasubopt |<--| active hash |
143 * +-----------------+ | aka lease |<--| active heap |
144 * | ia_xx | | pool ptr |-->| |
145 * | iasubopt array |<---| iaptr |<--| inactive heap |
146 * | lease ptr |--->| | | |
147 * +-----------------+ +------------+ +----------------+
148 * \endverbatim
150 * For the pool either the inactive heap will have a pointer
151 * or both the active heap and the active hash will have pointers.
153 * I think there are several major items to notice. The first is
154 * that as a lease moves around it will be added to and removed
155 * from the address hash table in the pool and between the active
156 * and inactive hash tables. The hash table and the active heap
157 * are used when the lease is either active or abandoned. The
158 * inactive heap is used for all other states. In particular a
159 * lease that has expired or been released will be cleaned
160 * (DDNS removal etc) and then moved to the inactive heap. After
161 * some time period (currently 1 hour) it will be freed.
163 * The second is that when a client requests specific addresses,
164 * either because it previously owned them or if the server supplied
165 * them as part of a solicit, the server will try to lookup the ia_xx
166 * associated with the client and find the addresses there. If it
167 * does find appropriate leases it moves them from the old IA to
168 * a new IA and eventually replaces the old IA with the new IA
169 * in the IA hash tables.
172 #include "config.h"
174 #include <sys/types.h>
175 #include <time.h>
176 #include <netinet/in.h>
178 #include <stdarg.h>
179 #include "dhcpd.h"
180 #include "omapip/omapip.h"
181 #include "omapip/hash.h"
182 #include <isc/md5.h>
184 HASH_FUNCTIONS(ia, unsigned char *, struct ia_xx, ia_hash_t,
185 ia_reference, ia_dereference, do_string_hash)
187 ia_hash_t *ia_na_active;
188 ia_hash_t *ia_ta_active;
189 ia_hash_t *ia_pd_active;
191 HASH_FUNCTIONS(iasubopt, struct in6_addr *, struct iasubopt, iasubopt_hash_t,
192 iasubopt_reference, iasubopt_dereference, do_string_hash)
194 struct ipv6_pool **pools;
195 int num_pools;
198 * Create a new IAADDR/PREFIX structure.
200 * - iasubopt must be a pointer to a (struct iasubopt *) pointer previously
201 * initialized to NULL
203 isc_result_t
204 iasubopt_allocate(struct iasubopt **iasubopt, const char *file, int line) {
205 struct iasubopt *tmp;
207 if (iasubopt == NULL) {
208 log_error("%s(%d): NULL pointer reference", file, line);
209 return DHCP_R_INVALIDARG;
211 if (*iasubopt != NULL) {
212 log_error("%s(%d): non-NULL pointer", file, line);
213 return DHCP_R_INVALIDARG;
216 tmp = dmalloc(sizeof(*tmp), file, line);
217 if (tmp == NULL) {
218 return ISC_R_NOMEMORY;
221 tmp->refcnt = 1;
222 tmp->state = FTS_FREE;
223 tmp->heap_index = -1;
224 tmp->plen = 255;
226 *iasubopt = tmp;
227 return ISC_R_SUCCESS;
231 * Reference an IAADDR/PREFIX structure.
233 * - iasubopt must be a pointer to a (struct iasubopt *) pointer previously
234 * initialized to NULL
236 isc_result_t
237 iasubopt_reference(struct iasubopt **iasubopt, struct iasubopt *src,
238 const char *file, int line) {
239 if (iasubopt == NULL) {
240 log_error("%s(%d): NULL pointer reference", file, line);
241 return DHCP_R_INVALIDARG;
243 if (*iasubopt != NULL) {
244 log_error("%s(%d): non-NULL pointer", file, line);
245 return DHCP_R_INVALIDARG;
247 if (src == NULL) {
248 log_error("%s(%d): NULL pointer reference", file, line);
249 return DHCP_R_INVALIDARG;
251 *iasubopt = src;
252 src->refcnt++;
253 return ISC_R_SUCCESS;
258 * Dereference an IAADDR/PREFIX structure.
260 * If it is the last reference, then the memory for the
261 * structure is freed.
263 isc_result_t
264 iasubopt_dereference(struct iasubopt **iasubopt, const char *file, int line) {
265 struct iasubopt *tmp;
267 if ((iasubopt == NULL) || (*iasubopt == NULL)) {
268 log_error("%s(%d): NULL pointer", file, line);
269 return DHCP_R_INVALIDARG;
272 tmp = *iasubopt;
273 *iasubopt = NULL;
275 tmp->refcnt--;
276 if (tmp->refcnt < 0) {
277 log_error("%s(%d): negative refcnt", file, line);
278 tmp->refcnt = 0;
280 if (tmp->refcnt == 0) {
281 if (tmp->ia != NULL) {
282 ia_dereference(&(tmp->ia), file, line);
284 if (tmp->ipv6_pool != NULL) {
285 ipv6_pool_dereference(&(tmp->ipv6_pool), file, line);
287 if (tmp->scope != NULL) {
288 binding_scope_dereference(&tmp->scope, file, line);
291 if (tmp->on_star.on_expiry != NULL) {
292 executable_statement_dereference
293 (&tmp->on_star.on_expiry, MDL);
295 if (tmp->on_star.on_commit != NULL) {
296 executable_statement_dereference
297 (&tmp->on_star.on_commit, MDL);
299 if (tmp->on_star.on_release != NULL) {
300 executable_statement_dereference
301 (&tmp->on_star.on_release, MDL);
304 dfree(tmp, file, line);
307 return ISC_R_SUCCESS;
311 * Make the key that we use for IA.
313 isc_result_t
314 ia_make_key(struct data_string *key, u_int32_t iaid,
315 const char *duid, unsigned int duid_len,
316 const char *file, int line) {
318 memset(key, 0, sizeof(*key));
319 key->len = duid_len + sizeof(iaid);
320 if (!buffer_allocate(&(key->buffer), key->len, file, line)) {
321 return ISC_R_NOMEMORY;
323 key->data = key->buffer->data;
324 memcpy((char *)key->data, &iaid, sizeof(iaid));
325 memcpy((char *)key->data + sizeof(iaid), duid, duid_len);
327 return ISC_R_SUCCESS;
331 * Create a new IA structure.
333 * - ia must be a pointer to a (struct ia_xx *) pointer previously
334 * initialized to NULL
335 * - iaid and duid are values from the client
337 * XXXsk: we don't concern ourself with the byte order of the IAID,
338 * which might be a problem if we transfer this structure
339 * between machines of different byte order
341 isc_result_t
342 ia_allocate(struct ia_xx **ia, u_int32_t iaid,
343 const char *duid, unsigned int duid_len,
344 const char *file, int line) {
345 struct ia_xx *tmp;
347 if (ia == NULL) {
348 log_error("%s(%d): NULL pointer reference", file, line);
349 return DHCP_R_INVALIDARG;
351 if (*ia != NULL) {
352 log_error("%s(%d): non-NULL pointer", file, line);
353 return DHCP_R_INVALIDARG;
356 tmp = dmalloc(sizeof(*tmp), file, line);
357 if (tmp == NULL) {
358 return ISC_R_NOMEMORY;
361 if (ia_make_key(&tmp->iaid_duid, iaid,
362 duid, duid_len, file, line) != ISC_R_SUCCESS) {
363 dfree(tmp, file, line);
364 return ISC_R_NOMEMORY;
367 tmp->refcnt = 1;
369 *ia = tmp;
370 return ISC_R_SUCCESS;
374 * Reference an IA structure.
376 * - ia must be a pointer to a (struct ia_xx *) pointer previously
377 * initialized to NULL
379 isc_result_t
380 ia_reference(struct ia_xx **ia, struct ia_xx *src,
381 const char *file, int line) {
382 if (ia == NULL) {
383 log_error("%s(%d): NULL pointer reference", file, line);
384 return DHCP_R_INVALIDARG;
386 if (*ia != NULL) {
387 log_error("%s(%d): non-NULL pointer", file, line);
388 return DHCP_R_INVALIDARG;
390 if (src == NULL) {
391 log_error("%s(%d): NULL pointer reference", file, line);
392 return DHCP_R_INVALIDARG;
394 *ia = src;
395 src->refcnt++;
396 return ISC_R_SUCCESS;
400 * Dereference an IA structure.
402 * If it is the last reference, then the memory for the
403 * structure is freed.
405 isc_result_t
406 ia_dereference(struct ia_xx **ia, const char *file, int line) {
407 struct ia_xx *tmp;
408 int i;
410 if ((ia == NULL) || (*ia == NULL)) {
411 log_error("%s(%d): NULL pointer", file, line);
412 return DHCP_R_INVALIDARG;
415 tmp = *ia;
416 *ia = NULL;
418 tmp->refcnt--;
419 if (tmp->refcnt < 0) {
420 log_error("%s(%d): negative refcnt", file, line);
421 tmp->refcnt = 0;
423 if (tmp->refcnt == 0) {
424 if (tmp->iasubopt != NULL) {
425 for (i=0; i<tmp->num_iasubopt; i++) {
426 iasubopt_dereference(&(tmp->iasubopt[i]),
427 file, line);
429 dfree(tmp->iasubopt, file, line);
431 data_string_forget(&(tmp->iaid_duid), file, line);
432 dfree(tmp, file, line);
434 return ISC_R_SUCCESS;
439 * Add an IAADDR/PREFIX entry to an IA structure.
441 isc_result_t
442 ia_add_iasubopt(struct ia_xx *ia, struct iasubopt *iasubopt,
443 const char *file, int line) {
444 int max;
445 struct iasubopt **new;
448 * Grow our array if we need to.
450 * Note: we pick 4 as the increment, as that seems a reasonable
451 * guess as to how many addresses/prefixes we might expect
452 * on an interface.
454 if (ia->max_iasubopt <= ia->num_iasubopt) {
455 max = ia->max_iasubopt + 4;
456 new = dmalloc(max * sizeof(struct iasubopt *), file, line);
457 if (new == NULL) {
458 return ISC_R_NOMEMORY;
460 memcpy(new, ia->iasubopt,
461 ia->num_iasubopt * sizeof(struct iasubopt *));
462 ia->iasubopt = new;
463 ia->max_iasubopt = max;
466 iasubopt_reference(&(ia->iasubopt[ia->num_iasubopt]), iasubopt,
467 file, line);
468 ia->num_iasubopt++;
470 return ISC_R_SUCCESS;
474 * Remove an IAADDR/PREFIX entry to an IA structure.
476 * Note: if a suboption appears more than once, then only ONE will be removed.
478 void
479 ia_remove_iasubopt(struct ia_xx *ia, struct iasubopt *iasubopt,
480 const char *file, int line) {
481 int i, j;
482 if (ia == NULL || iasubopt == NULL)
483 return;
485 for (i=0; i<ia->num_iasubopt; i++) {
486 if (ia->iasubopt[i] == iasubopt) {
487 /* remove this sub option */
488 iasubopt_dereference(&(ia->iasubopt[i]), file, line);
489 /* move remaining suboption pointers down one */
490 for (j=i+1; j < ia->num_iasubopt; j++) {
491 ia->iasubopt[j-1] = ia->iasubopt[j];
493 /* decrease our total count */
494 /* remove the back-reference in the suboption itself */
495 ia_dereference(&iasubopt->ia, file, line);
496 ia->num_iasubopt--;
497 return;
500 log_error("%s(%d): IAADDR/PREFIX not in IA", file, line);
504 * Remove all addresses/prefixes from an IA.
506 void
507 ia_remove_all_lease(struct ia_xx *ia, const char *file, int line) {
508 int i;
510 for (i=0; i<ia->num_iasubopt; i++) {
511 ia_dereference(&(ia->iasubopt[i]->ia), file, line);
512 iasubopt_dereference(&(ia->iasubopt[i]), file, line);
514 ia->num_iasubopt = 0;
518 * Compare two IA.
520 isc_boolean_t
521 ia_equal(const struct ia_xx *a, const struct ia_xx *b)
523 isc_boolean_t found;
524 int i, j;
527 * Handle cases where one or both of the inputs is NULL.
529 if (a == NULL) {
530 if (b == NULL) {
531 return ISC_TRUE;
532 } else {
533 return ISC_FALSE;
538 * Check the type is the same.
540 if (a->ia_type != b->ia_type) {
541 return ISC_FALSE;
545 * Check the DUID is the same.
547 if (a->iaid_duid.len != b->iaid_duid.len) {
548 return ISC_FALSE;
550 if (memcmp(a->iaid_duid.data,
551 b->iaid_duid.data, a->iaid_duid.len) != 0) {
552 return ISC_FALSE;
556 * Make sure we have the same number of addresses/prefixes in each.
558 if (a->num_iasubopt != b->num_iasubopt) {
559 return ISC_FALSE;
563 * Check that each address/prefix is present in both.
565 for (i=0; i<a->num_iasubopt; i++) {
566 found = ISC_FALSE;
567 for (j=0; j<a->num_iasubopt; j++) {
568 if (a->iasubopt[i]->plen != b->iasubopt[i]->plen)
569 continue;
570 if (memcmp(&(a->iasubopt[i]->addr),
571 &(b->iasubopt[j]->addr),
572 sizeof(struct in6_addr)) == 0) {
573 found = ISC_TRUE;
574 break;
577 if (!found) {
578 return ISC_FALSE;
583 * These are the same in every way we care about.
585 return ISC_TRUE;
589 * Helper function for lease heaps.
590 * Makes the top of the heap the oldest lease.
592 static isc_boolean_t
593 lease_older(void *a, void *b) {
594 struct iasubopt *la = (struct iasubopt *)a;
595 struct iasubopt *lb = (struct iasubopt *)b;
597 if (la->hard_lifetime_end_time == lb->hard_lifetime_end_time) {
598 return difftime(la->soft_lifetime_end_time,
599 lb->soft_lifetime_end_time) < 0;
600 } else {
601 return difftime(la->hard_lifetime_end_time,
602 lb->hard_lifetime_end_time) < 0;
607 * Helper function for lease address/prefix heaps.
608 * Callback when an address's position in the heap changes.
610 static void
611 lease_index_changed(void *iasubopt, unsigned int new_heap_index) {
612 ((struct iasubopt *)iasubopt)-> heap_index = new_heap_index;
618 * \brief Create a new IPv6 lease pool structure
620 * Allocate space for a new ipv6_pool structure and return a reference
621 * to it, includes setting the reference count to 1.
623 * \param pool = space for returning a referenced pointer to the pool.
624 * This must point to a space that has been initialzied
625 * to NULL by the caller.
626 * \param[in] type = The type of the pool NA, TA or PD
627 * \param[in] start_addr = The first address in the range for the pool
628 * \param[in] bits = The contiguous bits of the pool
631 * \return
632 * ISC_R_SUCCESS = The pool was successfully created, pool points to it.
633 * DHCP_R_INVALIDARG = One of the arugments was invalid, pool has not been
634 * modified
635 * ISC_R_NOMEMORY = The system wasn't able to allocate memory, pool has
636 * not been modified.
638 isc_result_t
639 ipv6_pool_allocate(struct ipv6_pool **pool, u_int16_t type,
640 const struct in6_addr *start_addr, int bits,
641 int units, const char *file, int line) {
642 struct ipv6_pool *tmp;
644 if (pool == NULL) {
645 log_error("%s(%d): NULL pointer reference", file, line);
646 return DHCP_R_INVALIDARG;
648 if (*pool != NULL) {
649 log_error("%s(%d): non-NULL pointer", file, line);
650 return DHCP_R_INVALIDARG;
653 tmp = dmalloc(sizeof(*tmp), file, line);
654 if (tmp == NULL) {
655 return ISC_R_NOMEMORY;
658 tmp->refcnt = 1;
659 tmp->pool_type = type;
660 tmp->start_addr = *start_addr;
661 tmp->bits = bits;
662 tmp->units = units;
663 if (!iasubopt_new_hash(&tmp->leases, DEFAULT_HASH_SIZE, file, line)) {
664 dfree(tmp, file, line);
665 return ISC_R_NOMEMORY;
667 if (isc_heap_create(dhcp_gbl_ctx.mctx, lease_older, lease_index_changed,
668 0, &(tmp->active_timeouts)) != ISC_R_SUCCESS) {
669 iasubopt_free_hash_table(&(tmp->leases), file, line);
670 dfree(tmp, file, line);
671 return ISC_R_NOMEMORY;
673 if (isc_heap_create(dhcp_gbl_ctx.mctx, lease_older, lease_index_changed,
674 0, &(tmp->inactive_timeouts)) != ISC_R_SUCCESS) {
675 isc_heap_destroy(&(tmp->active_timeouts));
676 iasubopt_free_hash_table(&(tmp->leases), file, line);
677 dfree(tmp, file, line);
678 return ISC_R_NOMEMORY;
681 *pool = tmp;
682 return ISC_R_SUCCESS;
687 * \brief reference an IPv6 pool structure.
689 * This function genreates a reference to an ipv6_pool structure
690 * and increments the reference count on the structure.
692 * \param[out] pool = space for returning a referenced pointer to the pool.
693 * This must point to a space that has been initialzied
694 * to NULL by the caller.
695 * \param[in] src = A pointer to the pool to reference. This must not be
696 * NULL.
698 * \return
699 * ISC_R_SUCCESS = The pool was successfully referenced, pool now points
700 * to src.
701 * DHCP_R_INVALIDARG = One of the arugments was invalid, pool has not been
702 * modified.
704 isc_result_t
705 ipv6_pool_reference(struct ipv6_pool **pool, struct ipv6_pool *src,
706 const char *file, int line) {
707 if (pool == NULL) {
708 log_error("%s(%d): NULL pointer reference", file, line);
709 return DHCP_R_INVALIDARG;
711 if (*pool != NULL) {
712 log_error("%s(%d): non-NULL pointer", file, line);
713 return DHCP_R_INVALIDARG;
715 if (src == NULL) {
716 log_error("%s(%d): NULL pointer reference", file, line);
717 return DHCP_R_INVALIDARG;
719 *pool = src;
720 src->refcnt++;
721 return ISC_R_SUCCESS;
725 * Note: Each IAADDR/PREFIX in a pool is referenced by the pool. This is needed
726 * to prevent the lease from being garbage collected out from under the
727 * pool.
729 * The references are made from the hash and from the heap. The following
730 * helper functions dereference these when a pool is destroyed.
734 * Helper function for pool cleanup.
735 * Dereference each of the hash entries in a pool.
737 static isc_result_t
738 dereference_hash_entry(const void *name, unsigned len, void *value) {
739 struct iasubopt *iasubopt = (struct iasubopt *)value;
741 iasubopt_dereference(&iasubopt, MDL);
742 return ISC_R_SUCCESS;
746 * Helper function for pool cleanup.
747 * Dereference each of the heap entries in a pool.
749 static void
750 dereference_heap_entry(void *value, void *dummy) {
751 struct iasubopt *iasubopt = (struct iasubopt *)value;
753 iasubopt_dereference(&iasubopt, MDL);
758 * \brief de-reference an IPv6 pool structure.
760 * This function decrements the reference count in an ipv6_pool structure.
761 * If this was the last reference then the memory for the structure is
762 * freed.
764 * \param[in] pool = A pointer to the pointer to the pool that should be
765 * de-referenced. On success the pointer to the pool
766 * is cleared. It must not be NULL and must not point
767 * to NULL.
769 * \return
770 * ISC_R_SUCCESS = The pool was successfully de-referenced, pool now points
771 * to NULL
772 * DHCP_R_INVALIDARG = One of the arugments was invalid, pool has not been
773 * modified.
775 isc_result_t
776 ipv6_pool_dereference(struct ipv6_pool **pool, const char *file, int line) {
777 struct ipv6_pool *tmp;
779 if ((pool == NULL) || (*pool == NULL)) {
780 log_error("%s(%d): NULL pointer", file, line);
781 return DHCP_R_INVALIDARG;
784 tmp = *pool;
785 *pool = NULL;
787 tmp->refcnt--;
788 if (tmp->refcnt < 0) {
789 log_error("%s(%d): negative refcnt", file, line);
790 tmp->refcnt = 0;
792 if (tmp->refcnt == 0) {
793 iasubopt_hash_foreach(tmp->leases, dereference_hash_entry);
794 iasubopt_free_hash_table(&(tmp->leases), file, line);
795 isc_heap_foreach(tmp->active_timeouts,
796 dereference_heap_entry, NULL);
797 isc_heap_destroy(&(tmp->active_timeouts));
798 isc_heap_foreach(tmp->inactive_timeouts,
799 dereference_heap_entry, NULL);
800 isc_heap_destroy(&(tmp->inactive_timeouts));
801 dfree(tmp, file, line);
804 return ISC_R_SUCCESS;
808 * Create an address by hashing the input, and using that for
809 * the non-network part.
811 static void
812 build_address6(struct in6_addr *addr,
813 const struct in6_addr *net_start_addr, int net_bits,
814 const struct data_string *input) {
815 isc_md5_t ctx;
816 int net_bytes;
817 int i;
818 char *str;
819 const char *net_str;
822 * Use MD5 to get a nice 128 bit hash of the input.
823 * Yes, we know MD5 isn't cryptographically sound.
824 * No, we don't care.
826 isc_md5_init(&ctx);
827 isc_md5_update(&ctx, input->data, input->len);
828 isc_md5_final(&ctx, (unsigned char *)addr);
831 * Copy the [0..128] network bits over.
833 str = (char *)addr;
834 net_str = (const char *)net_start_addr;
835 net_bytes = net_bits / 8;
836 for (i = 0; i < net_bytes; i++) {
837 str[i] = net_str[i];
839 switch (net_bits % 8) {
840 case 1: str[i] = (str[i] & 0x7F) | (net_str[i] & 0x80); break;
841 case 2: str[i] = (str[i] & 0x3F) | (net_str[i] & 0xC0); break;
842 case 3: str[i] = (str[i] & 0x1F) | (net_str[i] & 0xE0); break;
843 case 4: str[i] = (str[i] & 0x0F) | (net_str[i] & 0xF0); break;
844 case 5: str[i] = (str[i] & 0x07) | (net_str[i] & 0xF8); break;
845 case 6: str[i] = (str[i] & 0x03) | (net_str[i] & 0xFC); break;
846 case 7: str[i] = (str[i] & 0x01) | (net_str[i] & 0xFE); break;
850 * Set the universal/local bit ("u bit") to zero for /64s. The
851 * individual/group bit ("g bit") is unchanged, because the g-bit
852 * has no meaning when the u-bit is cleared.
854 if (net_bits == 64)
855 str[8] &= ~0x02;
859 * Create a temporary address by a variant of RFC 4941 algo.
860 * Note: this should not be used for prefixes shorter than 64 bits.
862 static void
863 build_temporary6(struct in6_addr *addr,
864 const struct in6_addr *net_start_addr, int net_bits,
865 const struct data_string *input) {
866 static u_int32_t history[2];
867 static u_int32_t counter = 0;
868 isc_md5_t ctx;
869 unsigned char md[16];
872 * First time/time to reseed.
873 * Please use a good pseudo-random generator here!
875 if (counter == 0) {
876 isc_random_get(&history[0]);
877 isc_random_get(&history[1]);
881 * Use MD5 as recommended by RFC 4941.
883 isc_md5_init(&ctx);
884 isc_md5_update(&ctx, (unsigned char *)&history[0], 8UL);
885 isc_md5_update(&ctx, input->data, input->len);
886 isc_md5_final(&ctx, md);
889 * Build the address.
891 if (net_bits == 64) {
892 memcpy(&addr->s6_addr[0], &net_start_addr->s6_addr[0], 8);
893 memcpy(&addr->s6_addr[8], md, 8);
894 addr->s6_addr[8] &= ~0x02;
895 } else {
896 int net_bytes;
897 int i;
898 char *str;
899 const char *net_str;
902 * Copy the [0..128] network bits over.
904 str = (char *)addr;
905 net_str = (const char *)net_start_addr;
906 net_bytes = net_bits / 8;
907 for (i = 0; i < net_bytes; i++) {
908 str[i] = net_str[i];
910 memcpy(str + net_bytes, md, 16 - net_bytes);
911 switch (net_bits % 8) {
912 case 1: str[i] = (str[i] & 0x7F) | (net_str[i] & 0x80); break;
913 case 2: str[i] = (str[i] & 0x3F) | (net_str[i] & 0xC0); break;
914 case 3: str[i] = (str[i] & 0x1F) | (net_str[i] & 0xE0); break;
915 case 4: str[i] = (str[i] & 0x0F) | (net_str[i] & 0xF0); break;
916 case 5: str[i] = (str[i] & 0x07) | (net_str[i] & 0xF8); break;
917 case 6: str[i] = (str[i] & 0x03) | (net_str[i] & 0xFC); break;
918 case 7: str[i] = (str[i] & 0x01) | (net_str[i] & 0xFE); break;
924 * Save history for the next call.
926 memcpy((unsigned char *)&history[0], md + 8, 8);
927 counter++;
930 /* Reserved Subnet Router Anycast ::0:0:0:0. */
931 static struct in6_addr rtany;
932 /* Reserved Subnet Anycasts ::fdff:ffff:ffff:ff80-::fdff:ffff:ffff:ffff. */
933 static struct in6_addr resany;
936 * Create a lease for the given address and client duid.
938 * - pool must be a pointer to a (struct ipv6_pool *) pointer previously
939 * initialized to NULL
941 * Right now we simply hash the DUID, and if we get a collision, we hash
942 * again until we find a free address. We try this a fixed number of times,
943 * to avoid getting stuck in a loop (this is important on small pools
944 * where we can run out of space).
946 * We return the number of attempts that it took to find an available
947 * lease. This tells callers when a pool is are filling up, as
948 * well as an indication of how full the pool is; statistically the
949 * more full a pool is the more attempts must be made before finding
950 * a free lease. Realistically this will only happen in very full
951 * pools.
953 * We probably want different algorithms depending on the network size, in
954 * the long term.
956 isc_result_t
957 create_lease6(struct ipv6_pool *pool, struct iasubopt **addr,
958 unsigned int *attempts,
959 const struct data_string *uid, time_t soft_lifetime_end_time) {
960 struct data_string ds;
961 struct in6_addr tmp;
962 struct iasubopt *test_iaaddr;
963 struct data_string new_ds;
964 struct iasubopt *iaaddr;
965 isc_result_t result;
966 isc_boolean_t reserved_iid;
967 static isc_boolean_t init_resiid = ISC_FALSE;
970 * Fill the reserved IIDs.
972 if (!init_resiid) {
973 memset(&rtany, 0, 16);
974 memset(&resany, 0, 8);
975 resany.s6_addr[8] = 0xfd;
976 memset(&resany.s6_addr[9], 0xff, 6);
977 init_resiid = ISC_TRUE;
981 * Use the UID as our initial seed for the hash
983 memset(&ds, 0, sizeof(ds));
984 data_string_copy(&ds, (struct data_string *)uid, MDL);
986 *attempts = 0;
987 for (;;) {
989 * Give up at some point.
991 if (++(*attempts) > 100) {
992 data_string_forget(&ds, MDL);
993 return ISC_R_NORESOURCES;
997 * Build a resource.
999 switch (pool->pool_type) {
1000 case D6O_IA_NA:
1001 /* address */
1002 build_address6(&tmp, &pool->start_addr,
1003 pool->bits, &ds);
1004 break;
1005 case D6O_IA_TA:
1006 /* temporary address */
1007 build_temporary6(&tmp, &pool->start_addr,
1008 pool->bits, &ds);
1009 break;
1010 case D6O_IA_PD:
1011 /* prefix */
1012 log_error("create_lease6: prefix pool.");
1013 return DHCP_R_INVALIDARG;
1014 default:
1015 log_error("create_lease6: untyped pool.");
1016 return DHCP_R_INVALIDARG;
1020 * Avoid reserved interface IDs. (cf. RFC 5453)
1022 reserved_iid = ISC_FALSE;
1023 if (memcmp(&tmp.s6_addr[8], &rtany.s6_addr[8], 8) == 0) {
1024 reserved_iid = ISC_TRUE;
1026 if (!reserved_iid &&
1027 (memcmp(&tmp.s6_addr[8], &resany.s6_addr[8], 7) == 0) &&
1028 ((tmp.s6_addr[15] & 0x80) == 0x80)) {
1029 reserved_iid = ISC_TRUE;
1033 * If this address is not in use, we're happy with it
1035 test_iaaddr = NULL;
1036 if (!reserved_iid &&
1037 (iasubopt_hash_lookup(&test_iaaddr, pool->leases,
1038 &tmp, sizeof(tmp), MDL) == 0)) {
1039 break;
1041 if (test_iaaddr != NULL)
1042 iasubopt_dereference(&test_iaaddr, MDL);
1045 * Otherwise, we create a new input, adding the address
1047 memset(&new_ds, 0, sizeof(new_ds));
1048 new_ds.len = ds.len + sizeof(tmp);
1049 if (!buffer_allocate(&new_ds.buffer, new_ds.len, MDL)) {
1050 data_string_forget(&ds, MDL);
1051 return ISC_R_NOMEMORY;
1053 new_ds.data = new_ds.buffer->data;
1054 memcpy(new_ds.buffer->data, ds.data, ds.len);
1055 memcpy(new_ds.buffer->data + ds.len, &tmp, sizeof(tmp));
1056 data_string_forget(&ds, MDL);
1057 data_string_copy(&ds, &new_ds, MDL);
1058 data_string_forget(&new_ds, MDL);
1061 data_string_forget(&ds, MDL);
1064 * We're happy with the address, create an IAADDR
1065 * to hold it.
1067 iaaddr = NULL;
1068 result = iasubopt_allocate(&iaaddr, MDL);
1069 if (result != ISC_R_SUCCESS) {
1070 return result;
1072 iaaddr->plen = 0;
1073 memcpy(&iaaddr->addr, &tmp, sizeof(iaaddr->addr));
1076 * Add the lease to the pool (note state is free, not active?!).
1078 result = add_lease6(pool, iaaddr, soft_lifetime_end_time);
1079 if (result == ISC_R_SUCCESS) {
1080 iasubopt_reference(addr, iaaddr, MDL);
1082 iasubopt_dereference(&iaaddr, MDL);
1083 return result;
1089 * \brief Cleans up leases when reading from a lease file
1091 * This function is only expected to be run when reading leases in from a file.
1092 * It checks to see if a lease already exists for the new leases's address.
1093 * We don't add expired leases to the structures when reading a lease file
1094 * which limits what can happen. We have two variables the owners of the leases
1095 * being the same or different and the new lease being active or non-active:
1096 * Owners active
1097 * same no remove old lease and its connections
1098 * same yes nothing to do, other code will update the structures.
1099 * diff no nothing to do
1100 * diff yes this combination shouldn't happen, we should only have a
1101 * single active lease per address at a time and that lease
1102 * should move to non-active before any other lease can
1103 * become active for that address.
1104 * Currently we delete the previous lease and pass an error
1105 * to the caller who should log an error.
1107 * When we remove a lease we remove it from the hash table and active heap
1108 * (remember only active leases are in the structures at this time) for the
1109 * pool, and from the IA's array. If, after we've removed the pointer from
1110 * IA's array to the lease, the IA has no more pointers we remove it from
1111 * the appropriate hash table as well.
1113 * \param[in] ia_table = the hash table for the IA
1114 * \param[in] pool = the pool to update
1115 * \param[in] lease = the new lease we want to add
1116 * \param[in] ia = the new ia we are building
1118 * \return
1119 * ISC_R_SUCCESS = the incoming lease and any previous lease were in
1120 * an expected state - one of the first 3 options above.
1121 * If necessary the old lease was removed.
1122 * ISC_R_FAILURE = there is already an active lease for the address in
1123 * the incoming lease. This shouldn't happen if it does
1124 * flag an error for the caller to log.
1127 isc_result_t
1128 cleanup_lease6(ia_hash_t *ia_table,
1129 struct ipv6_pool *pool,
1130 struct iasubopt *lease,
1131 struct ia_xx *ia) {
1133 struct iasubopt *test_iasubopt, *tmp_iasubopt;
1134 struct ia_xx *old_ia;
1135 isc_result_t status = ISC_R_SUCCESS;
1137 test_iasubopt = NULL;
1138 old_ia = NULL;
1141 * Look up the address - if we don't find a lease
1142 * we don't need to do anything.
1144 if (iasubopt_hash_lookup(&test_iasubopt, pool->leases,
1145 &lease->addr, sizeof(lease->addr),
1146 MDL) == 0) {
1147 return (ISC_R_SUCCESS);
1150 if (test_iasubopt->ia == NULL) {
1151 /* no old ia, no work to do */
1152 iasubopt_dereference(&test_iasubopt, MDL);
1153 return (status);
1156 ia_reference(&old_ia, test_iasubopt->ia, MDL);
1158 if ((old_ia->iaid_duid.len == ia->iaid_duid.len) &&
1159 (memcmp((unsigned char *)ia->iaid_duid.data,
1160 (unsigned char *)old_ia->iaid_duid.data,
1161 ia->iaid_duid.len) == 0)) {
1162 /* same IA */
1163 if ((lease->state == FTS_ACTIVE) ||
1164 (lease->state == FTS_ABANDONED)) {
1165 /* still active, no need to delete */
1166 goto cleanup;
1168 } else {
1169 /* different IA */
1170 if ((lease->state != FTS_ACTIVE) &&
1171 (lease->state != FTS_ABANDONED)) {
1172 /* new lease isn't active, no work */
1173 goto cleanup;
1177 * We appear to have two active leases, this shouldn't happen.
1178 * Before a second lease can be set to active the first lease
1179 * should be set to inactive (released, expired etc). For now
1180 * delete the previous lease and indicate a failure to the
1181 * caller so it can generate a warning.
1182 * In the future we may try and determine which is the better
1183 * lease to keep.
1186 status = ISC_R_FAILURE;
1190 * Remove the old lease from the active heap and from the hash table
1191 * then remove the lease from the IA and clean up the IA if necessary.
1193 isc_heap_delete(pool->active_timeouts, test_iasubopt->heap_index);
1194 pool->num_active--;
1196 iasubopt_hash_delete(pool->leases, &test_iasubopt->addr,
1197 sizeof(test_iasubopt->addr), MDL);
1198 ia_remove_iasubopt(old_ia, test_iasubopt, MDL);
1199 if (old_ia->num_iasubopt <= 0) {
1200 ia_hash_delete(ia_table,
1201 (unsigned char *)old_ia->iaid_duid.data,
1202 old_ia->iaid_duid.len, MDL);
1206 * We derefenrece the subopt here as we've just removed it from
1207 * the hash table in the pool. We need to make a copy as we
1208 * need to derefernece it again later.
1210 tmp_iasubopt = test_iasubopt;
1211 iasubopt_dereference(&tmp_iasubopt, MDL);
1213 cleanup:
1214 ia_dereference(&old_ia, MDL);
1217 * Clean up the reference, this is in addition to the deference
1218 * above after removing the entry from the hash table
1220 iasubopt_dereference(&test_iasubopt, MDL);
1222 return (status);
1226 * Put a lease in the pool directly. This is intended to be used when
1227 * loading leases from the file.
1229 isc_result_t
1230 add_lease6(struct ipv6_pool *pool, struct iasubopt *lease,
1231 time_t valid_lifetime_end_time) {
1232 isc_result_t insert_result;
1233 struct iasubopt *test_iasubopt;
1234 struct iasubopt *tmp_iasubopt;
1236 /* If a state was not assigned by the caller, assume active. */
1237 if (lease->state == 0)
1238 lease->state = FTS_ACTIVE;
1240 ipv6_pool_reference(&lease->ipv6_pool, pool, MDL);
1243 * If this IAADDR/PREFIX is already in our structures, remove the
1244 * old one.
1246 test_iasubopt = NULL;
1247 if (iasubopt_hash_lookup(&test_iasubopt, pool->leases,
1248 &lease->addr, sizeof(lease->addr), MDL)) {
1249 /* XXX: we should probably ask the lease what heap it is on
1250 * (as a consistency check).
1251 * XXX: we should probably have one function to "put this lease
1252 * on its heap" rather than doing these if's everywhere. If
1253 * you add more states to this list, don't.
1255 if ((test_iasubopt->state == FTS_ACTIVE) ||
1256 (test_iasubopt->state == FTS_ABANDONED)) {
1257 isc_heap_delete(pool->active_timeouts,
1258 test_iasubopt->heap_index);
1259 pool->num_active--;
1260 } else {
1261 isc_heap_delete(pool->inactive_timeouts,
1262 test_iasubopt->heap_index);
1263 pool->num_inactive--;
1266 iasubopt_hash_delete(pool->leases, &test_iasubopt->addr,
1267 sizeof(test_iasubopt->addr), MDL);
1270 * We're going to do a bit of evil trickery here.
1272 * We need to dereference the entry once to remove our
1273 * current reference (in test_iasubopt), and then one
1274 * more time to remove the reference left when the
1275 * address was added to the pool before.
1277 tmp_iasubopt = test_iasubopt;
1278 iasubopt_dereference(&test_iasubopt, MDL);
1279 iasubopt_dereference(&tmp_iasubopt, MDL);
1283 * Add IAADDR/PREFIX to our structures.
1285 tmp_iasubopt = NULL;
1286 iasubopt_reference(&tmp_iasubopt, lease, MDL);
1287 if ((tmp_iasubopt->state == FTS_ACTIVE) ||
1288 (tmp_iasubopt->state == FTS_ABANDONED)) {
1289 tmp_iasubopt->hard_lifetime_end_time = valid_lifetime_end_time;
1290 iasubopt_hash_add(pool->leases, &tmp_iasubopt->addr,
1291 sizeof(tmp_iasubopt->addr), lease, MDL);
1292 insert_result = isc_heap_insert(pool->active_timeouts,
1293 tmp_iasubopt);
1294 if (insert_result == ISC_R_SUCCESS)
1295 pool->num_active++;
1296 } else {
1297 tmp_iasubopt->soft_lifetime_end_time = valid_lifetime_end_time;
1298 insert_result = isc_heap_insert(pool->inactive_timeouts,
1299 tmp_iasubopt);
1300 if (insert_result == ISC_R_SUCCESS)
1301 pool->num_inactive++;
1303 if (insert_result != ISC_R_SUCCESS) {
1304 iasubopt_hash_delete(pool->leases, &lease->addr,
1305 sizeof(lease->addr), MDL);
1306 iasubopt_dereference(&tmp_iasubopt, MDL);
1307 return insert_result;
1311 * Note: we intentionally leave tmp_iasubopt referenced; there
1312 * is a reference in the heap/hash, after all.
1315 return ISC_R_SUCCESS;
1319 * Determine if an address is present in a pool or not.
1321 isc_boolean_t
1322 lease6_exists(const struct ipv6_pool *pool, const struct in6_addr *addr) {
1323 struct iasubopt *test_iaaddr;
1325 test_iaaddr = NULL;
1326 if (iasubopt_hash_lookup(&test_iaaddr, pool->leases,
1327 (void *)addr, sizeof(*addr), MDL)) {
1328 iasubopt_dereference(&test_iaaddr, MDL);
1329 return ISC_TRUE;
1330 } else {
1331 return ISC_FALSE;
1337 * \brief Check if address is available to a lease
1339 * Determine if the address in the lease is available to that
1340 * lease. Either the address isn't in use or it is in use
1341 * but by that lease.
1343 * \param[in] lease = lease to check
1345 * \return
1346 * ISC_TRUE = The lease is allowed to use that address
1347 * ISC_FALSE = The lease isn't allowed to use that address
1349 isc_boolean_t
1350 lease6_usable(struct iasubopt *lease) {
1351 struct iasubopt *test_iaaddr;
1352 isc_boolean_t status = ISC_TRUE;
1354 test_iaaddr = NULL;
1355 if (iasubopt_hash_lookup(&test_iaaddr, lease->ipv6_pool->leases,
1356 (void *)&lease->addr,
1357 sizeof(lease->addr), MDL)) {
1358 if (test_iaaddr != lease) {
1359 status = ISC_FALSE;
1361 iasubopt_dereference(&test_iaaddr, MDL);
1364 return (status);
1368 * Put the lease on our active pool.
1370 static isc_result_t
1371 move_lease_to_active(struct ipv6_pool *pool, struct iasubopt *lease) {
1372 isc_result_t insert_result;
1373 int old_heap_index;
1375 old_heap_index = lease->heap_index;
1376 insert_result = isc_heap_insert(pool->active_timeouts, lease);
1377 if (insert_result == ISC_R_SUCCESS) {
1378 iasubopt_hash_add(pool->leases, &lease->addr,
1379 sizeof(lease->addr), lease, MDL);
1380 isc_heap_delete(pool->inactive_timeouts, old_heap_index);
1381 pool->num_active++;
1382 pool->num_inactive--;
1383 lease->state = FTS_ACTIVE;
1385 return insert_result;
1390 * \brief Renew a lease in the pool.
1392 * The hard_lifetime_end_time of the lease should be set to
1393 * the current expiration time.
1394 * The soft_lifetime_end_time of the lease should be set to
1395 * the desired expiration time.
1397 * This routine will compare the two and call the correct
1398 * heap routine to move the lease. If the lease is active
1399 * and the new expiration time is greater (the normal case)
1400 * then we call isc_heap_decreased() as a larger time is a
1401 * lower priority. If the new expiration time is less then
1402 * we call isc_heap_increased().
1404 * If the lease is abandoned then it will be on the active list
1405 * and we will always call isc_heap_increased() as the previous
1406 * expiration would have been all 1s (as close as we can get
1407 * to infinite).
1409 * If the lease is moving to active we call that routine
1410 * which will move it from the inactive list to the active list.
1412 * \param pool = a pool the lease belongs to
1413 * \param lease = the lease to be renewed
1415 * \return result of the renew operation (ISC_R_SUCCESS if successful,
1416 ISC_R_NOMEMORY when run out of memory)
1418 isc_result_t
1419 renew_lease6(struct ipv6_pool *pool, struct iasubopt *lease) {
1420 time_t old_end_time = lease->hard_lifetime_end_time;
1421 lease->hard_lifetime_end_time = lease->soft_lifetime_end_time;
1422 lease->soft_lifetime_end_time = 0;
1424 if (lease->state == FTS_ACTIVE) {
1425 if (old_end_time <= lease->hard_lifetime_end_time) {
1426 isc_heap_decreased(pool->active_timeouts,
1427 lease->heap_index);
1428 } else {
1429 isc_heap_increased(pool->active_timeouts,
1430 lease->heap_index);
1432 return ISC_R_SUCCESS;
1433 } else if (lease->state == FTS_ABANDONED) {
1434 char tmp_addr[INET6_ADDRSTRLEN];
1435 lease->state = FTS_ACTIVE;
1436 isc_heap_increased(pool->active_timeouts, lease->heap_index);
1437 log_info("Reclaiming previously abandoned address %s",
1438 inet_ntop(AF_INET6, &(lease->addr), tmp_addr,
1439 sizeof(tmp_addr)));
1440 return ISC_R_SUCCESS;
1441 } else {
1442 return move_lease_to_active(pool, lease);
1447 * Put the lease on our inactive pool, with the specified state.
1449 static isc_result_t
1450 move_lease_to_inactive(struct ipv6_pool *pool, struct iasubopt *lease,
1451 binding_state_t state) {
1452 isc_result_t insert_result;
1453 int old_heap_index;
1455 old_heap_index = lease->heap_index;
1456 insert_result = isc_heap_insert(pool->inactive_timeouts, lease);
1457 if (insert_result == ISC_R_SUCCESS) {
1459 * Handle expire and release statements
1460 * To get here we must be active and have done a commit so
1461 * we should run the proper statements if they exist, though
1462 * that will change when we remove the inactive heap.
1463 * In addition we get rid of the references for both as we
1464 * can only do one (expire or release) on a lease
1466 if (lease->on_star.on_expiry != NULL) {
1467 if (state == FTS_EXPIRED) {
1468 execute_statements(NULL, NULL, NULL,
1469 NULL, NULL, NULL,
1470 &lease->scope,
1471 lease->on_star.on_expiry,
1472 &lease->on_star);
1474 executable_statement_dereference
1475 (&lease->on_star.on_expiry, MDL);
1478 if (lease->on_star.on_release != NULL) {
1479 if (state == FTS_RELEASED) {
1480 execute_statements(NULL, NULL, NULL,
1481 NULL, NULL, NULL,
1482 &lease->scope,
1483 lease->on_star.on_release,
1484 &lease->on_star);
1486 executable_statement_dereference
1487 (&lease->on_star.on_release, MDL);
1490 #if defined (NSUPDATE)
1491 /* Process events upon expiration. */
1492 if (pool->pool_type != D6O_IA_PD) {
1493 (void) ddns_removals(NULL, lease, NULL, ISC_FALSE);
1495 #endif
1497 /* Binding scopes are no longer valid after expiry or
1498 * release.
1500 if (lease->scope != NULL) {
1501 binding_scope_dereference(&lease->scope, MDL);
1504 iasubopt_hash_delete(pool->leases,
1505 &lease->addr, sizeof(lease->addr), MDL);
1506 isc_heap_delete(pool->active_timeouts, old_heap_index);
1507 lease->state = state;
1508 pool->num_active--;
1509 pool->num_inactive++;
1511 return insert_result;
1515 * Expire the oldest lease if it's lifetime_end_time is
1516 * older than the given time.
1518 * - leasep must be a pointer to a (struct iasubopt *) pointer previously
1519 * initialized to NULL
1521 * On return leasep has a reference to the removed entry. It is left
1522 * pointing to NULL if the oldest lease has not expired.
1524 isc_result_t
1525 expire_lease6(struct iasubopt **leasep, struct ipv6_pool *pool, time_t now) {
1526 struct iasubopt *tmp;
1527 isc_result_t result;
1529 if (leasep == NULL) {
1530 log_error("%s(%d): NULL pointer reference", MDL);
1531 return DHCP_R_INVALIDARG;
1533 if (*leasep != NULL) {
1534 log_error("%s(%d): non-NULL pointer", MDL);
1535 return DHCP_R_INVALIDARG;
1538 if (pool->num_active > 0) {
1539 tmp = (struct iasubopt *)
1540 isc_heap_element(pool->active_timeouts, 1);
1541 if (now > tmp->hard_lifetime_end_time) {
1542 result = move_lease_to_inactive(pool, tmp,
1543 FTS_EXPIRED);
1544 if (result == ISC_R_SUCCESS) {
1545 iasubopt_reference(leasep, tmp, MDL);
1547 return result;
1550 return ISC_R_SUCCESS;
1555 * For a declined lease, leave it on the "active" pool, but mark
1556 * it as declined. Give it an infinite (well, really long) life.
1558 isc_result_t
1559 decline_lease6(struct ipv6_pool *pool, struct iasubopt *lease) {
1560 isc_result_t result;
1562 if ((lease->state != FTS_ACTIVE) &&
1563 (lease->state != FTS_ABANDONED)) {
1564 result = move_lease_to_active(pool, lease);
1565 if (result != ISC_R_SUCCESS) {
1566 return result;
1569 lease->state = FTS_ABANDONED;
1570 lease->hard_lifetime_end_time = MAX_TIME;
1571 isc_heap_decreased(pool->active_timeouts, lease->heap_index);
1572 return ISC_R_SUCCESS;
1576 * Put the returned lease on our inactive pool.
1578 isc_result_t
1579 release_lease6(struct ipv6_pool *pool, struct iasubopt *lease) {
1580 if (lease->state == FTS_ACTIVE) {
1581 return move_lease_to_inactive(pool, lease, FTS_RELEASED);
1582 } else {
1583 return ISC_R_SUCCESS;
1588 * Create a prefix by hashing the input, and using that for
1589 * the part subject to allocation.
1591 static void
1592 build_prefix6(struct in6_addr *pref,
1593 const struct in6_addr *net_start_pref,
1594 int pool_bits, int pref_bits,
1595 const struct data_string *input) {
1596 isc_md5_t ctx;
1597 int net_bytes;
1598 int i;
1599 char *str;
1600 const char *net_str;
1603 * Use MD5 to get a nice 128 bit hash of the input.
1604 * Yes, we know MD5 isn't cryptographically sound.
1605 * No, we don't care.
1607 isc_md5_init(&ctx);
1608 isc_md5_update(&ctx, input->data, input->len);
1609 isc_md5_final(&ctx, (unsigned char *)pref);
1612 * Copy the network bits over.
1614 str = (char *)pref;
1615 net_str = (const char *)net_start_pref;
1616 net_bytes = pool_bits / 8;
1617 for (i = 0; i < net_bytes; i++) {
1618 str[i] = net_str[i];
1620 i = net_bytes;
1621 switch (pool_bits % 8) {
1622 case 1: str[i] = (str[i] & 0x7F) | (net_str[i] & 0x80); break;
1623 case 2: str[i] = (str[i] & 0x3F) | (net_str[i] & 0xC0); break;
1624 case 3: str[i] = (str[i] & 0x1F) | (net_str[i] & 0xE0); break;
1625 case 4: str[i] = (str[i] & 0x0F) | (net_str[i] & 0xF0); break;
1626 case 5: str[i] = (str[i] & 0x07) | (net_str[i] & 0xF8); break;
1627 case 6: str[i] = (str[i] & 0x03) | (net_str[i] & 0xFC); break;
1628 case 7: str[i] = (str[i] & 0x01) | (net_str[i] & 0xFE); break;
1631 * Zero the remaining bits.
1633 net_bytes = pref_bits / 8;
1634 for (i=net_bytes+1; i<16; i++) {
1635 str[i] = 0;
1637 i = net_bytes;
1638 switch (pref_bits % 8) {
1639 case 0: str[i] &= 0; break;
1640 case 1: str[i] &= 0x80; break;
1641 case 2: str[i] &= 0xC0; break;
1642 case 3: str[i] &= 0xE0; break;
1643 case 4: str[i] &= 0xF0; break;
1644 case 5: str[i] &= 0xF8; break;
1645 case 6: str[i] &= 0xFC; break;
1646 case 7: str[i] &= 0xFE; break;
1651 * Create a lease for the given prefix and client duid.
1653 * - pool must be a pointer to a (struct ipv6_pool *) pointer previously
1654 * initialized to NULL
1656 * Right now we simply hash the DUID, and if we get a collision, we hash
1657 * again until we find a free prefix. We try this a fixed number of times,
1658 * to avoid getting stuck in a loop (this is important on small pools
1659 * where we can run out of space).
1661 * We return the number of attempts that it took to find an available
1662 * prefix. This tells callers when a pool is are filling up, as
1663 * well as an indication of how full the pool is; statistically the
1664 * more full a pool is the more attempts must be made before finding
1665 * a free prefix. Realistically this will only happen in very full
1666 * pools.
1668 * We probably want different algorithms depending on the network size, in
1669 * the long term.
1671 isc_result_t
1672 create_prefix6(struct ipv6_pool *pool, struct iasubopt **pref,
1673 unsigned int *attempts,
1674 const struct data_string *uid,
1675 time_t soft_lifetime_end_time) {
1676 struct data_string ds;
1677 struct in6_addr tmp;
1678 struct iasubopt *test_iapref;
1679 struct data_string new_ds;
1680 struct iasubopt *iapref;
1681 isc_result_t result;
1684 * Use the UID as our initial seed for the hash
1686 memset(&ds, 0, sizeof(ds));
1687 data_string_copy(&ds, (struct data_string *)uid, MDL);
1689 *attempts = 0;
1690 for (;;) {
1692 * Give up at some point.
1694 if (++(*attempts) > 10) {
1695 data_string_forget(&ds, MDL);
1696 return ISC_R_NORESOURCES;
1700 * Build a prefix
1702 build_prefix6(&tmp, &pool->start_addr,
1703 pool->bits, pool->units, &ds);
1706 * If this prefix is not in use, we're happy with it
1708 test_iapref = NULL;
1709 if (iasubopt_hash_lookup(&test_iapref, pool->leases,
1710 &tmp, sizeof(tmp), MDL) == 0) {
1711 break;
1713 iasubopt_dereference(&test_iapref, MDL);
1716 * Otherwise, we create a new input, adding the prefix
1718 memset(&new_ds, 0, sizeof(new_ds));
1719 new_ds.len = ds.len + sizeof(tmp);
1720 if (!buffer_allocate(&new_ds.buffer, new_ds.len, MDL)) {
1721 data_string_forget(&ds, MDL);
1722 return ISC_R_NOMEMORY;
1724 new_ds.data = new_ds.buffer->data;
1725 memcpy(new_ds.buffer->data, ds.data, ds.len);
1726 memcpy(new_ds.buffer->data + ds.len, &tmp, sizeof(tmp));
1727 data_string_forget(&ds, MDL);
1728 data_string_copy(&ds, &new_ds, MDL);
1729 data_string_forget(&new_ds, MDL);
1732 data_string_forget(&ds, MDL);
1735 * We're happy with the prefix, create an IAPREFIX
1736 * to hold it.
1738 iapref = NULL;
1739 result = iasubopt_allocate(&iapref, MDL);
1740 if (result != ISC_R_SUCCESS) {
1741 return result;
1743 iapref->plen = (u_int8_t)pool->units;
1744 memcpy(&iapref->addr, &tmp, sizeof(iapref->addr));
1747 * Add the prefix to the pool (note state is free, not active?!).
1749 result = add_lease6(pool, iapref, soft_lifetime_end_time);
1750 if (result == ISC_R_SUCCESS) {
1751 iasubopt_reference(pref, iapref, MDL);
1753 iasubopt_dereference(&iapref, MDL);
1754 return result;
1758 * Determine if a prefix is present in a pool or not.
1760 isc_boolean_t
1761 prefix6_exists(const struct ipv6_pool *pool,
1762 const struct in6_addr *pref, u_int8_t plen) {
1763 struct iasubopt *test_iapref;
1765 if ((int)plen != pool->units)
1766 return ISC_FALSE;
1768 test_iapref = NULL;
1769 if (iasubopt_hash_lookup(&test_iapref, pool->leases,
1770 (void *)pref, sizeof(*pref), MDL)) {
1771 iasubopt_dereference(&test_iapref, MDL);
1772 return ISC_TRUE;
1773 } else {
1774 return ISC_FALSE;
1779 * Mark an IPv6 address/prefix as unavailable from a pool.
1781 * This is used for host entries and the addresses of the server itself.
1783 static isc_result_t
1784 mark_lease_unavailable(struct ipv6_pool *pool, const struct in6_addr *addr) {
1785 struct iasubopt *dummy_iasubopt;
1786 isc_result_t result;
1788 dummy_iasubopt = NULL;
1789 result = iasubopt_allocate(&dummy_iasubopt, MDL);
1790 if (result == ISC_R_SUCCESS) {
1791 dummy_iasubopt->addr = *addr;
1792 iasubopt_hash_add(pool->leases, &dummy_iasubopt->addr,
1793 sizeof(*addr), dummy_iasubopt, MDL);
1795 return result;
1799 * Add a pool.
1801 isc_result_t
1802 add_ipv6_pool(struct ipv6_pool *pool) {
1803 struct ipv6_pool **new_pools;
1805 new_pools = dmalloc(sizeof(struct ipv6_pool *) * (num_pools+1), MDL);
1806 if (new_pools == NULL) {
1807 return ISC_R_NOMEMORY;
1810 if (num_pools > 0) {
1811 memcpy(new_pools, pools,
1812 sizeof(struct ipv6_pool *) * num_pools);
1813 dfree(pools, MDL);
1815 pools = new_pools;
1817 pools[num_pools] = NULL;
1818 ipv6_pool_reference(&pools[num_pools], pool, MDL);
1819 num_pools++;
1820 return ISC_R_SUCCESS;
1823 static void
1824 cleanup_old_expired(struct ipv6_pool *pool) {
1825 struct iasubopt *tmp;
1826 struct ia_xx *ia;
1827 struct ia_xx *ia_active;
1828 unsigned char *tmpd;
1829 time_t timeout;
1831 while (pool->num_inactive > 0) {
1832 tmp = (struct iasubopt *)
1833 isc_heap_element(pool->inactive_timeouts, 1);
1834 if (tmp->hard_lifetime_end_time != 0) {
1835 timeout = tmp->hard_lifetime_end_time;
1836 timeout += EXPIRED_IPV6_CLEANUP_TIME;
1837 } else {
1838 timeout = tmp->soft_lifetime_end_time;
1840 if (cur_time < timeout) {
1841 break;
1844 isc_heap_delete(pool->inactive_timeouts, tmp->heap_index);
1845 pool->num_inactive--;
1847 if (tmp->ia != NULL) {
1849 * Check to see if this IA is in an active list,
1850 * but has no remaining resources. If so, remove it
1851 * from the active list.
1853 ia = NULL;
1854 ia_reference(&ia, tmp->ia, MDL);
1855 ia_remove_iasubopt(ia, tmp, MDL);
1856 ia_active = NULL;
1857 tmpd = (unsigned char *)ia->iaid_duid.data;
1858 if ((ia->ia_type == D6O_IA_NA) &&
1859 (ia->num_iasubopt <= 0) &&
1860 (ia_hash_lookup(&ia_active, ia_na_active, tmpd,
1861 ia->iaid_duid.len, MDL) == 0) &&
1862 (ia_active == ia)) {
1863 ia_hash_delete(ia_na_active, tmpd,
1864 ia->iaid_duid.len, MDL);
1866 if ((ia->ia_type == D6O_IA_TA) &&
1867 (ia->num_iasubopt <= 0) &&
1868 (ia_hash_lookup(&ia_active, ia_ta_active, tmpd,
1869 ia->iaid_duid.len, MDL) == 0) &&
1870 (ia_active == ia)) {
1871 ia_hash_delete(ia_ta_active, tmpd,
1872 ia->iaid_duid.len, MDL);
1874 if ((ia->ia_type == D6O_IA_PD) &&
1875 (ia->num_iasubopt <= 0) &&
1876 (ia_hash_lookup(&ia_active, ia_pd_active, tmpd,
1877 ia->iaid_duid.len, MDL) == 0) &&
1878 (ia_active == ia)) {
1879 ia_hash_delete(ia_pd_active, tmpd,
1880 ia->iaid_duid.len, MDL);
1882 ia_dereference(&ia, MDL);
1884 iasubopt_dereference(&tmp, MDL);
1888 static void
1889 lease_timeout_support(void *vpool) {
1890 struct ipv6_pool *pool;
1891 struct iasubopt *lease;
1893 pool = (struct ipv6_pool *)vpool;
1894 for (;;) {
1896 * Get the next lease scheduled to expire.
1898 * Note that if there are no leases in the pool,
1899 * expire_lease6() will return ISC_R_SUCCESS with
1900 * a NULL lease.
1902 * expire_lease6() will call move_lease_to_inactive() which
1903 * calls ddns_removals() do we want that on the standard
1904 * expiration timer or a special 'depref' timer? Original
1905 * query from DH, moved here by SAR.
1907 lease = NULL;
1908 if (expire_lease6(&lease, pool, cur_time) != ISC_R_SUCCESS) {
1909 break;
1911 if (lease == NULL) {
1912 break;
1915 write_ia(lease->ia);
1917 iasubopt_dereference(&lease, MDL);
1921 * If appropriate commit and rotate the lease file
1922 * As commit_leases_timed() checks to see if we've done any writes
1923 * we don't bother tracking if this function called write _ia
1925 (void) commit_leases_timed();
1928 * Do some cleanup of our expired leases.
1930 cleanup_old_expired(pool);
1933 * Schedule next round of expirations.
1935 schedule_lease_timeout(pool);
1939 * For a given pool, add a timer that will remove the next
1940 * lease to expire.
1942 void
1943 schedule_lease_timeout(struct ipv6_pool *pool) {
1944 struct iasubopt *tmp;
1945 time_t timeout;
1946 time_t next_timeout;
1947 struct timeval tv;
1949 next_timeout = MAX_TIME;
1951 if (pool->num_active > 0) {
1952 tmp = (struct iasubopt *)
1953 isc_heap_element(pool->active_timeouts, 1);
1954 if (tmp->hard_lifetime_end_time < next_timeout) {
1955 next_timeout = tmp->hard_lifetime_end_time + 1;
1959 if (pool->num_inactive > 0) {
1960 tmp = (struct iasubopt *)
1961 isc_heap_element(pool->inactive_timeouts, 1);
1962 if (tmp->hard_lifetime_end_time != 0) {
1963 timeout = tmp->hard_lifetime_end_time;
1964 timeout += EXPIRED_IPV6_CLEANUP_TIME;
1965 } else {
1966 timeout = tmp->soft_lifetime_end_time + 1;
1968 if (timeout < next_timeout) {
1969 next_timeout = timeout;
1973 if (next_timeout < MAX_TIME) {
1974 tv.tv_sec = next_timeout;
1975 tv.tv_usec = 0;
1976 add_timeout(&tv, lease_timeout_support, pool,
1977 (tvref_t)ipv6_pool_reference,
1978 (tvunref_t)ipv6_pool_dereference);
1983 * Schedule timeouts across all pools.
1985 void
1986 schedule_all_ipv6_lease_timeouts(void) {
1987 int i;
1989 for (i=0; i<num_pools; i++) {
1990 schedule_lease_timeout(pools[i]);
1995 * Given an address and the length of the network mask, return
1996 * only the network portion.
1998 * Examples:
2000 * "fe80::216:6fff:fe49:7d9b", length 64 = "fe80::"
2001 * "2001:888:1936:2:216:6fff:fe49:7d9b", length 48 = "2001:888:1936::"
2003 static void
2004 ipv6_network_portion(struct in6_addr *result,
2005 const struct in6_addr *addr, int bits) {
2006 unsigned char *addrp;
2007 int mask_bits;
2008 int bytes;
2009 int extra_bits;
2010 int i;
2012 static const unsigned char bitmasks[] = {
2013 0x00, 0xFE, 0xFC, 0xF8,
2014 0xF0, 0xE0, 0xC0, 0x80,
2018 * Sanity check our bits. ;)
2020 if ((bits < 0) || (bits > 128)) {
2021 log_fatal("ipv6_network_portion: bits %d not between 0 and 128",
2022 bits);
2026 * Copy our address portion.
2028 *result = *addr;
2029 addrp = ((unsigned char *)result) + 15;
2032 * Zero out masked portion.
2034 mask_bits = 128 - bits;
2035 bytes = mask_bits / 8;
2036 extra_bits = mask_bits % 8;
2038 for (i=0; i<bytes; i++) {
2039 *addrp = 0;
2040 addrp--;
2042 if (extra_bits) {
2043 *addrp &= bitmasks[extra_bits];
2048 * Determine if the given address/prefix is in the pool.
2050 isc_boolean_t
2051 ipv6_in_pool(const struct in6_addr *addr, const struct ipv6_pool *pool) {
2052 struct in6_addr tmp;
2054 ipv6_network_portion(&tmp, addr, pool->bits);
2055 if (memcmp(&tmp, &pool->start_addr, sizeof(tmp)) == 0) {
2056 return ISC_TRUE;
2057 } else {
2058 return ISC_FALSE;
2063 * Find the pool that contains the given address.
2065 * - pool must be a pointer to a (struct ipv6_pool *) pointer previously
2066 * initialized to NULL
2068 isc_result_t
2069 find_ipv6_pool(struct ipv6_pool **pool, u_int16_t type,
2070 const struct in6_addr *addr) {
2071 int i;
2073 if (pool == NULL) {
2074 log_error("%s(%d): NULL pointer reference", MDL);
2075 return DHCP_R_INVALIDARG;
2077 if (*pool != NULL) {
2078 log_error("%s(%d): non-NULL pointer", MDL);
2079 return DHCP_R_INVALIDARG;
2082 for (i=0; i<num_pools; i++) {
2083 if (pools[i]->pool_type != type)
2084 continue;
2085 if (ipv6_in_pool(addr, pools[i])) {
2086 ipv6_pool_reference(pool, pools[i], MDL);
2087 return ISC_R_SUCCESS;
2090 return ISC_R_NOTFOUND;
2094 * Helper function for the various functions that act across all
2095 * pools.
2097 static isc_result_t
2098 change_leases(struct ia_xx *ia,
2099 isc_result_t (*change_func)(struct ipv6_pool *,
2100 struct iasubopt *)) {
2101 isc_result_t retval;
2102 isc_result_t renew_retval;
2103 struct ipv6_pool *pool;
2104 struct in6_addr *addr;
2105 int i;
2107 retval = ISC_R_SUCCESS;
2108 for (i=0; i<ia->num_iasubopt; i++) {
2109 pool = NULL;
2110 addr = &ia->iasubopt[i]->addr;
2111 if (find_ipv6_pool(&pool, ia->ia_type,
2112 addr) == ISC_R_SUCCESS) {
2113 renew_retval = change_func(pool, ia->iasubopt[i]);
2114 if (renew_retval != ISC_R_SUCCESS) {
2115 retval = renew_retval;
2118 /* XXXsk: should we warn if we don't find a pool? */
2120 return retval;
2124 * Renew all leases in an IA from all pools.
2126 * The new lifetime should be in the soft_lifetime_end_time
2127 * and will be moved to hard_lifetime_end_time by renew_lease6.
2129 isc_result_t
2130 renew_leases(struct ia_xx *ia) {
2131 return change_leases(ia, renew_lease6);
2135 * Release all leases in an IA from all pools.
2137 isc_result_t
2138 release_leases(struct ia_xx *ia) {
2139 return change_leases(ia, release_lease6);
2143 * Decline all leases in an IA from all pools.
2145 isc_result_t
2146 decline_leases(struct ia_xx *ia) {
2147 return change_leases(ia, decline_lease6);
2150 #ifdef DHCPv6
2152 * Helper function to output leases.
2154 static int write_error;
2156 static isc_result_t
2157 write_ia_leases(const void *name, unsigned len, void *value) {
2158 struct ia_xx *ia = (struct ia_xx *)value;
2160 if (!write_error) {
2161 if (!write_ia(ia)) {
2162 write_error = 1;
2165 return ISC_R_SUCCESS;
2169 * Write all DHCPv6 information.
2172 write_leases6(void) {
2173 int nas, tas, pds;
2175 write_error = 0;
2176 write_server_duid();
2177 nas = ia_hash_foreach(ia_na_active, write_ia_leases);
2178 if (write_error) {
2179 return 0;
2181 tas = ia_hash_foreach(ia_ta_active, write_ia_leases);
2182 if (write_error) {
2183 return 0;
2185 pds = ia_hash_foreach(ia_pd_active, write_ia_leases);
2186 if (write_error) {
2187 return 0;
2190 log_info("Wrote %d NA, %d TA, %d PD leases to lease file.",
2191 nas, tas, pds);
2192 return 1;
2194 #endif /* DHCPv6 */
2196 static isc_result_t
2197 mark_hosts_unavailable_support(const void *name, unsigned len, void *value) {
2198 struct host_decl *h;
2199 struct data_string fixed_addr;
2200 struct in6_addr addr;
2201 struct ipv6_pool *p;
2203 h = (struct host_decl *)value;
2206 * If the host has no address, we don't need to mark anything.
2208 if (h->fixed_addr == NULL) {
2209 return ISC_R_SUCCESS;
2213 * Evaluate the fixed address.
2215 memset(&fixed_addr, 0, sizeof(fixed_addr));
2216 if (!evaluate_option_cache(&fixed_addr, NULL, NULL, NULL, NULL, NULL,
2217 &global_scope, h->fixed_addr, MDL)) {
2218 log_error("mark_hosts_unavailable: "
2219 "error evaluating host address.");
2220 return ISC_R_SUCCESS;
2222 if (fixed_addr.len != 16) {
2223 log_error("mark_hosts_unavailable: "
2224 "host address is not 128 bits.");
2225 return ISC_R_SUCCESS;
2227 memcpy(&addr, fixed_addr.data, 16);
2228 data_string_forget(&fixed_addr, MDL);
2231 * Find the pool holding this host, and mark the address.
2232 * (I suppose it is arguably valid to have a host that does not
2233 * sit in any pool.)
2235 p = NULL;
2236 if (find_ipv6_pool(&p, D6O_IA_NA, &addr) == ISC_R_SUCCESS) {
2237 mark_lease_unavailable(p, &addr);
2238 ipv6_pool_dereference(&p, MDL);
2240 if (find_ipv6_pool(&p, D6O_IA_TA, &addr) == ISC_R_SUCCESS) {
2241 mark_lease_unavailable(p, &addr);
2242 ipv6_pool_dereference(&p, MDL);
2245 return ISC_R_SUCCESS;
2248 void
2249 mark_hosts_unavailable(void) {
2250 hash_foreach(host_name_hash, mark_hosts_unavailable_support);
2253 static isc_result_t
2254 mark_phosts_unavailable_support(const void *name, unsigned len, void *value) {
2255 struct host_decl *h;
2256 struct iaddrcidrnetlist *l;
2257 struct in6_addr pref;
2258 struct ipv6_pool *p;
2260 h = (struct host_decl *)value;
2263 * If the host has no prefix, we don't need to mark anything.
2265 if (h->fixed_prefix == NULL) {
2266 return ISC_R_SUCCESS;
2270 * Get the fixed prefixes.
2272 for (l = h->fixed_prefix; l != NULL; l = l->next) {
2273 if (l->cidrnet.lo_addr.len != 16) {
2274 continue;
2276 memcpy(&pref, l->cidrnet.lo_addr.iabuf, 16);
2279 * Find the pool holding this host, and mark the prefix.
2280 * (I suppose it is arguably valid to have a host that does not
2281 * sit in any pool.)
2283 p = NULL;
2284 if (find_ipv6_pool(&p, D6O_IA_PD, &pref) != ISC_R_SUCCESS) {
2285 continue;
2287 if (l->cidrnet.bits != p->units) {
2288 ipv6_pool_dereference(&p, MDL);
2289 continue;
2291 mark_lease_unavailable(p, &pref);
2292 ipv6_pool_dereference(&p, MDL);
2295 return ISC_R_SUCCESS;
2298 void
2299 mark_phosts_unavailable(void) {
2300 hash_foreach(host_name_hash, mark_phosts_unavailable_support);
2303 void
2304 mark_interfaces_unavailable(void) {
2305 struct interface_info *ip;
2306 int i;
2307 struct ipv6_pool *p;
2309 ip = interfaces;
2310 while (ip != NULL) {
2311 for (i=0; i<ip->v6address_count; i++) {
2312 p = NULL;
2313 if (find_ipv6_pool(&p, D6O_IA_NA, &ip->v6addresses[i])
2314 == ISC_R_SUCCESS) {
2315 mark_lease_unavailable(p,
2316 &ip->v6addresses[i]);
2317 ipv6_pool_dereference(&p, MDL);
2319 if (find_ipv6_pool(&p, D6O_IA_TA, &ip->v6addresses[i])
2320 == ISC_R_SUCCESS) {
2321 mark_lease_unavailable(p,
2322 &ip->v6addresses[i]);
2323 ipv6_pool_dereference(&p, MDL);
2326 ip = ip->next;
2331 * \brief Create a new IPv6 pond structure.
2333 * Allocate space for a new ipv6_pond structure and return a reference
2334 * to it, includes setting the reference count to 1.
2336 * \param pond = space for returning a referenced pointer to the pond.
2337 * This must point to a space that has been initialzied
2338 * to NULL by the caller.
2340 * \return
2341 * ISC_R_SUCCESS = The pond was successfully created, pond points to it.
2342 * DHCP_R_INVALIDARG = One of the arugments was invalid, pond has not been
2343 * modified
2344 * ISC_R_NOMEMORY = The system wasn't able to allocate memory, pond has
2345 * not been modified.
2347 isc_result_t
2348 ipv6_pond_allocate(struct ipv6_pond **pond, const char *file, int line) {
2349 struct ipv6_pond *tmp;
2351 if (pond == NULL) {
2352 log_error("%s(%d): NULL pointer reference", file, line);
2353 return DHCP_R_INVALIDARG;
2355 if (*pond != NULL) {
2356 log_error("%s(%d): non-NULL pointer", file, line);
2357 return DHCP_R_INVALIDARG;
2360 tmp = dmalloc(sizeof(*tmp), file, line);
2361 if (tmp == NULL) {
2362 return ISC_R_NOMEMORY;
2365 tmp->refcnt = 1;
2367 *pond = tmp;
2368 return ISC_R_SUCCESS;
2373 * \brief reference an IPv6 pond structure.
2375 * This function genreates a reference to an ipv6_pond structure
2376 * and increments the reference count on the structure.
2378 * \param[out] pond = space for returning a referenced pointer to the pond.
2379 * This must point to a space that has been initialzied
2380 * to NULL by the caller.
2381 * \param[in] src = A pointer to the pond to reference. This must not be
2382 * NULL.
2384 * \return
2385 * ISC_R_SUCCESS = The pond was successfully referenced, pond now points
2386 * to src.
2387 * DHCP_R_INVALIDARG = One of the arugments was invalid, pond has not been
2388 * modified.
2390 isc_result_t
2391 ipv6_pond_reference(struct ipv6_pond **pond, struct ipv6_pond *src,
2392 const char *file, int line) {
2393 if (pond == NULL) {
2394 log_error("%s(%d): NULL pointer reference", file, line);
2395 return DHCP_R_INVALIDARG;
2397 if (*pond != NULL) {
2398 log_error("%s(%d): non-NULL pointer", file, line);
2399 return DHCP_R_INVALIDARG;
2401 if (src == NULL) {
2402 log_error("%s(%d): NULL pointer reference", file, line);
2403 return DHCP_R_INVALIDARG;
2405 *pond = src;
2406 src->refcnt++;
2407 return ISC_R_SUCCESS;
2412 * \brief de-reference an IPv6 pond structure.
2414 * This function decrements the reference count in an ipv6_pond structure.
2415 * If this was the last reference then the memory for the structure is
2416 * freed.
2418 * \param[in] pond = A pointer to the pointer to the pond that should be
2419 * de-referenced. On success the pointer to the pond
2420 * is cleared. It must not be NULL and must not point
2421 * to NULL.
2423 * \return
2424 * ISC_R_SUCCESS = The pond was successfully de-referenced, pond now points
2425 * to NULL
2426 * DHCP_R_INVALIDARG = One of the arugments was invalid, pond has not been
2427 * modified.
2430 isc_result_t
2431 ipv6_pond_dereference(struct ipv6_pond **pond, const char *file, int line) {
2432 struct ipv6_pond *tmp;
2434 if ((pond == NULL) || (*pond == NULL)) {
2435 log_error("%s(%d): NULL pointer", file, line);
2436 return DHCP_R_INVALIDARG;
2439 tmp = *pond;
2440 *pond = NULL;
2442 tmp->refcnt--;
2443 if (tmp->refcnt < 0) {
2444 log_error("%s(%d): negative refcnt", file, line);
2445 tmp->refcnt = 0;
2447 if (tmp->refcnt == 0) {
2448 dfree(tmp, file, line);
2451 return ISC_R_SUCCESS;
2454 /* unittest moved to server/tests/mdb6_unittest.c */