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.
23 * Copyright (c) 2016-2017, Chris Fraire <cfraire@me.com>.
25 * SELECTING state of the client state machine.
28 #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
)
111 * We first set up to collect OFFER/Advertise packets as they arrive.
112 * We then send out DISCOVER/Solicit probes. Then we wait a
113 * user-tunable number of seconds before seeing if OFFERs/
114 * Advertisements have come in response to our DISCOVER/Solicit. If
115 * none have come in, we continue to wait, sending out our DISCOVER/
116 * Solicit probes with exponential backoff. If no OFFER/Advertisement
117 * is ever received, we will wait forever (note that since we're
118 * event-driven though, we're still able to service other state
121 * Note that we do an reset_smach() here because we may be landing in
122 * dhcp_selecting() as a result of restarting DHCP, so the state
123 * machine may not be fresh.
127 if (!set_smach_state(dsmp
, SELECTING
)) {
129 "dhcp_selecting: cannot switch to SELECTING state; "
130 "reverting to INIT on %s", dsmp
->dsm_name
);
135 /* Remove the stale hostconf file, if there is any */
136 (void) remove_hostconf(dsmp
->dsm_name
, dsmp
->dsm_isv6
);
138 dsmp
->dsm_offer_timer
= iu_schedule_timer(tq
,
139 dsmp
->dsm_offer_wait
, dhcp_requesting
, dsmp
);
140 if (dsmp
->dsm_offer_timer
== -1) {
141 dhcpmsg(MSG_ERROR
, "dhcp_selecting: cannot schedule to read "
142 "%s packets", dsmp
->dsm_isv6
? "Advertise" : "OFFER");
149 * Assemble and send the DHCPDISCOVER or Solicit message.
151 * If this fails, we'll wait for the select timer to go off
152 * before trying again.
154 if (dsmp
->dsm_isv6
) {
157 if ((dpkt
= init_pkt(dsmp
, DHCPV6_MSG_SOLICIT
)) == NULL
) {
158 dhcpmsg(MSG_ERROR
, "dhcp_selecting: unable to set up "
163 /* Add an IA_NA option for our controlling LIF */
164 d6in
.d6in_iaid
= htonl(dsmp
->dsm_lif
->lif_iaid
);
165 d6in
.d6in_t1
= htonl(0);
166 d6in
.d6in_t2
= htonl(0);
167 (void) add_pkt_opt(dpkt
, DHCPV6_OPT_IA_NA
,
168 (dhcpv6_option_t
*)&d6in
+ 1,
169 sizeof (d6in
) - sizeof (dhcpv6_option_t
));
171 /* Option Request option for desired information */
172 (void) add_pkt_prl(dpkt
, dsmp
);
174 /* Enable Rapid-Commit */
175 (void) add_pkt_opt(dpkt
, DHCPV6_OPT_RAPID_COMMIT
, NULL
, 0);
177 /* xxx add Reconfigure Accept */
179 (void) send_pkt_v6(dsmp
, dpkt
, ipv6_all_dhcp_relay_and_servers
,
180 stop_selecting
, DHCPV6_SOL_TIMEOUT
, DHCPV6_SOL_MAX_RT
);
182 if ((dpkt
= init_pkt(dsmp
, DISCOVER
)) == NULL
) {
183 dhcpmsg(MSG_ERROR
, "dhcp_selecting: unable to set up "
189 * The max DHCP message size option is set to the interface
190 * MTU, minus the size of the UDP and IP headers.
192 (void) add_pkt_opt16(dpkt
, CD_MAX_DHCP_SIZE
,
193 htons(dsmp
->dsm_lif
->lif_max
- sizeof (struct udpiphdr
)));
194 (void) add_pkt_opt32(dpkt
, CD_LEASE_TIME
, htonl(DHCP_PERM
));
196 if (class_id_len
!= 0) {
197 (void) add_pkt_opt(dpkt
, CD_CLASS_ID
, class_id
,
200 (void) add_pkt_prl(dpkt
, dsmp
);
202 if (!dhcp_add_fqdn_opt(dpkt
, dsmp
))
203 (void) dhcp_add_hostname_opt(dpkt
, dsmp
);
205 (void) add_pkt_opt(dpkt
, CD_END
, NULL
, 0);
207 (void) send_pkt(dsmp
, dpkt
, htonl(INADDR_BROADCAST
),
213 (void) set_smach_state(dsmp
, INIT
);
214 dsmp
->dsm_dflags
|= DHCP_IF_FAILED
;
215 ipc_action_finish(dsmp
, DHCP_IPC_E_MEMORY
);
219 * stop_selecting(): decides when to stop retransmitting DISCOVERs -- only when
220 * abandoning the state machine. For DHCPv6, this timer may
221 * go off before the offer wait timer. If so, then this is a
222 * good time to check for valid Advertisements, so cancel the
223 * timer and go check.
225 * input: dhcp_smach_t *: the state machine DISCOVERs are being sent on
226 * unsigned int: the number of DISCOVERs sent so far
227 * output: boolean_t: B_TRUE if retransmissions should stop
232 stop_selecting(dhcp_smach_t
*dsmp
, unsigned int n_discovers
)
235 * If we're using v4 and the underlying LIF we're trying to configure
236 * has been touched by the user, then bail out.
238 if (!dsmp
->dsm_isv6
&& !verify_lif(dsmp
->dsm_lif
)) {
239 finished_smach(dsmp
, DHCP_IPC_E_UNKIF
);
243 if (dsmp
->dsm_recv_pkt_list
!= NULL
) {
244 dhcp_requesting(NULL
, dsmp
);
245 if (dsmp
->dsm_state
!= SELECTING
)