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