1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  *
26*0Sstevel@tonic-gate  * REQUESTING state of the client state machine.
27*0Sstevel@tonic-gate  */
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
30*0Sstevel@tonic-gate 
31*0Sstevel@tonic-gate #include <sys/types.h>
32*0Sstevel@tonic-gate #include <sys/stropts.h>	/* FLUSHR/FLUSHW */
33*0Sstevel@tonic-gate #include <netinet/in.h>
34*0Sstevel@tonic-gate #include <netinet/dhcp.h>
35*0Sstevel@tonic-gate #include <netinet/udp.h>
36*0Sstevel@tonic-gate #include <netinet/ip_var.h>
37*0Sstevel@tonic-gate #include <netinet/udp_var.h>
38*0Sstevel@tonic-gate #include <dhcp_hostconf.h>
39*0Sstevel@tonic-gate #include <arpa/inet.h>
40*0Sstevel@tonic-gate #include <string.h>
41*0Sstevel@tonic-gate #include <stdlib.h>
42*0Sstevel@tonic-gate #include <unistd.h>
43*0Sstevel@tonic-gate #include <dhcpmsg.h>
44*0Sstevel@tonic-gate 
45*0Sstevel@tonic-gate #include "states.h"
46*0Sstevel@tonic-gate #include "util.h"
47*0Sstevel@tonic-gate #include "packet.h"
48*0Sstevel@tonic-gate #include "interface.h"
49*0Sstevel@tonic-gate #include "agent.h"
50*0Sstevel@tonic-gate #include "defaults.h"
51*0Sstevel@tonic-gate 
52*0Sstevel@tonic-gate static PKT_LIST		*select_best(PKT_LIST **);
53*0Sstevel@tonic-gate static void		restart_dhcp(struct ifslist *);
54*0Sstevel@tonic-gate static stop_func_t	stop_requesting;
55*0Sstevel@tonic-gate 
56*0Sstevel@tonic-gate /*
57*0Sstevel@tonic-gate  * dhcp_requesting(): checks if OFFER packets to come in from DHCP servers.
58*0Sstevel@tonic-gate  *		      if so, chooses the best one, sends a REQUEST to the
59*0Sstevel@tonic-gate  *		      server and registers an event handler to receive
60*0Sstevel@tonic-gate  *		      the ACK/NAK
61*0Sstevel@tonic-gate  *
62*0Sstevel@tonic-gate  *   input: iu_tq_t *: unused
63*0Sstevel@tonic-gate  *	    void *: the interface receiving OFFER packets
64*0Sstevel@tonic-gate  *  output: void
65*0Sstevel@tonic-gate  */
66*0Sstevel@tonic-gate 
67*0Sstevel@tonic-gate /* ARGSUSED */
68*0Sstevel@tonic-gate void
69*0Sstevel@tonic-gate dhcp_requesting(iu_tq_t *tqp, void *arg)
70*0Sstevel@tonic-gate {
71*0Sstevel@tonic-gate 	struct ifslist		*ifsp = (struct ifslist *)arg;
72*0Sstevel@tonic-gate 	dhcp_pkt_t		*dpkt;
73*0Sstevel@tonic-gate 	PKT_LIST		*offer;
74*0Sstevel@tonic-gate 	lease_t			lease;
75*0Sstevel@tonic-gate 
76*0Sstevel@tonic-gate 	if (check_ifs(ifsp) == 0) {
77*0Sstevel@tonic-gate 		(void) release_ifs(ifsp);
78*0Sstevel@tonic-gate 		return;
79*0Sstevel@tonic-gate 	}
80*0Sstevel@tonic-gate 
81*0Sstevel@tonic-gate 	/*
82*0Sstevel@tonic-gate 	 * select the best OFFER; all others pitched.
83*0Sstevel@tonic-gate 	 */
84*0Sstevel@tonic-gate 
85*0Sstevel@tonic-gate 	offer = select_best(&ifsp->if_recv_pkt_list);
86*0Sstevel@tonic-gate 	if (offer == NULL) {
87*0Sstevel@tonic-gate 
88*0Sstevel@tonic-gate 		dhcpmsg(MSG_VERBOSE, "no OFFERs on %s, waiting...",
89*0Sstevel@tonic-gate 		    ifsp->if_name);
90*0Sstevel@tonic-gate 
91*0Sstevel@tonic-gate 		/*
92*0Sstevel@tonic-gate 		 * no acceptable OFFERs have come in.  reschedule
93*0Sstevel@tonic-gate 		 * ourselves for callback.
94*0Sstevel@tonic-gate 		 */
95*0Sstevel@tonic-gate 
96*0Sstevel@tonic-gate 		if (iu_schedule_timer(tq, ifsp->if_offer_wait,
97*0Sstevel@tonic-gate 		    dhcp_requesting, ifsp) == -1) {
98*0Sstevel@tonic-gate 
99*0Sstevel@tonic-gate 			/*
100*0Sstevel@tonic-gate 			 * ugh.  the best we can do at this point is
101*0Sstevel@tonic-gate 			 * revert back to INIT and wait for a user to
102*0Sstevel@tonic-gate 			 * restart us.
103*0Sstevel@tonic-gate 			 */
104*0Sstevel@tonic-gate 
105*0Sstevel@tonic-gate 			ifsp->if_state	 = INIT;
106*0Sstevel@tonic-gate 			ifsp->if_dflags |= DHCP_IF_FAILED;
107*0Sstevel@tonic-gate 
108*0Sstevel@tonic-gate 			stop_pkt_retransmission(ifsp);
109*0Sstevel@tonic-gate 			ipc_action_finish(ifsp, DHCP_IPC_E_MEMORY);
110*0Sstevel@tonic-gate 			async_finish(ifsp);
111*0Sstevel@tonic-gate 
112*0Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "dhcp_requesting: cannot "
113*0Sstevel@tonic-gate 			    "reschedule callback, reverting to INIT state on "
114*0Sstevel@tonic-gate 			    "%s", ifsp->if_name);
115*0Sstevel@tonic-gate 		} else
116*0Sstevel@tonic-gate 			hold_ifs(ifsp);
117*0Sstevel@tonic-gate 
118*0Sstevel@tonic-gate 		return;
119*0Sstevel@tonic-gate 	}
120*0Sstevel@tonic-gate 
121*0Sstevel@tonic-gate 	stop_pkt_retransmission(ifsp);
122*0Sstevel@tonic-gate 
123*0Sstevel@tonic-gate 	/*
124*0Sstevel@tonic-gate 	 * stop collecting packets.  check to see whether we got an
125*0Sstevel@tonic-gate 	 * OFFER or a BOOTP packet.  if we got a BOOTP packet, go to
126*0Sstevel@tonic-gate 	 * the BOUND state now.
127*0Sstevel@tonic-gate 	 */
128*0Sstevel@tonic-gate 
129*0Sstevel@tonic-gate 	if (iu_unregister_event(eh, ifsp->if_offer_id, NULL) != 0) {
130*0Sstevel@tonic-gate 		(void) release_ifs(ifsp);
131*0Sstevel@tonic-gate 		ifsp->if_offer_id = -1;
132*0Sstevel@tonic-gate 	}
133*0Sstevel@tonic-gate 
134*0Sstevel@tonic-gate 	if (offer->opts[CD_DHCP_TYPE] == NULL) {
135*0Sstevel@tonic-gate 
136*0Sstevel@tonic-gate 		ifsp->if_state = REQUESTING;
137*0Sstevel@tonic-gate 
138*0Sstevel@tonic-gate 		if (dhcp_bound(ifsp, offer) == 0) {
139*0Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "dhcp_requesting: dhcp_bound "
140*0Sstevel@tonic-gate 			    "failed for %s", ifsp->if_name);
141*0Sstevel@tonic-gate 			restart_dhcp(ifsp);
142*0Sstevel@tonic-gate 			return;
143*0Sstevel@tonic-gate 		}
144*0Sstevel@tonic-gate 
145*0Sstevel@tonic-gate 		return;
146*0Sstevel@tonic-gate 	}
147*0Sstevel@tonic-gate 
148*0Sstevel@tonic-gate 	/*
149*0Sstevel@tonic-gate 	 * if we got a message from the server, display it.
150*0Sstevel@tonic-gate 	 */
151*0Sstevel@tonic-gate 
152*0Sstevel@tonic-gate 	if (offer->opts[CD_MESSAGE] != NULL)
153*0Sstevel@tonic-gate 		print_server_msg(ifsp, offer->opts[CD_MESSAGE]);
154*0Sstevel@tonic-gate 
155*0Sstevel@tonic-gate 	/*
156*0Sstevel@tonic-gate 	 * assemble a DHCPREQUEST, with the ciaddr field set to 0,
157*0Sstevel@tonic-gate 	 * since we got here from the INIT state.
158*0Sstevel@tonic-gate 	 */
159*0Sstevel@tonic-gate 
160*0Sstevel@tonic-gate 	dpkt = init_pkt(ifsp, REQUEST);
161*0Sstevel@tonic-gate 
162*0Sstevel@tonic-gate 	/*
163*0Sstevel@tonic-gate 	 * grab the lease out of the OFFER; we know it's valid since
164*0Sstevel@tonic-gate 	 * select_best() already checked.  The max dhcp message size
165*0Sstevel@tonic-gate 	 * option is set to the interface max, minus the size of the udp and
166*0Sstevel@tonic-gate 	 * ip headers.
167*0Sstevel@tonic-gate 	 */
168*0Sstevel@tonic-gate 
169*0Sstevel@tonic-gate 	(void) memcpy(&lease, offer->opts[CD_LEASE_TIME]->value,
170*0Sstevel@tonic-gate 	    sizeof (lease_t));
171*0Sstevel@tonic-gate 
172*0Sstevel@tonic-gate 	add_pkt_opt32(dpkt, CD_LEASE_TIME, lease);
173*0Sstevel@tonic-gate 	add_pkt_opt16(dpkt, CD_MAX_DHCP_SIZE, htons(ifsp->if_max -
174*0Sstevel@tonic-gate 			sizeof (struct udpiphdr)));
175*0Sstevel@tonic-gate 	add_pkt_opt32(dpkt, CD_REQUESTED_IP_ADDR, offer->pkt->yiaddr.s_addr);
176*0Sstevel@tonic-gate 	add_pkt_opt(dpkt, CD_SERVER_ID, offer->opts[CD_SERVER_ID]->value,
177*0Sstevel@tonic-gate 	    offer->opts[CD_SERVER_ID]->len);
178*0Sstevel@tonic-gate 
179*0Sstevel@tonic-gate 	add_pkt_opt(dpkt, CD_CLASS_ID, class_id, class_id_len);
180*0Sstevel@tonic-gate 	add_pkt_opt(dpkt, CD_REQUEST_LIST, ifsp->if_prl, ifsp->if_prllen);
181*0Sstevel@tonic-gate 
182*0Sstevel@tonic-gate 	/*
183*0Sstevel@tonic-gate 	 * if_reqhost was set for this interface in dhcp_selecting()
184*0Sstevel@tonic-gate 	 * if the DF_REQUEST_HOSTNAME option set and a host name was
185*0Sstevel@tonic-gate 	 * found
186*0Sstevel@tonic-gate 	 */
187*0Sstevel@tonic-gate 	if (ifsp->if_reqhost != NULL) {
188*0Sstevel@tonic-gate 		add_pkt_opt(dpkt, CD_HOSTNAME, ifsp->if_reqhost,
189*0Sstevel@tonic-gate 		    strlen(ifsp->if_reqhost));
190*0Sstevel@tonic-gate 	}
191*0Sstevel@tonic-gate 	add_pkt_opt(dpkt, CD_END, NULL, 0);
192*0Sstevel@tonic-gate 
193*0Sstevel@tonic-gate 	/* all done with the offer */
194*0Sstevel@tonic-gate 	free_pkt_list(&offer);
195*0Sstevel@tonic-gate 
196*0Sstevel@tonic-gate 	/*
197*0Sstevel@tonic-gate 	 * send out the REQUEST, trying retransmissions.  either a NAK
198*0Sstevel@tonic-gate 	 * or too many REQUEST attempts will revert us to SELECTING.
199*0Sstevel@tonic-gate 	 */
200*0Sstevel@tonic-gate 
201*0Sstevel@tonic-gate 	ifsp->if_state = REQUESTING;
202*0Sstevel@tonic-gate 	(void) send_pkt(ifsp, dpkt, htonl(INADDR_BROADCAST), stop_requesting);
203*0Sstevel@tonic-gate 
204*0Sstevel@tonic-gate 	/*
205*0Sstevel@tonic-gate 	 * wait for an ACK or NAK to come back from the server.  if
206*0Sstevel@tonic-gate 	 * we can't register this event handler, then we won't be able
207*0Sstevel@tonic-gate 	 * to see the server's responses.  the best we can really do
208*0Sstevel@tonic-gate 	 * in that case is drop back to INIT and hope someone notices.
209*0Sstevel@tonic-gate 	 */
210*0Sstevel@tonic-gate 
211*0Sstevel@tonic-gate 	if (register_acknak(ifsp) == 0) {
212*0Sstevel@tonic-gate 
213*0Sstevel@tonic-gate 		ifsp->if_state	 = INIT;
214*0Sstevel@tonic-gate 		ifsp->if_dflags |= DHCP_IF_FAILED;
215*0Sstevel@tonic-gate 
216*0Sstevel@tonic-gate 		ipc_action_finish(ifsp, DHCP_IPC_E_MEMORY);
217*0Sstevel@tonic-gate 		async_finish(ifsp);
218*0Sstevel@tonic-gate 
219*0Sstevel@tonic-gate 		dhcpmsg(MSG_ERROR, "dhcp_requesting: cannot register to "
220*0Sstevel@tonic-gate 		    "collect ACK/NAK packets, reverting to INIT on %s",
221*0Sstevel@tonic-gate 		    ifsp->if_name);
222*0Sstevel@tonic-gate 	}
223*0Sstevel@tonic-gate }
224*0Sstevel@tonic-gate 
225*0Sstevel@tonic-gate /*
226*0Sstevel@tonic-gate  * select_best(): selects the best OFFER packet from a list of OFFER packets
227*0Sstevel@tonic-gate  *
228*0Sstevel@tonic-gate  *   input: PKT_LIST **: a list of packets to select the best from
229*0Sstevel@tonic-gate  *  output: PKT_LIST *: the best packet, or NULL if none are acceptable
230*0Sstevel@tonic-gate  */
231*0Sstevel@tonic-gate 
232*0Sstevel@tonic-gate static PKT_LIST *
233*0Sstevel@tonic-gate select_best(PKT_LIST **pkts)
234*0Sstevel@tonic-gate {
235*0Sstevel@tonic-gate 	PKT_LIST	*current, *best = NULL;
236*0Sstevel@tonic-gate 	uint32_t	points, best_points = 0;
237*0Sstevel@tonic-gate 
238*0Sstevel@tonic-gate 	/*
239*0Sstevel@tonic-gate 	 * pick out the best offer.  point system.
240*0Sstevel@tonic-gate 	 * what's important?
241*0Sstevel@tonic-gate 	 *
242*0Sstevel@tonic-gate 	 *	0) DHCP
243*0Sstevel@tonic-gate 	 *	1) no option overload
244*0Sstevel@tonic-gate 	 *	2) encapsulated vendor option
245*0Sstevel@tonic-gate 	 *	3) non-null sname and siaddr fields
246*0Sstevel@tonic-gate 	 *	4) non-null file field
247*0Sstevel@tonic-gate 	 *	5) hostname
248*0Sstevel@tonic-gate 	 *	6) subnetmask
249*0Sstevel@tonic-gate 	 *	7) router
250*0Sstevel@tonic-gate 	 */
251*0Sstevel@tonic-gate 
252*0Sstevel@tonic-gate 	for (current = *pkts; current != NULL; current = current->next) {
253*0Sstevel@tonic-gate 
254*0Sstevel@tonic-gate 		points = 0;
255*0Sstevel@tonic-gate 
256*0Sstevel@tonic-gate 		if (current->opts[CD_DHCP_TYPE] == NULL) {
257*0Sstevel@tonic-gate 			dhcpmsg(MSG_VERBOSE, "valid BOOTP reply");
258*0Sstevel@tonic-gate 			goto valid_offer;
259*0Sstevel@tonic-gate 		}
260*0Sstevel@tonic-gate 
261*0Sstevel@tonic-gate 		if (current->opts[CD_LEASE_TIME] == NULL) {
262*0Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "select_best: OFFER without "
263*0Sstevel@tonic-gate 			    "lease time");
264*0Sstevel@tonic-gate 			continue;
265*0Sstevel@tonic-gate 		}
266*0Sstevel@tonic-gate 
267*0Sstevel@tonic-gate 		if (current->opts[CD_LEASE_TIME]->len != sizeof (lease_t)) {
268*0Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "select_best: OFFER with garbled "
269*0Sstevel@tonic-gate 			    "lease time");
270*0Sstevel@tonic-gate 			continue;
271*0Sstevel@tonic-gate 		}
272*0Sstevel@tonic-gate 
273*0Sstevel@tonic-gate 		if (current->opts[CD_SERVER_ID] == NULL) {
274*0Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "select_best: OFFER without "
275*0Sstevel@tonic-gate 			    "server id");
276*0Sstevel@tonic-gate 			continue;
277*0Sstevel@tonic-gate 		}
278*0Sstevel@tonic-gate 
279*0Sstevel@tonic-gate 		if (current->opts[CD_SERVER_ID]->len != sizeof (ipaddr_t)) {
280*0Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "select_best: OFFER with garbled "
281*0Sstevel@tonic-gate 			    "server id");
282*0Sstevel@tonic-gate 			continue;
283*0Sstevel@tonic-gate 		}
284*0Sstevel@tonic-gate 
285*0Sstevel@tonic-gate 		/* valid DHCP OFFER.  see if we got our parameters. */
286*0Sstevel@tonic-gate 		dhcpmsg(MSG_VERBOSE, "valid OFFER packet");
287*0Sstevel@tonic-gate 		points += 30;
288*0Sstevel@tonic-gate 
289*0Sstevel@tonic-gate valid_offer:
290*0Sstevel@tonic-gate 		if (current->rfc1048)
291*0Sstevel@tonic-gate 			points += 5;
292*0Sstevel@tonic-gate 
293*0Sstevel@tonic-gate 		/*
294*0Sstevel@tonic-gate 		 * also could be faked, though more difficult because
295*0Sstevel@tonic-gate 		 * the encapsulation is hard to encode on a BOOTP
296*0Sstevel@tonic-gate 		 * server; plus there's not as much real estate in the
297*0Sstevel@tonic-gate 		 * packet for options, so it's likely this option
298*0Sstevel@tonic-gate 		 * would get dropped.
299*0Sstevel@tonic-gate 		 */
300*0Sstevel@tonic-gate 
301*0Sstevel@tonic-gate 		if (current->opts[CD_VENDOR_SPEC] != NULL)
302*0Sstevel@tonic-gate 			points += 80;
303*0Sstevel@tonic-gate 
304*0Sstevel@tonic-gate 		if (current->opts[CD_SUBNETMASK] != NULL)
305*0Sstevel@tonic-gate 			points++;
306*0Sstevel@tonic-gate 
307*0Sstevel@tonic-gate 		if (current->opts[CD_ROUTER] != NULL)
308*0Sstevel@tonic-gate 			points++;
309*0Sstevel@tonic-gate 
310*0Sstevel@tonic-gate 		if (current->opts[CD_HOSTNAME] != NULL)
311*0Sstevel@tonic-gate 			points += 5;
312*0Sstevel@tonic-gate 
313*0Sstevel@tonic-gate 		dhcpmsg(MSG_DEBUG, "select_best: OFFER had %d points", points);
314*0Sstevel@tonic-gate 
315*0Sstevel@tonic-gate 		if (points >= best_points) {
316*0Sstevel@tonic-gate 			best_points = points;
317*0Sstevel@tonic-gate 			best = current;
318*0Sstevel@tonic-gate 		}
319*0Sstevel@tonic-gate 	}
320*0Sstevel@tonic-gate 
321*0Sstevel@tonic-gate 	if (best != NULL) {
322*0Sstevel@tonic-gate 		dhcpmsg(MSG_DEBUG, "select_best: most points: %d", best_points);
323*0Sstevel@tonic-gate 		remove_from_pkt_list(pkts, best);
324*0Sstevel@tonic-gate 	} else
325*0Sstevel@tonic-gate 		dhcpmsg(MSG_DEBUG, "select_best: no valid OFFER/BOOTP reply");
326*0Sstevel@tonic-gate 
327*0Sstevel@tonic-gate 	free_pkt_list(pkts);
328*0Sstevel@tonic-gate 	return (best);
329*0Sstevel@tonic-gate }
330*0Sstevel@tonic-gate 
331*0Sstevel@tonic-gate /*
332*0Sstevel@tonic-gate  * dhcp_acknak(): processes reception of an ACK or NAK packet on an interface
333*0Sstevel@tonic-gate  *
334*0Sstevel@tonic-gate  *   input: iu_eh_t *: unused
335*0Sstevel@tonic-gate  *	    int: the file descriptor the ACK/NAK arrived on
336*0Sstevel@tonic-gate  *	    short: unused
337*0Sstevel@tonic-gate  *	    iu_event_id_t: the id of this event callback with the handler
338*0Sstevel@tonic-gate  *	    void *: the interface that received the ACK or NAK
339*0Sstevel@tonic-gate  *  output: void
340*0Sstevel@tonic-gate  */
341*0Sstevel@tonic-gate 
342*0Sstevel@tonic-gate /* ARGSUSED */
343*0Sstevel@tonic-gate void
344*0Sstevel@tonic-gate dhcp_acknak(iu_eh_t *ehp, int fd, short events, iu_event_id_t id, void *arg)
345*0Sstevel@tonic-gate {
346*0Sstevel@tonic-gate 	struct ifslist		*ifsp = (struct ifslist *)arg;
347*0Sstevel@tonic-gate 	PKT_LIST		*plp;
348*0Sstevel@tonic-gate 
349*0Sstevel@tonic-gate 	if (check_ifs(ifsp) == 0) {
350*0Sstevel@tonic-gate 		/* unregister_acknak() does our release_ifs() */
351*0Sstevel@tonic-gate 		(void) unregister_acknak(ifsp);
352*0Sstevel@tonic-gate 		(void) ioctl(fd, I_FLUSH, FLUSHR|FLUSHW);
353*0Sstevel@tonic-gate 		return;
354*0Sstevel@tonic-gate 	}
355*0Sstevel@tonic-gate 
356*0Sstevel@tonic-gate 	/*
357*0Sstevel@tonic-gate 	 * note that check_ifs() did our release_ifs() but we're not
358*0Sstevel@tonic-gate 	 * sure we're done yet; call hold_ifs() to reacquire our hold;
359*0Sstevel@tonic-gate 	 * if we're done, unregister_acknak() will release_ifs() below.
360*0Sstevel@tonic-gate 	 */
361*0Sstevel@tonic-gate 
362*0Sstevel@tonic-gate 	hold_ifs(ifsp);
363*0Sstevel@tonic-gate 
364*0Sstevel@tonic-gate 	if (recv_pkt(ifsp, fd, DHCP_PACK|DHCP_PNAK, B_FALSE) == 0)
365*0Sstevel@tonic-gate 		return;
366*0Sstevel@tonic-gate 
367*0Sstevel@tonic-gate 	/*
368*0Sstevel@tonic-gate 	 * we've got a packet; make sure it's acceptable before
369*0Sstevel@tonic-gate 	 * cancelling the REQUEST retransmissions.
370*0Sstevel@tonic-gate 	 */
371*0Sstevel@tonic-gate 
372*0Sstevel@tonic-gate 	plp = ifsp->if_recv_pkt_list;
373*0Sstevel@tonic-gate 	remove_from_pkt_list(&ifsp->if_recv_pkt_list, plp);
374*0Sstevel@tonic-gate 
375*0Sstevel@tonic-gate 	if (*plp->opts[CD_DHCP_TYPE]->value == ACK) {
376*0Sstevel@tonic-gate 		if (plp->opts[CD_LEASE_TIME] == NULL ||
377*0Sstevel@tonic-gate 		    plp->opts[CD_LEASE_TIME]->len != sizeof (lease_t)) {
378*0Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "dhcp_acknak: ACK packet on %s "
379*0Sstevel@tonic-gate 			    "missing mandatory lease option, ignored",
380*0Sstevel@tonic-gate 			    ifsp->if_name);
381*0Sstevel@tonic-gate 			ifsp->if_bad_offers++;
382*0Sstevel@tonic-gate 			free_pkt_list(&plp);
383*0Sstevel@tonic-gate 			return;
384*0Sstevel@tonic-gate 		}
385*0Sstevel@tonic-gate 		if ((ifsp->if_state == RENEWING ||
386*0Sstevel@tonic-gate 			ifsp->if_state == REBINDING) &&
387*0Sstevel@tonic-gate 			ifsp->if_addr.s_addr != plp->pkt->yiaddr.s_addr) {
388*0Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "dhcp_acknak: renewal ACK packet "
389*0Sstevel@tonic-gate 				"has a different IP address (%s), ignored",
390*0Sstevel@tonic-gate 				inet_ntoa(plp->pkt->yiaddr));
391*0Sstevel@tonic-gate 			ifsp->if_bad_offers++;
392*0Sstevel@tonic-gate 			free_pkt_list(&plp);
393*0Sstevel@tonic-gate 			return;
394*0Sstevel@tonic-gate 		}
395*0Sstevel@tonic-gate 	}
396*0Sstevel@tonic-gate 
397*0Sstevel@tonic-gate 	/*
398*0Sstevel@tonic-gate 	 * looks good; cancel the retransmission timer and unregister
399*0Sstevel@tonic-gate 	 * the acknak handler. ACK to BOUND, NAK back to SELECTING.
400*0Sstevel@tonic-gate 	 */
401*0Sstevel@tonic-gate 
402*0Sstevel@tonic-gate 	stop_pkt_retransmission(ifsp);
403*0Sstevel@tonic-gate 	(void) unregister_acknak(ifsp);
404*0Sstevel@tonic-gate 
405*0Sstevel@tonic-gate 	if (*(plp->opts[CD_DHCP_TYPE]->value) == NAK) {
406*0Sstevel@tonic-gate 		dhcpmsg(MSG_WARNING, "dhcp_acknak: NAK on interface %s",
407*0Sstevel@tonic-gate 		    ifsp->if_name);
408*0Sstevel@tonic-gate 		ifsp->if_bad_offers++;
409*0Sstevel@tonic-gate 		free_pkt_list(&plp);
410*0Sstevel@tonic-gate 		restart_dhcp(ifsp);
411*0Sstevel@tonic-gate 
412*0Sstevel@tonic-gate 		/*
413*0Sstevel@tonic-gate 		 * remove any bogus cached configuration we might have
414*0Sstevel@tonic-gate 		 * around (right now would only happen if we got here
415*0Sstevel@tonic-gate 		 * from INIT_REBOOT).
416*0Sstevel@tonic-gate 		 */
417*0Sstevel@tonic-gate 
418*0Sstevel@tonic-gate 		(void) remove_hostconf(ifsp->if_name);
419*0Sstevel@tonic-gate 		return;
420*0Sstevel@tonic-gate 	}
421*0Sstevel@tonic-gate 
422*0Sstevel@tonic-gate 	if (plp->opts[CD_SERVER_ID] == NULL ||
423*0Sstevel@tonic-gate 	    plp->opts[CD_SERVER_ID]->len != sizeof (ipaddr_t)) {
424*0Sstevel@tonic-gate 		dhcpmsg(MSG_ERROR, "dhcp_acknak: ACK with no valid server id, "
425*0Sstevel@tonic-gate 		    "restarting DHCP on %s", ifsp->if_name);
426*0Sstevel@tonic-gate 		ifsp->if_bad_offers++;
427*0Sstevel@tonic-gate 		free_pkt_list(&plp);
428*0Sstevel@tonic-gate 		restart_dhcp(ifsp);
429*0Sstevel@tonic-gate 		return;
430*0Sstevel@tonic-gate 	}
431*0Sstevel@tonic-gate 
432*0Sstevel@tonic-gate 	if (plp->opts[CD_MESSAGE] != NULL)
433*0Sstevel@tonic-gate 		print_server_msg(ifsp, plp->opts[CD_MESSAGE]);
434*0Sstevel@tonic-gate 
435*0Sstevel@tonic-gate 	if (dhcp_bound(ifsp, plp) == 0) {
436*0Sstevel@tonic-gate 		dhcpmsg(MSG_WARNING, "dhcp_acknak: dhcp_bound failed "
437*0Sstevel@tonic-gate 		    "for %s", ifsp->if_name);
438*0Sstevel@tonic-gate 		restart_dhcp(ifsp);
439*0Sstevel@tonic-gate 		return;
440*0Sstevel@tonic-gate 	}
441*0Sstevel@tonic-gate 
442*0Sstevel@tonic-gate 	dhcpmsg(MSG_VERBOSE, "ACK on interface %s", ifsp->if_name);
443*0Sstevel@tonic-gate }
444*0Sstevel@tonic-gate 
445*0Sstevel@tonic-gate /*
446*0Sstevel@tonic-gate  * restart_dhcp(): restarts DHCP (from INIT) on a given interface
447*0Sstevel@tonic-gate  *
448*0Sstevel@tonic-gate  *   input: struct ifslist *: the interface to restart DHCP on
449*0Sstevel@tonic-gate  *  output: void
450*0Sstevel@tonic-gate  */
451*0Sstevel@tonic-gate 
452*0Sstevel@tonic-gate static void
453*0Sstevel@tonic-gate restart_dhcp(struct ifslist *ifsp)
454*0Sstevel@tonic-gate {
455*0Sstevel@tonic-gate 	if (iu_schedule_timer(tq, DHCP_RESTART_WAIT, dhcp_start, ifsp) == -1) {
456*0Sstevel@tonic-gate 
457*0Sstevel@tonic-gate 		ifsp->if_state	 = INIT;
458*0Sstevel@tonic-gate 		ifsp->if_dflags |= DHCP_IF_FAILED;
459*0Sstevel@tonic-gate 
460*0Sstevel@tonic-gate 		ipc_action_finish(ifsp, DHCP_IPC_E_MEMORY);
461*0Sstevel@tonic-gate 		async_finish(ifsp);
462*0Sstevel@tonic-gate 
463*0Sstevel@tonic-gate 		dhcpmsg(MSG_ERROR, "restart_dhcp: cannot schedule dhcp_start, "
464*0Sstevel@tonic-gate 		    "reverting to INIT state on %s", ifsp->if_name);
465*0Sstevel@tonic-gate 	} else
466*0Sstevel@tonic-gate 		hold_ifs(ifsp);
467*0Sstevel@tonic-gate }
468*0Sstevel@tonic-gate 
469*0Sstevel@tonic-gate /*
470*0Sstevel@tonic-gate  * stop_requesting(): decides when to stop retransmitting REQUESTs
471*0Sstevel@tonic-gate  *
472*0Sstevel@tonic-gate  *   input: struct ifslist *: the interface REQUESTs are being sent on
473*0Sstevel@tonic-gate  *	    unsigned int: the number of REQUESTs sent so far
474*0Sstevel@tonic-gate  *  output: boolean_t: B_TRUE if retransmissions should stop
475*0Sstevel@tonic-gate  */
476*0Sstevel@tonic-gate 
477*0Sstevel@tonic-gate static boolean_t
478*0Sstevel@tonic-gate stop_requesting(struct ifslist *ifsp, unsigned int n_requests)
479*0Sstevel@tonic-gate {
480*0Sstevel@tonic-gate 	if (n_requests >= DHCP_MAX_REQUESTS) {
481*0Sstevel@tonic-gate 
482*0Sstevel@tonic-gate 		(void) unregister_acknak(ifsp);
483*0Sstevel@tonic-gate 
484*0Sstevel@tonic-gate 		dhcpmsg(MSG_INFO, "no ACK/NAK to REQUESTING REQUEST, "
485*0Sstevel@tonic-gate 		    "restarting DHCP on %s", ifsp->if_name);
486*0Sstevel@tonic-gate 
487*0Sstevel@tonic-gate 		dhcp_selecting(ifsp);
488*0Sstevel@tonic-gate 		return (B_TRUE);
489*0Sstevel@tonic-gate 	}
490*0Sstevel@tonic-gate 
491*0Sstevel@tonic-gate 	return (B_FALSE);
492*0Sstevel@tonic-gate }
493