]>
Commit | Line | Data |
---|---|---|
98bd7ca0 DH |
1 | /* dhc6.c - DHCPv6 client routines. */ |
2 | ||
3 | /* | |
dccb6edf | 4 | * Copyright (c) 2006-2008 by Internet Systems Consortium, Inc. ("ISC") |
98bd7ca0 DH |
5 | * |
6 | * Permission to use, copy, modify, and distribute this software for any | |
7 | * purpose with or without fee is hereby granted, provided that the above | |
8 | * copyright notice and this permission notice appear in all copies. | |
9 | * | |
10 | * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES | |
11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR | |
13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT | |
16 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
17 | * | |
18 | * Internet Systems Consortium, Inc. | |
19 | * 950 Charter Street | |
20 | * Redwood City, CA 94063 | |
21 | * <info@isc.org> | |
22 | * http://www.isc.org/ | |
23 | */ | |
24 | ||
98bd7ca0 DH |
25 | #include "dhcpd.h" |
26 | ||
fe5b0fdd DH |
27 | #ifdef DHCPv6 |
28 | ||
98bd7ca0 | 29 | struct sockaddr_in6 DHCPv6DestAddr; |
0c20eab3 DH |
30 | |
31 | /* Option definition structures that are used by the software - declared | |
32 | * here once and assigned at startup to save lookups. | |
33 | */ | |
98bd7ca0 | 34 | struct option *clientid_option = NULL; |
0c20eab3 | 35 | struct option *elapsed_option = NULL; |
98bd7ca0 | 36 | struct option *ia_na_option = NULL; |
1d9774ab FD |
37 | struct option *ia_ta_option = NULL; |
38 | struct option *ia_pd_option = NULL; | |
98bd7ca0 | 39 | struct option *iaaddr_option = NULL; |
1d9774ab | 40 | struct option *iaprefix_option = NULL; |
0c20eab3 | 41 | struct option *oro_option = NULL; |
98bd7ca0 DH |
42 | |
43 | static struct dhc6_lease *dhc6_dup_lease(struct dhc6_lease *lease, | |
06eb8bab SK |
44 | const char *file, int line); |
45 | static struct dhc6_ia *dhc6_dup_ia(struct dhc6_ia *ia, | |
46 | const char *file, int line); | |
98bd7ca0 | 47 | static struct dhc6_addr *dhc6_dup_addr(struct dhc6_addr *addr, |
06eb8bab | 48 | const char *file, int line); |
e32529a5 | 49 | static void dhc6_ia_destroy(struct dhc6_ia **src, const char *file, int line); |
98bd7ca0 DH |
50 | static isc_result_t dhc6_parse_ia_na(struct dhc6_ia **pia, |
51 | struct packet *packet, | |
52 | struct option_state *options); | |
1d9774ab FD |
53 | static isc_result_t dhc6_parse_ia_ta(struct dhc6_ia **pia, |
54 | struct packet *packet, | |
55 | struct option_state *options); | |
56 | static isc_result_t dhc6_parse_ia_pd(struct dhc6_ia **pia, | |
57 | struct packet *packet, | |
58 | struct option_state *options); | |
98bd7ca0 DH |
59 | static isc_result_t dhc6_parse_addrs(struct dhc6_addr **paddr, |
60 | struct packet *packet, | |
61 | struct option_state *options); | |
1d9774ab FD |
62 | static isc_result_t dhc6_parse_prefs(struct dhc6_addr **ppref, |
63 | struct packet *packet, | |
64 | struct option_state *options); | |
65 | static struct dhc6_ia *find_ia_na(struct dhc6_ia *head, const char *id); | |
98bd7ca0 DH |
66 | static struct dhc6_addr *find_addr(struct dhc6_addr *head, |
67 | struct iaddr *address); | |
68 | void init_handler(struct packet *packet, struct client_state *client); | |
6d7f9584 | 69 | void rapid_commit_handler(struct packet *packet, struct client_state *client); |
98bd7ca0 DH |
70 | void do_init6(void *input); |
71 | void do_confirm6(void *input); | |
72 | void reply_handler(struct packet *packet, struct client_state *client); | |
1d9774ab FD |
73 | static isc_result_t dhc6_add_ia_na(struct client_state *client, |
74 | struct data_string *packet, | |
75 | struct dhc6_lease *lease, | |
76 | u_int8_t message); | |
98bd7ca0 DH |
77 | static void dhc6_merge_lease(struct dhc6_lease *src, struct dhc6_lease *dst); |
78 | void do_select6(void *input); | |
79 | void do_refresh6(void *input); | |
af5fa176 | 80 | static void do_release6(void *input); |
98bd7ca0 DH |
81 | static void start_bound(struct client_state *client); |
82 | void bound_handler(struct packet *packet, struct client_state *client); | |
83 | void start_renew6(void *input); | |
84 | void start_rebind6(void *input); | |
85 | void do_depref(void *input); | |
86 | void do_expire(void *input); | |
87 | static void make_client6_options(struct client_state *client, | |
88 | struct option_state **op, | |
89 | struct dhc6_lease *lease, u_int8_t message); | |
06eb8bab SK |
90 | static void script_write_params6(struct client_state *client, |
91 | const char *prefix, | |
98bd7ca0 DH |
92 | struct option_state *options); |
93 | ||
94 | /* The "best" default DUID, since we cannot predict any information | |
95 | * about the system (such as whether or not the hardware addresses are | |
96 | * integrated into the motherboard or similar), is the "LLT", link local | |
97 | * plus time, DUID. | |
98 | * | |
99 | * Once generated, this duid is stored into the state database, and | |
100 | * retained across restarts. | |
101 | * | |
102 | * For the time being, there is probably a different state database for | |
103 | * every daemon, so this winds up being a per-interface identifier...which | |
104 | * is not how it is intended. Upcoming rearchitecting the client should | |
105 | * address this "one daemon model." | |
106 | */ | |
107 | void | |
06eb8bab | 108 | form_duid(struct data_string *duid, const char *file, int line) |
98bd7ca0 DH |
109 | { |
110 | struct interface_info *ip; | |
111 | int len; | |
112 | ||
113 | /* For now, just use the first interface on the list. */ | |
114 | ip = interfaces; | |
115 | ||
116 | if (ip == NULL) | |
117 | log_fatal("Impossible condition at %s:%d.", MDL); | |
118 | ||
119 | if ((ip->hw_address.hlen == 0) || | |
120 | (ip->hw_address.hlen > sizeof(ip->hw_address.hbuf))) | |
121 | log_fatal("Impossible hardware address length at %s:%d.", MDL); | |
122 | ||
123 | /* 2 bytes for the 'duid type' field. | |
124 | * 2 bytes for the 'htype' field. | |
125 | * 4 bytes for the 'current time'. | |
126 | * enough bytes for the hardware address (note that hw_address has | |
127 | * the 'htype' on byte zero). | |
128 | */ | |
129 | len = 8 + (ip->hw_address.hlen - 1); | |
130 | if (!buffer_allocate(&duid->buffer, len, MDL)) | |
131 | log_fatal("no memory for default DUID!"); | |
132 | duid->data = duid->buffer->data; | |
133 | duid->len = len; | |
134 | ||
135 | /* Basic Link Local Address type of DUID. */ | |
136 | putUShort(duid->buffer->data, DUID_LLT); | |
137 | putUShort(duid->buffer->data + 2, ip->hw_address.hbuf[0]); | |
7e9f7a1b | 138 | putULong(duid->buffer->data + 4, cur_time - DUID_TIME_EPOCH); |
98bd7ca0 DH |
139 | memcpy(duid->buffer->data + 8, ip->hw_address.hbuf + 1, |
140 | ip->hw_address.hlen - 1); | |
141 | } | |
142 | ||
143 | /* Assign DHCPv6 port numbers as a client. | |
144 | */ | |
145 | void | |
146 | dhcpv6_client_assignments(void) | |
147 | { | |
148 | struct servent *ent; | |
149 | unsigned code; | |
150 | ||
151 | if (path_dhclient_pid == NULL) | |
152 | path_dhclient_pid = _PATH_DHCLIENT6_PID; | |
153 | if (path_dhclient_db == NULL) | |
154 | path_dhclient_db = _PATH_DHCLIENT6_DB; | |
155 | ||
156 | if (local_port == 0) { | |
157 | ent = getservbyname("dhcpv6-client", "udp"); | |
158 | if (ent == NULL) | |
159 | local_port = htons(546); | |
160 | else | |
161 | local_port = ent->s_port; | |
162 | } | |
163 | ||
164 | if (remote_port == 0) { | |
165 | ent = getservbyname("dhcpv6-server", "udp"); | |
166 | if (ent == NULL) | |
167 | remote_port = htons(547); | |
168 | else | |
169 | remote_port = ent->s_port; | |
170 | } | |
171 | ||
172 | memset(&DHCPv6DestAddr, 0, sizeof(DHCPv6DestAddr)); | |
173 | DHCPv6DestAddr.sin6_family = AF_INET6; | |
174 | DHCPv6DestAddr.sin6_port = remote_port; | |
175 | inet_pton(AF_INET6, All_DHCP_Relay_Agents_and_Servers, | |
176 | &DHCPv6DestAddr.sin6_addr); | |
177 | ||
178 | code = D6O_CLIENTID; | |
179 | if (!option_code_hash_lookup(&clientid_option, | |
180 | dhcpv6_universe.code_hash, &code, 0, MDL)) | |
181 | log_fatal("Unable to find the CLIENTID option definition."); | |
182 | ||
0c20eab3 DH |
183 | code = D6O_ELAPSED_TIME; |
184 | if (!option_code_hash_lookup(&elapsed_option, | |
185 | dhcpv6_universe.code_hash, &code, 0, MDL)) | |
186 | log_fatal("Unable to find the ELAPSED_TIME option definition."); | |
187 | ||
98bd7ca0 DH |
188 | code = D6O_IA_NA; |
189 | if (!option_code_hash_lookup(&ia_na_option, dhcpv6_universe.code_hash, | |
190 | &code, 0, MDL)) | |
191 | log_fatal("Unable to find the IA_NA option definition."); | |
192 | ||
1d9774ab FD |
193 | code = D6O_IA_TA; |
194 | if (!option_code_hash_lookup(&ia_ta_option, dhcpv6_universe.code_hash, | |
195 | &code, 0, MDL)) | |
196 | log_fatal("Unable to find the IA_TA option definition."); | |
197 | ||
198 | code = D6O_IA_PD; | |
199 | if (!option_code_hash_lookup(&ia_pd_option, dhcpv6_universe.code_hash, | |
200 | &code, 0, MDL)) | |
201 | log_fatal("Unable to find the IA_PD option definition."); | |
202 | ||
98bd7ca0 DH |
203 | code = D6O_IAADDR; |
204 | if (!option_code_hash_lookup(&iaaddr_option, dhcpv6_universe.code_hash, | |
205 | &code, 0, MDL)) | |
206 | log_fatal("Unable to find the IAADDR option definition."); | |
207 | ||
1d9774ab FD |
208 | code = D6O_IAPREFIX; |
209 | if (!option_code_hash_lookup(&iaprefix_option, | |
210 | dhcpv6_universe.code_hash, | |
211 | &code, 0, MDL)) | |
212 | log_fatal("Unable to find the IAPREFIX option definition."); | |
213 | ||
0c20eab3 DH |
214 | code = D6O_ORO; |
215 | if (!option_code_hash_lookup(&oro_option, dhcpv6_universe.code_hash, | |
216 | &code, 0, MDL)) | |
217 | log_fatal("Unable to find the ORO option definition."); | |
98bd7ca0 DH |
218 | |
219 | #ifndef __CYGWIN32__ /* XXX */ | |
220 | endservent(); | |
221 | #endif | |
222 | } | |
223 | ||
224 | /* Instead of implementing RFC3315 RAND (section 14) as a float "between" | |
225 | * -0.1 and 0.1 non-inclusive, we implement it as an integer. | |
226 | * | |
227 | * The result is expected to follow this table: | |
228 | * | |
229 | * split range answer | |
230 | * - ERROR - base <= 0 | |
231 | * 0 1 0..0 1 <= base <= 10 | |
232 | * 1 3 -1..1 11 <= base <= 20 | |
233 | * 2 5 -2..2 21 <= base <= 30 | |
234 | * 3 7 -3..3 31 <= base <= 40 | |
235 | * ... | |
236 | * | |
237 | * XXX: For this to make sense, we really need to do timing on a | |
238 | * XXX: usec scale...we currently can assume zero for any value less than | |
239 | * XXX: 11, which are very common in early stages of transmission for most | |
240 | * XXX: messages. | |
241 | */ | |
242 | static TIME | |
243 | dhc6_rand(TIME base) | |
244 | { | |
245 | TIME rval; | |
246 | TIME range; | |
247 | TIME split; | |
248 | ||
249 | /* A zero or less timeout is a bad thing...we don't want to | |
250 | * DHCP-flood anyone. | |
251 | */ | |
252 | if (base <= 0) | |
253 | log_fatal("Impossible condition at %s:%d.", MDL); | |
254 | ||
255 | /* The first thing we do is count how many random integers we want | |
256 | * in either direction (best thought of as the maximum negative | |
257 | * integer, as we will subtract this potentially from a random 0). | |
258 | */ | |
259 | split = (base - 1) / 10; | |
260 | ||
261 | /* Don't bother with the rest of the math if we know we'll get 0. */ | |
262 | if (split == 0) | |
263 | return 0; | |
264 | ||
265 | /* Then we count the total number of integers in this set. This | |
266 | * is twice the number of integers in positive and negative | |
267 | * directions, plus zero (-1, 0, 1 is 3, -2..2 adds 2 to 5, so forth). | |
268 | */ | |
269 | range = (split * 2) + 1; | |
270 | ||
271 | /* Take a random number from [0..(range-1)]. */ | |
272 | rval = random(); | |
273 | rval %= range; | |
274 | ||
275 | /* Offset it to uncover potential negative values. */ | |
276 | rval -= split; | |
277 | ||
278 | return rval; | |
279 | } | |
280 | ||
c0216cb7 | 281 | /* Initialize message exchange timers (set RT from Initial-RT). */ |
98bd7ca0 | 282 | static void |
c0216cb7 | 283 | dhc6_retrans_init(struct client_state *client) |
98bd7ca0 DH |
284 | { |
285 | int xid; | |
286 | ||
c0216cb7 | 287 | /* Initialize timers. */ |
c0216cb7 DH |
288 | client->txcount = 0; |
289 | client->RT = client->IRT + dhc6_rand(client->IRT); | |
290 | ||
291 | /* Generate a new random 24-bit transaction ID for this exchange. */ | |
292 | ||
293 | #if (RAND_MAX >= 0x00ffffff) | |
294 | xid = random(); | |
295 | #elif (RAND_MAX >= 0x0000ffff) | |
296 | xid = (random() << 16) ^ random(); | |
297 | #elif (RAND_MAX >= 0x000000ff) | |
298 | xid = (random() << 16) ^ (random() << 8) ^ random(); | |
299 | #else | |
300 | # error "Random number generator of less than 8 bits not supported." | |
301 | #endif | |
98bd7ca0 DH |
302 | |
303 | client->dhcpv6_transaction_id[0] = (xid >> 16) & 0xff; | |
304 | client->dhcpv6_transaction_id[1] = (xid >> 8) & 0xff; | |
305 | client->dhcpv6_transaction_id[2] = xid & 0xff; | |
306 | } | |
307 | ||
98bd7ca0 DH |
308 | /* Advance the DHCPv6 retransmission state once. */ |
309 | static void | |
310 | dhc6_retrans_advance(struct client_state *client) | |
311 | { | |
be62cf06 | 312 | struct timeval elapsed; |
98bd7ca0 | 313 | |
be62cf06 FD |
314 | /* elapsed = cur - start */ |
315 | elapsed.tv_sec = cur_tv.tv_sec - client->start_time.tv_sec; | |
316 | elapsed.tv_usec = cur_tv.tv_usec - client->start_time.tv_usec; | |
317 | if (elapsed.tv_usec < 0) { | |
318 | elapsed.tv_sec -= 1; | |
319 | elapsed.tv_usec += 1000000; | |
320 | } | |
98bd7ca0 | 321 | /* retrans_advance is called after consuming client->RT. */ |
be62cf06 FD |
322 | /* elapsed += RT */ |
323 | elapsed.tv_sec += client->RT / 100; | |
324 | elapsed.tv_usec += (client->RT % 100) * 10000; | |
325 | if (elapsed.tv_usec >= 1000000) { | |
326 | elapsed.tv_sec += 1; | |
327 | elapsed.tv_usec -= 1000000; | |
328 | } | |
98bd7ca0 DH |
329 | |
330 | /* RT for each subsequent message transmission is based on the previous | |
331 | * value of RT: | |
332 | * | |
333 | * RT = 2*RTprev + RAND*RTprev | |
334 | */ | |
335 | client->RT += client->RT + dhc6_rand(client->RT); | |
336 | ||
337 | /* MRT specifies an upper bound on the value of RT (disregarding the | |
338 | * randomization added by the use of RAND). If MRT has a value of 0, | |
339 | * there is no upper limit on the value of RT. Otherwise: | |
340 | * | |
341 | * if (RT > MRT) | |
342 | * RT = MRT + RAND*MRT | |
343 | */ | |
344 | if ((client->MRT != 0) && (client->RT > client->MRT)) | |
345 | client->RT = client->MRT + dhc6_rand(client->MRT); | |
346 | ||
347 | /* Further, if there's an MRD, we should wake up upon reaching | |
348 | * the MRD rather than at some point after it. | |
349 | */ | |
be62cf06 FD |
350 | if (client->MRD == 0) { |
351 | /* Done. */ | |
352 | client->txcount++; | |
353 | return; | |
354 | } | |
355 | /* elapsed += client->RT */ | |
356 | elapsed.tv_sec += client->RT / 100; | |
357 | elapsed.tv_usec += (client->RT % 100) * 10000; | |
358 | if (elapsed.tv_usec >= 1000000) { | |
359 | elapsed.tv_sec += 1; | |
360 | elapsed.tv_usec -= 1000000; | |
361 | } | |
362 | if (elapsed.tv_sec >= client->MRD) { | |
363 | /* | |
364 | * wake at RT + cur = start + MRD | |
365 | */ | |
366 | client->RT = client->MRD + | |
367 | (client->start_time.tv_sec - cur_tv.tv_sec); | |
368 | client->RT = client->RT * 100 + | |
369 | (client->start_time.tv_usec - cur_tv.tv_usec) / 10000; | |
98bd7ca0 | 370 | } |
98bd7ca0 DH |
371 | client->txcount++; |
372 | } | |
373 | ||
374 | /* Quick validation of DHCPv6 ADVERTISE packet contents. */ | |
375 | static int | |
376 | valid_reply(struct packet *packet, struct client_state *client) | |
377 | { | |
378 | struct data_string sid, cid; | |
379 | struct option_cache *oc; | |
380 | int rval = ISC_TRUE; | |
381 | ||
382 | memset(&sid, 0, sizeof(sid)); | |
383 | memset(&cid, 0, sizeof(cid)); | |
384 | ||
385 | if (!lookup_option(&dhcpv6_universe, packet->options, D6O_SERVERID)) { | |
9ab0cc6a | 386 | log_error("Response without a server identifier received."); |
98bd7ca0 DH |
387 | rval = ISC_FALSE; |
388 | } | |
389 | ||
390 | oc = lookup_option(&dhcpv6_universe, packet->options, D6O_CLIENTID); | |
391 | if (!oc || | |
392 | !evaluate_option_cache(&sid, packet, NULL, client, packet->options, | |
393 | client->sent_options, &global_scope, oc, | |
394 | MDL)) { | |
9ab0cc6a | 395 | log_error("Response without a client identifier."); |
98bd7ca0 DH |
396 | rval = ISC_FALSE; |
397 | } | |
398 | ||
399 | oc = lookup_option(&dhcpv6_universe, client->sent_options, | |
400 | D6O_CLIENTID); | |
401 | if (!oc || | |
402 | !evaluate_option_cache(&cid, packet, NULL, client, | |
403 | client->sent_options, NULL, &global_scope, | |
404 | oc, MDL)) { | |
405 | log_error("Local client identifier is missing!"); | |
406 | rval = ISC_FALSE; | |
407 | } | |
408 | ||
409 | if (sid.len == 0 || | |
410 | sid.len != cid.len || | |
411 | memcmp(sid.data, cid.data, sid.len)) { | |
412 | log_error("Advertise with matching transaction ID, but " | |
413 | "mismatching client id."); | |
414 | rval = ISC_FALSE; | |
415 | } | |
416 | ||
417 | return rval; | |
418 | } | |
419 | ||
420 | /* Create a complete copy of a DHCPv6 lease structure. | |
421 | */ | |
422 | static struct dhc6_lease * | |
06eb8bab | 423 | dhc6_dup_lease(struct dhc6_lease *lease, const char *file, int line) |
98bd7ca0 DH |
424 | { |
425 | struct dhc6_lease *copy; | |
426 | struct dhc6_ia **insert_ia, *ia; | |
427 | ||
428 | copy = dmalloc(sizeof(*copy), file, line); | |
429 | if (copy == NULL) { | |
430 | log_error("Out of memory for v6 lease structure."); | |
431 | return NULL; | |
432 | } | |
433 | ||
434 | data_string_copy(©->server_id, &lease->server_id, file, line); | |
435 | copy->pref = lease->pref; | |
436 | ||
437 | memcpy(copy->dhcpv6_transaction_id, lease->dhcpv6_transaction_id, | |
438 | sizeof(copy->dhcpv6_transaction_id)); | |
439 | ||
440 | option_state_reference(©->options, lease->options, file, line); | |
441 | ||
442 | insert_ia = ©->bindings; | |
443 | for (ia = lease->bindings ; ia != NULL ; ia = ia->next) { | |
444 | *insert_ia = dhc6_dup_ia(ia, file, line); | |
445 | ||
446 | if (*insert_ia == NULL) { | |
e32529a5 | 447 | dhc6_lease_destroy(©, file, line); |
98bd7ca0 DH |
448 | return NULL; |
449 | } | |
450 | ||
451 | insert_ia = &(*insert_ia)->next; | |
452 | } | |
453 | ||
454 | return copy; | |
455 | } | |
456 | ||
457 | /* Duplicate an IA structure. | |
458 | */ | |
459 | static struct dhc6_ia * | |
06eb8bab | 460 | dhc6_dup_ia(struct dhc6_ia *ia, const char *file, int line) |
98bd7ca0 DH |
461 | { |
462 | struct dhc6_ia *copy; | |
463 | struct dhc6_addr **insert_addr, *addr; | |
464 | ||
465 | copy = dmalloc(sizeof(*ia), file, line); | |
466 | ||
467 | memcpy(copy->iaid, ia->iaid, sizeof(copy->iaid)); | |
468 | ||
1d9774ab | 469 | copy->ia_type = ia->ia_type; |
98bd7ca0 DH |
470 | copy->starts = ia->starts; |
471 | copy->renew = ia->renew; | |
472 | copy->rebind = ia->rebind; | |
473 | ||
474 | insert_addr = ©->addrs; | |
475 | for (addr = ia->addrs ; addr != NULL ; addr = addr->next) { | |
476 | *insert_addr = dhc6_dup_addr(addr, file, line); | |
477 | ||
478 | if (*insert_addr == NULL) { | |
e32529a5 | 479 | dhc6_ia_destroy(©, file, line); |
98bd7ca0 DH |
480 | return NULL; |
481 | } | |
482 | ||
483 | insert_addr = &(*insert_addr)->next; | |
484 | } | |
485 | ||
486 | if (ia->options != NULL) | |
487 | option_state_reference(©->options, ia->options, | |
488 | file, line); | |
489 | ||
490 | return copy; | |
491 | } | |
492 | ||
1d9774ab | 493 | /* Duplicate an IAADDR or IAPREFIX structure. |
98bd7ca0 DH |
494 | */ |
495 | static struct dhc6_addr * | |
06eb8bab | 496 | dhc6_dup_addr(struct dhc6_addr *addr, const char *file, int line) |
98bd7ca0 DH |
497 | { |
498 | struct dhc6_addr *copy; | |
499 | ||
500 | copy = dmalloc(sizeof(*addr), file, line); | |
501 | ||
502 | if (copy == NULL) | |
503 | return NULL; | |
504 | ||
505 | memcpy(©->address, &addr->address, sizeof(copy->address)); | |
506 | ||
1d9774ab | 507 | copy->plen = addr->plen; |
98bd7ca0 DH |
508 | copy->flags = addr->flags; |
509 | copy->starts = addr->starts; | |
510 | copy->preferred_life = addr->preferred_life; | |
511 | copy->max_life = addr->max_life; | |
512 | ||
513 | if (addr->options != NULL) | |
514 | option_state_reference(©->options, addr->options, | |
515 | file, line); | |
516 | ||
517 | return copy; | |
518 | } | |
519 | ||
520 | /* Form a DHCPv6 lease structure based upon packet contents. Creates and | |
1d9774ab | 521 | * populates IA's and any IAADDR/IAPREFIX's they contain. |
98bd7ca0 DH |
522 | */ |
523 | static struct dhc6_lease * | |
524 | dhc6_leaseify(struct packet *packet) | |
525 | { | |
526 | struct data_string ds; | |
527 | struct dhc6_lease *lease; | |
528 | struct option_cache *oc; | |
529 | ||
530 | lease = dmalloc(sizeof(*lease), MDL); | |
531 | if (lease == NULL) { | |
532 | log_error("Out of memory for v6 lease structure."); | |
533 | return NULL; | |
534 | } | |
535 | ||
536 | memcpy(lease->dhcpv6_transaction_id, packet->dhcpv6_transaction_id, 3); | |
537 | option_state_reference(&lease->options, packet->options, MDL); | |
538 | ||
539 | memset(&ds, 0, sizeof(ds)); | |
540 | ||
541 | /* Determine preference (default zero). */ | |
542 | oc = lookup_option(&dhcpv6_universe, lease->options, D6O_PREFERENCE); | |
543 | if (oc && | |
544 | evaluate_option_cache(&ds, packet, NULL, NULL, lease->options, | |
545 | NULL, &global_scope, oc, MDL)) { | |
546 | if (ds.len != 1) { | |
547 | log_error("Invalid length of DHCPv6 Preference option " | |
548 | "(%d != 1)", ds.len); | |
549 | data_string_forget(&ds, MDL); | |
e32529a5 | 550 | dhc6_lease_destroy(&lease, MDL); |
98bd7ca0 DH |
551 | return NULL; |
552 | } else { | |
553 | lease->pref = ds.data[0]; | |
554 | log_debug("RCV: X-- Preference %u.", | |
555 | (unsigned)lease->pref); | |
556 | } | |
557 | ||
558 | data_string_forget(&ds, MDL); | |
559 | } | |
560 | ||
561 | /* Dig into recursive DHCPv6 pockets for IA_NA and contained IAADDR | |
562 | * options. | |
563 | */ | |
564 | if (dhc6_parse_ia_na(&lease->bindings, packet, | |
565 | lease->options) != ISC_R_SUCCESS) { | |
566 | /* Error conditions are logged by the caller. */ | |
e32529a5 | 567 | dhc6_lease_destroy(&lease, MDL); |
98bd7ca0 DH |
568 | return NULL; |
569 | } | |
1d9774ab FD |
570 | /* Dig into recursive DHCPv6 pockets for IA_TA and contained IAADDR |
571 | * options. | |
572 | */ | |
573 | if (dhc6_parse_ia_ta(&lease->bindings, packet, | |
574 | lease->options) != ISC_R_SUCCESS) { | |
575 | /* Error conditions are logged by the caller. */ | |
576 | dhc6_lease_destroy(&lease, MDL); | |
577 | return NULL; | |
578 | } | |
579 | /* Dig into recursive DHCPv6 pockets for IA_PD and contained IAPREFIX | |
580 | * options. | |
581 | */ | |
582 | if (dhc6_parse_ia_pd(&lease->bindings, packet, | |
583 | lease->options) != ISC_R_SUCCESS) { | |
584 | /* Error conditions are logged by the caller. */ | |
585 | dhc6_lease_destroy(&lease, MDL); | |
586 | return NULL; | |
587 | } | |
98bd7ca0 DH |
588 | |
589 | /* This is last because in the future we may want to make a different | |
590 | * key based upon additional information from the packet (we may need | |
591 | * to allow multiple leases in one client state per server, but we're | |
592 | * not sure based on what additional keys now). | |
593 | */ | |
594 | oc = lookup_option(&dhcpv6_universe, packet->options, D6O_SERVERID); | |
595 | if (!evaluate_option_cache(&lease->server_id, packet, NULL, NULL, | |
596 | lease->options, NULL, &global_scope, | |
597 | oc, MDL) || | |
598 | lease->server_id.len == 0) { | |
599 | /* This should be impossible due to validation checks earlier. | |
600 | */ | |
601 | log_error("Invalid SERVERID option cache."); | |
e32529a5 | 602 | dhc6_lease_destroy(&lease, MDL); |
98bd7ca0 DH |
603 | return NULL; |
604 | } else { | |
605 | log_debug("RCV: X-- Server ID: %s", | |
606 | print_hex_1(lease->server_id.len, | |
607 | lease->server_id.data, 52)); | |
608 | } | |
609 | ||
610 | return lease; | |
611 | } | |
612 | ||
613 | static isc_result_t | |
614 | dhc6_parse_ia_na(struct dhc6_ia **pia, struct packet *packet, | |
615 | struct option_state *options) | |
616 | { | |
617 | struct data_string ds; | |
618 | struct dhc6_ia *ia; | |
619 | struct option_cache *oc; | |
620 | isc_result_t result; | |
621 | ||
622 | memset(&ds, 0, sizeof(ds)); | |
623 | ||
624 | oc = lookup_option(&dhcpv6_universe, options, D6O_IA_NA); | |
625 | for ( ; oc != NULL ; oc = oc->next) { | |
626 | ia = dmalloc(sizeof(*ia), MDL); | |
627 | if (ia == NULL) { | |
628 | log_error("Out of memory allocating IA_NA structure."); | |
629 | return ISC_R_NOMEMORY; | |
630 | } else if (evaluate_option_cache(&ds, packet, NULL, NULL, | |
631 | options, NULL, | |
632 | &global_scope, oc, MDL) && | |
633 | ds.len >= 12) { | |
634 | memcpy(ia->iaid, ds.data, 4); | |
1d9774ab | 635 | ia->ia_type = D6O_IA_NA; |
98bd7ca0 DH |
636 | ia->starts = cur_time; |
637 | ia->renew = getULong(ds.data + 4); | |
638 | ia->rebind = getULong(ds.data + 8); | |
639 | ||
640 | log_debug("RCV: X-- IA_NA %s", | |
641 | print_hex_1(4, ia->iaid, 59)); | |
642 | /* XXX: This should be the printed time I think. */ | |
643 | log_debug("RCV: | X-- starts %u", | |
644 | (unsigned)ia->starts); | |
645 | log_debug("RCV: | X-- t1 - renew +%u", ia->renew); | |
646 | log_debug("RCV: | X-- t2 - rebind +%u", ia->rebind); | |
647 | ||
648 | /* RFC3315 section 22.4, discard IA_NA's that | |
649 | * have t1 greater than t2, and both not zero. | |
650 | * Since RFC3315 defines this behaviour, it is not | |
651 | * an error - just normal operation. | |
652 | * | |
653 | * Note that RFC3315 says we MUST honor these values | |
654 | * if they are not zero. So insane values are | |
655 | * totally OK. | |
656 | */ | |
657 | if ((ia->renew > 0) && (ia->rebind > 0) && | |
658 | (ia->renew > ia->rebind)) { | |
659 | log_debug("RCV: | !-- INVALID renew/rebind " | |
660 | "times, IA_NA discarded."); | |
661 | dfree(ia, MDL); | |
662 | data_string_forget(&ds, MDL); | |
663 | continue; | |
664 | } | |
665 | ||
666 | if (ds.len > 12) { | |
667 | log_debug("RCV: | X-- [Options]"); | |
668 | ||
669 | if (!option_state_allocate(&ia->options, | |
670 | MDL)) { | |
671 | log_error("Out of memory allocating " | |
672 | "IA option state."); | |
673 | dfree(ia, MDL); | |
674 | data_string_forget(&ds, MDL); | |
675 | return ISC_R_NOMEMORY; | |
676 | } | |
677 | ||
678 | if (!parse_option_buffer(ia->options, | |
679 | ds.data + 12, | |
680 | ds.len - 12, | |
681 | &dhcpv6_universe)) { | |
682 | log_error("Corrupt IA_NA options."); | |
683 | option_state_dereference(&ia->options, | |
684 | MDL); | |
685 | dfree(ia, MDL); | |
686 | data_string_forget(&ds, MDL); | |
687 | return ISC_R_BADPARSE; | |
688 | } | |
689 | } | |
690 | data_string_forget(&ds, MDL); | |
691 | ||
692 | if (ia->options != NULL) { | |
693 | result = dhc6_parse_addrs(&ia->addrs, packet, | |
694 | ia->options); | |
695 | if (result != ISC_R_SUCCESS) { | |
696 | option_state_dereference(&ia->options, | |
697 | MDL); | |
698 | dfree(ia, MDL); | |
699 | return result; | |
700 | } | |
701 | } | |
702 | ||
703 | *pia = ia; | |
704 | pia = &ia->next; | |
705 | } else { | |
706 | log_error("Invalid IA_NA option cache."); | |
707 | dfree(ia, MDL); | |
708 | if (ds.len != 0) | |
709 | data_string_forget(&ds, MDL); | |
710 | return ISC_R_UNEXPECTED; | |
711 | } | |
712 | } | |
713 | ||
714 | return ISC_R_SUCCESS; | |
715 | } | |
716 | ||
1d9774ab FD |
717 | static isc_result_t |
718 | dhc6_parse_ia_ta(struct dhc6_ia **pia, struct packet *packet, | |
719 | struct option_state *options) | |
720 | { | |
721 | struct data_string ds; | |
722 | struct dhc6_ia *ia; | |
723 | struct option_cache *oc; | |
724 | isc_result_t result; | |
725 | ||
726 | memset(&ds, 0, sizeof(ds)); | |
727 | ||
728 | oc = lookup_option(&dhcpv6_universe, options, D6O_IA_TA); | |
729 | for ( ; oc != NULL ; oc = oc->next) { | |
730 | ia = dmalloc(sizeof(*ia), MDL); | |
731 | if (ia == NULL) { | |
732 | log_error("Out of memory allocating IA_TA structure."); | |
733 | return ISC_R_NOMEMORY; | |
734 | } else if (evaluate_option_cache(&ds, packet, NULL, NULL, | |
735 | options, NULL, | |
736 | &global_scope, oc, MDL) && | |
737 | ds.len >= 4) { | |
738 | memcpy(ia->iaid, ds.data, 4); | |
739 | ia->ia_type = D6O_IA_TA; | |
740 | ia->starts = cur_time; | |
741 | ||
742 | log_debug("RCV: X-- IA_TA %s", | |
743 | print_hex_1(4, ia->iaid, 59)); | |
744 | /* XXX: This should be the printed time I think. */ | |
745 | log_debug("RCV: | X-- starts %u", | |
746 | (unsigned)ia->starts); | |
747 | ||
748 | if (ds.len > 4) { | |
749 | log_debug("RCV: | X-- [Options]"); | |
750 | ||
751 | if (!option_state_allocate(&ia->options, | |
752 | MDL)) { | |
753 | log_error("Out of memory allocating " | |
754 | "IA option state."); | |
755 | dfree(ia, MDL); | |
756 | data_string_forget(&ds, MDL); | |
757 | return ISC_R_NOMEMORY; | |
758 | } | |
759 | ||
760 | if (!parse_option_buffer(ia->options, | |
761 | ds.data + 4, | |
762 | ds.len - 4, | |
763 | &dhcpv6_universe)) { | |
764 | log_error("Corrupt IA_TA options."); | |
765 | option_state_dereference(&ia->options, | |
766 | MDL); | |
767 | dfree(ia, MDL); | |
768 | data_string_forget(&ds, MDL); | |
769 | return ISC_R_BADPARSE; | |
770 | } | |
771 | } | |
772 | data_string_forget(&ds, MDL); | |
773 | ||
774 | if (ia->options != NULL) { | |
775 | result = dhc6_parse_addrs(&ia->addrs, packet, | |
776 | ia->options); | |
777 | if (result != ISC_R_SUCCESS) { | |
778 | option_state_dereference(&ia->options, | |
779 | MDL); | |
780 | dfree(ia, MDL); | |
781 | return result; | |
782 | } | |
783 | } | |
784 | ||
785 | *pia = ia; | |
786 | pia = &ia->next; | |
787 | } else { | |
788 | log_error("Invalid IA_TA option cache."); | |
789 | dfree(ia, MDL); | |
790 | if (ds.len != 0) | |
791 | data_string_forget(&ds, MDL); | |
792 | return ISC_R_UNEXPECTED; | |
793 | } | |
794 | } | |
795 | ||
796 | return ISC_R_SUCCESS; | |
797 | } | |
798 | ||
799 | static isc_result_t | |
800 | dhc6_parse_ia_pd(struct dhc6_ia **pia, struct packet *packet, | |
801 | struct option_state *options) | |
802 | { | |
803 | struct data_string ds; | |
804 | struct dhc6_ia *ia; | |
805 | struct option_cache *oc; | |
806 | isc_result_t result; | |
807 | ||
808 | memset(&ds, 0, sizeof(ds)); | |
809 | ||
810 | oc = lookup_option(&dhcpv6_universe, options, D6O_IA_PD); | |
811 | for ( ; oc != NULL ; oc = oc->next) { | |
812 | ia = dmalloc(sizeof(*ia), MDL); | |
813 | if (ia == NULL) { | |
814 | log_error("Out of memory allocating IA_PD structure."); | |
815 | return ISC_R_NOMEMORY; | |
816 | } else if (evaluate_option_cache(&ds, packet, NULL, NULL, | |
817 | options, NULL, | |
818 | &global_scope, oc, MDL) && | |
819 | ds.len >= 12) { | |
820 | memcpy(ia->iaid, ds.data, 4); | |
821 | ia->ia_type = D6O_IA_PD; | |
822 | ia->starts = cur_time; | |
823 | ia->renew = getULong(ds.data + 4); | |
824 | ia->rebind = getULong(ds.data + 8); | |
825 | ||
826 | log_debug("RCV: X-- IA_PD %s", | |
827 | print_hex_1(4, ia->iaid, 59)); | |
828 | /* XXX: This should be the printed time I think. */ | |
829 | log_debug("RCV: | X-- starts %u", | |
830 | (unsigned)ia->starts); | |
831 | log_debug("RCV: | X-- t1 - renew +%u", ia->renew); | |
832 | log_debug("RCV: | X-- t2 - rebind +%u", ia->rebind); | |
833 | ||
834 | /* RFC3315 section 22.4, discard IA_PD's that | |
835 | * have t1 greater than t2, and both not zero. | |
836 | * Since RFC3315 defines this behaviour, it is not | |
837 | * an error - just normal operation. | |
838 | */ | |
839 | if ((ia->renew > 0) && (ia->rebind > 0) && | |
840 | (ia->renew > ia->rebind)) { | |
841 | log_debug("RCV: | !-- INVALID renew/rebind " | |
842 | "times, IA_PD discarded."); | |
843 | dfree(ia, MDL); | |
844 | data_string_forget(&ds, MDL); | |
845 | continue; | |
846 | } | |
847 | ||
848 | if (ds.len > 12) { | |
849 | log_debug("RCV: | X-- [Options]"); | |
850 | ||
851 | if (!option_state_allocate(&ia->options, | |
852 | MDL)) { | |
853 | log_error("Out of memory allocating " | |
854 | "IA option state."); | |
855 | dfree(ia, MDL); | |
856 | data_string_forget(&ds, MDL); | |
857 | return ISC_R_NOMEMORY; | |
858 | } | |
859 | ||
860 | if (!parse_option_buffer(ia->options, | |
861 | ds.data + 12, | |
862 | ds.len - 12, | |
863 | &dhcpv6_universe)) { | |
864 | log_error("Corrupt IA_PD options."); | |
865 | option_state_dereference(&ia->options, | |
866 | MDL); | |
867 | dfree(ia, MDL); | |
868 | data_string_forget(&ds, MDL); | |
869 | return ISC_R_BADPARSE; | |
870 | } | |
871 | } | |
872 | data_string_forget(&ds, MDL); | |
873 | ||
874 | if (ia->options != NULL) { | |
875 | result = dhc6_parse_prefs(&ia->addrs, packet, | |
876 | ia->options); | |
877 | if (result != ISC_R_SUCCESS) { | |
878 | option_state_dereference(&ia->options, | |
879 | MDL); | |
880 | dfree(ia, MDL); | |
881 | return result; | |
882 | } | |
883 | } | |
884 | ||
885 | *pia = ia; | |
886 | pia = &ia->next; | |
887 | } else { | |
888 | log_error("Invalid IA_PD option cache."); | |
889 | dfree(ia, MDL); | |
890 | if (ds.len != 0) | |
891 | data_string_forget(&ds, MDL); | |
892 | return ISC_R_UNEXPECTED; | |
893 | } | |
894 | } | |
895 | ||
896 | return ISC_R_SUCCESS; | |
897 | } | |
898 | ||
98bd7ca0 DH |
899 | |
900 | static isc_result_t | |
901 | dhc6_parse_addrs(struct dhc6_addr **paddr, struct packet *packet, | |
902 | struct option_state *options) | |
903 | { | |
904 | struct data_string ds; | |
905 | struct option_cache *oc; | |
906 | struct dhc6_addr *addr; | |
907 | ||
908 | memset(&ds, 0, sizeof(ds)); | |
909 | ||
910 | oc = lookup_option(&dhcpv6_universe, options, D6O_IAADDR); | |
911 | for ( ; oc != NULL ; oc = oc->next) { | |
912 | addr = dmalloc(sizeof(*addr), MDL); | |
913 | if (addr == NULL) { | |
914 | log_error("Out of memory allocating " | |
915 | "address structure."); | |
916 | return ISC_R_NOMEMORY; | |
917 | } else if (evaluate_option_cache(&ds, packet, NULL, NULL, | |
918 | options, NULL, &global_scope, | |
919 | oc, MDL) && | |
920 | (ds.len >= 24)) { | |
921 | ||
922 | addr->address.len = 16; | |
923 | memcpy(addr->address.iabuf, ds.data, 16); | |
924 | addr->starts = cur_time; | |
925 | addr->preferred_life = getULong(ds.data + 16); | |
926 | addr->max_life = getULong(ds.data + 20); | |
927 | ||
928 | log_debug("RCV: | | X-- IAADDR %s", | |
929 | piaddr(addr->address)); | |
930 | log_debug("RCV: | | | X-- Preferred lifetime %u.", | |
931 | addr->preferred_life); | |
932 | log_debug("RCV: | | | X-- Max lifetime %u.", | |
933 | addr->max_life); | |
934 | ||
935 | /* RFC 3315 section 22.6 says we must discard | |
936 | * addresses whose pref is later than valid. | |
937 | */ | |
938 | if ((addr->preferred_life > addr->max_life)) { | |
939 | log_debug("RCV: | | | !-- INVALID lifetimes, " | |
940 | "IAADDR discarded. Check your " | |
941 | "server configuration."); | |
942 | dfree(addr, MDL); | |
943 | data_string_forget(&ds, MDL); | |
944 | continue; | |
945 | } | |
946 | ||
947 | /* Fortunately this is the last recursion in the | |
948 | * protocol. | |
949 | */ | |
950 | if (ds.len > 24) { | |
951 | if (!option_state_allocate(&addr->options, | |
952 | MDL)) { | |
953 | log_error("Out of memory allocating " | |
954 | "IAADDR option state."); | |
955 | dfree(addr, MDL); | |
956 | data_string_forget(&ds, MDL); | |
957 | return ISC_R_NOMEMORY; | |
958 | } | |
959 | ||
960 | if (!parse_option_buffer(addr->options, | |
961 | ds.data + 24, | |
962 | ds.len - 24, | |
963 | &dhcpv6_universe)) { | |
964 | log_error("Corrupt IAADDR options."); | |
965 | option_state_dereference(&addr->options, | |
966 | MDL); | |
967 | dfree(addr, MDL); | |
968 | data_string_forget(&ds, MDL); | |
969 | return ISC_R_BADPARSE; | |
970 | } | |
971 | } | |
972 | ||
973 | if (addr->options != NULL) | |
974 | log_debug("RCV: | | | X-- " | |
975 | "[Options]"); | |
976 | ||
977 | data_string_forget(&ds, MDL); | |
978 | ||
979 | *paddr = addr; | |
980 | paddr = &addr->next; | |
981 | } else { | |
982 | log_error("Invalid IAADDR option cache."); | |
983 | dfree(addr, MDL); | |
984 | if (ds.len != 0) | |
985 | data_string_forget(&ds, MDL); | |
986 | return ISC_R_UNEXPECTED; | |
987 | } | |
988 | } | |
989 | ||
990 | return ISC_R_SUCCESS; | |
991 | } | |
992 | ||
1d9774ab FD |
993 | static isc_result_t |
994 | dhc6_parse_prefs(struct dhc6_addr **ppref, struct packet *packet, | |
995 | struct option_state *options) | |
996 | { | |
997 | struct data_string ds; | |
998 | struct option_cache *oc; | |
999 | struct dhc6_addr *pref; | |
1000 | ||
1001 | memset(&ds, 0, sizeof(ds)); | |
1002 | ||
1003 | oc = lookup_option(&dhcpv6_universe, options, D6O_IAPREFIX); | |
1004 | for ( ; oc != NULL ; oc = oc->next) { | |
1005 | pref = dmalloc(sizeof(*pref), MDL); | |
1006 | if (pref == NULL) { | |
1007 | log_error("Out of memory allocating " | |
1008 | "prefix structure."); | |
1009 | return ISC_R_NOMEMORY; | |
1010 | } else if (evaluate_option_cache(&ds, packet, NULL, NULL, | |
1011 | options, NULL, &global_scope, | |
1012 | oc, MDL) && | |
1013 | (ds.len >= 25)) { | |
1014 | ||
1015 | pref->plen = getUChar(ds.data); | |
1016 | pref->address.len = 16; | |
1017 | memcpy(pref->address.iabuf, ds.data + 1, 16); | |
1018 | pref->starts = cur_time; | |
1019 | pref->preferred_life = getULong(ds.data + 17); | |
1020 | pref->max_life = getULong(ds.data + 21); | |
1021 | ||
1022 | log_debug("RCV: | | X-- IAPREFIX %s/%d", | |
1023 | piaddr(pref->address), (int)pref->plen); | |
1024 | log_debug("RCV: | | | X-- Preferred lifetime %u.", | |
1025 | pref->preferred_life); | |
1026 | log_debug("RCV: | | | X-- Max lifetime %u.", | |
1027 | pref->max_life); | |
1028 | ||
1029 | /* Sanity check over the prefix length */ | |
1030 | if ((pref->plen < 4) || (pref->plen > 128)) { | |
1031 | log_debug("RCV: | | | !-- INVALID prefix " | |
1032 | "length, IAPREFIX discarded. " | |
1033 | "Check your server configuration."); | |
1034 | dfree(pref, MDL); | |
1035 | data_string_forget(&ds, MDL); | |
1036 | continue; | |
1037 | } | |
1038 | /* RFC 3315 section 22.6 says we must discard | |
1039 | * prefixes whose pref is later than valid. | |
1040 | */ | |
1041 | if ((pref->preferred_life > pref->max_life)) { | |
1042 | log_debug("RCV: | | | !-- INVALID lifetimes, " | |
1043 | "IAPREFIX discarded. Check your " | |
1044 | "server configuration."); | |
1045 | dfree(pref, MDL); | |
1046 | data_string_forget(&ds, MDL); | |
1047 | continue; | |
1048 | } | |
1049 | ||
1050 | /* Fortunately this is the last recursion in the | |
1051 | * protocol. | |
1052 | */ | |
1053 | if (ds.len > 25) { | |
1054 | if (!option_state_allocate(&pref->options, | |
1055 | MDL)) { | |
1056 | log_error("Out of memory allocating " | |
1057 | "IAPREFIX option state."); | |
1058 | dfree(pref, MDL); | |
1059 | data_string_forget(&ds, MDL); | |
1060 | return ISC_R_NOMEMORY; | |
1061 | } | |
1062 | ||
1063 | if (!parse_option_buffer(pref->options, | |
1064 | ds.data + 25, | |
1065 | ds.len - 25, | |
1066 | &dhcpv6_universe)) { | |
1067 | log_error("Corrupt IAPREFIX options."); | |
1068 | option_state_dereference(&pref->options, | |
1069 | MDL); | |
1070 | dfree(pref, MDL); | |
1071 | data_string_forget(&ds, MDL); | |
1072 | return ISC_R_BADPARSE; | |
1073 | } | |
1074 | } | |
1075 | ||
1076 | if (pref->options != NULL) | |
1077 | log_debug("RCV: | | | X-- " | |
1078 | "[Options]"); | |
1079 | ||
1080 | data_string_forget(&ds, MDL); | |
1081 | ||
1082 | *ppref = pref; | |
1083 | ppref = &pref->next; | |
1084 | } else { | |
1085 | log_error("Invalid IAPREFIX option cache."); | |
1086 | dfree(pref, MDL); | |
1087 | if (ds.len != 0) | |
1088 | data_string_forget(&ds, MDL); | |
1089 | return ISC_R_UNEXPECTED; | |
1090 | } | |
1091 | } | |
1092 | ||
1093 | return ISC_R_SUCCESS; | |
1094 | } | |
1095 | ||
e32529a5 | 1096 | /* Clean up a lease object, deallocate all its parts, and set it to NULL. */ |
98bd7ca0 | 1097 | void |
e32529a5 | 1098 | dhc6_lease_destroy(struct dhc6_lease **src, const char *file, int line) |
98bd7ca0 DH |
1099 | { |
1100 | struct dhc6_ia *ia, *nia; | |
e32529a5 | 1101 | struct dhc6_lease *lease; |
98bd7ca0 | 1102 | |
e32529a5 EH |
1103 | if (src == NULL || *src == NULL) { |
1104 | log_error("Attempt to destroy null lease."); | |
98bd7ca0 | 1105 | return; |
e32529a5 EH |
1106 | } |
1107 | lease = *src; | |
98bd7ca0 DH |
1108 | |
1109 | if (lease->server_id.len != 0) | |
1110 | data_string_forget(&lease->server_id, file, line); | |
1111 | ||
1112 | for (ia = lease->bindings ; ia != NULL ; ia = nia) { | |
1113 | nia = ia->next; | |
1114 | ||
e32529a5 | 1115 | dhc6_ia_destroy(&ia, file, line); |
98bd7ca0 DH |
1116 | } |
1117 | ||
1118 | if (lease->options != NULL) | |
1119 | option_state_dereference(&lease->options, file, line); | |
1120 | ||
1121 | dfree(lease, file, line); | |
e32529a5 | 1122 | *src = NULL; |
98bd7ca0 DH |
1123 | } |
1124 | ||
e32529a5 EH |
1125 | /* |
1126 | * Traverse the addresses list, and destroy their contents, and NULL the | |
1127 | * list pointer. | |
1128 | */ | |
98bd7ca0 | 1129 | static void |
e32529a5 | 1130 | dhc6_ia_destroy(struct dhc6_ia **src, const char *file, int line) |
98bd7ca0 DH |
1131 | { |
1132 | struct dhc6_addr *addr, *naddr; | |
e32529a5 EH |
1133 | struct dhc6_ia *ia; |
1134 | ||
1135 | if (src == NULL || *src == NULL) { | |
1136 | log_error("Attempt to destroy null IA."); | |
1137 | return; | |
1138 | } | |
af9271c5 | 1139 | ia = *src; |
98bd7ca0 DH |
1140 | |
1141 | for (addr = ia->addrs ; addr != NULL ; addr = naddr) { | |
1142 | naddr = addr->next; | |
1143 | ||
1144 | if (addr->options != NULL) | |
1145 | option_state_dereference(&addr->options, file, line); | |
1146 | ||
1147 | dfree(addr, file, line); | |
1148 | } | |
1149 | ||
1150 | if (ia->options != NULL) | |
1151 | option_state_dereference(&ia->options, file, line); | |
1152 | ||
1153 | dfree(ia, file, line); | |
e32529a5 | 1154 | *src = NULL; |
98bd7ca0 DH |
1155 | } |
1156 | ||
1157 | /* For a given lease, insert it into the tail of the lease list. Upon | |
1158 | * finding a duplicate by server id, remove it and take over its position. | |
1159 | */ | |
1160 | static void | |
1161 | insert_lease(struct dhc6_lease **head, struct dhc6_lease *new) | |
1162 | { | |
1163 | while (*head != NULL) { | |
1164 | if ((*head)->server_id.len == new->server_id.len && | |
1165 | memcmp((*head)->server_id.data, new->server_id.data, | |
1166 | new->server_id.len) == 0) { | |
1167 | new->next = (*head)->next; | |
e32529a5 | 1168 | dhc6_lease_destroy(head, MDL); |
98bd7ca0 DH |
1169 | break; |
1170 | } | |
1171 | ||
1172 | head= &(*head)->next; | |
1173 | } | |
1174 | ||
1175 | *head = new; | |
1176 | return; | |
1177 | } | |
1178 | ||
1179 | /* Not really clear what to do here yet. | |
1180 | */ | |
1181 | static int | |
0c20eab3 | 1182 | dhc6_score_lease(struct client_state *client, struct dhc6_lease *lease) |
98bd7ca0 DH |
1183 | { |
1184 | struct dhc6_ia *ia; | |
1185 | struct dhc6_addr *addr; | |
0c20eab3 DH |
1186 | struct option **req; |
1187 | int i; | |
98bd7ca0 DH |
1188 | |
1189 | if (lease->score) | |
1190 | return lease->score; | |
1191 | ||
1192 | lease->score = 1; | |
1193 | ||
0c20eab3 DH |
1194 | /* If this lease lacks a required option, dump it. */ |
1195 | /* XXX: we should be able to cache the failure... */ | |
1196 | req = client->config->required_options; | |
1197 | if (req != NULL) { | |
1198 | for (i = 0 ; req[i] != NULL ; i++) { | |
1199 | if (lookup_option(&dhcpv6_universe, lease->options, | |
1200 | req[i]->code) == NULL) { | |
1201 | lease->score = 0; | |
1202 | return lease->score; | |
1203 | } | |
1204 | } | |
1205 | } | |
1206 | ||
1207 | /* If this lease contains a requested option, improve its | |
1208 | * score. | |
1209 | */ | |
1210 | req = client->config->requested_options; | |
1211 | if (req != NULL) { | |
1212 | for (i = 0 ; req[i] != NULL ; i++) { | |
1213 | if (lookup_option(&dhcpv6_universe, lease->options, | |
1214 | req[i]->code) != NULL) | |
1215 | lease->score++; | |
1216 | } | |
98bd7ca0 | 1217 | } |
98bd7ca0 DH |
1218 | |
1219 | for (ia = lease->bindings ; ia != NULL ; ia = ia->next) { | |
1220 | lease->score += 50; | |
1221 | ||
1222 | for (addr = ia->addrs ; addr != NULL ; addr = addr->next) { | |
1223 | lease->score += 100; | |
1224 | } | |
1225 | } | |
1226 | ||
1227 | return lease->score; | |
1228 | } | |
1229 | ||
1230 | /* start_init6() kicks off the process, transmitting a packet and | |
1231 | * scheduling a retransmission event. | |
1232 | */ | |
1233 | void | |
1234 | start_init6(struct client_state *client) | |
1235 | { | |
be62cf06 FD |
1236 | struct timeval tv; |
1237 | ||
98bd7ca0 DH |
1238 | log_debug("PRC: Soliciting for leases (INIT)."); |
1239 | client->state = S_INIT; | |
1240 | ||
98bd7ca0 | 1241 | /* Initialize timers, RFC3315 section 17.1.2. */ |
be62cf06 FD |
1242 | client->IRT = SOL_TIMEOUT * 100; |
1243 | client->MRT = SOL_MAX_RT * 100; | |
98bd7ca0 DH |
1244 | client->MRC = 0; |
1245 | client->MRD = 0; | |
1246 | ||
1247 | dhc6_retrans_init(client); | |
1248 | /* RFC3315 section 17.1.2 goes out of its way: | |
1249 | * | |
1250 | * Also, the first RT MUST be selected to be strictly greater than IRT | |
1251 | * by choosing RAND to be strictly greater than 0. | |
1252 | */ | |
be62cf06 FD |
1253 | /* if RAND < 0 then RAND = -RAND */ |
1254 | if (client->RT <= client->IRT) | |
1255 | client->RT = client->IRT + (client->IRT - client->RT); | |
1256 | /* if RAND == 0 then RAND = 1 */ | |
98bd7ca0 DH |
1257 | if (client->RT <= client->IRT) |
1258 | client->RT = client->IRT + 1; | |
1259 | ||
1260 | client->v6_handler = init_handler; | |
1261 | ||
1262 | /* RFC3315 section 17.1.2 says we MUST start the first packet | |
1263 | * between 0 and SOL_MAX_DELAY seconds. The good news is | |
1264 | * SOL_MAX_DELAY is 1. | |
1265 | */ | |
be62cf06 FD |
1266 | tv.tv_sec = cur_tv.tv_sec; |
1267 | tv.tv_usec = cur_tv.tv_usec; | |
1268 | tv.tv_usec += (random() % (SOL_MAX_DELAY * 100)) * 10000; | |
1269 | if (tv.tv_usec >= 1000000) { | |
1270 | tv.tv_sec += 1; | |
1271 | tv.tv_usec -= 1000000; | |
1272 | } | |
1273 | add_timeout(&tv, do_init6, client, NULL, NULL); | |
8ea19a71 DH |
1274 | |
1275 | if (nowait) | |
1276 | go_daemon(); | |
98bd7ca0 DH |
1277 | } |
1278 | ||
1279 | /* start_init6() kicks off an "init-reboot" version of the process, at | |
1280 | * startup to find out if old bindings are 'fair' and at runtime whenever | |
1281 | * a link cycles state we'll eventually want to do this. | |
1282 | */ | |
1283 | void | |
1284 | start_confirm6(struct client_state *client) | |
1285 | { | |
be62cf06 FD |
1286 | struct timeval tv; |
1287 | ||
98bd7ca0 DH |
1288 | /* If there is no active lease, there is nothing to check. */ |
1289 | if (client->active_lease == NULL) { | |
1290 | start_init6(client); | |
1291 | return; | |
1292 | } | |
1293 | ||
1294 | log_debug("PRC: Confirming active lease (INIT-REBOOT)."); | |
1295 | client->state = S_REBOOTING; | |
1296 | ||
98bd7ca0 | 1297 | /* Initialize timers, RFC3315 section 17.1.3. */ |
be62cf06 FD |
1298 | client->IRT = CNF_TIMEOUT * 100; |
1299 | client->MRT = CNF_MAX_RT * 100; | |
98bd7ca0 DH |
1300 | client->MRC = 0; |
1301 | client->MRD = CNF_MAX_RD; | |
1302 | ||
1303 | dhc6_retrans_init(client); | |
1304 | ||
1305 | client->v6_handler = reply_handler; | |
1306 | ||
be62cf06 FD |
1307 | /* RFC3315 section 18.1.2 says we MUST start the first packet |
1308 | * between 0 and CNF_MAX_DELAY seconds. The good news is | |
1309 | * CNF_MAX_DELAY is 1. | |
1310 | */ | |
1311 | tv.tv_sec = cur_tv.tv_sec; | |
1312 | tv.tv_usec = cur_tv.tv_usec; | |
1313 | tv.tv_usec += (random() % (CNF_MAX_DELAY * 100)) * 10000; | |
1314 | if (tv.tv_usec >= 1000000) { | |
1315 | tv.tv_sec += 1; | |
1316 | tv.tv_usec -= 1000000; | |
1317 | } | |
1318 | add_timeout(&tv, do_confirm6, client, NULL, NULL); | |
98bd7ca0 DH |
1319 | } |
1320 | ||
1321 | /* do_init6() marshals and transmits a solicit. | |
1322 | */ | |
1323 | void | |
1324 | do_init6(void *input) | |
1325 | { | |
1326 | struct client_state *client; | |
1327 | struct dhc6_ia *old_ia; | |
1328 | struct dhc6_addr *old_addr; | |
1329 | struct data_string ds; | |
1330 | struct data_string ia; | |
1331 | struct data_string addr; | |
be62cf06 | 1332 | struct timeval elapsed, tv; |
98bd7ca0 | 1333 | u_int32_t t1, t2; |
28868515 | 1334 | int idx, len, send_ret; |
98bd7ca0 DH |
1335 | |
1336 | client = input; | |
1337 | ||
1338 | /* In RFC3315 section 17.1.2, the retransmission timer is | |
1339 | * used as the selecting timer. | |
1340 | */ | |
1341 | if (client->advertised_leases != NULL) { | |
1342 | start_selecting6(client); | |
1343 | return; | |
1344 | } | |
1345 | ||
1346 | if ((client->MRC != 0) && (client->txcount > client->MRC)) { | |
1347 | log_info("Max retransmission count exceeded."); | |
1348 | return; | |
1349 | } | |
1350 | ||
be62cf06 FD |
1351 | /* |
1352 | * Start_time starts at the first transmission. | |
1353 | */ | |
1354 | if (client->txcount == 0) { | |
1355 | client->start_time.tv_sec = cur_tv.tv_sec; | |
1356 | client->start_time.tv_usec = cur_tv.tv_usec; | |
1357 | } | |
1358 | ||
1359 | /* elapsed = cur - start */ | |
1360 | elapsed.tv_sec = cur_tv.tv_sec - client->start_time.tv_sec; | |
1361 | elapsed.tv_usec = cur_tv.tv_usec - client->start_time.tv_usec; | |
1362 | if (elapsed.tv_usec < 0) { | |
1363 | elapsed.tv_sec -= 1; | |
1364 | elapsed.tv_usec += 1000000; | |
1365 | } | |
1366 | if ((client->MRD != 0) && (elapsed.tv_sec > client->MRD)) { | |
98bd7ca0 DH |
1367 | log_info("Max retransmission duration exceeded."); |
1368 | return; | |
1369 | } | |
1370 | ||
1371 | memset(&ds, 0, sizeof(ds)); | |
1372 | if (!buffer_allocate(&ds.buffer, 4, MDL)) { | |
1373 | log_error("Unable to allocate memory for SOLICIT."); | |
1374 | return; | |
1375 | } | |
1376 | ds.data = ds.buffer->data; | |
1377 | ds.len = 4; | |
1378 | ||
1379 | ds.buffer->data[0] = DHCPV6_SOLICIT; | |
1380 | memcpy(ds.buffer->data + 1, client->dhcpv6_transaction_id, 3); | |
1381 | ||
1382 | /* Form an elapsed option. */ | |
be62cf06 FD |
1383 | /* Maximum value is 65535 1/100s coded as 0xffff. */ |
1384 | if ((elapsed.tv_sec < 0) || (elapsed.tv_sec > 655) || | |
1385 | ((elapsed.tv_sec == 655) && (elapsed.tv_usec > 350000))) { | |
98bd7ca0 | 1386 | client->elapsed = 0xffff; |
be62cf06 FD |
1387 | } else { |
1388 | client->elapsed = elapsed.tv_sec * 100; | |
1389 | client->elapsed += elapsed.tv_usec / 10000; | |
1390 | } | |
98bd7ca0 | 1391 | |
be62cf06 FD |
1392 | if (client->elapsed == 0) |
1393 | log_debug("XMT: Forming Solicit, 0 ms elapsed."); | |
1394 | else | |
1395 | log_debug("XMT: Forming Solicit, %u0 ms elapsed.", | |
1396 | (unsigned)client->elapsed); | |
98bd7ca0 DH |
1397 | |
1398 | client->elapsed = htons(client->elapsed); | |
1399 | ||
1400 | make_client6_options(client, &client->sent_options, NULL, | |
1401 | DHCPV6_SOLICIT); | |
1402 | ||
1403 | /* Fetch any configured 'sent' options (includes DUID) in wire format. | |
1404 | */ | |
1405 | dhcpv6_universe.encapsulate(&ds, NULL, NULL, client, | |
1406 | NULL, client->sent_options, &global_scope, | |
1407 | &dhcpv6_universe); | |
1408 | ||
6d7f9584 FD |
1409 | /* Use a specific handler with rapid-commit. |
1410 | */ | |
1411 | if (lookup_option(&dhcpv6_universe, client->sent_options, | |
1412 | D6O_RAPID_COMMIT) != NULL) { | |
1413 | client->v6_handler = rapid_commit_handler; | |
1414 | } | |
1415 | ||
98bd7ca0 DH |
1416 | /* Append an IA_NA. */ |
1417 | /* XXX: maybe the IA_NA('s) should be put into the sent_options | |
1418 | * cache. They'd have to be pulled down as they also contain | |
1419 | * different option caches in the same universe... | |
1420 | */ | |
1421 | memset(&ia, 0, sizeof(ia)); | |
1422 | if (!buffer_allocate(&ia.buffer, 12, MDL)) { | |
1423 | log_error("Unable to allocate memory for IA_NA."); | |
1424 | data_string_forget(&ds, MDL); | |
1425 | return; | |
1426 | } | |
1427 | ia.data = ia.buffer->data; | |
1428 | ia.len = 12; | |
1429 | ||
1430 | /* A simple IAID is the last 4 bytes of the hardware address. */ | |
1431 | if (client->interface->hw_address.hlen > 4) { | |
1432 | idx = client->interface->hw_address.hlen - 4; | |
1433 | len = 4; | |
1434 | } else { | |
1435 | idx = 0; | |
1436 | len = client->interface->hw_address.hlen; | |
1437 | } | |
1438 | memcpy(ia.buffer->data, client->interface->hw_address.hbuf + idx, len); | |
1439 | ||
1440 | t1 = client->config->requested_lease / 2; | |
1441 | t2 = t1 + (t1 / 2); | |
1442 | putULong(ia.buffer->data + 4, t1); | |
1443 | putULong(ia.buffer->data + 8, t2); | |
1444 | ||
1445 | log_debug("XMT: X-- IA_NA %s", print_hex_1(4, ia.buffer->data, 55)); | |
1446 | log_debug("XMT: | X-- Request renew in +%u", (unsigned)t1); | |
1447 | log_debug("XMT: | X-- Request rebind in +%u", (unsigned)t2); | |
1448 | ||
1449 | if ((client->active_lease != NULL) && | |
1d9774ab FD |
1450 | ((old_ia = find_ia_na(client->active_lease->bindings, |
1451 | (char *)ia.buffer->data)) != NULL)) { | |
98bd7ca0 DH |
1452 | /* For each address in the old IA, request a binding. */ |
1453 | memset(&addr, 0, sizeof(addr)); | |
1454 | for (old_addr = old_ia->addrs ; old_addr != NULL ; | |
1455 | old_addr = old_addr->next) { | |
1456 | if (old_addr->address.len != 16) { | |
1457 | log_error("Invalid IPv6 address length %d. " | |
1458 | "Ignoring. (%s:%d)", | |
1459 | old_addr->address.len, MDL); | |
1460 | continue; | |
1461 | } | |
1462 | ||
1463 | if (!buffer_allocate(&addr.buffer, 24, MDL)) { | |
1464 | log_error("Unable to allocate memory for " | |
1465 | "IAADDR."); | |
1466 | data_string_forget(&ia, MDL); | |
1467 | data_string_forget(&ds, MDL); | |
1468 | return; | |
1469 | } | |
1470 | addr.data = addr.buffer->data; | |
1471 | addr.len = 24; | |
1472 | ||
1473 | memcpy(addr.buffer->data, old_addr->address.iabuf, 16); | |
1474 | ||
1475 | t1 = client->config->requested_lease; | |
1476 | t2 = t1 + (t1 / 2); | |
1477 | putULong(addr.buffer->data + 16, t1); | |
1478 | putULong(addr.buffer->data + 20, t2); | |
1479 | ||
1480 | log_debug("XMT: | X-- Request address %s.", | |
1481 | piaddr(old_addr->address)); | |
1482 | log_debug("XMT: | | X-- Request preferred in +%u", | |
1483 | (unsigned)t1); | |
1484 | log_debug("XMT: | | X-- Request valid in +%u", | |
1485 | (unsigned)t2); | |
1486 | ||
1487 | append_option(&ia, &dhcpv6_universe, iaaddr_option, | |
1488 | &addr); | |
1489 | ||
1490 | data_string_forget(&addr, MDL); | |
1491 | } | |
1492 | } | |
1493 | ||
1494 | append_option(&ds, &dhcpv6_universe, ia_na_option, &ia); | |
1495 | data_string_forget(&ia, MDL); | |
1496 | ||
1497 | /* Transmit and wait. */ | |
1498 | ||
be62cf06 | 1499 | log_info("XMT: Solicit on %s, interval %ld0ms.", |
98bd7ca0 | 1500 | client->name ? client->name : client->interface->name, |
9aa669fc | 1501 | (long int)client->RT); |
98bd7ca0 DH |
1502 | |
1503 | send_ret = send_packet6(client->interface, | |
1504 | ds.data, ds.len, &DHCPv6DestAddr); | |
1505 | if (send_ret != ds.len) { | |
1506 | log_error("dhc6: send_packet6() sent %d of %d bytes", | |
1507 | send_ret, ds.len); | |
1508 | } | |
1509 | ||
1510 | data_string_forget(&ds, MDL); | |
1511 | ||
be62cf06 FD |
1512 | /* Wait RT */ |
1513 | tv.tv_sec = cur_tv.tv_sec + client->RT / 100; | |
1514 | tv.tv_usec = cur_tv.tv_usec + (client->RT % 100) * 10000; | |
1515 | if (tv.tv_usec >= 1000000) { | |
1516 | tv.tv_sec += 1; | |
1517 | tv.tv_usec -= 1000000; | |
1518 | } | |
1519 | add_timeout(&tv, do_init6, client, NULL, NULL); | |
98bd7ca0 DH |
1520 | |
1521 | dhc6_retrans_advance(client); | |
1522 | } | |
1523 | ||
1524 | /* do_confirm6() creates a Confirm packet and transmits it. This function | |
1525 | * is called on every timeout to (re)transmit. | |
1526 | */ | |
1527 | void | |
1528 | do_confirm6(void *input) | |
1529 | { | |
1530 | struct client_state *client; | |
98bd7ca0 | 1531 | struct data_string ds; |
98bd7ca0 | 1532 | int send_ret; |
be62cf06 | 1533 | struct timeval elapsed, tv; |
98bd7ca0 DH |
1534 | |
1535 | client = input; | |
1536 | ||
1537 | if (client->active_lease == NULL) | |
1538 | log_fatal("Impossible condition at %s:%d.", MDL); | |
1539 | ||
1540 | /* In section 17.1.3, it is said: | |
1541 | * | |
1542 | * If the client receives no responses before the message | |
1543 | * transmission process terminates, as described in section 14, | |
1544 | * the client SHOULD continue to use any IP addresses, using the | |
1545 | * last known lifetimes for those addresses, and SHOULD continue | |
1546 | * to use any other previously obtained configuration parameters. | |
1547 | * | |
1548 | * So if confirm times out, we go active. | |
1549 | * | |
1550 | * XXX: Should we reduce all IA's t1 to 0, so that we renew and | |
1551 | * stick there until we get a reply? | |
1552 | */ | |
1553 | ||
1554 | if ((client->MRC != 0) && (client->txcount > client->MRC)) { | |
1555 | log_info("Max retransmission count exceeded."); | |
1556 | start_bound(client); | |
1557 | return; | |
1558 | } | |
1559 | ||
be62cf06 FD |
1560 | /* |
1561 | * Start_time starts at the first transmission. | |
1562 | */ | |
1563 | if (client->txcount == 0) { | |
1564 | client->start_time.tv_sec = cur_tv.tv_sec; | |
1565 | client->start_time.tv_usec = cur_tv.tv_usec; | |
1566 | } | |
1567 | ||
1568 | /* elapsed = cur - start */ | |
1569 | elapsed.tv_sec = cur_tv.tv_sec - client->start_time.tv_sec; | |
1570 | elapsed.tv_usec = cur_tv.tv_usec - client->start_time.tv_usec; | |
1571 | if (elapsed.tv_usec < 0) { | |
1572 | elapsed.tv_sec -= 1; | |
1573 | elapsed.tv_usec += 1000000; | |
1574 | } | |
1575 | if ((client->MRD != 0) && (elapsed.tv_sec > client->MRD)) { | |
98bd7ca0 DH |
1576 | log_info("Max retransmission duration exceeded."); |
1577 | start_bound(client); | |
1578 | return; | |
1579 | } | |
1580 | ||
1581 | memset(&ds, 0, sizeof(ds)); | |
1582 | if (!buffer_allocate(&ds.buffer, 4, MDL)) { | |
1583 | log_error("Unable to allocate memory for Confirm."); | |
1584 | return; | |
1585 | } | |
1586 | ds.data = ds.buffer->data; | |
1587 | ds.len = 4; | |
1588 | ||
1589 | ds.buffer->data[0] = DHCPV6_CONFIRM; | |
1590 | memcpy(ds.buffer->data + 1, client->dhcpv6_transaction_id, 3); | |
1591 | ||
1592 | /* Form an elapsed option. */ | |
be62cf06 FD |
1593 | /* Maximum value is 65535 1/100s coded as 0xffff. */ |
1594 | if ((elapsed.tv_sec < 0) || (elapsed.tv_sec > 655) || | |
1595 | ((elapsed.tv_sec == 655) && (elapsed.tv_usec > 350000))) { | |
98bd7ca0 | 1596 | client->elapsed = 0xffff; |
be62cf06 FD |
1597 | } else { |
1598 | client->elapsed = elapsed.tv_sec * 100; | |
1599 | client->elapsed += elapsed.tv_usec / 10000; | |
1600 | } | |
98bd7ca0 | 1601 | |
be62cf06 FD |
1602 | if (client->elapsed == 0) |
1603 | log_debug("XMT: Forming Confirm, 0 ms elapsed."); | |
1604 | else | |
1605 | log_debug("XMT: Forming Confirm, %u0 ms elapsed.", | |
1606 | (unsigned)client->elapsed); | |
98bd7ca0 DH |
1607 | |
1608 | client->elapsed = htons(client->elapsed); | |
1609 | ||
1610 | make_client6_options(client, &client->sent_options, | |
1611 | client->active_lease, DHCPV6_CONFIRM); | |
1612 | ||
1613 | /* Fetch any configured 'sent' options (includes DUID') in wire format. | |
1614 | */ | |
1615 | dhcpv6_universe.encapsulate(&ds, NULL, NULL, client, NULL, | |
1616 | client->sent_options, &global_scope, | |
1617 | &dhcpv6_universe); | |
1618 | ||
1619 | /* Append IA's. */ | |
1d9774ab FD |
1620 | if (dhc6_add_ia_na(client, &ds, client->active_lease, |
1621 | DHCPV6_CONFIRM) != ISC_R_SUCCESS) { | |
af5fa176 EH |
1622 | data_string_forget(&ds, MDL); |
1623 | return; | |
1624 | } | |
98bd7ca0 | 1625 | |
af5fa176 | 1626 | /* Transmit and wait. */ |
98bd7ca0 | 1627 | |
be62cf06 | 1628 | log_info("XMT: Confirm on %s, interval %ld0ms.", |
af5fa176 | 1629 | client->name ? client->name : client->interface->name, |
9aa669fc | 1630 | (long int)client->RT); |
98bd7ca0 | 1631 | |
af5fa176 EH |
1632 | send_ret = send_packet6(client->interface, ds.data, ds.len, |
1633 | &DHCPv6DestAddr); | |
1634 | if (send_ret != ds.len) { | |
1635 | log_error("dhc6: sendpacket6() sent %d of %d bytes", | |
1636 | send_ret, ds.len); | |
1637 | } | |
98bd7ca0 | 1638 | |
af5fa176 | 1639 | data_string_forget(&ds, MDL); |
98bd7ca0 | 1640 | |
be62cf06 FD |
1641 | /* Wait RT */ |
1642 | tv.tv_sec = cur_tv.tv_sec + client->RT / 100; | |
1643 | tv.tv_usec = cur_tv.tv_usec + (client->RT % 100) * 10000; | |
1644 | if (tv.tv_usec >= 1000000) { | |
1645 | tv.tv_sec += 1; | |
1646 | tv.tv_usec -= 1000000; | |
1647 | } | |
1648 | add_timeout(&tv, do_confirm6, client, NULL, NULL); | |
98bd7ca0 | 1649 | |
af5fa176 EH |
1650 | dhc6_retrans_advance(client); |
1651 | } | |
98bd7ca0 | 1652 | |
af5fa176 EH |
1653 | /* |
1654 | * Release addresses. | |
1655 | */ | |
1656 | void | |
1657 | start_release6(struct client_state *client) | |
1658 | { | |
af5fa176 EH |
1659 | /* Cancel any pending transmissions */ |
1660 | cancel_timeout(do_confirm6, client); | |
1661 | cancel_timeout(do_select6, client); | |
1662 | cancel_timeout(do_refresh6, client); | |
1663 | cancel_timeout(do_release6, client); | |
1664 | client->state = S_STOPPED; | |
1665 | ||
1666 | /* | |
1667 | * It is written: "The client MUST NOT use any of the addresses it | |
1668 | * is releasing as the source address in the Release message or in | |
1669 | * any subsequently transmitted message." So unconfigure now. | |
1670 | */ | |
1671 | unconfigure6(client, "RELEASE6"); | |
1672 | ||
af5fa176 | 1673 | /* Set timers per RFC3315 section 18.1.1. */ |
be62cf06 | 1674 | client->IRT = REL_TIMEOUT * 100; |
af5fa176 | 1675 | client->MRT = 0; |
be62cf06 | 1676 | client->MRC = REL_MAX_RC * 100; |
af5fa176 | 1677 | client->MRD = 0; |
98bd7ca0 | 1678 | |
af5fa176 EH |
1679 | dhc6_retrans_init(client); |
1680 | client->v6_handler = reply_handler; | |
1681 | ||
1682 | /* ("re")transmit the first packet. */ | |
1683 | do_release6(client); | |
1684 | } | |
1685 | /* | |
1686 | * do_release6() creates a Release packet and transmits it. | |
1687 | */ | |
1688 | static void | |
1689 | do_release6(void *input) | |
1690 | { | |
1691 | struct sockaddr_in6 unicast, *dest_addr = &DHCPv6DestAddr; | |
1692 | struct client_state *client; | |
af5fa176 | 1693 | struct data_string ds; |
af5fa176 | 1694 | struct option_cache *oc; |
af5fa176 | 1695 | int send_ret; |
be62cf06 | 1696 | struct timeval tv; |
af5fa176 EH |
1697 | |
1698 | client = input; | |
1699 | ||
1700 | if (client->active_lease == NULL) | |
96b620e5 | 1701 | return; |
af5fa176 EH |
1702 | |
1703 | if ((client->MRC != 0) && (client->txcount > client->MRC)) { | |
1704 | log_info("Max retransmission count exceeded."); | |
1705 | return; | |
1706 | } | |
1707 | ||
be62cf06 FD |
1708 | /* |
1709 | * Start_time starts at the first transmission. | |
1710 | */ | |
1711 | if (client->txcount == 0) { | |
1712 | client->start_time.tv_sec = cur_tv.tv_sec; | |
1713 | client->start_time.tv_usec = cur_tv.tv_usec; | |
1714 | } | |
1715 | ||
af5fa176 EH |
1716 | /* |
1717 | * Check whether the server has sent a unicast option; if so, we can | |
1718 | * use the address it specified. | |
1719 | */ | |
06eb8bab SK |
1720 | oc = lookup_option(&dhcpv6_universe, |
1721 | client->active_lease->options, D6O_UNICAST); | |
af5fa176 | 1722 | if (oc && evaluate_option_cache(&ds, NULL, NULL, NULL, |
06eb8bab SK |
1723 | client->active_lease->options, |
1724 | NULL, &global_scope, oc, MDL)) { | |
af5fa176 EH |
1725 | if (ds.len < 16) { |
1726 | log_error("Invalid unicast option length %d.", ds.len); | |
1727 | } else { | |
1728 | memset(&unicast, 0, sizeof(DHCPv6DestAddr)); | |
1729 | unicast.sin6_family = AF_INET6; | |
1730 | unicast.sin6_port = remote_port; | |
1731 | memcpy(&unicast.sin6_addr, ds.data, 16); | |
1732 | dest_addr = &unicast; | |
98bd7ca0 DH |
1733 | } |
1734 | ||
af5fa176 EH |
1735 | data_string_forget(&ds, MDL); |
1736 | } | |
98bd7ca0 | 1737 | |
af5fa176 EH |
1738 | memset(&ds, 0, sizeof(ds)); |
1739 | if (!buffer_allocate(&ds.buffer, 4, MDL)) { | |
1740 | log_error("Unable to allocate memory for Release."); | |
1741 | return; | |
98bd7ca0 DH |
1742 | } |
1743 | ||
af5fa176 EH |
1744 | ds.data = ds.buffer->data; |
1745 | ds.len = 4; | |
1746 | ds.buffer->data[0] = DHCPV6_RELEASE; | |
1747 | memcpy(ds.buffer->data + 1, client->dhcpv6_transaction_id, 3); | |
98bd7ca0 | 1748 | |
af5fa176 EH |
1749 | log_debug("XMT: Forming Release."); |
1750 | make_client6_options(client, &client->sent_options, | |
1751 | client->active_lease, DHCPV6_RELEASE); | |
1752 | dhcpv6_universe.encapsulate(&ds, NULL, NULL, client, NULL, | |
1753 | client->sent_options, &global_scope, | |
1754 | &dhcpv6_universe); | |
1755 | ||
1756 | /* Append IA's. */ | |
1d9774ab FD |
1757 | if (dhc6_add_ia_na(client, &ds, client->active_lease, |
1758 | DHCPV6_RELEASE) != ISC_R_SUCCESS) { | |
af5fa176 EH |
1759 | data_string_forget(&ds, MDL); |
1760 | return; | |
1761 | } | |
1762 | ||
1763 | /* Transmit and wait. */ | |
be62cf06 | 1764 | log_info("XMT: Release on %s, interval %ld0ms.", |
98bd7ca0 | 1765 | client->name ? client->name : client->interface->name, |
9aa669fc | 1766 | (long int)client->RT); |
98bd7ca0 DH |
1767 | |
1768 | send_ret = send_packet6(client->interface, ds.data, ds.len, | |
1769 | &DHCPv6DestAddr); | |
1770 | if (send_ret != ds.len) { | |
1771 | log_error("dhc6: sendpacket6() sent %d of %d bytes", | |
1772 | send_ret, ds.len); | |
1773 | } | |
1774 | ||
1775 | data_string_forget(&ds, MDL); | |
1776 | ||
be62cf06 FD |
1777 | /* Wait RT */ |
1778 | tv.tv_sec = cur_tv.tv_sec + client->RT / 100; | |
1779 | tv.tv_usec = cur_tv.tv_usec + (client->RT % 100) * 10000; | |
1780 | if (tv.tv_usec >= 1000000) { | |
1781 | tv.tv_sec += 1; | |
1782 | tv.tv_usec -= 1000000; | |
1783 | } | |
1784 | add_timeout(&tv, do_release6, client, NULL, NULL); | |
98bd7ca0 DH |
1785 | dhc6_retrans_advance(client); |
1786 | } | |
1787 | ||
1788 | /* status_log() just puts a status code into displayable form and logs it | |
1789 | * to info level. | |
1790 | */ | |
1791 | static void | |
06eb8bab | 1792 | status_log(int code, const char *scope, const char *additional, int len) |
98bd7ca0 | 1793 | { |
06eb8bab | 1794 | const char *msg = NULL; |
98bd7ca0 DH |
1795 | |
1796 | switch(code) { | |
1797 | case STATUS_Success: | |
e32529a5 | 1798 | msg = "Success"; |
98bd7ca0 DH |
1799 | break; |
1800 | ||
1801 | case STATUS_UnspecFail: | |
1802 | msg = "UnspecFail"; | |
1803 | break; | |
1804 | case STATUS_NoAddrsAvail: | |
1805 | msg = "NoAddrsAvail"; | |
1806 | break; | |
1807 | ||
1808 | case STATUS_NoBinding: | |
1809 | msg = "NoBinding"; | |
1810 | break; | |
1811 | ||
1812 | case STATUS_NotOnLink: | |
1813 | msg = "NotOnLink"; | |
1814 | break; | |
1815 | ||
1816 | case STATUS_UseMulticast: | |
1817 | msg = "UseMulticast"; | |
1818 | break; | |
1819 | ||
1820 | default: | |
1821 | msg = "UNKNOWN"; | |
1822 | break; | |
1823 | } | |
1824 | ||
1825 | if (len > 0) | |
1826 | log_info("%s status code %s: %s", scope, msg, | |
06eb8bab SK |
1827 | print_hex_1(len, |
1828 | (const unsigned char *)additional, 50)); | |
98bd7ca0 DH |
1829 | else |
1830 | log_info("%s status code %s.", scope, msg); | |
1831 | } | |
1832 | ||
1833 | /* Acquire a status code. | |
1834 | */ | |
1835 | static isc_result_t | |
1836 | dhc6_get_status_code(struct option_state *options, unsigned *code, | |
1837 | struct data_string *msg) | |
1838 | { | |
1839 | struct option_cache *oc; | |
1840 | struct data_string ds; | |
1841 | isc_result_t rval = ISC_R_SUCCESS; | |
1842 | ||
1843 | if ((options == NULL) || (code == NULL)) | |
1844 | return ISC_R_INVALIDARG; | |
1845 | ||
1846 | if ((msg != NULL) && (msg->len != 0)) | |
1847 | return ISC_R_INVALIDARG; | |
1848 | ||
1849 | memset(&ds, 0, sizeof(ds)); | |
1850 | ||
1851 | /* Assume success if there is no option. */ | |
1852 | *code = STATUS_Success; | |
1853 | ||
1854 | oc = lookup_option(&dhcpv6_universe, options, D6O_STATUS_CODE); | |
1855 | if ((oc != NULL) && | |
1856 | evaluate_option_cache(&ds, NULL, NULL, NULL, options, | |
1857 | NULL, &global_scope, oc, MDL)) { | |
1858 | if (ds.len < 2) { | |
1859 | log_error("Invalid status code length %d.", ds.len); | |
1860 | rval = ISC_R_FORMERR; | |
1861 | } else | |
1862 | *code = getUShort(ds.data); | |
1863 | ||
1864 | if ((msg != NULL) && (ds.len > 2)) { | |
1865 | data_string_copy(msg, &ds, MDL); | |
1866 | msg->data += 2; | |
1867 | msg->len -= 2; | |
1868 | } | |
1869 | ||
1870 | data_string_forget(&ds, MDL); | |
1871 | return rval; | |
1872 | } | |
1873 | ||
1874 | return ISC_R_NOTFOUND; | |
1875 | } | |
1876 | ||
1877 | /* Look at status codes in an advertise, and reform the return value. | |
1878 | */ | |
1879 | static isc_result_t | |
1880 | dhc6_check_status(isc_result_t rval, struct option_state *options, | |
06eb8bab | 1881 | const char *scope, unsigned *code) |
98bd7ca0 DH |
1882 | { |
1883 | struct data_string msg; | |
1884 | isc_result_t status; | |
1885 | ||
1886 | if ((scope == NULL) || (code == NULL)) | |
1887 | return ISC_R_INVALIDARG; | |
1888 | ||
1889 | /* If we don't find a code, we assume success. */ | |
1890 | *code = STATUS_Success; | |
1891 | ||
1892 | /* If there is no options cache, then there is no code. */ | |
1893 | if (options != NULL) { | |
1894 | memset(&msg, 0, sizeof(msg)); | |
1895 | status = dhc6_get_status_code(options, code, &msg); | |
1896 | ||
1897 | if (status == ISC_R_SUCCESS) { | |
28868515 | 1898 | status_log(*code, scope, (char *)msg.data, msg.len); |
98bd7ca0 DH |
1899 | data_string_forget(&msg, MDL); |
1900 | ||
1901 | if (*code != STATUS_Success) | |
1902 | rval = ISC_R_FAILURE; | |
1903 | ||
1904 | } else if (status != ISC_R_NOTFOUND) | |
1905 | rval = status; | |
1906 | } | |
1907 | ||
1908 | return rval; | |
1909 | } | |
1910 | ||
1911 | /* Look in the packet, any IA's, and any IAADDR's within those IA's to find | |
1912 | * status code options that are not SUCCESS. | |
1913 | */ | |
1914 | static isc_result_t | |
1915 | dhc6_check_advertise(struct dhc6_lease *lease) | |
1916 | { | |
1917 | struct dhc6_ia *ia; | |
1918 | struct dhc6_addr *addr; | |
1919 | isc_result_t rval = ISC_R_SUCCESS; | |
1920 | int have_addrs = ISC_FALSE; | |
1921 | unsigned code; | |
1d9774ab | 1922 | const char *scope; |
98bd7ca0 DH |
1923 | |
1924 | rval = dhc6_check_status(rval, lease->options, "message", &code); | |
1925 | ||
1926 | for (ia = lease->bindings ; ia != NULL ; ia = ia->next) { | |
1d9774ab FD |
1927 | switch (ia->ia_type) { |
1928 | case D6O_IA_NA: | |
1929 | default: | |
1930 | scope = "IA_NA"; | |
1931 | break; | |
1932 | case D6O_IA_TA: | |
1933 | scope = "IA_TA"; | |
1934 | break; | |
1935 | case D6O_IA_PD: | |
1936 | scope = "IA_PD"; | |
1937 | break; | |
1938 | } | |
1939 | rval = dhc6_check_status(rval, ia->options, scope, &code); | |
98bd7ca0 DH |
1940 | |
1941 | for (addr = ia->addrs ; addr != NULL ; addr = addr->next) { | |
1d9774ab FD |
1942 | if (ia->ia_type != D6O_IA_PD) |
1943 | scope = "IAADDR"; | |
1944 | else | |
1945 | scope = "IAPREFIX"; | |
98bd7ca0 | 1946 | rval = dhc6_check_status(rval, addr->options, |
1d9774ab | 1947 | scope, &code); |
98bd7ca0 DH |
1948 | have_addrs = ISC_TRUE; |
1949 | } | |
1950 | } | |
1951 | ||
1952 | if (have_addrs != ISC_TRUE) | |
1953 | rval = ISC_R_ADDRNOTAVAIL; | |
1954 | ||
1955 | return rval; | |
1956 | } | |
1957 | ||
6d7f9584 FD |
1958 | /* status code <-> action matrix for the client in INIT state |
1959 | * (rapid/commit). Returns always false as no action is defined. | |
1960 | */ | |
1961 | static isc_boolean_t | |
1962 | dhc6_init_action(struct client_state *client, isc_result_t rval, | |
1963 | unsigned code) | |
1964 | { | |
1965 | if (client == NULL) | |
1966 | return ISC_R_INVALIDARG; | |
1967 | ||
1968 | if (rval == ISC_R_SUCCESS) | |
1969 | return ISC_FALSE; | |
1970 | ||
1971 | /* No possible action in any case... */ | |
1972 | return ISC_FALSE; | |
1973 | } | |
1974 | ||
98bd7ca0 DH |
1975 | /* status code <-> action matrix for the client in SELECT state |
1976 | * (request/reply). Returns true if action was taken (and the | |
1977 | * packet should be ignored), or false if no action was taken. | |
1978 | */ | |
1979 | static isc_boolean_t | |
1980 | dhc6_select_action(struct client_state *client, isc_result_t rval, | |
1981 | unsigned code) | |
1982 | { | |
1983 | struct dhc6_lease *lease; | |
1984 | ||
1985 | if (client == NULL) | |
1986 | return ISC_R_INVALIDARG; | |
1987 | ||
1988 | if (rval == ISC_R_SUCCESS) | |
1989 | return ISC_FALSE; | |
1990 | ||
1991 | switch (code) { | |
1992 | /* We may have an earlier failure status code (so no | |
1993 | * success rval), and a success code now. This | |
1994 | * doesn't upgrade the rval to success, but it does | |
1995 | * mean we take no action here. | |
1996 | */ | |
1997 | case STATUS_Success: | |
1998 | /* Gimpy server, or possibly an attacker. */ | |
1999 | case STATUS_NoBinding: | |
2000 | case STATUS_UseMulticast: | |
2001 | /* Take no action. */ | |
2002 | return ISC_FALSE; | |
2003 | ||
2004 | /* If the server can't deal with us, either try the | |
2005 | * next advertised server, or continue retrying if there | |
2006 | * weren't any. | |
2007 | */ | |
2008 | default: | |
2009 | case STATUS_UnspecFail: | |
2010 | if (client->advertised_leases != NULL) { | |
e32529a5 | 2011 | dhc6_lease_destroy(&client->selected_lease, MDL); |
98bd7ca0 DH |
2012 | client->selected_lease = NULL; |
2013 | ||
2014 | start_selecting6(client); | |
2015 | ||
2016 | break; | |
2017 | } else /* Take no action - continue to retry. */ | |
2018 | return ISC_FALSE; | |
2019 | ||
2020 | /* If the server has no addresses, try other servers if | |
2021 | * we got some, otherwise go to INIT to hope for more | |
2022 | * servers. | |
2023 | */ | |
2024 | case STATUS_NoAddrsAvail: | |
2025 | if (client->state == S_REBOOTING) | |
2026 | return ISC_FALSE; | |
2027 | ||
2028 | if (client->selected_lease == NULL) | |
2029 | log_fatal("Impossible case at %s:%d.", MDL); | |
2030 | ||
e32529a5 | 2031 | dhc6_lease_destroy(&client->selected_lease, MDL); |
98bd7ca0 DH |
2032 | client->selected_lease = NULL; |
2033 | ||
2034 | if (client->advertised_leases != NULL) | |
2035 | start_selecting6(client); | |
2036 | else | |
2037 | start_init6(client); | |
2038 | ||
2039 | break; | |
2040 | ||
2041 | /* If we got a NotOnLink from a Confirm, then we're not | |
2042 | * on link. Kill the old-active binding and start over. | |
2043 | * | |
2044 | * If we got a NotOnLink from our Request, something weird | |
2045 | * happened. Start over from scratch anyway. | |
2046 | */ | |
2047 | case STATUS_NotOnLink: | |
2048 | if (client->state == S_REBOOTING) { | |
2049 | if (client->active_lease == NULL) | |
2050 | log_fatal("Impossible case at %s:%d.", MDL); | |
2051 | ||
e32529a5 | 2052 | dhc6_lease_destroy(&client->active_lease, MDL); |
98bd7ca0 DH |
2053 | } else { |
2054 | if (client->selected_lease == NULL) | |
2055 | log_fatal("Impossible case at %s:%d.", MDL); | |
2056 | ||
e32529a5 | 2057 | dhc6_lease_destroy(&client->selected_lease, MDL); |
98bd7ca0 DH |
2058 | client->selected_lease = NULL; |
2059 | ||
2060 | while (client->advertised_leases != NULL) { | |
2061 | lease = client->advertised_leases; | |
2062 | client->advertised_leases = lease->next; | |
2063 | ||
e32529a5 | 2064 | dhc6_lease_destroy(&lease, MDL); |
98bd7ca0 DH |
2065 | } |
2066 | } | |
2067 | ||
2068 | start_init6(client); | |
2069 | break; | |
2070 | } | |
2071 | ||
2072 | return ISC_TRUE; | |
2073 | } | |
2074 | ||
2075 | static void | |
2076 | dhc6_withdraw_lease(struct client_state *client) | |
2077 | { | |
2078 | struct dhc6_ia *ia; | |
2079 | struct dhc6_addr *addr; | |
98bd7ca0 DH |
2080 | |
2081 | if ((client == NULL) || (client->active_lease == NULL)) | |
2082 | return; | |
2083 | ||
2084 | for (ia = client->active_lease->bindings ; ia != NULL ; | |
2085 | ia = ia->next) { | |
2086 | for (addr = ia->addrs ; addr != NULL ; addr = addr->next) { | |
2087 | addr->max_life = addr->preferred_life = 0; | |
2088 | } | |
2089 | } | |
2090 | ||
2091 | /* Perform expiry. */ | |
2092 | do_expire(client); | |
2093 | } | |
2094 | ||
2095 | /* status code <-> action matrix for the client in BOUND state | |
2096 | * (request/reply). Returns true if action was taken (and the | |
2097 | * packet should be ignored), or false if no action was taken. | |
2098 | */ | |
2099 | static isc_boolean_t | |
2100 | dhc6_reply_action(struct client_state *client, isc_result_t rval, | |
2101 | unsigned code) | |
2102 | { | |
2103 | ||
2104 | if (client == NULL) | |
2105 | return ISC_R_INVALIDARG; | |
2106 | ||
2107 | if (rval == ISC_R_SUCCESS) | |
2108 | return ISC_FALSE; | |
2109 | ||
2110 | switch (code) { | |
2111 | /* It's possible an earlier status code set rval to a failure | |
2112 | * code, and we've encountered a later success. | |
2113 | */ | |
2114 | case STATUS_Success: | |
2115 | /* In "refreshes" (where we get replies), we probably | |
2116 | * still have a valid lease. So "take no action" and | |
2117 | * the upper levels will keep retrying until the lease | |
2118 | * expires (or we rebind). | |
2119 | */ | |
2120 | case STATUS_UnspecFail: | |
2121 | /* For unknown codes...it's a soft (retryable) error. */ | |
2122 | default: | |
2123 | return ISC_FALSE; | |
2124 | ||
2125 | /* The server is telling us to use a multicast address, so | |
2126 | * we have to delete the unicast option from the active | |
2127 | * lease, then allow retransmission to occur normally. | |
2128 | * (XXX: It might be preferable in this case to retransmit | |
2129 | * sooner than the current interval, but for now we don't.) | |
2130 | */ | |
2131 | case STATUS_UseMulticast: | |
2132 | if(client->active_lease != NULL) | |
2133 | delete_option(&dhcp_universe, | |
2134 | client->active_lease->options, | |
2135 | D6O_UNICAST); | |
2136 | return ISC_FALSE; | |
2137 | ||
2138 | /* "When the client receives a NotOnLink status from the | |
2139 | * server in response to a Request, the client can either | |
2140 | * re-issue the Request without specifying any addresses | |
2141 | * or restart the DHCP server discovery process." | |
2142 | * | |
2143 | * This is strange. If competing server evaluation is | |
2144 | * useful (and therefore in the protocol), then why would | |
2145 | * a client's first reaction be to request from the same | |
2146 | * server on a different link? Surely you'd want to | |
2147 | * re-evaluate your server selection. | |
2148 | * | |
2149 | * Well, I guess that's the answer. | |
2150 | */ | |
2151 | case STATUS_NotOnLink: | |
2152 | /* In this case, we need to rescind all current active | |
2153 | * bindings (just 'expire' them all normally, if early). | |
2154 | * They're no use to us on the wrong link. Then head back | |
2155 | * to init, redo server selection and get new addresses. | |
2156 | */ | |
2157 | dhc6_withdraw_lease(client); | |
2158 | return ISC_TRUE; | |
2159 | ||
2160 | /* "If the status code is NoAddrsAvail, the client has | |
2161 | * received no usable addresses in the IA and may choose | |
2162 | * to try obtaining addresses for the IA from another | |
2163 | * server." | |
2164 | */ | |
2165 | case STATUS_NoAddrsAvail: | |
2166 | /* Head back to init, keeping any active bindings (!). */ | |
2167 | start_init6(client); | |
2168 | break; | |
2169 | ||
2170 | /* - sends a Request message if the IA contained a Status | |
2171 | * Code option with the NoBinding status (and does not | |
2172 | * send any additional Renew/Rebind messages) | |
2173 | */ | |
2174 | case STATUS_NoBinding: | |
2175 | if (client->advertised_leases != NULL) | |
2176 | log_fatal("Impossible condition at %s:%d.", MDL); | |
2177 | ||
2178 | client->advertised_leases = | |
2179 | dhc6_dup_lease(client->active_lease, MDL); | |
2180 | start_selecting6(client); | |
2181 | break; | |
2182 | } | |
2183 | ||
2184 | return ISC_TRUE; | |
2185 | } | |
2186 | ||
2187 | /* Look at a new and old lease, and make sure the new information is not | |
2188 | * losing us any state. | |
2189 | */ | |
2190 | static isc_result_t | |
2191 | dhc6_check_reply(struct client_state *client, struct dhc6_lease *new) | |
2192 | { | |
2193 | isc_boolean_t (*action)(struct client_state *, isc_result_t, unsigned); | |
2194 | struct dhc6_ia *ia; | |
2195 | struct dhc6_addr *addr; | |
28868515 | 2196 | isc_result_t rval = ISC_R_SUCCESS; |
98bd7ca0 | 2197 | unsigned code; |
1d9774ab | 2198 | const char *scope; |
98bd7ca0 DH |
2199 | int nscore, sscore; |
2200 | ||
2201 | if ((client == NULL) || (new == NULL)) | |
2202 | return ISC_R_INVALIDARG; | |
2203 | ||
2204 | switch (client->state) { | |
6d7f9584 FD |
2205 | case S_INIT: |
2206 | action = dhc6_init_action; | |
2207 | break; | |
2208 | ||
98bd7ca0 DH |
2209 | case S_SELECTING: |
2210 | case S_REBOOTING: | |
2211 | action = dhc6_select_action; | |
2212 | break; | |
2213 | ||
2214 | case S_RENEWING: | |
2215 | case S_REBINDING: | |
2216 | action = dhc6_reply_action; | |
2217 | break; | |
2218 | ||
2219 | default: | |
2220 | log_fatal("Impossible condition at %s:%d.", MDL); | |
2221 | return ISC_R_CANCELED; | |
2222 | } | |
2223 | ||
2224 | /* If there is a code to extract, and if there is some | |
2225 | * action to take based on that code, then take the action | |
2226 | * and do not continue. | |
2227 | */ | |
2228 | rval = dhc6_check_status(rval, new->options, "message", &code); | |
2229 | if (action(client, rval, code)) | |
2230 | return ISC_R_CANCELED; | |
2231 | ||
2232 | for (ia = new->bindings ; ia != NULL ; ia = ia->next) { | |
1d9774ab FD |
2233 | switch (ia->ia_type) { |
2234 | case D6O_IA_NA: | |
2235 | default: | |
2236 | scope = "IA_NA"; | |
2237 | break; | |
2238 | case D6O_IA_TA: | |
2239 | scope = "IA_TA"; | |
2240 | break; | |
2241 | case D6O_IA_PD: | |
2242 | scope = "IA_PD"; | |
2243 | break; | |
2244 | } | |
2245 | rval = dhc6_check_status(rval, ia->options, | |
2246 | scope, &code); | |
98bd7ca0 DH |
2247 | if (action(client, rval, code)) |
2248 | return ISC_R_CANCELED; | |
2249 | ||
2250 | for (addr = ia->addrs ; addr != NULL ; | |
2251 | addr = addr->next) { | |
1d9774ab FD |
2252 | if (ia->ia_type != D6O_IA_PD) |
2253 | scope = "IAADDR"; | |
2254 | else | |
2255 | scope = "IAPREFIX"; | |
98bd7ca0 | 2256 | rval = dhc6_check_status(rval, addr->options, |
1d9774ab | 2257 | scope, &code); |
98bd7ca0 DH |
2258 | if (action(client, rval, code)) |
2259 | return ISC_R_CANCELED; | |
2260 | } | |
2261 | } | |
2262 | ||
9ab0cc6a DH |
2263 | /* A Confirm->Reply is unsuitable for comparison to the old lease. */ |
2264 | if (client->state == S_REBOOTING) | |
2265 | return rval; | |
2266 | ||
6d7f9584 FD |
2267 | /* No old lease in rapid-commit. */ |
2268 | if (client->state == S_INIT) | |
2269 | return rval; | |
2270 | ||
98bd7ca0 DH |
2271 | switch (client->state) { |
2272 | case S_SELECTING: | |
2273 | /* Compare the new lease with the selected lease to make | |
2274 | * sure there is no risky business. | |
2275 | */ | |
0c20eab3 DH |
2276 | nscore = dhc6_score_lease(client, new); |
2277 | sscore = dhc6_score_lease(client, client->selected_lease); | |
98bd7ca0 DH |
2278 | if ((client->advertised_leases != NULL) && |
2279 | (nscore < (sscore / 2))) { | |
2280 | /* XXX: An attacker might reply this way to make | |
2281 | * XXX: sure we latch onto their configuration. | |
2282 | * XXX: We might want to ignore the packet and | |
2283 | * XXX: schedule re-selection at the next timeout? | |
2284 | */ | |
2285 | log_error("PRC: BAIT AND SWITCH detected. Score of " | |
2286 | "supplied lease (%d) is substantially " | |
2287 | "smaller than the advertised score (%d). " | |
2288 | "Trying other servers.", | |
2289 | nscore, sscore); | |
2290 | ||
e32529a5 | 2291 | dhc6_lease_destroy(&client->selected_lease, MDL); |
98bd7ca0 DH |
2292 | client->selected_lease = NULL; |
2293 | ||
2294 | start_selecting6(client); | |
2295 | ||
2296 | return ISC_R_CANCELED; | |
2297 | } | |
2298 | break; | |
2299 | ||
2300 | case S_RENEWING: | |
2301 | case S_REBINDING: | |
2302 | /* This leaves one RFC3315 status check unimplemented: | |
2303 | * | |
2304 | * - sends a Renew/Rebind if the IA is not in the Reply | |
2305 | * message | |
2306 | * | |
2307 | * We rely on the scheduling system to note that the IA has | |
2308 | * not left Renewal/Rebinding/whatever since it still carries | |
2309 | * old times from the last successful binding. So this is | |
2310 | * implemented actually, just not explicitly. | |
2311 | */ | |
2312 | break; | |
2313 | ||
2314 | default: | |
2315 | log_fatal("REALLY impossible condition at %s:%d.", MDL); | |
2316 | return ISC_R_CANCELED; | |
2317 | } | |
2318 | ||
2319 | return rval; | |
2320 | } | |
2321 | ||
2322 | /* While in init state, we only collect advertisements. If there happens | |
2323 | * to be an advertisement with a preference option of 255, that's an | |
2324 | * automatic exit. Otherwise, we collect advertisements until our timeout | |
2325 | * expires (client->RT). | |
2326 | */ | |
2327 | void | |
2328 | init_handler(struct packet *packet, struct client_state *client) | |
2329 | { | |
28868515 | 2330 | struct dhc6_lease *lease; |
98bd7ca0 DH |
2331 | |
2332 | /* In INIT state, we send solicits, we only expect to get | |
6d7f9584 | 2333 | * advertises (rapid commit has its own handler). |
98bd7ca0 DH |
2334 | */ |
2335 | if (packet->dhcpv6_msg_type != DHCPV6_ADVERTISE) | |
2336 | return; | |
2337 | ||
2338 | /* RFC3315 section 15.3 validation (same as 15.10 since we | |
2339 | * always include a client id). | |
2340 | */ | |
2341 | if (!valid_reply(packet, client)) { | |
2342 | log_error("Invalid Advertise - rejecting."); | |
2343 | return; | |
2344 | } | |
2345 | ||
2346 | lease = dhc6_leaseify(packet); | |
2347 | ||
2348 | if (dhc6_check_advertise(lease) != ISC_R_SUCCESS) { | |
2349 | log_debug("PRC: Lease failed to satisfy."); | |
e32529a5 | 2350 | dhc6_lease_destroy(&lease, MDL); |
98bd7ca0 DH |
2351 | return; |
2352 | } | |
2353 | ||
2354 | insert_lease(&client->advertised_leases, lease); | |
2355 | ||
2356 | /* According to RFC3315 section 17.1.2, the client MUST wait for | |
2357 | * the first RT before selecting a lease. But on the 400th RT, | |
2358 | * we dont' want to wait the full timeout if we finally get an | |
2359 | * advertise. We could probably wait a second, but ohwell, | |
2360 | * RFC3315 doesn't say so. | |
2361 | * | |
2362 | * If the lease is highest possible preference, 255, RFC3315 claims | |
2363 | * we should continue immediately even on the first RT. We probably | |
2364 | * should not if the advertise contains less than one IA and address. | |
2365 | */ | |
2366 | if ((client->txcount > 1) || | |
0c20eab3 DH |
2367 | ((lease->pref == 255) && |
2368 | (dhc6_score_lease(client, lease) > 150))) { | |
98bd7ca0 DH |
2369 | log_debug("RCV: Advertisement immediately selected."); |
2370 | cancel_timeout(do_init6, client); | |
2371 | start_selecting6(client); | |
2372 | } else | |
2373 | log_debug("RCV: Advertisement recorded."); | |
2374 | } | |
2375 | ||
6d7f9584 FD |
2376 | /* Specific version of init_handler() for rapid-commit. |
2377 | */ | |
2378 | void | |
2379 | rapid_commit_handler(struct packet *packet, struct client_state *client) | |
2380 | { | |
2381 | struct dhc6_lease *lease; | |
2382 | isc_result_t check_status; | |
2383 | ||
2384 | /* On ADVERTISE just fall back to the init_handler(). | |
2385 | */ | |
2386 | if (packet->dhcpv6_msg_type == DHCPV6_ADVERTISE) { | |
2387 | init_handler(packet, client); | |
2388 | return; | |
2389 | } else if (packet->dhcpv6_msg_type != DHCPV6_REPLY) | |
2390 | return; | |
2391 | ||
2392 | /* RFC3315 section 15.10 validation (same as 15.3 since we | |
2393 | * always include a client id). | |
2394 | */ | |
2395 | if (!valid_reply(packet, client)) { | |
2396 | log_error("Invalid Reply - rejecting."); | |
2397 | return; | |
2398 | } | |
2399 | ||
2400 | /* A rapid-commit option MUST be here. */ | |
2401 | if (lookup_option(&dhcpv6_universe, packet->options, | |
2402 | D6O_RAPID_COMMIT) == 0) { | |
2403 | log_error("Reply without Rapid-Commit - rejecting."); | |
2404 | return; | |
2405 | } | |
2406 | ||
2407 | lease = dhc6_leaseify(packet); | |
2408 | ||
2409 | /* This is an out of memory condition...hopefully a temporary | |
2410 | * problem. Returning now makes us try to retransmit later. | |
2411 | */ | |
2412 | if (lease == NULL) | |
2413 | return; | |
2414 | ||
2415 | check_status = dhc6_check_reply(client, lease); | |
2416 | if (check_status != ISC_R_SUCCESS) { | |
2417 | dhc6_lease_destroy(&lease, MDL); | |
2418 | return; | |
2419 | } | |
2420 | ||
2421 | /* Jump to the selecting state. */ | |
2422 | cancel_timeout(do_init6, client); | |
2423 | client->state = S_SELECTING; | |
2424 | ||
2425 | /* Merge any bindings in the active lease (if there is one) into | |
2426 | * the new active lease. | |
2427 | */ | |
2428 | dhc6_merge_lease(client->active_lease, lease); | |
2429 | ||
2430 | /* Cleanup if a previous attempt to go bound failed. */ | |
2431 | if (client->old_lease != NULL) { | |
2432 | dhc6_lease_destroy(&client->old_lease, MDL); | |
2433 | client->old_lease = NULL; | |
2434 | } | |
2435 | ||
2436 | /* Make this lease active and BIND to it. */ | |
2437 | if (client->active_lease != NULL) | |
2438 | client->old_lease = client->active_lease; | |
2439 | client->active_lease = lease; | |
2440 | ||
2441 | /* We're done with the ADVERTISEd leases, if any. */ | |
2442 | while(client->advertised_leases != NULL) { | |
2443 | lease = client->advertised_leases; | |
2444 | client->advertised_leases = lease->next; | |
2445 | ||
2446 | dhc6_lease_destroy(&lease, MDL); | |
2447 | } | |
2448 | ||
2449 | start_bound(client); | |
2450 | } | |
2451 | ||
98bd7ca0 DH |
2452 | /* Find the 'best' lease in the cache of advertised leases (usually). From |
2453 | * RFC3315 Section 17.1.3: | |
2454 | * | |
2455 | * Upon receipt of one or more valid Advertise messages, the client | |
2456 | * selects one or more Advertise messages based upon the following | |
2457 | * criteria. | |
2458 | * | |
2459 | * - Those Advertise messages with the highest server preference value | |
2460 | * are preferred over all other Advertise messages. | |
2461 | * | |
2462 | * - Within a group of Advertise messages with the same server | |
2463 | * preference value, a client MAY select those servers whose | |
2464 | * Advertise messages advertise information of interest to the | |
2465 | * client. For example, the client may choose a server that returned | |
2466 | * an advertisement with configuration options of interest to the | |
2467 | * client. | |
2468 | * | |
2469 | * - The client MAY choose a less-preferred server if that server has a | |
2470 | * better set of advertised parameters, such as the available | |
2471 | * addresses advertised in IAs. | |
2472 | * | |
2473 | * Note that the first and third contradict each other. The third should | |
2474 | * probably be taken to mean that the client should prefer answers that | |
2475 | * offer bindings, even if that violates the preference rule. | |
2476 | * | |
2477 | * The above also isn't deterministic where there are ties. So the final | |
2478 | * tiebreaker we add, if all other values are equal, is to compare the | |
2479 | * server identifiers and to select the numerically lower one. | |
2480 | */ | |
2481 | static struct dhc6_lease * | |
0c20eab3 | 2482 | dhc6_best_lease(struct client_state *client, struct dhc6_lease **head) |
98bd7ca0 DH |
2483 | { |
2484 | struct dhc6_lease **rpos, *rval, **candp, *cand; | |
2485 | int cscore, rscore; | |
2486 | ||
2487 | if (head == NULL || *head == NULL) | |
2488 | return NULL; | |
2489 | ||
2490 | rpos = head; | |
2491 | rval = *rpos; | |
0c20eab3 | 2492 | rscore = dhc6_score_lease(client, rval); |
98bd7ca0 DH |
2493 | candp = &rval->next; |
2494 | cand = *candp; | |
2495 | ||
2496 | log_debug("PRC: Considering best lease."); | |
2497 | log_debug("PRC: X-- Initial candidate %s (s: %d, p: %u).", | |
2498 | print_hex_1(rval->server_id.len, | |
2499 | rval->server_id.data, 48), | |
2500 | rscore, (unsigned)rval->pref); | |
2501 | ||
2502 | for (; cand != NULL ; candp = &cand->next, cand = *candp) { | |
0c20eab3 | 2503 | cscore = dhc6_score_lease(client, cand); |
98bd7ca0 DH |
2504 | |
2505 | log_debug("PRC: X-- Candidate %s (s: %d, p: %u).", | |
2506 | print_hex_1(cand->server_id.len, | |
2507 | cand->server_id.data, 48), | |
2508 | cscore, (unsigned)cand->pref); | |
2509 | ||
2510 | /* Above you'll find quoted RFC3315 Section 17.1.3. | |
2511 | * | |
2512 | * The third clause tells us to give up on leases that | |
2513 | * have no bindings even if their preference is better. | |
2514 | * So where our 'selected' lease's score is less than 150 | |
2515 | * (1 ia + 1 addr), choose any candidate >= 150. | |
2516 | * | |
2517 | * The first clause tells us to make preference the primary | |
2518 | * deciding factor. So if it's lower, reject, if it's | |
2519 | * higher, select. | |
2520 | * | |
2521 | * The second clause tells us where the preference is | |
2522 | * equal, we should use 'our judgement' of what we like | |
2523 | * to see in an advertisement primarily. | |
2524 | * | |
2525 | * But there can still be a tie. To make this deterministic, | |
2526 | * we compare the server identifiers and select the binary | |
2527 | * lowest. | |
2528 | * | |
2529 | * Since server id's are unique in this list, there is | |
2530 | * no further tie to break. | |
2531 | */ | |
2532 | if ((rscore < 150) && (cscore >= 150)) { | |
a0006737 | 2533 | log_debug("PRC: | X-- Selected, has bindings."); |
98bd7ca0 DH |
2534 | } else if (cand->pref < rval->pref) { |
2535 | log_debug("PRC: | X-- Rejected, lower preference."); | |
2536 | continue; | |
2537 | } else if (cand->pref > rval->pref) { | |
2538 | log_debug("PRC: | X-- Selected, higher preference."); | |
2539 | } else if (cscore > rscore) { | |
2540 | log_debug("PRC: | X-- Selected, equal preference, " | |
2541 | "higher score."); | |
2542 | } else if (cscore < rscore) { | |
2543 | log_debug("PRC: | X-- Rejected, equal preference, " | |
2544 | "lower score."); | |
2545 | continue; | |
2546 | } else if ((cand->server_id.len < rval->server_id.len) || | |
2547 | ((cand->server_id.len == rval->server_id.len) && | |
2548 | (memcmp(cand->server_id.data, | |
2549 | rval->server_id.data, | |
2550 | cand->server_id.len) < 0))) { | |
2551 | log_debug("PRC: | X-- Selected, equal preference, " | |
2552 | "equal score, binary lesser server ID."); | |
2553 | } else { | |
2554 | log_debug("PRC: | X-- Rejected, equal preference, " | |
2555 | "equal score, binary greater server ID."); | |
2556 | continue; | |
2557 | } | |
2558 | ||
2559 | rpos = candp; | |
2560 | rval = cand; | |
2561 | rscore = cscore; | |
2562 | } | |
2563 | ||
2564 | /* Remove the selected lease from the chain. */ | |
2565 | *rpos = rval->next; | |
2566 | ||
2567 | return rval; | |
2568 | } | |
2569 | ||
2570 | /* Select a lease out of the advertised leases and setup state to try and | |
2571 | * acquire that lease. | |
2572 | */ | |
2573 | void | |
2574 | start_selecting6(struct client_state *client) | |
2575 | { | |
2576 | struct dhc6_lease *lease; | |
98bd7ca0 DH |
2577 | |
2578 | if (client->advertised_leases == NULL) { | |
2579 | log_error("Can not enter DHCPv6 SELECTING state with no " | |
2580 | "leases to select from!"); | |
2581 | return; | |
2582 | } | |
2583 | ||
2584 | log_debug("PRC: Selecting best advertised lease."); | |
2585 | client->state = S_SELECTING; | |
2586 | ||
0c20eab3 | 2587 | lease = dhc6_best_lease(client, &client->advertised_leases); |
98bd7ca0 DH |
2588 | |
2589 | if (lease == NULL) | |
2590 | log_fatal("Impossible error at %s:%d.", MDL); | |
2591 | ||
2592 | client->selected_lease = lease; | |
2593 | ||
98bd7ca0 | 2594 | /* Set timers per RFC3315 section 18.1.1. */ |
be62cf06 FD |
2595 | client->IRT = REQ_TIMEOUT * 100; |
2596 | client->MRT = REQ_MAX_RT * 100; | |
98bd7ca0 DH |
2597 | client->MRC = REQ_MAX_RC; |
2598 | client->MRD = 0; | |
2599 | ||
2600 | dhc6_retrans_init(client); | |
2601 | ||
2602 | client->v6_handler = reply_handler; | |
2603 | ||
2604 | /* ("re")transmit the first packet. */ | |
2605 | do_select6(client); | |
2606 | } | |
2607 | ||
2608 | /* Transmit a Request to select a lease offered in Advertisements. In | |
2609 | * the event of failure, either move on to the next-best advertised lease, | |
2610 | * or head back to INIT state if there are none. | |
2611 | */ | |
2612 | void | |
2613 | do_select6(void *input) | |
2614 | { | |
2615 | struct client_state *client; | |
2616 | struct dhc6_lease *lease; | |
98bd7ca0 | 2617 | struct data_string ds; |
be62cf06 | 2618 | struct timeval elapsed, tv; |
98bd7ca0 | 2619 | int abort = ISC_FALSE; |
28868515 | 2620 | int send_ret; |
98bd7ca0 DH |
2621 | |
2622 | client = input; | |
2623 | ||
2624 | /* 'lease' is fewer characters to type. */ | |
2625 | lease = client->selected_lease; | |
2626 | if (lease == NULL || lease->bindings == NULL) { | |
2627 | log_error("Illegal to attempt selection without selecting " | |
2628 | "a lease."); | |
2629 | return; | |
2630 | } | |
2631 | ||
2632 | if ((client->MRC != 0) && (client->txcount > client->MRC)) { | |
2633 | log_info("Max retransmission count exceeded."); | |
2634 | abort = ISC_TRUE; | |
2635 | } | |
2636 | ||
be62cf06 FD |
2637 | /* |
2638 | * Start_time starts at the first transmission. | |
2639 | */ | |
2640 | if (client->txcount == 0) { | |
2641 | client->start_time.tv_sec = cur_tv.tv_sec; | |
2642 | client->start_time.tv_usec = cur_tv.tv_usec; | |
2643 | } | |
2644 | ||
2645 | /* elapsed = cur - start */ | |
2646 | elapsed.tv_sec = cur_tv.tv_sec - client->start_time.tv_sec; | |
2647 | elapsed.tv_usec = cur_tv.tv_usec - client->start_time.tv_usec; | |
2648 | if (elapsed.tv_usec < 0) { | |
2649 | elapsed.tv_sec -= 1; | |
2650 | elapsed.tv_usec += 1000000; | |
2651 | } | |
2652 | if ((client->MRD != 0) && (elapsed.tv_sec > client->MRD)) { | |
98bd7ca0 DH |
2653 | log_info("Max retransmission duration exceeded."); |
2654 | abort = ISC_TRUE; | |
2655 | } | |
2656 | ||
2657 | if (abort) { | |
2658 | log_debug("PRC: Lease %s failed.", | |
2659 | print_hex_1(lease->server_id.len, | |
2660 | lease->server_id.data, 56)); | |
2661 | ||
2662 | /* Get rid of the lease that timed/counted out. */ | |
e32529a5 | 2663 | dhc6_lease_destroy(&lease, MDL); |
98bd7ca0 DH |
2664 | client->selected_lease = NULL; |
2665 | ||
2666 | /* If there are more leases great. If not, get more. */ | |
2667 | if (client->advertised_leases != NULL) | |
2668 | start_selecting6(client); | |
2669 | else | |
2670 | start_init6(client); | |
2671 | ||
2672 | return; | |
2673 | } | |
2674 | ||
2675 | /* Now make a packet that looks suspiciously like the one we | |
2676 | * got from the server. But different. | |
2677 | * | |
2678 | * XXX: I guess IAID is supposed to be something the client | |
2679 | * indicates and uses as a key to its internal state. It is | |
2680 | * kind of odd to ask the server for IA's whose IAID the client | |
2681 | * did not manufacture. We first need a formal dhclient.conf | |
2682 | * construct for the iaid, then we can delve into this matter | |
2683 | * more properly. In the time being, this will work. | |
2684 | */ | |
2685 | memset(&ds, 0, sizeof(ds)); | |
2686 | if (!buffer_allocate(&ds.buffer, 4, MDL)) { | |
2687 | log_error("Unable to allocate memory for REQUEST."); | |
2688 | return; | |
2689 | } | |
2690 | ds.data = ds.buffer->data; | |
2691 | ds.len = 4; | |
2692 | ||
2693 | ds.buffer->data[0] = DHCPV6_REQUEST; | |
2694 | memcpy(ds.buffer->data + 1, client->dhcpv6_transaction_id, 3); | |
2695 | ||
2696 | /* Form an elapsed option. */ | |
be62cf06 FD |
2697 | /* Maximum value is 65535 1/100s coded as 0xffff. */ |
2698 | if ((elapsed.tv_sec < 0) || (elapsed.tv_sec > 655) || | |
2699 | ((elapsed.tv_sec == 655) && (elapsed.tv_usec > 350000))) { | |
98bd7ca0 | 2700 | client->elapsed = 0xffff; |
be62cf06 FD |
2701 | } else { |
2702 | client->elapsed = elapsed.tv_sec * 100; | |
2703 | client->elapsed += elapsed.tv_usec / 10000; | |
2704 | } | |
98bd7ca0 | 2705 | |
be62cf06 FD |
2706 | if (client->elapsed == 0) |
2707 | log_debug("XMT: Forming Request, 0 ms elapsed."); | |
2708 | else | |
2709 | log_debug("XMT: Forming Request, %u0 ms elapsed.", | |
2710 | (unsigned)client->elapsed); | |
98bd7ca0 DH |
2711 | |
2712 | client->elapsed = htons(client->elapsed); | |
2713 | ||
2714 | make_client6_options(client, &client->sent_options, lease, | |
2715 | DHCPV6_REQUEST); | |
2716 | ||
2717 | /* Fetch any configured 'sent' options (includes DUID) in wire format. | |
2718 | */ | |
2719 | dhcpv6_universe.encapsulate(&ds, NULL, NULL, client, | |
2720 | NULL, client->sent_options, &global_scope, | |
2721 | &dhcpv6_universe); | |
2722 | ||
1d9774ab FD |
2723 | /* Now append any IA's, and within them any IAADDR/IAPREFIXs. */ |
2724 | if (dhc6_add_ia_na(client, &ds, lease, | |
2725 | DHCPV6_REQUEST) != ISC_R_SUCCESS) { | |
98bd7ca0 DH |
2726 | data_string_forget(&ds, MDL); |
2727 | return; | |
2728 | } | |
2729 | ||
be62cf06 | 2730 | log_info("XMT: Request on %s, interval %ld0ms.", |
98bd7ca0 | 2731 | client->name ? client->name : client->interface->name, |
9aa669fc | 2732 | (long int)client->RT); |
98bd7ca0 DH |
2733 | |
2734 | send_ret = send_packet6(client->interface, | |
2735 | ds.data, ds.len, &DHCPv6DestAddr); | |
2736 | if (send_ret != ds.len) { | |
2737 | log_error("dhc6: send_packet6() sent %d of %d bytes", | |
2738 | send_ret, ds.len); | |
2739 | } | |
2740 | ||
2741 | data_string_forget(&ds, MDL); | |
2742 | ||
be62cf06 FD |
2743 | /* Wait RT */ |
2744 | tv.tv_sec = cur_tv.tv_sec + client->RT / 100; | |
2745 | tv.tv_usec = cur_tv.tv_usec + (client->RT % 100) * 10000; | |
2746 | if (tv.tv_usec >= 1000000) { | |
2747 | tv.tv_sec += 1; | |
2748 | tv.tv_usec -= 1000000; | |
2749 | } | |
2750 | add_timeout(&tv, do_select6, client, NULL, NULL); | |
98bd7ca0 DH |
2751 | |
2752 | dhc6_retrans_advance(client); | |
2753 | } | |
2754 | ||
1d9774ab FD |
2755 | /* For each IA_NA in the lease, for each address in the IA_NA, |
2756 | * append that information onto the packet-so-far. | |
98bd7ca0 DH |
2757 | */ |
2758 | static isc_result_t | |
1d9774ab FD |
2759 | dhc6_add_ia_na(struct client_state *client, struct data_string *packet, |
2760 | struct dhc6_lease *lease, u_int8_t message) | |
98bd7ca0 DH |
2761 | { |
2762 | struct data_string iads; | |
2763 | struct data_string addrds; | |
2764 | struct dhc6_addr *addr; | |
2765 | struct dhc6_ia *ia; | |
2766 | isc_result_t rval = ISC_R_SUCCESS; | |
2767 | TIME t1, t2; | |
af5fa176 | 2768 | |
98bd7ca0 DH |
2769 | memset(&iads, 0, sizeof(iads)); |
2770 | memset(&addrds, 0, sizeof(addrds)); | |
af5fa176 EH |
2771 | for (ia = lease->bindings; |
2772 | ia != NULL && rval == ISC_R_SUCCESS; | |
2773 | ia = ia->next) { | |
1d9774ab FD |
2774 | if (ia->ia_type != D6O_IA_NA) |
2775 | continue; | |
2776 | ||
98bd7ca0 | 2777 | if (!buffer_allocate(&iads.buffer, 12, MDL)) { |
af5fa176 EH |
2778 | log_error("Unable to allocate memory for IA_NA."); |
2779 | rval = ISC_R_NOMEMORY; | |
98bd7ca0 DH |
2780 | break; |
2781 | } | |
98bd7ca0 | 2782 | |
af5fa176 | 2783 | /* Copy the IAID into the packet buffer. */ |
98bd7ca0 | 2784 | memcpy(iads.buffer->data, ia->iaid, 4); |
af5fa176 EH |
2785 | iads.data = iads.buffer->data; |
2786 | iads.len = 12; | |
98bd7ca0 | 2787 | |
af5fa176 EH |
2788 | switch (message) { |
2789 | case DHCPV6_REQUEST: | |
2790 | case DHCPV6_RENEW: | |
2791 | case DHCPV6_REBIND: | |
2792 | ||
2793 | t1 = client->config->requested_lease / 2; | |
2794 | t2 = t1 + (t1 / 2); | |
98bd7ca0 | 2795 | #if MAX_TIME > 0xffffffff |
af5fa176 EH |
2796 | if (t1 > 0xffffffff) |
2797 | t1 = 0xffffffff; | |
2798 | if (t2 > 0xffffffff) | |
2799 | t2 = 0xffffffff; | |
98bd7ca0 | 2800 | #endif |
af5fa176 EH |
2801 | putULong(iads.buffer->data + 4, t1); |
2802 | putULong(iads.buffer->data + 8, t2); | |
2803 | ||
2804 | log_debug("XMT: X-- IA_NA %s", | |
2805 | print_hex_1(4, iads.data, 59)); | |
2806 | log_debug("XMT: | X-- Requested renew +%u", | |
2807 | (unsigned) t1); | |
2808 | log_debug("XMT: | X-- Requested rebind +%u", | |
2809 | (unsigned) t2); | |
2810 | break; | |
2811 | ||
2812 | case DHCPV6_CONFIRM: | |
2813 | case DHCPV6_RELEASE: | |
2814 | /* Set t1 and t2 to zero; server will ignore them */ | |
2815 | memset(iads.buffer->data + 4, 0, 8); | |
2816 | log_debug("XMT: X-- IA_NA %s", | |
2817 | print_hex_1(4, iads.buffer->data, 55)); | |
2818 | ||
2819 | break; | |
2820 | ||
2821 | default: | |
2822 | log_fatal("Impossible condition at %s:%d.", MDL); | |
2823 | } | |
98bd7ca0 | 2824 | |
af5fa176 | 2825 | for (addr = ia->addrs ; addr != NULL ; addr = addr->next) { |
703873ab DH |
2826 | /* |
2827 | * Do not confirm expired addresses, do not request | |
2828 | * expired addresses (but we keep them around for | |
2829 | * solicit). | |
2830 | */ | |
2831 | if (addr->flags & DHC6_ADDR_EXPIRED) | |
2832 | continue; | |
2833 | ||
af5fa176 EH |
2834 | if (addr->address.len != 16) { |
2835 | log_error("Illegal IPv6 address length (%d), " | |
2836 | "ignoring. (%s:%d)", | |
2837 | addr->address.len, MDL); | |
2838 | continue; | |
2839 | } | |
98bd7ca0 | 2840 | |
98bd7ca0 DH |
2841 | if (!buffer_allocate(&addrds.buffer, 24, MDL)) { |
2842 | log_error("Unable to allocate memory for " | |
2843 | "IAADDR."); | |
af5fa176 | 2844 | rval = ISC_R_NOMEMORY; |
98bd7ca0 DH |
2845 | break; |
2846 | } | |
af5fa176 | 2847 | |
98bd7ca0 DH |
2848 | addrds.data = addrds.buffer->data; |
2849 | addrds.len = 24; | |
2850 | ||
af5fa176 | 2851 | /* Copy the address into the packet buffer. */ |
98bd7ca0 | 2852 | memcpy(addrds.buffer->data, addr->address.iabuf, 16); |
98bd7ca0 | 2853 | |
af5fa176 EH |
2854 | /* Copy in additional information as appropriate */ |
2855 | switch (message) { | |
2856 | case DHCPV6_REQUEST: | |
2857 | case DHCPV6_RENEW: | |
2858 | case DHCPV6_REBIND: | |
2859 | t1 = client->config->requested_lease; | |
2860 | t2 = t1 + 300; | |
2861 | putULong(addrds.buffer->data + 16, t1); | |
2862 | putULong(addrds.buffer->data + 20, t2); | |
2863 | ||
2864 | log_debug("XMT: | | X-- IAADDR %s", | |
2865 | piaddr(addr->address)); | |
2866 | log_debug("XMT: | | | X-- Preferred " | |
2867 | "lifetime +%u", (unsigned)t1); | |
2868 | log_debug("XMT: | | | X-- Max lifetime +%u", | |
2869 | (unsigned)t2); | |
2870 | ||
2871 | break; | |
2872 | ||
2873 | case DHCPV6_CONFIRM: | |
2874 | /* | |
2875 | * Set preferred and max life to zero, | |
2876 | * per 17.1.3. | |
2877 | */ | |
2878 | memset(addrds.buffer->data + 16, 0, 8); | |
2879 | log_debug("XMT: | X-- Confirm Address %s", | |
2880 | piaddr(addr->address)); | |
2881 | break; | |
2882 | ||
2883 | case DHCPV6_RELEASE: | |
2884 | /* Preferred and max life are irrelevant */ | |
2885 | memset(addrds.buffer->data + 16, 0, 8); | |
2886 | log_debug("XMT: | X-- Release Address %s", | |
2887 | piaddr(addr->address)); | |
2888 | break; | |
2889 | ||
2890 | default: | |
2891 | log_fatal("Impossible condition at %s:%d.", | |
2892 | MDL); | |
2893 | } | |
98bd7ca0 DH |
2894 | |
2895 | append_option(&iads, &dhcpv6_universe, iaaddr_option, | |
af5fa176 | 2896 | &addrds); |
98bd7ca0 DH |
2897 | data_string_forget(&addrds, MDL); |
2898 | } | |
2899 | ||
af5fa176 EH |
2900 | /* |
2901 | * It doesn't make sense to make a request without an | |
98bd7ca0 DH |
2902 | * address. |
2903 | */ | |
af5fa176 EH |
2904 | if (ia->addrs == NULL) { |
2905 | log_debug("!!!: V IA_NA has no IAADDRs - removed."); | |
2906 | rval = ISC_R_FAILURE; | |
2907 | } else if (rval == ISC_R_SUCCESS) { | |
98bd7ca0 DH |
2908 | log_debug("XMT: V IA_NA appended."); |
2909 | append_option(packet, &dhcpv6_universe, ia_na_option, | |
2910 | &iads); | |
98bd7ca0 DH |
2911 | } |
2912 | ||
2913 | data_string_forget(&iads, MDL); | |
2914 | } | |
2915 | ||
af5fa176 | 2916 | return rval; |
98bd7ca0 DH |
2917 | } |
2918 | ||
2919 | /* reply_handler() accepts a Reply while we're attempting Select or Renew or | |
2920 | * Rebind. Basically any Reply packet. | |
2921 | */ | |
2922 | void | |
2923 | reply_handler(struct packet *packet, struct client_state *client) | |
2924 | { | |
28868515 | 2925 | struct dhc6_lease *lease; |
98bd7ca0 DH |
2926 | isc_result_t check_status; |
2927 | ||
2928 | if (packet->dhcpv6_msg_type != DHCPV6_REPLY) | |
2929 | return; | |
2930 | ||
2931 | /* RFC3315 section 15.10 validation (same as 15.3 since we | |
2932 | * always include a client id). | |
2933 | */ | |
2934 | if (!valid_reply(packet, client)) { | |
7b22a9b4 | 2935 | log_error("Invalid Reply - rejecting."); |
98bd7ca0 DH |
2936 | return; |
2937 | } | |
2938 | ||
2939 | lease = dhc6_leaseify(packet); | |
2940 | ||
2941 | /* This is an out of memory condition...hopefully a temporary | |
2942 | * problem. Returning now makes us try to retransmit later. | |
2943 | */ | |
2944 | if (lease == NULL) | |
2945 | return; | |
2946 | ||
2947 | check_status = dhc6_check_reply(client, lease); | |
2948 | if (check_status != ISC_R_SUCCESS) { | |
e32529a5 | 2949 | dhc6_lease_destroy(&lease, MDL); |
98bd7ca0 DH |
2950 | |
2951 | /* If no action was taken, but there is an error, then | |
2952 | * we wait for a retransmission. | |
2953 | */ | |
2954 | if (check_status != ISC_R_CANCELED) | |
2955 | return; | |
2956 | } | |
2957 | ||
a0006737 | 2958 | /* We're done retransmitting at this point. */ |
98bd7ca0 DH |
2959 | cancel_timeout(do_confirm6, client); |
2960 | cancel_timeout(do_select6, client); | |
2961 | cancel_timeout(do_refresh6, client); | |
af5fa176 | 2962 | cancel_timeout(do_release6, client); |
98bd7ca0 DH |
2963 | |
2964 | /* Action was taken, so now that we've torn down our scheduled | |
2965 | * retransmissions, return. | |
2966 | */ | |
2967 | if (check_status == ISC_R_CANCELED) | |
2968 | return; | |
2969 | ||
2970 | if (client->selected_lease != NULL) { | |
e32529a5 | 2971 | dhc6_lease_destroy(&client->selected_lease, MDL); |
98bd7ca0 DH |
2972 | client->selected_lease = NULL; |
2973 | } | |
2974 | ||
af5fa176 EH |
2975 | /* If this is in response to a Release, clean up and return. */ |
2976 | if (client->state == S_STOPPED) { | |
2977 | if (client->active_lease == NULL) | |
2978 | log_fatal("Impossible condition at %s:%d.", MDL); | |
2979 | ||
e32529a5 | 2980 | dhc6_lease_destroy(&client->active_lease, MDL); |
af5fa176 EH |
2981 | client->active_lease = NULL; |
2982 | return; | |
2983 | } | |
2984 | ||
98bd7ca0 DH |
2985 | /* If this is in response to a confirm, we use the lease we've |
2986 | * already got, not the reply we were sent. | |
2987 | */ | |
2988 | if (client->state == S_REBOOTING) { | |
2989 | if (client->active_lease == NULL) | |
2990 | log_fatal("Impossible condition at %s:%d.", MDL); | |
2991 | ||
e32529a5 | 2992 | dhc6_lease_destroy(&lease, MDL); |
98bd7ca0 DH |
2993 | start_bound(client); |
2994 | return; | |
2995 | } | |
2996 | ||
2997 | /* Merge any bindings in the active lease (if there is one) into | |
2998 | * the new active lease. | |
2999 | */ | |
3000 | dhc6_merge_lease(client->active_lease, lease); | |
3001 | ||
3002 | /* Cleanup if a previous attempt to go bound failed. */ | |
3003 | if (client->old_lease != NULL) { | |
e32529a5 | 3004 | dhc6_lease_destroy(&client->old_lease, MDL); |
98bd7ca0 DH |
3005 | client->old_lease = NULL; |
3006 | } | |
3007 | ||
3008 | /* Make this lease active and BIND to it. */ | |
3009 | if (client->active_lease != NULL) | |
3010 | client->old_lease = client->active_lease; | |
3011 | client->active_lease = lease; | |
3012 | ||
3013 | /* We're done with the ADVERTISEd leases, if any. */ | |
3014 | while(client->advertised_leases != NULL) { | |
3015 | lease = client->advertised_leases; | |
3016 | client->advertised_leases = lease->next; | |
3017 | ||
e32529a5 | 3018 | dhc6_lease_destroy(&lease, MDL); |
98bd7ca0 DH |
3019 | } |
3020 | ||
3021 | start_bound(client); | |
3022 | } | |
3023 | ||
3024 | /* DHCPv6 packets are a little sillier than they needed to be - the root | |
3025 | * packet contains options, then IA's which contain options, then within | |
3026 | * that IAADDR's which contain options. | |
3027 | * | |
3028 | * To sort this out at dhclient-script time (which fetches config parameters | |
3029 | * in environment variables), start_bound() iterates over each IAADDR, and | |
3030 | * calls this function to marshall an environment variable set that includes | |
3031 | * the most-specific option values related to that IAADDR in particular. | |
3032 | * | |
a0006737 | 3033 | * To achieve this, we load environment variables for the root options space, |
98bd7ca0 DH |
3034 | * then the IA, then the IAADDR. Any duplicate option names will be |
3035 | * over-written by the later versions. | |
3036 | */ | |
3037 | static void | |
06eb8bab | 3038 | dhc6_marshall_values(const char *prefix, struct client_state *client, |
98bd7ca0 DH |
3039 | struct dhc6_lease *lease, struct dhc6_ia *ia, |
3040 | struct dhc6_addr *addr) | |
3041 | { | |
3042 | /* Option cache contents, in descending order of | |
3043 | * scope. | |
3044 | */ | |
3045 | if ((lease != NULL) && (lease->options != NULL)) | |
3046 | script_write_params6(client, prefix, lease->options); | |
3047 | if ((ia != NULL) && (ia->options != NULL)) | |
3048 | script_write_params6(client, prefix, ia->options); | |
3049 | if ((addr != NULL) && (addr->options != NULL)) | |
3050 | script_write_params6(client, prefix, addr->options); | |
3051 | ||
3052 | /* addr fields. */ | |
3053 | if (addr != NULL) { | |
3054 | /* Current practice is that all subnets are /64's, but | |
3055 | * some suspect this may not be permanent. | |
3056 | */ | |
3057 | client_envadd(client, prefix, "ip6_prefixlen", "%d", 64); | |
3058 | client_envadd(client, prefix, "ip6_address", "%s", | |
3059 | piaddr(addr->address)); | |
3060 | client_envadd(client, prefix, "life_starts", "%d", | |
3061 | (int)(addr->starts)); | |
3062 | client_envadd(client, prefix, "preferred_life", "%d", | |
3063 | (int)(addr->preferred_life)); | |
3064 | client_envadd(client, prefix, "max_life", "%d", | |
3065 | (int)(addr->max_life)); | |
3066 | } | |
3067 | ||
3068 | /* ia fields. */ | |
3069 | if (ia != NULL) { | |
3070 | client_envadd(client, prefix, "iaid", "%s", | |
3071 | print_hex_1(4, ia->iaid, 12)); | |
3072 | client_envadd(client, prefix, "starts", "%d", | |
3073 | (int)(ia->starts)); | |
3074 | client_envadd(client, prefix, "renew", "%u", ia->renew); | |
3075 | client_envadd(client, prefix, "rebind", "%u", ia->rebind); | |
3076 | } | |
3077 | } | |
3078 | ||
3079 | /* Look at where the client's active lease is sitting. If it's looking to | |
3080 | * time out on renew, rebind, depref, or expiration, do those things. | |
3081 | */ | |
3082 | static void | |
3083 | dhc6_check_times(struct client_state *client) | |
3084 | { | |
3085 | struct dhc6_lease *lease; | |
3086 | struct dhc6_ia *ia; | |
3087 | struct dhc6_addr *addr; | |
3088 | TIME renew=MAX_TIME, rebind=MAX_TIME, depref=MAX_TIME, | |
3089 | lo_expire=MAX_TIME, hi_expire=0, tmp; | |
3090 | int has_addrs = ISC_FALSE; | |
be62cf06 | 3091 | struct timeval tv; |
98bd7ca0 DH |
3092 | |
3093 | lease = client->active_lease; | |
3094 | ||
3095 | /* Bit spammy. We should probably keep record of scheduled | |
3096 | * events instead. | |
3097 | */ | |
3098 | cancel_timeout(start_renew6, client); | |
3099 | cancel_timeout(start_rebind6, client); | |
3100 | cancel_timeout(do_depref, client); | |
3101 | cancel_timeout(do_expire, client); | |
3102 | ||
3103 | for(ia = lease->bindings ; ia != NULL ; ia = ia->next) { | |
703873ab DH |
3104 | TIME this_ia_lo_expire, this_ia_hi_expire, use_expire; |
3105 | ||
1d9774ab FD |
3106 | if (ia->ia_type != D6O_IA_NA) |
3107 | continue; | |
3108 | ||
703873ab DH |
3109 | this_ia_lo_expire = MAX_TIME; |
3110 | this_ia_hi_expire = 0; | |
3111 | ||
98bd7ca0 DH |
3112 | for (addr = ia->addrs ; addr != NULL ; addr = addr->next) { |
3113 | if(!(addr->flags & DHC6_ADDR_DEPREFFED)) { | |
3114 | if (addr->preferred_life == 0xffffffff) | |
3115 | tmp = MAX_TIME; | |
3116 | else | |
3117 | tmp = addr->starts + | |
3118 | addr->preferred_life; | |
3119 | ||
3120 | if (tmp < depref) | |
3121 | depref = tmp; | |
3122 | } | |
3123 | ||
3124 | if (!(addr->flags & DHC6_ADDR_EXPIRED)) { | |
703873ab | 3125 | /* Find EPOCH-relative expiration. */ |
98bd7ca0 DH |
3126 | if (addr->max_life == 0xffffffff) |
3127 | tmp = MAX_TIME; | |
3128 | else | |
3129 | tmp = addr->starts + addr->max_life; | |
3130 | ||
703873ab DH |
3131 | /* Make the times ia->starts relative. */ |
3132 | tmp -= ia->starts; | |
3133 | ||
3134 | if (tmp > this_ia_hi_expire) | |
3135 | this_ia_hi_expire = tmp; | |
3136 | if (tmp < this_ia_lo_expire) | |
3137 | this_ia_lo_expire = tmp; | |
98bd7ca0 DH |
3138 | |
3139 | has_addrs = ISC_TRUE; | |
3140 | } | |
3141 | } | |
3142 | ||
703873ab DH |
3143 | /* These times are ia->starts relative. */ |
3144 | if (this_ia_lo_expire <= (this_ia_hi_expire / 2)) | |
3145 | use_expire = this_ia_hi_expire; | |
3146 | else | |
3147 | use_expire = this_ia_lo_expire; | |
3148 | ||
3149 | /* | |
3150 | * If the auto-selected expiration time is "infinite", or | |
3151 | * zero, assert a reasonable default. | |
3152 | */ | |
3153 | if ((use_expire == MAX_TIME) || (use_expire <= 1)) | |
3154 | use_expire = client->config->requested_lease / 2; | |
3155 | else | |
3156 | use_expire /= 2; | |
98bd7ca0 | 3157 | |
703873ab DH |
3158 | if (ia->renew == 0) { |
3159 | tmp = ia->starts + use_expire; | |
98bd7ca0 DH |
3160 | } else if(ia->renew == 0xffffffff) |
3161 | tmp = MAX_TIME; | |
3162 | else | |
3163 | tmp = ia->starts + ia->renew; | |
3164 | ||
3165 | if (tmp < renew) | |
3166 | renew = tmp; | |
3167 | ||
3168 | if (ia->rebind == 0) { | |
703873ab DH |
3169 | /* Set rebind to 3/4 expiration interval. */ |
3170 | tmp = ia->starts + use_expire + (use_expire / 2); | |
98bd7ca0 DH |
3171 | } else if (ia->renew == 0xffffffff) |
3172 | tmp = MAX_TIME; | |
3173 | else | |
3174 | tmp = ia->starts + ia->rebind; | |
3175 | ||
3176 | if (tmp < rebind) | |
3177 | rebind = tmp; | |
703873ab DH |
3178 | |
3179 | /* | |
3180 | * Return expiration ranges to EPOCH relative for event | |
3181 | * scheduling (add_timeout()). | |
3182 | */ | |
3183 | this_ia_hi_expire += ia->starts; | |
3184 | this_ia_lo_expire += ia->starts; | |
3185 | ||
3186 | if (this_ia_hi_expire > hi_expire) | |
3187 | hi_expire = this_ia_hi_expire; | |
3188 | if (this_ia_lo_expire < lo_expire) | |
3189 | lo_expire = this_ia_lo_expire; | |
98bd7ca0 DH |
3190 | } |
3191 | ||
3192 | /* If there are no addresses, give up, go to INIT. | |
3193 | * Note that if an address is unexpired with a date in the past, | |
3194 | * we're scheduling an expiration event to ocurr in the past. We | |
3195 | * could probably optimize this to expire now (but then there's | |
3196 | * recursion). | |
3197 | * | |
3198 | * In the future, we may decide that we're done here, or to | |
3199 | * schedule a future request (using 4-pkt info-request model). | |
3200 | */ | |
3201 | if (has_addrs == ISC_FALSE) { | |
e32529a5 | 3202 | dhc6_lease_destroy(&client->active_lease, MDL); |
98bd7ca0 DH |
3203 | client->active_lease = NULL; |
3204 | ||
3205 | /* Go back to the beginning. */ | |
3206 | start_init6(client); | |
3207 | return; | |
3208 | } | |
3209 | ||
3210 | switch(client->state) { | |
3211 | case S_BOUND: | |
3212 | /* We'd like to hit renewing, but if rebinding has already | |
3213 | * passed (time warp), head straight there. | |
3214 | */ | |
3215 | if ((rebind > cur_time) && (renew < rebind)) { | |
3216 | log_debug("PRC: Renewal event scheduled in %d seconds, " | |
3217 | "to run for %u seconds.", | |
3218 | (int)(renew - cur_time), | |
3219 | (unsigned)(rebind - renew)); | |
be62cf06 FD |
3220 | client->next_MRD = rebind; |
3221 | tv.tv_sec = renew; | |
3222 | tv.tv_usec = 0; | |
3223 | add_timeout(&tv, start_renew6, client, NULL, NULL); | |
98bd7ca0 DH |
3224 | |
3225 | break; | |
3226 | } | |
3227 | /* FALL THROUGH */ | |
3228 | case S_RENEWING: | |
3229 | /* While actively renewing, MRD is bounded by the time | |
3230 | * we stop renewing and start rebinding. This helps us | |
3231 | * process the state change on time. | |
3232 | */ | |
3233 | client->MRD = rebind - cur_time; | |
3234 | if (rebind != MAX_TIME) { | |
3235 | log_debug("PRC: Rebind event scheduled in %d seconds, " | |
3236 | "to run for %d seconds.", | |
3237 | (int)(rebind - cur_time), | |
3238 | (int)(hi_expire - rebind)); | |
be62cf06 FD |
3239 | client->next_MRD = hi_expire; |
3240 | tv.tv_sec = rebind; | |
3241 | tv.tv_usec = 0; | |
3242 | add_timeout(&tv, start_rebind6, client, NULL, NULL); | |
98bd7ca0 DH |
3243 | } |
3244 | break; | |
3245 | ||
3246 | case S_REBINDING: | |
3247 | /* For now, we rebind up until the last lease expires. In | |
3248 | * the future, we might want to start SOLICITing when we've | |
3249 | * depreffed an address. | |
3250 | */ | |
3251 | client->MRD = hi_expire - cur_time; | |
3252 | break; | |
3253 | ||
3254 | default: | |
3255 | log_fatal("Impossible condition at %s:%d.", MDL); | |
3256 | } | |
3257 | ||
3258 | /* Separately, set a time at which we will depref and expire | |
3259 | * leases. This might happen with multiple addresses while we | |
3260 | * keep trying to refresh. | |
3261 | */ | |
3262 | if (depref != MAX_TIME) { | |
3263 | log_debug("PRC: Depreference scheduled in %d seconds.", | |
3264 | (int)(depref - cur_time)); | |
be62cf06 FD |
3265 | tv.tv_sec = depref; |
3266 | tv.tv_usec = 0; | |
3267 | add_timeout(&tv, do_depref, client, NULL, NULL); | |
98bd7ca0 DH |
3268 | } |
3269 | if (lo_expire != MAX_TIME) { | |
3270 | log_debug("PRC: Expiration scheduled in %d seconds.", | |
3271 | (int)(lo_expire - cur_time)); | |
be62cf06 FD |
3272 | tv.tv_sec = lo_expire; |
3273 | tv.tv_usec = 0; | |
3274 | add_timeout(&tv, do_expire, client, NULL, NULL); | |
98bd7ca0 DH |
3275 | } |
3276 | } | |
3277 | ||
3278 | /* In a given IA chain, find the IA with the same 'iaid'. */ | |
3279 | static struct dhc6_ia * | |
1d9774ab | 3280 | find_ia_na(struct dhc6_ia *head, const char *id) |
98bd7ca0 DH |
3281 | { |
3282 | struct dhc6_ia *ia; | |
3283 | ||
3284 | for (ia = head ; ia != NULL ; ia = ia->next) { | |
1d9774ab FD |
3285 | if (ia->ia_type != D6O_IA_NA) |
3286 | continue; | |
98bd7ca0 DH |
3287 | if (memcmp(ia->iaid, id, 4) == 0) |
3288 | return ia; | |
3289 | } | |
3290 | ||
3291 | return NULL; | |
3292 | } | |
3293 | ||
3294 | /* In a given address chain, find a matching address. */ | |
3295 | static struct dhc6_addr * | |
3296 | find_addr(struct dhc6_addr *head, struct iaddr *address) | |
3297 | { | |
3298 | struct dhc6_addr *addr; | |
3299 | ||
3300 | for (addr = head ; addr != NULL ; addr = addr->next) { | |
3301 | if ((addr->address.len == address->len) && | |
3302 | (memcmp(addr->address.iabuf, address->iabuf, | |
3303 | address->len) == 0)) | |
3304 | return addr; | |
3305 | } | |
3306 | ||
3307 | return NULL; | |
3308 | } | |
3309 | ||
3310 | /* Merge the bindings from the source lease into the destination lease | |
3311 | * structure, where they are missing. We have to copy the stateful | |
3312 | * objects rather than move them over, because later code needs to be | |
3313 | * able to compare new versus old if they contain any bindings. | |
3314 | */ | |
3315 | static void | |
3316 | dhc6_merge_lease(struct dhc6_lease *src, struct dhc6_lease *dst) | |
3317 | { | |
3318 | struct dhc6_ia *sia, *dia, *tia; | |
3319 | struct dhc6_addr *saddr, *daddr, *taddr; | |
3320 | int changes = 0; | |
3321 | ||
3322 | if ((dst == NULL) || (src == NULL)) | |
3323 | return; | |
3324 | ||
3325 | for (sia = src->bindings ; sia != NULL ; sia = sia->next) { | |
1d9774ab FD |
3326 | if (sia->ia_type != D6O_IA_NA) |
3327 | continue; | |
3328 | dia = find_ia_na(dst->bindings, (char *)sia->iaid); | |
98bd7ca0 DH |
3329 | |
3330 | if (dia == NULL) { | |
3331 | tia = dhc6_dup_ia(sia, MDL); | |
3332 | ||
3333 | if (tia == NULL) | |
3334 | log_fatal("Out of memory merging lease - " | |
3335 | "Unable to continue without losing " | |
3336 | "state! (%s:%d)", MDL); | |
3337 | ||
3338 | /* XXX: consider sorting? */ | |
3339 | tia->next = dst->bindings; | |
3340 | dst->bindings = tia; | |
3341 | changes = 1; | |
3342 | } else { | |
3343 | for (saddr = sia->addrs ; saddr != NULL ; | |
3344 | saddr = saddr->next) { | |
3345 | daddr = find_addr(dia->addrs, | |
3346 | &saddr->address); | |
3347 | ||
3348 | if (daddr == NULL) { | |
3349 | taddr = dhc6_dup_addr(saddr, MDL); | |
3350 | ||
3351 | if (taddr == NULL) | |
3352 | log_fatal("Out of memory " | |
3353 | "merging lease - " | |
3354 | "Unable to continue " | |
3355 | "without losing " | |
3356 | "state! (%s:%d)", | |
3357 | MDL); | |
3358 | ||
3359 | /* XXX: consider sorting? */ | |
3360 | taddr->next = dia->addrs; | |
3361 | dia->addrs = taddr; | |
3362 | changes = 1; | |
3363 | } | |
3364 | } | |
3365 | } | |
3366 | } | |
3367 | ||
3368 | /* If we made changes, reset the score to 0 so it is recalculated. */ | |
3369 | if (changes) | |
3370 | dst->score = 0; | |
3371 | } | |
3372 | ||
3373 | /* We've either finished selecting or succeeded in Renew or Rebinding our | |
3374 | * lease. In all cases we got a Reply. Give dhclient-script a tickle | |
3375 | * to inform it about the new values, and then lay in wait for the next | |
3376 | * event. | |
3377 | */ | |
3378 | void | |
3379 | start_bound(struct client_state *client) | |
3380 | { | |
3381 | struct dhc6_ia *ia, *oldia; | |
3382 | struct dhc6_addr *addr, *oldaddr; | |
3383 | struct dhc6_lease *lease, *old; | |
06eb8bab | 3384 | const char *reason; |
98bd7ca0 DH |
3385 | TIME dns_update_offset = 1; |
3386 | ||
3387 | lease = client->active_lease; | |
3388 | if (lease == NULL) { | |
3389 | log_error("Cannot enter bound state unless an active lease " | |
3390 | "is selected."); | |
3391 | return; | |
3392 | } | |
3393 | old = client->old_lease; | |
3394 | ||
3395 | client->v6_handler = bound_handler; | |
3396 | ||
3397 | switch (client->state) { | |
3398 | case S_SELECTING: | |
3399 | case S_REBOOTING: /* Pretend we got bound. */ | |
3400 | reason = "BOUND6"; | |
3401 | break; | |
3402 | ||
3403 | case S_RENEWING: | |
3404 | reason = "RENEW6"; | |
3405 | break; | |
3406 | ||
3407 | case S_REBINDING: | |
3408 | reason = "REBIND6"; | |
3409 | break; | |
3410 | ||
3411 | default: | |
3412 | log_fatal("Impossible condition at %s:%d.", MDL); | |
6705543f DH |
3413 | /* Silence compiler warnings. */ |
3414 | return; | |
98bd7ca0 DH |
3415 | } |
3416 | ||
3417 | log_debug("PRC: Bound to lease %s.", | |
3418 | print_hex_1(client->active_lease->server_id.len, | |
3419 | client->active_lease->server_id.data, 55)); | |
3420 | client->state = S_BOUND; | |
3421 | ||
3422 | write_client6_lease(client, lease, 0, 1); | |
3423 | ||
3424 | oldia = NULL; | |
3425 | for (ia = lease->bindings ; ia != NULL ; ia = ia->next) { | |
1d9774ab FD |
3426 | if (ia->ia_type != D6O_IA_NA) |
3427 | continue; | |
3428 | ||
98bd7ca0 | 3429 | if (old != NULL) |
1d9774ab | 3430 | oldia = find_ia_na(old->bindings, (char *)ia->iaid); |
98bd7ca0 DH |
3431 | else |
3432 | oldia = NULL; | |
3433 | ||
3434 | for (addr = ia->addrs ; addr != NULL ; addr = addr->next) { | |
3435 | if (oldia != NULL) | |
3436 | oldaddr = find_addr(oldia->addrs, | |
3437 | &addr->address); | |
3438 | else | |
3439 | oldaddr = NULL; | |
3440 | ||
3441 | if (oldaddr == NULL) | |
3442 | dhclient_schedule_updates(client, | |
3443 | &addr->address, | |
3444 | dns_update_offset++); | |
3445 | ||
3446 | /* Shell out to setup the new binding. */ | |
3447 | script_init(client, reason, NULL); | |
3448 | ||
3449 | if (old != NULL) | |
3450 | dhc6_marshall_values("old_", client, old, | |
3451 | oldia, oldaddr); | |
3452 | dhc6_marshall_values("new_", client, lease, ia, addr); | |
3453 | ||
3454 | script_go(client); | |
3455 | } | |
3456 | ||
3457 | /* XXX: maybe we should loop on the old values instead? */ | |
3458 | if (ia->addrs == NULL) { | |
3459 | script_init(client, reason, NULL); | |
3460 | ||
3461 | if (old != NULL) | |
3462 | dhc6_marshall_values("old_", client, old, | |
8ea19a71 DH |
3463 | oldia, |
3464 | oldia != NULL ? | |
3465 | oldia->addrs : NULL); | |
98bd7ca0 DH |
3466 | |
3467 | dhc6_marshall_values("new_", client, lease, ia, | |
3468 | NULL); | |
8ea19a71 DH |
3469 | |
3470 | script_go(client); | |
98bd7ca0 DH |
3471 | } |
3472 | } | |
3473 | ||
3474 | /* XXX: maybe we should loop on the old values instead? */ | |
3475 | if (lease->bindings == NULL) { | |
3476 | script_init(client, reason, NULL); | |
3477 | ||
3478 | if (old != NULL) | |
3479 | dhc6_marshall_values("old_", client, old, | |
3480 | old->bindings, | |
3481 | (old->bindings != NULL) ? | |
3482 | old->bindings->addrs : NULL); | |
3483 | ||
3484 | dhc6_marshall_values("new_", client, lease, NULL, NULL); | |
8ea19a71 DH |
3485 | |
3486 | script_go(client); | |
98bd7ca0 DH |
3487 | } |
3488 | ||
8ea19a71 DH |
3489 | go_daemon(); |
3490 | ||
98bd7ca0 | 3491 | if (client->old_lease != NULL) { |
e32529a5 | 3492 | dhc6_lease_destroy(&client->old_lease, MDL); |
98bd7ca0 DH |
3493 | client->old_lease = NULL; |
3494 | } | |
3495 | ||
3496 | /* Schedule events. */ | |
3497 | dhc6_check_times(client); | |
3498 | } | |
3499 | ||
3500 | /* While bound, ignore packets. In the future we'll want to answer | |
3501 | * Reconfigure-Request messages and the like. | |
3502 | */ | |
3503 | void | |
3504 | bound_handler(struct packet *packet, struct client_state *client) | |
3505 | { | |
3506 | log_debug("RCV: Input packets are ignored once bound."); | |
3507 | } | |
3508 | ||
3509 | /* start_renew6() gets us all ready to go to start transmitting Renew packets. | |
be62cf06 FD |
3510 | * Note that client->next_MRD must be set before entering this function - |
3511 | * it must be set to the time at which the client should start Rebinding. | |
98bd7ca0 DH |
3512 | */ |
3513 | void | |
3514 | start_renew6(void *input) | |
3515 | { | |
3516 | struct client_state *client; | |
3517 | ||
3518 | client = (struct client_state *)input; | |
3519 | ||
3520 | log_info("PRC: Renewing lease on %s.", | |
3521 | client->name ? client->name : client->interface->name); | |
3522 | client->state = S_RENEWING; | |
3523 | ||
3524 | client->v6_handler = reply_handler; | |
3525 | ||
3526 | /* Times per RFC3315 section 18.1.3. */ | |
be62cf06 FD |
3527 | client->IRT = REN_TIMEOUT * 100; |
3528 | client->MRT = REN_MAX_RT * 100; | |
98bd7ca0 DH |
3529 | client->MRC = 0; |
3530 | /* MRD is special in renew - we need to set it by checking timer | |
3531 | * state. | |
3532 | */ | |
be62cf06 | 3533 | client->MRD = client->next_MRD - cur_time; |
98bd7ca0 DH |
3534 | |
3535 | dhc6_retrans_init(client); | |
3536 | ||
3537 | client->refresh_type = DHCPV6_RENEW; | |
3538 | do_refresh6(client); | |
3539 | } | |
3540 | ||
3541 | /* do_refresh6() transmits one DHCPv6 packet, be it a Renew or Rebind, and | |
3542 | * gives the retransmission state a bump for the next time. Note that | |
3543 | * client->refresh_type must be set before entering this function. | |
3544 | */ | |
3545 | void | |
3546 | do_refresh6(void *input) | |
3547 | { | |
3548 | struct option_cache *oc; | |
3549 | struct sockaddr_in6 unicast, *dest_addr = &DHCPv6DestAddr; | |
3550 | struct data_string ds; | |
3551 | struct client_state *client; | |
3552 | struct dhc6_lease *lease; | |
be62cf06 | 3553 | struct timeval elapsed, tv; |
98bd7ca0 DH |
3554 | int send_ret; |
3555 | ||
3556 | client = (struct client_state *)input; | |
3557 | ||
3558 | lease = client->active_lease; | |
3559 | if (lease == NULL) { | |
3560 | log_error("Cannot renew without an active binding."); | |
3561 | return; | |
3562 | } | |
3563 | ||
3564 | /* Ensure we're emitting a valid message type. */ | |
3565 | switch (client->refresh_type) { | |
3566 | case DHCPV6_RENEW: | |
3567 | case DHCPV6_REBIND: | |
3568 | break; | |
3569 | ||
3570 | default: | |
3571 | log_fatal("Internal inconsistency (%d) at %s:%d.", | |
3572 | client->refresh_type, MDL); | |
3573 | } | |
3574 | ||
be62cf06 FD |
3575 | /* |
3576 | * Start_time starts at the first transmission. | |
3577 | */ | |
3578 | if (client->txcount == 0) { | |
3579 | client->start_time.tv_sec = cur_tv.tv_sec; | |
3580 | client->start_time.tv_usec = cur_tv.tv_usec; | |
3581 | } | |
3582 | ||
3583 | /* elapsed = cur - start */ | |
3584 | elapsed.tv_sec = cur_tv.tv_sec - client->start_time.tv_sec; | |
3585 | elapsed.tv_usec = cur_tv.tv_usec - client->start_time.tv_usec; | |
3586 | if (elapsed.tv_usec < 0) { | |
3587 | elapsed.tv_sec -= 1; | |
3588 | elapsed.tv_usec += 1000000; | |
3589 | } | |
98bd7ca0 | 3590 | if (((client->MRC != 0) && (client->txcount > client->MRC)) || |
be62cf06 | 3591 | ((client->MRD != 0) && (elapsed.tv_sec >= client->MRD))) { |
98bd7ca0 DH |
3592 | /* We're done. Move on to the next phase, if any. */ |
3593 | dhc6_check_times(client); | |
3594 | return; | |
3595 | } | |
3596 | ||
3597 | /* | |
3598 | * Check whether the server has sent a unicast option; if so, we can | |
b51c785f | 3599 | * use the address it specified for RENEWs. |
98bd7ca0 DH |
3600 | */ |
3601 | oc = lookup_option(&dhcpv6_universe, lease->options, D6O_UNICAST); | |
3602 | if (oc && evaluate_option_cache(&ds, NULL, NULL, NULL, | |
3603 | lease->options, NULL, &global_scope, | |
3604 | oc, MDL)) { | |
3605 | if (ds.len < 16) { | |
3606 | log_error("Invalid unicast option length %d.", ds.len); | |
3607 | } else { | |
3608 | memset(&unicast, 0, sizeof(DHCPv6DestAddr)); | |
3609 | unicast.sin6_family = AF_INET6; | |
3610 | unicast.sin6_port = remote_port; | |
3611 | memcpy(&unicast.sin6_addr, ds.data, 16); | |
b51c785f FD |
3612 | if (client->refresh_type == DHCPV6_RENEW) { |
3613 | dest_addr = &unicast; | |
3614 | } | |
98bd7ca0 DH |
3615 | } |
3616 | ||
3617 | data_string_forget(&ds, MDL); | |
3618 | } | |
3619 | ||
3620 | /* Commence forming a renew packet. */ | |
3621 | memset(&ds, 0, sizeof(ds)); | |
3622 | if (!buffer_allocate(&ds.buffer, 4, MDL)) { | |
3623 | log_error("Unable to allocate memory for packet."); | |
3624 | return; | |
3625 | } | |
3626 | ds.data = ds.buffer->data; | |
3627 | ds.len = 4; | |
3628 | ||
3629 | ds.buffer->data[0] = client->refresh_type; | |
3630 | memcpy(ds.buffer->data + 1, client->dhcpv6_transaction_id, 3); | |
3631 | ||
be62cf06 FD |
3632 | /* Form an elapsed option. */ |
3633 | /* Maximum value is 65535 1/100s coded as 0xffff. */ | |
3634 | if ((elapsed.tv_sec < 0) || (elapsed.tv_sec > 655) || | |
3635 | ((elapsed.tv_sec == 655) && (elapsed.tv_usec > 350000))) { | |
98bd7ca0 | 3636 | client->elapsed = 0xffff; |
be62cf06 FD |
3637 | } else { |
3638 | client->elapsed = elapsed.tv_sec * 100; | |
3639 | client->elapsed += elapsed.tv_usec / 10000; | |
3640 | } | |
98bd7ca0 | 3641 | |
be62cf06 FD |
3642 | if (client->elapsed == 0) |
3643 | log_debug("XMT: Forming %s, 0 ms elapsed.", | |
3644 | dhcpv6_type_names[client->refresh_type]); | |
3645 | else | |
3646 | log_debug("XMT: Forming %s, %u0 ms elapsed.", | |
3647 | dhcpv6_type_names[client->refresh_type], | |
3648 | (unsigned)client->elapsed); | |
98bd7ca0 DH |
3649 | |
3650 | client->elapsed = htons(client->elapsed); | |
3651 | ||
3652 | make_client6_options(client, &client->sent_options, lease, | |
3653 | client->refresh_type); | |
3654 | ||
3655 | /* Put in any options from the sent cache. */ | |
3656 | dhcpv6_universe.encapsulate(&ds, NULL, NULL, client, NULL, | |
3657 | client->sent_options, &global_scope, | |
3658 | &dhcpv6_universe); | |
3659 | ||
af5fa176 | 3660 | /* Append IA's */ |
1d9774ab FD |
3661 | if (dhc6_add_ia_na(client, &ds, lease, |
3662 | client->refresh_type) != ISC_R_SUCCESS) { | |
98bd7ca0 DH |
3663 | data_string_forget(&ds, MDL); |
3664 | return; | |
3665 | } | |
3666 | ||
be62cf06 | 3667 | log_info("XMT: %s on %s, interval %ld0ms.", |
98bd7ca0 DH |
3668 | dhcpv6_type_names[client->refresh_type], |
3669 | client->name ? client->name : client->interface->name, | |
9aa669fc | 3670 | (long int)client->RT); |
98bd7ca0 DH |
3671 | |
3672 | send_ret = send_packet6(client->interface, ds.data, ds.len, dest_addr); | |
3673 | ||
3674 | if (send_ret != ds.len) { | |
3675 | log_error("dhc6: send_packet6() sent %d of %d bytes", | |
3676 | send_ret, ds.len); | |
3677 | } | |
3678 | ||
3679 | data_string_forget(&ds, MDL); | |
3680 | ||
be62cf06 FD |
3681 | /* Wait RT */ |
3682 | tv.tv_sec = cur_tv.tv_sec + client->RT / 100; | |
3683 | tv.tv_usec = cur_tv.tv_usec + (client->RT % 100) * 10000; | |
3684 | if (tv.tv_usec >= 1000000) { | |
3685 | tv.tv_sec += 1; | |
3686 | tv.tv_usec -= 1000000; | |
3687 | } | |
3688 | add_timeout(&tv, do_refresh6, client, NULL, NULL); | |
98bd7ca0 DH |
3689 | |
3690 | dhc6_retrans_advance(client); | |
3691 | } | |
3692 | ||
3693 | /* start_rebind6() gets us all set up to go and rebind a lease. Note that | |
be62cf06 | 3694 | * client->next_MRD must be set before entering this function. In this case, |
98bd7ca0 DH |
3695 | * MRD must be set to the maximum time any address in the packet will |
3696 | * expire. | |
3697 | */ | |
3698 | void | |
3699 | start_rebind6(void *input) | |
3700 | { | |
3701 | struct client_state *client; | |
3702 | ||
3703 | client = (struct client_state *)input; | |
3704 | ||
3705 | log_info("PRC: Rebinding lease on %s.", | |
3706 | client->name ? client->name : client->interface->name); | |
3707 | client->state = S_REBINDING; | |
3708 | ||
3709 | client->v6_handler = reply_handler; | |
3710 | ||
3711 | /* Times per RFC3315 section 18.1.4. */ | |
be62cf06 FD |
3712 | client->IRT = REB_TIMEOUT * 100; |
3713 | client->MRT = REB_MAX_RT * 100; | |
98bd7ca0 DH |
3714 | client->MRC = 0; |
3715 | /* MRD is special in rebind - it's determined by the timer | |
3716 | * state. | |
3717 | */ | |
be62cf06 | 3718 | client->MRD = client->next_MRD - cur_time; |
98bd7ca0 DH |
3719 | |
3720 | dhc6_retrans_init(client); | |
3721 | ||
3722 | client->refresh_type = DHCPV6_REBIND; | |
3723 | do_refresh6(client); | |
3724 | } | |
3725 | ||
3726 | /* do_depref() runs through a given lease's addresses, for each that has | |
3727 | * not yet been depreffed, shells out to the dhclient-script to inform it | |
3728 | * of the status change. The dhclient-script should then do...something... | |
3729 | * to encourage applications to move off the address and onto one of the | |
3730 | * remaining 'preferred' addresses. | |
3731 | */ | |
3732 | void | |
3733 | do_depref(void *input) | |
3734 | { | |
3735 | struct client_state *client; | |
3736 | struct dhc6_lease *lease; | |
3737 | struct dhc6_ia *ia; | |
3738 | struct dhc6_addr *addr; | |
3739 | ||
3740 | client = (struct client_state *)input; | |
3741 | ||
3742 | lease = client->active_lease; | |
3743 | if (lease == NULL) | |
3744 | return; | |
3745 | ||
3746 | for (ia = lease->bindings ; ia != NULL ; ia = ia->next) { | |
1d9774ab FD |
3747 | if (ia->ia_type != D6O_IA_NA) |
3748 | continue; | |
98bd7ca0 DH |
3749 | for (addr = ia->addrs ; addr != NULL ; addr = addr->next) { |
3750 | if (addr->flags & DHC6_ADDR_DEPREFFED) | |
3751 | continue; | |
3752 | ||
3753 | if (addr->starts + addr->preferred_life <= cur_time) { | |
3754 | script_init(client, "DEPREF6", NULL); | |
3755 | dhc6_marshall_values("cur_", client, lease, | |
3756 | ia, addr); | |
3757 | script_go(client); | |
3758 | ||
3759 | log_info("PRC: Address %s depreferred.", | |
3760 | print_hex_1(addr->address.len, | |
3761 | addr->address.iabuf, | |
3762 | 50)); | |
3763 | ||
3764 | ||
3765 | addr->flags |= DHC6_ADDR_DEPREFFED; | |
3766 | ||
3767 | /* Remove DDNS bindings at depref time. */ | |
3768 | if (client->config->do_forward_update) | |
3769 | client_dns_update(client, 0, 0, | |
3770 | &addr->address); | |
3771 | } | |
3772 | } | |
3773 | } | |
3774 | ||
3775 | dhc6_check_times(client); | |
3776 | } | |
3777 | ||
3778 | /* do_expire() searches through all the addresses on a given lease, and | |
3779 | * expires/removes any addresses that are no longer valid. | |
3780 | */ | |
3781 | void | |
3782 | do_expire(void *input) | |
3783 | { | |
3784 | struct client_state *client; | |
3785 | struct dhc6_lease *lease; | |
3786 | struct dhc6_ia *ia; | |
3787 | struct dhc6_addr *addr; | |
3788 | int has_addrs = ISC_FALSE; | |
3789 | ||
3790 | client = (struct client_state *)input; | |
3791 | ||
3792 | lease = client->active_lease; | |
3793 | if (lease == NULL) | |
3794 | return; | |
3795 | ||
3796 | for (ia = lease->bindings ; ia != NULL ; ia = ia->next) { | |
1d9774ab FD |
3797 | if (ia->ia_type != D6O_IA_NA) |
3798 | continue; | |
98bd7ca0 DH |
3799 | for (addr = ia->addrs ; addr != NULL ; addr = addr->next) { |
3800 | if (addr->flags & DHC6_ADDR_EXPIRED) | |
3801 | continue; | |
3802 | ||
3803 | if (addr->starts + addr->max_life <= cur_time) { | |
3804 | script_init(client, "EXPIRE6", NULL); | |
3805 | dhc6_marshall_values("old_", client, lease, | |
3806 | ia, addr); | |
3807 | script_go(client); | |
3808 | ||
3809 | addr->flags |= DHC6_ADDR_EXPIRED; | |
3810 | ||
3811 | log_info("PRC: Address %s expired.", | |
3812 | print_hex_1(addr->address.len, | |
3813 | addr->address.iabuf, | |
3814 | 50)); | |
3815 | ||
3816 | /* We remove DNS records at depref time, but | |
3817 | * it is possible that we might get here | |
3818 | * without depreffing. | |
3819 | */ | |
3820 | if (client->config->do_forward_update && | |
3821 | !(addr->flags & DHC6_ADDR_DEPREFFED)) | |
3822 | client_dns_update(client, 0, 0, | |
3823 | &addr->address); | |
3824 | ||
3825 | continue; | |
3826 | } | |
3827 | ||
3828 | has_addrs = ISC_TRUE; | |
3829 | } | |
3830 | } | |
3831 | ||
3832 | /* Clean up empty leases. */ | |
3833 | if (has_addrs == ISC_FALSE) { | |
3834 | log_info("PRC: Bound lease is devoid of active addresses." | |
3835 | " Re-initializing."); | |
3836 | ||
e32529a5 | 3837 | dhc6_lease_destroy(&lease, MDL); |
98bd7ca0 DH |
3838 | client->active_lease = NULL; |
3839 | ||
3840 | start_init6(client); | |
3841 | return; | |
3842 | } | |
3843 | ||
3844 | /* Schedule the next run through. */ | |
3845 | dhc6_check_times(client); | |
3846 | } | |
3847 | ||
af5fa176 EH |
3848 | /* |
3849 | * Run client script to unconfigure interface. | |
3850 | * Called with reason STOP6 when dhclient -x is run, or with reason | |
3851 | * RELEASE6 when server has replied to a Release message. | |
3852 | */ | |
3853 | void | |
3854 | unconfigure6(struct client_state *client, const char *reason) | |
3855 | { | |
af5fa176 EH |
3856 | struct dhc6_ia *ia; |
3857 | struct dhc6_addr *addr; | |
3858 | ||
3859 | if (client->active_lease == NULL) | |
3860 | return; | |
3861 | ||
3862 | for (ia = client->active_lease->bindings ; ia != NULL ; ia = ia->next) { | |
1d9774ab FD |
3863 | if (ia->ia_type != D6O_IA_NA) |
3864 | continue; | |
3865 | ||
af5fa176 EH |
3866 | for (addr = ia->addrs ; addr != NULL ; addr = addr->next) { |
3867 | script_init(client, reason, NULL); | |
06eb8bab SK |
3868 | dhc6_marshall_values("old_", client, |
3869 | client->active_lease, ia, addr); | |
af5fa176 EH |
3870 | script_go(client); |
3871 | ||
3872 | if (client->config->do_forward_update) | |
3873 | client_dns_update(client, 0, 0, &addr->address); | |
3874 | } | |
3875 | } | |
3876 | } | |
3877 | ||
98bd7ca0 DH |
3878 | /* make_client6_options() fetches option caches relevant to the client's |
3879 | * scope and places them into the sent_options cache. This cache is later | |
3880 | * used to populate DHCPv6 output packets with options. | |
3881 | */ | |
3882 | static void | |
3883 | make_client6_options(struct client_state *client, struct option_state **op, | |
3884 | struct dhc6_lease *lease, u_int8_t message) | |
3885 | { | |
98bd7ca0 | 3886 | struct option_cache *oc; |
0c20eab3 | 3887 | struct option **req; |
e5d83524 DH |
3888 | struct buffer *buffer; |
3889 | int buflen, i, oro_len; | |
98bd7ca0 DH |
3890 | |
3891 | if ((op == NULL) || (client == NULL)) | |
3892 | return; | |
3893 | ||
3894 | if (*op) | |
3895 | option_state_dereference(op, MDL); | |
3896 | ||
0c20eab3 | 3897 | /* Create a cache to carry options to transmission. */ |
98bd7ca0 DH |
3898 | option_state_allocate(op, MDL); |
3899 | ||
0c20eab3 | 3900 | /* Create and store an 'elapsed time' option in the cache. */ |
98bd7ca0 DH |
3901 | oc = NULL; |
3902 | if (option_cache_allocate(&oc, MDL)) { | |
3903 | const unsigned char *cdata; | |
3904 | ||
3905 | cdata = (unsigned char *)&client->elapsed; | |
3906 | ||
3907 | if (make_const_data(&oc->expression, cdata, 2, 0, 0, MDL)) { | |
3908 | option_reference(&oc->option, elapsed_option, MDL); | |
3909 | save_option(&dhcpv6_universe, *op, oc); | |
3910 | } | |
3911 | ||
3912 | option_cache_dereference(&oc, MDL); | |
3913 | } | |
3914 | ||
9ab0cc6a DH |
3915 | /* Bring in any configured options to send. */ |
3916 | if (client->config->on_transmission) | |
3917 | execute_statements_in_scope(NULL, NULL, NULL, client, | |
3918 | lease ? lease->options : NULL, | |
3919 | *op, &global_scope, | |
3920 | client->config->on_transmission, | |
3921 | NULL); | |
3922 | ||
6d7f9584 FD |
3923 | /* Rapid-commit is only for SOLICITs. */ |
3924 | if (message != DHCPV6_SOLICIT) | |
3925 | delete_option(&dhcpv6_universe, *op, D6O_RAPID_COMMIT); | |
3926 | ||
98bd7ca0 DH |
3927 | /* See if the user configured a DUID in a relevant scope. If not, |
3928 | * introduce our default manufactured id. | |
3929 | */ | |
3930 | if ((oc = lookup_option(&dhcpv6_universe, *op, | |
3931 | D6O_CLIENTID)) == NULL) { | |
3932 | if (!option_cache(&oc, &default_duid, NULL, clientid_option, | |
3933 | MDL)) | |
3934 | log_fatal("Failure assembling a DUID."); | |
3935 | ||
3936 | save_option(&dhcpv6_universe, *op, oc); | |
3937 | option_cache_dereference(&oc, MDL); | |
3938 | } | |
3939 | ||
3940 | /* In cases where we're responding to a single server, put the | |
3941 | * server's id in the response. | |
3942 | * | |
3943 | * Note that lease is NULL for SOLICIT or INFO request messages, | |
3944 | * and otherwise MUST be present. | |
3945 | */ | |
3946 | if (lease == NULL) { | |
3947 | if ((message != DHCPV6_SOLICIT) && | |
3948 | (message != DHCPV6_INFORMATION_REQUEST)) | |
3949 | log_fatal("Impossible condition at %s:%d.", MDL); | |
3950 | } else if ((message != DHCPV6_REBIND) && | |
3951 | (message != DHCPV6_CONFIRM)) { | |
3952 | oc = lookup_option(&dhcpv6_universe, lease->options, | |
3953 | D6O_SERVERID); | |
3954 | if (oc != NULL) | |
3955 | save_option(&dhcpv6_universe, *op, oc); | |
3956 | } | |
3957 | ||
0c20eab3 DH |
3958 | /* 'send dhcp6.oro foo;' syntax we used in 4.0.0a1/a2 has been |
3959 | * deprecated by adjustments to the 'request' syntax also used for | |
3960 | * DHCPv4. | |
3961 | */ | |
9ab0cc6a | 3962 | if (lookup_option(&dhcpv6_universe, *op, D6O_ORO) != NULL) |
0c20eab3 | 3963 | log_error("'send dhcp6.oro' syntax is deprecated, please " |
44a67e65 | 3964 | "use the 'request' syntax (\"man dhclient.conf\")."); |
0c20eab3 DH |
3965 | |
3966 | /* Construct and store an ORO (Option Request Option). It is a | |
3967 | * fatal error to fail to send an ORO (of at least zero length). | |
3968 | * | |
3969 | * Discussion: RFC3315 appears to be inconsistent in its statements | |
3970 | * of whether or not the ORO is mandatory. In section 18.1.1 | |
3971 | * ("Creation and Transmission of Request Messages"): | |
3972 | * | |
3973 | * The client MUST include an Option Request option (see section | |
3974 | * 22.7) to indicate the options the client is interested in | |
3975 | * receiving. The client MAY include options with data values as | |
3976 | * hints to the server about parameter values the client would like | |
3977 | * to have returned. | |
3978 | * | |
3979 | * This MUST is missing from the creation/transmission of other | |
3980 | * messages (such as Renew and Rebind), and the section 22.7 ("Option | |
3981 | * Request Option" format and definition): | |
3982 | * | |
3983 | * A client MAY include an Option Request option in a Solicit, | |
3984 | * Request, Renew, Rebind, Confirm or Information-request message to | |
3985 | * inform the server about options the client wants the server to | |
3986 | * send to the client. A server MAY include an Option Request | |
3987 | * option in a Reconfigure option to indicate which options the | |
3988 | * client should request from the server. | |
3989 | * | |
3990 | * seems to relax the requirement from MUST to MAY (and still other | |
3991 | * language in RFC3315 supports this). | |
3992 | * | |
3993 | * In lieu of a clarification of RFC3315, we will conform with the | |
3994 | * MUST. Instead of an absent ORO, we will if there are no options | |
3995 | * to request supply an empty ORO. Theoretically, an absent ORO is | |
3996 | * difficult to interpret (does the client want all options or no | |
3997 | * options?). A zero-length ORO is intuitively clear: requesting | |
3998 | * nothing. | |
3999 | */ | |
e5d83524 DH |
4000 | buffer = NULL; |
4001 | oro_len = 0; | |
0c20eab3 | 4002 | buflen = 32; |
e5d83524 | 4003 | if (!buffer_allocate(&buffer, buflen, MDL)) |
0c20eab3 | 4004 | log_fatal("Out of memory constructing DHCPv6 ORO."); |
0c20eab3 DH |
4005 | req = client->config->requested_options; |
4006 | if (req != NULL) { | |
4007 | for (i = 0 ; req[i] != NULL ; i++) { | |
e5d83524 | 4008 | if (buflen == oro_len) { |
0c20eab3 DH |
4009 | struct buffer *tmpbuf = NULL; |
4010 | ||
4011 | buflen += 32; | |
4012 | ||
4013 | /* Shell game. */ | |
e5d83524 DH |
4014 | buffer_reference(&tmpbuf, buffer, MDL); |
4015 | buffer_dereference(&buffer, MDL); | |
0c20eab3 | 4016 | |
e5d83524 | 4017 | if (!buffer_allocate(&buffer, buflen, MDL)) |
0c20eab3 DH |
4018 | log_fatal("Out of memory resizing " |
4019 | "DHCPv6 ORO buffer."); | |
4020 | ||
e5d83524 | 4021 | memcpy(buffer->data, tmpbuf->data, oro_len); |
0c20eab3 DH |
4022 | |
4023 | buffer_dereference(&tmpbuf, MDL); | |
4024 | } | |
4025 | ||
4026 | if (req[i]->universe == &dhcpv6_universe) { | |
4027 | /* Append the code to the ORO. */ | |
e5d83524 | 4028 | putUShort(buffer->data + oro_len, |
0c20eab3 | 4029 | req[i]->code); |
e5d83524 | 4030 | oro_len += 2; |
0c20eab3 DH |
4031 | } |
4032 | } | |
4033 | } | |
4034 | ||
4035 | oc = NULL; | |
e5d83524 DH |
4036 | if (make_const_option_cache(&oc, &buffer, NULL, oro_len, |
4037 | oro_option, MDL)) { | |
0c20eab3 DH |
4038 | save_option(&dhcpv6_universe, *op, oc); |
4039 | } else { | |
4040 | log_fatal("Unable to create ORO option cache."); | |
4041 | } | |
4042 | ||
e5d83524 DH |
4043 | /* |
4044 | * Note: make_const_option_cache() consumes the buffer, we do not | |
4045 | * need to dereference it (XXX). | |
98bd7ca0 | 4046 | */ |
e5d83524 | 4047 | option_cache_dereference(&oc, MDL); |
98bd7ca0 DH |
4048 | } |
4049 | ||
4050 | /* A clone of the DHCPv4 script_write_params() minus the DHCPv4-specific | |
4051 | * filename, server-name, etc specifics. | |
4052 | * | |
4053 | * Simply, store all values present in all universes of the option state | |
4054 | * (probably derived from a DHCPv6 packet) into environment variables | |
4055 | * named after the option names (and universe names) but with the 'prefix' | |
4056 | * prepended. | |
4057 | * | |
4058 | * Later, dhclient-script may compare for example "new_time_servers" and | |
4059 | * "old_time_servers" for differences, and only upon detecting a change | |
4060 | * bother to rewrite ntp.conf and restart it. Or something along those | |
4061 | * generic lines. | |
4062 | */ | |
4063 | static void | |
06eb8bab | 4064 | script_write_params6(struct client_state *client, const char *prefix, |
98bd7ca0 DH |
4065 | struct option_state *options) |
4066 | { | |
4067 | struct envadd_state es; | |
4068 | int i; | |
4069 | ||
4070 | if (options == NULL) | |
4071 | return; | |
4072 | ||
4073 | es.client = client; | |
4074 | es.prefix = prefix; | |
4075 | ||
4076 | for (i = 0 ; i < options->universe_count ; i++) { | |
4077 | option_space_foreach(NULL, NULL, client, NULL, options, | |
4078 | &global_scope, universes[i], &es, | |
4079 | client_option_envadd); | |
4080 | } | |
4081 | } | |
4082 | ||
fe5b0fdd | 4083 | #endif /* DHCPv6 */ |