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 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
25 * INIT_REBOOT state of the DHCP client state machine.
28 #include <sys/types.h>
31 #include <sys/socket.h>
32 #include <netinet/in.h>
33 #include <netinet/dhcp.h>
34 #include <netinet/udp.h>
35 #include <netinet/ip_var.h>
36 #include <netinet/udp_var.h>
44 #include "interface.h"
47 static stop_func_t stop_init_reboot
;
50 * dhcp_init_reboot_v4(): attempts to reuse a cached configuration for a state
53 * input: dhcp_smach_t *: the state machine to examine for reuse
58 dhcp_init_reboot_v4(dhcp_smach_t
*dsmp
)
62 char hostfile
[PATH_MAX
+ 1];
65 * assemble DHCPREQUEST message. The max dhcp message size
66 * option is set to the interface max, minus the size of the udp and
70 dpkt
= init_pkt(dsmp
, REQUEST
);
71 (void) add_pkt_opt32(dpkt
, CD_REQUESTED_IP_ADDR
,
72 dsmp
->dsm_ack
->pkt
->yiaddr
.s_addr
);
74 (void) add_pkt_opt32(dpkt
, CD_LEASE_TIME
, htonl(DHCP_PERM
));
75 (void) add_pkt_opt16(dpkt
, CD_MAX_DHCP_SIZE
,
76 htons(dsmp
->dsm_lif
->lif_pif
->pif_max
- sizeof (struct udpiphdr
)));
78 if (class_id_len
!= 0)
79 (void) add_pkt_opt(dpkt
, CD_CLASS_ID
, class_id
, class_id_len
);
80 (void) add_pkt_prl(dpkt
, dsmp
);
83 * Set CD_HOSTNAME option if REQUEST_HOSTNAME is set and a hostname
84 * is found in /etc/hostname.<ifname>
86 if (df_get_bool(dsmp
->dsm_name
, dsmp
->dsm_isv6
, DF_REQUEST_HOSTNAME
)) {
87 (void) snprintf(hostfile
, sizeof (hostfile
), "/etc/hostname.%s",
90 if ((reqhost
= iffile_to_hostname(hostfile
)) != NULL
) {
91 dhcpmsg(MSG_DEBUG
, "dhcp_selecting: host %s", reqhost
);
92 if ((dsmp
->dsm_reqhost
= strdup(reqhost
)) != NULL
)
93 (void) add_pkt_opt(dpkt
, CD_HOSTNAME
,
95 strlen(dsmp
->dsm_reqhost
));
97 dhcpmsg(MSG_WARNING
, "dhcp_selecting: cannot"
98 " allocate memory for host name option");
101 "dhcp_selecting: no hostname for %s",
106 (void) add_pkt_opt(dpkt
, CD_END
, NULL
, 0);
108 (void) send_pkt(dsmp
, dpkt
, htonl(INADDR_BROADCAST
), stop_init_reboot
);
113 * dhcp_init_reboot_v6(): attempts to reuse a cached configuration for a state
114 * machine. Create a Confirm message and multicast it
117 * input: dhcp_smach_t *: the state machine to examine for reuse
122 dhcp_init_reboot_v6(dhcp_smach_t
*dsmp
)
125 dhcpv6_option_t
*d6o
, *d6so
, *popt
;
128 dhcpv6_iaaddr_t d6ia
;
132 * Assemble a Confirm message based on the current ack.
135 dpkt
= init_pkt(dsmp
, DHCPV6_MSG_CONFIRM
);
138 * Loop over and copy IA_NAs and IAADDRs we have in our last ack. This
139 * is what we'll be requesting.
142 while ((d6o
= dhcpv6_pkt_option(dsmp
->dsm_ack
, d6o
, DHCPV6_OPT_IA_NA
,
146 * Copy in IA_NA option from the ack. Note that we use zero
147 * for all timers in accordance with RFC 3315. (It would make
148 * some sense to say what we think the current timers are as
149 * a hint to the server, but the RFC doesn't agree.)
151 if (olen
< sizeof (dhcpv6_ia_na_t
))
153 (void) memcpy(&d6in
, d6o
, sizeof (d6in
));
156 popt
= add_pkt_opt(dpkt
, DHCPV6_OPT_IA_NA
,
157 (char *)&d6in
+ sizeof (*d6o
),
158 sizeof (d6in
) - sizeof (*d6o
));
163 * Now loop over the IAADDR suboptions and add those.
165 obase
= (char *)d6o
+ sizeof (dhcpv6_ia_na_t
);
166 olen
-= sizeof (dhcpv6_ia_na_t
);
168 while ((d6so
= dhcpv6_find_option(obase
, olen
, d6so
,
169 DHCPV6_OPT_IAADDR
, &solen
)) != NULL
) {
170 if (solen
< sizeof (dhcpv6_iaaddr_t
))
172 (void) memcpy(&d6ia
, d6so
, sizeof (d6ia
));
173 d6ia
.d6ia_preflife
= 0;
174 d6ia
.d6ia_vallife
= 0;
175 if (add_pkt_subopt(dpkt
, popt
, DHCPV6_OPT_IAADDR
,
176 (char *)&d6ia
+ sizeof (*d6so
),
177 sizeof (d6ia
) - sizeof (*d6so
)) == NULL
)
182 /* Add required Option Request option */
183 (void) add_pkt_prl(dpkt
, dsmp
);
185 (void) send_pkt_v6(dsmp
, dpkt
, ipv6_all_dhcp_relay_and_servers
,
186 stop_init_reboot
, DHCPV6_CNF_TIMEOUT
, DHCPV6_CNF_MAX_RT
);
191 if (!set_start_timer(dsmp
))
192 dhcp_selecting(dsmp
);
196 * dhcp_init_reboot(): attempts to reuse a cached configuration for a state
199 * input: dhcp_smach_t *: the state machine to examine for reuse
204 dhcp_init_reboot(dhcp_smach_t
*dsmp
)
206 dhcpmsg(MSG_VERBOSE
, "%s has cached configuration - entering "
207 "INIT_REBOOT", dsmp
->dsm_name
);
209 if (!set_smach_state(dsmp
, INIT_REBOOT
)) {
210 dhcpmsg(MSG_ERROR
, "dhcp_init_reboot: cannot register to "
211 "collect ACK/NAK packets, reverting to INIT on %s",
214 dsmp
->dsm_dflags
|= DHCP_IF_FAILED
;
215 (void) set_smach_state(dsmp
, INIT
);
216 ipc_action_finish(dsmp
, DHCP_IPC_E_MEMORY
);
221 dhcp_init_reboot_v6(dsmp
);
223 dhcp_init_reboot_v4(dsmp
);
227 * stop_init_reboot(): decides when to stop retransmitting REQUESTs
229 * input: dhcp_smach_t *: the state machine sending the REQUESTs
230 * unsigned int: the number of REQUESTs sent so far
231 * output: boolean_t: B_TRUE if retransmissions should stop
235 stop_init_reboot(dhcp_smach_t
*dsmp
, unsigned int n_requests
)
237 if (dsmp
->dsm_isv6
) {
238 uint_t nowabs
, maxabs
;
240 nowabs
= NSEC2MSEC(gethrtime());
241 maxabs
= NSEC2MSEC(dsmp
->dsm_neg_hrtime
) + DHCPV6_CNF_MAX_RD
;
242 if (nowabs
< maxabs
) {
243 /* Cap the timer based on the maximum */
244 if (nowabs
+ dsmp
->dsm_send_timeout
> maxabs
)
245 dsmp
->dsm_send_timeout
= maxabs
- nowabs
;
249 if (n_requests
< DHCP_MAX_REQUESTS
)
253 if (df_get_bool(dsmp
->dsm_name
, dsmp
->dsm_isv6
,
254 DF_VERIFIED_LEASE_ONLY
)) {
256 "unable to verify existing lease on %s; restarting",
258 dhcp_selecting(dsmp
);
262 if (dsmp
->dsm_isv6
) {
263 dhcpmsg(MSG_INFO
, "no Reply to Confirm, using remainder of "
264 "existing lease on %s", dsmp
->dsm_name
);
266 dhcpmsg(MSG_INFO
, "no ACK/NAK to INIT_REBOOT REQUEST, "
267 "using remainder of existing lease on %s", dsmp
->dsm_name
);
271 * We already stuck our old ack in dsmp->dsm_ack and relativized the
272 * packet times, so we can just pretend that the server sent it to us
273 * and move to bound. If that fails, fall back to selecting.
276 if (dhcp_bound(dsmp
, NULL
)) {
277 if (dsmp
->dsm_isv6
) {
278 if (!save_server_id(dsmp
, dsmp
->dsm_ack
))
280 server_unicast_option(dsmp
, dsmp
->dsm_ack
);
284 dhcpmsg(MSG_INFO
, "unable to use saved lease on %s; restarting",
286 dhcp_selecting(dsmp
);