17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5c2934490Sdh  * Common Development and Distribution License (the "License").
6c2934490Sdh  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22e11c3f44Smeem  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
24*b31320a7SChris Fraire  * Copyright (c) 2016-2017, Chris Fraire <cfraire@me.com>.
257c478bd9Sstevel@tonic-gate  *
267c478bd9Sstevel@tonic-gate  * REQUESTING state of the client state machine.
277c478bd9Sstevel@tonic-gate  */
287c478bd9Sstevel@tonic-gate 
29d04ccbb3Scarlsonj #include <stdlib.h>
30d04ccbb3Scarlsonj #include <string.h>
31d04ccbb3Scarlsonj #include <search.h>
327c478bd9Sstevel@tonic-gate #include <sys/types.h>
337c478bd9Sstevel@tonic-gate #include <netinet/in.h>
347c478bd9Sstevel@tonic-gate #include <netinet/dhcp.h>
357c478bd9Sstevel@tonic-gate #include <netinet/udp.h>
367c478bd9Sstevel@tonic-gate #include <netinet/ip_var.h>
377c478bd9Sstevel@tonic-gate #include <netinet/udp_var.h>
387c478bd9Sstevel@tonic-gate #include <arpa/inet.h>
39d04ccbb3Scarlsonj #include <dhcp_hostconf.h>
40d04ccbb3Scarlsonj #include <dhcpagent_util.h>
417c478bd9Sstevel@tonic-gate #include <dhcpmsg.h>
427c478bd9Sstevel@tonic-gate 
437c478bd9Sstevel@tonic-gate #include "states.h"
447c478bd9Sstevel@tonic-gate #include "util.h"
457c478bd9Sstevel@tonic-gate #include "packet.h"
467c478bd9Sstevel@tonic-gate #include "interface.h"
477c478bd9Sstevel@tonic-gate #include "agent.h"
487c478bd9Sstevel@tonic-gate 
49d04ccbb3Scarlsonj static PKT_LIST		*select_best(dhcp_smach_t *);
50d04ccbb3Scarlsonj static void		request_failed(dhcp_smach_t *);
517c478bd9Sstevel@tonic-gate static stop_func_t	stop_requesting;
527c478bd9Sstevel@tonic-gate 
53d04ccbb3Scarlsonj /*
54d04ccbb3Scarlsonj  * send_v6_request(): sends a DHCPv6 Request message and switches to REQUESTING
55d04ccbb3Scarlsonj  *		      state.  This is a separate function because a NoBinding
56d04ccbb3Scarlsonj  *		      response can also cause us to do this.
57d04ccbb3Scarlsonj  *
58d04ccbb3Scarlsonj  *   input: dhcp_smach_t *: the state machine
59d04ccbb3Scarlsonj  *  output: none
60d04ccbb3Scarlsonj  */
61d04ccbb3Scarlsonj 
62d04ccbb3Scarlsonj void
send_v6_request(dhcp_smach_t * dsmp)63d04ccbb3Scarlsonj send_v6_request(dhcp_smach_t *dsmp)
64d04ccbb3Scarlsonj {
65d04ccbb3Scarlsonj 	dhcp_pkt_t *dpkt;
66d04ccbb3Scarlsonj 	dhcpv6_ia_na_t d6in;
67d04ccbb3Scarlsonj 
68d04ccbb3Scarlsonj 	dpkt = init_pkt(dsmp, DHCPV6_MSG_REQUEST);
69d04ccbb3Scarlsonj 	(void) add_pkt_opt(dpkt, DHCPV6_OPT_SERVERID, dsmp->dsm_serverid,
70d04ccbb3Scarlsonj 	    dsmp->dsm_serveridlen);
71d04ccbb3Scarlsonj 
72d04ccbb3Scarlsonj 	/* Add an IA_NA option for our controlling LIF */
73d04ccbb3Scarlsonj 	d6in.d6in_iaid = htonl(dsmp->dsm_lif->lif_iaid);
74d04ccbb3Scarlsonj 	d6in.d6in_t1 = htonl(0);
75d04ccbb3Scarlsonj 	d6in.d6in_t2 = htonl(0);
76d04ccbb3Scarlsonj 	(void) add_pkt_opt(dpkt, DHCPV6_OPT_IA_NA,
77d04ccbb3Scarlsonj 	    (dhcpv6_option_t *)&d6in + 1,
78d04ccbb3Scarlsonj 	    sizeof (d6in) - sizeof (dhcpv6_option_t));
79d04ccbb3Scarlsonj 
80d04ccbb3Scarlsonj 	/* Add required Option Request option */
81d04ccbb3Scarlsonj 	(void) add_pkt_prl(dpkt, dsmp);
82d04ccbb3Scarlsonj 
83d04ccbb3Scarlsonj 	(void) send_pkt_v6(dsmp, dpkt, dsmp->dsm_server, stop_requesting,
84d04ccbb3Scarlsonj 	    DHCPV6_REQ_TIMEOUT, DHCPV6_REQ_MAX_RT);
85d04ccbb3Scarlsonj 
86d04ccbb3Scarlsonj 	/* For DHCPv6, state switch cannot fail */
87d04ccbb3Scarlsonj 	(void) set_smach_state(dsmp, REQUESTING);
88d04ccbb3Scarlsonj }
89d04ccbb3Scarlsonj 
90d04ccbb3Scarlsonj /*
91d04ccbb3Scarlsonj  * server_unicast_option(): determines the server address to use based on the
92d04ccbb3Scarlsonj  *			    DHCPv6 Server Unicast option present in the given
93d04ccbb3Scarlsonj  *			    packet.
94d04ccbb3Scarlsonj  *
95d04ccbb3Scarlsonj  *   input: dhcp_smach_t *: the state machine
96d04ccbb3Scarlsonj  *	    PKT_LIST *: received packet (Advertisement or Reply)
97d04ccbb3Scarlsonj  *  output: none
98d04ccbb3Scarlsonj  */
99d04ccbb3Scarlsonj 
100d04ccbb3Scarlsonj void
server_unicast_option(dhcp_smach_t * dsmp,PKT_LIST * plp)101d04ccbb3Scarlsonj server_unicast_option(dhcp_smach_t *dsmp, PKT_LIST *plp)
102d04ccbb3Scarlsonj {
103d04ccbb3Scarlsonj 	const dhcpv6_option_t *d6o;
104d04ccbb3Scarlsonj 	uint_t olen;
105d04ccbb3Scarlsonj 
106d04ccbb3Scarlsonj 	d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_UNICAST, &olen);
107d04ccbb3Scarlsonj 	olen -= sizeof (*d6o);
108d04ccbb3Scarlsonj 	/* LINTED: no consequent */
109d04ccbb3Scarlsonj 	if (d6o == NULL) {
110d04ccbb3Scarlsonj 		/* No Server Unicast option specified */
111d04ccbb3Scarlsonj 	} else if (olen != sizeof (dsmp->dsm_server)) {
112d04ccbb3Scarlsonj 		dhcpmsg(MSG_WARNING, "server_unicast_option: %s has Server "
113d04ccbb3Scarlsonj 		    "Unicast option with bad length",
114d04ccbb3Scarlsonj 		    pkt_type_to_string(pkt_recv_type(plp), B_TRUE));
115d04ccbb3Scarlsonj 	} else {
116d04ccbb3Scarlsonj 		in6_addr_t addr;
117d04ccbb3Scarlsonj 
118d04ccbb3Scarlsonj 		(void) memcpy(&addr, d6o + 1, olen);
119d04ccbb3Scarlsonj 		if (IN6_IS_ADDR_UNSPECIFIED(&addr)) {
120d04ccbb3Scarlsonj 			dhcpmsg(MSG_WARNING, "server_unicast_option: unicast "
121d04ccbb3Scarlsonj 			    "to unspecified address ignored");
122d04ccbb3Scarlsonj 		} else if (IN6_IS_ADDR_MULTICAST(&addr)) {
123d04ccbb3Scarlsonj 			dhcpmsg(MSG_WARNING, "server_unicast_option: unicast "
124d04ccbb3Scarlsonj 			    "to multicast address ignored");
125d04ccbb3Scarlsonj 		} else if (IN6_IS_ADDR_V4COMPAT(&addr) ||
126d04ccbb3Scarlsonj 		    IN6_IS_ADDR_V4MAPPED(&addr)) {
127d04ccbb3Scarlsonj 			dhcpmsg(MSG_WARNING, "server_unicast_option: unicast "
128d04ccbb3Scarlsonj 			    "to invalid address ignored");
129d04ccbb3Scarlsonj 		} else {
130d04ccbb3Scarlsonj 			dsmp->dsm_server = addr;
131d04ccbb3Scarlsonj 		}
132d04ccbb3Scarlsonj 	}
133d04ccbb3Scarlsonj }
134d04ccbb3Scarlsonj 
1357c478bd9Sstevel@tonic-gate /*
1367c478bd9Sstevel@tonic-gate  * dhcp_requesting(): checks if OFFER packets to come in from DHCP servers.
1377c478bd9Sstevel@tonic-gate  *		      if so, chooses the best one, sends a REQUEST to the
1387c478bd9Sstevel@tonic-gate  *		      server and registers an event handler to receive
139d04ccbb3Scarlsonj  *		      the ACK/NAK.  This may be called by the offer timer or
140d04ccbb3Scarlsonj  *		      by any function that wants to check for offers after
141d04ccbb3Scarlsonj  *		      canceling that timer.
1427c478bd9Sstevel@tonic-gate  *
143d04ccbb3Scarlsonj  *   input: iu_tq_t *: timer queue; non-NULL if this is a timer callback
144d04ccbb3Scarlsonj  *	    void *: the state machine receiving OFFER packets
1457c478bd9Sstevel@tonic-gate  *  output: void
1467c478bd9Sstevel@tonic-gate  */
1477c478bd9Sstevel@tonic-gate 
1487c478bd9Sstevel@tonic-gate void
dhcp_requesting(iu_tq_t * tqp,void * arg)1497c478bd9Sstevel@tonic-gate dhcp_requesting(iu_tq_t *tqp, void *arg)
1507c478bd9Sstevel@tonic-gate {
151d04ccbb3Scarlsonj 	dhcp_smach_t		*dsmp = arg;
1527c478bd9Sstevel@tonic-gate 	dhcp_pkt_t		*dpkt;
1537c478bd9Sstevel@tonic-gate 	PKT_LIST		*offer;
1547c478bd9Sstevel@tonic-gate 	lease_t			lease;
155d04ccbb3Scarlsonj 	boolean_t		isv6 = dsmp->dsm_isv6;
1567c478bd9Sstevel@tonic-gate 
157d04ccbb3Scarlsonj 	/*
158d04ccbb3Scarlsonj 	 * We assume here that if tqp is set, then this means we're being
159d04ccbb3Scarlsonj 	 * called back by the offer wait timer.  If so, then drop our hold
160d04ccbb3Scarlsonj 	 * on the state machine.  Otherwise, cancel the timer if it's running.
161d04ccbb3Scarlsonj 	 */
162d04ccbb3Scarlsonj 	if (tqp != NULL) {
163d04ccbb3Scarlsonj 		dhcpmsg(MSG_VERBOSE,
164d04ccbb3Scarlsonj 		    "dhcp_requesting: offer wait timer on v%d %s",
165d04ccbb3Scarlsonj 		    isv6 ? 6 : 4, dsmp->dsm_name);
166d04ccbb3Scarlsonj 		dsmp->dsm_offer_timer = -1;
167d04ccbb3Scarlsonj 		if (!verify_smach(dsmp))
168d04ccbb3Scarlsonj 			return;
169d04ccbb3Scarlsonj 	} else {
170d04ccbb3Scarlsonj 		cancel_offer_timer(dsmp);
1717c478bd9Sstevel@tonic-gate 	}
1727c478bd9Sstevel@tonic-gate 
1737c478bd9Sstevel@tonic-gate 	/*
1747c478bd9Sstevel@tonic-gate 	 * select the best OFFER; all others pitched.
1757c478bd9Sstevel@tonic-gate 	 */
1767c478bd9Sstevel@tonic-gate 
177d04ccbb3Scarlsonj 	offer = select_best(dsmp);
1787c478bd9Sstevel@tonic-gate 	if (offer == NULL) {
1797c478bd9Sstevel@tonic-gate 
180d04ccbb3Scarlsonj 		dhcpmsg(MSG_VERBOSE,
181d04ccbb3Scarlsonj 		    "no OFFERs/Advertisements on %s, waiting...",
182d04ccbb3Scarlsonj 		    dsmp->dsm_name);
1837c478bd9Sstevel@tonic-gate 
1847c478bd9Sstevel@tonic-gate 		/*
1857c478bd9Sstevel@tonic-gate 		 * no acceptable OFFERs have come in.  reschedule
186d04ccbb3Scarlsonj 		 * ourself for callback.
1877c478bd9Sstevel@tonic-gate 		 */
1887c478bd9Sstevel@tonic-gate 
189d04ccbb3Scarlsonj 		if ((dsmp->dsm_offer_timer = iu_schedule_timer(tq,
190d04ccbb3Scarlsonj 		    dsmp->dsm_offer_wait, dhcp_requesting, dsmp)) == -1) {
1917c478bd9Sstevel@tonic-gate 
1927c478bd9Sstevel@tonic-gate 			/*
1937c478bd9Sstevel@tonic-gate 			 * ugh.  the best we can do at this point is
1947c478bd9Sstevel@tonic-gate 			 * revert back to INIT and wait for a user to
1957c478bd9Sstevel@tonic-gate 			 * restart us.
1967c478bd9Sstevel@tonic-gate 			 */
1977c478bd9Sstevel@tonic-gate 
1987c478bd9Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "dhcp_requesting: cannot "
1997c478bd9Sstevel@tonic-gate 			    "reschedule callback, reverting to INIT state on "
200d04ccbb3Scarlsonj 			    "%s", dsmp->dsm_name);
201d04ccbb3Scarlsonj 
202d04ccbb3Scarlsonj 			stop_pkt_retransmission(dsmp);
203d04ccbb3Scarlsonj 			(void) set_smach_state(dsmp, INIT);
204d04ccbb3Scarlsonj 			dsmp->dsm_dflags |= DHCP_IF_FAILED;
205d04ccbb3Scarlsonj 			ipc_action_finish(dsmp, DHCP_IPC_E_MEMORY);
206d04ccbb3Scarlsonj 		} else {
207d04ccbb3Scarlsonj 			hold_smach(dsmp);
208d04ccbb3Scarlsonj 		}
2097c478bd9Sstevel@tonic-gate 
2107c478bd9Sstevel@tonic-gate 		return;
2117c478bd9Sstevel@tonic-gate 	}
2127c478bd9Sstevel@tonic-gate 
2137c478bd9Sstevel@tonic-gate 	/*
214d04ccbb3Scarlsonj 	 * With IPv4, the DHCPREQUEST packet we're about to transmit implicitly
215d04ccbb3Scarlsonj 	 * declines all other offers we've received.  We can no longer use any
216d04ccbb3Scarlsonj 	 * cached offers, so we must discard them now.  With DHCPv6, though,
217d04ccbb3Scarlsonj 	 * we're permitted to hang onto the advertisements (offers) and try
218d04ccbb3Scarlsonj 	 * them if the preferred one doesn't pan out.
2197c478bd9Sstevel@tonic-gate 	 */
220d04ccbb3Scarlsonj 	if (!isv6)
221d04ccbb3Scarlsonj 		free_pkt_list(&dsmp->dsm_recv_pkt_list);
2227c478bd9Sstevel@tonic-gate 
223d04ccbb3Scarlsonj 	/* stop collecting packets. */
2247c478bd9Sstevel@tonic-gate 
225d04ccbb3Scarlsonj 	stop_pkt_retransmission(dsmp);
2267c478bd9Sstevel@tonic-gate 
227d04ccbb3Scarlsonj 	/*
228d04ccbb3Scarlsonj 	 * For IPv4, check to see whether we got an OFFER or a BOOTP packet.
229d04ccbb3Scarlsonj 	 * If we got a BOOTP packet, go to the BOUND state now.
230d04ccbb3Scarlsonj 	 */
231d04ccbb3Scarlsonj 	if (!isv6 && offer->opts[CD_DHCP_TYPE] == NULL) {
232d04ccbb3Scarlsonj 		free_pkt_list(&dsmp->dsm_recv_pkt_list);
233d04ccbb3Scarlsonj 
234d04ccbb3Scarlsonj 		if (!set_smach_state(dsmp, REQUESTING)) {
235d04ccbb3Scarlsonj 			dhcp_restart(dsmp);
236d04ccbb3Scarlsonj 			return;
237d04ccbb3Scarlsonj 		}
2387c478bd9Sstevel@tonic-gate 
239d04ccbb3Scarlsonj 		if (!dhcp_bound(dsmp, offer)) {
2407c478bd9Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "dhcp_requesting: dhcp_bound "
241d04ccbb3Scarlsonj 			    "failed for %s", dsmp->dsm_name);
242d04ccbb3Scarlsonj 			dhcp_restart(dsmp);
2437c478bd9Sstevel@tonic-gate 			return;
2447c478bd9Sstevel@tonic-gate 		}
2457c478bd9Sstevel@tonic-gate 
2467c478bd9Sstevel@tonic-gate 		return;
2477c478bd9Sstevel@tonic-gate 	}
2487c478bd9Sstevel@tonic-gate 
249*b31320a7SChris Fraire 	save_domainname(dsmp, offer);
250*b31320a7SChris Fraire 
251d04ccbb3Scarlsonj 	if (isv6) {
252d04ccbb3Scarlsonj 		const char *estr, *msg;
253d04ccbb3Scarlsonj 		const dhcpv6_option_t *d6o;
254d04ccbb3Scarlsonj 		uint_t olen, msglen;
255d04ccbb3Scarlsonj 
256d04ccbb3Scarlsonj 		/* If there's a Status Code option, print the message */
257d04ccbb3Scarlsonj 		d6o = dhcpv6_pkt_option(offer, NULL, DHCPV6_OPT_STATUS_CODE,
258d04ccbb3Scarlsonj 		    &olen);
259d04ccbb3Scarlsonj 		(void) dhcpv6_status_code(d6o, olen, &estr, &msg, &msglen);
260d04ccbb3Scarlsonj 		print_server_msg(dsmp, msg, msglen);
2617c478bd9Sstevel@tonic-gate 
262d04ccbb3Scarlsonj 		/* Copy in the Server ID (guaranteed to be present now) */
263d04ccbb3Scarlsonj 		if (!save_server_id(dsmp, offer))
264d04ccbb3Scarlsonj 			goto failure;
265d04ccbb3Scarlsonj 
266d04ccbb3Scarlsonj 		/*
267d04ccbb3Scarlsonj 		 * Determine how to send this message.  If the Advertisement
268d04ccbb3Scarlsonj 		 * (offer) has the unicast option, then use the address
269d04ccbb3Scarlsonj 		 * specified in the option.  Otherwise, send via multicast.
270d04ccbb3Scarlsonj 		 */
271d04ccbb3Scarlsonj 		server_unicast_option(dsmp, offer);
272d04ccbb3Scarlsonj 
273d04ccbb3Scarlsonj 		send_v6_request(dsmp);
274d04ccbb3Scarlsonj 	} else {
275d04ccbb3Scarlsonj 		/* if we got a message from the server, display it. */
276d04ccbb3Scarlsonj 		if (offer->opts[CD_MESSAGE] != NULL) {
277d04ccbb3Scarlsonj 			print_server_msg(dsmp,
278d04ccbb3Scarlsonj 			    (char *)offer->opts[CD_MESSAGE]->value,
279d04ccbb3Scarlsonj 			    offer->opts[CD_MESSAGE]->len);
280d04ccbb3Scarlsonj 		}
281d04ccbb3Scarlsonj 
282d04ccbb3Scarlsonj 		/*
283d04ccbb3Scarlsonj 		 * assemble a DHCPREQUEST, with the ciaddr field set to 0,
284d04ccbb3Scarlsonj 		 * since we got here from the INIT state.
285d04ccbb3Scarlsonj 		 */
286d04ccbb3Scarlsonj 
287d04ccbb3Scarlsonj 		dpkt = init_pkt(dsmp, REQUEST);
288d04ccbb3Scarlsonj 
289d04ccbb3Scarlsonj 		/*
290d04ccbb3Scarlsonj 		 * Grab the lease out of the OFFER; we know it's valid because
291d04ccbb3Scarlsonj 		 * select_best() already checked.  The max dhcp message size
292d04ccbb3Scarlsonj 		 * option is set to the interface max, minus the size of the
293d04ccbb3Scarlsonj 		 * udp and ip headers.
294d04ccbb3Scarlsonj 		 */
295d04ccbb3Scarlsonj 
296d04ccbb3Scarlsonj 		(void) memcpy(&lease, offer->opts[CD_LEASE_TIME]->value,
297d04ccbb3Scarlsonj 		    sizeof (lease_t));
298d04ccbb3Scarlsonj 
299d04ccbb3Scarlsonj 		(void) add_pkt_opt32(dpkt, CD_LEASE_TIME, lease);
300d04ccbb3Scarlsonj 		(void) add_pkt_opt16(dpkt, CD_MAX_DHCP_SIZE,
301d04ccbb3Scarlsonj 		    htons(dsmp->dsm_lif->lif_max - sizeof (struct udpiphdr)));
302d04ccbb3Scarlsonj 		(void) add_pkt_opt32(dpkt, CD_REQUESTED_IP_ADDR,
303d04ccbb3Scarlsonj 		    offer->pkt->yiaddr.s_addr);
304d04ccbb3Scarlsonj 		(void) add_pkt_opt(dpkt, CD_SERVER_ID,
305d04ccbb3Scarlsonj 		    offer->opts[CD_SERVER_ID]->value,
306d04ccbb3Scarlsonj 		    offer->opts[CD_SERVER_ID]->len);
307d04ccbb3Scarlsonj 
308f4b3ec61Sdh 		if (class_id_len != 0) {
309f4b3ec61Sdh 			(void) add_pkt_opt(dpkt, CD_CLASS_ID, class_id,
310f4b3ec61Sdh 			    class_id_len);
311f4b3ec61Sdh 		}
312d04ccbb3Scarlsonj 		(void) add_pkt_prl(dpkt, dsmp);
313d04ccbb3Scarlsonj 
314d04ccbb3Scarlsonj 		/*
315d04ccbb3Scarlsonj 		 * dsm_reqhost was set for this state machine in
316d04ccbb3Scarlsonj 		 * dhcp_selecting() if the DF_REQUEST_HOSTNAME option set and a
317d04ccbb3Scarlsonj 		 * host name was found
318d04ccbb3Scarlsonj 		 */
319*b31320a7SChris Fraire 		if (!dhcp_add_fqdn_opt(dpkt, dsmp) &&
320*b31320a7SChris Fraire 		    dsmp->dsm_reqhost != NULL) {
321d04ccbb3Scarlsonj 			(void) add_pkt_opt(dpkt, CD_HOSTNAME, dsmp->dsm_reqhost,
322d04ccbb3Scarlsonj 			    strlen(dsmp->dsm_reqhost));
323d04ccbb3Scarlsonj 		}
324d04ccbb3Scarlsonj 		(void) add_pkt_opt(dpkt, CD_END, NULL, 0);
325d04ccbb3Scarlsonj 
326d04ccbb3Scarlsonj 		/*
327d04ccbb3Scarlsonj 		 * send out the REQUEST, trying retransmissions.  either a NAK
328d04ccbb3Scarlsonj 		 * or too many REQUEST attempts will revert us to SELECTING.
329d04ccbb3Scarlsonj 		 */
330d04ccbb3Scarlsonj 
331d04ccbb3Scarlsonj 		if (!set_smach_state(dsmp, REQUESTING)) {
332d04ccbb3Scarlsonj 			dhcpmsg(MSG_ERROR, "dhcp_requesting: cannot switch to "
333d04ccbb3Scarlsonj 			    "REQUESTING state; reverting to INIT on %s",
334d04ccbb3Scarlsonj 			    dsmp->dsm_name);
335d04ccbb3Scarlsonj 			goto failure;
336d04ccbb3Scarlsonj 		}
337d04ccbb3Scarlsonj 
338d04ccbb3Scarlsonj 		(void) send_pkt(dsmp, dpkt, htonl(INADDR_BROADCAST),
339d04ccbb3Scarlsonj 		    stop_requesting);
340d04ccbb3Scarlsonj 	}
341d04ccbb3Scarlsonj 
342d04ccbb3Scarlsonj 	/* all done with the offer */
343d04ccbb3Scarlsonj 	free_pkt_entry(offer);
344d04ccbb3Scarlsonj 
345d04ccbb3Scarlsonj 	return;
346d04ccbb3Scarlsonj 
347d04ccbb3Scarlsonj failure:
348d04ccbb3Scarlsonj 	dsmp->dsm_dflags |= DHCP_IF_FAILED;
349d04ccbb3Scarlsonj 	(void) set_smach_state(dsmp, INIT);
350d04ccbb3Scarlsonj 	ipc_action_finish(dsmp, DHCP_IPC_E_MEMORY);
351d04ccbb3Scarlsonj 	free_pkt_list(&dsmp->dsm_recv_pkt_list);
352d04ccbb3Scarlsonj }
353d04ccbb3Scarlsonj 
354d04ccbb3Scarlsonj /*
355d04ccbb3Scarlsonj  * compute_points_v6(): compute the number of "points" for a given v6
356d04ccbb3Scarlsonj  *			advertisement.
357d04ccbb3Scarlsonj  *
358d04ccbb3Scarlsonj  *   input: const PKT_LIST *: packet to inspect
359d04ccbb3Scarlsonj  *	    const dhcp_smach_t *: state machine that received the packet
360d04ccbb3Scarlsonj  *  output: int: -1 to discard, -2 to accept immediately, >=0 for preference.
361d04ccbb3Scarlsonj  */
362d04ccbb3Scarlsonj 
363d04ccbb3Scarlsonj static int
compute_points_v6(const PKT_LIST * pkt,const dhcp_smach_t * dsmp)364d04ccbb3Scarlsonj compute_points_v6(const PKT_LIST *pkt, const dhcp_smach_t *dsmp)
365d04ccbb3Scarlsonj {
366d04ccbb3Scarlsonj 	char abuf[INET6_ADDRSTRLEN];
367d04ccbb3Scarlsonj 	int points = 0;
368d04ccbb3Scarlsonj 	const dhcpv6_option_t *d6o, *d6so;
369d04ccbb3Scarlsonj 	uint_t olen, solen;
370d04ccbb3Scarlsonj 	int i;
371d04ccbb3Scarlsonj 	const char *estr, *msg;
372d04ccbb3Scarlsonj 	uint_t msglen;
3737c478bd9Sstevel@tonic-gate 
3747c478bd9Sstevel@tonic-gate 	/*
375d04ccbb3Scarlsonj 	 * Look through the packet contents.  Valid packets must have our
376d04ccbb3Scarlsonj 	 * client ID and a server ID, which has already been checked by
377e704a8f2Smeem 	 * dhcp_packet_lif.  Bonus points for each option.
3787c478bd9Sstevel@tonic-gate 	 */
3797c478bd9Sstevel@tonic-gate 
380d04ccbb3Scarlsonj 	/* One point for having a valid message. */
381d04ccbb3Scarlsonj 	points++;
3827c478bd9Sstevel@tonic-gate 
3837c478bd9Sstevel@tonic-gate 	/*
384d04ccbb3Scarlsonj 	 * Per RFC 3315, if the Advertise message says, "yes, we have no
385d04ccbb3Scarlsonj 	 * bananas today," then ignore the entire message.  (Why it's just
386d04ccbb3Scarlsonj 	 * _this_ error and no other is a bit of a mystery, but a standard is a
387d04ccbb3Scarlsonj 	 * standard.)
3887c478bd9Sstevel@tonic-gate 	 */
389d04ccbb3Scarlsonj 	d6o = dhcpv6_pkt_option(pkt, NULL, DHCPV6_OPT_STATUS_CODE, &olen);
390d04ccbb3Scarlsonj 	if (dhcpv6_status_code(d6o, olen, &estr, &msg, &msglen) ==
391d04ccbb3Scarlsonj 	    DHCPV6_STAT_NOADDRS) {
392d04ccbb3Scarlsonj 		dhcpmsg(MSG_INFO,
393d04ccbb3Scarlsonj 		    "discard advertisement from %s on %s: no address status",
394d04ccbb3Scarlsonj 		    inet_ntop(AF_INET6,
395d04ccbb3Scarlsonj 		    &((struct sockaddr_in6 *)&pkt->pktfrom)->sin6_addr,
396d04ccbb3Scarlsonj 		    abuf, sizeof (abuf)), dsmp->dsm_name);
397d04ccbb3Scarlsonj 		return (-1);
398d04ccbb3Scarlsonj 	}
3997c478bd9Sstevel@tonic-gate 
400d04ccbb3Scarlsonj 	/* Two points for each batch of offered IP addresses */
401d04ccbb3Scarlsonj 	d6o = NULL;
402d04ccbb3Scarlsonj 	while ((d6o = dhcpv6_pkt_option(pkt, d6o, DHCPV6_OPT_IA_NA,
403d04ccbb3Scarlsonj 	    &olen)) != NULL) {
4047c478bd9Sstevel@tonic-gate 
405d04ccbb3Scarlsonj 		/*
406d04ccbb3Scarlsonj 		 * Note that it's possible to have "no bananas" on an
407d04ccbb3Scarlsonj 		 * individual IA.  We must look for that here.
408d04ccbb3Scarlsonj 		 *
409d04ccbb3Scarlsonj 		 * RFC 3315 section 17.1.3 does not refer to the status code
410d04ccbb3Scarlsonj 		 * embedded in the IA itself.  However, the TAHI test suite
411d04ccbb3Scarlsonj 		 * checks for this specific case.  Because it's extremely
412d04ccbb3Scarlsonj 		 * unlikely that any usable server is going to report that it
413d04ccbb3Scarlsonj 		 * has no addresses on a network using DHCP for address
414d04ccbb3Scarlsonj 		 * assignment, we allow such messages to be dropped.
415d04ccbb3Scarlsonj 		 */
416d04ccbb3Scarlsonj 		d6so = dhcpv6_find_option(
417d04ccbb3Scarlsonj 		    (const char *)d6o + sizeof (dhcpv6_ia_na_t),
418d04ccbb3Scarlsonj 		    olen - sizeof (dhcpv6_ia_na_t), NULL,
419d04ccbb3Scarlsonj 		    DHCPV6_OPT_STATUS_CODE, &solen);
420d04ccbb3Scarlsonj 		if (dhcpv6_status_code(d6so, solen, &estr, &msg, &msglen) ==
421d04ccbb3Scarlsonj 		    DHCPV6_STAT_NOADDRS)
422d04ccbb3Scarlsonj 			return (-1);
423d04ccbb3Scarlsonj 		points += 2;
424d04ccbb3Scarlsonj 	}
4257c478bd9Sstevel@tonic-gate 
4267c478bd9Sstevel@tonic-gate 	/*
427d04ccbb3Scarlsonj 	 * Note that we drive on in the case where there are no addresses.  The
428d04ccbb3Scarlsonj 	 * hope here is that we'll at least get some useful configuration
429d04ccbb3Scarlsonj 	 * information.
4307c478bd9Sstevel@tonic-gate 	 */
4317c478bd9Sstevel@tonic-gate 
432d04ccbb3Scarlsonj 	/* One point for each requested option */
433d04ccbb3Scarlsonj 	for (i = 0; i < dsmp->dsm_prllen; i++) {
434d04ccbb3Scarlsonj 		if (dhcpv6_pkt_option(pkt, NULL, dsmp->dsm_prl[i], NULL) !=
435d04ccbb3Scarlsonj 		    NULL)
436d04ccbb3Scarlsonj 			points++;
437d04ccbb3Scarlsonj 	}
4387c478bd9Sstevel@tonic-gate 
4397c478bd9Sstevel@tonic-gate 	/*
440d04ccbb3Scarlsonj 	 * Ten points for each point of "preference."  Note: the value 255 is
441d04ccbb3Scarlsonj 	 * special.  It means "stop right now and select this server."
4427c478bd9Sstevel@tonic-gate 	 */
443d04ccbb3Scarlsonj 	d6o = dhcpv6_pkt_option(pkt, NULL, DHCPV6_OPT_PREFERENCE, &olen);
444d04ccbb3Scarlsonj 	if (d6o != NULL && olen == sizeof (*d6o) + 1) {
445d04ccbb3Scarlsonj 		int pref = *(const uchar_t *)(d6o + 1);
446d04ccbb3Scarlsonj 
447d04ccbb3Scarlsonj 		if (pref == 255)
448d04ccbb3Scarlsonj 			return (-2);
449d04ccbb3Scarlsonj 		points += 10 * pref;
450d04ccbb3Scarlsonj 	}
451d04ccbb3Scarlsonj 
452d04ccbb3Scarlsonj 	return (points);
453d04ccbb3Scarlsonj }
454d04ccbb3Scarlsonj 
455d04ccbb3Scarlsonj /*
456d04ccbb3Scarlsonj  * compute_points_v4(): compute the number of "points" for a given v4 offer.
457d04ccbb3Scarlsonj  *
458d04ccbb3Scarlsonj  *   input: const PKT_LIST *: packet to inspect
459d04ccbb3Scarlsonj  *	    const dhcp_smach_t *: state machine that received the packet
460d04ccbb3Scarlsonj  *  output: int: -1 to discard, >=0 for preference.
461d04ccbb3Scarlsonj  */
462d04ccbb3Scarlsonj 
463d04ccbb3Scarlsonj static int
compute_points_v4(const PKT_LIST * pkt)464d04ccbb3Scarlsonj compute_points_v4(const PKT_LIST *pkt)
465d04ccbb3Scarlsonj {
466d04ccbb3Scarlsonj 	int points = 0;
467d04ccbb3Scarlsonj 
468d04ccbb3Scarlsonj 	if (pkt->opts[CD_DHCP_TYPE] == NULL) {
469d04ccbb3Scarlsonj 		dhcpmsg(MSG_VERBOSE, "compute_points_v4: valid BOOTP reply");
470d04ccbb3Scarlsonj 		goto valid_offer;
471d04ccbb3Scarlsonj 	}
472d04ccbb3Scarlsonj 
473d04ccbb3Scarlsonj 	if (pkt->opts[CD_LEASE_TIME] == NULL) {
474d04ccbb3Scarlsonj 		dhcpmsg(MSG_WARNING, "compute_points_v4: OFFER without lease "
475d04ccbb3Scarlsonj 		    "time");
476d04ccbb3Scarlsonj 		return (-1);
477d04ccbb3Scarlsonj 	}
478d04ccbb3Scarlsonj 
479d04ccbb3Scarlsonj 	if (pkt->opts[CD_LEASE_TIME]->len != sizeof (lease_t)) {
480d04ccbb3Scarlsonj 		dhcpmsg(MSG_WARNING, "compute_points_v4: OFFER with garbled "
481d04ccbb3Scarlsonj 		    "lease time");
482d04ccbb3Scarlsonj 		return (-1);
483d04ccbb3Scarlsonj 	}
484d04ccbb3Scarlsonj 
485d04ccbb3Scarlsonj 	if (pkt->opts[CD_SERVER_ID] == NULL) {
486d04ccbb3Scarlsonj 		dhcpmsg(MSG_WARNING, "compute_points_v4: OFFER without server "
487d04ccbb3Scarlsonj 		    "id");
488d04ccbb3Scarlsonj 		return (-1);
489d04ccbb3Scarlsonj 	}
490d04ccbb3Scarlsonj 
491d04ccbb3Scarlsonj 	if (pkt->opts[CD_SERVER_ID]->len != sizeof (ipaddr_t)) {
492d04ccbb3Scarlsonj 		dhcpmsg(MSG_WARNING, "compute_points_v4: OFFER with garbled "
493d04ccbb3Scarlsonj 		    "server id");
494d04ccbb3Scarlsonj 		return (-1);
495d04ccbb3Scarlsonj 	}
496d04ccbb3Scarlsonj 
497d04ccbb3Scarlsonj 	/* valid DHCP OFFER.  see if we got our parameters. */
498d04ccbb3Scarlsonj 	dhcpmsg(MSG_VERBOSE, "compute_points_v4: valid OFFER packet");
499d04ccbb3Scarlsonj 	points += 30;
5007c478bd9Sstevel@tonic-gate 
501d04ccbb3Scarlsonj valid_offer:
502d04ccbb3Scarlsonj 	if (pkt->rfc1048)
503d04ccbb3Scarlsonj 		points += 5;
5047c478bd9Sstevel@tonic-gate 
5057c478bd9Sstevel@tonic-gate 	/*
506d04ccbb3Scarlsonj 	 * also could be faked, though more difficult because the encapsulation
507d04ccbb3Scarlsonj 	 * is hard to encode on a BOOTP server; plus there's not as much real
508d04ccbb3Scarlsonj 	 * estate in the packet for options, so it's likely this option would
509d04ccbb3Scarlsonj 	 * get dropped.
5107c478bd9Sstevel@tonic-gate 	 */
5117c478bd9Sstevel@tonic-gate 
512d04ccbb3Scarlsonj 	if (pkt->opts[CD_VENDOR_SPEC] != NULL)
513d04ccbb3Scarlsonj 		points += 80;
5147c478bd9Sstevel@tonic-gate 
515d04ccbb3Scarlsonj 	if (pkt->opts[CD_SUBNETMASK] != NULL)
516d04ccbb3Scarlsonj 		points++;
5177c478bd9Sstevel@tonic-gate 
518d04ccbb3Scarlsonj 	if (pkt->opts[CD_ROUTER] != NULL)
519d04ccbb3Scarlsonj 		points++;
5207c478bd9Sstevel@tonic-gate 
521d04ccbb3Scarlsonj 	if (pkt->opts[CD_HOSTNAME] != NULL)
522d04ccbb3Scarlsonj 		points += 5;
523d04ccbb3Scarlsonj 
524d04ccbb3Scarlsonj 	return (points);
5257c478bd9Sstevel@tonic-gate }
5267c478bd9Sstevel@tonic-gate 
5277c478bd9Sstevel@tonic-gate /*
528d04ccbb3Scarlsonj  * select_best(): selects the best offer from a list of IPv4 OFFER packets or
529d04ccbb3Scarlsonj  *		  DHCPv6 Advertise packets.
5307c478bd9Sstevel@tonic-gate  *
531d04ccbb3Scarlsonj  *   input: dhcp_smach_t *: state machine with enqueued offers
5327c478bd9Sstevel@tonic-gate  *  output: PKT_LIST *: the best packet, or NULL if none are acceptable
5337c478bd9Sstevel@tonic-gate  */
5347c478bd9Sstevel@tonic-gate 
5357c478bd9Sstevel@tonic-gate static PKT_LIST *
select_best(dhcp_smach_t * dsmp)536d04ccbb3Scarlsonj select_best(dhcp_smach_t *dsmp)
5377c478bd9Sstevel@tonic-gate {
538d04ccbb3Scarlsonj 	PKT_LIST	*current = dsmp->dsm_recv_pkt_list;
539d04ccbb3Scarlsonj 	PKT_LIST	*next, *best = NULL;
540d04ccbb3Scarlsonj 	int		points, best_points = -1;
5417c478bd9Sstevel@tonic-gate 
5427c478bd9Sstevel@tonic-gate 	/*
5437c478bd9Sstevel@tonic-gate 	 * pick out the best offer.  point system.
544d04ccbb3Scarlsonj 	 * what's important for IPv4?
5457c478bd9Sstevel@tonic-gate 	 *
546d04ccbb3Scarlsonj 	 *	0) DHCP (30 points)
5477c478bd9Sstevel@tonic-gate 	 *	1) no option overload
548d04ccbb3Scarlsonj 	 *	2) encapsulated vendor option (80 points)
5497c478bd9Sstevel@tonic-gate 	 *	3) non-null sname and siaddr fields
5507c478bd9Sstevel@tonic-gate 	 *	4) non-null file field
551d04ccbb3Scarlsonj 	 *	5) hostname (5 points)
552d04ccbb3Scarlsonj 	 *	6) subnetmask (1 point)
553d04ccbb3Scarlsonj 	 *	7) router (1 point)
5547c478bd9Sstevel@tonic-gate 	 */
5557c478bd9Sstevel@tonic-gate 
556d04ccbb3Scarlsonj 	for (; current != NULL; current = next) {
557d04ccbb3Scarlsonj 		next = current->next;
5587c478bd9Sstevel@tonic-gate 
559d04ccbb3Scarlsonj 		points = current->isv6 ?
560d04ccbb3Scarlsonj 		    compute_points_v6(current, dsmp) :
561d04ccbb3Scarlsonj 		    compute_points_v4(current);
5627c478bd9Sstevel@tonic-gate 
563d04ccbb3Scarlsonj 		/*
564d04ccbb3Scarlsonj 		 * Just discard any unacceptable entries we encounter.
565d04ccbb3Scarlsonj 		 */
566d04ccbb3Scarlsonj 		if (points == -1) {
567d04ccbb3Scarlsonj 			remque(current);
568d04ccbb3Scarlsonj 			free_pkt_entry(current);
5697c478bd9Sstevel@tonic-gate 			continue;
5707c478bd9Sstevel@tonic-gate 		}
5717c478bd9Sstevel@tonic-gate 
572d04ccbb3Scarlsonj 		dhcpmsg(MSG_DEBUG, "select_best: OFFER had %d points", points);
5737c478bd9Sstevel@tonic-gate 
574d04ccbb3Scarlsonj 		/* Special case: stop now and select */
575d04ccbb3Scarlsonj 		if (points == -2) {
576d04ccbb3Scarlsonj 			best = current;
577d04ccbb3Scarlsonj 			break;
5787c478bd9Sstevel@tonic-gate 		}
5797c478bd9Sstevel@tonic-gate 
5807c478bd9Sstevel@tonic-gate 		if (points >= best_points) {
5817c478bd9Sstevel@tonic-gate 			best_points = points;
5827c478bd9Sstevel@tonic-gate 			best = current;
5837c478bd9Sstevel@tonic-gate 		}
5847c478bd9Sstevel@tonic-gate 	}
5857c478bd9Sstevel@tonic-gate 
5867c478bd9Sstevel@tonic-gate 	if (best != NULL) {
5877c478bd9Sstevel@tonic-gate 		dhcpmsg(MSG_DEBUG, "select_best: most points: %d", best_points);
588d04ccbb3Scarlsonj 		remque(best);
589d04ccbb3Scarlsonj 	} else {
5907c478bd9Sstevel@tonic-gate 		dhcpmsg(MSG_DEBUG, "select_best: no valid OFFER/BOOTP reply");
591d04ccbb3Scarlsonj 	}
5927c478bd9Sstevel@tonic-gate 
5937c478bd9Sstevel@tonic-gate 	return (best);
5947c478bd9Sstevel@tonic-gate }
5957c478bd9Sstevel@tonic-gate 
5967c478bd9Sstevel@tonic-gate /*
597d04ccbb3Scarlsonj  * accept_v4_acknak(): determine what to do with a DHCPv4 ACK/NAK based on the
598d04ccbb3Scarlsonj  *		       current state.  If we're renewing or rebinding, the ACK
599d04ccbb3Scarlsonj  *		       must be for the same address and must have a new lease
600d04ccbb3Scarlsonj  *		       time.  If it's a NAK, then our cache is garbage, and we
601d04ccbb3Scarlsonj  *		       must restart.  Finally, call dhcp_bound on accepted
602d04ccbb3Scarlsonj  *		       ACKs.
6037c478bd9Sstevel@tonic-gate  *
604d04ccbb3Scarlsonj  *   input: dhcp_smach_t *: the state machine to handle the ACK/NAK
605d04ccbb3Scarlsonj  *	    PKT_LIST *: the ACK/NAK message
6067c478bd9Sstevel@tonic-gate  *  output: void
6077c478bd9Sstevel@tonic-gate  */
6087c478bd9Sstevel@tonic-gate 
609d04ccbb3Scarlsonj static void
accept_v4_acknak(dhcp_smach_t * dsmp,PKT_LIST * plp)610d04ccbb3Scarlsonj accept_v4_acknak(dhcp_smach_t *dsmp, PKT_LIST *plp)
6117c478bd9Sstevel@tonic-gate {
612906cb642Scarlsonj 	/* Account for received and processed messages */
613906cb642Scarlsonj 	dsmp->dsm_received++;
614906cb642Scarlsonj 
6157c478bd9Sstevel@tonic-gate 	if (*plp->opts[CD_DHCP_TYPE]->value == ACK) {
616cfb9c9abScarlsonj 		if (dsmp->dsm_state != INFORM_SENT &&
617cfb9c9abScarlsonj 		    dsmp->dsm_state != INFORMATION &&
618cfb9c9abScarlsonj 		    (plp->opts[CD_LEASE_TIME] == NULL ||
619cfb9c9abScarlsonj 		    plp->opts[CD_LEASE_TIME]->len != sizeof (lease_t))) {
620d04ccbb3Scarlsonj 			dhcpmsg(MSG_WARNING, "accept_v4_acknak: ACK packet on "
621d04ccbb3Scarlsonj 			    "%s missing mandatory lease option, ignored",
622d04ccbb3Scarlsonj 			    dsmp->dsm_name);
623d04ccbb3Scarlsonj 			dsmp->dsm_bad_offers++;
624d04ccbb3Scarlsonj 			free_pkt_entry(plp);
6257c478bd9Sstevel@tonic-gate 			return;
6267c478bd9Sstevel@tonic-gate 		}
627d04ccbb3Scarlsonj 		if ((dsmp->dsm_state == RENEWING ||
628d04ccbb3Scarlsonj 		    dsmp->dsm_state == REBINDING) &&
629d04ccbb3Scarlsonj 		    dsmp->dsm_leases->dl_lifs->lif_addr !=
630d04ccbb3Scarlsonj 		    plp->pkt->yiaddr.s_addr) {
631d04ccbb3Scarlsonj 			dhcpmsg(MSG_WARNING, "accept_v4_acknak: renewal ACK "
632d04ccbb3Scarlsonj 			    "packet has a different IP address (%s), ignored",
633d04ccbb3Scarlsonj 			    inet_ntoa(plp->pkt->yiaddr));
634d04ccbb3Scarlsonj 			dsmp->dsm_bad_offers++;
635d04ccbb3Scarlsonj 			free_pkt_entry(plp);
6367c478bd9Sstevel@tonic-gate 			return;
6377c478bd9Sstevel@tonic-gate 		}
6387c478bd9Sstevel@tonic-gate 	}
6397c478bd9Sstevel@tonic-gate 
6407c478bd9Sstevel@tonic-gate 	/*
6417c478bd9Sstevel@tonic-gate 	 * looks good; cancel the retransmission timer and unregister
6427c478bd9Sstevel@tonic-gate 	 * the acknak handler. ACK to BOUND, NAK back to SELECTING.
6437c478bd9Sstevel@tonic-gate 	 */
6447c478bd9Sstevel@tonic-gate 
645d04ccbb3Scarlsonj 	stop_pkt_retransmission(dsmp);
6467c478bd9Sstevel@tonic-gate 
647d04ccbb3Scarlsonj 	if (*plp->opts[CD_DHCP_TYPE]->value == NAK) {
648d04ccbb3Scarlsonj 		dhcpmsg(MSG_WARNING, "accept_v4_acknak: NAK on interface %s",
649d04ccbb3Scarlsonj 		    dsmp->dsm_name);
650d04ccbb3Scarlsonj 		dsmp->dsm_bad_offers++;
651d04ccbb3Scarlsonj 		free_pkt_entry(plp);
652d04ccbb3Scarlsonj 		dhcp_restart(dsmp);
6537c478bd9Sstevel@tonic-gate 
6547c478bd9Sstevel@tonic-gate 		/*
6557c478bd9Sstevel@tonic-gate 		 * remove any bogus cached configuration we might have
6567c478bd9Sstevel@tonic-gate 		 * around (right now would only happen if we got here
6577c478bd9Sstevel@tonic-gate 		 * from INIT_REBOOT).
6587c478bd9Sstevel@tonic-gate 		 */
6597c478bd9Sstevel@tonic-gate 
660d04ccbb3Scarlsonj 		(void) remove_hostconf(dsmp->dsm_name, dsmp->dsm_isv6);
6617c478bd9Sstevel@tonic-gate 		return;
6627c478bd9Sstevel@tonic-gate 	}
6637c478bd9Sstevel@tonic-gate 
6647c478bd9Sstevel@tonic-gate 	if (plp->opts[CD_SERVER_ID] == NULL ||
6657c478bd9Sstevel@tonic-gate 	    plp->opts[CD_SERVER_ID]->len != sizeof (ipaddr_t)) {
666d04ccbb3Scarlsonj 		dhcpmsg(MSG_ERROR, "accept_v4_acknak: ACK with no valid "
667906cb642Scarlsonj 		    "server id on %s", dsmp->dsm_name);
668d04ccbb3Scarlsonj 		dsmp->dsm_bad_offers++;
669d04ccbb3Scarlsonj 		free_pkt_entry(plp);
670d04ccbb3Scarlsonj 		dhcp_restart(dsmp);
6717c478bd9Sstevel@tonic-gate 		return;
6727c478bd9Sstevel@tonic-gate 	}
6737c478bd9Sstevel@tonic-gate 
674d04ccbb3Scarlsonj 	if (plp->opts[CD_MESSAGE] != NULL) {
675d04ccbb3Scarlsonj 		print_server_msg(dsmp, (char *)plp->opts[CD_MESSAGE]->value,
676d04ccbb3Scarlsonj 		    plp->opts[CD_MESSAGE]->len);
677d04ccbb3Scarlsonj 	}
678d04ccbb3Scarlsonj 
679d04ccbb3Scarlsonj 	dhcpmsg(MSG_VERBOSE, "accept_v4_acknak: ACK on %s", dsmp->dsm_name);
680d04ccbb3Scarlsonj 	if (!dhcp_bound(dsmp, plp)) {
681d04ccbb3Scarlsonj 		dhcpmsg(MSG_WARNING, "accept_v4_acknak: dhcp_bound failed "
682d04ccbb3Scarlsonj 		    "for %s", dsmp->dsm_name);
683d04ccbb3Scarlsonj 		dhcp_restart(dsmp);
684d04ccbb3Scarlsonj 	}
685d04ccbb3Scarlsonj }
686d04ccbb3Scarlsonj 
687d04ccbb3Scarlsonj /*
688d04ccbb3Scarlsonj  * accept_v6_message(): determine what to do with a DHCPv6 message based on the
689d04ccbb3Scarlsonj  *			current state.
690d04ccbb3Scarlsonj  *
691d04ccbb3Scarlsonj  *   input: dhcp_smach_t *: the state machine to handle the message
692d04ccbb3Scarlsonj  *	    PKT_LIST *: the DHCPv6 message
693d04ccbb3Scarlsonj  *	    const char *: type of message (for logging)
694d04ccbb3Scarlsonj  *	    uchar_t: type of message (extracted from packet)
695d04ccbb3Scarlsonj  *  output: void
696d04ccbb3Scarlsonj  */
697d04ccbb3Scarlsonj 
698d04ccbb3Scarlsonj static void
accept_v6_message(dhcp_smach_t * dsmp,PKT_LIST * plp,const char * pname,uchar_t recv_type)699d04ccbb3Scarlsonj accept_v6_message(dhcp_smach_t *dsmp, PKT_LIST *plp, const char *pname,
700d04ccbb3Scarlsonj     uchar_t recv_type)
701d04ccbb3Scarlsonj {
702d04ccbb3Scarlsonj 	const dhcpv6_option_t *d6o;
703d04ccbb3Scarlsonj 	uint_t olen;
704d04ccbb3Scarlsonj 	const char *estr, *msg;
705d04ccbb3Scarlsonj 	uint_t msglen;
706d04ccbb3Scarlsonj 	int status;
7077c478bd9Sstevel@tonic-gate 
708906cb642Scarlsonj 	/* Account for received and processed messages */
709906cb642Scarlsonj 	dsmp->dsm_received++;
710906cb642Scarlsonj 
711906cb642Scarlsonj 	/* We don't yet support Reconfigure at all. */
712906cb642Scarlsonj 	if (recv_type == DHCPV6_MSG_RECONFIGURE) {
713906cb642Scarlsonj 		dhcpmsg(MSG_VERBOSE, "accept_v6_message: ignored Reconfigure "
714906cb642Scarlsonj 		    "on %s", dsmp->dsm_name);
715906cb642Scarlsonj 		free_pkt_entry(plp);
716906cb642Scarlsonj 		return;
717906cb642Scarlsonj 	}
718906cb642Scarlsonj 
719d04ccbb3Scarlsonj 	/*
720d04ccbb3Scarlsonj 	 * All valid DHCPv6 messages must have our Client ID specified.
721d04ccbb3Scarlsonj 	 */
722d04ccbb3Scarlsonj 	d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_CLIENTID, &olen);
723d04ccbb3Scarlsonj 	olen -= sizeof (*d6o);
724d04ccbb3Scarlsonj 	if (d6o == NULL || olen != dsmp->dsm_cidlen ||
725d04ccbb3Scarlsonj 	    memcmp(d6o + 1, dsmp->dsm_cid, olen) != 0) {
726d04ccbb3Scarlsonj 		dhcpmsg(MSG_VERBOSE,
727d04ccbb3Scarlsonj 		    "accept_v6_message: discarded %s on %s: %s Client ID",
728d04ccbb3Scarlsonj 		    pname, dsmp->dsm_name, d6o == NULL ? "no" : "wrong");
729d04ccbb3Scarlsonj 		free_pkt_entry(plp);
7307c478bd9Sstevel@tonic-gate 		return;
7317c478bd9Sstevel@tonic-gate 	}
7327c478bd9Sstevel@tonic-gate 
733d04ccbb3Scarlsonj 	/*
734d04ccbb3Scarlsonj 	 * All valid DHCPv6 messages must have a Server ID specified.
735d04ccbb3Scarlsonj 	 *
736d04ccbb3Scarlsonj 	 * If this is a Reply and it's not in response to Solicit, Confirm,
737d04ccbb3Scarlsonj 	 * Rebind, or Information-Request, then it must also match the Server
738d04ccbb3Scarlsonj 	 * ID we're expecting.
739d04ccbb3Scarlsonj 	 *
740d04ccbb3Scarlsonj 	 * For Reply in the Solicit, Confirm, Rebind, and Information-Request
741d04ccbb3Scarlsonj 	 * cases, the Server ID needs to be saved.  This is done inside of
742d04ccbb3Scarlsonj 	 * dhcp_bound().
743d04ccbb3Scarlsonj 	 */
744d04ccbb3Scarlsonj 	d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_SERVERID, &olen);
745d04ccbb3Scarlsonj 	if (d6o == NULL) {
746d04ccbb3Scarlsonj 		dhcpmsg(MSG_DEBUG,
747d04ccbb3Scarlsonj 		    "accept_v6_message: discarded %s on %s: no Server ID",
748d04ccbb3Scarlsonj 		    pname, dsmp->dsm_name);
749d04ccbb3Scarlsonj 		free_pkt_entry(plp);
750d04ccbb3Scarlsonj 		return;
751d04ccbb3Scarlsonj 	}
752d04ccbb3Scarlsonj 	if (recv_type == DHCPV6_MSG_REPLY && dsmp->dsm_state != SELECTING &&
753d04ccbb3Scarlsonj 	    dsmp->dsm_state != INIT_REBOOT && dsmp->dsm_state != REBINDING &&
754d04ccbb3Scarlsonj 	    dsmp->dsm_state != INFORM_SENT) {
755d04ccbb3Scarlsonj 		olen -= sizeof (*d6o);
756d04ccbb3Scarlsonj 		if (olen != dsmp->dsm_serveridlen ||
757d04ccbb3Scarlsonj 		    memcmp(d6o + 1, dsmp->dsm_serverid, olen) != 0) {
758d04ccbb3Scarlsonj 			dhcpmsg(MSG_DEBUG, "accept_v6_message: discarded %s on "
759d04ccbb3Scarlsonj 			    "%s: wrong Server ID", pname, dsmp->dsm_name);
760d04ccbb3Scarlsonj 			free_pkt_entry(plp);
761d04ccbb3Scarlsonj 			return;
762d04ccbb3Scarlsonj 		}
763d04ccbb3Scarlsonj 	}
764d04ccbb3Scarlsonj 
765d04ccbb3Scarlsonj 	/*
766d04ccbb3Scarlsonj 	 * Break out of the switch if the input message needs to be discarded.
767d04ccbb3Scarlsonj 	 * Return from the function if the message has been enqueued or
768d04ccbb3Scarlsonj 	 * consumed.
769d04ccbb3Scarlsonj 	 */
770d04ccbb3Scarlsonj 	switch (dsmp->dsm_state) {
771d04ccbb3Scarlsonj 	case SELECTING:
772d04ccbb3Scarlsonj 		/* A Reply message signifies a Rapid-Commit. */
773d04ccbb3Scarlsonj 		if (recv_type == DHCPV6_MSG_REPLY) {
774d04ccbb3Scarlsonj 			if (dhcpv6_pkt_option(plp, NULL,
775d04ccbb3Scarlsonj 			    DHCPV6_OPT_RAPID_COMMIT, &olen) == NULL) {
776d04ccbb3Scarlsonj 				dhcpmsg(MSG_DEBUG, "accept_v6_message: Reply "
777d04ccbb3Scarlsonj 				    "on %s lacks Rapid-Commit; ignoring",
778d04ccbb3Scarlsonj 				    dsmp->dsm_name);
779d04ccbb3Scarlsonj 				break;
780d04ccbb3Scarlsonj 			}
781d04ccbb3Scarlsonj 			dhcpmsg(MSG_VERBOSE,
782d04ccbb3Scarlsonj 			    "accept_v6_message: rapid-commit Reply on %s",
783d04ccbb3Scarlsonj 			    dsmp->dsm_name);
784d04ccbb3Scarlsonj 			cancel_offer_timer(dsmp);
785d04ccbb3Scarlsonj 			goto rapid_commit;
786d04ccbb3Scarlsonj 		}
787d04ccbb3Scarlsonj 
788d04ccbb3Scarlsonj 		/* Otherwise, we're looking for Advertisements. */
789d04ccbb3Scarlsonj 		if (recv_type != DHCPV6_MSG_ADVERTISE)
790d04ccbb3Scarlsonj 			break;
791d04ccbb3Scarlsonj 
792d04ccbb3Scarlsonj 		/*
793d04ccbb3Scarlsonj 		 * Special case: if this advertisement has preference 255, then
794d04ccbb3Scarlsonj 		 * we must stop right now and select this server.
795d04ccbb3Scarlsonj 		 */
796d04ccbb3Scarlsonj 		d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_PREFERENCE,
797d04ccbb3Scarlsonj 		    &olen);
798d04ccbb3Scarlsonj 		if (d6o != NULL && olen == sizeof (*d6o) + 1 &&
799d04ccbb3Scarlsonj 		    *(const uchar_t *)(d6o + 1) == 255) {
800d04ccbb3Scarlsonj 			pkt_smach_enqueue(dsmp, plp);
801d04ccbb3Scarlsonj 			dhcpmsg(MSG_DEBUG, "accept_v6_message: preference 255;"
802d04ccbb3Scarlsonj 			    " immediate Request on %s", dsmp->dsm_name);
803d04ccbb3Scarlsonj 			dhcp_requesting(NULL, dsmp);
804d04ccbb3Scarlsonj 		} else {
805d04ccbb3Scarlsonj 			pkt_smach_enqueue(dsmp, plp);
806d04ccbb3Scarlsonj 		}
807d04ccbb3Scarlsonj 		return;
808d04ccbb3Scarlsonj 
809d04ccbb3Scarlsonj 	case PRE_BOUND:
810d04ccbb3Scarlsonj 	case BOUND:
811d04ccbb3Scarlsonj 		/*
812d04ccbb3Scarlsonj 		 * Not looking for anything in these states.  (If we
813d04ccbb3Scarlsonj 		 * implemented reconfigure, that might go here.)
814d04ccbb3Scarlsonj 		 */
815d04ccbb3Scarlsonj 		break;
816d04ccbb3Scarlsonj 
817d04ccbb3Scarlsonj 	case REQUESTING:
818d04ccbb3Scarlsonj 	case INIT_REBOOT:
819d04ccbb3Scarlsonj 	case RENEWING:
820d04ccbb3Scarlsonj 	case REBINDING:
821d04ccbb3Scarlsonj 	case INFORM_SENT:
822d04ccbb3Scarlsonj 		/*
823d04ccbb3Scarlsonj 		 * We're looking for Reply messages.
824d04ccbb3Scarlsonj 		 */
825d04ccbb3Scarlsonj 		if (recv_type != DHCPV6_MSG_REPLY)
826d04ccbb3Scarlsonj 			break;
827d04ccbb3Scarlsonj 		dhcpmsg(MSG_VERBOSE,
828d04ccbb3Scarlsonj 		    "accept_v6_message: received Reply message on %s",
829d04ccbb3Scarlsonj 		    dsmp->dsm_name);
830d04ccbb3Scarlsonj 	rapid_commit:
831d04ccbb3Scarlsonj 		/*
832d04ccbb3Scarlsonj 		 * Extract the status code option.  If one is present and the
833d04ccbb3Scarlsonj 		 * request failed, then try to go to another advertisement in
834d04ccbb3Scarlsonj 		 * the list or restart the selection machinery.
835d04ccbb3Scarlsonj 		 */
836d04ccbb3Scarlsonj 		d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_STATUS_CODE,
837d04ccbb3Scarlsonj 		    &olen);
838d04ccbb3Scarlsonj 		status = dhcpv6_status_code(d6o, olen, &estr, &msg, &msglen);
839d04ccbb3Scarlsonj 		/*
840d04ccbb3Scarlsonj 		 * Check for the UseMulticast status code.  If this is present,
841d04ccbb3Scarlsonj 		 * and if we were actually using unicast, then drop back and
842d04ccbb3Scarlsonj 		 * try again.  If we weren't using unicast, then just pretend
843d04ccbb3Scarlsonj 		 * we never saw this message -- the peer is confused.  (TAHI
844d04ccbb3Scarlsonj 		 * does this.)
845d04ccbb3Scarlsonj 		 */
846d04ccbb3Scarlsonj 		if (status == DHCPV6_STAT_USEMCAST) {
847d04ccbb3Scarlsonj 			if (IN6_IS_ADDR_MULTICAST(
848d04ccbb3Scarlsonj 			    &dsmp->dsm_send_dest.v6.sin6_addr)) {
849d04ccbb3Scarlsonj 				break;
850d04ccbb3Scarlsonj 			} else {
851d04ccbb3Scarlsonj 				free_pkt_entry(plp);
852d04ccbb3Scarlsonj 				dsmp->dsm_send_dest.v6.sin6_addr =
853d04ccbb3Scarlsonj 				    ipv6_all_dhcp_relay_and_servers;
854d04ccbb3Scarlsonj 				retransmit_now(dsmp);
855d04ccbb3Scarlsonj 				return;
856d04ccbb3Scarlsonj 			}
857d04ccbb3Scarlsonj 		}
858d04ccbb3Scarlsonj 		print_server_msg(dsmp, msg, msglen);
859d04ccbb3Scarlsonj 		/*
860d04ccbb3Scarlsonj 		 * We treat NoBinding at the top level as "success."  Granted,
861d04ccbb3Scarlsonj 		 * this doesn't make much sense, but the TAHI test suite does
862d04ccbb3Scarlsonj 		 * this.  NoBinding really only makes sense in the context of a
863d04ccbb3Scarlsonj 		 * specific IA, as it refers to the GUID:IAID binding, so
864d04ccbb3Scarlsonj 		 * ignoring it at the top level is safe.
865d04ccbb3Scarlsonj 		 */
866d04ccbb3Scarlsonj 		if (status == DHCPV6_STAT_SUCCESS ||
867d04ccbb3Scarlsonj 		    status == DHCPV6_STAT_NOBINDING) {
868d04ccbb3Scarlsonj 			if (dhcp_bound(dsmp, plp)) {
869d04ccbb3Scarlsonj 				/*
870d04ccbb3Scarlsonj 				 * dhcp_bound will stop retransmission on
871d04ccbb3Scarlsonj 				 * success, if that's called for.
872d04ccbb3Scarlsonj 				 */
873d04ccbb3Scarlsonj 				server_unicast_option(dsmp, plp);
874d04ccbb3Scarlsonj 			} else {
875d04ccbb3Scarlsonj 				stop_pkt_retransmission(dsmp);
876d04ccbb3Scarlsonj 				dhcpmsg(MSG_WARNING, "accept_v6_message: "
877d04ccbb3Scarlsonj 				    "dhcp_bound failed for %s", dsmp->dsm_name);
878d04ccbb3Scarlsonj 				(void) remove_hostconf(dsmp->dsm_name,
879d04ccbb3Scarlsonj 				    dsmp->dsm_isv6);
880906cb642Scarlsonj 				dhcp_restart(dsmp);
881d04ccbb3Scarlsonj 			}
882d04ccbb3Scarlsonj 		} else {
883d04ccbb3Scarlsonj 			dhcpmsg(MSG_WARNING, "accept_v6_message: Reply: %s",
884d04ccbb3Scarlsonj 			    estr);
885d04ccbb3Scarlsonj 			stop_pkt_retransmission(dsmp);
886d04ccbb3Scarlsonj 			free_pkt_entry(plp);
887d04ccbb3Scarlsonj 			if (dsmp->dsm_state == INFORM_SENT) {
888d04ccbb3Scarlsonj 				(void) set_smach_state(dsmp, INIT);
889d04ccbb3Scarlsonj 				ipc_action_finish(dsmp, DHCP_IPC_E_SRVFAILED);
890d04ccbb3Scarlsonj 			} else {
891d04ccbb3Scarlsonj 				(void) remove_hostconf(dsmp->dsm_name,
892d04ccbb3Scarlsonj 				    dsmp->dsm_isv6);
893d04ccbb3Scarlsonj 				request_failed(dsmp);
894d04ccbb3Scarlsonj 			}
895d04ccbb3Scarlsonj 		}
896d04ccbb3Scarlsonj 		return;
897d04ccbb3Scarlsonj 
898d04ccbb3Scarlsonj 	case DECLINING:
899d04ccbb3Scarlsonj 		/*
900d04ccbb3Scarlsonj 		 * We're looking for Reply messages.
901d04ccbb3Scarlsonj 		 */
902d04ccbb3Scarlsonj 		if (recv_type != DHCPV6_MSG_REPLY)
903d04ccbb3Scarlsonj 			break;
904d04ccbb3Scarlsonj 		stop_pkt_retransmission(dsmp);
905d04ccbb3Scarlsonj 		/*
906d04ccbb3Scarlsonj 		 * Extract the status code option.  Note that it's not a
907d04ccbb3Scarlsonj 		 * failure if the server reports an error.
908d04ccbb3Scarlsonj 		 */
909d04ccbb3Scarlsonj 		d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_STATUS_CODE,
910d04ccbb3Scarlsonj 		    &olen);
911d04ccbb3Scarlsonj 		if (dhcpv6_status_code(d6o, olen, &estr, &msg,
912d04ccbb3Scarlsonj 		    &msglen) == DHCPV6_STAT_SUCCESS) {
913d04ccbb3Scarlsonj 			print_server_msg(dsmp, msg, msglen);
914d04ccbb3Scarlsonj 		} else {
915d04ccbb3Scarlsonj 			dhcpmsg(MSG_WARNING, "accept_v6_message: Reply: %s",
916d04ccbb3Scarlsonj 			    estr);
917d04ccbb3Scarlsonj 		}
918d04ccbb3Scarlsonj 		free_pkt_entry(plp);
919d04ccbb3Scarlsonj 		if (dsmp->dsm_leases == NULL) {
920d04ccbb3Scarlsonj 			dhcpmsg(MSG_VERBOSE, "accept_v6_message: %s has no "
921906cb642Scarlsonj 			    "leases left", dsmp->dsm_name);
922d04ccbb3Scarlsonj 			dhcp_restart(dsmp);
923d04ccbb3Scarlsonj 		} else if (dsmp->dsm_lif_wait == 0) {
924d04ccbb3Scarlsonj 			(void) set_smach_state(dsmp, BOUND);
925d04ccbb3Scarlsonj 		} else {
926d04ccbb3Scarlsonj 			(void) set_smach_state(dsmp, PRE_BOUND);
927d04ccbb3Scarlsonj 		}
928d04ccbb3Scarlsonj 		return;
929d04ccbb3Scarlsonj 
930d04ccbb3Scarlsonj 	case RELEASING:
931d04ccbb3Scarlsonj 		/*
932d04ccbb3Scarlsonj 		 * We're looking for Reply messages.
933d04ccbb3Scarlsonj 		 */
934d04ccbb3Scarlsonj 		if (recv_type != DHCPV6_MSG_REPLY)
935d04ccbb3Scarlsonj 			break;
936d04ccbb3Scarlsonj 		stop_pkt_retransmission(dsmp);
937d04ccbb3Scarlsonj 		/*
938d04ccbb3Scarlsonj 		 * Extract the status code option.
939d04ccbb3Scarlsonj 		 */
940d04ccbb3Scarlsonj 		d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_STATUS_CODE,
941d04ccbb3Scarlsonj 		    &olen);
942d04ccbb3Scarlsonj 		if (dhcpv6_status_code(d6o, olen, &estr, &msg,
943d04ccbb3Scarlsonj 		    &msglen) == DHCPV6_STAT_SUCCESS) {
944d04ccbb3Scarlsonj 			print_server_msg(dsmp, msg, msglen);
945d04ccbb3Scarlsonj 		} else {
946d04ccbb3Scarlsonj 			dhcpmsg(MSG_WARNING, "accept_v6_message: Reply: %s",
947d04ccbb3Scarlsonj 			    estr);
948d04ccbb3Scarlsonj 		}
949d04ccbb3Scarlsonj 		free_pkt_entry(plp);
950d04ccbb3Scarlsonj 		finished_smach(dsmp, DHCP_IPC_SUCCESS);
951d04ccbb3Scarlsonj 		return;
952d04ccbb3Scarlsonj 	}
953d04ccbb3Scarlsonj 
954d04ccbb3Scarlsonj 	/*
955d04ccbb3Scarlsonj 	 * Break from above switch means that the message must be discarded.
956d04ccbb3Scarlsonj 	 */
957d04ccbb3Scarlsonj 	dhcpmsg(MSG_VERBOSE,
958d04ccbb3Scarlsonj 	    "accept_v6_message: discarded v6 %s on %s; state %s",
959d04ccbb3Scarlsonj 	    pname, dsmp->dsm_name, dhcp_state_to_string(dsmp->dsm_state));
960d04ccbb3Scarlsonj 	free_pkt_entry(plp);
961d04ccbb3Scarlsonj }
962d04ccbb3Scarlsonj 
963d04ccbb3Scarlsonj /*
964e704a8f2Smeem  * dhcp_acknak_global(): Processes reception of an ACK or NAK packet on the
965d04ccbb3Scarlsonj  *			 global socket -- broadcast packets for IPv4, all
966d04ccbb3Scarlsonj  *			 packets for DHCPv6.
967d04ccbb3Scarlsonj  *
968d04ccbb3Scarlsonj  *   input: iu_eh_t *: unused
969d04ccbb3Scarlsonj  *	    int: the global file descriptor the ACK/NAK arrived on
970d04ccbb3Scarlsonj  *	    short: unused
971d04ccbb3Scarlsonj  *	    iu_event_id_t: unused
972d04ccbb3Scarlsonj  *	    void *: unused
973d04ccbb3Scarlsonj  *  output: void
974d04ccbb3Scarlsonj  */
975d04ccbb3Scarlsonj 
976d04ccbb3Scarlsonj /* ARGSUSED */
977d04ccbb3Scarlsonj void
dhcp_acknak_global(iu_eh_t * ehp,int fd,short events,iu_event_id_t id,void * arg)978e704a8f2Smeem dhcp_acknak_global(iu_eh_t *ehp, int fd, short events, iu_event_id_t id,
979d04ccbb3Scarlsonj     void *arg)
980d04ccbb3Scarlsonj {
981d04ccbb3Scarlsonj 	PKT_LIST	*plp;
982d04ccbb3Scarlsonj 	dhcp_pif_t	*pif;
983d04ccbb3Scarlsonj 	uchar_t		recv_type;
984d04ccbb3Scarlsonj 	const char	*pname;
985d04ccbb3Scarlsonj 	uint_t		xid;
986d04ccbb3Scarlsonj 	dhcp_smach_t	*dsmp;
987d04ccbb3Scarlsonj 	boolean_t	isv6 = (fd == v6_sock_fd);
988e704a8f2Smeem 	struct sockaddr_in sin;
989e704a8f2Smeem 	const char	*reason;
990e704a8f2Smeem 	size_t		sinlen = sizeof (sin);
991e704a8f2Smeem 	int		sock;
992d04ccbb3Scarlsonj 
993e704a8f2Smeem 	plp = recv_pkt(fd, get_max_mtu(isv6), isv6);
994948f2876Sss 	if (plp == NULL)
995d04ccbb3Scarlsonj 		return;
996d04ccbb3Scarlsonj 
997d04ccbb3Scarlsonj 	recv_type = pkt_recv_type(plp);
998d04ccbb3Scarlsonj 	pname = pkt_type_to_string(recv_type, isv6);
999d04ccbb3Scarlsonj 
1000d04ccbb3Scarlsonj 	/*
1001f7d61273Smeem 	 * Find the corresponding state machine and pif.
1002d04ccbb3Scarlsonj 	 *
1003d04ccbb3Scarlsonj 	 * Note that DHCPv6 Reconfigure would be special: it's not the reply to
1004d04ccbb3Scarlsonj 	 * any transaction, and thus we would need to search on transaction ID
1005e704a8f2Smeem 	 * zero (all state machines) to find the match.	 However, Reconfigure
1006d04ccbb3Scarlsonj 	 * is not yet supported.
1007d04ccbb3Scarlsonj 	 */
1008d04ccbb3Scarlsonj 	xid = pkt_get_xid(plp->pkt, isv6);
1009e704a8f2Smeem 
1010d04ccbb3Scarlsonj 	for (dsmp = lookup_smach_by_xid(xid, NULL, isv6); dsmp != NULL;
1011d04ccbb3Scarlsonj 	    dsmp = lookup_smach_by_xid(xid, dsmp, isv6)) {
1012f7d61273Smeem 		pif = dsmp->dsm_lif->lif_pif;
1013e11c3f44Smeem 		if (pif->pif_index == plp->ifindex ||
1014e11c3f44Smeem 		    pif->pif_under_ipmp && pif->pif_grindex == plp->ifindex)
1015d04ccbb3Scarlsonj 			break;
1016d04ccbb3Scarlsonj 	}
1017e704a8f2Smeem 
1018e704a8f2Smeem 	if (dsmp == NULL) {
1019f7d61273Smeem 		dhcpmsg(MSG_VERBOSE, "dhcp_acknak_global: ignored v%d %s packet"
1020f7d61273Smeem 		    " on ifindex %d: unknown state machine", isv6 ? 6 : 4,
1021f7d61273Smeem 		    pname, plp->ifindex);
1022f7d61273Smeem 		free_pkt_entry(plp);
1023f7d61273Smeem 		return;
1024f7d61273Smeem 	}
1025f7d61273Smeem 
1026f7d61273Smeem 	if (!isv6 && !pkt_v4_match(recv_type, DHCP_PACK|DHCP_PNAK)) {
1027f7d61273Smeem 		reason = "not ACK or NAK";
1028e704a8f2Smeem 		goto drop;
1029e704a8f2Smeem 	}
1030e704a8f2Smeem 
1031e704a8f2Smeem 	/*
1032e704a8f2Smeem 	 * For IPv4, most packets will be handled by dhcp_packet_lif().  The
1033e704a8f2Smeem 	 * only exceptions are broadcast packets sent when lif_sock_ip_fd has
1034e704a8f2Smeem 	 * bound to something other than INADDR_ANY.
1035e704a8f2Smeem 	 */
1036e704a8f2Smeem 	if (!isv6) {
1037e704a8f2Smeem 		sock = dsmp->dsm_lif->lif_sock_ip_fd;
1038e704a8f2Smeem 
1039e704a8f2Smeem 		if (getsockname(sock, (struct sockaddr *)&sin, &sinlen) != -1 &&
1040e704a8f2Smeem 		    sin.sin_addr.s_addr == INADDR_ANY) {
1041e704a8f2Smeem 			reason = "handled by lif_sock_ip_fd";
1042e704a8f2Smeem 			goto drop;
1043e704a8f2Smeem 		}
1044d04ccbb3Scarlsonj 	}
1045d04ccbb3Scarlsonj 
1046d04ccbb3Scarlsonj 	/*
1047d04ccbb3Scarlsonj 	 * We've got a packet; make sure it's acceptable and cancel the REQUEST
1048d04ccbb3Scarlsonj 	 * retransmissions.
1049d04ccbb3Scarlsonj 	 */
1050d04ccbb3Scarlsonj 	if (isv6)
1051d04ccbb3Scarlsonj 		accept_v6_message(dsmp, plp, pname, recv_type);
1052d04ccbb3Scarlsonj 	else
1053d04ccbb3Scarlsonj 		accept_v4_acknak(dsmp, plp);
1054e704a8f2Smeem 	return;
1055e704a8f2Smeem drop:
1056e704a8f2Smeem 	dhcpmsg(MSG_VERBOSE, "dhcp_acknak_global: ignored v%d %s packet for %s "
1057e704a8f2Smeem 	    "received on global socket: %s", isv6 ? 6 : 4, pname, pif->pif_name,
1058e704a8f2Smeem 	    reason);
1059e704a8f2Smeem 	free_pkt_entry(plp);
1060d04ccbb3Scarlsonj }
1061d04ccbb3Scarlsonj 
1062d04ccbb3Scarlsonj /*
1063d04ccbb3Scarlsonj  * request_failed(): Attempt to request an address has failed.  Take an
1064d04ccbb3Scarlsonj  *		     appropriate action.
1065d04ccbb3Scarlsonj  *
1066d04ccbb3Scarlsonj  *   input: dhcp_smach_t *: state machine that has failed
1067d04ccbb3Scarlsonj  *  output: void
1068d04ccbb3Scarlsonj  */
1069d04ccbb3Scarlsonj 
1070d04ccbb3Scarlsonj static void
request_failed(dhcp_smach_t * dsmp)1071d04ccbb3Scarlsonj request_failed(dhcp_smach_t *dsmp)
1072d04ccbb3Scarlsonj {
1073d04ccbb3Scarlsonj 	PKT_LIST *offer;
1074d04ccbb3Scarlsonj 
1075d04ccbb3Scarlsonj 	dsmp->dsm_server = ipv6_all_dhcp_relay_and_servers;
1076d04ccbb3Scarlsonj 	if ((offer = select_best(dsmp)) != NULL) {
1077d04ccbb3Scarlsonj 		insque(offer, &dsmp->dsm_recv_pkt_list);
1078d04ccbb3Scarlsonj 		dhcp_requesting(NULL, dsmp);
1079d04ccbb3Scarlsonj 	} else {
1080d04ccbb3Scarlsonj 		dhcpmsg(MSG_INFO, "no offers left on %s; restarting",
1081d04ccbb3Scarlsonj 		    dsmp->dsm_name);
1082d04ccbb3Scarlsonj 		dhcp_selecting(dsmp);
1083d04ccbb3Scarlsonj 	}
10847c478bd9Sstevel@tonic-gate }
10857c478bd9Sstevel@tonic-gate 
10867c478bd9Sstevel@tonic-gate /*
1087e704a8f2Smeem  * dhcp_packet_lif(): Processes reception of an ACK, NAK, or OFFER packet on
1088e704a8f2Smeem  *		      a given logical interface for IPv4 (only).
10897c478bd9Sstevel@tonic-gate  *
1090d04ccbb3Scarlsonj  *   input: iu_eh_t *: unused
1091e704a8f2Smeem  *	    int: the file descriptor the packet arrived on
1092d04ccbb3Scarlsonj  *	    short: unused
1093d04ccbb3Scarlsonj  *	    iu_event_id_t: the id of this event callback with the handler
1094d04ccbb3Scarlsonj  *	    void *: pointer to logical interface receiving message
10957c478bd9Sstevel@tonic-gate  *  output: void
10967c478bd9Sstevel@tonic-gate  */
10977c478bd9Sstevel@tonic-gate 
1098d04ccbb3Scarlsonj /* ARGSUSED */
109969bb4bb4Scarlsonj void
dhcp_packet_lif(iu_eh_t * ehp,int fd,short events,iu_event_id_t id,void * arg)1100e704a8f2Smeem dhcp_packet_lif(iu_eh_t *ehp, int fd, short events, iu_event_id_t id,
1101d04ccbb3Scarlsonj     void *arg)
11027c478bd9Sstevel@tonic-gate {
1103d04ccbb3Scarlsonj 	dhcp_lif_t	*lif = arg;
1104d04ccbb3Scarlsonj 	PKT_LIST	*plp;
1105d04ccbb3Scarlsonj 	uchar_t		recv_type;
1106d04ccbb3Scarlsonj 	const char	*pname;
1107d04ccbb3Scarlsonj 	uint_t		xid;
1108d04ccbb3Scarlsonj 	dhcp_smach_t	*dsmp;
1109d04ccbb3Scarlsonj 
1110e704a8f2Smeem 	if ((plp = recv_pkt(fd, lif->lif_max, B_FALSE)) == NULL)
1111d04ccbb3Scarlsonj 		return;
11127c478bd9Sstevel@tonic-gate 
1113d04ccbb3Scarlsonj 	recv_type = pkt_recv_type(plp);
1114d04ccbb3Scarlsonj 	pname = pkt_type_to_string(recv_type, B_FALSE);
11157c478bd9Sstevel@tonic-gate 
1116e704a8f2Smeem 	if (!pkt_v4_match(recv_type,
1117e704a8f2Smeem 	    DHCP_PACK | DHCP_PNAK | DHCP_PUNTYPED | DHCP_POFFER)) {
1118e704a8f2Smeem 		dhcpmsg(MSG_VERBOSE, "dhcp_packet_lif: ignored v4 %s packet "
1119d04ccbb3Scarlsonj 		    "received via LIF %s", pname, lif->lif_name);
1120d04ccbb3Scarlsonj 		free_pkt_entry(plp);
1121d04ccbb3Scarlsonj 		return;
1122d04ccbb3Scarlsonj 	}
11237c478bd9Sstevel@tonic-gate 
1124d04ccbb3Scarlsonj 	/*
1125e704a8f2Smeem 	 * Find the corresponding state machine.
1126d04ccbb3Scarlsonj 	 */
1127d04ccbb3Scarlsonj 	xid = pkt_get_xid(plp->pkt, B_FALSE);
1128d04ccbb3Scarlsonj 	for (dsmp = lookup_smach_by_xid(xid, NULL, B_FALSE); dsmp != NULL;
1129d04ccbb3Scarlsonj 	    dsmp = lookup_smach_by_xid(xid, dsmp, B_FALSE)) {
1130d04ccbb3Scarlsonj 		if (dsmp->dsm_lif == lif)
1131d04ccbb3Scarlsonj 			break;
1132d04ccbb3Scarlsonj 	}
1133d04ccbb3Scarlsonj 
1134e704a8f2Smeem 	if (dsmp == NULL)
1135e704a8f2Smeem 		goto drop;
1136e704a8f2Smeem 
1137e704a8f2Smeem 	if (pkt_v4_match(recv_type, DHCP_PACK|DHCP_PNAK)) {
1138e704a8f2Smeem 		/*
1139e704a8f2Smeem 		 * We've got an ACK/NAK; make sure it's acceptable and cancel
1140e704a8f2Smeem 		 * the REQUEST retransmissions.
1141e704a8f2Smeem 		 */
1142e704a8f2Smeem 		accept_v4_acknak(dsmp, plp);
1143e704a8f2Smeem 	} else {
1144e704a8f2Smeem 		if (is_bound_state(dsmp->dsm_state))
1145e704a8f2Smeem 			goto drop;
1146e704a8f2Smeem 		/*
1147e704a8f2Smeem 		 * Must be an OFFER or a BOOTP message: enqueue it for later
1148e704a8f2Smeem 		 * processing by select_best().
1149e704a8f2Smeem 		 */
1150e704a8f2Smeem 		pkt_smach_enqueue(dsmp, plp);
1151e704a8f2Smeem 	}
1152e704a8f2Smeem 	return;
1153e704a8f2Smeem drop:
1154e704a8f2Smeem 	dhcpmsg(MSG_VERBOSE, "dhcp_packet_lif: ignored %s packet xid "
1155e704a8f2Smeem 	    "%x received via LIF %s; %s", pname, xid, lif->lif_name,
1156e704a8f2Smeem 	    dsmp == NULL ? "unknown state machine" : "bound");
1157e704a8f2Smeem 	free_pkt_entry(plp);
1158d04ccbb3Scarlsonj }
1159d04ccbb3Scarlsonj 
1160d04ccbb3Scarlsonj /*
1161906cb642Scarlsonj  * dhcp_restart(): restarts DHCP (from INIT) on a given state machine, but only
1162906cb642Scarlsonj  *		   if we're leasing addresses.  Doesn't restart for information-
1163906cb642Scarlsonj  *		   only interfaces.
1164d04ccbb3Scarlsonj  *
1165d04ccbb3Scarlsonj  *   input: dhcp_smach_t *: the state machine to restart DHCP on
1166d04ccbb3Scarlsonj  *  output: void
1167d04ccbb3Scarlsonj  */
1168d04ccbb3Scarlsonj 
1169d04ccbb3Scarlsonj void
dhcp_restart(dhcp_smach_t * dsmp)1170d04ccbb3Scarlsonj dhcp_restart(dhcp_smach_t *dsmp)
1171d04ccbb3Scarlsonj {
1172906cb642Scarlsonj 	if (dsmp->dsm_state == INFORM_SENT || dsmp->dsm_state == INFORMATION)
1173906cb642Scarlsonj 		return;
1174906cb642Scarlsonj 
1175d04ccbb3Scarlsonj 	/*
1176d04ccbb3Scarlsonj 	 * As we're returning to INIT state, we need to discard any leases we
1177d04ccbb3Scarlsonj 	 * may have, and (for v4) canonize the LIF.  There's a bit of tension
1178d04ccbb3Scarlsonj 	 * between keeping around a possibly still working address, and obeying
1179d04ccbb3Scarlsonj 	 * the RFCs.  A more elaborate design would be to mark the addresses as
1180d04ccbb3Scarlsonj 	 * DEPRECATED, and then start a removal timer.  Such a design would
1181d04ccbb3Scarlsonj 	 * probably compromise testing.
1182d04ccbb3Scarlsonj 	 */
1183d04ccbb3Scarlsonj 	deprecate_leases(dsmp);
1184d04ccbb3Scarlsonj 
1185cfb9c9abScarlsonj 	if (!set_start_timer(dsmp)) {
118669bb4bb4Scarlsonj 		dhcpmsg(MSG_ERROR, "dhcp_restart: cannot schedule dhcp_start, "
1187d04ccbb3Scarlsonj 		    "reverting to INIT state on %s", dsmp->dsm_name);
1188d04ccbb3Scarlsonj 
1189d04ccbb3Scarlsonj 		(void) set_smach_state(dsmp, INIT);
1190d04ccbb3Scarlsonj 		dsmp->dsm_dflags |= DHCP_IF_FAILED;
1191d04ccbb3Scarlsonj 		ipc_action_finish(dsmp, DHCP_IPC_E_MEMORY);
1192906cb642Scarlsonj 	} else {
1193906cb642Scarlsonj 		dhcpmsg(MSG_DEBUG, "dhcp_restart: restarting DHCP on %s",
1194906cb642Scarlsonj 		    dsmp->dsm_name);
1195d04ccbb3Scarlsonj 	}
11967c478bd9Sstevel@tonic-gate }
11977c478bd9Sstevel@tonic-gate 
11987c478bd9Sstevel@tonic-gate /*
11997c478bd9Sstevel@tonic-gate  * stop_requesting(): decides when to stop retransmitting REQUESTs
12007c478bd9Sstevel@tonic-gate  *
1201d04ccbb3Scarlsonj  *   input: dhcp_smach_t *: the state machine REQUESTs are being sent from
12027c478bd9Sstevel@tonic-gate  *	    unsigned int: the number of REQUESTs sent so far
12037c478bd9Sstevel@tonic-gate  *  output: boolean_t: B_TRUE if retransmissions should stop
12047c478bd9Sstevel@tonic-gate  */
12057c478bd9Sstevel@tonic-gate 
12067c478bd9Sstevel@tonic-gate static boolean_t
stop_requesting(dhcp_smach_t * dsmp,unsigned int n_requests)1207d04ccbb3Scarlsonj stop_requesting(dhcp_smach_t *dsmp, unsigned int n_requests)
12087c478bd9Sstevel@tonic-gate {
1209d04ccbb3Scarlsonj 	uint_t maxreq;
12107c478bd9Sstevel@tonic-gate 
1211d04ccbb3Scarlsonj 	maxreq = dsmp->dsm_isv6 ? DHCPV6_REQ_MAX_RC : DHCP_MAX_REQUESTS;
1212d04ccbb3Scarlsonj 	if (n_requests >= maxreq) {
12137c478bd9Sstevel@tonic-gate 
1214d04ccbb3Scarlsonj 		dhcpmsg(MSG_INFO, "no ACK/NAK/Reply to REQUEST on %s",
1215d04ccbb3Scarlsonj 		    dsmp->dsm_name);
12167c478bd9Sstevel@tonic-gate 
1217d04ccbb3Scarlsonj 		request_failed(dsmp);
12187c478bd9Sstevel@tonic-gate 		return (B_TRUE);
1219d04ccbb3Scarlsonj 	} else {
1220d04ccbb3Scarlsonj 		return (B_FALSE);
12217c478bd9Sstevel@tonic-gate 	}
12227c478bd9Sstevel@tonic-gate }
1223