1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright (c) 2017, Chris Fraire <cfraire@me.com>.
24 *
25 * BOUND state of the DHCP client state machine.
26 */
27
28 #include <sys/socket.h>
29 #include <sys/types.h>
30 #include <string.h>
31 #include <netinet/in.h>
32 #include <sys/sockio.h>
33 #include <unistd.h>
34 #include <time.h>
35 #include <arpa/inet.h>
36 #include <stdlib.h>
37 #include <search.h>
38 #include <sys/sysmacros.h>
39 #include <dhcp_hostconf.h>
40 #include <dhcpagent_util.h>
41 #include <dhcpmsg.h>
42
43 #include "states.h"
44 #include "packet.h"
45 #include "util.h"
46 #include "agent.h"
47 #include "interface.h"
48 #include "script_handler.h"
49
50 /*
51 * Possible outcomes for IPv6 binding attempt.
52 */
53 enum v6_bind_result {
54 v6Restart, /* report failure and restart state machine */
55 v6Resent, /* new Request message has been sent */
56 v6Done /* successful binding */
57 };
58
59 static enum v6_bind_result configure_v6_leases(dhcp_smach_t *);
60 static boolean_t configure_v4_lease(dhcp_smach_t *);
61 static boolean_t configure_v4_timers(dhcp_smach_t *);
62
63 /*
64 * bound_event_cb(): callback for script_start on the event EVENT_BOUND
65 *
66 * input: dhcp_smach_t *: the state machine configured
67 * void *: unused
68 * output: int: always 1
69 */
70
71 /* ARGSUSED1 */
72 static int
bound_event_cb(dhcp_smach_t * dsmp,void * arg)73 bound_event_cb(dhcp_smach_t *dsmp, void *arg)
74 {
75 if (dsmp->dsm_ia.ia_fd != -1)
76 ipc_action_finish(dsmp, DHCP_IPC_SUCCESS);
77 else
78 async_finish(dsmp);
79 return (1);
80 }
81
82 /*
83 * dhcp_bound(): configures an state machine and interfaces using information
84 * contained in the ACK/Reply packet and sets up lease timers.
85 * Before starting, the requested address is verified by
86 * Duplicate Address Detection to make sure it's not in use.
87 *
88 * input: dhcp_smach_t *: the state machine to move to bound
89 * PKT_LIST *: the ACK/Reply packet, or NULL to use dsmp->dsm_ack
90 * output: boolean_t: B_TRUE on success, B_FALSE on failure
91 */
92
93 boolean_t
dhcp_bound(dhcp_smach_t * dsmp,PKT_LIST * ack)94 dhcp_bound(dhcp_smach_t *dsmp, PKT_LIST *ack)
95 {
96 DHCPSTATE oldstate;
97 lease_t new_lease;
98 dhcp_lif_t *lif;
99 dhcp_lease_t *dlp;
100 enum v6_bind_result v6b;
101
102 if (ack != NULL) {
103 /* If ack we're replacing is not the original, then free it */
104 if (dsmp->dsm_ack != dsmp->dsm_orig_ack)
105 free_pkt_entry(dsmp->dsm_ack);
106 dsmp->dsm_ack = ack;
107 /* Save the first ack as the original */
108 if (dsmp->dsm_orig_ack == NULL)
109 dsmp->dsm_orig_ack = ack;
110
111 save_domainname(dsmp, ack);
112 }
113
114 oldstate = dsmp->dsm_state;
115 switch (oldstate) {
116
117 case ADOPTING:
118 /* Note that adoption occurs only for IPv4 DHCP. */
119
120 /* Ignore BOOTP */
121 if (ack->opts[CD_DHCP_TYPE] == NULL)
122 return (B_FALSE);
123
124 /*
125 * if we're adopting a lease, the lease timers
126 * only provide an upper bound since we don't know
127 * from what time they are relative to. assume we
128 * have a lease time of at most DHCP_ADOPT_LEASE_MAX.
129 */
130 (void) memcpy(&new_lease, ack->opts[CD_LEASE_TIME]->value,
131 sizeof (lease_t));
132
133 new_lease = htonl(MIN(ntohl(new_lease), DHCP_ADOPT_LEASE_MAX));
134
135 (void) memcpy(ack->opts[CD_LEASE_TIME]->value, &new_lease,
136 sizeof (lease_t));
137
138 /*
139 * we have no idea when the REQUEST that generated
140 * this ACK was sent, but for diagnostic purposes
141 * we'll assume its close to the current time.
142 */
143 dsmp->dsm_newstart_monosec = monosec();
144
145 if (dsmp->dsm_isv6) {
146 if ((v6b = configure_v6_leases(dsmp)) != v6Done)
147 return (v6b == v6Resent);
148 } else {
149 if (!configure_v4_lease(dsmp))
150 return (B_FALSE);
151
152 if (!configure_v4_timers(dsmp))
153 return (B_FALSE);
154 }
155
156 dsmp->dsm_curstart_monosec = dsmp->dsm_newstart_monosec;
157 write_lease_to_hostconf(dsmp);
158 break;
159
160 case SELECTING:
161 case REQUESTING:
162 case INIT_REBOOT:
163
164 if (dsmp->dsm_isv6) {
165 if ((v6b = configure_v6_leases(dsmp)) != v6Done)
166 return (v6b == v6Resent);
167 } else {
168 if (!configure_v4_lease(dsmp))
169 return (B_FALSE);
170
171 if (!configure_v4_timers(dsmp))
172 return (B_FALSE);
173
174 if (!clear_lif_deprecated(dsmp->dsm_lif))
175 return (B_FALSE);
176 }
177
178 /* Stop sending requests now */
179 stop_pkt_retransmission(dsmp);
180
181 /*
182 * If we didn't end up with any usable leases, then we have a
183 * problem.
184 */
185 if (dsmp->dsm_leases == NULL) {
186 dhcpmsg(MSG_WARNING,
187 "dhcp_bound: no address lease established");
188 return (B_FALSE);
189 }
190
191 /*
192 * If this is a Rapid-Commit (selecting state) or if we're
193 * dealing with a reboot (init-reboot), then we will have a new
194 * server ID to save.
195 */
196 if (ack != NULL &&
197 (oldstate == SELECTING || oldstate == INIT_REBOOT) &&
198 dsmp->dsm_isv6 && !save_server_id(dsmp, ack)) {
199 dhcpmsg(MSG_ERROR,
200 "dhcp_bound: unable to save server ID on %s",
201 dsmp->dsm_name);
202 return (B_FALSE);
203 }
204
205 /*
206 * We will continue configuring the interfaces via
207 * dhcp_bound_complete, once kernel DAD completes. If no new
208 * leases were created (which can happen on an init-reboot used
209 * for link-up confirmation), then go straight to bound state.
210 */
211 if (!set_smach_state(dsmp, PRE_BOUND))
212 return (B_FALSE);
213 if (dsmp->dsm_lif_wait == 0)
214 dhcp_bound_complete(dsmp);
215 break;
216
217 case PRE_BOUND:
218 case BOUND:
219 case INFORMATION:
220 /* This is just a duplicate ack; silently ignore it */
221 return (B_TRUE);
222
223 case RENEWING:
224 case REBINDING:
225
226 if (dsmp->dsm_isv6) {
227 if ((v6b = configure_v6_leases(dsmp)) != v6Done)
228 return (v6b == v6Resent);
229 } else {
230 if (!configure_v4_timers(dsmp))
231 return (B_FALSE);
232 if (!clear_lif_deprecated(dsmp->dsm_lif))
233 return (B_FALSE);
234 }
235
236 /*
237 * If some or all of the leases were torn down by the server,
238 * then handle that as an expiry. When the script is done
239 * running for the LOSS6 event, we'll end up back here.
240 */
241 if ((lif = find_expired_lif(dsmp)) != NULL) {
242 hold_lif(lif);
243 dhcp_expire(NULL, lif);
244 while ((lif = find_expired_lif(dsmp)) != NULL) {
245 dlp = lif->lif_lease;
246 unplumb_lif(lif);
247 if (dlp->dl_nlifs == 0)
248 remove_lease(dlp);
249 }
250 if (dsmp->dsm_leases == NULL)
251 return (B_FALSE);
252 }
253
254 if (oldstate == REBINDING && dsmp->dsm_isv6 &&
255 !save_server_id(dsmp, ack)) {
256 return (B_FALSE);
257 }
258
259 /*
260 * Handle Renew/Rebind that fails to address one of our leases.
261 * (Should just never happen, but RFC 3315 section 18.1.8
262 * requires it, and TAHI tests for it.)
263 */
264 for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
265 if (dlp->dl_stale && dlp->dl_nlifs > 0)
266 break;
267 }
268 if (dlp != NULL) {
269 dhcpmsg(MSG_DEBUG, "dhcp_bound: lease not updated; "
270 "allow retransmit");
271 return (B_TRUE);
272 }
273
274 if (!set_smach_state(dsmp, BOUND))
275 return (B_FALSE);
276
277 (void) script_start(dsmp, dsmp->dsm_isv6 ? EVENT_EXTEND6 :
278 EVENT_EXTEND, bound_event_cb, NULL, NULL);
279
280 dsmp->dsm_curstart_monosec = dsmp->dsm_newstart_monosec;
281 write_lease_to_hostconf(dsmp);
282
283 /* Stop sending requests now */
284 stop_pkt_retransmission(dsmp);
285 break;
286
287 case INFORM_SENT:
288
289 if (dsmp->dsm_isv6 && !save_server_id(dsmp, ack)) {
290 return (B_FALSE);
291 }
292
293 (void) bound_event_cb(dsmp, NULL);
294 if (!set_smach_state(dsmp, INFORMATION))
295 return (B_FALSE);
296
297 /* Stop sending requests now */
298 stop_pkt_retransmission(dsmp);
299 break;
300
301 default:
302 /* something is really bizarre... */
303 dhcpmsg(MSG_DEBUG,
304 "dhcp_bound: called in unexpected state: %s",
305 dhcp_state_to_string(dsmp->dsm_state));
306 return (B_FALSE);
307 }
308
309 return (B_TRUE);
310 }
311
312 /*
313 * dhcp_bound_complete(): complete interface configuration after DAD
314 *
315 * input: dhcp_smach_t *: the state machine now ready
316 * output: none
317 */
318
319 void
dhcp_bound_complete(dhcp_smach_t * dsmp)320 dhcp_bound_complete(dhcp_smach_t *dsmp)
321 {
322 PKT_LIST *ack;
323 DHCP_OPT *router_list;
324 int i;
325 DHCPSTATE oldstate;
326 dhcp_lif_t *lif;
327
328 /*
329 * Do bound state entry processing only if running IPv4. There's no
330 * need for this with DHCPv6 because link-locals are used for I/O and
331 * because DHCPv6 isn't entangled with routing.
332 */
333 if (dsmp->dsm_isv6) {
334 (void) set_smach_state(dsmp, BOUND);
335 dhcpmsg(MSG_DEBUG, "dhcp_bound_complete: bound %s",
336 dsmp->dsm_name);
337 (void) script_start(dsmp, EVENT_BOUND6, bound_event_cb, NULL,
338 NULL);
339 dsmp->dsm_curstart_monosec = dsmp->dsm_newstart_monosec;
340 write_lease_to_hostconf(dsmp);
341 return;
342 }
343
344 /*
345 * Add each provided router; we'll clean them up when the
346 * state machine goes away or when our lease expires.
347 *
348 * Note that we do not handle default routers on IPv4 logicals;
349 * see README for details.
350 */
351
352 ack = dsmp->dsm_ack;
353 router_list = ack->opts[CD_ROUTER];
354 for (i = 0; i < dsmp->dsm_pillen; i++) {
355 if (dsmp->dsm_pil[i] == CD_ROUTER)
356 router_list = NULL;
357 }
358 lif = dsmp->dsm_lif;
359 if (router_list != NULL &&
360 (router_list->len % sizeof (ipaddr_t)) == 0 &&
361 strchr(lif->lif_name, ':') == NULL &&
362 !lif->lif_pif->pif_under_ipmp) {
363
364 dsmp->dsm_nrouters = router_list->len / sizeof (ipaddr_t);
365 dsmp->dsm_routers = malloc(router_list->len);
366 if (dsmp->dsm_routers == NULL) {
367 dhcpmsg(MSG_ERR, "dhcp_bound_complete: cannot allocate "
368 "default router list, ignoring default routers");
369 dsmp->dsm_nrouters = 0;
370 }
371
372 for (i = 0; i < dsmp->dsm_nrouters; i++) {
373
374 (void) memcpy(&dsmp->dsm_routers[i].s_addr,
375 router_list->value + (i * sizeof (ipaddr_t)),
376 sizeof (ipaddr_t));
377
378 if (!add_default_route(lif->lif_pif->pif_index,
379 &dsmp->dsm_routers[i])) {
380 dhcpmsg(MSG_ERR, "dhcp_bound_complete: cannot "
381 "add default router %s on %s", inet_ntoa(
382 dsmp->dsm_routers[i]), dsmp->dsm_name);
383 dsmp->dsm_routers[i].s_addr = htonl(INADDR_ANY);
384 continue;
385 }
386
387 dhcpmsg(MSG_INFO, "added default router %s on %s",
388 inet_ntoa(dsmp->dsm_routers[i]), dsmp->dsm_name);
389 }
390 }
391
392 oldstate = dsmp->dsm_state;
393 if (!set_smach_state(dsmp, BOUND)) {
394 dhcpmsg(MSG_ERR,
395 "dhcp_bound_complete: cannot set bound state on %s",
396 dsmp->dsm_name);
397 return;
398 }
399
400 dhcpmsg(MSG_DEBUG, "dhcp_bound_complete: bound %s", dsmp->dsm_name);
401
402 /*
403 * We're now committed to this binding, so if it came from BOOTP, set
404 * the flag.
405 */
406
407 if (ack->opts[CD_DHCP_TYPE] == NULL)
408 dsmp->dsm_dflags |= DHCP_IF_BOOTP;
409
410 /*
411 * If the previous state was ADOPTING, event loop has not been started
412 * at this time; so don't run the EVENT_BOUND script.
413 */
414 if (oldstate != ADOPTING) {
415 (void) script_start(dsmp, EVENT_BOUND, bound_event_cb, NULL,
416 NULL);
417 }
418
419 dsmp->dsm_curstart_monosec = dsmp->dsm_newstart_monosec;
420 write_lease_to_hostconf(dsmp);
421 }
422
423 /*
424 * fuzzify(): adds some "fuzz" to a t1/t2 time, in accordance with RFC2131.
425 * We use up to plus or minus 2% jitter in the time. This is a
426 * small value, but the timers involved are typically long. A
427 * common T1 value is one day, and the fuzz is up to 28.8 minutes;
428 * plenty of time to make sure that individual clients don't renew
429 * all at the same time.
430 *
431 * input: uint32_t: the number of seconds until lease expiration
432 * double: the approximate percentage of that time to return
433 * output: double: a number approximating (sec * pct)
434 */
435
436 static double
fuzzify(uint32_t sec,double pct)437 fuzzify(uint32_t sec, double pct)
438 {
439 return (sec * (pct + (drand48() - 0.5) / 25.0));
440 }
441
442 /*
443 * get_pkt_times(): pulls the lease times out of a v4 DHCP packet and stores
444 * them as host byte-order relative times in the passed in
445 * parameters.
446 *
447 * input: PKT_LIST *: the packet to pull the packet times from
448 * lease_t *: where to store the relative lease time in hbo
449 * lease_t *: where to store the relative t1 time in hbo
450 * lease_t *: where to store the relative t2 time in hbo
451 * output: void
452 */
453
454 static void
get_pkt_times(PKT_LIST * ack,lease_t * lease,lease_t * t1,lease_t * t2)455 get_pkt_times(PKT_LIST *ack, lease_t *lease, lease_t *t1, lease_t *t2)
456 {
457 *lease = DHCP_PERM;
458 *t1 = DHCP_PERM;
459 *t2 = DHCP_PERM;
460
461 if (ack->opts[CD_DHCP_TYPE] == NULL) {
462 dhcpmsg(MSG_VERBOSE,
463 "get_pkt_times: BOOTP response; infinite lease");
464 return;
465 }
466 if (ack->opts[CD_LEASE_TIME] == NULL) {
467 dhcpmsg(MSG_VERBOSE,
468 "get_pkt_times: no lease option provided");
469 return;
470 }
471 if (ack->opts[CD_LEASE_TIME]->len != sizeof (lease_t)) {
472 dhcpmsg(MSG_VERBOSE, "get_pkt_times: invalid lease option");
473 }
474
475 (void) memcpy(lease, ack->opts[CD_LEASE_TIME]->value, sizeof (lease_t));
476 *lease = ntohl(*lease);
477
478 if (*lease == DHCP_PERM) {
479 dhcpmsg(MSG_VERBOSE, "get_pkt_times: infinite lease granted");
480 return;
481 }
482
483 if (ack->opts[CD_T1_TIME] != NULL &&
484 ack->opts[CD_T1_TIME]->len == sizeof (lease_t)) {
485 (void) memcpy(t1, ack->opts[CD_T1_TIME]->value, sizeof (*t1));
486 *t1 = ntohl(*t1);
487 }
488
489 if (ack->opts[CD_T2_TIME] != NULL &&
490 ack->opts[CD_T2_TIME]->len == sizeof (lease_t)) {
491 (void) memcpy(t2, ack->opts[CD_T2_TIME]->value, sizeof (*t2));
492 *t2 = ntohl(*t2);
493 }
494
495 if ((*t1 == DHCP_PERM) || (*t1 >= *lease))
496 *t1 = (lease_t)fuzzify(*lease, DHCP_T1_FACT);
497
498 if ((*t2 == DHCP_PERM) || (*t2 > *lease) || (*t2 <= *t1))
499 *t2 = (lease_t)fuzzify(*lease, DHCP_T2_FACT);
500
501 dhcpmsg(MSG_VERBOSE, "get_pkt_times: lease %u t1 %u t2 %u",
502 *lease, *t1, *t2);
503 }
504
505 /*
506 * configure_v4_timers(): configures the lease timers on a v4 state machine
507 *
508 * input: dhcp_smach_t *: the state machine to configure
509 * output: boolean_t: B_TRUE on success, B_FALSE on failure
510 */
511
512 static boolean_t
configure_v4_timers(dhcp_smach_t * dsmp)513 configure_v4_timers(dhcp_smach_t *dsmp)
514 {
515 PKT_LIST *ack = dsmp->dsm_ack;
516 lease_t lease, t1, t2;
517 dhcp_lease_t *dlp;
518 dhcp_lif_t *lif;
519
520 /* v4 has just one lease per state machine, and one LIF */
521 dlp = dsmp->dsm_leases;
522 lif = dlp->dl_lifs;
523
524 /*
525 * If it's DHCP, but there's no valid lease time, then complain,
526 * decline the lease and return error.
527 */
528 if (ack->opts[CD_DHCP_TYPE] != NULL &&
529 (ack->opts[CD_LEASE_TIME] == NULL ||
530 ack->opts[CD_LEASE_TIME]->len != sizeof (lease_t))) {
531 lif_mark_decline(lif, "Missing or corrupted lease time");
532 send_declines(dsmp);
533 dhcpmsg(MSG_WARNING, "configure_v4_timers: %s lease time in "
534 "ACK on %s", ack->opts[CD_LEASE_TIME] == NULL ? "missing" :
535 "corrupt", dsmp->dsm_name);
536 return (B_FALSE);
537 }
538
539 /* Stop the T1 and T2 timers */
540 cancel_lease_timers(dlp);
541
542 /* Stop the LEASE timer */
543 cancel_lif_timers(lif);
544
545 /*
546 * type has already been verified as ACK. if type is not set,
547 * then we got a BOOTP packet. we now fetch the t1, t2, and
548 * lease options out of the packet into variables. they are
549 * returned as relative host-byte-ordered times.
550 */
551
552 get_pkt_times(ack, &lease, &t1, &t2);
553
554 /*
555 * if the current lease is mysteriously close to the new
556 * lease, warn the user. unless there's less than a minute
557 * left, round to the closest minute.
558 */
559
560 if (lif->lif_expire.dt_start != 0 &&
561 abs((dsmp->dsm_newstart_monosec + lease) -
562 (dsmp->dsm_curstart_monosec + lif->lif_expire.dt_start)) <
563 DHCP_LEASE_EPS) {
564 const char *noext = "configure_v4_timers: lease renewed but "
565 "time not extended";
566 int msg_level;
567 uint_t minleft;
568
569 if (lif->lif_expire.dt_start < DHCP_LEASE_ERROR_THRESH)
570 msg_level = MSG_ERROR;
571 else
572 msg_level = MSG_VERBOSE;
573
574 minleft = (lif->lif_expire.dt_start + 30) / 60;
575
576 if (lif->lif_expire.dt_start < 60) {
577 dhcpmsg(msg_level, "%s; expires in %d seconds",
578 noext, lif->lif_expire.dt_start);
579 } else if (minleft == 1) {
580 dhcpmsg(msg_level, "%s; expires in 1 minute", noext);
581 } else if (minleft > 120) {
582 dhcpmsg(msg_level, "%s; expires in %d hours",
583 noext, (minleft + 30) / 60);
584 } else {
585 dhcpmsg(msg_level, "%s; expires in %d minutes",
586 noext, minleft);
587 }
588 }
589
590 init_timer(&dlp->dl_t1, t1);
591 init_timer(&dlp->dl_t2, t2);
592 init_timer(&lif->lif_expire, lease);
593
594 if (lease == DHCP_PERM) {
595 dhcpmsg(MSG_INFO,
596 "configure_v4_timers: %s acquired permanent lease",
597 dsmp->dsm_name);
598 return (B_TRUE);
599 }
600
601 dhcpmsg(MSG_INFO, "configure_v4_timers: %s acquired lease, expires %s",
602 dsmp->dsm_name,
603 monosec_to_string(dsmp->dsm_newstart_monosec + lease));
604
605 dhcpmsg(MSG_INFO, "configure_v4_timers: %s begins renewal at %s",
606 dsmp->dsm_name, monosec_to_string(dsmp->dsm_newstart_monosec +
607 dlp->dl_t1.dt_start));
608
609 dhcpmsg(MSG_INFO, "configure_v4_timers: %s begins rebinding at %s",
610 dsmp->dsm_name, monosec_to_string(dsmp->dsm_newstart_monosec +
611 dlp->dl_t2.dt_start));
612
613 /*
614 * according to RFC2131, there is no minimum lease time, but don't
615 * set up renew/rebind timers if lease is shorter than DHCP_REBIND_MIN.
616 */
617
618 if (!schedule_lif_timer(lif, &lif->lif_expire, dhcp_expire))
619 goto failure;
620
621 if (lease < DHCP_REBIND_MIN) {
622 dhcpmsg(MSG_WARNING, "configure_v4_timers: lease on %s is for "
623 "less than %d seconds!", dsmp->dsm_name, DHCP_REBIND_MIN);
624 return (B_TRUE);
625 }
626
627 if (!schedule_lease_timer(dlp, &dlp->dl_t1, dhcp_renew))
628 goto failure;
629
630 if (!schedule_lease_timer(dlp, &dlp->dl_t2, dhcp_rebind))
631 goto failure;
632
633 return (B_TRUE);
634
635 failure:
636 cancel_lease_timers(dlp);
637 cancel_lif_timers(lif);
638 dhcpmsg(MSG_WARNING,
639 "configure_v4_timers: cannot schedule lease timers");
640 return (B_FALSE);
641 }
642
643 /*
644 * configure_v6_leases(): configures the IPv6 leases on a state machine from
645 * the current DHCPv6 ACK. We need to scan the ACK,
646 * create a lease for each IA_NA, and a new LIF for each
647 * IAADDR.
648 *
649 * input: dhcp_smach_t *: the machine to configure (with a valid dsm_ack)
650 * output: enum v6_bind_result: restart, resend, or done
651 */
652
653 static enum v6_bind_result
configure_v6_leases(dhcp_smach_t * dsmp)654 configure_v6_leases(dhcp_smach_t *dsmp)
655 {
656 const dhcpv6_option_t *d6o, *d6so, *d6sso;
657 const char *optbase, *estr, *msg;
658 uint_t olen, solen, ssolen, msglen;
659 dhcpv6_ia_na_t d6in;
660 dhcpv6_iaaddr_t d6ia;
661 dhcp_lease_t *dlp;
662 uint32_t shortest;
663 dhcp_lif_t *lif;
664 uint_t nlifs;
665 boolean_t got_iana = B_FALSE;
666 uint_t scode;
667
668 for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next)
669 dlp->dl_stale = B_TRUE;
670
671 d6o = NULL;
672 while ((d6o = dhcpv6_pkt_option(dsmp->dsm_ack, d6o, DHCPV6_OPT_IA_NA,
673 &olen)) != NULL) {
674 if (olen < sizeof (d6in)) {
675 dhcpmsg(MSG_WARNING,
676 "configure_v6_leases: garbled IA_NA");
677 continue;
678 }
679
680 /*
681 * Check the IAID. It should be for our controlling LIF. If a
682 * single state machine needs to use multiple IAIDs, then this
683 * will need to change.
684 */
685 (void) memcpy(&d6in, d6o, sizeof (d6in));
686 d6in.d6in_iaid = ntohl(d6in.d6in_iaid);
687 if (d6in.d6in_iaid != dsmp->dsm_lif->lif_iaid) {
688 dhcpmsg(MSG_WARNING, "configure_v6_leases: ignored "
689 "IA_NA for IAID %x (not %x)", d6in.d6in_iaid,
690 dsmp->dsm_lif->lif_iaid);
691 continue;
692 }
693
694 /*
695 * See notes below; there's only one IA_NA and a single IAID
696 * for now.
697 */
698 if ((dlp = dsmp->dsm_leases) != NULL)
699 dlp->dl_stale = B_FALSE;
700
701 /*
702 * Note that some bug-ridden servers will try to give us
703 * multiple IA_NA options for a single IAID. We ignore
704 * duplicates.
705 */
706 if (got_iana) {
707 dhcpmsg(MSG_WARNING, "configure_v6_leases: unexpected "
708 "extra IA_NA ignored");
709 continue;
710 }
711
712 d6in.d6in_t1 = ntohl(d6in.d6in_t1);
713 d6in.d6in_t2 = ntohl(d6in.d6in_t2);
714
715 /* RFC 3315 required check for invalid T1/T2 combinations */
716 if (d6in.d6in_t1 > d6in.d6in_t2 && d6in.d6in_t2 != 0) {
717 dhcpmsg(MSG_WARNING, "configure_v6_leases: ignored "
718 "IA_NA with invalid T1 %u > T2 %u", d6in.d6in_t1,
719 d6in.d6in_t2);
720 continue;
721 }
722
723 /*
724 * There may be a status code here. Process if present.
725 */
726 optbase = (const char *)d6o + sizeof (d6in);
727 olen -= sizeof (d6in);
728 d6so = dhcpv6_find_option(optbase, olen, NULL,
729 DHCPV6_OPT_STATUS_CODE, &solen);
730 scode = dhcpv6_status_code(d6so, solen, &estr, &msg, &msglen);
731 if (scode != DHCPV6_STAT_SUCCESS) {
732 dhcpmsg(MSG_WARNING,
733 "configure_v6_leases: IA_NA: %s: %.*s",
734 estr, msglen, msg);
735 }
736 print_server_msg(dsmp, msg, msglen);
737
738 /*
739 * Other errors are possible here. According to RFC 3315
740 * section 18.1.8, we ignore the entire IA if it gives the "no
741 * addresses" status code. We may try another server if we
742 * like -- we instead opt to allow the addresses to expire and
743 * then try a new server.
744 *
745 * If the status code is "no binding," then we must go back and
746 * redo the Request. Surprisingly, it doesn't matter if it's
747 * any other code.
748 */
749 if (scode == DHCPV6_STAT_NOADDRS) {
750 dhcpmsg(MSG_DEBUG, "configure_v6_leases: ignoring "
751 "no-addrs status in IA_NA");
752 continue;
753 }
754
755 if (scode == DHCPV6_STAT_NOBINDING) {
756 send_v6_request(dsmp);
757 return (v6Resent);
758 }
759
760 /*
761 * Find or create the lease structure. This part is simple,
762 * because we support only IA_NA and a single IAID. This means
763 * there's only one lease structure. The design supports
764 * multiple lease structures so that IA_TA and IA_PD can be
765 * added later.
766 */
767 if ((dlp = dsmp->dsm_leases) == NULL &&
768 (dlp = insert_lease(dsmp)) == NULL) {
769 dhcpmsg(MSG_ERROR, "configure_v6_leases: unable to "
770 "allocate memory for lease");
771 return (v6Restart);
772 }
773
774 /*
775 * Iterate over the IAADDR options contained within this IA_NA.
776 */
777 shortest = DHCPV6_INFTIME;
778 d6so = NULL;
779 while ((d6so = dhcpv6_find_option(optbase, olen, d6so,
780 DHCPV6_OPT_IAADDR, &solen)) != NULL) {
781 if (solen < sizeof (d6ia)) {
782 dhcpmsg(MSG_WARNING,
783 "configure_v6_leases: garbled IAADDR");
784 continue;
785 }
786 (void) memcpy(&d6ia, d6so, sizeof (d6ia));
787
788 d6ia.d6ia_preflife = ntohl(d6ia.d6ia_preflife);
789 d6ia.d6ia_vallife = ntohl(d6ia.d6ia_vallife);
790
791 /* RFC 3315 required validity check */
792 if (d6ia.d6ia_preflife > d6ia.d6ia_vallife) {
793 dhcpmsg(MSG_WARNING,
794 "configure_v6_leases: ignored IAADDR with "
795 "preferred lifetime %u > valid %u",
796 d6ia.d6ia_preflife, d6ia.d6ia_vallife);
797 continue;
798 }
799
800 /*
801 * RFC 3315 allows a status code to be buried inside
802 * the IAADDR option. Look for it, and process if
803 * present. Process in a manner similar to that for
804 * the IA itself; TAHI checks for this. Real servers
805 * likely won't do this.
806 */
807 d6sso = dhcpv6_find_option((const char *)d6so +
808 sizeof (d6ia), solen - sizeof (d6ia), NULL,
809 DHCPV6_OPT_STATUS_CODE, &ssolen);
810 scode = dhcpv6_status_code(d6sso, ssolen, &estr, &msg,
811 &msglen);
812 print_server_msg(dsmp, msg, msglen);
813 if (scode == DHCPV6_STAT_NOADDRS) {
814 dhcpmsg(MSG_DEBUG, "configure_v6_leases: "
815 "ignoring no-addrs status in IAADDR");
816 continue;
817 }
818 if (scode == DHCPV6_STAT_NOBINDING) {
819 send_v6_request(dsmp);
820 return (v6Resent);
821 }
822 if (scode != DHCPV6_STAT_SUCCESS) {
823 dhcpmsg(MSG_WARNING,
824 "configure_v6_leases: IAADDR: %s", estr);
825 }
826
827 /*
828 * Locate the existing LIF within the lease associated
829 * with this address, if any.
830 */
831 lif = dlp->dl_lifs;
832 for (nlifs = dlp->dl_nlifs; nlifs > 0;
833 nlifs--, lif = lif->lif_next) {
834 if (IN6_ARE_ADDR_EQUAL(&d6ia.d6ia_addr,
835 &lif->lif_v6addr))
836 break;
837 }
838
839 /*
840 * If the server has set the lifetime to zero, then
841 * delete the LIF. Otherwise, set the new LIF expiry
842 * time, adding the LIF if necessary.
843 */
844 if (d6ia.d6ia_vallife == 0) {
845 /* If it was found, then it's expired */
846 if (nlifs != 0) {
847 dhcpmsg(MSG_DEBUG,
848 "configure_v6_leases: lif %s has "
849 "expired", lif->lif_name);
850 lif->lif_expired = B_TRUE;
851 }
852 continue;
853 }
854
855 /* If it wasn't found, then create it now. */
856 if (nlifs == 0) {
857 lif = plumb_lif(dsmp->dsm_lif->lif_pif,
858 &d6ia.d6ia_addr);
859 if (lif == NULL)
860 continue;
861 if (++dlp->dl_nlifs == 1) {
862 dlp->dl_lifs = lif;
863 } else {
864 remque(lif);
865 insque(lif, dlp->dl_lifs);
866 }
867 lif->lif_lease = dlp;
868 lif->lif_dad_wait = _B_TRUE;
869 dsmp->dsm_lif_wait++;
870 } else {
871 /* If it was found, cancel timer */
872 cancel_lif_timers(lif);
873 if (d6ia.d6ia_preflife != 0 &&
874 !clear_lif_deprecated(lif)) {
875 unplumb_lif(lif);
876 continue;
877 }
878 }
879
880 /* Set the new expiry timers */
881 init_timer(&lif->lif_preferred, d6ia.d6ia_preflife);
882 init_timer(&lif->lif_expire, d6ia.d6ia_vallife);
883
884 /*
885 * If the preferred lifetime is over now, then the LIF
886 * is deprecated. If it's the same as the expiry time,
887 * then we don't need a separate timer for it.
888 */
889 if (d6ia.d6ia_preflife == 0) {
890 set_lif_deprecated(lif);
891 } else if (d6ia.d6ia_preflife != DHCPV6_INFTIME &&
892 d6ia.d6ia_preflife != d6ia.d6ia_vallife &&
893 !schedule_lif_timer(lif, &lif->lif_preferred,
894 dhcp_deprecate)) {
895 unplumb_lif(lif);
896 continue;
897 }
898
899 if (d6ia.d6ia_vallife != DHCPV6_INFTIME &&
900 !schedule_lif_timer(lif, &lif->lif_expire,
901 dhcp_expire)) {
902 unplumb_lif(lif);
903 continue;
904 }
905
906 if (d6ia.d6ia_preflife < shortest)
907 shortest = d6ia.d6ia_preflife;
908 }
909
910 if (dlp->dl_nlifs == 0) {
911 dhcpmsg(MSG_WARNING,
912 "configure_v6_leases: no IAADDRs found in IA_NA");
913 remove_lease(dlp);
914 continue;
915 }
916
917 if (d6in.d6in_t1 == 0 && d6in.d6in_t2 == 0) {
918 /* Default values from RFC 3315: 0.5 and 0.8 */
919 if ((d6in.d6in_t1 = shortest / 2) == 0)
920 d6in.d6in_t1 = 1;
921 d6in.d6in_t2 = shortest - shortest / 5;
922 }
923
924 cancel_lease_timers(dlp);
925 init_timer(&dlp->dl_t1, d6in.d6in_t1);
926 init_timer(&dlp->dl_t2, d6in.d6in_t2);
927
928 if ((d6in.d6in_t1 != DHCPV6_INFTIME &&
929 !schedule_lease_timer(dlp, &dlp->dl_t1, dhcp_renew)) ||
930 (d6in.d6in_t2 != DHCPV6_INFTIME &&
931 !schedule_lease_timer(dlp, &dlp->dl_t2, dhcp_rebind))) {
932 dhcpmsg(MSG_WARNING, "configure_v6_leases: unable to "
933 "set renew/rebind timers");
934 } else {
935 got_iana = B_TRUE;
936 }
937 }
938
939 if (!got_iana) {
940 dhcpmsg(MSG_WARNING,
941 "configure_v6_leases: no usable IA_NA option found");
942 }
943
944 return (v6Done);
945 }
946
947 /*
948 * configure_v4_lease(): configures the IPv4 lease on a state machine from
949 * the current DHCP ACK. There's only one lease and LIF
950 * per state machine in IPv4.
951 *
952 * input: dhcp_smach_t *: the machine to configure (with a valid dsm_ack)
953 * output: boolean_t: B_TRUE on success, B_FALSE on failure
954 */
955
956 static boolean_t
configure_v4_lease(dhcp_smach_t * dsmp)957 configure_v4_lease(dhcp_smach_t *dsmp)
958 {
959 struct lifreq lifr;
960 struct sockaddr_in *sin;
961 PKT_LIST *ack = dsmp->dsm_ack;
962 dhcp_lease_t *dlp;
963 dhcp_lif_t *lif;
964 uint32_t addrhbo;
965 struct in_addr inaddr;
966
967 /*
968 * if we're using DHCP, then we'll have a valid CD_SERVER_ID
969 * (we checked in dhcp_acknak()); set it now so that
970 * dsmp->dsm_server is valid in case we need to send_decline().
971 * note that we use comparisons against opts[CD_DHCP_TYPE]
972 * since we haven't set DHCP_IF_BOOTP yet (we don't do that
973 * until we're sure we want the offered address.)
974 */
975
976 if (ack->opts[CD_DHCP_TYPE] != NULL) {
977 (void) memcpy(&inaddr, ack->opts[CD_SERVER_ID]->value,
978 sizeof (inaddr));
979 IN6_INADDR_TO_V4MAPPED(&inaddr, &dsmp->dsm_server);
980 }
981
982 /*
983 * There needs to be exactly one lease for IPv4, and that lease
984 * controls the main LIF for the state machine. If it doesn't exist
985 * yet, then create it now.
986 */
987 if ((dlp = dsmp->dsm_leases) == NULL &&
988 (dlp = insert_lease(dsmp)) == NULL) {
989 dhcpmsg(MSG_ERROR, "configure_v4_lease: unable to allocate "
990 "memory for lease");
991 return (B_FALSE);
992 }
993 if (dlp->dl_nlifs == 0) {
994 dlp->dl_lifs = dsmp->dsm_lif;
995 dlp->dl_nlifs = 1;
996
997 /* The lease holds a reference on the LIF */
998 hold_lif(dlp->dl_lifs);
999 dlp->dl_lifs->lif_lease = dlp;
1000 }
1001
1002 lif = dlp->dl_lifs;
1003
1004 IN6_INADDR_TO_V4MAPPED(&ack->pkt->yiaddr, &lif->lif_v6addr);
1005 addrhbo = ntohl(ack->pkt->yiaddr.s_addr);
1006 if ((addrhbo & IN_CLASSA_NET) == 0 ||
1007 (addrhbo >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET ||
1008 IN_CLASSD(addrhbo)) {
1009 dhcpmsg(MSG_ERROR,
1010 "configure_v4_lease: got invalid IP address %s for %s",
1011 inet_ntoa(ack->pkt->yiaddr), lif->lif_name);
1012 return (B_FALSE);
1013 }
1014
1015 (void) memset(&lifr, 0, sizeof (struct lifreq));
1016 (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
1017
1018 /*
1019 * bring the interface online. note that there is no optimal
1020 * order here: it is considered bad taste (and in > solaris 7,
1021 * likely illegal) to bring an interface up before it has an
1022 * ip address. however, due to an apparent bug in sun fddi
1023 * 5.0, fddi will not obtain a network routing entry unless
1024 * the interface is brought up before it has an ip address.
1025 * we take the lesser of the two evils; if fddi customers have
1026 * problems, they can get a newer fddi distribution which
1027 * fixes the problem.
1028 */
1029
1030 sin = (struct sockaddr_in *)&lifr.lifr_addr;
1031 sin->sin_family = AF_INET;
1032
1033 (void) memset(&lif->lif_v6mask, 0xff, sizeof (lif->lif_v6mask));
1034 if (ack->opts[CD_SUBNETMASK] != NULL &&
1035 ack->opts[CD_SUBNETMASK]->len == sizeof (inaddr)) {
1036
1037 (void) memcpy(&inaddr, ack->opts[CD_SUBNETMASK]->value,
1038 sizeof (inaddr));
1039
1040 } else {
1041
1042 if (ack->opts[CD_SUBNETMASK] != NULL &&
1043 ack->opts[CD_SUBNETMASK]->len != sizeof (inaddr)) {
1044 dhcpmsg(MSG_WARNING, "configure_v4_lease: specified "
1045 "subnet mask length is %d instead of %d, ignoring",
1046 ack->opts[CD_SUBNETMASK]->len, sizeof (ipaddr_t));
1047 } else {
1048 dhcpmsg(MSG_WARNING, "configure_v4_lease: no IP "
1049 "netmask specified for %s, making best guess",
1050 lif->lif_name);
1051 }
1052
1053 /*
1054 * no legitimate IP subnet mask specified.. use best
1055 * guess. recall that lif_addr is in network order, so
1056 * imagine it's 0x11223344: then when it is read into
1057 * a register on x86, it becomes 0x44332211, so we
1058 * must ntohl() it to convert it to 0x11223344 in
1059 * order to use the macros in <netinet/in.h>.
1060 */
1061
1062 if (IN_CLASSA(addrhbo))
1063 inaddr.s_addr = htonl(IN_CLASSA_NET);
1064 else if (IN_CLASSB(addrhbo))
1065 inaddr.s_addr = htonl(IN_CLASSB_NET);
1066 else if (IN_CLASSC(addrhbo))
1067 inaddr.s_addr = htonl(IN_CLASSC_NET);
1068 else {
1069 /*
1070 * Cant be Class D as that is multicast
1071 * Must be Class E
1072 */
1073 inaddr.s_addr = htonl(IN_CLASSE_NET);
1074 }
1075 }
1076 lif->lif_v6mask._S6_un._S6_u32[3] = inaddr.s_addr;
1077
1078 sin->sin_addr = inaddr;
1079 dhcpmsg(MSG_INFO, "configure_v4_lease: setting IP netmask to %s on %s",
1080 inet_ntoa(sin->sin_addr), lif->lif_name);
1081
1082 if (ioctl(v4_sock_fd, SIOCSLIFNETMASK, &lifr) == -1) {
1083 dhcpmsg(MSG_ERR, "configure_v4_lease: cannot set IP netmask "
1084 "on %s", lif->lif_name);
1085 return (B_FALSE);
1086 }
1087
1088 IN6_V4MAPPED_TO_INADDR(&lif->lif_v6addr, &sin->sin_addr);
1089 dhcpmsg(MSG_INFO, "configure_v4_lease: setting IP address to %s on %s",
1090 inet_ntoa(sin->sin_addr), lif->lif_name);
1091
1092 if (ioctl(v4_sock_fd, SIOCSLIFADDR, &lifr) == -1) {
1093 dhcpmsg(MSG_ERR, "configure_v4_lease: cannot set IP address "
1094 "on %s", lif->lif_name);
1095 return (B_FALSE);
1096 }
1097
1098 if (!lif->lif_dad_wait) {
1099 lif->lif_dad_wait = _B_TRUE;
1100 dsmp->dsm_lif_wait++;
1101 }
1102
1103 if (ack->opts[CD_BROADCASTADDR] != NULL &&
1104 ack->opts[CD_BROADCASTADDR]->len == sizeof (inaddr)) {
1105
1106 (void) memcpy(&inaddr, ack->opts[CD_BROADCASTADDR]->value,
1107 sizeof (inaddr));
1108
1109 } else {
1110
1111 if (ack->opts[CD_BROADCASTADDR] != NULL &&
1112 ack->opts[CD_BROADCASTADDR]->len != sizeof (inaddr)) {
1113 dhcpmsg(MSG_WARNING, "configure_v4_lease: specified "
1114 "broadcast address length is %d instead of %d, "
1115 "ignoring", ack->opts[CD_BROADCASTADDR]->len,
1116 sizeof (inaddr));
1117 } else {
1118 dhcpmsg(MSG_WARNING, "configure_v4_lease: no IP "
1119 "broadcast specified for %s, making best guess",
1120 lif->lif_name);
1121 }
1122
1123 /*
1124 * no legitimate IP broadcast specified. compute it
1125 * from the IP address and netmask.
1126 */
1127
1128 IN6_V4MAPPED_TO_INADDR(&lif->lif_v6addr, &inaddr);
1129 inaddr.s_addr |= ~lif->lif_v6mask._S6_un._S6_u32[3];
1130 }
1131
1132 /*
1133 * the kernel will set the broadcast address for us as part of
1134 * bringing the interface up. since experience has shown that dhcp
1135 * servers sometimes provide a bogus broadcast address, we let the
1136 * kernel set it so that it's guaranteed to be correct.
1137 *
1138 * also, note any inconsistencies and save the broadcast address the
1139 * kernel set so that we can watch for changes to it.
1140 */
1141
1142 if (ioctl(v4_sock_fd, SIOCGLIFBRDADDR, &lifr) == -1) {
1143 dhcpmsg(MSG_ERR, "configure_v4_lease: cannot get broadcast "
1144 "address for %s", lif->lif_name);
1145 return (B_FALSE);
1146 }
1147
1148 if (inaddr.s_addr != sin->sin_addr.s_addr) {
1149 dhcpmsg(MSG_WARNING, "configure_v4_lease: incorrect broadcast "
1150 "address %s specified for %s; ignoring", inet_ntoa(inaddr),
1151 lif->lif_name);
1152 }
1153
1154 lif->lif_broadcast = sin->sin_addr.s_addr;
1155 dhcpmsg(MSG_INFO,
1156 "configure_v4_lease: using broadcast address %s on %s",
1157 inet_ntoa(inaddr), lif->lif_name);
1158 return (B_TRUE);
1159 }
1160
1161 /*
1162 * save_server_id(): save off the new DHCPv6 Server ID
1163 *
1164 * input: dhcp_smach_t *: the state machine to use
1165 * PKT_LIST *: the packet with the Reply message
1166 * output: boolean_t: B_TRUE on success, B_FALSE on failure
1167 */
1168
1169 boolean_t
save_server_id(dhcp_smach_t * dsmp,PKT_LIST * msg)1170 save_server_id(dhcp_smach_t *dsmp, PKT_LIST *msg)
1171 {
1172 const dhcpv6_option_t *d6o;
1173 uint_t olen;
1174
1175 d6o = dhcpv6_pkt_option(msg, NULL, DHCPV6_OPT_SERVERID, &olen);
1176 if (d6o == NULL)
1177 return (B_FALSE);
1178 olen -= sizeof (*d6o);
1179 free(dsmp->dsm_serverid);
1180 if ((dsmp->dsm_serverid = malloc(olen)) == NULL) {
1181 return (B_FALSE);
1182 } else {
1183 dsmp->dsm_serveridlen = olen;
1184 (void) memcpy(dsmp->dsm_serverid, d6o + 1, olen);
1185 return (B_TRUE);
1186 }
1187 }
1188