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 * SELECTING state of the client state machine.
27 #include <sys/types.h>
33 #include <netinet/in.h>
34 #include <net/route.h>
36 #include <netinet/dhcp.h>
37 #include <netinet/udp.h>
38 #include <netinet/ip_var.h>
39 #include <netinet/udp_var.h>
41 #include <dhcp_hostconf.h>
46 #include "interface.h"
50 static stop_func_t stop_selecting
;
53 * dhcp_start(): starts DHCP on a state machine
55 * input: iu_tq_t *: unused
56 * void *: the state machine on which to start DHCP
62 dhcp_start(iu_tq_t
*tqp
, void *arg
)
64 dhcp_smach_t
*dsmp
= arg
;
66 dsmp
->dsm_start_timer
= -1;
67 (void) set_smach_state(dsmp
, INIT
);
68 if (verify_smach(dsmp
)) {
69 dhcpmsg(MSG_VERBOSE
, "starting DHCP on %s", dsmp
->dsm_name
);
75 * set_start_timer(): sets a random timer to start a DHCP state machine
77 * input: dhcp_smach_t *: the state machine on which to start DHCP
78 * output: boolean_t: B_TRUE if a timer is now running
82 set_start_timer(dhcp_smach_t
*dsmp
)
84 if (dsmp
->dsm_start_timer
!= -1)
87 dsmp
->dsm_start_timer
= iu_schedule_timer_ms(tq
,
88 lrand48() % DHCP_SELECT_WAIT
, dhcp_start
, dsmp
);
89 if (dsmp
->dsm_start_timer
== -1)
97 * dhcp_selecting(): sends a DISCOVER and sets up reception of OFFERs for
98 * IPv4, or sends a Solicit and sets up reception of
99 * Advertisements for DHCPv6.
101 * input: dhcp_smach_t *: the state machine on which to send the DISCOVER
106 dhcp_selecting(dhcp_smach_t
*dsmp
)
110 char hostfile
[PATH_MAX
+ 1];
113 * We first set up to collect OFFER/Advertise packets as they arrive.
114 * We then send out DISCOVER/Solicit probes. Then we wait a
115 * user-tunable number of seconds before seeing if OFFERs/
116 * Advertisements have come in response to our DISCOVER/Solicit. If
117 * none have come in, we continue to wait, sending out our DISCOVER/
118 * Solicit probes with exponential backoff. If no OFFER/Advertisement
119 * is ever received, we will wait forever (note that since we're
120 * event-driven though, we're still able to service other state
123 * Note that we do an reset_smach() here because we may be landing in
124 * dhcp_selecting() as a result of restarting DHCP, so the state
125 * machine may not be fresh.
129 if (!set_smach_state(dsmp
, SELECTING
)) {
131 "dhcp_selecting: cannot switch to SELECTING state; "
132 "reverting to INIT on %s", dsmp
->dsm_name
);
137 /* Remove the stale hostconf file, if there is any */
138 (void) remove_hostconf(dsmp
->dsm_name
, dsmp
->dsm_isv6
);
140 dsmp
->dsm_offer_timer
= iu_schedule_timer(tq
,
141 dsmp
->dsm_offer_wait
, dhcp_requesting
, dsmp
);
142 if (dsmp
->dsm_offer_timer
== -1) {
143 dhcpmsg(MSG_ERROR
, "dhcp_selecting: cannot schedule to read "
144 "%s packets", dsmp
->dsm_isv6
? "Advertise" : "OFFER");
151 * Assemble and send the DHCPDISCOVER or Solicit message.
153 * If this fails, we'll wait for the select timer to go off
154 * before trying again.
156 if (dsmp
->dsm_isv6
) {
159 if ((dpkt
= init_pkt(dsmp
, DHCPV6_MSG_SOLICIT
)) == NULL
) {
160 dhcpmsg(MSG_ERROR
, "dhcp_selecting: unable to set up "
165 /* Add an IA_NA option for our controlling LIF */
166 d6in
.d6in_iaid
= htonl(dsmp
->dsm_lif
->lif_iaid
);
167 d6in
.d6in_t1
= htonl(0);
168 d6in
.d6in_t2
= htonl(0);
169 (void) add_pkt_opt(dpkt
, DHCPV6_OPT_IA_NA
,
170 (dhcpv6_option_t
*)&d6in
+ 1,
171 sizeof (d6in
) - sizeof (dhcpv6_option_t
));
173 /* Option Request option for desired information */
174 (void) add_pkt_prl(dpkt
, dsmp
);
176 /* Enable Rapid-Commit */
177 (void) add_pkt_opt(dpkt
, DHCPV6_OPT_RAPID_COMMIT
, NULL
, 0);
179 /* xxx add Reconfigure Accept */
181 (void) send_pkt_v6(dsmp
, dpkt
, ipv6_all_dhcp_relay_and_servers
,
182 stop_selecting
, DHCPV6_SOL_TIMEOUT
, DHCPV6_SOL_MAX_RT
);
184 if ((dpkt
= init_pkt(dsmp
, DISCOVER
)) == NULL
) {
185 dhcpmsg(MSG_ERROR
, "dhcp_selecting: unable to set up "
191 * The max DHCP message size option is set to the interface
192 * MTU, minus the size of the UDP and IP headers.
194 (void) add_pkt_opt16(dpkt
, CD_MAX_DHCP_SIZE
,
195 htons(dsmp
->dsm_lif
->lif_max
- sizeof (struct udpiphdr
)));
196 (void) add_pkt_opt32(dpkt
, CD_LEASE_TIME
, htonl(DHCP_PERM
));
198 if (class_id_len
!= 0) {
199 (void) add_pkt_opt(dpkt
, CD_CLASS_ID
, class_id
,
202 (void) add_pkt_prl(dpkt
, dsmp
);
204 if (df_get_bool(dsmp
->dsm_name
, dsmp
->dsm_isv6
,
205 DF_REQUEST_HOSTNAME
)) {
207 "dhcp_selecting: DF_REQUEST_HOSTNAME");
208 (void) snprintf(hostfile
, sizeof (hostfile
),
209 "/etc/hostname.%s", dsmp
->dsm_name
);
211 if ((reqhost
= iffile_to_hostname(hostfile
)) != NULL
) {
212 dhcpmsg(MSG_DEBUG
, "dhcp_selecting: host %s",
214 dsmp
->dsm_reqhost
= strdup(reqhost
);
215 if (dsmp
->dsm_reqhost
!= NULL
)
216 (void) add_pkt_opt(dpkt
, CD_HOSTNAME
,
218 strlen(dsmp
->dsm_reqhost
));
221 "dhcp_selecting: cannot allocate "
222 "memory for host name option");
225 (void) add_pkt_opt(dpkt
, CD_END
, NULL
, 0);
227 (void) send_pkt(dsmp
, dpkt
, htonl(INADDR_BROADCAST
),
233 (void) set_smach_state(dsmp
, INIT
);
234 dsmp
->dsm_dflags
|= DHCP_IF_FAILED
;
235 ipc_action_finish(dsmp
, DHCP_IPC_E_MEMORY
);
239 * stop_selecting(): decides when to stop retransmitting DISCOVERs -- only when
240 * abandoning the state machine. For DHCPv6, this timer may
241 * go off before the offer wait timer. If so, then this is a
242 * good time to check for valid Advertisements, so cancel the
243 * timer and go check.
245 * input: dhcp_smach_t *: the state machine DISCOVERs are being sent on
246 * unsigned int: the number of DISCOVERs sent so far
247 * output: boolean_t: B_TRUE if retransmissions should stop
252 stop_selecting(dhcp_smach_t
*dsmp
, unsigned int n_discovers
)
255 * If we're using v4 and the underlying LIF we're trying to configure
256 * has been touched by the user, then bail out.
258 if (!dsmp
->dsm_isv6
&& !verify_lif(dsmp
->dsm_lif
)) {
259 finished_smach(dsmp
, DHCP_IPC_E_UNKIF
);
263 if (dsmp
->dsm_recv_pkt_list
!= NULL
) {
264 dhcp_requesting(NULL
, dsmp
);
265 if (dsmp
->dsm_state
!= SELECTING
)