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