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