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