10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
52157Sdh155122  * Common Development and Distribution License (the "License").
62157Sdh155122  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*3431Scarlsonj  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  *
250Sstevel@tonic-gate  * REQUESTING state of the client state machine.
260Sstevel@tonic-gate  */
270Sstevel@tonic-gate 
280Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
290Sstevel@tonic-gate 
30*3431Scarlsonj #include <stdlib.h>
31*3431Scarlsonj #include <string.h>
32*3431Scarlsonj #include <search.h>
330Sstevel@tonic-gate #include <sys/types.h>
340Sstevel@tonic-gate #include <netinet/in.h>
350Sstevel@tonic-gate #include <netinet/dhcp.h>
360Sstevel@tonic-gate #include <netinet/udp.h>
370Sstevel@tonic-gate #include <netinet/ip_var.h>
380Sstevel@tonic-gate #include <netinet/udp_var.h>
39*3431Scarlsonj #include <arpa/inet.h>
400Sstevel@tonic-gate #include <dhcp_hostconf.h>
41*3431Scarlsonj #include <dhcpagent_util.h>
420Sstevel@tonic-gate #include <dhcpmsg.h>
430Sstevel@tonic-gate 
440Sstevel@tonic-gate #include "states.h"
450Sstevel@tonic-gate #include "util.h"
460Sstevel@tonic-gate #include "packet.h"
470Sstevel@tonic-gate #include "interface.h"
480Sstevel@tonic-gate #include "agent.h"
490Sstevel@tonic-gate 
50*3431Scarlsonj static PKT_LIST		*select_best(dhcp_smach_t *);
51*3431Scarlsonj static void		request_failed(dhcp_smach_t *);
520Sstevel@tonic-gate static stop_func_t	stop_requesting;
530Sstevel@tonic-gate 
540Sstevel@tonic-gate /*
55*3431Scarlsonj  * send_v6_request(): sends a DHCPv6 Request message and switches to REQUESTING
56*3431Scarlsonj  *		      state.  This is a separate function because a NoBinding
57*3431Scarlsonj  *		      response can also cause us to do this.
58*3431Scarlsonj  *
59*3431Scarlsonj  *   input: dhcp_smach_t *: the state machine
60*3431Scarlsonj  *  output: none
61*3431Scarlsonj  */
62*3431Scarlsonj 
63*3431Scarlsonj void
64*3431Scarlsonj send_v6_request(dhcp_smach_t *dsmp)
65*3431Scarlsonj {
66*3431Scarlsonj 	dhcp_pkt_t *dpkt;
67*3431Scarlsonj 	dhcpv6_ia_na_t d6in;
68*3431Scarlsonj 
69*3431Scarlsonj 	dpkt = init_pkt(dsmp, DHCPV6_MSG_REQUEST);
70*3431Scarlsonj 	(void) add_pkt_opt(dpkt, DHCPV6_OPT_SERVERID, dsmp->dsm_serverid,
71*3431Scarlsonj 	    dsmp->dsm_serveridlen);
72*3431Scarlsonj 
73*3431Scarlsonj 	/* Add an IA_NA option for our controlling LIF */
74*3431Scarlsonj 	d6in.d6in_iaid = htonl(dsmp->dsm_lif->lif_iaid);
75*3431Scarlsonj 	d6in.d6in_t1 = htonl(0);
76*3431Scarlsonj 	d6in.d6in_t2 = htonl(0);
77*3431Scarlsonj 	(void) add_pkt_opt(dpkt, DHCPV6_OPT_IA_NA,
78*3431Scarlsonj 	    (dhcpv6_option_t *)&d6in + 1,
79*3431Scarlsonj 	    sizeof (d6in) - sizeof (dhcpv6_option_t));
80*3431Scarlsonj 
81*3431Scarlsonj 	/* Add required Option Request option */
82*3431Scarlsonj 	(void) add_pkt_prl(dpkt, dsmp);
83*3431Scarlsonj 
84*3431Scarlsonj 	(void) send_pkt_v6(dsmp, dpkt, dsmp->dsm_server, stop_requesting,
85*3431Scarlsonj 	    DHCPV6_REQ_TIMEOUT, DHCPV6_REQ_MAX_RT);
86*3431Scarlsonj 
87*3431Scarlsonj 	/* For DHCPv6, state switch cannot fail */
88*3431Scarlsonj 	(void) set_smach_state(dsmp, REQUESTING);
89*3431Scarlsonj }
90*3431Scarlsonj 
91*3431Scarlsonj /*
92*3431Scarlsonj  * server_unicast_option(): determines the server address to use based on the
93*3431Scarlsonj  *			    DHCPv6 Server Unicast option present in the given
94*3431Scarlsonj  *			    packet.
95*3431Scarlsonj  *
96*3431Scarlsonj  *   input: dhcp_smach_t *: the state machine
97*3431Scarlsonj  *	    PKT_LIST *: received packet (Advertisement or Reply)
98*3431Scarlsonj  *  output: none
99*3431Scarlsonj  */
100*3431Scarlsonj 
101*3431Scarlsonj void
102*3431Scarlsonj server_unicast_option(dhcp_smach_t *dsmp, PKT_LIST *plp)
103*3431Scarlsonj {
104*3431Scarlsonj 	const dhcpv6_option_t *d6o;
105*3431Scarlsonj 	uint_t olen;
106*3431Scarlsonj 
107*3431Scarlsonj 	d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_UNICAST, &olen);
108*3431Scarlsonj 	olen -= sizeof (*d6o);
109*3431Scarlsonj 	/* LINTED: no consequent */
110*3431Scarlsonj 	if (d6o == NULL) {
111*3431Scarlsonj 		/* No Server Unicast option specified */
112*3431Scarlsonj 	} else if (olen != sizeof (dsmp->dsm_server)) {
113*3431Scarlsonj 		dhcpmsg(MSG_WARNING, "server_unicast_option: %s has Server "
114*3431Scarlsonj 		    "Unicast option with bad length",
115*3431Scarlsonj 		    pkt_type_to_string(pkt_recv_type(plp), B_TRUE));
116*3431Scarlsonj 	} else {
117*3431Scarlsonj 		in6_addr_t addr;
118*3431Scarlsonj 
119*3431Scarlsonj 		(void) memcpy(&addr, d6o + 1, olen);
120*3431Scarlsonj 		if (IN6_IS_ADDR_UNSPECIFIED(&addr)) {
121*3431Scarlsonj 			dhcpmsg(MSG_WARNING, "server_unicast_option: unicast "
122*3431Scarlsonj 			    "to unspecified address ignored");
123*3431Scarlsonj 		} else if (IN6_IS_ADDR_MULTICAST(&addr)) {
124*3431Scarlsonj 			dhcpmsg(MSG_WARNING, "server_unicast_option: unicast "
125*3431Scarlsonj 			    "to multicast address ignored");
126*3431Scarlsonj 		} else if (IN6_IS_ADDR_V4COMPAT(&addr) ||
127*3431Scarlsonj 		    IN6_IS_ADDR_V4MAPPED(&addr)) {
128*3431Scarlsonj 			dhcpmsg(MSG_WARNING, "server_unicast_option: unicast "
129*3431Scarlsonj 			    "to invalid address ignored");
130*3431Scarlsonj 		} else {
131*3431Scarlsonj 			dsmp->dsm_server = addr;
132*3431Scarlsonj 		}
133*3431Scarlsonj 	}
134*3431Scarlsonj }
135*3431Scarlsonj 
136*3431Scarlsonj /*
1370Sstevel@tonic-gate  * dhcp_requesting(): checks if OFFER packets to come in from DHCP servers.
1380Sstevel@tonic-gate  *		      if so, chooses the best one, sends a REQUEST to the
1390Sstevel@tonic-gate  *		      server and registers an event handler to receive
140*3431Scarlsonj  *		      the ACK/NAK.  This may be called by the offer timer or
141*3431Scarlsonj  *		      by any function that wants to check for offers after
142*3431Scarlsonj  *		      canceling that timer.
1430Sstevel@tonic-gate  *
144*3431Scarlsonj  *   input: iu_tq_t *: timer queue; non-NULL if this is a timer callback
145*3431Scarlsonj  *	    void *: the state machine receiving OFFER packets
1460Sstevel@tonic-gate  *  output: void
1470Sstevel@tonic-gate  */
1480Sstevel@tonic-gate 
1490Sstevel@tonic-gate void
1500Sstevel@tonic-gate dhcp_requesting(iu_tq_t *tqp, void *arg)
1510Sstevel@tonic-gate {
152*3431Scarlsonj 	dhcp_smach_t		*dsmp = arg;
1530Sstevel@tonic-gate 	dhcp_pkt_t		*dpkt;
1540Sstevel@tonic-gate 	PKT_LIST		*offer;
1550Sstevel@tonic-gate 	lease_t			lease;
156*3431Scarlsonj 	boolean_t		isv6 = dsmp->dsm_isv6;
1572157Sdh155122 
158*3431Scarlsonj 	/*
159*3431Scarlsonj 	 * We assume here that if tqp is set, then this means we're being
160*3431Scarlsonj 	 * called back by the offer wait timer.  If so, then drop our hold
161*3431Scarlsonj 	 * on the state machine.  Otherwise, cancel the timer if it's running.
162*3431Scarlsonj 	 */
163*3431Scarlsonj 	if (tqp != NULL) {
164*3431Scarlsonj 		dhcpmsg(MSG_VERBOSE,
165*3431Scarlsonj 		    "dhcp_requesting: offer wait timer on v%d %s",
166*3431Scarlsonj 		    isv6 ? 6 : 4, dsmp->dsm_name);
167*3431Scarlsonj 		dsmp->dsm_offer_timer = -1;
168*3431Scarlsonj 		if (!verify_smach(dsmp))
169*3431Scarlsonj 			return;
170*3431Scarlsonj 	} else {
171*3431Scarlsonj 		cancel_offer_timer(dsmp);
1720Sstevel@tonic-gate 	}
1730Sstevel@tonic-gate 
1740Sstevel@tonic-gate 	/*
1750Sstevel@tonic-gate 	 * select the best OFFER; all others pitched.
1760Sstevel@tonic-gate 	 */
1770Sstevel@tonic-gate 
178*3431Scarlsonj 	offer = select_best(dsmp);
1790Sstevel@tonic-gate 	if (offer == NULL) {
1800Sstevel@tonic-gate 
181*3431Scarlsonj 		dhcpmsg(MSG_VERBOSE,
182*3431Scarlsonj 		    "no OFFERs/Advertisements on %s, waiting...",
183*3431Scarlsonj 		    dsmp->dsm_name);
1840Sstevel@tonic-gate 
1850Sstevel@tonic-gate 		/*
1860Sstevel@tonic-gate 		 * no acceptable OFFERs have come in.  reschedule
187*3431Scarlsonj 		 * ourself for callback.
1880Sstevel@tonic-gate 		 */
1890Sstevel@tonic-gate 
190*3431Scarlsonj 		if ((dsmp->dsm_offer_timer = iu_schedule_timer(tq,
191*3431Scarlsonj 		    dsmp->dsm_offer_wait, dhcp_requesting, dsmp)) == -1) {
1920Sstevel@tonic-gate 
1930Sstevel@tonic-gate 			/*
1940Sstevel@tonic-gate 			 * ugh.  the best we can do at this point is
1950Sstevel@tonic-gate 			 * revert back to INIT and wait for a user to
1960Sstevel@tonic-gate 			 * restart us.
1970Sstevel@tonic-gate 			 */
1980Sstevel@tonic-gate 
1990Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "dhcp_requesting: cannot "
2000Sstevel@tonic-gate 			    "reschedule callback, reverting to INIT state on "
201*3431Scarlsonj 			    "%s", dsmp->dsm_name);
2020Sstevel@tonic-gate 
203*3431Scarlsonj 			stop_pkt_retransmission(dsmp);
204*3431Scarlsonj 			(void) set_smach_state(dsmp, INIT);
205*3431Scarlsonj 			dsmp->dsm_dflags |= DHCP_IF_FAILED;
206*3431Scarlsonj 			ipc_action_finish(dsmp, DHCP_IPC_E_MEMORY);
207*3431Scarlsonj 		} else {
208*3431Scarlsonj 			hold_smach(dsmp);
2090Sstevel@tonic-gate 		}
2100Sstevel@tonic-gate 
2110Sstevel@tonic-gate 		return;
2120Sstevel@tonic-gate 	}
2130Sstevel@tonic-gate 
2140Sstevel@tonic-gate 	/*
215*3431Scarlsonj 	 * With IPv4, the DHCPREQUEST packet we're about to transmit implicitly
216*3431Scarlsonj 	 * declines all other offers we've received.  We can no longer use any
217*3431Scarlsonj 	 * cached offers, so we must discard them now.  With DHCPv6, though,
218*3431Scarlsonj 	 * we're permitted to hang onto the advertisements (offers) and try
219*3431Scarlsonj 	 * them if the preferred one doesn't pan out.
2200Sstevel@tonic-gate 	 */
221*3431Scarlsonj 	if (!isv6)
222*3431Scarlsonj 		free_pkt_list(&dsmp->dsm_recv_pkt_list);
2230Sstevel@tonic-gate 
224*3431Scarlsonj 	/* stop collecting packets. */
2250Sstevel@tonic-gate 
226*3431Scarlsonj 	stop_pkt_retransmission(dsmp);
2270Sstevel@tonic-gate 
2280Sstevel@tonic-gate 	/*
229*3431Scarlsonj 	 * For IPv4, check to see whether we got an OFFER or a BOOTP packet.
230*3431Scarlsonj 	 * If we got a BOOTP packet, go to the BOUND state now.
2310Sstevel@tonic-gate 	 */
232*3431Scarlsonj 	if (!isv6 && offer->opts[CD_DHCP_TYPE] == NULL) {
233*3431Scarlsonj 		free_pkt_list(&dsmp->dsm_recv_pkt_list);
234*3431Scarlsonj 
235*3431Scarlsonj 		if (!set_smach_state(dsmp, REQUESTING)) {
236*3431Scarlsonj 			dhcp_restart(dsmp);
237*3431Scarlsonj 			return;
238*3431Scarlsonj 		}
239*3431Scarlsonj 
240*3431Scarlsonj 		if (!dhcp_bound(dsmp, offer)) {
241*3431Scarlsonj 			dhcpmsg(MSG_WARNING, "dhcp_requesting: dhcp_bound "
242*3431Scarlsonj 			    "failed for %s", dsmp->dsm_name);
243*3431Scarlsonj 			dhcp_restart(dsmp);
244*3431Scarlsonj 			return;
245*3431Scarlsonj 		}
246*3431Scarlsonj 
247*3431Scarlsonj 		return;
2480Sstevel@tonic-gate 	}
249*3431Scarlsonj 
250*3431Scarlsonj 	if (isv6) {
251*3431Scarlsonj 		const char *estr, *msg;
252*3431Scarlsonj 		const dhcpv6_option_t *d6o;
253*3431Scarlsonj 		uint_t olen, msglen;
254*3431Scarlsonj 
255*3431Scarlsonj 		/* If there's a Status Code option, print the message */
256*3431Scarlsonj 		d6o = dhcpv6_pkt_option(offer, NULL, DHCPV6_OPT_STATUS_CODE,
257*3431Scarlsonj 		    &olen);
258*3431Scarlsonj 		(void) dhcpv6_status_code(d6o, olen, &estr, &msg, &msglen);
259*3431Scarlsonj 		print_server_msg(dsmp, msg, msglen);
260*3431Scarlsonj 
261*3431Scarlsonj 		/* Copy in the Server ID (guaranteed to be present now) */
262*3431Scarlsonj 		if (!save_server_id(dsmp, offer))
263*3431Scarlsonj 			goto failure;
264*3431Scarlsonj 
265*3431Scarlsonj 		/*
266*3431Scarlsonj 		 * Determine how to send this message.  If the Advertisement
267*3431Scarlsonj 		 * (offer) has the unicast option, then use the address
268*3431Scarlsonj 		 * specified in the option.  Otherwise, send via multicast.
269*3431Scarlsonj 		 */
270*3431Scarlsonj 		server_unicast_option(dsmp, offer);
271*3431Scarlsonj 
272*3431Scarlsonj 		send_v6_request(dsmp);
273*3431Scarlsonj 	} else {
274*3431Scarlsonj 		/* if we got a message from the server, display it. */
275*3431Scarlsonj 		if (offer->opts[CD_MESSAGE] != NULL) {
276*3431Scarlsonj 			print_server_msg(dsmp,
277*3431Scarlsonj 			    (char *)offer->opts[CD_MESSAGE]->value,
278*3431Scarlsonj 			    offer->opts[CD_MESSAGE]->len);
279*3431Scarlsonj 		}
280*3431Scarlsonj 
281*3431Scarlsonj 		/*
282*3431Scarlsonj 		 * assemble a DHCPREQUEST, with the ciaddr field set to 0,
283*3431Scarlsonj 		 * since we got here from the INIT state.
284*3431Scarlsonj 		 */
285*3431Scarlsonj 
286*3431Scarlsonj 		dpkt = init_pkt(dsmp, REQUEST);
287*3431Scarlsonj 
288*3431Scarlsonj 		/*
289*3431Scarlsonj 		 * Grab the lease out of the OFFER; we know it's valid because
290*3431Scarlsonj 		 * select_best() already checked.  The max dhcp message size
291*3431Scarlsonj 		 * option is set to the interface max, minus the size of the
292*3431Scarlsonj 		 * udp and ip headers.
293*3431Scarlsonj 		 */
294*3431Scarlsonj 
295*3431Scarlsonj 		(void) memcpy(&lease, offer->opts[CD_LEASE_TIME]->value,
296*3431Scarlsonj 		    sizeof (lease_t));
297*3431Scarlsonj 
298*3431Scarlsonj 		(void) add_pkt_opt32(dpkt, CD_LEASE_TIME, lease);
299*3431Scarlsonj 		(void) add_pkt_opt16(dpkt, CD_MAX_DHCP_SIZE,
300*3431Scarlsonj 		    htons(dsmp->dsm_lif->lif_max - sizeof (struct udpiphdr)));
301*3431Scarlsonj 		(void) add_pkt_opt32(dpkt, CD_REQUESTED_IP_ADDR,
302*3431Scarlsonj 		    offer->pkt->yiaddr.s_addr);
303*3431Scarlsonj 		(void) add_pkt_opt(dpkt, CD_SERVER_ID,
304*3431Scarlsonj 		    offer->opts[CD_SERVER_ID]->value,
305*3431Scarlsonj 		    offer->opts[CD_SERVER_ID]->len);
306*3431Scarlsonj 
307*3431Scarlsonj 		(void) add_pkt_opt(dpkt, CD_CLASS_ID, class_id, class_id_len);
308*3431Scarlsonj 		(void) add_pkt_prl(dpkt, dsmp);
309*3431Scarlsonj 
310*3431Scarlsonj 		/*
311*3431Scarlsonj 		 * dsm_reqhost was set for this state machine in
312*3431Scarlsonj 		 * dhcp_selecting() if the DF_REQUEST_HOSTNAME option set and a
313*3431Scarlsonj 		 * host name was found
314*3431Scarlsonj 		 */
315*3431Scarlsonj 		if (dsmp->dsm_reqhost != NULL) {
316*3431Scarlsonj 			(void) add_pkt_opt(dpkt, CD_HOSTNAME, dsmp->dsm_reqhost,
317*3431Scarlsonj 			    strlen(dsmp->dsm_reqhost));
318*3431Scarlsonj 		}
319*3431Scarlsonj 		(void) add_pkt_opt(dpkt, CD_END, NULL, 0);
320*3431Scarlsonj 
321*3431Scarlsonj 		/*
322*3431Scarlsonj 		 * send out the REQUEST, trying retransmissions.  either a NAK
323*3431Scarlsonj 		 * or too many REQUEST attempts will revert us to SELECTING.
324*3431Scarlsonj 		 */
325*3431Scarlsonj 
326*3431Scarlsonj 		if (!set_smach_state(dsmp, REQUESTING)) {
327*3431Scarlsonj 			dhcpmsg(MSG_ERROR, "dhcp_requesting: cannot switch to "
328*3431Scarlsonj 			    "REQUESTING state; reverting to INIT on %s",
329*3431Scarlsonj 			    dsmp->dsm_name);
330*3431Scarlsonj 			goto failure;
331*3431Scarlsonj 		}
332*3431Scarlsonj 
333*3431Scarlsonj 		(void) send_pkt(dsmp, dpkt, htonl(INADDR_BROADCAST),
334*3431Scarlsonj 		    stop_requesting);
335*3431Scarlsonj 	}
3360Sstevel@tonic-gate 
3370Sstevel@tonic-gate 	/* all done with the offer */
338*3431Scarlsonj 	free_pkt_entry(offer);
3390Sstevel@tonic-gate 
340*3431Scarlsonj 	return;
3410Sstevel@tonic-gate 
342*3431Scarlsonj failure:
343*3431Scarlsonj 	dsmp->dsm_dflags |= DHCP_IF_FAILED;
344*3431Scarlsonj 	(void) set_smach_state(dsmp, INIT);
345*3431Scarlsonj 	ipc_action_finish(dsmp, DHCP_IPC_E_MEMORY);
346*3431Scarlsonj 	free_pkt_list(&dsmp->dsm_recv_pkt_list);
3470Sstevel@tonic-gate }
3480Sstevel@tonic-gate 
3490Sstevel@tonic-gate /*
350*3431Scarlsonj  * compute_points_v6(): compute the number of "points" for a given v6
351*3431Scarlsonj  *			advertisement.
3520Sstevel@tonic-gate  *
353*3431Scarlsonj  *   input: const PKT_LIST *: packet to inspect
354*3431Scarlsonj  *	    const dhcp_smach_t *: state machine that received the packet
355*3431Scarlsonj  *  output: int: -1 to discard, -2 to accept immediately, >=0 for preference.
356*3431Scarlsonj  */
357*3431Scarlsonj 
358*3431Scarlsonj static int
359*3431Scarlsonj compute_points_v6(const PKT_LIST *pkt, const dhcp_smach_t *dsmp)
360*3431Scarlsonj {
361*3431Scarlsonj 	char abuf[INET6_ADDRSTRLEN];
362*3431Scarlsonj 	int points = 0;
363*3431Scarlsonj 	const dhcpv6_option_t *d6o, *d6so;
364*3431Scarlsonj 	uint_t olen, solen;
365*3431Scarlsonj 	int i;
366*3431Scarlsonj 	const char *estr, *msg;
367*3431Scarlsonj 	uint_t msglen;
368*3431Scarlsonj 
369*3431Scarlsonj 	/*
370*3431Scarlsonj 	 * Look through the packet contents.  Valid packets must have our
371*3431Scarlsonj 	 * client ID and a server ID, which has already been checked by
372*3431Scarlsonj 	 * dhcp_acknak_lif.  Bonus points for each option.
373*3431Scarlsonj 	 */
374*3431Scarlsonj 
375*3431Scarlsonj 	/* One point for having a valid message. */
376*3431Scarlsonj 	points++;
377*3431Scarlsonj 
378*3431Scarlsonj 	/*
379*3431Scarlsonj 	 * Per RFC 3315, if the Advertise message says, "yes, we have no
380*3431Scarlsonj 	 * bananas today," then ignore the entire message.  (Why it's just
381*3431Scarlsonj 	 * _this_ error and no other is a bit of a mystery, but a standard is a
382*3431Scarlsonj 	 * standard.)
383*3431Scarlsonj 	 */
384*3431Scarlsonj 	d6o = dhcpv6_pkt_option(pkt, NULL, DHCPV6_OPT_STATUS_CODE, &olen);
385*3431Scarlsonj 	if (dhcpv6_status_code(d6o, olen, &estr, &msg, &msglen) ==
386*3431Scarlsonj 	    DHCPV6_STAT_NOADDRS) {
387*3431Scarlsonj 		dhcpmsg(MSG_INFO,
388*3431Scarlsonj 		    "discard advertisement from %s on %s: no address status",
389*3431Scarlsonj 		    inet_ntop(AF_INET6,
390*3431Scarlsonj 		    &((struct sockaddr_in6 *)&pkt->pktfrom)->sin6_addr,
391*3431Scarlsonj 		    abuf, sizeof (abuf)), dsmp->dsm_name);
392*3431Scarlsonj 		return (-1);
393*3431Scarlsonj 	}
394*3431Scarlsonj 
395*3431Scarlsonj 	/* Two points for each batch of offered IP addresses */
396*3431Scarlsonj 	d6o = NULL;
397*3431Scarlsonj 	while ((d6o = dhcpv6_pkt_option(pkt, d6o, DHCPV6_OPT_IA_NA,
398*3431Scarlsonj 	    &olen)) != NULL) {
399*3431Scarlsonj 
400*3431Scarlsonj 		/*
401*3431Scarlsonj 		 * Note that it's possible to have "no bananas" on an
402*3431Scarlsonj 		 * individual IA.  We must look for that here.
403*3431Scarlsonj 		 *
404*3431Scarlsonj 		 * RFC 3315 section 17.1.3 does not refer to the status code
405*3431Scarlsonj 		 * embedded in the IA itself.  However, the TAHI test suite
406*3431Scarlsonj 		 * checks for this specific case.  Because it's extremely
407*3431Scarlsonj 		 * unlikely that any usable server is going to report that it
408*3431Scarlsonj 		 * has no addresses on a network using DHCP for address
409*3431Scarlsonj 		 * assignment, we allow such messages to be dropped.
410*3431Scarlsonj 		 */
411*3431Scarlsonj 		d6so = dhcpv6_find_option(
412*3431Scarlsonj 		    (const char *)d6o + sizeof (dhcpv6_ia_na_t),
413*3431Scarlsonj 		    olen - sizeof (dhcpv6_ia_na_t), NULL,
414*3431Scarlsonj 		    DHCPV6_OPT_STATUS_CODE, &solen);
415*3431Scarlsonj 		if (dhcpv6_status_code(d6so, solen, &estr, &msg, &msglen) ==
416*3431Scarlsonj 		    DHCPV6_STAT_NOADDRS)
417*3431Scarlsonj 			return (-1);
418*3431Scarlsonj 		points += 2;
419*3431Scarlsonj 	}
420*3431Scarlsonj 
421*3431Scarlsonj 	/*
422*3431Scarlsonj 	 * Note that we drive on in the case where there are no addresses.  The
423*3431Scarlsonj 	 * hope here is that we'll at least get some useful configuration
424*3431Scarlsonj 	 * information.
425*3431Scarlsonj 	 */
426*3431Scarlsonj 
427*3431Scarlsonj 	/* One point for each requested option */
428*3431Scarlsonj 	for (i = 0; i < dsmp->dsm_prllen; i++) {
429*3431Scarlsonj 		if (dhcpv6_pkt_option(pkt, NULL, dsmp->dsm_prl[i], NULL) !=
430*3431Scarlsonj 		    NULL)
431*3431Scarlsonj 			points++;
432*3431Scarlsonj 	}
433*3431Scarlsonj 
434*3431Scarlsonj 	/*
435*3431Scarlsonj 	 * Ten points for each point of "preference."  Note: the value 255 is
436*3431Scarlsonj 	 * special.  It means "stop right now and select this server."
437*3431Scarlsonj 	 */
438*3431Scarlsonj 	d6o = dhcpv6_pkt_option(pkt, NULL, DHCPV6_OPT_PREFERENCE, &olen);
439*3431Scarlsonj 	if (d6o != NULL && olen == sizeof (*d6o) + 1) {
440*3431Scarlsonj 		int pref = *(const uchar_t *)(d6o + 1);
441*3431Scarlsonj 
442*3431Scarlsonj 		if (pref == 255)
443*3431Scarlsonj 			return (-2);
444*3431Scarlsonj 		points += 10 * pref;
445*3431Scarlsonj 	}
446*3431Scarlsonj 
447*3431Scarlsonj 	return (points);
448*3431Scarlsonj }
449*3431Scarlsonj 
450*3431Scarlsonj /*
451*3431Scarlsonj  * compute_points_v4(): compute the number of "points" for a given v4 offer.
452*3431Scarlsonj  *
453*3431Scarlsonj  *   input: const PKT_LIST *: packet to inspect
454*3431Scarlsonj  *	    const dhcp_smach_t *: state machine that received the packet
455*3431Scarlsonj  *  output: int: -1 to discard, >=0 for preference.
456*3431Scarlsonj  */
457*3431Scarlsonj 
458*3431Scarlsonj static int
459*3431Scarlsonj compute_points_v4(const PKT_LIST *pkt)
460*3431Scarlsonj {
461*3431Scarlsonj 	int points = 0;
462*3431Scarlsonj 
463*3431Scarlsonj 	if (pkt->opts[CD_DHCP_TYPE] == NULL) {
464*3431Scarlsonj 		dhcpmsg(MSG_VERBOSE, "compute_points_v4: valid BOOTP reply");
465*3431Scarlsonj 		goto valid_offer;
466*3431Scarlsonj 	}
467*3431Scarlsonj 
468*3431Scarlsonj 	if (pkt->opts[CD_LEASE_TIME] == NULL) {
469*3431Scarlsonj 		dhcpmsg(MSG_WARNING, "compute_points_v4: OFFER without lease "
470*3431Scarlsonj 		    "time");
471*3431Scarlsonj 		return (-1);
472*3431Scarlsonj 	}
473*3431Scarlsonj 
474*3431Scarlsonj 	if (pkt->opts[CD_LEASE_TIME]->len != sizeof (lease_t)) {
475*3431Scarlsonj 		dhcpmsg(MSG_WARNING, "compute_points_v4: OFFER with garbled "
476*3431Scarlsonj 		    "lease time");
477*3431Scarlsonj 		return (-1);
478*3431Scarlsonj 	}
479*3431Scarlsonj 
480*3431Scarlsonj 	if (pkt->opts[CD_SERVER_ID] == NULL) {
481*3431Scarlsonj 		dhcpmsg(MSG_WARNING, "compute_points_v4: OFFER without server "
482*3431Scarlsonj 		    "id");
483*3431Scarlsonj 		return (-1);
484*3431Scarlsonj 	}
485*3431Scarlsonj 
486*3431Scarlsonj 	if (pkt->opts[CD_SERVER_ID]->len != sizeof (ipaddr_t)) {
487*3431Scarlsonj 		dhcpmsg(MSG_WARNING, "compute_points_v4: OFFER with garbled "
488*3431Scarlsonj 		    "server id");
489*3431Scarlsonj 		return (-1);
490*3431Scarlsonj 	}
491*3431Scarlsonj 
492*3431Scarlsonj 	/* valid DHCP OFFER.  see if we got our parameters. */
493*3431Scarlsonj 	dhcpmsg(MSG_VERBOSE, "compute_points_v4: valid OFFER packet");
494*3431Scarlsonj 	points += 30;
495*3431Scarlsonj 
496*3431Scarlsonj valid_offer:
497*3431Scarlsonj 	if (pkt->rfc1048)
498*3431Scarlsonj 		points += 5;
499*3431Scarlsonj 
500*3431Scarlsonj 	/*
501*3431Scarlsonj 	 * also could be faked, though more difficult because the encapsulation
502*3431Scarlsonj 	 * is hard to encode on a BOOTP server; plus there's not as much real
503*3431Scarlsonj 	 * estate in the packet for options, so it's likely this option would
504*3431Scarlsonj 	 * get dropped.
505*3431Scarlsonj 	 */
506*3431Scarlsonj 
507*3431Scarlsonj 	if (pkt->opts[CD_VENDOR_SPEC] != NULL)
508*3431Scarlsonj 		points += 80;
509*3431Scarlsonj 
510*3431Scarlsonj 	if (pkt->opts[CD_SUBNETMASK] != NULL)
511*3431Scarlsonj 		points++;
512*3431Scarlsonj 
513*3431Scarlsonj 	if (pkt->opts[CD_ROUTER] != NULL)
514*3431Scarlsonj 		points++;
515*3431Scarlsonj 
516*3431Scarlsonj 	if (pkt->opts[CD_HOSTNAME] != NULL)
517*3431Scarlsonj 		points += 5;
518*3431Scarlsonj 
519*3431Scarlsonj 	return (points);
520*3431Scarlsonj }
521*3431Scarlsonj 
522*3431Scarlsonj /*
523*3431Scarlsonj  * select_best(): selects the best offer from a list of IPv4 OFFER packets or
524*3431Scarlsonj  *		  DHCPv6 Advertise packets.
525*3431Scarlsonj  *
526*3431Scarlsonj  *   input: dhcp_smach_t *: state machine with enqueued offers
5270Sstevel@tonic-gate  *  output: PKT_LIST *: the best packet, or NULL if none are acceptable
5280Sstevel@tonic-gate  */
5290Sstevel@tonic-gate 
5300Sstevel@tonic-gate static PKT_LIST *
531*3431Scarlsonj select_best(dhcp_smach_t *dsmp)
5320Sstevel@tonic-gate {
533*3431Scarlsonj 	PKT_LIST	*current = dsmp->dsm_recv_pkt_list;
534*3431Scarlsonj 	PKT_LIST	*next, *best = NULL;
535*3431Scarlsonj 	int		points, best_points = -1;
5360Sstevel@tonic-gate 
5370Sstevel@tonic-gate 	/*
5380Sstevel@tonic-gate 	 * pick out the best offer.  point system.
539*3431Scarlsonj 	 * what's important for IPv4?
5400Sstevel@tonic-gate 	 *
541*3431Scarlsonj 	 *	0) DHCP (30 points)
5420Sstevel@tonic-gate 	 *	1) no option overload
543*3431Scarlsonj 	 *	2) encapsulated vendor option (80 points)
5440Sstevel@tonic-gate 	 *	3) non-null sname and siaddr fields
5450Sstevel@tonic-gate 	 *	4) non-null file field
546*3431Scarlsonj 	 *	5) hostname (5 points)
547*3431Scarlsonj 	 *	6) subnetmask (1 point)
548*3431Scarlsonj 	 *	7) router (1 point)
5490Sstevel@tonic-gate 	 */
5500Sstevel@tonic-gate 
551*3431Scarlsonj 	for (; current != NULL; current = next) {
552*3431Scarlsonj 		next = current->next;
5530Sstevel@tonic-gate 
554*3431Scarlsonj 		points = current->isv6 ?
555*3431Scarlsonj 		    compute_points_v6(current, dsmp) :
556*3431Scarlsonj 		    compute_points_v4(current);
5570Sstevel@tonic-gate 
558*3431Scarlsonj 		/*
559*3431Scarlsonj 		 * Just discard any unacceptable entries we encounter.
560*3431Scarlsonj 		 */
561*3431Scarlsonj 		if (points == -1) {
562*3431Scarlsonj 			remque(current);
563*3431Scarlsonj 			free_pkt_entry(current);
5640Sstevel@tonic-gate 			continue;
5650Sstevel@tonic-gate 		}
5660Sstevel@tonic-gate 
567*3431Scarlsonj 		dhcpmsg(MSG_DEBUG, "select_best: OFFER had %d points", points);
5680Sstevel@tonic-gate 
569*3431Scarlsonj 		/* Special case: stop now and select */
570*3431Scarlsonj 		if (points == -2) {
571*3431Scarlsonj 			best = current;
572*3431Scarlsonj 			break;
573*3431Scarlsonj 		}
5740Sstevel@tonic-gate 
5750Sstevel@tonic-gate 		if (points >= best_points) {
5760Sstevel@tonic-gate 			best_points = points;
5770Sstevel@tonic-gate 			best = current;
5780Sstevel@tonic-gate 		}
5790Sstevel@tonic-gate 	}
5800Sstevel@tonic-gate 
5810Sstevel@tonic-gate 	if (best != NULL) {
5820Sstevel@tonic-gate 		dhcpmsg(MSG_DEBUG, "select_best: most points: %d", best_points);
583*3431Scarlsonj 		remque(best);
584*3431Scarlsonj 	} else {
5850Sstevel@tonic-gate 		dhcpmsg(MSG_DEBUG, "select_best: no valid OFFER/BOOTP reply");
586*3431Scarlsonj 	}
5870Sstevel@tonic-gate 
5880Sstevel@tonic-gate 	return (best);
5890Sstevel@tonic-gate }
5900Sstevel@tonic-gate 
5910Sstevel@tonic-gate /*
592*3431Scarlsonj  * accept_v4_acknak(): determine what to do with a DHCPv4 ACK/NAK based on the
593*3431Scarlsonj  *		       current state.  If we're renewing or rebinding, the ACK
594*3431Scarlsonj  *		       must be for the same address and must have a new lease
595*3431Scarlsonj  *		       time.  If it's a NAK, then our cache is garbage, and we
596*3431Scarlsonj  *		       must restart.  Finally, call dhcp_bound on accepted
597*3431Scarlsonj  *		       ACKs.
5980Sstevel@tonic-gate  *
599*3431Scarlsonj  *   input: dhcp_smach_t *: the state machine to handle the ACK/NAK
600*3431Scarlsonj  *	    PKT_LIST *: the ACK/NAK message
6010Sstevel@tonic-gate  *  output: void
6020Sstevel@tonic-gate  */
6030Sstevel@tonic-gate 
604*3431Scarlsonj static void
605*3431Scarlsonj accept_v4_acknak(dhcp_smach_t *dsmp, PKT_LIST *plp)
6060Sstevel@tonic-gate {
6070Sstevel@tonic-gate 	if (*plp->opts[CD_DHCP_TYPE]->value == ACK) {
6080Sstevel@tonic-gate 		if (plp->opts[CD_LEASE_TIME] == NULL ||
6090Sstevel@tonic-gate 		    plp->opts[CD_LEASE_TIME]->len != sizeof (lease_t)) {
610*3431Scarlsonj 			dhcpmsg(MSG_WARNING, "accept_v4_acknak: ACK packet on "
611*3431Scarlsonj 			    "%s missing mandatory lease option, ignored",
612*3431Scarlsonj 			    dsmp->dsm_name);
613*3431Scarlsonj 			dsmp->dsm_bad_offers++;
614*3431Scarlsonj 			free_pkt_entry(plp);
6150Sstevel@tonic-gate 			return;
6160Sstevel@tonic-gate 		}
617*3431Scarlsonj 		if ((dsmp->dsm_state == RENEWING ||
618*3431Scarlsonj 		    dsmp->dsm_state == REBINDING) &&
619*3431Scarlsonj 		    dsmp->dsm_leases->dl_lifs->lif_addr !=
620*3431Scarlsonj 		    plp->pkt->yiaddr.s_addr) {
621*3431Scarlsonj 			dhcpmsg(MSG_WARNING, "accept_v4_acknak: renewal ACK "
622*3431Scarlsonj 			    "packet has a different IP address (%s), ignored",
623*3431Scarlsonj 			    inet_ntoa(plp->pkt->yiaddr));
624*3431Scarlsonj 			dsmp->dsm_bad_offers++;
625*3431Scarlsonj 			free_pkt_entry(plp);
6260Sstevel@tonic-gate 			return;
6270Sstevel@tonic-gate 		}
6280Sstevel@tonic-gate 	}
6290Sstevel@tonic-gate 
6300Sstevel@tonic-gate 	/*
6310Sstevel@tonic-gate 	 * looks good; cancel the retransmission timer and unregister
6320Sstevel@tonic-gate 	 * the acknak handler. ACK to BOUND, NAK back to SELECTING.
6330Sstevel@tonic-gate 	 */
6340Sstevel@tonic-gate 
635*3431Scarlsonj 	stop_pkt_retransmission(dsmp);
6360Sstevel@tonic-gate 
637*3431Scarlsonj 	if (*plp->opts[CD_DHCP_TYPE]->value == NAK) {
638*3431Scarlsonj 		dhcpmsg(MSG_WARNING, "accept_v4_acknak: NAK on interface %s",
639*3431Scarlsonj 		    dsmp->dsm_name);
640*3431Scarlsonj 		dsmp->dsm_bad_offers++;
641*3431Scarlsonj 		free_pkt_entry(plp);
642*3431Scarlsonj 		dhcp_restart(dsmp);
6430Sstevel@tonic-gate 
6440Sstevel@tonic-gate 		/*
6450Sstevel@tonic-gate 		 * remove any bogus cached configuration we might have
6460Sstevel@tonic-gate 		 * around (right now would only happen if we got here
6470Sstevel@tonic-gate 		 * from INIT_REBOOT).
6480Sstevel@tonic-gate 		 */
6490Sstevel@tonic-gate 
650*3431Scarlsonj 		(void) remove_hostconf(dsmp->dsm_name, dsmp->dsm_isv6);
6510Sstevel@tonic-gate 		return;
6520Sstevel@tonic-gate 	}
6530Sstevel@tonic-gate 
6540Sstevel@tonic-gate 	if (plp->opts[CD_SERVER_ID] == NULL ||
6550Sstevel@tonic-gate 	    plp->opts[CD_SERVER_ID]->len != sizeof (ipaddr_t)) {
656*3431Scarlsonj 		dhcpmsg(MSG_ERROR, "accept_v4_acknak: ACK with no valid "
657*3431Scarlsonj 		    "server id, restarting DHCP on %s", dsmp->dsm_name);
658*3431Scarlsonj 		dsmp->dsm_bad_offers++;
659*3431Scarlsonj 		free_pkt_entry(plp);
660*3431Scarlsonj 		dhcp_restart(dsmp);
661*3431Scarlsonj 		return;
662*3431Scarlsonj 	}
663*3431Scarlsonj 
664*3431Scarlsonj 	if (plp->opts[CD_MESSAGE] != NULL) {
665*3431Scarlsonj 		print_server_msg(dsmp, (char *)plp->opts[CD_MESSAGE]->value,
666*3431Scarlsonj 		    plp->opts[CD_MESSAGE]->len);
667*3431Scarlsonj 	}
668*3431Scarlsonj 
669*3431Scarlsonj 	dhcpmsg(MSG_VERBOSE, "accept_v4_acknak: ACK on %s", dsmp->dsm_name);
670*3431Scarlsonj 	if (!dhcp_bound(dsmp, plp)) {
671*3431Scarlsonj 		dhcpmsg(MSG_WARNING, "accept_v4_acknak: dhcp_bound failed "
672*3431Scarlsonj 		    "for %s", dsmp->dsm_name);
673*3431Scarlsonj 		dhcp_restart(dsmp);
674*3431Scarlsonj 	}
675*3431Scarlsonj }
676*3431Scarlsonj 
677*3431Scarlsonj /*
678*3431Scarlsonj  * accept_v6_message(): determine what to do with a DHCPv6 message based on the
679*3431Scarlsonj  *			current state.
680*3431Scarlsonj  *
681*3431Scarlsonj  *   input: dhcp_smach_t *: the state machine to handle the message
682*3431Scarlsonj  *	    PKT_LIST *: the DHCPv6 message
683*3431Scarlsonj  *	    const char *: type of message (for logging)
684*3431Scarlsonj  *	    uchar_t: type of message (extracted from packet)
685*3431Scarlsonj  *  output: void
686*3431Scarlsonj  */
687*3431Scarlsonj 
688*3431Scarlsonj static void
689*3431Scarlsonj accept_v6_message(dhcp_smach_t *dsmp, PKT_LIST *plp, const char *pname,
690*3431Scarlsonj     uchar_t recv_type)
691*3431Scarlsonj {
692*3431Scarlsonj 	const dhcpv6_option_t *d6o;
693*3431Scarlsonj 	uint_t olen;
694*3431Scarlsonj 	const char *estr, *msg;
695*3431Scarlsonj 	uint_t msglen;
696*3431Scarlsonj 	int status;
697*3431Scarlsonj 
698*3431Scarlsonj 	/*
699*3431Scarlsonj 	 * All valid DHCPv6 messages must have our Client ID specified.
700*3431Scarlsonj 	 */
701*3431Scarlsonj 	d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_CLIENTID, &olen);
702*3431Scarlsonj 	olen -= sizeof (*d6o);
703*3431Scarlsonj 	if (d6o == NULL || olen != dsmp->dsm_cidlen ||
704*3431Scarlsonj 	    memcmp(d6o + 1, dsmp->dsm_cid, olen) != 0) {
705*3431Scarlsonj 		dhcpmsg(MSG_VERBOSE,
706*3431Scarlsonj 		    "accept_v6_message: discarded %s on %s: %s Client ID",
707*3431Scarlsonj 		    pname, dsmp->dsm_name, d6o == NULL ? "no" : "wrong");
708*3431Scarlsonj 		free_pkt_entry(plp);
7090Sstevel@tonic-gate 		return;
7100Sstevel@tonic-gate 	}
7110Sstevel@tonic-gate 
712*3431Scarlsonj 	/*
713*3431Scarlsonj 	 * All valid DHCPv6 messages must have a Server ID specified.
714*3431Scarlsonj 	 *
715*3431Scarlsonj 	 * If this is a Reply and it's not in response to Solicit, Confirm,
716*3431Scarlsonj 	 * Rebind, or Information-Request, then it must also match the Server
717*3431Scarlsonj 	 * ID we're expecting.
718*3431Scarlsonj 	 *
719*3431Scarlsonj 	 * For Reply in the Solicit, Confirm, Rebind, and Information-Request
720*3431Scarlsonj 	 * cases, the Server ID needs to be saved.  This is done inside of
721*3431Scarlsonj 	 * dhcp_bound().
722*3431Scarlsonj 	 */
723*3431Scarlsonj 	d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_SERVERID, &olen);
724*3431Scarlsonj 	if (d6o == NULL) {
725*3431Scarlsonj 		dhcpmsg(MSG_DEBUG,
726*3431Scarlsonj 		    "accept_v6_message: discarded %s on %s: no Server ID",
727*3431Scarlsonj 		    pname, dsmp->dsm_name);
728*3431Scarlsonj 		free_pkt_entry(plp);
729*3431Scarlsonj 		return;
730*3431Scarlsonj 	}
731*3431Scarlsonj 	if (recv_type == DHCPV6_MSG_REPLY && dsmp->dsm_state != SELECTING &&
732*3431Scarlsonj 	    dsmp->dsm_state != INIT_REBOOT && dsmp->dsm_state != REBINDING &&
733*3431Scarlsonj 	    dsmp->dsm_state != INFORM_SENT) {
734*3431Scarlsonj 		olen -= sizeof (*d6o);
735*3431Scarlsonj 		if (olen != dsmp->dsm_serveridlen ||
736*3431Scarlsonj 		    memcmp(d6o + 1, dsmp->dsm_serverid, olen) != 0) {
737*3431Scarlsonj 			dhcpmsg(MSG_DEBUG, "accept_v6_message: discarded %s on "
738*3431Scarlsonj 			    "%s: wrong Server ID", pname, dsmp->dsm_name);
739*3431Scarlsonj 			free_pkt_entry(plp);
740*3431Scarlsonj 			return;
741*3431Scarlsonj 		}
742*3431Scarlsonj 	}
743*3431Scarlsonj 
744*3431Scarlsonj 	/*
745*3431Scarlsonj 	 * Break out of the switch if the input message needs to be discarded.
746*3431Scarlsonj 	 * Return from the function if the message has been enqueued or
747*3431Scarlsonj 	 * consumed.
748*3431Scarlsonj 	 */
749*3431Scarlsonj 	switch (dsmp->dsm_state) {
750*3431Scarlsonj 	case SELECTING:
751*3431Scarlsonj 		/* A Reply message signifies a Rapid-Commit. */
752*3431Scarlsonj 		if (recv_type == DHCPV6_MSG_REPLY) {
753*3431Scarlsonj 			if (dhcpv6_pkt_option(plp, NULL,
754*3431Scarlsonj 			    DHCPV6_OPT_RAPID_COMMIT, &olen) == NULL) {
755*3431Scarlsonj 				dhcpmsg(MSG_DEBUG, "accept_v6_message: Reply "
756*3431Scarlsonj 				    "on %s lacks Rapid-Commit; ignoring",
757*3431Scarlsonj 				    dsmp->dsm_name);
758*3431Scarlsonj 				break;
759*3431Scarlsonj 			}
760*3431Scarlsonj 			dhcpmsg(MSG_VERBOSE,
761*3431Scarlsonj 			    "accept_v6_message: rapid-commit Reply on %s",
762*3431Scarlsonj 			    dsmp->dsm_name);
763*3431Scarlsonj 			cancel_offer_timer(dsmp);
764*3431Scarlsonj 			goto rapid_commit;
765*3431Scarlsonj 		}
766*3431Scarlsonj 
767*3431Scarlsonj 		/* Otherwise, we're looking for Advertisements. */
768*3431Scarlsonj 		if (recv_type != DHCPV6_MSG_ADVERTISE)
769*3431Scarlsonj 			break;
770*3431Scarlsonj 
771*3431Scarlsonj 		/*
772*3431Scarlsonj 		 * Special case: if this advertisement has preference 255, then
773*3431Scarlsonj 		 * we must stop right now and select this server.
774*3431Scarlsonj 		 */
775*3431Scarlsonj 		d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_PREFERENCE,
776*3431Scarlsonj 		    &olen);
777*3431Scarlsonj 		if (d6o != NULL && olen == sizeof (*d6o) + 1 &&
778*3431Scarlsonj 		    *(const uchar_t *)(d6o + 1) == 255) {
779*3431Scarlsonj 			pkt_smach_enqueue(dsmp, plp);
780*3431Scarlsonj 			dhcpmsg(MSG_DEBUG, "accept_v6_message: preference 255;"
781*3431Scarlsonj 			    " immediate Request on %s", dsmp->dsm_name);
782*3431Scarlsonj 			dhcp_requesting(NULL, dsmp);
783*3431Scarlsonj 		} else {
784*3431Scarlsonj 			pkt_smach_enqueue(dsmp, plp);
785*3431Scarlsonj 		}
786*3431Scarlsonj 		return;
787*3431Scarlsonj 
788*3431Scarlsonj 	case PRE_BOUND:
789*3431Scarlsonj 	case BOUND:
790*3431Scarlsonj 		/*
791*3431Scarlsonj 		 * Not looking for anything in these states.  (If we
792*3431Scarlsonj 		 * implemented reconfigure, that might go here.)
793*3431Scarlsonj 		 */
794*3431Scarlsonj 		break;
7950Sstevel@tonic-gate 
796*3431Scarlsonj 	case REQUESTING:
797*3431Scarlsonj 	case INIT_REBOOT:
798*3431Scarlsonj 	case RENEWING:
799*3431Scarlsonj 	case REBINDING:
800*3431Scarlsonj 	case INFORM_SENT:
801*3431Scarlsonj 		/*
802*3431Scarlsonj 		 * We're looking for Reply messages.
803*3431Scarlsonj 		 */
804*3431Scarlsonj 		if (recv_type != DHCPV6_MSG_REPLY)
805*3431Scarlsonj 			break;
806*3431Scarlsonj 		dhcpmsg(MSG_VERBOSE,
807*3431Scarlsonj 		    "accept_v6_message: received Reply message on %s",
808*3431Scarlsonj 		    dsmp->dsm_name);
809*3431Scarlsonj 	rapid_commit:
810*3431Scarlsonj 		/*
811*3431Scarlsonj 		 * Extract the status code option.  If one is present and the
812*3431Scarlsonj 		 * request failed, then try to go to another advertisement in
813*3431Scarlsonj 		 * the list or restart the selection machinery.
814*3431Scarlsonj 		 */
815*3431Scarlsonj 		d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_STATUS_CODE,
816*3431Scarlsonj 		    &olen);
817*3431Scarlsonj 		status = dhcpv6_status_code(d6o, olen, &estr, &msg, &msglen);
818*3431Scarlsonj 		/*
819*3431Scarlsonj 		 * Check for the UseMulticast status code.  If this is present,
820*3431Scarlsonj 		 * and if we were actually using unicast, then drop back and
821*3431Scarlsonj 		 * try again.  If we weren't using unicast, then just pretend
822*3431Scarlsonj 		 * we never saw this message -- the peer is confused.  (TAHI
823*3431Scarlsonj 		 * does this.)
824*3431Scarlsonj 		 */
825*3431Scarlsonj 		if (status == DHCPV6_STAT_USEMCAST) {
826*3431Scarlsonj 			if (IN6_IS_ADDR_MULTICAST(
827*3431Scarlsonj 			    &dsmp->dsm_send_dest.v6.sin6_addr)) {
828*3431Scarlsonj 				break;
829*3431Scarlsonj 			} else {
830*3431Scarlsonj 				free_pkt_entry(plp);
831*3431Scarlsonj 				dsmp->dsm_send_dest.v6.sin6_addr =
832*3431Scarlsonj 				    ipv6_all_dhcp_relay_and_servers;
833*3431Scarlsonj 				retransmit_now(dsmp);
834*3431Scarlsonj 				return;
835*3431Scarlsonj 			}
836*3431Scarlsonj 		}
837*3431Scarlsonj 		print_server_msg(dsmp, msg, msglen);
838*3431Scarlsonj 		/*
839*3431Scarlsonj 		 * We treat NoBinding at the top level as "success."  Granted,
840*3431Scarlsonj 		 * this doesn't make much sense, but the TAHI test suite does
841*3431Scarlsonj 		 * this.  NoBinding really only makes sense in the context of a
842*3431Scarlsonj 		 * specific IA, as it refers to the GUID:IAID binding, so
843*3431Scarlsonj 		 * ignoring it at the top level is safe.
844*3431Scarlsonj 		 */
845*3431Scarlsonj 		if (status == DHCPV6_STAT_SUCCESS ||
846*3431Scarlsonj 		    status == DHCPV6_STAT_NOBINDING) {
847*3431Scarlsonj 			if (dhcp_bound(dsmp, plp)) {
848*3431Scarlsonj 				/*
849*3431Scarlsonj 				 * dhcp_bound will stop retransmission on
850*3431Scarlsonj 				 * success, if that's called for.
851*3431Scarlsonj 				 */
852*3431Scarlsonj 				server_unicast_option(dsmp, plp);
853*3431Scarlsonj 			} else {
854*3431Scarlsonj 				stop_pkt_retransmission(dsmp);
855*3431Scarlsonj 				dhcpmsg(MSG_WARNING, "accept_v6_message: "
856*3431Scarlsonj 				    "dhcp_bound failed for %s", dsmp->dsm_name);
857*3431Scarlsonj 				(void) remove_hostconf(dsmp->dsm_name,
858*3431Scarlsonj 				    dsmp->dsm_isv6);
859*3431Scarlsonj 				if (dsmp->dsm_state != INFORM_SENT)
860*3431Scarlsonj 					dhcp_restart(dsmp);
861*3431Scarlsonj 			}
862*3431Scarlsonj 		} else {
863*3431Scarlsonj 			dhcpmsg(MSG_WARNING, "accept_v6_message: Reply: %s",
864*3431Scarlsonj 			    estr);
865*3431Scarlsonj 			stop_pkt_retransmission(dsmp);
866*3431Scarlsonj 			free_pkt_entry(plp);
867*3431Scarlsonj 			if (dsmp->dsm_state == INFORM_SENT) {
868*3431Scarlsonj 				(void) set_smach_state(dsmp, INIT);
869*3431Scarlsonj 				ipc_action_finish(dsmp, DHCP_IPC_E_SRVFAILED);
870*3431Scarlsonj 			} else {
871*3431Scarlsonj 				(void) remove_hostconf(dsmp->dsm_name,
872*3431Scarlsonj 				    dsmp->dsm_isv6);
873*3431Scarlsonj 				request_failed(dsmp);
874*3431Scarlsonj 			}
875*3431Scarlsonj 		}
876*3431Scarlsonj 		return;
877*3431Scarlsonj 
878*3431Scarlsonj 	case DECLINING:
879*3431Scarlsonj 		/*
880*3431Scarlsonj 		 * We're looking for Reply messages.
881*3431Scarlsonj 		 */
882*3431Scarlsonj 		if (recv_type != DHCPV6_MSG_REPLY)
883*3431Scarlsonj 			break;
884*3431Scarlsonj 		stop_pkt_retransmission(dsmp);
885*3431Scarlsonj 		/*
886*3431Scarlsonj 		 * Extract the status code option.  Note that it's not a
887*3431Scarlsonj 		 * failure if the server reports an error.
888*3431Scarlsonj 		 */
889*3431Scarlsonj 		d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_STATUS_CODE,
890*3431Scarlsonj 		    &olen);
891*3431Scarlsonj 		if (dhcpv6_status_code(d6o, olen, &estr, &msg,
892*3431Scarlsonj 		    &msglen) == DHCPV6_STAT_SUCCESS) {
893*3431Scarlsonj 			print_server_msg(dsmp, msg, msglen);
894*3431Scarlsonj 		} else {
895*3431Scarlsonj 			dhcpmsg(MSG_WARNING, "accept_v6_message: Reply: %s",
896*3431Scarlsonj 			    estr);
897*3431Scarlsonj 		}
898*3431Scarlsonj 		free_pkt_entry(plp);
899*3431Scarlsonj 		if (dsmp->dsm_leases == NULL) {
900*3431Scarlsonj 			dhcpmsg(MSG_VERBOSE, "accept_v6_message: %s has no "
901*3431Scarlsonj 			    "leases left; restarting", dsmp->dsm_name);
902*3431Scarlsonj 			dhcp_restart(dsmp);
903*3431Scarlsonj 		} else if (dsmp->dsm_lif_wait == 0) {
904*3431Scarlsonj 			(void) set_smach_state(dsmp, BOUND);
905*3431Scarlsonj 		} else {
906*3431Scarlsonj 			(void) set_smach_state(dsmp, PRE_BOUND);
907*3431Scarlsonj 		}
908*3431Scarlsonj 		return;
909*3431Scarlsonj 
910*3431Scarlsonj 	case RELEASING:
911*3431Scarlsonj 		/*
912*3431Scarlsonj 		 * We're looking for Reply messages.
913*3431Scarlsonj 		 */
914*3431Scarlsonj 		if (recv_type != DHCPV6_MSG_REPLY)
915*3431Scarlsonj 			break;
916*3431Scarlsonj 		stop_pkt_retransmission(dsmp);
917*3431Scarlsonj 		/*
918*3431Scarlsonj 		 * Extract the status code option.
919*3431Scarlsonj 		 */
920*3431Scarlsonj 		d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_STATUS_CODE,
921*3431Scarlsonj 		    &olen);
922*3431Scarlsonj 		if (dhcpv6_status_code(d6o, olen, &estr, &msg,
923*3431Scarlsonj 		    &msglen) == DHCPV6_STAT_SUCCESS) {
924*3431Scarlsonj 			print_server_msg(dsmp, msg, msglen);
925*3431Scarlsonj 		} else {
926*3431Scarlsonj 			dhcpmsg(MSG_WARNING, "accept_v6_message: Reply: %s",
927*3431Scarlsonj 			    estr);
928*3431Scarlsonj 		}
929*3431Scarlsonj 		free_pkt_entry(plp);
930*3431Scarlsonj 		finished_smach(dsmp, DHCP_IPC_SUCCESS);
9310Sstevel@tonic-gate 		return;
9320Sstevel@tonic-gate 	}
9330Sstevel@tonic-gate 
934*3431Scarlsonj 	/*
935*3431Scarlsonj 	 * Break from above switch means that the message must be discarded.
936*3431Scarlsonj 	 */
937*3431Scarlsonj 	dhcpmsg(MSG_VERBOSE,
938*3431Scarlsonj 	    "accept_v6_message: discarded v6 %s on %s; state %s",
939*3431Scarlsonj 	    pname, dsmp->dsm_name, dhcp_state_to_string(dsmp->dsm_state));
940*3431Scarlsonj 	free_pkt_entry(plp);
9410Sstevel@tonic-gate }
9420Sstevel@tonic-gate 
9430Sstevel@tonic-gate /*
944*3431Scarlsonj  * dhcp_acknak_common(): Processes reception of an ACK or NAK packet on the
945*3431Scarlsonj  *			 global socket -- broadcast packets for IPv4, all
946*3431Scarlsonj  *			 packets for DHCPv6.
9470Sstevel@tonic-gate  *
948*3431Scarlsonj  *   input: iu_eh_t *: unused
949*3431Scarlsonj  *	    int: the global file descriptor the ACK/NAK arrived on
950*3431Scarlsonj  *	    short: unused
951*3431Scarlsonj  *	    iu_event_id_t: unused
952*3431Scarlsonj  *	    void *: unused
953*3431Scarlsonj  *  output: void
954*3431Scarlsonj  */
955*3431Scarlsonj 
956*3431Scarlsonj /* ARGSUSED */
957*3431Scarlsonj void
958*3431Scarlsonj dhcp_acknak_common(iu_eh_t *ehp, int fd, short events, iu_event_id_t id,
959*3431Scarlsonj     void *arg)
960*3431Scarlsonj {
961*3431Scarlsonj 	PKT_LIST	*plp;
962*3431Scarlsonj 	dhcp_pif_t	*pif;
963*3431Scarlsonj 	uchar_t		recv_type;
964*3431Scarlsonj 	const char	*pname;
965*3431Scarlsonj 	uint_t		xid;
966*3431Scarlsonj 	dhcp_smach_t	*dsmp;
967*3431Scarlsonj 	boolean_t	isv6 = (fd == v6_sock_fd);
968*3431Scarlsonj 
969*3431Scarlsonj 	if ((plp = recv_pkt(fd, get_max_mtu(isv6), isv6, B_FALSE)) == NULL)
970*3431Scarlsonj 		return;
971*3431Scarlsonj 
972*3431Scarlsonj 	pif = lookup_pif_by_index(plp->ifindex, isv6);
973*3431Scarlsonj 	if (pif == NULL) {
974*3431Scarlsonj 		dhcpmsg(MSG_VERBOSE, "dhcp_acknak_common: ignored packet "
975*3431Scarlsonj 		    "received on v%d ifIndex %d", isv6 ? 6 : 4, plp->ifindex);
976*3431Scarlsonj 		free_pkt_entry(plp);
977*3431Scarlsonj 		return;
978*3431Scarlsonj 	}
979*3431Scarlsonj 
980*3431Scarlsonj 	recv_type = pkt_recv_type(plp);
981*3431Scarlsonj 	pname = pkt_type_to_string(recv_type, isv6);
982*3431Scarlsonj 	if (!isv6 && !pkt_v4_match(recv_type, DHCP_PACK|DHCP_PNAK)) {
983*3431Scarlsonj 		dhcpmsg(MSG_VERBOSE, "dhcp_acknak_common: ignored %s packet "
984*3431Scarlsonj 		    "received via broadcast on %s", pname, pif->pif_name);
985*3431Scarlsonj 		free_pkt_entry(plp);
986*3431Scarlsonj 		return;
987*3431Scarlsonj 	}
988*3431Scarlsonj 
989*3431Scarlsonj 	if (isv6 && recv_type == DHCPV6_MSG_RECONFIGURE) {
990*3431Scarlsonj 		dhcpmsg(MSG_VERBOSE, "dhcp_acknak_common: ignored v6 "
991*3431Scarlsonj 		    "Reconfigure received via %s", pif->pif_name);
992*3431Scarlsonj 		free_pkt_entry(plp);
993*3431Scarlsonj 		return;
994*3431Scarlsonj 	}
995*3431Scarlsonj 
996*3431Scarlsonj 	/*
997*3431Scarlsonj 	 * Find the corresponding state machine not using DLPI.
998*3431Scarlsonj 	 *
999*3431Scarlsonj 	 * Note that DHCPv6 Reconfigure would be special: it's not the reply to
1000*3431Scarlsonj 	 * any transaction, and thus we would need to search on transaction ID
1001*3431Scarlsonj 	 * zero (all state machines) to find the match.  However, Reconfigure
1002*3431Scarlsonj 	 * is not yet supported.
1003*3431Scarlsonj 	 */
1004*3431Scarlsonj 	xid = pkt_get_xid(plp->pkt, isv6);
1005*3431Scarlsonj 	for (dsmp = lookup_smach_by_xid(xid, NULL, isv6); dsmp != NULL;
1006*3431Scarlsonj 	    dsmp = lookup_smach_by_xid(xid, dsmp, isv6)) {
1007*3431Scarlsonj 		if (dsmp->dsm_lif->lif_pif == pif)
1008*3431Scarlsonj 			break;
1009*3431Scarlsonj 	}
1010*3431Scarlsonj 	if (dsmp == NULL || dsmp->dsm_using_dlpi) {
1011*3431Scarlsonj 		dhcpmsg(MSG_VERBOSE, "dhcp_acknak_common: ignored %s packet "
1012*3431Scarlsonj 		    "received via broadcast %s; %s", pname, pif->pif_name,
1013*3431Scarlsonj 		    dsmp == NULL ? "unknown state machine" : "not using DLPI");
1014*3431Scarlsonj 		free_pkt_entry(plp);
1015*3431Scarlsonj 		return;
1016*3431Scarlsonj 	}
1017*3431Scarlsonj 
1018*3431Scarlsonj 	/*
1019*3431Scarlsonj 	 * We've got a packet; make sure it's acceptable and cancel the REQUEST
1020*3431Scarlsonj 	 * retransmissions.
1021*3431Scarlsonj 	 */
1022*3431Scarlsonj 	if (isv6)
1023*3431Scarlsonj 		accept_v6_message(dsmp, plp, pname, recv_type);
1024*3431Scarlsonj 	else
1025*3431Scarlsonj 		accept_v4_acknak(dsmp, plp);
1026*3431Scarlsonj }
1027*3431Scarlsonj 
1028*3431Scarlsonj /*
1029*3431Scarlsonj  * request_failed(): Attempt to request an address has failed.  Take an
1030*3431Scarlsonj  *		     appropriate action.
1031*3431Scarlsonj  *
1032*3431Scarlsonj  *   input: dhcp_smach_t *: state machine that has failed
1033*3431Scarlsonj  *  output: void
1034*3431Scarlsonj  */
1035*3431Scarlsonj 
1036*3431Scarlsonj static void
1037*3431Scarlsonj request_failed(dhcp_smach_t *dsmp)
1038*3431Scarlsonj {
1039*3431Scarlsonj 	PKT_LIST *offer;
1040*3431Scarlsonj 
1041*3431Scarlsonj 	dsmp->dsm_server = ipv6_all_dhcp_relay_and_servers;
1042*3431Scarlsonj 	if ((offer = select_best(dsmp)) != NULL) {
1043*3431Scarlsonj 		insque(offer, &dsmp->dsm_recv_pkt_list);
1044*3431Scarlsonj 		dhcp_requesting(NULL, dsmp);
1045*3431Scarlsonj 	} else {
1046*3431Scarlsonj 		dhcpmsg(MSG_INFO, "no offers left on %s; restarting",
1047*3431Scarlsonj 		    dsmp->dsm_name);
1048*3431Scarlsonj 		dhcp_selecting(dsmp);
1049*3431Scarlsonj 	}
1050*3431Scarlsonj }
1051*3431Scarlsonj 
1052*3431Scarlsonj /*
1053*3431Scarlsonj  * dhcp_acknak_lif(): Processes reception of an ACK or NAK packet on a given
1054*3431Scarlsonj  *		      logical interface for IPv4 (only).
1055*3431Scarlsonj  *
1056*3431Scarlsonj  *   input: iu_eh_t *: unused
1057*3431Scarlsonj  *	    int: the global file descriptor the ACK/NAK arrived on
1058*3431Scarlsonj  *	    short: unused
1059*3431Scarlsonj  *	    iu_event_id_t: the id of this event callback with the handler
1060*3431Scarlsonj  *	    void *: pointer to logical interface receiving message
1061*3431Scarlsonj  *  output: void
1062*3431Scarlsonj  */
1063*3431Scarlsonj 
1064*3431Scarlsonj /* ARGSUSED */
1065*3431Scarlsonj void
1066*3431Scarlsonj dhcp_acknak_lif(iu_eh_t *ehp, int fd, short events, iu_event_id_t id,
1067*3431Scarlsonj     void *arg)
1068*3431Scarlsonj {
1069*3431Scarlsonj 	dhcp_lif_t	*lif = arg;
1070*3431Scarlsonj 	PKT_LIST	*plp;
1071*3431Scarlsonj 	uchar_t		recv_type;
1072*3431Scarlsonj 	const char	*pname;
1073*3431Scarlsonj 	uint_t		xid;
1074*3431Scarlsonj 	dhcp_smach_t	*dsmp;
1075*3431Scarlsonj 
1076*3431Scarlsonj 	if ((plp = recv_pkt(fd, lif->lif_max, B_FALSE, B_FALSE)) == NULL)
1077*3431Scarlsonj 		return;
1078*3431Scarlsonj 
1079*3431Scarlsonj 	recv_type = pkt_recv_type(plp);
1080*3431Scarlsonj 	pname = pkt_type_to_string(recv_type, B_FALSE);
1081*3431Scarlsonj 
1082*3431Scarlsonj 	if (!pkt_v4_match(recv_type, DHCP_PACK | DHCP_PNAK)) {
1083*3431Scarlsonj 		dhcpmsg(MSG_VERBOSE, "dhcp_acknak_lif: ignored v4 %s packet "
1084*3431Scarlsonj 		    "received via LIF %s", pname, lif->lif_name);
1085*3431Scarlsonj 		free_pkt_entry(plp);
1086*3431Scarlsonj 		return;
1087*3431Scarlsonj 	}
1088*3431Scarlsonj 
1089*3431Scarlsonj 	/*
1090*3431Scarlsonj 	 * Find the corresponding state machine not using DLPI.
1091*3431Scarlsonj 	 */
1092*3431Scarlsonj 	xid = pkt_get_xid(plp->pkt, B_FALSE);
1093*3431Scarlsonj 	for (dsmp = lookup_smach_by_xid(xid, NULL, B_FALSE); dsmp != NULL;
1094*3431Scarlsonj 	    dsmp = lookup_smach_by_xid(xid, dsmp, B_FALSE)) {
1095*3431Scarlsonj 		if (dsmp->dsm_lif == lif)
1096*3431Scarlsonj 			break;
1097*3431Scarlsonj 	}
1098*3431Scarlsonj 	if (dsmp == NULL || dsmp->dsm_using_dlpi) {
1099*3431Scarlsonj 		dhcpmsg(MSG_VERBOSE, "dhcp_acknak_lif: ignored %s packet xid "
1100*3431Scarlsonj 		    "%x received via LIF %s; %s", pname, xid, lif->lif_name,
1101*3431Scarlsonj 		    dsmp == NULL ? "unknown state machine" : "not using DLPI");
1102*3431Scarlsonj 		free_pkt_entry(plp);
1103*3431Scarlsonj 		return;
1104*3431Scarlsonj 	}
1105*3431Scarlsonj 
1106*3431Scarlsonj 	/*
1107*3431Scarlsonj 	 * We've got a packet; make sure it's acceptable and cancel the REQUEST
1108*3431Scarlsonj 	 * retransmissions.
1109*3431Scarlsonj 	 */
1110*3431Scarlsonj 	accept_v4_acknak(dsmp, plp);
1111*3431Scarlsonj }
1112*3431Scarlsonj 
1113*3431Scarlsonj /*
1114*3431Scarlsonj  * dhcp_restart(): restarts DHCP (from INIT) on a given state machine
1115*3431Scarlsonj  *
1116*3431Scarlsonj  *   input: dhcp_smach_t *: the state machine to restart DHCP on
11170Sstevel@tonic-gate  *  output: void
11180Sstevel@tonic-gate  */
11190Sstevel@tonic-gate 
11202546Scarlsonj void
1121*3431Scarlsonj dhcp_restart(dhcp_smach_t *dsmp)
11220Sstevel@tonic-gate {
1123*3431Scarlsonj 	/*
1124*3431Scarlsonj 	 * As we're returning to INIT state, we need to discard any leases we
1125*3431Scarlsonj 	 * may have, and (for v4) canonize the LIF.  There's a bit of tension
1126*3431Scarlsonj 	 * between keeping around a possibly still working address, and obeying
1127*3431Scarlsonj 	 * the RFCs.  A more elaborate design would be to mark the addresses as
1128*3431Scarlsonj 	 * DEPRECATED, and then start a removal timer.  Such a design would
1129*3431Scarlsonj 	 * probably compromise testing.
1130*3431Scarlsonj 	 */
1131*3431Scarlsonj 	deprecate_leases(dsmp);
11320Sstevel@tonic-gate 
1133*3431Scarlsonj 	if (iu_schedule_timer(tq, DHCP_RESTART_WAIT, dhcp_start, dsmp) == -1) {
11342546Scarlsonj 		dhcpmsg(MSG_ERROR, "dhcp_restart: cannot schedule dhcp_start, "
1135*3431Scarlsonj 		    "reverting to INIT state on %s", dsmp->dsm_name);
1136*3431Scarlsonj 
1137*3431Scarlsonj 		(void) set_smach_state(dsmp, INIT);
1138*3431Scarlsonj 		dsmp->dsm_dflags |= DHCP_IF_FAILED;
1139*3431Scarlsonj 		ipc_action_finish(dsmp, DHCP_IPC_E_MEMORY);
1140*3431Scarlsonj 	} else {
1141*3431Scarlsonj 		hold_smach(dsmp);
1142*3431Scarlsonj 	}
11430Sstevel@tonic-gate }
11440Sstevel@tonic-gate 
11450Sstevel@tonic-gate /*
11460Sstevel@tonic-gate  * stop_requesting(): decides when to stop retransmitting REQUESTs
11470Sstevel@tonic-gate  *
1148*3431Scarlsonj  *   input: dhcp_smach_t *: the state machine REQUESTs are being sent from
11490Sstevel@tonic-gate  *	    unsigned int: the number of REQUESTs sent so far
11500Sstevel@tonic-gate  *  output: boolean_t: B_TRUE if retransmissions should stop
11510Sstevel@tonic-gate  */
11520Sstevel@tonic-gate 
11530Sstevel@tonic-gate static boolean_t
1154*3431Scarlsonj stop_requesting(dhcp_smach_t *dsmp, unsigned int n_requests)
11550Sstevel@tonic-gate {
1156*3431Scarlsonj 	uint_t maxreq;
11570Sstevel@tonic-gate 
1158*3431Scarlsonj 	maxreq = dsmp->dsm_isv6 ? DHCPV6_REQ_MAX_RC : DHCP_MAX_REQUESTS;
1159*3431Scarlsonj 	if (n_requests >= maxreq) {
11600Sstevel@tonic-gate 
1161*3431Scarlsonj 		dhcpmsg(MSG_INFO, "no ACK/NAK/Reply to REQUEST on %s",
1162*3431Scarlsonj 		    dsmp->dsm_name);
11630Sstevel@tonic-gate 
1164*3431Scarlsonj 		request_failed(dsmp);
11650Sstevel@tonic-gate 		return (B_TRUE);
1166*3431Scarlsonj 	} else {
1167*3431Scarlsonj 		return (B_FALSE);
11680Sstevel@tonic-gate 	}
11690Sstevel@tonic-gate }
1170