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