8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / cmd-inet / sbin / dhcpagent / bound.c
blob9907b8397d5d42bc97df8c12eb44015c17787aab
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
24 * BOUND state of the DHCP client state machine.
27 #include <sys/socket.h>
28 #include <sys/types.h>
29 #include <string.h>
30 #include <netinet/in.h>
31 #include <sys/sockio.h>
32 #include <unistd.h>
33 #include <time.h>
34 #include <arpa/inet.h>
35 #include <stdlib.h>
36 #include <search.h>
37 #include <sys/sysmacros.h>
38 #include <dhcp_hostconf.h>
39 #include <dhcpagent_util.h>
40 #include <dhcpmsg.h>
42 #include "states.h"
43 #include "packet.h"
44 #include "util.h"
45 #include "agent.h"
46 #include "interface.h"
47 #include "script_handler.h"
50 * Possible outcomes for IPv6 binding attempt.
52 enum v6_bind_result {
53 v6Restart, /* report failure and restart state machine */
54 v6Resent, /* new Request message has been sent */
55 v6Done /* successful binding */
58 static enum v6_bind_result configure_v6_leases(dhcp_smach_t *);
59 static boolean_t configure_v4_lease(dhcp_smach_t *);
60 static boolean_t configure_v4_timers(dhcp_smach_t *);
63 * bound_event_cb(): callback for script_start on the event EVENT_BOUND
65 * input: dhcp_smach_t *: the state machine configured
66 * void *: unused
67 * output: int: always 1
70 /* ARGSUSED1 */
71 static int
72 bound_event_cb(dhcp_smach_t *dsmp, void *arg)
74 if (dsmp->dsm_ia.ia_fd != -1)
75 ipc_action_finish(dsmp, DHCP_IPC_SUCCESS);
76 else
77 async_finish(dsmp);
78 return (1);
82 * dhcp_bound(): configures an state machine and interfaces using information
83 * contained in the ACK/Reply packet and sets up lease timers.
84 * Before starting, the requested address is verified by
85 * Duplicate Address Detection to make sure it's not in use.
87 * input: dhcp_smach_t *: the state machine to move to bound
88 * PKT_LIST *: the ACK/Reply packet, or NULL to use dsmp->dsm_ack
89 * output: boolean_t: B_TRUE on success, B_FALSE on failure
92 boolean_t
93 dhcp_bound(dhcp_smach_t *dsmp, PKT_LIST *ack)
95 DHCPSTATE oldstate;
96 lease_t new_lease;
97 dhcp_lif_t *lif;
98 dhcp_lease_t *dlp;
99 enum v6_bind_result v6b;
101 if (ack != NULL) {
102 /* If ack we're replacing is not the original, then free it */
103 if (dsmp->dsm_ack != dsmp->dsm_orig_ack)
104 free_pkt_entry(dsmp->dsm_ack);
105 dsmp->dsm_ack = ack;
106 /* Save the first ack as the original */
107 if (dsmp->dsm_orig_ack == NULL)
108 dsmp->dsm_orig_ack = ack;
111 oldstate = dsmp->dsm_state;
112 switch (oldstate) {
114 case ADOPTING:
115 /* Note that adoption occurs only for IPv4 DHCP. */
117 /* Ignore BOOTP */
118 if (ack->opts[CD_DHCP_TYPE] == NULL)
119 return (B_FALSE);
122 * if we're adopting a lease, the lease timers
123 * only provide an upper bound since we don't know
124 * from what time they are relative to. assume we
125 * have a lease time of at most DHCP_ADOPT_LEASE_MAX.
127 (void) memcpy(&new_lease, ack->opts[CD_LEASE_TIME]->value,
128 sizeof (lease_t));
130 new_lease = htonl(MIN(ntohl(new_lease), DHCP_ADOPT_LEASE_MAX));
132 (void) memcpy(ack->opts[CD_LEASE_TIME]->value, &new_lease,
133 sizeof (lease_t));
136 * we have no idea when the REQUEST that generated
137 * this ACK was sent, but for diagnostic purposes
138 * we'll assume its close to the current time.
140 dsmp->dsm_newstart_monosec = monosec();
142 if (dsmp->dsm_isv6) {
143 if ((v6b = configure_v6_leases(dsmp)) != v6Done)
144 return (v6b == v6Resent);
145 } else {
146 if (!configure_v4_lease(dsmp))
147 return (B_FALSE);
149 if (!configure_v4_timers(dsmp))
150 return (B_FALSE);
153 dsmp->dsm_curstart_monosec = dsmp->dsm_newstart_monosec;
154 write_lease_to_hostconf(dsmp);
155 break;
157 case SELECTING:
158 case REQUESTING:
159 case INIT_REBOOT:
161 if (dsmp->dsm_isv6) {
162 if ((v6b = configure_v6_leases(dsmp)) != v6Done)
163 return (v6b == v6Resent);
164 } else {
165 if (!configure_v4_lease(dsmp))
166 return (B_FALSE);
168 if (!configure_v4_timers(dsmp))
169 return (B_FALSE);
171 if (!clear_lif_deprecated(dsmp->dsm_lif))
172 return (B_FALSE);
175 /* Stop sending requests now */
176 stop_pkt_retransmission(dsmp);
179 * If we didn't end up with any usable leases, then we have a
180 * problem.
182 if (dsmp->dsm_leases == NULL) {
183 dhcpmsg(MSG_WARNING,
184 "dhcp_bound: no address lease established");
185 return (B_FALSE);
189 * If this is a Rapid-Commit (selecting state) or if we're
190 * dealing with a reboot (init-reboot), then we will have a new
191 * server ID to save.
193 if (ack != NULL &&
194 (oldstate == SELECTING || oldstate == INIT_REBOOT) &&
195 dsmp->dsm_isv6 && !save_server_id(dsmp, ack)) {
196 dhcpmsg(MSG_ERROR,
197 "dhcp_bound: unable to save server ID on %s",
198 dsmp->dsm_name);
199 return (B_FALSE);
203 * We will continue configuring the interfaces via
204 * dhcp_bound_complete, once kernel DAD completes. If no new
205 * leases were created (which can happen on an init-reboot used
206 * for link-up confirmation), then go straight to bound state.
208 if (!set_smach_state(dsmp, PRE_BOUND))
209 return (B_FALSE);
210 if (dsmp->dsm_lif_wait == 0)
211 dhcp_bound_complete(dsmp);
212 break;
214 case PRE_BOUND:
215 case BOUND:
216 case INFORMATION:
217 /* This is just a duplicate ack; silently ignore it */
218 return (B_TRUE);
220 case RENEWING:
221 case REBINDING:
223 if (dsmp->dsm_isv6) {
224 if ((v6b = configure_v6_leases(dsmp)) != v6Done)
225 return (v6b == v6Resent);
226 } else {
227 if (!configure_v4_timers(dsmp))
228 return (B_FALSE);
229 if (!clear_lif_deprecated(dsmp->dsm_lif))
230 return (B_FALSE);
234 * If some or all of the leases were torn down by the server,
235 * then handle that as an expiry. When the script is done
236 * running for the LOSS6 event, we'll end up back here.
238 if ((lif = find_expired_lif(dsmp)) != NULL) {
239 hold_lif(lif);
240 dhcp_expire(NULL, lif);
241 while ((lif = find_expired_lif(dsmp)) != NULL) {
242 dlp = lif->lif_lease;
243 unplumb_lif(lif);
244 if (dlp->dl_nlifs == 0)
245 remove_lease(dlp);
247 if (dsmp->dsm_leases == NULL)
248 return (B_FALSE);
251 if (oldstate == REBINDING && dsmp->dsm_isv6 &&
252 !save_server_id(dsmp, ack)) {
253 return (B_FALSE);
257 * Handle Renew/Rebind that fails to address one of our leases.
258 * (Should just never happen, but RFC 3315 section 18.1.8
259 * requires it, and TAHI tests for it.)
261 for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
262 if (dlp->dl_stale && dlp->dl_nlifs > 0)
263 break;
265 if (dlp != NULL) {
266 dhcpmsg(MSG_DEBUG, "dhcp_bound: lease not updated; "
267 "allow retransmit");
268 return (B_TRUE);
271 if (!set_smach_state(dsmp, BOUND))
272 return (B_FALSE);
274 (void) script_start(dsmp, dsmp->dsm_isv6 ? EVENT_EXTEND6 :
275 EVENT_EXTEND, bound_event_cb, NULL, NULL);
277 dsmp->dsm_curstart_monosec = dsmp->dsm_newstart_monosec;
278 write_lease_to_hostconf(dsmp);
280 /* Stop sending requests now */
281 stop_pkt_retransmission(dsmp);
282 break;
284 case INFORM_SENT:
286 if (dsmp->dsm_isv6 && !save_server_id(dsmp, ack)) {
287 return (B_FALSE);
290 (void) bound_event_cb(dsmp, NULL);
291 if (!set_smach_state(dsmp, INFORMATION))
292 return (B_FALSE);
294 /* Stop sending requests now */
295 stop_pkt_retransmission(dsmp);
296 break;
298 default:
299 /* something is really bizarre... */
300 dhcpmsg(MSG_DEBUG,
301 "dhcp_bound: called in unexpected state: %s",
302 dhcp_state_to_string(dsmp->dsm_state));
303 return (B_FALSE);
306 return (B_TRUE);
310 * dhcp_bound_complete(): complete interface configuration after DAD
312 * input: dhcp_smach_t *: the state machine now ready
313 * output: none
316 void
317 dhcp_bound_complete(dhcp_smach_t *dsmp)
319 PKT_LIST *ack;
320 DHCP_OPT *router_list;
321 int i;
322 DHCPSTATE oldstate;
323 dhcp_lif_t *lif;
326 * Do bound state entry processing only if running IPv4. There's no
327 * need for this with DHCPv6 because link-locals are used for I/O and
328 * because DHCPv6 isn't entangled with routing.
330 if (dsmp->dsm_isv6) {
331 (void) set_smach_state(dsmp, BOUND);
332 dhcpmsg(MSG_DEBUG, "dhcp_bound_complete: bound %s",
333 dsmp->dsm_name);
334 (void) script_start(dsmp, EVENT_BOUND6, bound_event_cb, NULL,
335 NULL);
336 dsmp->dsm_curstart_monosec = dsmp->dsm_newstart_monosec;
337 write_lease_to_hostconf(dsmp);
338 return;
342 * Add each provided router; we'll clean them up when the
343 * state machine goes away or when our lease expires.
345 * Note that we do not handle default routers on IPv4 logicals;
346 * see README for details.
349 ack = dsmp->dsm_ack;
350 router_list = ack->opts[CD_ROUTER];
351 for (i = 0; i < dsmp->dsm_pillen; i++) {
352 if (dsmp->dsm_pil[i] == CD_ROUTER)
353 router_list = NULL;
355 lif = dsmp->dsm_lif;
356 if (router_list != NULL &&
357 (router_list->len % sizeof (ipaddr_t)) == 0 &&
358 strchr(lif->lif_name, ':') == NULL &&
359 !lif->lif_pif->pif_under_ipmp) {
361 dsmp->dsm_nrouters = router_list->len / sizeof (ipaddr_t);
362 dsmp->dsm_routers = malloc(router_list->len);
363 if (dsmp->dsm_routers == NULL) {
364 dhcpmsg(MSG_ERR, "dhcp_bound_complete: cannot allocate "
365 "default router list, ignoring default routers");
366 dsmp->dsm_nrouters = 0;
369 for (i = 0; i < dsmp->dsm_nrouters; i++) {
371 (void) memcpy(&dsmp->dsm_routers[i].s_addr,
372 router_list->value + (i * sizeof (ipaddr_t)),
373 sizeof (ipaddr_t));
375 if (!add_default_route(lif->lif_pif->pif_index,
376 &dsmp->dsm_routers[i])) {
377 dhcpmsg(MSG_ERR, "dhcp_bound_complete: cannot "
378 "add default router %s on %s", inet_ntoa(
379 dsmp->dsm_routers[i]), dsmp->dsm_name);
380 dsmp->dsm_routers[i].s_addr = htonl(INADDR_ANY);
381 continue;
384 dhcpmsg(MSG_INFO, "added default router %s on %s",
385 inet_ntoa(dsmp->dsm_routers[i]), dsmp->dsm_name);
389 oldstate = dsmp->dsm_state;
390 if (!set_smach_state(dsmp, BOUND)) {
391 dhcpmsg(MSG_ERR,
392 "dhcp_bound_complete: cannot set bound state on %s",
393 dsmp->dsm_name);
394 return;
397 dhcpmsg(MSG_DEBUG, "dhcp_bound_complete: bound %s", dsmp->dsm_name);
400 * We're now committed to this binding, so if it came from BOOTP, set
401 * the flag.
404 if (ack->opts[CD_DHCP_TYPE] == NULL)
405 dsmp->dsm_dflags |= DHCP_IF_BOOTP;
408 * If the previous state was ADOPTING, event loop has not been started
409 * at this time; so don't run the EVENT_BOUND script.
411 if (oldstate != ADOPTING) {
412 (void) script_start(dsmp, EVENT_BOUND, bound_event_cb, NULL,
413 NULL);
416 dsmp->dsm_curstart_monosec = dsmp->dsm_newstart_monosec;
417 write_lease_to_hostconf(dsmp);
421 * fuzzify(): adds some "fuzz" to a t1/t2 time, in accordance with RFC2131.
422 * We use up to plus or minus 2% jitter in the time. This is a
423 * small value, but the timers involved are typically long. A
424 * common T1 value is one day, and the fuzz is up to 28.8 minutes;
425 * plenty of time to make sure that individual clients don't renew
426 * all at the same time.
428 * input: uint32_t: the number of seconds until lease expiration
429 * double: the approximate percentage of that time to return
430 * output: double: a number approximating (sec * pct)
433 static double
434 fuzzify(uint32_t sec, double pct)
436 return (sec * (pct + (drand48() - 0.5) / 25.0));
440 * get_pkt_times(): pulls the lease times out of a v4 DHCP packet and stores
441 * them as host byte-order relative times in the passed in
442 * parameters.
444 * input: PKT_LIST *: the packet to pull the packet times from
445 * lease_t *: where to store the relative lease time in hbo
446 * lease_t *: where to store the relative t1 time in hbo
447 * lease_t *: where to store the relative t2 time in hbo
448 * output: void
451 static void
452 get_pkt_times(PKT_LIST *ack, lease_t *lease, lease_t *t1, lease_t *t2)
454 *lease = DHCP_PERM;
455 *t1 = DHCP_PERM;
456 *t2 = DHCP_PERM;
458 if (ack->opts[CD_DHCP_TYPE] == NULL) {
459 dhcpmsg(MSG_VERBOSE,
460 "get_pkt_times: BOOTP response; infinite lease");
461 return;
463 if (ack->opts[CD_LEASE_TIME] == NULL) {
464 dhcpmsg(MSG_VERBOSE,
465 "get_pkt_times: no lease option provided");
466 return;
468 if (ack->opts[CD_LEASE_TIME]->len != sizeof (lease_t)) {
469 dhcpmsg(MSG_VERBOSE, "get_pkt_times: invalid lease option");
472 (void) memcpy(lease, ack->opts[CD_LEASE_TIME]->value, sizeof (lease_t));
473 *lease = ntohl(*lease);
475 if (*lease == DHCP_PERM) {
476 dhcpmsg(MSG_VERBOSE, "get_pkt_times: infinite lease granted");
477 return;
480 if (ack->opts[CD_T1_TIME] != NULL &&
481 ack->opts[CD_T1_TIME]->len == sizeof (lease_t)) {
482 (void) memcpy(t1, ack->opts[CD_T1_TIME]->value, sizeof (*t1));
483 *t1 = ntohl(*t1);
486 if (ack->opts[CD_T2_TIME] != NULL &&
487 ack->opts[CD_T2_TIME]->len == sizeof (lease_t)) {
488 (void) memcpy(t2, ack->opts[CD_T2_TIME]->value, sizeof (*t2));
489 *t2 = ntohl(*t2);
492 if ((*t1 == DHCP_PERM) || (*t1 >= *lease))
493 *t1 = (lease_t)fuzzify(*lease, DHCP_T1_FACT);
495 if ((*t2 == DHCP_PERM) || (*t2 > *lease) || (*t2 <= *t1))
496 *t2 = (lease_t)fuzzify(*lease, DHCP_T2_FACT);
498 dhcpmsg(MSG_VERBOSE, "get_pkt_times: lease %u t1 %u t2 %u",
499 *lease, *t1, *t2);
503 * configure_v4_timers(): configures the lease timers on a v4 state machine
505 * input: dhcp_smach_t *: the state machine to configure
506 * output: boolean_t: B_TRUE on success, B_FALSE on failure
509 static boolean_t
510 configure_v4_timers(dhcp_smach_t *dsmp)
512 PKT_LIST *ack = dsmp->dsm_ack;
513 lease_t lease, t1, t2;
514 dhcp_lease_t *dlp;
515 dhcp_lif_t *lif;
517 /* v4 has just one lease per state machine, and one LIF */
518 dlp = dsmp->dsm_leases;
519 lif = dlp->dl_lifs;
522 * If it's DHCP, but there's no valid lease time, then complain,
523 * decline the lease and return error.
525 if (ack->opts[CD_DHCP_TYPE] != NULL &&
526 (ack->opts[CD_LEASE_TIME] == NULL ||
527 ack->opts[CD_LEASE_TIME]->len != sizeof (lease_t))) {
528 lif_mark_decline(lif, "Missing or corrupted lease time");
529 send_declines(dsmp);
530 dhcpmsg(MSG_WARNING, "configure_v4_timers: %s lease time in "
531 "ACK on %s", ack->opts[CD_LEASE_TIME] == NULL ? "missing" :
532 "corrupt", dsmp->dsm_name);
533 return (B_FALSE);
536 /* Stop the T1 and T2 timers */
537 cancel_lease_timers(dlp);
539 /* Stop the LEASE timer */
540 cancel_lif_timers(lif);
543 * type has already been verified as ACK. if type is not set,
544 * then we got a BOOTP packet. we now fetch the t1, t2, and
545 * lease options out of the packet into variables. they are
546 * returned as relative host-byte-ordered times.
549 get_pkt_times(ack, &lease, &t1, &t2);
552 * if the current lease is mysteriously close to the new
553 * lease, warn the user. unless there's less than a minute
554 * left, round to the closest minute.
557 if (lif->lif_expire.dt_start != 0 &&
558 abs((dsmp->dsm_newstart_monosec + lease) -
559 (dsmp->dsm_curstart_monosec + lif->lif_expire.dt_start)) <
560 DHCP_LEASE_EPS) {
561 const char *noext = "configure_v4_timers: lease renewed but "
562 "time not extended";
563 int msg_level;
564 uint_t minleft;
566 if (lif->lif_expire.dt_start < DHCP_LEASE_ERROR_THRESH)
567 msg_level = MSG_ERROR;
568 else
569 msg_level = MSG_VERBOSE;
571 minleft = (lif->lif_expire.dt_start + 30) / 60;
573 if (lif->lif_expire.dt_start < 60) {
574 dhcpmsg(msg_level, "%s; expires in %d seconds",
575 noext, lif->lif_expire.dt_start);
576 } else if (minleft == 1) {
577 dhcpmsg(msg_level, "%s; expires in 1 minute", noext);
578 } else if (minleft > 120) {
579 dhcpmsg(msg_level, "%s; expires in %d hours",
580 noext, (minleft + 30) / 60);
581 } else {
582 dhcpmsg(msg_level, "%s; expires in %d minutes",
583 noext, minleft);
587 init_timer(&dlp->dl_t1, t1);
588 init_timer(&dlp->dl_t2, t2);
589 init_timer(&lif->lif_expire, lease);
591 if (lease == DHCP_PERM) {
592 dhcpmsg(MSG_INFO,
593 "configure_v4_timers: %s acquired permanent lease",
594 dsmp->dsm_name);
595 return (B_TRUE);
598 dhcpmsg(MSG_INFO, "configure_v4_timers: %s acquired lease, expires %s",
599 dsmp->dsm_name,
600 monosec_to_string(dsmp->dsm_newstart_monosec + lease));
602 dhcpmsg(MSG_INFO, "configure_v4_timers: %s begins renewal at %s",
603 dsmp->dsm_name, monosec_to_string(dsmp->dsm_newstart_monosec +
604 dlp->dl_t1.dt_start));
606 dhcpmsg(MSG_INFO, "configure_v4_timers: %s begins rebinding at %s",
607 dsmp->dsm_name, monosec_to_string(dsmp->dsm_newstart_monosec +
608 dlp->dl_t2.dt_start));
611 * according to RFC2131, there is no minimum lease time, but don't
612 * set up renew/rebind timers if lease is shorter than DHCP_REBIND_MIN.
615 if (!schedule_lif_timer(lif, &lif->lif_expire, dhcp_expire))
616 goto failure;
618 if (lease < DHCP_REBIND_MIN) {
619 dhcpmsg(MSG_WARNING, "configure_v4_timers: lease on %s is for "
620 "less than %d seconds!", dsmp->dsm_name, DHCP_REBIND_MIN);
621 return (B_TRUE);
624 if (!schedule_lease_timer(dlp, &dlp->dl_t1, dhcp_renew))
625 goto failure;
627 if (!schedule_lease_timer(dlp, &dlp->dl_t2, dhcp_rebind))
628 goto failure;
630 return (B_TRUE);
632 failure:
633 cancel_lease_timers(dlp);
634 cancel_lif_timers(lif);
635 dhcpmsg(MSG_WARNING,
636 "configure_v4_timers: cannot schedule lease timers");
637 return (B_FALSE);
641 * configure_v6_leases(): configures the IPv6 leases on a state machine from
642 * the current DHCPv6 ACK. We need to scan the ACK,
643 * create a lease for each IA_NA, and a new LIF for each
644 * IAADDR.
646 * input: dhcp_smach_t *: the machine to configure (with a valid dsm_ack)
647 * output: enum v6_bind_result: restart, resend, or done
650 static enum v6_bind_result
651 configure_v6_leases(dhcp_smach_t *dsmp)
653 const dhcpv6_option_t *d6o, *d6so, *d6sso;
654 const char *optbase, *estr, *msg;
655 uint_t olen, solen, ssolen, msglen;
656 dhcpv6_ia_na_t d6in;
657 dhcpv6_iaaddr_t d6ia;
658 dhcp_lease_t *dlp;
659 uint32_t shortest;
660 dhcp_lif_t *lif;
661 uint_t nlifs;
662 boolean_t got_iana = B_FALSE;
663 uint_t scode;
665 for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next)
666 dlp->dl_stale = B_TRUE;
668 d6o = NULL;
669 while ((d6o = dhcpv6_pkt_option(dsmp->dsm_ack, d6o, DHCPV6_OPT_IA_NA,
670 &olen)) != NULL) {
671 if (olen < sizeof (d6in)) {
672 dhcpmsg(MSG_WARNING,
673 "configure_v6_leases: garbled IA_NA");
674 continue;
678 * Check the IAID. It should be for our controlling LIF. If a
679 * single state machine needs to use multiple IAIDs, then this
680 * will need to change.
682 (void) memcpy(&d6in, d6o, sizeof (d6in));
683 d6in.d6in_iaid = ntohl(d6in.d6in_iaid);
684 if (d6in.d6in_iaid != dsmp->dsm_lif->lif_iaid) {
685 dhcpmsg(MSG_WARNING, "configure_v6_leases: ignored "
686 "IA_NA for IAID %x (not %x)", d6in.d6in_iaid,
687 dsmp->dsm_lif->lif_iaid);
688 continue;
692 * See notes below; there's only one IA_NA and a single IAID
693 * for now.
695 if ((dlp = dsmp->dsm_leases) != NULL)
696 dlp->dl_stale = B_FALSE;
699 * Note that some bug-ridden servers will try to give us
700 * multiple IA_NA options for a single IAID. We ignore
701 * duplicates.
703 if (got_iana) {
704 dhcpmsg(MSG_WARNING, "configure_v6_leases: unexpected "
705 "extra IA_NA ignored");
706 continue;
709 d6in.d6in_t1 = ntohl(d6in.d6in_t1);
710 d6in.d6in_t2 = ntohl(d6in.d6in_t2);
712 /* RFC 3315 required check for invalid T1/T2 combinations */
713 if (d6in.d6in_t1 > d6in.d6in_t2 && d6in.d6in_t2 != 0) {
714 dhcpmsg(MSG_WARNING, "configure_v6_leases: ignored "
715 "IA_NA with invalid T1 %u > T2 %u", d6in.d6in_t1,
716 d6in.d6in_t2);
717 continue;
721 * There may be a status code here. Process if present.
723 optbase = (const char *)d6o + sizeof (d6in);
724 olen -= sizeof (d6in);
725 d6so = dhcpv6_find_option(optbase, olen, NULL,
726 DHCPV6_OPT_STATUS_CODE, &solen);
727 scode = dhcpv6_status_code(d6so, solen, &estr, &msg, &msglen);
728 if (scode != DHCPV6_STAT_SUCCESS) {
729 dhcpmsg(MSG_WARNING,
730 "configure_v6_leases: IA_NA: %s: %.*s",
731 estr, msglen, msg);
733 print_server_msg(dsmp, msg, msglen);
736 * Other errors are possible here. According to RFC 3315
737 * section 18.1.8, we ignore the entire IA if it gives the "no
738 * addresses" status code. We may try another server if we
739 * like -- we instead opt to allow the addresses to expire and
740 * then try a new server.
742 * If the status code is "no binding," then we must go back and
743 * redo the Request. Surprisingly, it doesn't matter if it's
744 * any other code.
746 if (scode == DHCPV6_STAT_NOADDRS) {
747 dhcpmsg(MSG_DEBUG, "configure_v6_leases: ignoring "
748 "no-addrs status in IA_NA");
749 continue;
752 if (scode == DHCPV6_STAT_NOBINDING) {
753 send_v6_request(dsmp);
754 return (v6Resent);
758 * Find or create the lease structure. This part is simple,
759 * because we support only IA_NA and a single IAID. This means
760 * there's only one lease structure. The design supports
761 * multiple lease structures so that IA_TA and IA_PD can be
762 * added later.
764 if ((dlp = dsmp->dsm_leases) == NULL &&
765 (dlp = insert_lease(dsmp)) == NULL) {
766 dhcpmsg(MSG_ERROR, "configure_v6_leases: unable to "
767 "allocate memory for lease");
768 return (v6Restart);
772 * Iterate over the IAADDR options contained within this IA_NA.
774 shortest = DHCPV6_INFTIME;
775 d6so = NULL;
776 while ((d6so = dhcpv6_find_option(optbase, olen, d6so,
777 DHCPV6_OPT_IAADDR, &solen)) != NULL) {
778 if (solen < sizeof (d6ia)) {
779 dhcpmsg(MSG_WARNING,
780 "configure_v6_leases: garbled IAADDR");
781 continue;
783 (void) memcpy(&d6ia, d6so, sizeof (d6ia));
785 d6ia.d6ia_preflife = ntohl(d6ia.d6ia_preflife);
786 d6ia.d6ia_vallife = ntohl(d6ia.d6ia_vallife);
788 /* RFC 3315 required validity check */
789 if (d6ia.d6ia_preflife > d6ia.d6ia_vallife) {
790 dhcpmsg(MSG_WARNING,
791 "configure_v6_leases: ignored IAADDR with "
792 "preferred lifetime %u > valid %u",
793 d6ia.d6ia_preflife, d6ia.d6ia_vallife);
794 continue;
798 * RFC 3315 allows a status code to be buried inside
799 * the IAADDR option. Look for it, and process if
800 * present. Process in a manner similar to that for
801 * the IA itself; TAHI checks for this. Real servers
802 * likely won't do this.
804 d6sso = dhcpv6_find_option((const char *)d6so +
805 sizeof (d6ia), solen - sizeof (d6ia), NULL,
806 DHCPV6_OPT_STATUS_CODE, &ssolen);
807 scode = dhcpv6_status_code(d6sso, ssolen, &estr, &msg,
808 &msglen);
809 print_server_msg(dsmp, msg, msglen);
810 if (scode == DHCPV6_STAT_NOADDRS) {
811 dhcpmsg(MSG_DEBUG, "configure_v6_leases: "
812 "ignoring no-addrs status in IAADDR");
813 continue;
815 if (scode == DHCPV6_STAT_NOBINDING) {
816 send_v6_request(dsmp);
817 return (v6Resent);
819 if (scode != DHCPV6_STAT_SUCCESS) {
820 dhcpmsg(MSG_WARNING,
821 "configure_v6_leases: IAADDR: %s", estr);
825 * Locate the existing LIF within the lease associated
826 * with this address, if any.
828 lif = dlp->dl_lifs;
829 for (nlifs = dlp->dl_nlifs; nlifs > 0;
830 nlifs--, lif = lif->lif_next) {
831 if (IN6_ARE_ADDR_EQUAL(&d6ia.d6ia_addr,
832 &lif->lif_v6addr))
833 break;
837 * If the server has set the lifetime to zero, then
838 * delete the LIF. Otherwise, set the new LIF expiry
839 * time, adding the LIF if necessary.
841 if (d6ia.d6ia_vallife == 0) {
842 /* If it was found, then it's expired */
843 if (nlifs != 0) {
844 dhcpmsg(MSG_DEBUG,
845 "configure_v6_leases: lif %s has "
846 "expired", lif->lif_name);
847 lif->lif_expired = B_TRUE;
849 continue;
852 /* If it wasn't found, then create it now. */
853 if (nlifs == 0) {
854 lif = plumb_lif(dsmp->dsm_lif->lif_pif,
855 &d6ia.d6ia_addr);
856 if (lif == NULL)
857 continue;
858 if (++dlp->dl_nlifs == 1) {
859 dlp->dl_lifs = lif;
860 } else {
861 remque(lif);
862 insque(lif, dlp->dl_lifs);
864 lif->lif_lease = dlp;
865 lif->lif_dad_wait = _B_TRUE;
866 dsmp->dsm_lif_wait++;
867 } else {
868 /* If it was found, cancel timer */
869 cancel_lif_timers(lif);
870 if (d6ia.d6ia_preflife != 0 &&
871 !clear_lif_deprecated(lif)) {
872 unplumb_lif(lif);
873 continue;
877 /* Set the new expiry timers */
878 init_timer(&lif->lif_preferred, d6ia.d6ia_preflife);
879 init_timer(&lif->lif_expire, d6ia.d6ia_vallife);
882 * If the preferred lifetime is over now, then the LIF
883 * is deprecated. If it's the same as the expiry time,
884 * then we don't need a separate timer for it.
886 if (d6ia.d6ia_preflife == 0) {
887 set_lif_deprecated(lif);
888 } else if (d6ia.d6ia_preflife != DHCPV6_INFTIME &&
889 d6ia.d6ia_preflife != d6ia.d6ia_vallife &&
890 !schedule_lif_timer(lif, &lif->lif_preferred,
891 dhcp_deprecate)) {
892 unplumb_lif(lif);
893 continue;
896 if (d6ia.d6ia_vallife != DHCPV6_INFTIME &&
897 !schedule_lif_timer(lif, &lif->lif_expire,
898 dhcp_expire)) {
899 unplumb_lif(lif);
900 continue;
903 if (d6ia.d6ia_preflife < shortest)
904 shortest = d6ia.d6ia_preflife;
907 if (dlp->dl_nlifs == 0) {
908 dhcpmsg(MSG_WARNING,
909 "configure_v6_leases: no IAADDRs found in IA_NA");
910 remove_lease(dlp);
911 continue;
914 if (d6in.d6in_t1 == 0 && d6in.d6in_t2 == 0) {
915 /* Default values from RFC 3315: 0.5 and 0.8 */
916 if ((d6in.d6in_t1 = shortest / 2) == 0)
917 d6in.d6in_t1 = 1;
918 d6in.d6in_t2 = shortest - shortest / 5;
921 cancel_lease_timers(dlp);
922 init_timer(&dlp->dl_t1, d6in.d6in_t1);
923 init_timer(&dlp->dl_t2, d6in.d6in_t2);
925 if ((d6in.d6in_t1 != DHCPV6_INFTIME &&
926 !schedule_lease_timer(dlp, &dlp->dl_t1, dhcp_renew)) ||
927 (d6in.d6in_t2 != DHCPV6_INFTIME &&
928 !schedule_lease_timer(dlp, &dlp->dl_t2, dhcp_rebind))) {
929 dhcpmsg(MSG_WARNING, "configure_v6_leases: unable to "
930 "set renew/rebind timers");
931 } else {
932 got_iana = B_TRUE;
936 if (!got_iana) {
937 dhcpmsg(MSG_WARNING,
938 "configure_v6_leases: no usable IA_NA option found");
941 return (v6Done);
945 * configure_v4_lease(): configures the IPv4 lease on a state machine from
946 * the current DHCP ACK. There's only one lease and LIF
947 * per state machine in IPv4.
949 * input: dhcp_smach_t *: the machine to configure (with a valid dsm_ack)
950 * output: boolean_t: B_TRUE on success, B_FALSE on failure
953 static boolean_t
954 configure_v4_lease(dhcp_smach_t *dsmp)
956 struct lifreq lifr;
957 struct sockaddr_in *sin;
958 PKT_LIST *ack = dsmp->dsm_ack;
959 dhcp_lease_t *dlp;
960 dhcp_lif_t *lif;
961 uint32_t addrhbo;
962 struct in_addr inaddr;
965 * if we're using DHCP, then we'll have a valid CD_SERVER_ID
966 * (we checked in dhcp_acknak()); set it now so that
967 * dsmp->dsm_server is valid in case we need to send_decline().
968 * note that we use comparisons against opts[CD_DHCP_TYPE]
969 * since we haven't set DHCP_IF_BOOTP yet (we don't do that
970 * until we're sure we want the offered address.)
973 if (ack->opts[CD_DHCP_TYPE] != NULL) {
974 (void) memcpy(&inaddr, ack->opts[CD_SERVER_ID]->value,
975 sizeof (inaddr));
976 IN6_INADDR_TO_V4MAPPED(&inaddr, &dsmp->dsm_server);
980 * There needs to be exactly one lease for IPv4, and that lease
981 * controls the main LIF for the state machine. If it doesn't exist
982 * yet, then create it now.
984 if ((dlp = dsmp->dsm_leases) == NULL &&
985 (dlp = insert_lease(dsmp)) == NULL) {
986 dhcpmsg(MSG_ERROR, "configure_v4_lease: unable to allocate "
987 "memory for lease");
988 return (B_FALSE);
990 if (dlp->dl_nlifs == 0) {
991 dlp->dl_lifs = dsmp->dsm_lif;
992 dlp->dl_nlifs = 1;
994 /* The lease holds a reference on the LIF */
995 hold_lif(dlp->dl_lifs);
996 dlp->dl_lifs->lif_lease = dlp;
999 lif = dlp->dl_lifs;
1001 IN6_INADDR_TO_V4MAPPED(&ack->pkt->yiaddr, &lif->lif_v6addr);
1002 addrhbo = ntohl(ack->pkt->yiaddr.s_addr);
1003 if ((addrhbo & IN_CLASSA_NET) == 0 ||
1004 (addrhbo >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET ||
1005 IN_CLASSD(addrhbo)) {
1006 dhcpmsg(MSG_ERROR,
1007 "configure_v4_lease: got invalid IP address %s for %s",
1008 inet_ntoa(ack->pkt->yiaddr), lif->lif_name);
1009 return (B_FALSE);
1012 (void) memset(&lifr, 0, sizeof (struct lifreq));
1013 (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
1016 * bring the interface online. note that there is no optimal
1017 * order here: it is considered bad taste (and in > solaris 7,
1018 * likely illegal) to bring an interface up before it has an
1019 * ip address. however, due to an apparent bug in sun fddi
1020 * 5.0, fddi will not obtain a network routing entry unless
1021 * the interface is brought up before it has an ip address.
1022 * we take the lesser of the two evils; if fddi customers have
1023 * problems, they can get a newer fddi distribution which
1024 * fixes the problem.
1027 sin = (struct sockaddr_in *)&lifr.lifr_addr;
1028 sin->sin_family = AF_INET;
1030 (void) memset(&lif->lif_v6mask, 0xff, sizeof (lif->lif_v6mask));
1031 if (ack->opts[CD_SUBNETMASK] != NULL &&
1032 ack->opts[CD_SUBNETMASK]->len == sizeof (inaddr)) {
1034 (void) memcpy(&inaddr, ack->opts[CD_SUBNETMASK]->value,
1035 sizeof (inaddr));
1037 } else {
1039 if (ack->opts[CD_SUBNETMASK] != NULL &&
1040 ack->opts[CD_SUBNETMASK]->len != sizeof (inaddr)) {
1041 dhcpmsg(MSG_WARNING, "configure_v4_lease: specified "
1042 "subnet mask length is %d instead of %d, ignoring",
1043 ack->opts[CD_SUBNETMASK]->len, sizeof (ipaddr_t));
1044 } else {
1045 dhcpmsg(MSG_WARNING, "configure_v4_lease: no IP "
1046 "netmask specified for %s, making best guess",
1047 lif->lif_name);
1051 * no legitimate IP subnet mask specified.. use best
1052 * guess. recall that lif_addr is in network order, so
1053 * imagine it's 0x11223344: then when it is read into
1054 * a register on x86, it becomes 0x44332211, so we
1055 * must ntohl() it to convert it to 0x11223344 in
1056 * order to use the macros in <netinet/in.h>.
1059 if (IN_CLASSA(addrhbo))
1060 inaddr.s_addr = htonl(IN_CLASSA_NET);
1061 else if (IN_CLASSB(addrhbo))
1062 inaddr.s_addr = htonl(IN_CLASSB_NET);
1063 else if (IN_CLASSC(addrhbo))
1064 inaddr.s_addr = htonl(IN_CLASSC_NET);
1065 else {
1067 * Cant be Class D as that is multicast
1068 * Must be Class E
1070 inaddr.s_addr = htonl(IN_CLASSE_NET);
1073 lif->lif_v6mask._S6_un._S6_u32[3] = inaddr.s_addr;
1075 sin->sin_addr = inaddr;
1076 dhcpmsg(MSG_INFO, "configure_v4_lease: setting IP netmask to %s on %s",
1077 inet_ntoa(sin->sin_addr), lif->lif_name);
1079 if (ioctl(v4_sock_fd, SIOCSLIFNETMASK, &lifr) == -1) {
1080 dhcpmsg(MSG_ERR, "configure_v4_lease: cannot set IP netmask "
1081 "on %s", lif->lif_name);
1082 return (B_FALSE);
1085 IN6_V4MAPPED_TO_INADDR(&lif->lif_v6addr, &sin->sin_addr);
1086 dhcpmsg(MSG_INFO, "configure_v4_lease: setting IP address to %s on %s",
1087 inet_ntoa(sin->sin_addr), lif->lif_name);
1089 if (ioctl(v4_sock_fd, SIOCSLIFADDR, &lifr) == -1) {
1090 dhcpmsg(MSG_ERR, "configure_v4_lease: cannot set IP address "
1091 "on %s", lif->lif_name);
1092 return (B_FALSE);
1095 if (!lif->lif_dad_wait) {
1096 lif->lif_dad_wait = _B_TRUE;
1097 dsmp->dsm_lif_wait++;
1100 if (ack->opts[CD_BROADCASTADDR] != NULL &&
1101 ack->opts[CD_BROADCASTADDR]->len == sizeof (inaddr)) {
1103 (void) memcpy(&inaddr, ack->opts[CD_BROADCASTADDR]->value,
1104 sizeof (inaddr));
1106 } else {
1108 if (ack->opts[CD_BROADCASTADDR] != NULL &&
1109 ack->opts[CD_BROADCASTADDR]->len != sizeof (inaddr)) {
1110 dhcpmsg(MSG_WARNING, "configure_v4_lease: specified "
1111 "broadcast address length is %d instead of %d, "
1112 "ignoring", ack->opts[CD_BROADCASTADDR]->len,
1113 sizeof (inaddr));
1114 } else {
1115 dhcpmsg(MSG_WARNING, "configure_v4_lease: no IP "
1116 "broadcast specified for %s, making best guess",
1117 lif->lif_name);
1121 * no legitimate IP broadcast specified. compute it
1122 * from the IP address and netmask.
1125 IN6_V4MAPPED_TO_INADDR(&lif->lif_v6addr, &inaddr);
1126 inaddr.s_addr |= ~lif->lif_v6mask._S6_un._S6_u32[3];
1130 * the kernel will set the broadcast address for us as part of
1131 * bringing the interface up. since experience has shown that dhcp
1132 * servers sometimes provide a bogus broadcast address, we let the
1133 * kernel set it so that it's guaranteed to be correct.
1135 * also, note any inconsistencies and save the broadcast address the
1136 * kernel set so that we can watch for changes to it.
1139 if (ioctl(v4_sock_fd, SIOCGLIFBRDADDR, &lifr) == -1) {
1140 dhcpmsg(MSG_ERR, "configure_v4_lease: cannot get broadcast "
1141 "address for %s", lif->lif_name);
1142 return (B_FALSE);
1145 if (inaddr.s_addr != sin->sin_addr.s_addr) {
1146 dhcpmsg(MSG_WARNING, "configure_v4_lease: incorrect broadcast "
1147 "address %s specified for %s; ignoring", inet_ntoa(inaddr),
1148 lif->lif_name);
1151 lif->lif_broadcast = sin->sin_addr.s_addr;
1152 dhcpmsg(MSG_INFO,
1153 "configure_v4_lease: using broadcast address %s on %s",
1154 inet_ntoa(inaddr), lif->lif_name);
1155 return (B_TRUE);
1159 * save_server_id(): save off the new DHCPv6 Server ID
1161 * input: dhcp_smach_t *: the state machine to use
1162 * PKT_LIST *: the packet with the Reply message
1163 * output: boolean_t: B_TRUE on success, B_FALSE on failure
1166 boolean_t
1167 save_server_id(dhcp_smach_t *dsmp, PKT_LIST *msg)
1169 const dhcpv6_option_t *d6o;
1170 uint_t olen;
1172 d6o = dhcpv6_pkt_option(msg, NULL, DHCPV6_OPT_SERVERID, &olen);
1173 if (d6o == NULL)
1174 return (B_FALSE);
1175 olen -= sizeof (*d6o);
1176 free(dsmp->dsm_serverid);
1177 if ((dsmp->dsm_serverid = malloc(olen)) == NULL) {
1178 return (B_FALSE);
1179 } else {
1180 dsmp->dsm_serveridlen = olen;
1181 (void) memcpy(dsmp->dsm_serverid, d6o + 1, olen);
1182 return (B_TRUE);