xref: /illumos-gate/usr/src/cmd/cmd-inet/sbin/dhcpagent/select.c (revision 60a3f738d56f92ae8b80e4b62a2331c6e1f2311f)
1 /*
2  * CDDL HEADER START
3  *
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.
7  *
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.
12  *
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]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  *
25  * SELECTING state of the client state machine.
26  */
27 
28 #pragma ident	"%Z%%M%	%I%	%E% SMI"
29 
30 #include <sys/types.h>
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <strings.h>
34 #include <time.h>
35 #include <limits.h>
36 #include <netinet/in.h>
37 #include <sys/socket.h>
38 #include <net/route.h>
39 #include <net/if.h>
40 #include <netinet/dhcp.h>
41 #include <netinet/udp.h>
42 #include <netinet/ip_var.h>
43 #include <netinet/udp_var.h>
44 #include <stropts.h>				/* FLUSHR/FLUSHW */
45 #include <dhcpmsg.h>
46 
47 #include "states.h"
48 #include "agent.h"
49 #include "util.h"
50 #include "interface.h"
51 #include "packet.h"
52 #include "defaults.h"
53 
54 static iu_eh_callback_t	dhcp_collect_offers;
55 static stop_func_t	stop_selecting;
56 
57 /*
58  * dhcp_start(): starts DHCP on an interface
59  *
60  *   input: iu_tq_t *: unused
61  *	    void *: the interface to start DHCP on
62  *  output: void
63  */
64 
65 /* ARGSUSED */
66 void
67 dhcp_start(iu_tq_t *tqp, void *arg)
68 {
69 	struct ifslist	*ifsp = (struct ifslist *)arg;
70 
71 	if (check_ifs(ifsp) == 0) {
72 		(void) release_ifs(ifsp);
73 		return;
74 	}
75 
76 	dhcpmsg(MSG_VERBOSE, "starting DHCP on %s", ifsp->if_name);
77 	dhcp_selecting(ifsp);
78 }
79 
80 /*
81  * dhcp_selecting(): sends a DISCOVER and sets up reception for an OFFER
82  *
83  *   input: struct ifslist *: the interface to send the DISCOVER on, ...
84  *  output: void
85  */
86 
87 void
88 dhcp_selecting(struct ifslist *ifsp)
89 {
90 	dhcp_pkt_t		*dpkt;
91 	const char		*reqhost;
92 	char			hostfile[PATH_MAX + 1];
93 
94 	/*
95 	 * we first set up to collect OFFER packets as they arrive.
96 	 * we then send out DISCOVER probes.  then we wait at a
97 	 * user-tunable number of seconds before seeing if OFFERs have
98 	 * come in response to our DISCOVER.  if none have come in, we
99 	 * continue to wait, sending out our DISCOVER probes with
100 	 * exponential backoff.  if an OFFER is never received, we
101 	 * will wait forever (note that since we're event-driven
102 	 * though, we're still able to service other interfaces.)
103 	 *
104 	 * note that we do an reset_ifs() here because we may be
105 	 * landing in dhcp_selecting() as a result of restarting DHCP,
106 	 * so the ifs may not be fresh.
107 	 */
108 
109 	reset_ifs(ifsp);
110 	ifsp->if_state = SELECTING;
111 
112 	if ((ifsp->if_offer_id = iu_register_event(eh, ifsp->if_dlpi_fd, POLLIN,
113 	    dhcp_collect_offers, ifsp)) == -1) {
114 
115 		dhcpmsg(MSG_ERROR, "dhcp_selecting: cannot register to collect "
116 		    "OFFER packets, reverting to INIT on %s",
117 		    ifsp->if_name);
118 
119 		ifsp->if_state   = INIT;
120 		ifsp->if_dflags |= DHCP_IF_FAILED;
121 		ipc_action_finish(ifsp, DHCP_IPC_E_MEMORY);
122 		async_finish(ifsp);
123 		return;
124 	} else
125 		hold_ifs(ifsp);
126 
127 
128 	if ((ifsp->if_offer_timer = iu_schedule_timer(tq,
129 	    ifsp->if_offer_wait, dhcp_requesting, ifsp)) == -1) {
130 
131 		dhcpmsg(MSG_ERROR, "dhcp_selecting: cannot schedule to read "
132 		    "OFFER packets");
133 
134 		if (iu_unregister_event(eh, ifsp->if_offer_id, NULL) != 0) {
135 			ifsp->if_offer_id = -1;
136 			(void) release_ifs(ifsp);
137 		}
138 
139 		ifsp->if_state   = INIT;
140 		ifsp->if_dflags |= DHCP_IF_FAILED;
141 		ipc_action_finish(ifsp, DHCP_IPC_E_MEMORY);
142 		async_finish(ifsp);
143 		return;
144 	} else
145 		hold_ifs(ifsp);
146 
147 	/*
148 	 * Assemble DHCPDISCOVER message.  The max dhcp message size
149 	 * option is set to the interface max, minus the size of the udp and
150 	 * ip headers.
151 	 */
152 
153 	dpkt = init_pkt(ifsp, DISCOVER);
154 
155 	add_pkt_opt16(dpkt, CD_MAX_DHCP_SIZE, htons(ifsp->if_max -
156 			sizeof (struct udpiphdr)));
157 	add_pkt_opt32(dpkt, CD_LEASE_TIME, htonl(DHCP_PERM));
158 
159 	add_pkt_opt(dpkt, CD_CLASS_ID, class_id, class_id_len);
160 	add_pkt_opt(dpkt, CD_REQUEST_LIST, ifsp->if_prl, ifsp->if_prllen);
161 
162 	if (df_get_bool(ifsp->if_name, DF_REQUEST_HOSTNAME)) {
163 		dhcpmsg(MSG_DEBUG, "dhcp_selecting: DF_REQUEST_HOSTNAME");
164 		(void) snprintf(hostfile, sizeof (hostfile), "/etc/hostname.%s",
165 		    ifsp->if_name);
166 
167 		if ((reqhost = iffile_to_hostname(hostfile)) != NULL) {
168 			dhcpmsg(MSG_DEBUG, "dhcp_selecting: host %s", reqhost);
169 			if ((ifsp->if_reqhost = strdup(reqhost)) != NULL)
170 				add_pkt_opt(dpkt, CD_HOSTNAME, ifsp->if_reqhost,
171 				    strlen(ifsp->if_reqhost));
172 			else
173 				dhcpmsg(MSG_WARNING, "dhcp_selecting: cannot"
174 				    " allocate memory for host name option");
175 		}
176 	}
177 	add_pkt_opt(dpkt, CD_END, NULL, 0);
178 
179 	(void) send_pkt(ifsp, dpkt, htonl(INADDR_BROADCAST), stop_selecting);
180 }
181 
182 /*
183  * dhcp_collect_offers(): collects incoming OFFERs to a DISCOVER
184  *
185  *   input: iu_eh_t *: unused
186  *	    int: the file descriptor the OFFER arrived on
187  *	    short: unused
188  *	    iu_event_id_t: the id of this event callback with the handler
189  *	    void *: the interface that received the OFFER
190  *  output: void
191  */
192 
193 /* ARGSUSED */
194 static void
195 dhcp_collect_offers(iu_eh_t *eh, int fd, short events, iu_event_id_t id,
196     void *arg)
197 {
198 	struct ifslist	*ifsp = (struct ifslist *)arg;
199 
200 	if (verify_ifs(ifsp) == 0) {
201 		(void) ioctl(fd, I_FLUSH, FLUSHR|FLUSHW);
202 		return;
203 	}
204 
205 	/*
206 	 * DHCP_PUNTYPED messages are BOOTP server responses.
207 	 */
208 
209 	(void) recv_pkt(ifsp, fd, DHCP_POFFER|DHCP_PUNTYPED, B_TRUE);
210 }
211 
212 /*
213  * stop_selecting(): decides when to stop retransmitting DISCOVERs (never)
214  *
215  *   input: struct ifslist *: the interface DISCOVERs are being sent on
216  *	    unsigned int: the number of DISCOVERs sent so far
217  *  output: boolean_t: B_TRUE if retransmissions should stop
218  */
219 
220 /* ARGSUSED */
221 static boolean_t
222 stop_selecting(struct ifslist *ifsp, unsigned int n_discovers)
223 {
224 	return (B_FALSE);
225 }
226