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]
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>
30 #include <netinet/in.h>
31 #include <sys/sockio.h>
34 #include <arpa/inet.h>
37 #include <sys/sysmacros.h>
38 #include <dhcp_hostconf.h>
39 #include <dhcpagent_util.h>
46 #include "interface.h"
47 #include "script_handler.h"
50 * Possible outcomes for IPv6 binding attempt.
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
67 * output: int: always 1
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
);
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
93 dhcp_bound(dhcp_smach_t
*dsmp
, PKT_LIST
*ack
)
99 enum v6_bind_result v6b
;
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
);
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
;
115 /* Note that adoption occurs only for IPv4 DHCP. */
118 if (ack
->opts
[CD_DHCP_TYPE
] == NULL
)
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
,
130 new_lease
= htonl(MIN(ntohl(new_lease
), DHCP_ADOPT_LEASE_MAX
));
132 (void) memcpy(ack
->opts
[CD_LEASE_TIME
]->value
, &new_lease
,
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
);
146 if (!configure_v4_lease(dsmp
))
149 if (!configure_v4_timers(dsmp
))
153 dsmp
->dsm_curstart_monosec
= dsmp
->dsm_newstart_monosec
;
154 write_lease_to_hostconf(dsmp
);
161 if (dsmp
->dsm_isv6
) {
162 if ((v6b
= configure_v6_leases(dsmp
)) != v6Done
)
163 return (v6b
== v6Resent
);
165 if (!configure_v4_lease(dsmp
))
168 if (!configure_v4_timers(dsmp
))
171 if (!clear_lif_deprecated(dsmp
->dsm_lif
))
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
182 if (dsmp
->dsm_leases
== NULL
) {
184 "dhcp_bound: no address lease established");
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
194 (oldstate
== SELECTING
|| oldstate
== INIT_REBOOT
) &&
195 dsmp
->dsm_isv6
&& !save_server_id(dsmp
, ack
)) {
197 "dhcp_bound: unable to save server ID on %s",
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
))
210 if (dsmp
->dsm_lif_wait
== 0)
211 dhcp_bound_complete(dsmp
);
217 /* This is just a duplicate ack; silently ignore it */
223 if (dsmp
->dsm_isv6
) {
224 if ((v6b
= configure_v6_leases(dsmp
)) != v6Done
)
225 return (v6b
== v6Resent
);
227 if (!configure_v4_timers(dsmp
))
229 if (!clear_lif_deprecated(dsmp
->dsm_lif
))
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
) {
240 dhcp_expire(NULL
, lif
);
241 while ((lif
= find_expired_lif(dsmp
)) != NULL
) {
242 dlp
= lif
->lif_lease
;
244 if (dlp
->dl_nlifs
== 0)
247 if (dsmp
->dsm_leases
== NULL
)
251 if (oldstate
== REBINDING
&& dsmp
->dsm_isv6
&&
252 !save_server_id(dsmp
, ack
)) {
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)
266 dhcpmsg(MSG_DEBUG
, "dhcp_bound: lease not updated; "
271 if (!set_smach_state(dsmp
, BOUND
))
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
);
286 if (dsmp
->dsm_isv6
&& !save_server_id(dsmp
, ack
)) {
290 (void) bound_event_cb(dsmp
, NULL
);
291 if (!set_smach_state(dsmp
, INFORMATION
))
294 /* Stop sending requests now */
295 stop_pkt_retransmission(dsmp
);
299 /* something is really bizarre... */
301 "dhcp_bound: called in unexpected state: %s",
302 dhcp_state_to_string(dsmp
->dsm_state
));
310 * dhcp_bound_complete(): complete interface configuration after DAD
312 * input: dhcp_smach_t *: the state machine now ready
317 dhcp_bound_complete(dhcp_smach_t
*dsmp
)
320 DHCP_OPT
*router_list
;
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",
334 (void) script_start(dsmp
, EVENT_BOUND6
, bound_event_cb
, NULL
,
336 dsmp
->dsm_curstart_monosec
= dsmp
->dsm_newstart_monosec
;
337 write_lease_to_hostconf(dsmp
);
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.
350 router_list
= ack
->opts
[CD_ROUTER
];
351 for (i
= 0; i
< dsmp
->dsm_pillen
; i
++) {
352 if (dsmp
->dsm_pil
[i
] == CD_ROUTER
)
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
)),
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
);
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
)) {
392 "dhcp_bound_complete: cannot set bound state on %s",
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
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
,
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)
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
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
452 get_pkt_times(PKT_LIST
*ack
, lease_t
*lease
, lease_t
*t1
, lease_t
*t2
)
458 if (ack
->opts
[CD_DHCP_TYPE
] == NULL
) {
460 "get_pkt_times: BOOTP response; infinite lease");
463 if (ack
->opts
[CD_LEASE_TIME
] == NULL
) {
465 "get_pkt_times: no lease option provided");
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");
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
));
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
));
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",
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
510 configure_v4_timers(dhcp_smach_t
*dsmp
)
512 PKT_LIST
*ack
= dsmp
->dsm_ack
;
513 lease_t lease
, t1
, t2
;
517 /* v4 has just one lease per state machine, and one LIF */
518 dlp
= dsmp
->dsm_leases
;
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");
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
);
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
)) <
561 const char *noext
= "configure_v4_timers: lease renewed but "
566 if (lif
->lif_expire
.dt_start
< DHCP_LEASE_ERROR_THRESH
)
567 msg_level
= MSG_ERROR
;
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);
582 dhcpmsg(msg_level
, "%s; expires in %d minutes",
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
) {
593 "configure_v4_timers: %s acquired permanent lease",
598 dhcpmsg(MSG_INFO
, "configure_v4_timers: %s acquired lease, expires %s",
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
))
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
);
624 if (!schedule_lease_timer(dlp
, &dlp
->dl_t1
, dhcp_renew
))
627 if (!schedule_lease_timer(dlp
, &dlp
->dl_t2
, dhcp_rebind
))
633 cancel_lease_timers(dlp
);
634 cancel_lif_timers(lif
);
636 "configure_v4_timers: cannot schedule lease timers");
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
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
;
657 dhcpv6_iaaddr_t d6ia
;
662 boolean_t got_iana
= B_FALSE
;
665 for (dlp
= dsmp
->dsm_leases
; dlp
!= NULL
; dlp
= dlp
->dl_next
)
666 dlp
->dl_stale
= B_TRUE
;
669 while ((d6o
= dhcpv6_pkt_option(dsmp
->dsm_ack
, d6o
, DHCPV6_OPT_IA_NA
,
671 if (olen
< sizeof (d6in
)) {
673 "configure_v6_leases: garbled IA_NA");
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
);
692 * See notes below; there's only one IA_NA and a single IAID
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
704 dhcpmsg(MSG_WARNING
, "configure_v6_leases: unexpected "
705 "extra IA_NA ignored");
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
,
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
) {
730 "configure_v6_leases: IA_NA: %s: %.*s",
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
746 if (scode
== DHCPV6_STAT_NOADDRS
) {
747 dhcpmsg(MSG_DEBUG
, "configure_v6_leases: ignoring "
748 "no-addrs status in IA_NA");
752 if (scode
== DHCPV6_STAT_NOBINDING
) {
753 send_v6_request(dsmp
);
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
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");
772 * Iterate over the IAADDR options contained within this IA_NA.
774 shortest
= DHCPV6_INFTIME
;
776 while ((d6so
= dhcpv6_find_option(optbase
, olen
, d6so
,
777 DHCPV6_OPT_IAADDR
, &solen
)) != NULL
) {
778 if (solen
< sizeof (d6ia
)) {
780 "configure_v6_leases: garbled IAADDR");
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
) {
791 "configure_v6_leases: ignored IAADDR with "
792 "preferred lifetime %u > valid %u",
793 d6ia
.d6ia_preflife
, d6ia
.d6ia_vallife
);
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
,
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");
815 if (scode
== DHCPV6_STAT_NOBINDING
) {
816 send_v6_request(dsmp
);
819 if (scode
!= DHCPV6_STAT_SUCCESS
) {
821 "configure_v6_leases: IAADDR: %s", estr
);
825 * Locate the existing LIF within the lease associated
826 * with this address, if any.
829 for (nlifs
= dlp
->dl_nlifs
; nlifs
> 0;
830 nlifs
--, lif
= lif
->lif_next
) {
831 if (IN6_ARE_ADDR_EQUAL(&d6ia
.d6ia_addr
,
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 */
845 "configure_v6_leases: lif %s has "
846 "expired", lif
->lif_name
);
847 lif
->lif_expired
= B_TRUE
;
852 /* If it wasn't found, then create it now. */
854 lif
= plumb_lif(dsmp
->dsm_lif
->lif_pif
,
858 if (++dlp
->dl_nlifs
== 1) {
862 insque(lif
, dlp
->dl_lifs
);
864 lif
->lif_lease
= dlp
;
865 lif
->lif_dad_wait
= _B_TRUE
;
866 dsmp
->dsm_lif_wait
++;
868 /* If it was found, cancel timer */
869 cancel_lif_timers(lif
);
870 if (d6ia
.d6ia_preflife
!= 0 &&
871 !clear_lif_deprecated(lif
)) {
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
,
896 if (d6ia
.d6ia_vallife
!= DHCPV6_INFTIME
&&
897 !schedule_lif_timer(lif
, &lif
->lif_expire
,
903 if (d6ia
.d6ia_preflife
< shortest
)
904 shortest
= d6ia
.d6ia_preflife
;
907 if (dlp
->dl_nlifs
== 0) {
909 "configure_v6_leases: no IAADDRs found in IA_NA");
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)
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");
938 "configure_v6_leases: no usable IA_NA option found");
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
954 configure_v4_lease(dhcp_smach_t
*dsmp
)
957 struct sockaddr_in
*sin
;
958 PKT_LIST
*ack
= dsmp
->dsm_ack
;
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
,
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 "
990 if (dlp
->dl_nlifs
== 0) {
991 dlp
->dl_lifs
= dsmp
->dsm_lif
;
994 /* The lease holds a reference on the LIF */
995 hold_lif(dlp
->dl_lifs
);
996 dlp
->dl_lifs
->lif_lease
= dlp
;
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
)) {
1007 "configure_v4_lease: got invalid IP address %s for %s",
1008 inet_ntoa(ack
->pkt
->yiaddr
), lif
->lif_name
);
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
,
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
));
1045 dhcpmsg(MSG_WARNING
, "configure_v4_lease: no IP "
1046 "netmask specified for %s, making best guess",
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
);
1067 * Cant be Class D as that is multicast
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
);
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
);
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
,
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
,
1115 dhcpmsg(MSG_WARNING
, "configure_v4_lease: no IP "
1116 "broadcast specified for %s, making best guess",
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
);
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
),
1151 lif
->lif_broadcast
= sin
->sin_addr
.s_addr
;
1153 "configure_v4_lease: using broadcast address %s on %s",
1154 inet_ntoa(inaddr
), lif
->lif_name
);
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
1167 save_server_id(dhcp_smach_t
*dsmp
, PKT_LIST
*msg
)
1169 const dhcpv6_option_t
*d6o
;
1172 d6o
= dhcpv6_pkt_option(msg
, NULL
, DHCPV6_OPT_SERVERID
, &olen
);
1175 olen
-= sizeof (*d6o
);
1176 free(dsmp
->dsm_serverid
);
1177 if ((dsmp
->dsm_serverid
= malloc(olen
)) == NULL
) {
1180 dsmp
->dsm_serveridlen
= olen
;
1181 (void) memcpy(dsmp
->dsm_serverid
, d6o
+ 1, olen
);