]> git.ipfire.org Git - thirdparty/dhcp.git/blame - server/mdb6.c
Update README - added section on Release status which was in the TOC
[thirdparty/dhcp.git] / server / mdb6.c
CommitLineData
98bd7ca0 1/*
417b7b4a 2 * Copyright (C) 2007-2017 by Internet Systems Consortium, Inc. ("ISC")
98bd7ca0 3 *
7512d88b
TM
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
98bd7ca0
DH
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
bc7f8b8e
SR
17/*!
18 * \todo assert()
19 * \todo simplify functions, as pool is now in iaaddr
20 */
98bd7ca0 21
bc7f8b8e
SR
22/*! \file server/mdb6.c
23 *
24 * \page ipv6structures IPv6 Structures Overview
25 *
26 * A brief description of the IPv6 structures as reverse engineered.
27 *
01fa619f
SR
28 * There are four major data structures in the lease configuraion.
29 *
30 * - shared_network - The shared network is the outer enclosing scope for a
31 * network region that shares a broadcast domain. It is
32 * composed of one or more subnets all of which are valid
33 * in the given region. The share network may be
34 * explicitly defined or implicitly created if there is
35 * only a subnet statement. This structrure is shared
36 * with v4. Each shared network statment or naked subnet
37 * will map to one of these structures
38 *
39 * - subnet - The subnet structure mostly specifies the address range
40 * that could be valid in a given region. This structute
41 * doesn't include the addresses that the server can delegate
42 * those are in the ipv6_pool. This structure is also shared
43 * with v4. Each subnet statement will map to one of these
44 * structures.
45 *
46 * - ipv6_pond - The pond structure is a grouping of the address and prefix
47 * information via the pointers to the ipv6_pool and the
48 * allowability of this pool for given clinets via the permit
49 * lists and the valid TIMEs. This is equivilent to the v4
50 * pool structure and would have been named ip6_pool except
51 * that the name was already in use. Generally each pool6
52 * statement will map to one of these structures. In addition
53 * there may be one or for each group of naked range6 and
54 * prefix6 statements within a shared network that share
55 * the same group of statements.
0b2ec8c9
SR
56 *
57 * - ipv6_pool - this contains information about a pool of addresses or prefixes
01fa619f
SR
58 * that the server is using. This includes a hash table that
59 * tracks the active items and a pair of heap tables one for
60 * active items and one for non-active items. The heap tables
61 * are used to determine the next items to be modified due to
62 * timing events (expire mostly).
63 *
64 * The linkages then look like this:
65 * \verbatim
66 *+--------------+ +-------------+
67 *|Shared Network| | ipv6_pond |
68 *| group | | group |
69 *| | | permit info |
70 *| | | next ---->
71 *| ponds ---->| |
72 *| |<---- shared |
73 *| Subnets | | pools |
74 *+-----|--------+ +------|------+
75 * | ^ | ^
76 * | | v |
77 * | | +-----------|-+
78 * | | | ipv6_pool | |
79 * | | | type | |
80 * | | | ipv6_pond |
81 * | | | |
82 * | | | next ---->
83 * | | | |
84 * | | | subnet |
85 * | | +-----|-------+
86 * | | |
87 * | | v
88 * | | +-------------+
89 * | | | subnet |
90 * | +---------- shared |
91 * +----------->| |
92 * | group |
93 * +-------------+
94 *
95 * The shared network contains a list of all the subnets that are on a broadcast
96 * doamin. These can be used to determine if an address makes sense in a given
97 * domain, but the subnets do not contain the addresses the server can delegate.
98 * Those are stored in the ponds and pools.
99 *
100 * In the simple case to find an acceptable address the server would first find
101 * the shared network the client is on based on either the interface used to
102 * receive the request or the relay agent's information. From the shared
103 * network the server will walk through it's list of ponds. For each pond it
104 * will evaluate the permit information against the (already done) classification.
105 * If it finds an acceptable pond it will then walk through the pools for that
106 * pond. The server first checks the type of the pool (NA, TA and PD) agaisnt the
107 * request and if they match it attemps to find an address within that pool. On
108 * success the address is used, on failure the server steps to the next pool and
109 * if necessary to the next pond.
110 *
111 * When the server is successful in finding an address it will execute any
112 * statements assocaited with the pond, then the subnet, then the shared
113 * network the group field is for in the above picture).
114 *
115 * In configurations that don't include either a shared network or a pool6
116 * statement (or both) the missing pieces are created.
117 *
118 *
119 * There are three major data structuress involved in the lease database:
120 *
121 * - ipv6_pool - see above
0b2ec8c9 122 * - ia_xx - this contains information about a single IA from a request
bc7f8b8e
SR
123 * normally it will contain one pointer to a lease for the client
124 * but it may contain more in some circumstances. There are 3
0b2ec8c9 125 * hash tables to aid in accessing these one each for NA, TA and PD.
01fa619f
SR
126 * - iasubopt - the v6 lease structure. These are created dynamically when
127 * a client asks for something and will eventually be destroyed
128 * if the client doesn't re-ask for that item. A lease has space
129 * for backpointers to the IA and to the pool to which it belongs.
130 * The pool backpointer is always filled, the IA pointer may not be.
bc7f8b8e
SR
131 *
132 * In normal use we then have something like this:
133 *
0b2ec8c9 134 * \verbatim
bc7f8b8e
SR
135 * ia hash tables
136 * ia_na_active +----------------+
137 * ia_ta_active +------------+ | pool |
138 * ia_pd_active | iasubopt |<--| active hash |
139 * +-----------------+ | aka lease |<--| active heap |
140 * | ia_xx | | pool ptr |-->| |
141 * | iasubopt array |<---| iaptr |<--| inactive heap |
142 * | lease ptr |--->| | | |
143 * +-----------------+ +------------+ +----------------+
0b2ec8c9 144 * \endverbatim
bc7f8b8e
SR
145 *
146 * For the pool either the inactive heap will have a pointer
147 * or both the active heap and the active hash will have pointers.
148 *
149 * I think there are several major items to notice. The first is
150 * that as a lease moves around it will be added to and removed
151 * from the address hash table in the pool and between the active
152 * and inactive hash tables. The hash table and the active heap
153 * are used when the lease is either active or abandoned. The
154 * inactive heap is used for all other states. In particular a
155 * lease that has expired or been released will be cleaned
156 * (DDNS removal etc) and then moved to the inactive heap. After
157 * some time period (currently 1 hour) it will be freed.
158 *
159 * The second is that when a client requests specific addresses,
160 * either because it previously owned them or if the server supplied
161 * them as part of a solicit, the server will try to lookup the ia_xx
162 * associated with the client and find the addresses there. If it
163 * does find appropriate leases it moves them from the old IA to
164 * a new IA and eventually replaces the old IA with the new IA
165 * in the IA hash tables.
166 *
167 */
d1f31a00
FD
168#include "config.h"
169
99fe695e 170#include <sys/types.h>
98bd7ca0
DH
171#include <time.h>
172#include <netinet/in.h>
173
98bd7ca0 174#include <stdarg.h>
98bd7ca0 175#include "dhcpd.h"
6705543f 176#include "omapip/omapip.h"
98bd7ca0 177#include "omapip/hash.h"
98bf1607 178#include <isc/md5.h>
98bd7ca0 179
9322442f 180HASH_FUNCTIONS(ia, unsigned char *, struct ia_xx, ia_hash_t,
a3528574 181 ia_reference, ia_dereference, do_string_hash)
98bd7ca0 182
9322442f
FD
183ia_hash_t *ia_na_active;
184ia_hash_t *ia_ta_active;
185ia_hash_t *ia_pd_active;
98bd7ca0 186
1d17db44 187HASH_FUNCTIONS(iasubopt, struct in6_addr *, struct iasubopt, iasubopt_hash_t,
a3528574 188 iasubopt_reference, iasubopt_dereference, do_string_hash)
98bd7ca0
DH
189
190struct ipv6_pool **pools;
191int num_pools;
192
193/*
9322442f 194 * Create a new IAADDR/PREFIX structure.
98bd7ca0 195 *
1d17db44 196 * - iasubopt must be a pointer to a (struct iasubopt *) pointer previously
98bd7ca0
DH
197 * initialized to NULL
198 */
199isc_result_t
1d17db44
FD
200iasubopt_allocate(struct iasubopt **iasubopt, const char *file, int line) {
201 struct iasubopt *tmp;
98bd7ca0 202
1d17db44 203 if (iasubopt == NULL) {
98bd7ca0 204 log_error("%s(%d): NULL pointer reference", file, line);
98bf1607 205 return DHCP_R_INVALIDARG;
98bd7ca0 206 }
1d17db44 207 if (*iasubopt != NULL) {
98bd7ca0 208 log_error("%s(%d): non-NULL pointer", file, line);
98bf1607 209 return DHCP_R_INVALIDARG;
98bd7ca0
DH
210 }
211
212 tmp = dmalloc(sizeof(*tmp), file, line);
213 if (tmp == NULL) {
214 return ISC_R_NOMEMORY;
215 }
216
217 tmp->refcnt = 1;
218 tmp->state = FTS_FREE;
abacf8ad
TM
219 tmp->active_index = 0;
220 tmp->inactive_index = 0;
9322442f 221 tmp->plen = 255;
98bd7ca0 222
1d17db44 223 *iasubopt = tmp;
98bd7ca0
DH
224 return ISC_R_SUCCESS;
225}
226
227/*
9322442f 228 * Reference an IAADDR/PREFIX structure.
98bd7ca0 229 *
1d17db44 230 * - iasubopt must be a pointer to a (struct iasubopt *) pointer previously
98bd7ca0
DH
231 * initialized to NULL
232 */
233isc_result_t
1d17db44 234iasubopt_reference(struct iasubopt **iasubopt, struct iasubopt *src,
98bd7ca0 235 const char *file, int line) {
1d17db44 236 if (iasubopt == NULL) {
98bd7ca0 237 log_error("%s(%d): NULL pointer reference", file, line);
98bf1607 238 return DHCP_R_INVALIDARG;
98bd7ca0 239 }
1d17db44 240 if (*iasubopt != NULL) {
98bd7ca0 241 log_error("%s(%d): non-NULL pointer", file, line);
98bf1607 242 return DHCP_R_INVALIDARG;
98bd7ca0
DH
243 }
244 if (src == NULL) {
245 log_error("%s(%d): NULL pointer reference", file, line);
98bf1607 246 return DHCP_R_INVALIDARG;
98bd7ca0 247 }
1d17db44 248 *iasubopt = src;
98bd7ca0
DH
249 src->refcnt++;
250 return ISC_R_SUCCESS;
251}
252
253
254/*
9322442f 255 * Dereference an IAADDR/PREFIX structure.
98bd7ca0
DH
256 *
257 * If it is the last reference, then the memory for the
258 * structure is freed.
259 */
260isc_result_t
1d17db44
FD
261iasubopt_dereference(struct iasubopt **iasubopt, const char *file, int line) {
262 struct iasubopt *tmp;
98bd7ca0 263
1d17db44 264 if ((iasubopt == NULL) || (*iasubopt == NULL)) {
98bd7ca0 265 log_error("%s(%d): NULL pointer", file, line);
98bf1607 266 return DHCP_R_INVALIDARG;
98bd7ca0
DH
267 }
268
1d17db44
FD
269 tmp = *iasubopt;
270 *iasubopt = NULL;
98bd7ca0
DH
271
272 tmp->refcnt--;
273 if (tmp->refcnt < 0) {
274 log_error("%s(%d): negative refcnt", file, line);
275 tmp->refcnt = 0;
276 }
277 if (tmp->refcnt == 0) {
9322442f
FD
278 if (tmp->ia != NULL) {
279 ia_dereference(&(tmp->ia), file, line);
98bd7ca0
DH
280 }
281 if (tmp->ipv6_pool != NULL) {
282 ipv6_pool_dereference(&(tmp->ipv6_pool), file, line);
283 }
284 if (tmp->scope != NULL) {
285 binding_scope_dereference(&tmp->scope, file, line);
286 }
a7341359
SR
287
288 if (tmp->on_star.on_expiry != NULL) {
289 executable_statement_dereference
290 (&tmp->on_star.on_expiry, MDL);
291 }
292 if (tmp->on_star.on_commit != NULL) {
293 executable_statement_dereference
294 (&tmp->on_star.on_commit, MDL);
295 }
296 if (tmp->on_star.on_release != NULL) {
297 executable_statement_dereference
298 (&tmp->on_star.on_release, MDL);
299 }
300
98bd7ca0
DH
301 dfree(tmp, file, line);
302 }
303
304 return ISC_R_SUCCESS;
305}
306
307/*
1d9774ab 308 * Make the key that we use for IA.
98bd7ca0
DH
309 */
310isc_result_t
1d9774ab
FD
311ia_make_key(struct data_string *key, u_int32_t iaid,
312 const char *duid, unsigned int duid_len,
313 const char *file, int line) {
98bd7ca0
DH
314
315 memset(key, 0, sizeof(*key));
316 key->len = duid_len + sizeof(iaid);
317 if (!buffer_allocate(&(key->buffer), key->len, file, line)) {
318 return ISC_R_NOMEMORY;
319 }
320 key->data = key->buffer->data;
321 memcpy((char *)key->data, &iaid, sizeof(iaid));
322 memcpy((char *)key->data + sizeof(iaid), duid, duid_len);
323
324 return ISC_R_SUCCESS;
325}
326
327/*
1d9774ab 328 * Create a new IA structure.
98bd7ca0 329 *
9322442f 330 * - ia must be a pointer to a (struct ia_xx *) pointer previously
98bd7ca0
DH
331 * initialized to NULL
332 * - iaid and duid are values from the client
333 *
334 * XXXsk: we don't concern ourself with the byte order of the IAID,
335 * which might be a problem if we transfer this structure
336 * between machines of different byte order
337 */
338isc_result_t
9322442f
FD
339ia_allocate(struct ia_xx **ia, u_int32_t iaid,
340 const char *duid, unsigned int duid_len,
341 const char *file, int line) {
342 struct ia_xx *tmp;
98bd7ca0 343
1d9774ab 344 if (ia == NULL) {
98bd7ca0 345 log_error("%s(%d): NULL pointer reference", file, line);
98bf1607 346 return DHCP_R_INVALIDARG;
98bd7ca0 347 }
1d9774ab 348 if (*ia != NULL) {
98bd7ca0 349 log_error("%s(%d): non-NULL pointer", file, line);
98bf1607 350 return DHCP_R_INVALIDARG;
98bd7ca0
DH
351 }
352
353 tmp = dmalloc(sizeof(*tmp), file, line);
354 if (tmp == NULL) {
355 return ISC_R_NOMEMORY;
356 }
357
1d9774ab
FD
358 if (ia_make_key(&tmp->iaid_duid, iaid,
359 duid, duid_len, file, line) != ISC_R_SUCCESS) {
98bd7ca0
DH
360 dfree(tmp, file, line);
361 return ISC_R_NOMEMORY;
362 }
363
364 tmp->refcnt = 1;
365
1d9774ab 366 *ia = tmp;
98bd7ca0
DH
367 return ISC_R_SUCCESS;
368}
369
370/*
1d9774ab 371 * Reference an IA structure.
98bd7ca0 372 *
9322442f 373 * - ia must be a pointer to a (struct ia_xx *) pointer previously
98bd7ca0
DH
374 * initialized to NULL
375 */
376isc_result_t
9322442f
FD
377ia_reference(struct ia_xx **ia, struct ia_xx *src,
378 const char *file, int line) {
1d9774ab 379 if (ia == NULL) {
98bd7ca0 380 log_error("%s(%d): NULL pointer reference", file, line);
98bf1607 381 return DHCP_R_INVALIDARG;
98bd7ca0 382 }
1d9774ab 383 if (*ia != NULL) {
98bd7ca0 384 log_error("%s(%d): non-NULL pointer", file, line);
98bf1607 385 return DHCP_R_INVALIDARG;
98bd7ca0
DH
386 }
387 if (src == NULL) {
388 log_error("%s(%d): NULL pointer reference", file, line);
98bf1607 389 return DHCP_R_INVALIDARG;
98bd7ca0 390 }
1d9774ab 391 *ia = src;
98bd7ca0
DH
392 src->refcnt++;
393 return ISC_R_SUCCESS;
394}
395
396/*
1d9774ab 397 * Dereference an IA structure.
98bd7ca0
DH
398 *
399 * If it is the last reference, then the memory for the
400 * structure is freed.
401 */
402isc_result_t
9322442f
FD
403ia_dereference(struct ia_xx **ia, const char *file, int line) {
404 struct ia_xx *tmp;
98bd7ca0
DH
405 int i;
406
1d9774ab 407 if ((ia == NULL) || (*ia == NULL)) {
98bd7ca0 408 log_error("%s(%d): NULL pointer", file, line);
98bf1607 409 return DHCP_R_INVALIDARG;
98bd7ca0
DH
410 }
411
1d9774ab
FD
412 tmp = *ia;
413 *ia = NULL;
98bd7ca0
DH
414
415 tmp->refcnt--;
416 if (tmp->refcnt < 0) {
417 log_error("%s(%d): negative refcnt", file, line);
418 tmp->refcnt = 0;
419 }
420 if (tmp->refcnt == 0) {
1d17db44
FD
421 if (tmp->iasubopt != NULL) {
422 for (i=0; i<tmp->num_iasubopt; i++) {
423 iasubopt_dereference(&(tmp->iasubopt[i]),
424 file, line);
98bd7ca0 425 }
1d17db44 426 dfree(tmp->iasubopt, file, line);
98bd7ca0
DH
427 }
428 data_string_forget(&(tmp->iaid_duid), file, line);
429 dfree(tmp, file, line);
430 }
431 return ISC_R_SUCCESS;
432}
433
434
435/*
9322442f 436 * Add an IAADDR/PREFIX entry to an IA structure.
98bd7ca0
DH
437 */
438isc_result_t
1d17db44
FD
439ia_add_iasubopt(struct ia_xx *ia, struct iasubopt *iasubopt,
440 const char *file, int line) {
98bd7ca0 441 int max;
1d17db44 442 struct iasubopt **new;
98bd7ca0
DH
443
444 /*
445 * Grow our array if we need to.
446 *
447 * Note: we pick 4 as the increment, as that seems a reasonable
9322442f
FD
448 * guess as to how many addresses/prefixes we might expect
449 * on an interface.
98bd7ca0 450 */
1d17db44
FD
451 if (ia->max_iasubopt <= ia->num_iasubopt) {
452 max = ia->max_iasubopt + 4;
453 new = dmalloc(max * sizeof(struct iasubopt *), file, line);
98bd7ca0
DH
454 if (new == NULL) {
455 return ISC_R_NOMEMORY;
456 }
1d17db44
FD
457 memcpy(new, ia->iasubopt,
458 ia->num_iasubopt * sizeof(struct iasubopt *));
459 ia->iasubopt = new;
460 ia->max_iasubopt = max;
98bd7ca0
DH
461 }
462
1d17db44
FD
463 iasubopt_reference(&(ia->iasubopt[ia->num_iasubopt]), iasubopt,
464 file, line);
465 ia->num_iasubopt++;
98bd7ca0
DH
466
467 return ISC_R_SUCCESS;
468}
469
470/*
9322442f 471 * Remove an IAADDR/PREFIX entry to an IA structure.
98bd7ca0 472 *
1d17db44 473 * Note: if a suboption appears more than once, then only ONE will be removed.
98bd7ca0
DH
474 */
475void
1d17db44
FD
476ia_remove_iasubopt(struct ia_xx *ia, struct iasubopt *iasubopt,
477 const char *file, int line) {
98bd7ca0 478 int i, j;
a1a15031
TM
479 if (ia == NULL || iasubopt == NULL)
480 return;
98bd7ca0 481
1d17db44
FD
482 for (i=0; i<ia->num_iasubopt; i++) {
483 if (ia->iasubopt[i] == iasubopt) {
484 /* remove this sub option */
485 iasubopt_dereference(&(ia->iasubopt[i]), file, line);
486 /* move remaining suboption pointers down one */
487 for (j=i+1; j < ia->num_iasubopt; j++) {
488 ia->iasubopt[j-1] = ia->iasubopt[j];
98bd7ca0
DH
489 }
490 /* decrease our total count */
1d17db44
FD
491 /* remove the back-reference in the suboption itself */
492 ia_dereference(&iasubopt->ia, file, line);
493 ia->num_iasubopt--;
98bd7ca0
DH
494 return;
495 }
496 }
9322442f 497 log_error("%s(%d): IAADDR/PREFIX not in IA", file, line);
98bd7ca0
DH
498}
499
727cae26 500/*
9322442f 501 * Remove all addresses/prefixes from an IA.
d9b43370
SK
502 */
503void
1d17db44 504ia_remove_all_lease(struct ia_xx *ia, const char *file, int line) {
28868515 505 int i;
d9b43370 506
1d17db44
FD
507 for (i=0; i<ia->num_iasubopt; i++) {
508 ia_dereference(&(ia->iasubopt[i]->ia), file, line);
509 iasubopt_dereference(&(ia->iasubopt[i]), file, line);
d9b43370 510 }
1d17db44 511 ia->num_iasubopt = 0;
d9b43370
SK
512}
513
b9137d42 514/*
1d9774ab 515 * Compare two IA.
b9137d42
SK
516 */
517isc_boolean_t
9322442f 518ia_equal(const struct ia_xx *a, const struct ia_xx *b)
b9137d42
SK
519{
520 isc_boolean_t found;
521 int i, j;
522
523 /*
524 * Handle cases where one or both of the inputs is NULL.
525 */
526 if (a == NULL) {
527 if (b == NULL) {
528 return ISC_TRUE;
529 } else {
530 return ISC_FALSE;
531 }
532 }
533
1d9774ab
FD
534 /*
535 * Check the type is the same.
536 */
537 if (a->ia_type != b->ia_type) {
538 return ISC_FALSE;
539 }
540
b9137d42
SK
541 /*
542 * Check the DUID is the same.
543 */
544 if (a->iaid_duid.len != b->iaid_duid.len) {
545 return ISC_FALSE;
546 }
547 if (memcmp(a->iaid_duid.data,
548 b->iaid_duid.data, a->iaid_duid.len) != 0) {
549 return ISC_FALSE;
550 }
551
552 /*
9322442f 553 * Make sure we have the same number of addresses/prefixes in each.
b9137d42 554 */
1d17db44 555 if (a->num_iasubopt != b->num_iasubopt) {
b9137d42
SK
556 return ISC_FALSE;
557 }
558
559 /*
9322442f 560 * Check that each address/prefix is present in both.
b9137d42 561 */
1d17db44 562 for (i=0; i<a->num_iasubopt; i++) {
b9137d42 563 found = ISC_FALSE;
1d17db44
FD
564 for (j=0; j<a->num_iasubopt; j++) {
565 if (a->iasubopt[i]->plen != b->iasubopt[i]->plen)
9322442f 566 continue;
1d17db44
FD
567 if (memcmp(&(a->iasubopt[i]->addr),
568 &(b->iasubopt[j]->addr),
1d9774ab 569 sizeof(struct in6_addr)) == 0) {
b9137d42
SK
570 found = ISC_TRUE;
571 break;
572 }
573 }
574 if (!found) {
575 return ISC_FALSE;
576 }
577 }
578
579 /*
580 * These are the same in every way we care about.
581 */
582 return ISC_TRUE;
583}
584
1d9774ab
FD
585/*
586 * Helper function for lease heaps.
587 * Makes the top of the heap the oldest lease.
1d9774ab
FD
588 */
589static isc_boolean_t
590lease_older(void *a, void *b) {
1d17db44
FD
591 struct iasubopt *la = (struct iasubopt *)a;
592 struct iasubopt *lb = (struct iasubopt *)b;
1d9774ab 593
1d17db44
FD
594 if (la->hard_lifetime_end_time == lb->hard_lifetime_end_time) {
595 return difftime(la->soft_lifetime_end_time,
596 lb->soft_lifetime_end_time) < 0;
5d89d60f 597 } else {
1d17db44
FD
598 return difftime(la->hard_lifetime_end_time,
599 lb->hard_lifetime_end_time) < 0;
5d89d60f 600 }
1d9774ab
FD
601}
602
603/*
abacf8ad 604 * Helper functions for lease address/prefix heaps.
1d9774ab
FD
605 * Callback when an address's position in the heap changes.
606 */
607static void
abacf8ad
TM
608active_changed(void *iasubopt, unsigned int new_heap_index) {
609 ((struct iasubopt *)iasubopt)->active_index = new_heap_index;
1d9774ab
FD
610}
611
abacf8ad
TM
612static void
613inactive_changed(void *iasubopt, unsigned int new_heap_index) {
614 ((struct iasubopt *)iasubopt)->inactive_index = new_heap_index;
615}
1d9774ab 616
01fa619f 617/*!
1d9774ab 618 *
01fa619f
SR
619 * \brief Create a new IPv6 lease pool structure
620 *
621 * Allocate space for a new ipv6_pool structure and return a reference
622 * to it, includes setting the reference count to 1.
623 *
624 * \param pool = space for returning a referenced pointer to the pool.
625 * This must point to a space that has been initialzied
626 * to NULL by the caller.
627 * \param[in] type = The type of the pool NA, TA or PD
628 * \param[in] start_addr = The first address in the range for the pool
629 * \param[in] bits = The contiguous bits of the pool
630
631 *
632 * \return
633 * ISC_R_SUCCESS = The pool was successfully created, pool points to it.
634 * DHCP_R_INVALIDARG = One of the arugments was invalid, pool has not been
635 * modified
636 * ISC_R_NOMEMORY = The system wasn't able to allocate memory, pool has
637 * not been modified.
1d9774ab
FD
638 */
639isc_result_t
9322442f 640ipv6_pool_allocate(struct ipv6_pool **pool, u_int16_t type,
1d9774ab 641 const struct in6_addr *start_addr, int bits,
9322442f 642 int units, const char *file, int line) {
1d9774ab
FD
643 struct ipv6_pool *tmp;
644
645 if (pool == NULL) {
646 log_error("%s(%d): NULL pointer reference", file, line);
98bf1607 647 return DHCP_R_INVALIDARG;
1d9774ab
FD
648 }
649 if (*pool != NULL) {
650 log_error("%s(%d): non-NULL pointer", file, line);
98bf1607 651 return DHCP_R_INVALIDARG;
1d9774ab
FD
652 }
653
654 tmp = dmalloc(sizeof(*tmp), file, line);
655 if (tmp == NULL) {
656 return ISC_R_NOMEMORY;
657 }
658
659 tmp->refcnt = 1;
9322442f 660 tmp->pool_type = type;
1d9774ab
FD
661 tmp->start_addr = *start_addr;
662 tmp->bits = bits;
9322442f 663 tmp->units = units;
1d17db44 664 if (!iasubopt_new_hash(&tmp->leases, DEFAULT_HASH_SIZE, file, line)) {
1d9774ab
FD
665 dfree(tmp, file, line);
666 return ISC_R_NOMEMORY;
667 }
abacf8ad 668 if (isc_heap_create(dhcp_gbl_ctx.mctx, lease_older, active_changed,
1d9774ab 669 0, &(tmp->active_timeouts)) != ISC_R_SUCCESS) {
1d17db44 670 iasubopt_free_hash_table(&(tmp->leases), file, line);
1d9774ab
FD
671 dfree(tmp, file, line);
672 return ISC_R_NOMEMORY;
673 }
abacf8ad 674 if (isc_heap_create(dhcp_gbl_ctx.mctx, lease_older, inactive_changed,
1d9774ab
FD
675 0, &(tmp->inactive_timeouts)) != ISC_R_SUCCESS) {
676 isc_heap_destroy(&(tmp->active_timeouts));
1d17db44 677 iasubopt_free_hash_table(&(tmp->leases), file, line);
1d9774ab
FD
678 dfree(tmp, file, line);
679 return ISC_R_NOMEMORY;
680 }
681
682 *pool = tmp;
683 return ISC_R_SUCCESS;
684}
685
01fa619f 686/*!
1d9774ab 687 *
01fa619f
SR
688 * \brief reference an IPv6 pool structure.
689 *
690 * This function genreates a reference to an ipv6_pool structure
691 * and increments the reference count on the structure.
692 *
693 * \param[out] pool = space for returning a referenced pointer to the pool.
694 * This must point to a space that has been initialzied
695 * to NULL by the caller.
696 * \param[in] src = A pointer to the pool to reference. This must not be
697 * NULL.
698 *
699 * \return
700 * ISC_R_SUCCESS = The pool was successfully referenced, pool now points
701 * to src.
702 * DHCP_R_INVALIDARG = One of the arugments was invalid, pool has not been
703 * modified.
1d9774ab
FD
704 */
705isc_result_t
706ipv6_pool_reference(struct ipv6_pool **pool, struct ipv6_pool *src,
707 const char *file, int line) {
708 if (pool == NULL) {
709 log_error("%s(%d): NULL pointer reference", file, line);
98bf1607 710 return DHCP_R_INVALIDARG;
1d9774ab
FD
711 }
712 if (*pool != NULL) {
713 log_error("%s(%d): non-NULL pointer", file, line);
98bf1607 714 return DHCP_R_INVALIDARG;
1d9774ab
FD
715 }
716 if (src == NULL) {
717 log_error("%s(%d): NULL pointer reference", file, line);
98bf1607 718 return DHCP_R_INVALIDARG;
1d9774ab
FD
719 }
720 *pool = src;
721 src->refcnt++;
722 return ISC_R_SUCCESS;
723}
724
725/*
9322442f 726 * Note: Each IAADDR/PREFIX in a pool is referenced by the pool. This is needed
1d17db44 727 * to prevent the lease from being garbage collected out from under the
1d9774ab
FD
728 * pool.
729 *
730 * The references are made from the hash and from the heap. The following
731 * helper functions dereference these when a pool is destroyed.
732 */
733
734/*
735 * Helper function for pool cleanup.
736 * Dereference each of the hash entries in a pool.
737 */
738static isc_result_t
739dereference_hash_entry(const void *name, unsigned len, void *value) {
1d17db44 740 struct iasubopt *iasubopt = (struct iasubopt *)value;
1d9774ab 741
1d17db44 742 iasubopt_dereference(&iasubopt, MDL);
1d9774ab
FD
743 return ISC_R_SUCCESS;
744}
745
746/*
747 * Helper function for pool cleanup.
748 * Dereference each of the heap entries in a pool.
749 */
750static void
751dereference_heap_entry(void *value, void *dummy) {
1d17db44 752 struct iasubopt *iasubopt = (struct iasubopt *)value;
1d9774ab 753
1d17db44 754 iasubopt_dereference(&iasubopt, MDL);
1d9774ab
FD
755}
756
01fa619f 757/*!
1d9774ab 758 *
01fa619f
SR
759 * \brief de-reference an IPv6 pool structure.
760 *
761 * This function decrements the reference count in an ipv6_pool structure.
762 * If this was the last reference then the memory for the structure is
763 * freed.
764 *
765 * \param[in] pool = A pointer to the pointer to the pool that should be
766 * de-referenced. On success the pointer to the pool
767 * is cleared. It must not be NULL and must not point
768 * to NULL.
769 *
770 * \return
771 * ISC_R_SUCCESS = The pool was successfully de-referenced, pool now points
772 * to NULL
773 * DHCP_R_INVALIDARG = One of the arugments was invalid, pool has not been
774 * modified.
1d9774ab
FD
775 */
776isc_result_t
777ipv6_pool_dereference(struct ipv6_pool **pool, const char *file, int line) {
778 struct ipv6_pool *tmp;
779
780 if ((pool == NULL) || (*pool == NULL)) {
781 log_error("%s(%d): NULL pointer", file, line);
98bf1607 782 return DHCP_R_INVALIDARG;
1d9774ab
FD
783 }
784
785 tmp = *pool;
786 *pool = NULL;
787
788 tmp->refcnt--;
789 if (tmp->refcnt < 0) {
790 log_error("%s(%d): negative refcnt", file, line);
791 tmp->refcnt = 0;
792 }
793 if (tmp->refcnt == 0) {
1d17db44
FD
794 iasubopt_hash_foreach(tmp->leases, dereference_hash_entry);
795 iasubopt_free_hash_table(&(tmp->leases), file, line);
1d9774ab
FD
796 isc_heap_foreach(tmp->active_timeouts,
797 dereference_heap_entry, NULL);
798 isc_heap_destroy(&(tmp->active_timeouts));
799 isc_heap_foreach(tmp->inactive_timeouts,
800 dereference_heap_entry, NULL);
801 isc_heap_destroy(&(tmp->inactive_timeouts));
802 dfree(tmp, file, line);
803 }
804
805 return ISC_R_SUCCESS;
806}
807
1d9774ab
FD
808/*
809 * Create an address by hashing the input, and using that for
810 * the non-network part.
811 */
812static void
5d89d60f 813build_address6(struct in6_addr *addr,
1d9774ab
FD
814 const struct in6_addr *net_start_addr, int net_bits,
815 const struct data_string *input) {
98bf1607 816 isc_md5_t ctx;
1d9774ab
FD
817 int net_bytes;
818 int i;
819 char *str;
820 const char *net_str;
821
822 /*
823 * Use MD5 to get a nice 128 bit hash of the input.
824 * Yes, we know MD5 isn't cryptographically sound.
825 * No, we don't care.
826 */
98bf1607
SR
827 isc_md5_init(&ctx);
828 isc_md5_update(&ctx, input->data, input->len);
829 isc_md5_final(&ctx, (unsigned char *)addr);
1d9774ab
FD
830
831 /*
783259b1 832 * Copy the [0..128] network bits over.
1d9774ab
FD
833 */
834 str = (char *)addr;
835 net_str = (const char *)net_start_addr;
836 net_bytes = net_bits / 8;
783259b1 837 for (i = 0; i < net_bytes; i++) {
1d9774ab
FD
838 str[i] = net_str[i];
839 }
840 switch (net_bits % 8) {
841 case 1: str[i] = (str[i] & 0x7F) | (net_str[i] & 0x80); break;
842 case 2: str[i] = (str[i] & 0x3F) | (net_str[i] & 0xC0); break;
843 case 3: str[i] = (str[i] & 0x1F) | (net_str[i] & 0xE0); break;
844 case 4: str[i] = (str[i] & 0x0F) | (net_str[i] & 0xF0); break;
845 case 5: str[i] = (str[i] & 0x07) | (net_str[i] & 0xF8); break;
846 case 6: str[i] = (str[i] & 0x03) | (net_str[i] & 0xFC); break;
847 case 7: str[i] = (str[i] & 0x01) | (net_str[i] & 0xFE); break;
848 }
53883ced
DH
849
850 /*
851 * Set the universal/local bit ("u bit") to zero for /64s. The
852 * individual/group bit ("g bit") is unchanged, because the g-bit
853 * has no meaning when the u-bit is cleared.
854 */
1d9774ab
FD
855 if (net_bits == 64)
856 str[8] &= ~0x02;
857}
858
417b7b4a
TM
859#ifdef EUI_64
860int
861valid_eui_64_duid(const struct data_string* uid, int offset) {
862 if (uid->len == (offset + EUI_64_ID_LEN)) {
863 const unsigned char* duid = uid->data + offset;
864 return (((duid[0] == 0x00 && duid[1] == 0x03) &&
865 (duid[2] == 0x00 && duid[3] == 0x1b)));
866 }
867
868 return(0);
869}
870
871
872/*
873 * Create an EUI-64 address
874 */
875static isc_result_t
876build_address6_eui_64(struct in6_addr *addr,
877 const struct in6_addr *net_start_addr, int net_bits,
878 const struct data_string *iaid_duid, int duid_beg) {
879
880 if (net_bits != 64) {
881 log_error("build_address_eui_64: network is not 64 bits");
882 return (ISC_R_FAILURE);
883 }
884
885 if (valid_eui_64_duid(iaid_duid, duid_beg)) {
886 const unsigned char *duid = iaid_duid->data + duid_beg;
887
888 /* copy network prefix to the high 64 bits */
889 memcpy(addr->s6_addr, net_start_addr->s6_addr, 8);
890
891 /* copy Link-layer address to low 64 bits */
892 memcpy(addr->s6_addr + 8, duid + 4, 8);
893
894 /* RFC-3315 Any address assigned by a server that is based
895 * on an EUI-64 identifier MUST include an interface identifier
896 * with the "u" (universal/local) and "g" (individual/group)
897 * bits of the interface identifier set appropriately, as
898 * indicated in section 2.5.1 of RFC 2373 [5]. */
899 addr->s6_addr[8] |= 0x02;
900 return (ISC_R_SUCCESS);
901 }
902
903 log_error("build_address_eui_64: iaid_duid not a valid EUI-64: %s",
904 print_hex_1(iaid_duid->len, iaid_duid->data, 60));
905 return (ISC_R_FAILURE);
906}
907
908int
909valid_for_eui_64_pool(struct ipv6_pool* pool, struct data_string* uid,
910 int duid_beg, struct in6_addr* ia_addr) {
911 struct in6_addr test_addr;
912 /* If it's not an EUI-64 pool bail */
913 if (!pool->ipv6_pond->use_eui_64) {
914 return (0);
915 }
916
917 if (!valid_eui_64_duid(uid, duid_beg)) {
918 /* Dynamic lease in a now eui_64 pond, toss it*/
919 return (0);
920 }
921
922 /* Call build_address6_eui_64() and compare it's result to
923 * this lease and see if they match. */
924 memset (&test_addr, 0, sizeof(test_addr));
925 build_address6_eui_64(&test_addr, &pool->start_addr, pool->bits,
926 uid, duid_beg);
927
928 return (!memcmp(ia_addr, &test_addr, sizeof(test_addr)));
929}
930#endif
931
932
80c9fdb0
FD
933/*
934 * Create a temporary address by a variant of RFC 4941 algo.
783259b1 935 * Note: this should not be used for prefixes shorter than 64 bits.
80c9fdb0
FD
936 */
937static void
5d89d60f 938build_temporary6(struct in6_addr *addr,
783259b1 939 const struct in6_addr *net_start_addr, int net_bits,
80c9fdb0 940 const struct data_string *input) {
98bf1607 941 static u_int32_t history[2];
80c9fdb0 942 static u_int32_t counter = 0;
98bf1607 943 isc_md5_t ctx;
80c9fdb0 944 unsigned char md[16];
80c9fdb0
FD
945
946 /*
947 * First time/time to reseed.
948 * Please use a good pseudo-random generator here!
949 */
950 if (counter == 0) {
98bf1607
SR
951 isc_random_get(&history[0]);
952 isc_random_get(&history[1]);
80c9fdb0
FD
953 }
954
955 /*
956 * Use MD5 as recommended by RFC 4941.
957 */
98bf1607
SR
958 isc_md5_init(&ctx);
959 isc_md5_update(&ctx, (unsigned char *)&history[0], 8UL);
960 isc_md5_update(&ctx, input->data, input->len);
961 isc_md5_final(&ctx, md);
80c9fdb0
FD
962
963 /*
964 * Build the address.
965 */
783259b1
FD
966 if (net_bits == 64) {
967 memcpy(&addr->s6_addr[0], &net_start_addr->s6_addr[0], 8);
968 memcpy(&addr->s6_addr[8], md, 8);
969 addr->s6_addr[8] &= ~0x02;
970 } else {
971 int net_bytes;
972 int i;
973 char *str;
974 const char *net_str;
975
976 /*
977 * Copy the [0..128] network bits over.
978 */
979 str = (char *)addr;
980 net_str = (const char *)net_start_addr;
981 net_bytes = net_bits / 8;
982 for (i = 0; i < net_bytes; i++) {
983 str[i] = net_str[i];
984 }
985 memcpy(str + net_bytes, md, 16 - net_bytes);
986 switch (net_bits % 8) {
987 case 1: str[i] = (str[i] & 0x7F) | (net_str[i] & 0x80); break;
988 case 2: str[i] = (str[i] & 0x3F) | (net_str[i] & 0xC0); break;
989 case 3: str[i] = (str[i] & 0x1F) | (net_str[i] & 0xE0); break;
990 case 4: str[i] = (str[i] & 0x0F) | (net_str[i] & 0xF0); break;
991 case 5: str[i] = (str[i] & 0x07) | (net_str[i] & 0xF8); break;
992 case 6: str[i] = (str[i] & 0x03) | (net_str[i] & 0xFC); break;
993 case 7: str[i] = (str[i] & 0x01) | (net_str[i] & 0xFE); break;
994 }
995 }
996
80c9fdb0
FD
997
998 /*
999 * Save history for the next call.
1000 */
98bf1607 1001 memcpy((unsigned char *)&history[0], md + 8, 8);
80c9fdb0
FD
1002 counter++;
1003}
1004
1d9774ab 1005/* Reserved Subnet Router Anycast ::0:0:0:0. */
c4fea0db 1006static struct in6_addr rtany;
0674055a 1007/* Reserved Subnet Anycasts ::fdff:ffff:ffff:ff80-::fdff:ffff:ffff:ffff. */
c4fea0db 1008static struct in6_addr resany;
0674055a 1009
98bd7ca0 1010/*
1d9774ab
FD
1011 * Create a lease for the given address and client duid.
1012 *
01fa619f 1013 * - pool must be a pointer to a (struct ipv6_pool *) pointer previously
1d9774ab
FD
1014 * initialized to NULL
1015 *
1016 * Right now we simply hash the DUID, and if we get a collision, we hash
1017 * again until we find a free address. We try this a fixed number of times,
1018 * to avoid getting stuck in a loop (this is important on small pools
1019 * where we can run out of space).
1020 *
1021 * We return the number of attempts that it took to find an available
1022 * lease. This tells callers when a pool is are filling up, as
1023 * well as an indication of how full the pool is; statistically the
1024 * more full a pool is the more attempts must be made before finding
1025 * a free lease. Realistically this will only happen in very full
1026 * pools.
1027 *
1028 * We probably want different algorithms depending on the network size, in
1029 * the long term.
1030 */
1031isc_result_t
1d17db44 1032create_lease6(struct ipv6_pool *pool, struct iasubopt **addr,
5d89d60f
FD
1033 unsigned int *attempts,
1034 const struct data_string *uid, time_t soft_lifetime_end_time) {
1d9774ab
FD
1035 struct data_string ds;
1036 struct in6_addr tmp;
1d17db44 1037 struct iasubopt *test_iaaddr;
1d9774ab 1038 struct data_string new_ds;
1d17db44 1039 struct iasubopt *iaaddr;
1d9774ab
FD
1040 isc_result_t result;
1041 isc_boolean_t reserved_iid;
1042 static isc_boolean_t init_resiid = ISC_FALSE;
1043
1044 /*
1045 * Fill the reserved IIDs.
1046 */
1047 if (!init_resiid) {
1048 memset(&rtany, 0, 16);
1049 memset(&resany, 0, 8);
1050 resany.s6_addr[8] = 0xfd;
1051 memset(&resany.s6_addr[9], 0xff, 6);
1052 init_resiid = ISC_TRUE;
1053 }
1054
1055 /*
1056 * Use the UID as our initial seed for the hash
1057 */
1058 memset(&ds, 0, sizeof(ds));
1059 data_string_copy(&ds, (struct data_string *)uid, MDL);
1060
1061 *attempts = 0;
1062 for (;;) {
1063 /*
1064 * Give up at some point.
1065 */
1066 if (++(*attempts) > 100) {
1067 data_string_forget(&ds, MDL);
1068 return ISC_R_NORESOURCES;
1069 }
1070
1071 /*
9322442f 1072 * Build a resource.
1d9774ab 1073 */
9322442f
FD
1074 switch (pool->pool_type) {
1075 case D6O_IA_NA:
1076 /* address */
5d89d60f 1077 build_address6(&tmp, &pool->start_addr,
80c9fdb0 1078 pool->bits, &ds);
9322442f
FD
1079 break;
1080 case D6O_IA_TA:
1081 /* temporary address */
783259b1
FD
1082 build_temporary6(&tmp, &pool->start_addr,
1083 pool->bits, &ds);
9322442f
FD
1084 break;
1085 case D6O_IA_PD:
1086 /* prefix */
1087 log_error("create_lease6: prefix pool.");
0cd94b5e 1088 data_string_forget(&ds, MDL);
98bf1607 1089 return DHCP_R_INVALIDARG;
9322442f
FD
1090 default:
1091 log_error("create_lease6: untyped pool.");
0cd94b5e 1092 data_string_forget(&ds, MDL);
98bf1607 1093 return DHCP_R_INVALIDARG;
80c9fdb0 1094 }
1d9774ab
FD
1095
1096 /*
de6c9af6 1097 * Avoid reserved interface IDs. (cf. RFC 5453)
1d9774ab
FD
1098 */
1099 reserved_iid = ISC_FALSE;
de6c9af6 1100 if (memcmp(&tmp.s6_addr[8], &rtany.s6_addr[8], 8) == 0) {
1d9774ab
FD
1101 reserved_iid = ISC_TRUE;
1102 }
1103 if (!reserved_iid &&
de6c9af6 1104 (memcmp(&tmp.s6_addr[8], &resany.s6_addr[8], 7) == 0) &&
1d9774ab
FD
1105 ((tmp.s6_addr[15] & 0x80) == 0x80)) {
1106 reserved_iid = ISC_TRUE;
1107 }
1108
1109 /*
1110 * If this address is not in use, we're happy with it
1111 */
1112 test_iaaddr = NULL;
1113 if (!reserved_iid &&
1d17db44
FD
1114 (iasubopt_hash_lookup(&test_iaaddr, pool->leases,
1115 &tmp, sizeof(tmp), MDL) == 0)) {
1d9774ab
FD
1116 break;
1117 }
1118 if (test_iaaddr != NULL)
1d17db44 1119 iasubopt_dereference(&test_iaaddr, MDL);
1d9774ab
FD
1120
1121 /*
1122 * Otherwise, we create a new input, adding the address
1123 */
1124 memset(&new_ds, 0, sizeof(new_ds));
1125 new_ds.len = ds.len + sizeof(tmp);
1126 if (!buffer_allocate(&new_ds.buffer, new_ds.len, MDL)) {
1127 data_string_forget(&ds, MDL);
1128 return ISC_R_NOMEMORY;
1129 }
1130 new_ds.data = new_ds.buffer->data;
1131 memcpy(new_ds.buffer->data, ds.data, ds.len);
1132 memcpy(new_ds.buffer->data + ds.len, &tmp, sizeof(tmp));
1133 data_string_forget(&ds, MDL);
1134 data_string_copy(&ds, &new_ds, MDL);
1135 data_string_forget(&new_ds, MDL);
1136 }
1137
1138 data_string_forget(&ds, MDL);
1139
1140 /*
1141 * We're happy with the address, create an IAADDR
1142 * to hold it.
1143 */
1144 iaaddr = NULL;
1d17db44 1145 result = iasubopt_allocate(&iaaddr, MDL);
1d9774ab
FD
1146 if (result != ISC_R_SUCCESS) {
1147 return result;
1148 }
9322442f 1149 iaaddr->plen = 0;
1d9774ab
FD
1150 memcpy(&iaaddr->addr, &tmp, sizeof(iaaddr->addr));
1151
1152 /*
80c9fdb0 1153 * Add the lease to the pool (note state is free, not active?!).
1d9774ab 1154 */
5d89d60f 1155 result = add_lease6(pool, iaaddr, soft_lifetime_end_time);
1d9774ab 1156 if (result == ISC_R_SUCCESS) {
1d17db44 1157 iasubopt_reference(addr, iaaddr, MDL);
1d9774ab 1158 }
1d17db44 1159 iasubopt_dereference(&iaaddr, MDL);
1d9774ab
FD
1160 return result;
1161}
1162
417b7b4a
TM
1163#ifdef EUI_64
1164/*!
1165 * \brief Assign an EUI-64 address from a pool for a given iaid-duid
1166 *
1167 * \param pool - pool from which the address is assigned
1168 * \param iaddr - pointer to the iasubopt to contain the assigned address is
1169 * \param uid - data_string containing the iaid-duid tuple
1170 * \param soft_lifetime_end_time - lifetime of the lease for a solicit?
1171 *
1172 * \return status indicating success or nature of the failure
1173*/
1174isc_result_t
1175create_lease6_eui_64(struct ipv6_pool *pool, struct iasubopt **addr,
1176 const struct data_string *uid,
1177 time_t soft_lifetime_end_time) {
1178 struct in6_addr tmp;
1179 struct iasubopt *test_iaaddr;
1180 struct iasubopt *iaaddr;
1181 isc_result_t result;
1182 static isc_boolean_t init_resiid = ISC_FALSE;
1183
1184 /* Fill the reserved IIDs. */
1185 if (!init_resiid) {
1186 memset(&rtany, 0, 16);
1187 memset(&resany, 0, 8);
1188 resany.s6_addr[8] = 0xfd;
1189 memset(&resany.s6_addr[9], 0xff, 6);
1190 init_resiid = ISC_TRUE;
1191 }
1192
1193 /* Pool must be IA_NA */
1194 if (pool->pool_type != D6O_IA_NA) {
1195 log_error("create_lease6_eui_64: pool type is not IA_NA.");
1196 return (DHCP_R_INVALIDARG);
1197 }
1198
1199 /* Attempt to build the address */
1200 if (build_address6_eui_64 (&tmp, &pool->start_addr, pool->bits,
1201 uid, IAID_LEN) != ISC_R_SUCCESS) {
1202 log_error("create_lease6_eui_64: build_address6_eui_64 failed");
1203 return (ISC_R_FAILURE);
1204 }
1205
1206 /* Avoid reserved interface IDs. (cf. RFC 5453) */
1207 if ((memcmp(&tmp.s6_addr[8], &rtany.s6_addr[8], 8) == 0) ||
1208 ((memcmp(&tmp.s6_addr[8], &resany.s6_addr[8], 7) == 0) &&
1209 ((tmp.s6_addr[15] & 0x80) == 0x80))) {
1210 log_error("create_lease6_eui_64: "
1211 "address conflicts with reserved IID");
1212 return (ISC_R_FAILURE);
1213 }
1214
1215 /* If this address is not in use, we're happy with it */
1216 test_iaaddr = NULL;
1217 if (iasubopt_hash_lookup(&test_iaaddr, pool->leases,
1218 &tmp, sizeof(tmp), MDL) != 0) {
1219
1220 /* See if it's ours. Static leases won't have an ia */
1221 int ours = 0;
1222 if (!test_iaaddr->ia) {
1223 log_error("create_lease6_eui_64: "
1224 "address %s is assigned to static lease",
1225 pin6_addr(&test_iaaddr->addr));
1226 } else {
1227 /* Not sure if this can actually happen */
1228 struct data_string* found = &test_iaaddr->ia->iaid_duid;
1229 ours = ((found->len == uid->len) &&
1230 (!memcmp(found->data, uid->data, uid->len)));
1231 log_error("create_lease6_eui_64: "
1232 "address %s belongs to %s",
1233 pin6_addr(&test_iaaddr->addr),
1234 print_hex_1(found->len, found->data, 60));
1235 }
1236
1237 iasubopt_dereference(&test_iaaddr, MDL);
1238 if (!ours) {
1239 /* Cant' use it */
1240 return (ISC_R_FAILURE);
1241 }
1242 }
1243
1244 /* We're happy with the address, create an IAADDR to hold it. */
1245 iaaddr = NULL;
1246 result = iasubopt_allocate(&iaaddr, MDL);
1247 if (result != ISC_R_SUCCESS) {
1248 log_error("create_lease6_eui_64: could not allocate iasubop");
1249 return result;
1250 }
1251 iaaddr->plen = 0;
1252 memcpy(&iaaddr->addr, &tmp, sizeof(iaaddr->addr));
1253
1254 /* Add the lease to the pool and the reply */
1255 result = add_lease6(pool, iaaddr, soft_lifetime_end_time);
1256 if (result == ISC_R_SUCCESS) {
1257 iasubopt_reference(addr, iaaddr, MDL);
1258 }
1259
1260 iasubopt_dereference(&iaaddr, MDL);
1261 return result;
1262}
1263#endif
bc7f8b8e 1264
0b2ec8c9 1265/*!
bc7f8b8e
SR
1266 *
1267 * \brief Cleans up leases when reading from a lease file
1268 *
1269 * This function is only expected to be run when reading leases in from a file.
1270 * It checks to see if a lease already exists for the new leases's address.
1271 * We don't add expired leases to the structures when reading a lease file
1272 * which limits what can happen. We have two variables the owners of the leases
1273 * being the same or different and the new lease being active or non-active:
1274 * Owners active
1275 * same no remove old lease and its connections
1276 * same yes nothing to do, other code will update the structures.
1277 * diff no nothing to do
1278 * diff yes this combination shouldn't happen, we should only have a
1279 * single active lease per address at a time and that lease
1280 * should move to non-active before any other lease can
1281 * become active for that address.
1282 * Currently we delete the previous lease and pass an error
1283 * to the caller who should log an error.
1284 *
1285 * When we remove a lease we remove it from the hash table and active heap
1286 * (remember only active leases are in the structures at this time) for the
1287 * pool, and from the IA's array. If, after we've removed the pointer from
1288 * IA's array to the lease, the IA has no more pointers we remove it from
1289 * the appropriate hash table as well.
1290 *
1291 * \param[in] ia_table = the hash table for the IA
1292 * \param[in] pool = the pool to update
1293 * \param[in] lease = the new lease we want to add
1294 * \param[in] ia = the new ia we are building
1295 *
1296 * \return
1297 * ISC_R_SUCCESS = the incoming lease and any previous lease were in
1298 * an expected state - one of the first 3 options above.
1299 * If necessary the old lease was removed.
1300 * ISC_R_FAILURE = there is already an active lease for the address in
1301 * the incoming lease. This shouldn't happen if it does
1302 * flag an error for the caller to log.
1303 */
1304
1305isc_result_t
1306cleanup_lease6(ia_hash_t *ia_table,
1307 struct ipv6_pool *pool,
1308 struct iasubopt *lease,
1309 struct ia_xx *ia) {
1310
1311 struct iasubopt *test_iasubopt, *tmp_iasubopt;
1312 struct ia_xx *old_ia;
1313 isc_result_t status = ISC_R_SUCCESS;
1314
1315 test_iasubopt = NULL;
1316 old_ia = NULL;
1317
1318 /*
1319 * Look up the address - if we don't find a lease
1320 * we don't need to do anything.
1321 */
1322 if (iasubopt_hash_lookup(&test_iasubopt, pool->leases,
1323 &lease->addr, sizeof(lease->addr),
1324 MDL) == 0) {
1325 return (ISC_R_SUCCESS);
1326 }
1327
1328 if (test_iasubopt->ia == NULL) {
1329 /* no old ia, no work to do */
1330 iasubopt_dereference(&test_iasubopt, MDL);
1331 return (status);
1332 }
1333
1334 ia_reference(&old_ia, test_iasubopt->ia, MDL);
1335
1336 if ((old_ia->iaid_duid.len == ia->iaid_duid.len) &&
1337 (memcmp((unsigned char *)ia->iaid_duid.data,
1338 (unsigned char *)old_ia->iaid_duid.data,
1339 ia->iaid_duid.len) == 0)) {
1340 /* same IA */
1341 if ((lease->state == FTS_ACTIVE) ||
1342 (lease->state == FTS_ABANDONED)) {
1343 /* still active, no need to delete */
1344 goto cleanup;
1345 }
1346 } else {
1347 /* different IA */
1348 if ((lease->state != FTS_ACTIVE) &&
1349 (lease->state != FTS_ABANDONED)) {
1350 /* new lease isn't active, no work */
1351 goto cleanup;
1352 }
1353
1354 /*
1355 * We appear to have two active leases, this shouldn't happen.
1356 * Before a second lease can be set to active the first lease
1357 * should be set to inactive (released, expired etc). For now
1358 * delete the previous lease and indicate a failure to the
1359 * caller so it can generate a warning.
1360 * In the future we may try and determine which is the better
1361 * lease to keep.
1362 */
1363
1364 status = ISC_R_FAILURE;
1365 }
1366
1367 /*
1368 * Remove the old lease from the active heap and from the hash table
1369 * then remove the lease from the IA and clean up the IA if necessary.
1370 */
abacf8ad 1371 isc_heap_delete(pool->active_timeouts, test_iasubopt->active_index);
bc7f8b8e 1372 pool->num_active--;
250f7134
SR
1373 if (pool->ipv6_pond)
1374 pool->ipv6_pond->num_active--;
bc7f8b8e 1375
fb98e02e
TM
1376 if (lease->state == FTS_ABANDONED) {
1377 pool->num_abandoned--;
1378 if (pool->ipv6_pond)
1379 pool->ipv6_pond->num_abandoned--;
1380 }
1381
bc7f8b8e
SR
1382 iasubopt_hash_delete(pool->leases, &test_iasubopt->addr,
1383 sizeof(test_iasubopt->addr), MDL);
1384 ia_remove_iasubopt(old_ia, test_iasubopt, MDL);
1385 if (old_ia->num_iasubopt <= 0) {
1386 ia_hash_delete(ia_table,
1387 (unsigned char *)old_ia->iaid_duid.data,
1388 old_ia->iaid_duid.len, MDL);
1389 }
1390
1391 /*
1392 * We derefenrece the subopt here as we've just removed it from
1393 * the hash table in the pool. We need to make a copy as we
1394 * need to derefernece it again later.
1395 */
1396 tmp_iasubopt = test_iasubopt;
1397 iasubopt_dereference(&tmp_iasubopt, MDL);
1398
1399 cleanup:
1400 ia_dereference(&old_ia, MDL);
1401
1402 /*
1403 * Clean up the reference, this is in addition to the deference
1404 * above after removing the entry from the hash table
1405 */
1406 iasubopt_dereference(&test_iasubopt, MDL);
1407
1408 return (status);
1409}
1410
1d9774ab
FD
1411/*
1412 * Put a lease in the pool directly. This is intended to be used when
1413 * loading leases from the file.
1414 */
1415isc_result_t
1d17db44 1416add_lease6(struct ipv6_pool *pool, struct iasubopt *lease,
1d9774ab
FD
1417 time_t valid_lifetime_end_time) {
1418 isc_result_t insert_result;
1d17db44
FD
1419 struct iasubopt *test_iasubopt;
1420 struct iasubopt *tmp_iasubopt;
1d9774ab
FD
1421
1422 /* If a state was not assigned by the caller, assume active. */
1d17db44
FD
1423 if (lease->state == 0)
1424 lease->state = FTS_ACTIVE;
1d9774ab 1425
1d17db44 1426 ipv6_pool_reference(&lease->ipv6_pool, pool, MDL);
1d9774ab
FD
1427
1428 /*
9322442f 1429 * If this IAADDR/PREFIX is already in our structures, remove the
1d9774ab
FD
1430 * old one.
1431 */
1d17db44
FD
1432 test_iasubopt = NULL;
1433 if (iasubopt_hash_lookup(&test_iasubopt, pool->leases,
1434 &lease->addr, sizeof(lease->addr), MDL)) {
1435 /* XXX: we should probably ask the lease what heap it is on
1d9774ab
FD
1436 * (as a consistency check).
1437 * XXX: we should probably have one function to "put this lease
1438 * on its heap" rather than doing these if's everywhere. If
1439 * you add more states to this list, don't.
1440 */
1d17db44
FD
1441 if ((test_iasubopt->state == FTS_ACTIVE) ||
1442 (test_iasubopt->state == FTS_ABANDONED)) {
1d9774ab 1443 isc_heap_delete(pool->active_timeouts,
abacf8ad 1444 test_iasubopt->active_index);
1d9774ab 1445 pool->num_active--;
250f7134
SR
1446 if (pool->ipv6_pond)
1447 pool->ipv6_pond->num_active--;
fb98e02e
TM
1448
1449 if (test_iasubopt->state == FTS_ABANDONED) {
1450 pool->num_abandoned--;
1451 if (pool->ipv6_pond)
1452 pool->ipv6_pond->num_abandoned--;
1453 }
1d9774ab
FD
1454 } else {
1455 isc_heap_delete(pool->inactive_timeouts,
abacf8ad 1456 test_iasubopt->inactive_index);
1d9774ab
FD
1457 pool->num_inactive--;
1458 }
1459
1d17db44
FD
1460 iasubopt_hash_delete(pool->leases, &test_iasubopt->addr,
1461 sizeof(test_iasubopt->addr), MDL);
1d9774ab
FD
1462
1463 /*
1464 * We're going to do a bit of evil trickery here.
1465 *
1466 * We need to dereference the entry once to remove our
1d17db44 1467 * current reference (in test_iasubopt), and then one
1d9774ab
FD
1468 * more time to remove the reference left when the
1469 * address was added to the pool before.
1470 */
1d17db44
FD
1471 tmp_iasubopt = test_iasubopt;
1472 iasubopt_dereference(&test_iasubopt, MDL);
1473 iasubopt_dereference(&tmp_iasubopt, MDL);
1d9774ab
FD
1474 }
1475
1476 /*
9322442f 1477 * Add IAADDR/PREFIX to our structures.
1d9774ab 1478 */
1d17db44
FD
1479 tmp_iasubopt = NULL;
1480 iasubopt_reference(&tmp_iasubopt, lease, MDL);
1481 if ((tmp_iasubopt->state == FTS_ACTIVE) ||
1482 (tmp_iasubopt->state == FTS_ABANDONED)) {
1483 tmp_iasubopt->hard_lifetime_end_time = valid_lifetime_end_time;
1484 iasubopt_hash_add(pool->leases, &tmp_iasubopt->addr,
1485 sizeof(tmp_iasubopt->addr), lease, MDL);
1d9774ab 1486 insert_result = isc_heap_insert(pool->active_timeouts,
1d17db44 1487 tmp_iasubopt);
250f7134 1488 if (insert_result == ISC_R_SUCCESS) {
1d9774ab 1489 pool->num_active++;
250f7134
SR
1490 if (pool->ipv6_pond)
1491 pool->ipv6_pond->num_active++;
fb98e02e
TM
1492
1493 if (tmp_iasubopt->state == FTS_ABANDONED) {
1494 pool->num_abandoned++;
1495 if (pool->ipv6_pond)
1496 pool->ipv6_pond->num_abandoned++;
1497 }
250f7134
SR
1498 }
1499
1d9774ab 1500 } else {
1d17db44 1501 tmp_iasubopt->soft_lifetime_end_time = valid_lifetime_end_time;
1d9774ab 1502 insert_result = isc_heap_insert(pool->inactive_timeouts,
1d17db44 1503 tmp_iasubopt);
1d9774ab
FD
1504 if (insert_result == ISC_R_SUCCESS)
1505 pool->num_inactive++;
1506 }
1507 if (insert_result != ISC_R_SUCCESS) {
1d17db44
FD
1508 iasubopt_hash_delete(pool->leases, &lease->addr,
1509 sizeof(lease->addr), MDL);
1510 iasubopt_dereference(&tmp_iasubopt, MDL);
1d9774ab
FD
1511 return insert_result;
1512 }
1513
1514 /*
1d17db44 1515 * Note: we intentionally leave tmp_iasubopt referenced; there
1d9774ab
FD
1516 * is a reference in the heap/hash, after all.
1517 */
1518
1519 return ISC_R_SUCCESS;
1520}
1521
1522/*
1523 * Determine if an address is present in a pool or not.
1524 */
1525isc_boolean_t
1526lease6_exists(const struct ipv6_pool *pool, const struct in6_addr *addr) {
1d17db44 1527 struct iasubopt *test_iaaddr;
1d9774ab
FD
1528
1529 test_iaaddr = NULL;
1d17db44
FD
1530 if (iasubopt_hash_lookup(&test_iaaddr, pool->leases,
1531 (void *)addr, sizeof(*addr), MDL)) {
1532 iasubopt_dereference(&test_iaaddr, MDL);
1d9774ab
FD
1533 return ISC_TRUE;
1534 } else {
1535 return ISC_FALSE;
1536 }
1537}
1538
bc7f8b8e
SR
1539/*!
1540 *
1541 * \brief Check if address is available to a lease
1542 *
1543 * Determine if the address in the lease is available to that
1544 * lease. Either the address isn't in use or it is in use
1545 * but by that lease.
1546 *
1547 * \param[in] lease = lease to check
1548 *
1549 * \return
1550 * ISC_TRUE = The lease is allowed to use that address
1551 * ISC_FALSE = The lease isn't allowed to use that address
1552 */
1553isc_boolean_t
1554lease6_usable(struct iasubopt *lease) {
1555 struct iasubopt *test_iaaddr;
1556 isc_boolean_t status = ISC_TRUE;
1557
1558 test_iaaddr = NULL;
1559 if (iasubopt_hash_lookup(&test_iaaddr, lease->ipv6_pool->leases,
1560 (void *)&lease->addr,
1561 sizeof(lease->addr), MDL)) {
1562 if (test_iaaddr != lease) {
1563 status = ISC_FALSE;
1564 }
1565 iasubopt_dereference(&test_iaaddr, MDL);
1566 }
1567
1568 return (status);
1569}
1570
1d9774ab
FD
1571/*
1572 * Put the lease on our active pool.
1573 */
1574static isc_result_t
1d17db44 1575move_lease_to_active(struct ipv6_pool *pool, struct iasubopt *lease) {
1d9774ab 1576 isc_result_t insert_result;
1d9774ab 1577
1d17db44 1578 insert_result = isc_heap_insert(pool->active_timeouts, lease);
1d9774ab 1579 if (insert_result == ISC_R_SUCCESS) {
1d17db44
FD
1580 iasubopt_hash_add(pool->leases, &lease->addr,
1581 sizeof(lease->addr), lease, MDL);
abacf8ad
TM
1582 isc_heap_delete(pool->inactive_timeouts,
1583 lease->inactive_index);
1d9774ab
FD
1584 pool->num_active++;
1585 pool->num_inactive--;
1d17db44 1586 lease->state = FTS_ACTIVE;
250f7134
SR
1587 if (pool->ipv6_pond)
1588 pool->ipv6_pond->num_active++;
fb98e02e 1589
1d9774ab
FD
1590 }
1591 return insert_result;
1592}
1593
0b2ec8c9 1594/*!
01fa619f 1595 *
0b2ec8c9
SR
1596 * \brief Renew a lease in the pool.
1597 *
1598 * The hard_lifetime_end_time of the lease should be set to
1599 * the current expiration time.
1600 * The soft_lifetime_end_time of the lease should be set to
1601 * the desired expiration time.
1602 *
1603 * This routine will compare the two and call the correct
1604 * heap routine to move the lease. If the lease is active
1605 * and the new expiration time is greater (the normal case)
1606 * then we call isc_heap_decreased() as a larger time is a
1607 * lower priority. If the new expiration time is less then
1608 * we call isc_heap_increased().
1609 *
1610 * If the lease is abandoned then it will be on the active list
1611 * and we will always call isc_heap_increased() as the previous
1612 * expiration would have been all 1s (as close as we can get
1613 * to infinite).
1d9774ab 1614 *
0b2ec8c9
SR
1615 * If the lease is moving to active we call that routine
1616 * which will move it from the inactive list to the active list.
1d9774ab 1617 *
01fa619f
SR
1618 * \param pool = a pool the lease belongs to
1619 * \param lease = the lease to be renewed
0b2ec8c9
SR
1620 *
1621 * \return result of the renew operation (ISC_R_SUCCESS if successful,
1622 ISC_R_NOMEMORY when run out of memory)
1d9774ab
FD
1623 */
1624isc_result_t
1d17db44 1625renew_lease6(struct ipv6_pool *pool, struct iasubopt *lease) {
0b2ec8c9
SR
1626 time_t old_end_time = lease->hard_lifetime_end_time;
1627 lease->hard_lifetime_end_time = lease->soft_lifetime_end_time;
1628 lease->soft_lifetime_end_time = 0;
1629
1d17db44 1630 if (lease->state == FTS_ACTIVE) {
0b2ec8c9
SR
1631 if (old_end_time <= lease->hard_lifetime_end_time) {
1632 isc_heap_decreased(pool->active_timeouts,
abacf8ad 1633 lease->active_index);
0b2ec8c9
SR
1634 } else {
1635 isc_heap_increased(pool->active_timeouts,
abacf8ad 1636 lease->active_index);
0b2ec8c9 1637 }
1d9774ab 1638 return ISC_R_SUCCESS;
c8b189f1
SR
1639 } else if (lease->state == FTS_ABANDONED) {
1640 char tmp_addr[INET6_ADDRSTRLEN];
1641 lease->state = FTS_ACTIVE;
abacf8ad 1642 isc_heap_increased(pool->active_timeouts, lease->active_index);
c8b189f1
SR
1643 log_info("Reclaiming previously abandoned address %s",
1644 inet_ntop(AF_INET6, &(lease->addr), tmp_addr,
1645 sizeof(tmp_addr)));
fb98e02e
TM
1646
1647 pool->num_abandoned--;
1648 if (pool->ipv6_pond)
1649 pool->ipv6_pond->num_abandoned--;
1650
c8b189f1 1651 return ISC_R_SUCCESS;
1d9774ab 1652 } else {
1d17db44 1653 return move_lease_to_active(pool, lease);
1d9774ab
FD
1654 }
1655}
1656
1657/*
1658 * Put the lease on our inactive pool, with the specified state.
1659 */
1660static isc_result_t
1d17db44 1661move_lease_to_inactive(struct ipv6_pool *pool, struct iasubopt *lease,
1d9774ab
FD
1662 binding_state_t state) {
1663 isc_result_t insert_result;
1d9774ab 1664
1d17db44 1665 insert_result = isc_heap_insert(pool->inactive_timeouts, lease);
1d9774ab 1666 if (insert_result == ISC_R_SUCCESS) {
a7341359
SR
1667 /*
1668 * Handle expire and release statements
1669 * To get here we must be active and have done a commit so
1670 * we should run the proper statements if they exist, though
1671 * that will change when we remove the inactive heap.
1672 * In addition we get rid of the references for both as we
1673 * can only do one (expire or release) on a lease
1674 */
1675 if (lease->on_star.on_expiry != NULL) {
1676 if (state == FTS_EXPIRED) {
1677 execute_statements(NULL, NULL, NULL,
1678 NULL, NULL, NULL,
1679 &lease->scope,
1680 lease->on_star.on_expiry,
1681 &lease->on_star);
1682 }
1683 executable_statement_dereference
1684 (&lease->on_star.on_expiry, MDL);
1685 }
1686
1687 if (lease->on_star.on_release != NULL) {
1688 if (state == FTS_RELEASED) {
1689 execute_statements(NULL, NULL, NULL,
1690 NULL, NULL, NULL,
1691 &lease->scope,
1692 lease->on_star.on_release,
1693 &lease->on_star);
1694 }
1695 executable_statement_dereference
1696 (&lease->on_star.on_release, MDL);
1697 }
1698
98bf1607 1699#if defined (NSUPDATE)
1d9774ab 1700 /* Process events upon expiration. */
9322442f 1701 if (pool->pool_type != D6O_IA_PD) {
d13db163 1702 (void) ddns_removals(NULL, lease, NULL, ISC_FALSE);
9322442f 1703 }
98bf1607 1704#endif
1d9774ab
FD
1705
1706 /* Binding scopes are no longer valid after expiry or
1707 * release.
1708 */
1d17db44
FD
1709 if (lease->scope != NULL) {
1710 binding_scope_dereference(&lease->scope, MDL);
1d9774ab
FD
1711 }
1712
1d17db44
FD
1713 iasubopt_hash_delete(pool->leases,
1714 &lease->addr, sizeof(lease->addr), MDL);
abacf8ad 1715 isc_heap_delete(pool->active_timeouts, lease->active_index);
1d17db44 1716 lease->state = state;
1d9774ab
FD
1717 pool->num_active--;
1718 pool->num_inactive++;
250f7134
SR
1719 if (pool->ipv6_pond)
1720 pool->ipv6_pond->num_active--;
fb98e02e
TM
1721
1722 if (lease->state == FTS_ABANDONED) {
1723 pool->num_abandoned--;
1724 if (pool->ipv6_pond)
1725 pool->ipv6_pond->num_abandoned--;
1726 }
1d9774ab
FD
1727 }
1728 return insert_result;
1729}
1730
1731/*
1732 * Expire the oldest lease if it's lifetime_end_time is
1733 * older than the given time.
98bd7ca0 1734 *
1d17db44 1735 * - leasep must be a pointer to a (struct iasubopt *) pointer previously
1d9774ab
FD
1736 * initialized to NULL
1737 *
1d17db44 1738 * On return leasep has a reference to the removed entry. It is left
1d9774ab
FD
1739 * pointing to NULL if the oldest lease has not expired.
1740 */
1741isc_result_t
1d17db44
FD
1742expire_lease6(struct iasubopt **leasep, struct ipv6_pool *pool, time_t now) {
1743 struct iasubopt *tmp;
1d9774ab
FD
1744 isc_result_t result;
1745
1d17db44 1746 if (leasep == NULL) {
1d9774ab 1747 log_error("%s(%d): NULL pointer reference", MDL);
98bf1607 1748 return DHCP_R_INVALIDARG;
1d9774ab 1749 }
1d17db44 1750 if (*leasep != NULL) {
1d9774ab 1751 log_error("%s(%d): non-NULL pointer", MDL);
98bf1607 1752 return DHCP_R_INVALIDARG;
1d9774ab
FD
1753 }
1754
1755 if (pool->num_active > 0) {
1d17db44
FD
1756 tmp = (struct iasubopt *)
1757 isc_heap_element(pool->active_timeouts, 1);
5d89d60f 1758 if (now > tmp->hard_lifetime_end_time) {
1d17db44
FD
1759 result = move_lease_to_inactive(pool, tmp,
1760 FTS_EXPIRED);
1d9774ab 1761 if (result == ISC_R_SUCCESS) {
1d17db44 1762 iasubopt_reference(leasep, tmp, MDL);
1d9774ab
FD
1763 }
1764 return result;
1765 }
1766 }
1767 return ISC_R_SUCCESS;
1768}
1769
1770
1771/*
1772 * For a declined lease, leave it on the "active" pool, but mark
1773 * it as declined. Give it an infinite (well, really long) life.
1774 */
1775isc_result_t
1d17db44 1776decline_lease6(struct ipv6_pool *pool, struct iasubopt *lease) {
1d9774ab
FD
1777 isc_result_t result;
1778
c8b189f1
SR
1779 if ((lease->state != FTS_ACTIVE) &&
1780 (lease->state != FTS_ABANDONED)) {
1d17db44 1781 result = move_lease_to_active(pool, lease);
1d9774ab
FD
1782 if (result != ISC_R_SUCCESS) {
1783 return result;
1784 }
1785 }
1d17db44 1786 lease->state = FTS_ABANDONED;
fb98e02e
TM
1787
1788 pool->num_abandoned++;
1789 if (pool->ipv6_pond)
1790 pool->ipv6_pond->num_abandoned++;
1791
1d17db44 1792 lease->hard_lifetime_end_time = MAX_TIME;
abacf8ad 1793 isc_heap_decreased(pool->active_timeouts, lease->active_index);
1d9774ab
FD
1794 return ISC_R_SUCCESS;
1795}
1796
1797/*
1798 * Put the returned lease on our inactive pool.
1799 */
1800isc_result_t
1d17db44
FD
1801release_lease6(struct ipv6_pool *pool, struct iasubopt *lease) {
1802 if (lease->state == FTS_ACTIVE) {
1803 return move_lease_to_inactive(pool, lease, FTS_RELEASED);
1d9774ab
FD
1804 } else {
1805 return ISC_R_SUCCESS;
1806 }
1807}
1808
1809/*
1810 * Create a prefix by hashing the input, and using that for
1811 * the part subject to allocation.
1812 */
a1a15031 1813void
5d89d60f 1814build_prefix6(struct in6_addr *pref,
1d9774ab
FD
1815 const struct in6_addr *net_start_pref,
1816 int pool_bits, int pref_bits,
1817 const struct data_string *input) {
98bf1607 1818 isc_md5_t ctx;
1d9774ab
FD
1819 int net_bytes;
1820 int i;
1821 char *str;
1822 const char *net_str;
1823
1824 /*
1825 * Use MD5 to get a nice 128 bit hash of the input.
1826 * Yes, we know MD5 isn't cryptographically sound.
1827 * No, we don't care.
1828 */
98bf1607
SR
1829 isc_md5_init(&ctx);
1830 isc_md5_update(&ctx, input->data, input->len);
1831 isc_md5_final(&ctx, (unsigned char *)pref);
1d9774ab
FD
1832
1833 /*
1834 * Copy the network bits over.
1835 */
1836 str = (char *)pref;
1837 net_str = (const char *)net_start_pref;
1838 net_bytes = pool_bits / 8;
783259b1 1839 for (i = 0; i < net_bytes; i++) {
1d9774ab
FD
1840 str[i] = net_str[i];
1841 }
80c9fdb0 1842 i = net_bytes;
1d9774ab
FD
1843 switch (pool_bits % 8) {
1844 case 1: str[i] = (str[i] & 0x7F) | (net_str[i] & 0x80); break;
1845 case 2: str[i] = (str[i] & 0x3F) | (net_str[i] & 0xC0); break;
1846 case 3: str[i] = (str[i] & 0x1F) | (net_str[i] & 0xE0); break;
1847 case 4: str[i] = (str[i] & 0x0F) | (net_str[i] & 0xF0); break;
1848 case 5: str[i] = (str[i] & 0x07) | (net_str[i] & 0xF8); break;
1849 case 6: str[i] = (str[i] & 0x03) | (net_str[i] & 0xFC); break;
1850 case 7: str[i] = (str[i] & 0x01) | (net_str[i] & 0xFE); break;
1851 }
1852 /*
1853 * Zero the remaining bits.
1854 */
1855 net_bytes = pref_bits / 8;
1856 for (i=net_bytes+1; i<16; i++) {
1857 str[i] = 0;
1858 }
80c9fdb0 1859 i = net_bytes;
1d9774ab 1860 switch (pref_bits % 8) {
80c9fdb0
FD
1861 case 0: str[i] &= 0; break;
1862 case 1: str[i] &= 0x80; break;
1863 case 2: str[i] &= 0xC0; break;
1864 case 3: str[i] &= 0xE0; break;
1865 case 4: str[i] &= 0xF0; break;
1866 case 5: str[i] &= 0xF8; break;
1867 case 6: str[i] &= 0xFC; break;
1868 case 7: str[i] &= 0xFE; break;
1d9774ab
FD
1869 }
1870}
1871
1872/*
1873 * Create a lease for the given prefix and client duid.
1874 *
01fa619f 1875 * - pool must be a pointer to a (struct ipv6_pool *) pointer previously
98bd7ca0
DH
1876 * initialized to NULL
1877 *
1878 * Right now we simply hash the DUID, and if we get a collision, we hash
1d9774ab 1879 * again until we find a free prefix. We try this a fixed number of times,
98bd7ca0
DH
1880 * to avoid getting stuck in a loop (this is important on small pools
1881 * where we can run out of space).
1882 *
1883 * We return the number of attempts that it took to find an available
1d9774ab 1884 * prefix. This tells callers when a pool is are filling up, as
98bd7ca0
DH
1885 * well as an indication of how full the pool is; statistically the
1886 * more full a pool is the more attempts must be made before finding
1d9774ab 1887 * a free prefix. Realistically this will only happen in very full
98bd7ca0
DH
1888 * pools.
1889 *
1890 * We probably want different algorithms depending on the network size, in
1891 * the long term.
1892 */
1893isc_result_t
1d17db44 1894create_prefix6(struct ipv6_pool *pool, struct iasubopt **pref,
5d89d60f
FD
1895 unsigned int *attempts,
1896 const struct data_string *uid,
1897 time_t soft_lifetime_end_time) {
98bd7ca0
DH
1898 struct data_string ds;
1899 struct in6_addr tmp;
1d17db44 1900 struct iasubopt *test_iapref;
98bd7ca0 1901 struct data_string new_ds;
1d17db44 1902 struct iasubopt *iapref;
d9b43370 1903 isc_result_t result;
98bd7ca0
DH
1904
1905 /*
1906 * Use the UID as our initial seed for the hash
1907 */
1908 memset(&ds, 0, sizeof(ds));
1909 data_string_copy(&ds, (struct data_string *)uid, MDL);
1910
1911 *attempts = 0;
1912 for (;;) {
1913 /*
1914 * Give up at some point.
1915 */
1d9774ab 1916 if (++(*attempts) > 10) {
98bd7ca0
DH
1917 data_string_forget(&ds, MDL);
1918 return ISC_R_NORESOURCES;
1919 }
1920
1921 /*
5d89d60f 1922 * Build a prefix
0674055a 1923 */
9322442f
FD
1924 build_prefix6(&tmp, &pool->start_addr,
1925 pool->bits, pool->units, &ds);
0674055a 1926
98bd7ca0 1927 /*
1d9774ab 1928 * If this prefix is not in use, we're happy with it
98bd7ca0 1929 */
1d9774ab 1930 test_iapref = NULL;
1d17db44
FD
1931 if (iasubopt_hash_lookup(&test_iapref, pool->leases,
1932 &tmp, sizeof(tmp), MDL) == 0) {
98bd7ca0
DH
1933 break;
1934 }
1d17db44 1935 iasubopt_dereference(&test_iapref, MDL);
98bd7ca0
DH
1936
1937 /*
1d9774ab 1938 * Otherwise, we create a new input, adding the prefix
98bd7ca0
DH
1939 */
1940 memset(&new_ds, 0, sizeof(new_ds));
9322442f
FD
1941 new_ds.len = ds.len + sizeof(tmp);
1942 if (!buffer_allocate(&new_ds.buffer, new_ds.len, MDL)) {
1943 data_string_forget(&ds, MDL);
1944 return ISC_R_NOMEMORY;
4f8a4a88 1945 }
9322442f
FD
1946 new_ds.data = new_ds.buffer->data;
1947 memcpy(new_ds.buffer->data, ds.data, ds.len);
6c7e6157 1948 memcpy(&new_ds.buffer->data[0] + ds.len, &tmp, sizeof(tmp));
9322442f
FD
1949 data_string_forget(&ds, MDL);
1950 data_string_copy(&ds, &new_ds, MDL);
1951 data_string_forget(&new_ds, MDL);
d9b43370 1952 }
98bd7ca0 1953
9322442f 1954 data_string_forget(&ds, MDL);
98bd7ca0 1955
9322442f
FD
1956 /*
1957 * We're happy with the prefix, create an IAPREFIX
1958 * to hold it.
1959 */
1960 iapref = NULL;
1d17db44 1961 result = iasubopt_allocate(&iapref, MDL);
9322442f
FD
1962 if (result != ISC_R_SUCCESS) {
1963 return result;
98bd7ca0 1964 }
9322442f
FD
1965 iapref->plen = (u_int8_t)pool->units;
1966 memcpy(&iapref->addr, &tmp, sizeof(iapref->addr));
98bd7ca0 1967
9322442f
FD
1968 /*
1969 * Add the prefix to the pool (note state is free, not active?!).
1970 */
1971 result = add_lease6(pool, iapref, soft_lifetime_end_time);
1972 if (result == ISC_R_SUCCESS) {
1d17db44 1973 iasubopt_reference(pref, iapref, MDL);
98bd7ca0 1974 }
1d17db44 1975 iasubopt_dereference(&iapref, MDL);
9322442f 1976 return result;
98bd7ca0
DH
1977}
1978
98bd7ca0 1979/*
9322442f 1980 * Determine if a prefix is present in a pool or not.
98bd7ca0 1981 */
9322442f
FD
1982isc_boolean_t
1983prefix6_exists(const struct ipv6_pool *pool,
1984 const struct in6_addr *pref, u_int8_t plen) {
1d17db44 1985 struct iasubopt *test_iapref;
9322442f
FD
1986
1987 if ((int)plen != pool->units)
1988 return ISC_FALSE;
1989
1990 test_iapref = NULL;
1d17db44
FD
1991 if (iasubopt_hash_lookup(&test_iapref, pool->leases,
1992 (void *)pref, sizeof(*pref), MDL)) {
1993 iasubopt_dereference(&test_iapref, MDL);
9322442f 1994 return ISC_TRUE;
d9b43370 1995 } else {
9322442f 1996 return ISC_FALSE;
d9b43370 1997 }
98bd7ca0
DH
1998}
1999
2000/*
9322442f 2001 * Mark an IPv6 address/prefix as unavailable from a pool.
98bd7ca0
DH
2002 *
2003 * This is used for host entries and the addresses of the server itself.
2004 */
2005isc_result_t
9322442f 2006mark_lease_unavailable(struct ipv6_pool *pool, const struct in6_addr *addr) {
1d17db44 2007 struct iasubopt *dummy_iasubopt;
98bd7ca0
DH
2008 isc_result_t result;
2009
1d17db44
FD
2010 dummy_iasubopt = NULL;
2011 result = iasubopt_allocate(&dummy_iasubopt, MDL);
98bd7ca0 2012 if (result == ISC_R_SUCCESS) {
1d17db44
FD
2013 dummy_iasubopt->addr = *addr;
2014 iasubopt_hash_add(pool->leases, &dummy_iasubopt->addr,
2015 sizeof(*addr), dummy_iasubopt, MDL);
98bd7ca0
DH
2016 }
2017 return result;
2018}
2019
2020/*
2021 * Add a pool.
2022 */
2023isc_result_t
2024add_ipv6_pool(struct ipv6_pool *pool) {
2025 struct ipv6_pool **new_pools;
2026
2027 new_pools = dmalloc(sizeof(struct ipv6_pool *) * (num_pools+1), MDL);
2028 if (new_pools == NULL) {
2029 return ISC_R_NOMEMORY;
2030 }
2031
2032 if (num_pools > 0) {
2033 memcpy(new_pools, pools,
2034 sizeof(struct ipv6_pool *) * num_pools);
2035 dfree(pools, MDL);
2036 }
2037 pools = new_pools;
2038
2039 pools[num_pools] = NULL;
2040 ipv6_pool_reference(&pools[num_pools], pool, MDL);
2041 num_pools++;
2042 return ISC_R_SUCCESS;
2043}
2044
d9b43370
SK
2045static void
2046cleanup_old_expired(struct ipv6_pool *pool) {
1d17db44 2047 struct iasubopt *tmp;
9322442f
FD
2048 struct ia_xx *ia;
2049 struct ia_xx *ia_active;
4f8a4a88 2050 unsigned char *tmpd;
5d89d60f 2051 time_t timeout;
d9b43370
SK
2052
2053 while (pool->num_inactive > 0) {
1d17db44
FD
2054 tmp = (struct iasubopt *)
2055 isc_heap_element(pool->inactive_timeouts, 1);
5d89d60f
FD
2056 if (tmp->hard_lifetime_end_time != 0) {
2057 timeout = tmp->hard_lifetime_end_time;
2058 timeout += EXPIRED_IPV6_CLEANUP_TIME;
2059 } else {
2060 timeout = tmp->soft_lifetime_end_time;
2061 }
2062 if (cur_time < timeout) {
d9b43370
SK
2063 break;
2064 }
2065
abacf8ad 2066 isc_heap_delete(pool->inactive_timeouts, tmp->inactive_index);
d9b43370
SK
2067 pool->num_inactive--;
2068
9322442f 2069 if (tmp->ia != NULL) {
4f8a4a88 2070 /*
9322442f
FD
2071 * Check to see if this IA is in an active list,
2072 * but has no remaining resources. If so, remove it
4f8a4a88
SK
2073 * from the active list.
2074 */
1d9774ab 2075 ia = NULL;
9322442f 2076 ia_reference(&ia, tmp->ia, MDL);
1d17db44 2077 ia_remove_iasubopt(ia, tmp, MDL);
1d9774ab
FD
2078 ia_active = NULL;
2079 tmpd = (unsigned char *)ia->iaid_duid.data;
2080 if ((ia->ia_type == D6O_IA_NA) &&
1d17db44 2081 (ia->num_iasubopt <= 0) &&
9322442f
FD
2082 (ia_hash_lookup(&ia_active, ia_na_active, tmpd,
2083 ia->iaid_duid.len, MDL) == 0) &&
1d9774ab 2084 (ia_active == ia)) {
9322442f
FD
2085 ia_hash_delete(ia_na_active, tmpd,
2086 ia->iaid_duid.len, MDL);
1d9774ab
FD
2087 }
2088 if ((ia->ia_type == D6O_IA_TA) &&
1d17db44 2089 (ia->num_iasubopt <= 0) &&
9322442f
FD
2090 (ia_hash_lookup(&ia_active, ia_ta_active, tmpd,
2091 ia->iaid_duid.len, MDL) == 0) &&
2092 (ia_active == ia)) {
2093 ia_hash_delete(ia_ta_active, tmpd,
2094 ia->iaid_duid.len, MDL);
2095 }
2096 if ((ia->ia_type == D6O_IA_PD) &&
1d17db44 2097 (ia->num_iasubopt <= 0) &&
9322442f
FD
2098 (ia_hash_lookup(&ia_active, ia_pd_active, tmpd,
2099 ia->iaid_duid.len, MDL) == 0) &&
1d9774ab 2100 (ia_active == ia)) {
9322442f
FD
2101 ia_hash_delete(ia_pd_active, tmpd,
2102 ia->iaid_duid.len, MDL);
07b9a351 2103 }
9322442f 2104 ia_dereference(&ia, MDL);
d9b43370 2105 }
1d17db44 2106 iasubopt_dereference(&tmp, MDL);
d9b43370
SK
2107 }
2108}
2109
2110static void
2111lease_timeout_support(void *vpool) {
2112 struct ipv6_pool *pool;
1d17db44 2113 struct iasubopt *lease;
d9b43370
SK
2114
2115 pool = (struct ipv6_pool *)vpool;
2116 for (;;) {
2117 /*
2118 * Get the next lease scheduled to expire.
2119 *
2120 * Note that if there are no leases in the pool,
2121 * expire_lease6() will return ISC_R_SUCCESS with
2122 * a NULL lease.
0ef9a46e
SR
2123 *
2124 * expire_lease6() will call move_lease_to_inactive() which
2125 * calls ddns_removals() do we want that on the standard
2126 * expiration timer or a special 'depref' timer? Original
2127 * query from DH, moved here by SAR.
d9b43370 2128 */
1d17db44
FD
2129 lease = NULL;
2130 if (expire_lease6(&lease, pool, cur_time) != ISC_R_SUCCESS) {
d9b43370
SK
2131 break;
2132 }
1d17db44 2133 if (lease == NULL) {
d9b43370
SK
2134 break;
2135 }
2136
1d17db44 2137 write_ia(lease->ia);
d9b43370 2138
1d17db44 2139 iasubopt_dereference(&lease, MDL);
d9b43370
SK
2140 }
2141
cbbd2714
SR
2142 /*
2143 * If appropriate commit and rotate the lease file
2144 * As commit_leases_timed() checks to see if we've done any writes
2145 * we don't bother tracking if this function called write _ia
2146 */
2147 (void) commit_leases_timed();
2148
d9b43370
SK
2149 /*
2150 * Do some cleanup of our expired leases.
2151 */
2152 cleanup_old_expired(pool);
2153
2154 /*
2155 * Schedule next round of expirations.
2156 */
2157 schedule_lease_timeout(pool);
2158}
2159
98bd7ca0 2160/*
d9b43370
SK
2161 * For a given pool, add a timer that will remove the next
2162 * lease to expire.
98bd7ca0
DH
2163 */
2164void
d9b43370 2165schedule_lease_timeout(struct ipv6_pool *pool) {
1d17db44 2166 struct iasubopt *tmp;
d9b43370
SK
2167 time_t timeout;
2168 time_t next_timeout;
be62cf06 2169 struct timeval tv;
d9b43370
SK
2170
2171 next_timeout = MAX_TIME;
2172
2173 if (pool->num_active > 0) {
1d17db44
FD
2174 tmp = (struct iasubopt *)
2175 isc_heap_element(pool->active_timeouts, 1);
5d89d60f
FD
2176 if (tmp->hard_lifetime_end_time < next_timeout) {
2177 next_timeout = tmp->hard_lifetime_end_time + 1;
d9b43370
SK
2178 }
2179 }
2180
2181 if (pool->num_inactive > 0) {
1d17db44
FD
2182 tmp = (struct iasubopt *)
2183 isc_heap_element(pool->inactive_timeouts, 1);
5d89d60f
FD
2184 if (tmp->hard_lifetime_end_time != 0) {
2185 timeout = tmp->hard_lifetime_end_time;
2186 timeout += EXPIRED_IPV6_CLEANUP_TIME;
2187 } else {
2188 timeout = tmp->soft_lifetime_end_time + 1;
2189 }
d9b43370
SK
2190 if (timeout < next_timeout) {
2191 next_timeout = timeout;
2192 }
2193 }
2194
2195 if (next_timeout < MAX_TIME) {
be62cf06
FD
2196 tv.tv_sec = next_timeout;
2197 tv.tv_usec = 0;
2198 add_timeout(&tv, lease_timeout_support, pool,
d9b43370
SK
2199 (tvref_t)ipv6_pool_reference,
2200 (tvunref_t)ipv6_pool_dereference);
2201 }
2202}
2203
2204/*
2205 * Schedule timeouts across all pools.
2206 */
2207void
2208schedule_all_ipv6_lease_timeouts(void) {
98bd7ca0 2209 int i;
98bd7ca0
DH
2210
2211 for (i=0; i<num_pools; i++) {
d9b43370 2212 schedule_lease_timeout(pools[i]);
98bd7ca0
DH
2213 }
2214}
2215
2216/*
2217 * Given an address and the length of the network mask, return
2218 * only the network portion.
2219 *
2220 * Examples:
2221 *
2222 * "fe80::216:6fff:fe49:7d9b", length 64 = "fe80::"
2223 * "2001:888:1936:2:216:6fff:fe49:7d9b", length 48 = "2001:888:1936::"
2224 */
2225static void
2226ipv6_network_portion(struct in6_addr *result,
2227 const struct in6_addr *addr, int bits) {
2228 unsigned char *addrp;
2229 int mask_bits;
2230 int bytes;
2231 int extra_bits;
2232 int i;
2233
2234 static const unsigned char bitmasks[] = {
2235 0x00, 0xFE, 0xFC, 0xF8,
2236 0xF0, 0xE0, 0xC0, 0x80,
2237 };
2238
2239 /*
2240 * Sanity check our bits. ;)
2241 */
2242 if ((bits < 0) || (bits > 128)) {
2243 log_fatal("ipv6_network_portion: bits %d not between 0 and 128",
2244 bits);
2245 }
2246
2247 /*
2248 * Copy our address portion.
2249 */
2250 *result = *addr;
2251 addrp = ((unsigned char *)result) + 15;
2252
2253 /*
2254 * Zero out masked portion.
2255 */
2256 mask_bits = 128 - bits;
2257 bytes = mask_bits / 8;
2258 extra_bits = mask_bits % 8;
2259
2260 for (i=0; i<bytes; i++) {
2261 *addrp = 0;
2262 addrp--;
2263 }
2264 if (extra_bits) {
2265 *addrp &= bitmasks[extra_bits];
2266 }
2267}
2268
2269/*
9322442f 2270 * Determine if the given address/prefix is in the pool.
98bd7ca0
DH
2271 */
2272isc_boolean_t
9322442f 2273ipv6_in_pool(const struct in6_addr *addr, const struct ipv6_pool *pool) {
98bd7ca0 2274 struct in6_addr tmp;
9322442f
FD
2275
2276 ipv6_network_portion(&tmp, addr, pool->bits);
98bd7ca0
DH
2277 if (memcmp(&tmp, &pool->start_addr, sizeof(tmp)) == 0) {
2278 return ISC_TRUE;
2279 } else {
2280 return ISC_FALSE;
2281 }
2282}
2283
2284/*
2285 * Find the pool that contains the given address.
2286 *
2287 * - pool must be a pointer to a (struct ipv6_pool *) pointer previously
2288 * initialized to NULL
2289 */
2290isc_result_t
9322442f 2291find_ipv6_pool(struct ipv6_pool **pool, u_int16_t type,
80c9fdb0 2292 const struct in6_addr *addr) {
98bd7ca0
DH
2293 int i;
2294
2295 if (pool == NULL) {
2296 log_error("%s(%d): NULL pointer reference", MDL);
98bf1607 2297 return DHCP_R_INVALIDARG;
98bd7ca0
DH
2298 }
2299 if (*pool != NULL) {
2300 log_error("%s(%d): non-NULL pointer", MDL);
98bf1607 2301 return DHCP_R_INVALIDARG;
98bd7ca0
DH
2302 }
2303
2304 for (i=0; i<num_pools; i++) {
9322442f 2305 if (pools[i]->pool_type != type)
80c9fdb0 2306 continue;
9322442f 2307 if (ipv6_in_pool(addr, pools[i])) {
98bd7ca0
DH
2308 ipv6_pool_reference(pool, pools[i], MDL);
2309 return ISC_R_SUCCESS;
2310 }
2311 }
2312 return ISC_R_NOTFOUND;
2313}
2314
2315/*
2316 * Helper function for the various functions that act across all
2317 * pools.
2318 */
2319static isc_result_t
9322442f 2320change_leases(struct ia_xx *ia,
1d17db44
FD
2321 isc_result_t (*change_func)(struct ipv6_pool *,
2322 struct iasubopt *)) {
98bd7ca0
DH
2323 isc_result_t retval;
2324 isc_result_t renew_retval;
2325 struct ipv6_pool *pool;
2326 struct in6_addr *addr;
9322442f 2327 int i;
98bd7ca0
DH
2328
2329 retval = ISC_R_SUCCESS;
1d17db44 2330 for (i=0; i<ia->num_iasubopt; i++) {
98bd7ca0 2331 pool = NULL;
1d17db44 2332 addr = &ia->iasubopt[i]->addr;
9322442f
FD
2333 if (find_ipv6_pool(&pool, ia->ia_type,
2334 addr) == ISC_R_SUCCESS) {
1d17db44 2335 renew_retval = change_func(pool, ia->iasubopt[i]);
98bd7ca0
DH
2336 if (renew_retval != ISC_R_SUCCESS) {
2337 retval = renew_retval;
2338 }
2339 }
2340 /* XXXsk: should we warn if we don't find a pool? */
2341 }
2342 return retval;
2343}
2344
2345/*
1d9774ab 2346 * Renew all leases in an IA from all pools.
98bd7ca0 2347 *
0b2ec8c9
SR
2348 * The new lifetime should be in the soft_lifetime_end_time
2349 * and will be moved to hard_lifetime_end_time by renew_lease6.
98bd7ca0
DH
2350 */
2351isc_result_t
9322442f 2352renew_leases(struct ia_xx *ia) {
1d9774ab
FD
2353 return change_leases(ia, renew_lease6);
2354}
2355
2356/*
2357 * Release all leases in an IA from all pools.
2358 */
2359isc_result_t
9322442f 2360release_leases(struct ia_xx *ia) {
1d9774ab
FD
2361 return change_leases(ia, release_lease6);
2362}
2363
2364/*
2365 * Decline all leases in an IA from all pools.
2366 */
2367isc_result_t
9322442f 2368decline_leases(struct ia_xx *ia) {
1d9774ab
FD
2369 return change_leases(ia, decline_lease6);
2370}
2371
28868515 2372#ifdef DHCPv6
98bd7ca0
DH
2373/*
2374 * Helper function to output leases.
2375 */
2376static int write_error;
2377
2378static isc_result_t
1d9774ab 2379write_ia_leases(const void *name, unsigned len, void *value) {
9322442f 2380 struct ia_xx *ia = (struct ia_xx *)value;
1d9774ab
FD
2381
2382 if (!write_error) {
2383 if (!write_ia(ia)) {
2384 write_error = 1;
2385 }
2386 }
2387 return ISC_R_SUCCESS;
2388}
2389
98bd7ca0
DH
2390/*
2391 * Write all DHCPv6 information.
2392 */
2393int
2394write_leases6(void) {
f88446f1
SR
2395 int nas, tas, pds;
2396
98bd7ca0
DH
2397 write_error = 0;
2398 write_server_duid();
f88446f1 2399 nas = ia_hash_foreach(ia_na_active, write_ia_leases);
1d9774ab
FD
2400 if (write_error) {
2401 return 0;
2402 }
f88446f1 2403 tas = ia_hash_foreach(ia_ta_active, write_ia_leases);
1d9774ab
FD
2404 if (write_error) {
2405 return 0;
2406 }
f88446f1 2407 pds = ia_hash_foreach(ia_pd_active, write_ia_leases);
98bd7ca0
DH
2408 if (write_error) {
2409 return 0;
2410 }
f88446f1
SR
2411
2412 log_info("Wrote %d NA, %d TA, %d PD leases to lease file.",
2413 nas, tas, pds);
98bd7ca0
DH
2414 return 1;
2415}
fe5b0fdd 2416#endif /* DHCPv6 */
98bd7ca0
DH
2417
2418static isc_result_t
2419mark_hosts_unavailable_support(const void *name, unsigned len, void *value) {
2420 struct host_decl *h;
2421 struct data_string fixed_addr;
2422 struct in6_addr addr;
2423 struct ipv6_pool *p;
2424
2425 h = (struct host_decl *)value;
2426
2427 /*
2428 * If the host has no address, we don't need to mark anything.
2429 */
2430 if (h->fixed_addr == NULL) {
2431 return ISC_R_SUCCESS;
2432 }
2433
2434 /*
2435 * Evaluate the fixed address.
2436 */
2437 memset(&fixed_addr, 0, sizeof(fixed_addr));
2438 if (!evaluate_option_cache(&fixed_addr, NULL, NULL, NULL, NULL, NULL,
2439 &global_scope, h->fixed_addr, MDL)) {
2440 log_error("mark_hosts_unavailable: "
2441 "error evaluating host address.");
2442 return ISC_R_SUCCESS;
2443 }
2444 if (fixed_addr.len != 16) {
2445 log_error("mark_hosts_unavailable: "
2446 "host address is not 128 bits.");
2447 return ISC_R_SUCCESS;
2448 }
2449 memcpy(&addr, fixed_addr.data, 16);
2450 data_string_forget(&fixed_addr, MDL);
2451
2452 /*
2453 * Find the pool holding this host, and mark the address.
2454 * (I suppose it is arguably valid to have a host that does not
2455 * sit in any pool.)
2456 */
2457 p = NULL;
9322442f
FD
2458 if (find_ipv6_pool(&p, D6O_IA_NA, &addr) == ISC_R_SUCCESS) {
2459 mark_lease_unavailable(p, &addr);
80c9fdb0
FD
2460 ipv6_pool_dereference(&p, MDL);
2461 }
9322442f
FD
2462 if (find_ipv6_pool(&p, D6O_IA_TA, &addr) == ISC_R_SUCCESS) {
2463 mark_lease_unavailable(p, &addr);
98bd7ca0
DH
2464 ipv6_pool_dereference(&p, MDL);
2465 }
2466
2467 return ISC_R_SUCCESS;
2468}
2469
2470void
2471mark_hosts_unavailable(void) {
2472 hash_foreach(host_name_hash, mark_hosts_unavailable_support);
2473}
2474
80c9fdb0
FD
2475static isc_result_t
2476mark_phosts_unavailable_support(const void *name, unsigned len, void *value) {
2477 struct host_decl *h;
2478 struct iaddrcidrnetlist *l;
2479 struct in6_addr pref;
9322442f 2480 struct ipv6_pool *p;
80c9fdb0
FD
2481
2482 h = (struct host_decl *)value;
2483
2484 /*
2485 * If the host has no prefix, we don't need to mark anything.
2486 */
2487 if (h->fixed_prefix == NULL) {
2488 return ISC_R_SUCCESS;
2489 }
2490
2491 /*
2492 * Get the fixed prefixes.
2493 */
2494 for (l = h->fixed_prefix; l != NULL; l = l->next) {
2495 if (l->cidrnet.lo_addr.len != 16) {
2496 continue;
2497 }
2498 memcpy(&pref, l->cidrnet.lo_addr.iabuf, 16);
2499
2500 /*
2501 * Find the pool holding this host, and mark the prefix.
2502 * (I suppose it is arguably valid to have a host that does not
2503 * sit in any pool.)
2504 */
2505 p = NULL;
9322442f 2506 if (find_ipv6_pool(&p, D6O_IA_PD, &pref) != ISC_R_SUCCESS) {
80c9fdb0
FD
2507 continue;
2508 }
9322442f
FD
2509 if (l->cidrnet.bits != p->units) {
2510 ipv6_pool_dereference(&p, MDL);
80c9fdb0
FD
2511 continue;
2512 }
9322442f
FD
2513 mark_lease_unavailable(p, &pref);
2514 ipv6_pool_dereference(&p, MDL);
80c9fdb0
FD
2515 }
2516
2517 return ISC_R_SUCCESS;
2518}
2519
2520void
2521mark_phosts_unavailable(void) {
2522 hash_foreach(host_name_hash, mark_phosts_unavailable_support);
2523}
2524
98bd7ca0
DH
2525void
2526mark_interfaces_unavailable(void) {
2527 struct interface_info *ip;
2528 int i;
2529 struct ipv6_pool *p;
2530
2531 ip = interfaces;
2532 while (ip != NULL) {
2533 for (i=0; i<ip->v6address_count; i++) {
2534 p = NULL;
9322442f 2535 if (find_ipv6_pool(&p, D6O_IA_NA, &ip->v6addresses[i])
80c9fdb0 2536 == ISC_R_SUCCESS) {
9322442f
FD
2537 mark_lease_unavailable(p,
2538 &ip->v6addresses[i]);
80c9fdb0
FD
2539 ipv6_pool_dereference(&p, MDL);
2540 }
9322442f 2541 if (find_ipv6_pool(&p, D6O_IA_TA, &ip->v6addresses[i])
98bd7ca0 2542 == ISC_R_SUCCESS) {
9322442f
FD
2543 mark_lease_unavailable(p,
2544 &ip->v6addresses[i]);
98bd7ca0
DH
2545 ipv6_pool_dereference(&p, MDL);
2546 }
2547 }
2548 ip = ip->next;
2549 }
2550}
2551
01fa619f
SR
2552/*!
2553 * \brief Create a new IPv6 pond structure.
2554 *
2555 * Allocate space for a new ipv6_pond structure and return a reference
2556 * to it, includes setting the reference count to 1.
2557 *
2558 * \param pond = space for returning a referenced pointer to the pond.
2559 * This must point to a space that has been initialzied
2560 * to NULL by the caller.
2561 *
2562 * \return
2563 * ISC_R_SUCCESS = The pond was successfully created, pond points to it.
2564 * DHCP_R_INVALIDARG = One of the arugments was invalid, pond has not been
2565 * modified
2566 * ISC_R_NOMEMORY = The system wasn't able to allocate memory, pond has
2567 * not been modified.
2568 */
2569isc_result_t
2570ipv6_pond_allocate(struct ipv6_pond **pond, const char *file, int line) {
2571 struct ipv6_pond *tmp;
2572
2573 if (pond == NULL) {
2574 log_error("%s(%d): NULL pointer reference", file, line);
2575 return DHCP_R_INVALIDARG;
2576 }
2577 if (*pond != NULL) {
2578 log_error("%s(%d): non-NULL pointer", file, line);
2579 return DHCP_R_INVALIDARG;
2580 }
2581
2582 tmp = dmalloc(sizeof(*tmp), file, line);
2583 if (tmp == NULL) {
2584 return ISC_R_NOMEMORY;
2585 }
2586
2587 tmp->refcnt = 1;
2588
2589 *pond = tmp;
2590 return ISC_R_SUCCESS;
2591}
2592
2593/*!
2594 *
2595 * \brief reference an IPv6 pond structure.
2596 *
2597 * This function genreates a reference to an ipv6_pond structure
2598 * and increments the reference count on the structure.
2599 *
2600 * \param[out] pond = space for returning a referenced pointer to the pond.
2601 * This must point to a space that has been initialzied
2602 * to NULL by the caller.
2603 * \param[in] src = A pointer to the pond to reference. This must not be
2604 * NULL.
2605 *
2606 * \return
2607 * ISC_R_SUCCESS = The pond was successfully referenced, pond now points
2608 * to src.
2609 * DHCP_R_INVALIDARG = One of the arugments was invalid, pond has not been
2610 * modified.
2611 */
2612isc_result_t
2613ipv6_pond_reference(struct ipv6_pond **pond, struct ipv6_pond *src,
2614 const char *file, int line) {
2615 if (pond == NULL) {
2616 log_error("%s(%d): NULL pointer reference", file, line);
2617 return DHCP_R_INVALIDARG;
2618 }
2619 if (*pond != NULL) {
2620 log_error("%s(%d): non-NULL pointer", file, line);
2621 return DHCP_R_INVALIDARG;
2622 }
2623 if (src == NULL) {
2624 log_error("%s(%d): NULL pointer reference", file, line);
2625 return DHCP_R_INVALIDARG;
2626 }
2627 *pond = src;
2628 src->refcnt++;
2629 return ISC_R_SUCCESS;
2630}
2631
2632/*!
2633 *
2634 * \brief de-reference an IPv6 pond structure.
2635 *
2636 * This function decrements the reference count in an ipv6_pond structure.
2637 * If this was the last reference then the memory for the structure is
2638 * freed.
2639 *
2640 * \param[in] pond = A pointer to the pointer to the pond that should be
2641 * de-referenced. On success the pointer to the pond
2642 * is cleared. It must not be NULL and must not point
2643 * to NULL.
2644 *
2645 * \return
2646 * ISC_R_SUCCESS = The pond was successfully de-referenced, pond now points
2647 * to NULL
2648 * DHCP_R_INVALIDARG = One of the arugments was invalid, pond has not been
2649 * modified.
2650 */
2651
2652isc_result_t
2653ipv6_pond_dereference(struct ipv6_pond **pond, const char *file, int line) {
2654 struct ipv6_pond *tmp;
2655
2656 if ((pond == NULL) || (*pond == NULL)) {
2657 log_error("%s(%d): NULL pointer", file, line);
2658 return DHCP_R_INVALIDARG;
2659 }
2660
2661 tmp = *pond;
2662 *pond = NULL;
2663
2664 tmp->refcnt--;
2665 if (tmp->refcnt < 0) {
2666 log_error("%s(%d): negative refcnt", file, line);
2667 tmp->refcnt = 0;
2668 }
2669 if (tmp->refcnt == 0) {
2670 dfree(tmp, file, line);
2671 }
2672
2673 return ISC_R_SUCCESS;
2674}
2675
417b7b4a
TM
2676#ifdef EUI_64
2677/*
2678 * Enables/disables EUI-64 address assignment for a pond
2679 *
2680 * Excecutes statements down to the pond's scope and sets the pond's
2681 * use_eui_64 flag accordingly. In addition it iterates over the
2682 * pond's pools ensuring they are all /64. Anything else is deemed
2683 * invalid for EUI-64. It returns the number of invalid pools
2684 * detected. This is done post-parsing as use-eui-64 can be set
2685 * down to the pool scope and we can't reliably do it until the
2686 * entire configuration has been parsed.
2687 */
2688int
2689set_eui_64(struct ipv6_pond *pond) {
2690 int invalid_cnt = 0;
2691 struct option_state* options = NULL;
2692 struct option_cache *oc = NULL;
2693 option_state_allocate(&options, MDL);
2694 execute_statements_in_scope(NULL, NULL, NULL, NULL, NULL, options,
2695 &global_scope, pond->group, NULL, NULL);
2696
2697 pond->use_eui_64 =
2698 ((oc = lookup_option(&server_universe, options, SV_USE_EUI_64))
2699 &&
2700 (evaluate_boolean_option_cache (NULL, NULL, NULL, NULL,
2701 options, NULL, &global_scope,
2702 oc, MDL)));
2703 if (pond->use_eui_64) {
2704 // Check all pools are valid
2705 int i = 0;
2706 struct ipv6_pool* p;
2707 while((p = pond->ipv6_pools[i++]) != NULL) {
2708 if (p->bits != 64) {
2709 log_error("Pool %s/%d cannot use EUI-64,"
2710 " prefix must 64",
2711 pin6_addr(&p->start_addr), p->bits);
2712 invalid_cnt++;
2713 } else {
2714 log_debug("Pool: %s/%d - will use EUI-64",
2715 pin6_addr(&p->start_addr), p->bits);
2716 }
2717 }
2718 }
2719
2720 /* Don't need the options anymore. */
2721 option_state_dereference(&options, MDL);
2722 return (invalid_cnt);
2723}
2724#endif
2725
fb98e02e
TM
2726/*
2727 * Emits a log for each pond that has been flagged as being a "jumbo range"
2728 * A pond is considered a "jumbo range" when the total number of elements
2729 * exceeds the maximum value of POND_TRACK_MAX (currently maximum value
2730 * that can be stored by ipv6_pond.num_total). Since we disable threshold
2731 * logging for jumbo ranges, we need to report this to the user. This
2732 * function allows us to report jumbo ponds after config parsing, so the
2733 * logs can be seen both on the console (-T) and the log facility (i.e syslog).
2734 *
2735 * Note, threshold logging is done at the pond level, so we need emit a list
2736 * of the addresses ranges of the pools in the pond affected.
2737 */
2738void
2739report_jumbo_ranges() {
2740 struct shared_network* s;
2741 char log_buf[1084];
417b7b4a
TM
2742#ifdef EUI_64
2743 int invalid_cnt = 0;
2744#endif
fb98e02e
TM
2745
2746 /* Loop thru all the networks looking for jumbo range ponds */
2747 for (s = shared_networks; s; s = s -> next) {
2748 struct ipv6_pond* pond = s->ipv6_pond;
2749 while (pond) {
417b7b4a
TM
2750#ifdef EUI_64
2751 /* while we're here, set the pond's use_eui_64 flag */
2752 invalid_cnt += set_eui_64(pond);
2753#endif
fb98e02e
TM
2754 /* if its a jumbo and has pools(sanity check) */
2755 if (pond->jumbo_range == 1 && (pond->ipv6_pools)) {
2756 struct ipv6_pool* pool;
2757 char *bufptr = log_buf;
2758 size_t space_left = sizeof(log_buf) - 1;
2759 int i = 0;
2760 int used = 0;
2761
2762 /* Build list containing the start-address/CIDR
2763 * of each pool */
2764 *bufptr = '\0';
2765 while ((pool = pond->ipv6_pools[i++]) &&
2766 (space_left > (INET6_ADDRSTRLEN + 6))) {
2767 /* more than one so add a comma */
2768 if (i > 1) {
2769 *bufptr++ = ',';
2770 *bufptr++ = ' ';
2771 *bufptr = '\0';
2772 space_left -= 2;
2773 }
2774
2775 /* add the address */
2776 inet_ntop(AF_INET6, &pool->start_addr,
2777 bufptr, INET6_ADDRSTRLEN);
2778
2779 used = strlen(bufptr);
2780 bufptr += used;
2781 space_left -= used;
2782
2783 /* add the CIDR */
2784 sprintf (bufptr, "/%d",pool->bits);
2785 used = strlen(bufptr);
2786 bufptr += used;
2787 space_left -= used;
2788 *bufptr = '\0';
2789 }
2790
2791 log_info("Threshold logging disabled for shared"
2792 " subnet of ranges: %s", log_buf);
2793 }
2794 pond = pond->next;
2795 }
417b7b4a
TM
2796
2797 }
2798
2799#ifdef EUI_64
2800 if (invalid_cnt) {
2801 log_fatal ("%d pool(s) are invalid for EUI-64 use",
2802 invalid_cnt);
fb98e02e 2803 }
417b7b4a 2804#endif
fb98e02e
TM
2805}
2806
62a9eb91
TM
2807
2808/*
2809 * \brief Tests that 16-bit hardware type is less than 256
2810 *
2811 * XXX: DHCPv6 gives a 16-bit field for the htype. DHCPv4 gives an
2812 * 8-bit field. To change the semantics of the generic 'hardware'
2813 * structure, we would have to adjust many DHCPv4 sources (from
2814 * interface to DHCPv4 lease code), and we would have to update the
2815 * 'hardware' config directive (probably being reverse compatible and
2816 * providing a new upgrade/replacement primitive). This is a little
2817 * too much to change for now. Hopefully we will revisit this before
2818 * hardware types exceeding 8 bits are assigned.
2819 *
2820 * Uses a static variable to limit log occurence to once per startup
2821 *
2822 * \param htype hardware type value to test
2823 *
2824 * \return returns 0 if the value is too large
2825 *
2826*/
2827int htype_bounds_check(uint16_t htype) {
2828 static int log_once = 0;
2829
2830 if (htype & 0xFF00) {
2831 if (!log_once) {
2832 log_error("Attention: At least one client advertises a "
2833 "hardware type of %d, which exceeds the software "
2834 "limitation of 255.", htype);
2835 log_once = 1;
2836 }
2837
2838 return(0);
2839 }
2840
2841 return(1);
2842}
2843
2844/*!
2845 * \brief Look for hosts by MAC address if it's available
2846 *
2847 * Checks the inbound packet against host declarations which specified:
2848 *
2849 * "hardware ethernet <MAC>;"
2850 *
2851 * For directly connected clients, the function will use the MAC address
2852 * contained in packet:haddr if it's populated. \TODO - While the logic is in
2853 * place for this search, the socket layer does not yet populate packet:haddr,
2854 * this is to be done under rt41523.
2855 *
2856 * For relayed clients, the function will use the MAC address from the
2857 * client-linklayer-address option if it has been supplied by the relay
2858 * directly connected to the client.
2859 *
2860 * \param hp[out] - pointer to storage for the host delcaration if found
2861 * \param packet - received packet
2862 * \param opt_state - option state to search
2863 * \param file - source file
2864 * \param line - line number
2865 *
2866 * \return non-zero if a matching host was found, zero otherwise
2867*/
2868int find_hosts_by_haddr6(struct host_decl **hp,
2869 struct packet *packet,
2870 struct option_state *opt_state,
2871 const char *file, int line) {
2872 int found = 0;
2873 int htype;
2874 int hlen;
2875
2876 /* For directly connected clients, use packet:haddr if populated */
2877 if (packet->dhcpv6_container_packet == NULL) {
2878 if (packet->haddr) {
2879 htype = packet->haddr->hbuf[0];
2880 hlen = packet->haddr->hlen - 1,
2881 log_debug("find_hosts_by_haddr6: using packet->haddr,"
2882 " type: %d, len: %d", htype, hlen);
2883 found = find_hosts_by_haddr (hp, htype,
2884 &packet->haddr->hbuf[1],
2885 hlen, MDL);
2886 }
2887 } else {
2888 /* The first container packet is the from the relay directly
2889 * connected to the client. Per RFC 6939, that is only relay
2890 * that may supply the client linklayer address option. */
2891 struct packet *relay_packet = packet->dhcpv6_container_packet;
2892 struct option_state *relay_state = relay_packet->options;
2893 struct data_string rel_addr;
2894 struct option_cache *oc;
2895
2896 /* Look for the option in the first relay packet */
2897 oc = lookup_option(&dhcpv6_universe, relay_state,
2898 D6O_CLIENT_LINKLAYER_ADDR);
2899 if (!oc) {
2900 /* Not there, so bail */
2901 return (0);
2902 }
2903
2904 /* The option is present, fetch the address data */
2905 memset(&rel_addr, 0, sizeof(rel_addr));
2906 if (!evaluate_option_cache(&rel_addr, relay_packet, NULL, NULL,
2907 relay_state, NULL, &global_scope,
2908 oc, MDL)) {
2909 log_error("find_hosts_by_add6:"
2910 "Error evaluating option cache");
2911 return (0);
2912 }
2913
2914 /* The relay address data should be:
2915 * byte 0 - 1 = hardware type
2916 * bytes 2 - hlen = hardware address
2917 * where hlen ( hardware address len) is option data len - 2 */
2918 hlen = rel_addr.len - 2;
2919 if (hlen > 0 && hlen <= HARDWARE_ADDR_LEN) {
2920 htype = getUShort(rel_addr.data);
2921 if (htype_bounds_check(htype)) {
2922 /* Looks valid, let's search with it */
2923 log_debug("find_hosts_by_haddr6:"
2924 "using relayed haddr"
2925 " type: %d, len: %d", htype, hlen);
2926 found = find_hosts_by_haddr (hp, htype,
2927 &rel_addr.data[2],
2928 hlen, MDL);
2929 }
2930 }
2931
2932 data_string_forget(&rel_addr, MDL);
2933 }
2934
2935 return (found);
2936}
2937
2938/*
2939 * find_host_by_duid_chaddr() synthesizes a DHCPv4-like 'hardware'
2940 * parameter from a DHCPv6 supplied DUID (client-identifier option),
2941 * and may seek to use client or relay supplied hardware addresses.
2942 */
2943int
2944find_hosts_by_duid_chaddr(struct host_decl **host,
2945 const struct data_string *client_id) {
2946 int htype, hlen;
2947 const unsigned char *chaddr;
2948
2949 /*
2950 * The DUID-LL and DUID-LLT must have a 2-byte DUID type and 2-byte
2951 * htype.
2952 */
2953 if (client_id->len < 4)
2954 return 0;
2955
2956 /*
2957 * The third and fourth octets of the DUID-LL and DUID-LLT
2958 * is the hardware type, but in 16 bits.
2959 */
2960 htype = getUShort(client_id->data + 2);
2961 hlen = 0;
2962 chaddr = NULL;
2963
2964 /* The first two octets of the DUID identify the type. */
2965 switch(getUShort(client_id->data)) {
2966 case DUID_LLT:
2967 if (client_id->len > 8) {
2968 hlen = client_id->len - 8;
2969 chaddr = client_id->data + 8;
2970 }
2971 break;
2972
2973 case DUID_LL:
2974 /*
2975 * Note that client_id->len must be greater than or equal
2976 * to four to get to this point in the function.
2977 */
2978 hlen = client_id->len - 4;
2979 chaddr = client_id->data + 4;
2980 break;
2981
2982 default:
2983 break;
2984 }
2985
2986 if ((hlen == 0) || (hlen > HARDWARE_ADDR_LEN) ||
2987 !htype_bounds_check(htype)) {
2988 return (0);
2989 }
2990
2991 return find_hosts_by_haddr(host, htype, chaddr, hlen, MDL);
2992}
2993
2994/*
2995 * \brief Finds a host record that matches the packet, if any
2996 *
2997 * This function centralizes the logic for matching v6 client
2998 * packets to host declarations. We check in the following order
2999 * for matches with:
3000 *
3001 * 1. client_id if specified
3002 * 2. MAC address when explicitly available
3003 * 3. packet option
3004 * 4. synthesized hardware address - this is done last as some
3005 * synthesis methods are not consided to be reliable
3006 *
3007 * \param[out] host - pointer to storage for the located host
3008 * \param packet - inbound client packet
3009 * \param client_id - client identifier (if one)
3010 * \param file - source file
3011 * \param line - source file line number
3012 * \return non-zero if a host is found, zero otherwise
3013*/
3014int
3015find_hosts6(struct host_decl** host, struct packet* packet,
3016 const struct data_string* client_id, char* file, int line) {
3017 return (find_hosts_by_uid(host, client_id->data, client_id->len, MDL)
3018 || find_hosts_by_haddr6(host, packet, packet->options, MDL)
3019 || find_hosts_by_option(host, packet, packet->options, MDL)
3020 || find_hosts_by_duid_chaddr(host, client_id));
3021}
3022
a1a15031 3023/* unittest moved to server/tests/mdb6_unittest.c */