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 /*
223431Scarlsonj  * 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 
303431Scarlsonj #include <stdlib.h>
313431Scarlsonj #include <string.h>
323431Scarlsonj #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>
393431Scarlsonj #include <arpa/inet.h>
400Sstevel@tonic-gate #include <dhcp_hostconf.h>
413431Scarlsonj #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 
503431Scarlsonj static PKT_LIST		*select_best(dhcp_smach_t *);
513431Scarlsonj static void		request_failed(dhcp_smach_t *);
520Sstevel@tonic-gate static stop_func_t	stop_requesting;
530Sstevel@tonic-gate 
540Sstevel@tonic-gate /*
553431Scarlsonj  * send_v6_request(): sends a DHCPv6 Request message and switches to REQUESTING
563431Scarlsonj  *		      state.  This is a separate function because a NoBinding
573431Scarlsonj  *		      response can also cause us to do this.
583431Scarlsonj  *
593431Scarlsonj  *   input: dhcp_smach_t *: the state machine
603431Scarlsonj  *  output: none
613431Scarlsonj  */
623431Scarlsonj 
633431Scarlsonj void
643431Scarlsonj send_v6_request(dhcp_smach_t *dsmp)
653431Scarlsonj {
663431Scarlsonj 	dhcp_pkt_t *dpkt;
673431Scarlsonj 	dhcpv6_ia_na_t d6in;
683431Scarlsonj 
693431Scarlsonj 	dpkt = init_pkt(dsmp, DHCPV6_MSG_REQUEST);
703431Scarlsonj 	(void) add_pkt_opt(dpkt, DHCPV6_OPT_SERVERID, dsmp->dsm_serverid,
713431Scarlsonj 	    dsmp->dsm_serveridlen);
723431Scarlsonj 
733431Scarlsonj 	/* Add an IA_NA option for our controlling LIF */
743431Scarlsonj 	d6in.d6in_iaid = htonl(dsmp->dsm_lif->lif_iaid);
753431Scarlsonj 	d6in.d6in_t1 = htonl(0);
763431Scarlsonj 	d6in.d6in_t2 = htonl(0);
773431Scarlsonj 	(void) add_pkt_opt(dpkt, DHCPV6_OPT_IA_NA,
783431Scarlsonj 	    (dhcpv6_option_t *)&d6in + 1,
793431Scarlsonj 	    sizeof (d6in) - sizeof (dhcpv6_option_t));
803431Scarlsonj 
813431Scarlsonj 	/* Add required Option Request option */
823431Scarlsonj 	(void) add_pkt_prl(dpkt, dsmp);
833431Scarlsonj 
843431Scarlsonj 	(void) send_pkt_v6(dsmp, dpkt, dsmp->dsm_server, stop_requesting,
853431Scarlsonj 	    DHCPV6_REQ_TIMEOUT, DHCPV6_REQ_MAX_RT);
863431Scarlsonj 
873431Scarlsonj 	/* For DHCPv6, state switch cannot fail */
883431Scarlsonj 	(void) set_smach_state(dsmp, REQUESTING);
893431Scarlsonj }
903431Scarlsonj 
913431Scarlsonj /*
923431Scarlsonj  * server_unicast_option(): determines the server address to use based on the
933431Scarlsonj  *			    DHCPv6 Server Unicast option present in the given
943431Scarlsonj  *			    packet.
953431Scarlsonj  *
963431Scarlsonj  *   input: dhcp_smach_t *: the state machine
973431Scarlsonj  *	    PKT_LIST *: received packet (Advertisement or Reply)
983431Scarlsonj  *  output: none
993431Scarlsonj  */
1003431Scarlsonj 
1013431Scarlsonj void
1023431Scarlsonj server_unicast_option(dhcp_smach_t *dsmp, PKT_LIST *plp)
1033431Scarlsonj {
1043431Scarlsonj 	const dhcpv6_option_t *d6o;
1053431Scarlsonj 	uint_t olen;
1063431Scarlsonj 
1073431Scarlsonj 	d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_UNICAST, &olen);
1083431Scarlsonj 	olen -= sizeof (*d6o);
1093431Scarlsonj 	/* LINTED: no consequent */
1103431Scarlsonj 	if (d6o == NULL) {
1113431Scarlsonj 		/* No Server Unicast option specified */
1123431Scarlsonj 	} else if (olen != sizeof (dsmp->dsm_server)) {
1133431Scarlsonj 		dhcpmsg(MSG_WARNING, "server_unicast_option: %s has Server "
1143431Scarlsonj 		    "Unicast option with bad length",
1153431Scarlsonj 		    pkt_type_to_string(pkt_recv_type(plp), B_TRUE));
1163431Scarlsonj 	} else {
1173431Scarlsonj 		in6_addr_t addr;
1183431Scarlsonj 
1193431Scarlsonj 		(void) memcpy(&addr, d6o + 1, olen);
1203431Scarlsonj 		if (IN6_IS_ADDR_UNSPECIFIED(&addr)) {
1213431Scarlsonj 			dhcpmsg(MSG_WARNING, "server_unicast_option: unicast "
1223431Scarlsonj 			    "to unspecified address ignored");
1233431Scarlsonj 		} else if (IN6_IS_ADDR_MULTICAST(&addr)) {
1243431Scarlsonj 			dhcpmsg(MSG_WARNING, "server_unicast_option: unicast "
1253431Scarlsonj 			    "to multicast address ignored");
1263431Scarlsonj 		} else if (IN6_IS_ADDR_V4COMPAT(&addr) ||
1273431Scarlsonj 		    IN6_IS_ADDR_V4MAPPED(&addr)) {
1283431Scarlsonj 			dhcpmsg(MSG_WARNING, "server_unicast_option: unicast "
1293431Scarlsonj 			    "to invalid address ignored");
1303431Scarlsonj 		} else {
1313431Scarlsonj 			dsmp->dsm_server = addr;
1323431Scarlsonj 		}
1333431Scarlsonj 	}
1343431Scarlsonj }
1353431Scarlsonj 
1363431Scarlsonj /*
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
1403431Scarlsonj  *		      the ACK/NAK.  This may be called by the offer timer or
1413431Scarlsonj  *		      by any function that wants to check for offers after
1423431Scarlsonj  *		      canceling that timer.
1430Sstevel@tonic-gate  *
1443431Scarlsonj  *   input: iu_tq_t *: timer queue; non-NULL if this is a timer callback
1453431Scarlsonj  *	    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 {
1523431Scarlsonj 	dhcp_smach_t		*dsmp = arg;
1530Sstevel@tonic-gate 	dhcp_pkt_t		*dpkt;
1540Sstevel@tonic-gate 	PKT_LIST		*offer;
1550Sstevel@tonic-gate 	lease_t			lease;
1563431Scarlsonj 	boolean_t		isv6 = dsmp->dsm_isv6;
1572157Sdh155122 
1583431Scarlsonj 	/*
1593431Scarlsonj 	 * We assume here that if tqp is set, then this means we're being
1603431Scarlsonj 	 * called back by the offer wait timer.  If so, then drop our hold
1613431Scarlsonj 	 * on the state machine.  Otherwise, cancel the timer if it's running.
1623431Scarlsonj 	 */
1633431Scarlsonj 	if (tqp != NULL) {
1643431Scarlsonj 		dhcpmsg(MSG_VERBOSE,
1653431Scarlsonj 		    "dhcp_requesting: offer wait timer on v%d %s",
1663431Scarlsonj 		    isv6 ? 6 : 4, dsmp->dsm_name);
1673431Scarlsonj 		dsmp->dsm_offer_timer = -1;
1683431Scarlsonj 		if (!verify_smach(dsmp))
1693431Scarlsonj 			return;
1703431Scarlsonj 	} else {
1713431Scarlsonj 		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 
1783431Scarlsonj 	offer = select_best(dsmp);
1790Sstevel@tonic-gate 	if (offer == NULL) {
1800Sstevel@tonic-gate 
1813431Scarlsonj 		dhcpmsg(MSG_VERBOSE,
1823431Scarlsonj 		    "no OFFERs/Advertisements on %s, waiting...",
1833431Scarlsonj 		    dsmp->dsm_name);
1840Sstevel@tonic-gate 
1850Sstevel@tonic-gate 		/*
1860Sstevel@tonic-gate 		 * no acceptable OFFERs have come in.  reschedule
1873431Scarlsonj 		 * ourself for callback.
1880Sstevel@tonic-gate 		 */
1890Sstevel@tonic-gate 
1903431Scarlsonj 		if ((dsmp->dsm_offer_timer = iu_schedule_timer(tq,
1913431Scarlsonj 		    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 "
2013431Scarlsonj 			    "%s", dsmp->dsm_name);
2020Sstevel@tonic-gate 
2033431Scarlsonj 			stop_pkt_retransmission(dsmp);
2043431Scarlsonj 			(void) set_smach_state(dsmp, INIT);
2053431Scarlsonj 			dsmp->dsm_dflags |= DHCP_IF_FAILED;
2063431Scarlsonj 			ipc_action_finish(dsmp, DHCP_IPC_E_MEMORY);
2073431Scarlsonj 		} else {
2083431Scarlsonj 			hold_smach(dsmp);
2090Sstevel@tonic-gate 		}
2100Sstevel@tonic-gate 
2110Sstevel@tonic-gate 		return;
2120Sstevel@tonic-gate 	}
2130Sstevel@tonic-gate 
2140Sstevel@tonic-gate 	/*
2153431Scarlsonj 	 * With IPv4, the DHCPREQUEST packet we're about to transmit implicitly
2163431Scarlsonj 	 * declines all other offers we've received.  We can no longer use any
2173431Scarlsonj 	 * cached offers, so we must discard them now.  With DHCPv6, though,
2183431Scarlsonj 	 * we're permitted to hang onto the advertisements (offers) and try
2193431Scarlsonj 	 * them if the preferred one doesn't pan out.
2200Sstevel@tonic-gate 	 */
2213431Scarlsonj 	if (!isv6)
2223431Scarlsonj 		free_pkt_list(&dsmp->dsm_recv_pkt_list);
2230Sstevel@tonic-gate 
2243431Scarlsonj 	/* stop collecting packets. */
2250Sstevel@tonic-gate 
2263431Scarlsonj 	stop_pkt_retransmission(dsmp);
2270Sstevel@tonic-gate 
2280Sstevel@tonic-gate 	/*
2293431Scarlsonj 	 * For IPv4, check to see whether we got an OFFER or a BOOTP packet.
2303431Scarlsonj 	 * If we got a BOOTP packet, go to the BOUND state now.
2310Sstevel@tonic-gate 	 */
2323431Scarlsonj 	if (!isv6 && offer->opts[CD_DHCP_TYPE] == NULL) {
2333431Scarlsonj 		free_pkt_list(&dsmp->dsm_recv_pkt_list);
2343431Scarlsonj 
2353431Scarlsonj 		if (!set_smach_state(dsmp, REQUESTING)) {
2363431Scarlsonj 			dhcp_restart(dsmp);
2373431Scarlsonj 			return;
2383431Scarlsonj 		}
2393431Scarlsonj 
2403431Scarlsonj 		if (!dhcp_bound(dsmp, offer)) {
2413431Scarlsonj 			dhcpmsg(MSG_WARNING, "dhcp_requesting: dhcp_bound "
2423431Scarlsonj 			    "failed for %s", dsmp->dsm_name);
2433431Scarlsonj 			dhcp_restart(dsmp);
2443431Scarlsonj 			return;
2453431Scarlsonj 		}
2463431Scarlsonj 
2473431Scarlsonj 		return;
2480Sstevel@tonic-gate 	}
2493431Scarlsonj 
2503431Scarlsonj 	if (isv6) {
2513431Scarlsonj 		const char *estr, *msg;
2523431Scarlsonj 		const dhcpv6_option_t *d6o;
2533431Scarlsonj 		uint_t olen, msglen;
2543431Scarlsonj 
2553431Scarlsonj 		/* If there's a Status Code option, print the message */
2563431Scarlsonj 		d6o = dhcpv6_pkt_option(offer, NULL, DHCPV6_OPT_STATUS_CODE,
2573431Scarlsonj 		    &olen);
2583431Scarlsonj 		(void) dhcpv6_status_code(d6o, olen, &estr, &msg, &msglen);
2593431Scarlsonj 		print_server_msg(dsmp, msg, msglen);
2603431Scarlsonj 
2613431Scarlsonj 		/* Copy in the Server ID (guaranteed to be present now) */
2623431Scarlsonj 		if (!save_server_id(dsmp, offer))
2633431Scarlsonj 			goto failure;
2643431Scarlsonj 
2653431Scarlsonj 		/*
2663431Scarlsonj 		 * Determine how to send this message.  If the Advertisement
2673431Scarlsonj 		 * (offer) has the unicast option, then use the address
2683431Scarlsonj 		 * specified in the option.  Otherwise, send via multicast.
2693431Scarlsonj 		 */
2703431Scarlsonj 		server_unicast_option(dsmp, offer);
2713431Scarlsonj 
2723431Scarlsonj 		send_v6_request(dsmp);
2733431Scarlsonj 	} else {
2743431Scarlsonj 		/* if we got a message from the server, display it. */
2753431Scarlsonj 		if (offer->opts[CD_MESSAGE] != NULL) {
2763431Scarlsonj 			print_server_msg(dsmp,
2773431Scarlsonj 			    (char *)offer->opts[CD_MESSAGE]->value,
2783431Scarlsonj 			    offer->opts[CD_MESSAGE]->len);
2793431Scarlsonj 		}
2803431Scarlsonj 
2813431Scarlsonj 		/*
2823431Scarlsonj 		 * assemble a DHCPREQUEST, with the ciaddr field set to 0,
2833431Scarlsonj 		 * since we got here from the INIT state.
2843431Scarlsonj 		 */
2853431Scarlsonj 
2863431Scarlsonj 		dpkt = init_pkt(dsmp, REQUEST);
2873431Scarlsonj 
2883431Scarlsonj 		/*
2893431Scarlsonj 		 * Grab the lease out of the OFFER; we know it's valid because
2903431Scarlsonj 		 * select_best() already checked.  The max dhcp message size
2913431Scarlsonj 		 * option is set to the interface max, minus the size of the
2923431Scarlsonj 		 * udp and ip headers.
2933431Scarlsonj 		 */
2943431Scarlsonj 
2953431Scarlsonj 		(void) memcpy(&lease, offer->opts[CD_LEASE_TIME]->value,
2963431Scarlsonj 		    sizeof (lease_t));
2973431Scarlsonj 
2983431Scarlsonj 		(void) add_pkt_opt32(dpkt, CD_LEASE_TIME, lease);
2993431Scarlsonj 		(void) add_pkt_opt16(dpkt, CD_MAX_DHCP_SIZE,
3003431Scarlsonj 		    htons(dsmp->dsm_lif->lif_max - sizeof (struct udpiphdr)));
3013431Scarlsonj 		(void) add_pkt_opt32(dpkt, CD_REQUESTED_IP_ADDR,
3023431Scarlsonj 		    offer->pkt->yiaddr.s_addr);
3033431Scarlsonj 		(void) add_pkt_opt(dpkt, CD_SERVER_ID,
3043431Scarlsonj 		    offer->opts[CD_SERVER_ID]->value,
3053431Scarlsonj 		    offer->opts[CD_SERVER_ID]->len);
3063431Scarlsonj 
3073448Sdh155122 		if (class_id_len != 0) {
3083448Sdh155122 			(void) add_pkt_opt(dpkt, CD_CLASS_ID, class_id,
3093448Sdh155122 			    class_id_len);
3103448Sdh155122 		}
3113431Scarlsonj 		(void) add_pkt_prl(dpkt, dsmp);
3123431Scarlsonj 
3133431Scarlsonj 		/*
3143431Scarlsonj 		 * dsm_reqhost was set for this state machine in
3153431Scarlsonj 		 * dhcp_selecting() if the DF_REQUEST_HOSTNAME option set and a
3163431Scarlsonj 		 * host name was found
3173431Scarlsonj 		 */
3183431Scarlsonj 		if (dsmp->dsm_reqhost != NULL) {
3193431Scarlsonj 			(void) add_pkt_opt(dpkt, CD_HOSTNAME, dsmp->dsm_reqhost,
3203431Scarlsonj 			    strlen(dsmp->dsm_reqhost));
3213431Scarlsonj 		}
3223431Scarlsonj 		(void) add_pkt_opt(dpkt, CD_END, NULL, 0);
3233431Scarlsonj 
3243431Scarlsonj 		/*
3253431Scarlsonj 		 * send out the REQUEST, trying retransmissions.  either a NAK
3263431Scarlsonj 		 * or too many REQUEST attempts will revert us to SELECTING.
3273431Scarlsonj 		 */
3283431Scarlsonj 
3293431Scarlsonj 		if (!set_smach_state(dsmp, REQUESTING)) {
3303431Scarlsonj 			dhcpmsg(MSG_ERROR, "dhcp_requesting: cannot switch to "
3313431Scarlsonj 			    "REQUESTING state; reverting to INIT on %s",
3323431Scarlsonj 			    dsmp->dsm_name);
3333431Scarlsonj 			goto failure;
3343431Scarlsonj 		}
3353431Scarlsonj 
3363431Scarlsonj 		(void) send_pkt(dsmp, dpkt, htonl(INADDR_BROADCAST),
3373431Scarlsonj 		    stop_requesting);
3383431Scarlsonj 	}
3390Sstevel@tonic-gate 
3400Sstevel@tonic-gate 	/* all done with the offer */
3413431Scarlsonj 	free_pkt_entry(offer);
3420Sstevel@tonic-gate 
3433431Scarlsonj 	return;
3440Sstevel@tonic-gate 
3453431Scarlsonj failure:
3463431Scarlsonj 	dsmp->dsm_dflags |= DHCP_IF_FAILED;
3473431Scarlsonj 	(void) set_smach_state(dsmp, INIT);
3483431Scarlsonj 	ipc_action_finish(dsmp, DHCP_IPC_E_MEMORY);
3493431Scarlsonj 	free_pkt_list(&dsmp->dsm_recv_pkt_list);
3500Sstevel@tonic-gate }
3510Sstevel@tonic-gate 
3520Sstevel@tonic-gate /*
3533431Scarlsonj  * compute_points_v6(): compute the number of "points" for a given v6
3543431Scarlsonj  *			advertisement.
3550Sstevel@tonic-gate  *
3563431Scarlsonj  *   input: const PKT_LIST *: packet to inspect
3573431Scarlsonj  *	    const dhcp_smach_t *: state machine that received the packet
3583431Scarlsonj  *  output: int: -1 to discard, -2 to accept immediately, >=0 for preference.
3593431Scarlsonj  */
3603431Scarlsonj 
3613431Scarlsonj static int
3623431Scarlsonj compute_points_v6(const PKT_LIST *pkt, const dhcp_smach_t *dsmp)
3633431Scarlsonj {
3643431Scarlsonj 	char abuf[INET6_ADDRSTRLEN];
3653431Scarlsonj 	int points = 0;
3663431Scarlsonj 	const dhcpv6_option_t *d6o, *d6so;
3673431Scarlsonj 	uint_t olen, solen;
3683431Scarlsonj 	int i;
3693431Scarlsonj 	const char *estr, *msg;
3703431Scarlsonj 	uint_t msglen;
3713431Scarlsonj 
3723431Scarlsonj 	/*
3733431Scarlsonj 	 * Look through the packet contents.  Valid packets must have our
3743431Scarlsonj 	 * client ID and a server ID, which has already been checked by
375*5381Smeem 	 * dhcp_packet_lif.  Bonus points for each option.
3763431Scarlsonj 	 */
3773431Scarlsonj 
3783431Scarlsonj 	/* One point for having a valid message. */
3793431Scarlsonj 	points++;
3803431Scarlsonj 
3813431Scarlsonj 	/*
3823431Scarlsonj 	 * Per RFC 3315, if the Advertise message says, "yes, we have no
3833431Scarlsonj 	 * bananas today," then ignore the entire message.  (Why it's just
3843431Scarlsonj 	 * _this_ error and no other is a bit of a mystery, but a standard is a
3853431Scarlsonj 	 * standard.)
3863431Scarlsonj 	 */
3873431Scarlsonj 	d6o = dhcpv6_pkt_option(pkt, NULL, DHCPV6_OPT_STATUS_CODE, &olen);
3883431Scarlsonj 	if (dhcpv6_status_code(d6o, olen, &estr, &msg, &msglen) ==
3893431Scarlsonj 	    DHCPV6_STAT_NOADDRS) {
3903431Scarlsonj 		dhcpmsg(MSG_INFO,
3913431Scarlsonj 		    "discard advertisement from %s on %s: no address status",
3923431Scarlsonj 		    inet_ntop(AF_INET6,
3933431Scarlsonj 		    &((struct sockaddr_in6 *)&pkt->pktfrom)->sin6_addr,
3943431Scarlsonj 		    abuf, sizeof (abuf)), dsmp->dsm_name);
3953431Scarlsonj 		return (-1);
3963431Scarlsonj 	}
3973431Scarlsonj 
3983431Scarlsonj 	/* Two points for each batch of offered IP addresses */
3993431Scarlsonj 	d6o = NULL;
4003431Scarlsonj 	while ((d6o = dhcpv6_pkt_option(pkt, d6o, DHCPV6_OPT_IA_NA,
4013431Scarlsonj 	    &olen)) != NULL) {
4023431Scarlsonj 
4033431Scarlsonj 		/*
4043431Scarlsonj 		 * Note that it's possible to have "no bananas" on an
4053431Scarlsonj 		 * individual IA.  We must look for that here.
4063431Scarlsonj 		 *
4073431Scarlsonj 		 * RFC 3315 section 17.1.3 does not refer to the status code
4083431Scarlsonj 		 * embedded in the IA itself.  However, the TAHI test suite
4093431Scarlsonj 		 * checks for this specific case.  Because it's extremely
4103431Scarlsonj 		 * unlikely that any usable server is going to report that it
4113431Scarlsonj 		 * has no addresses on a network using DHCP for address
4123431Scarlsonj 		 * assignment, we allow such messages to be dropped.
4133431Scarlsonj 		 */
4143431Scarlsonj 		d6so = dhcpv6_find_option(
4153431Scarlsonj 		    (const char *)d6o + sizeof (dhcpv6_ia_na_t),
4163431Scarlsonj 		    olen - sizeof (dhcpv6_ia_na_t), NULL,
4173431Scarlsonj 		    DHCPV6_OPT_STATUS_CODE, &solen);
4183431Scarlsonj 		if (dhcpv6_status_code(d6so, solen, &estr, &msg, &msglen) ==
4193431Scarlsonj 		    DHCPV6_STAT_NOADDRS)
4203431Scarlsonj 			return (-1);
4213431Scarlsonj 		points += 2;
4223431Scarlsonj 	}
4233431Scarlsonj 
4243431Scarlsonj 	/*
4253431Scarlsonj 	 * Note that we drive on in the case where there are no addresses.  The
4263431Scarlsonj 	 * hope here is that we'll at least get some useful configuration
4273431Scarlsonj 	 * information.
4283431Scarlsonj 	 */
4293431Scarlsonj 
4303431Scarlsonj 	/* One point for each requested option */
4313431Scarlsonj 	for (i = 0; i < dsmp->dsm_prllen; i++) {
4323431Scarlsonj 		if (dhcpv6_pkt_option(pkt, NULL, dsmp->dsm_prl[i], NULL) !=
4333431Scarlsonj 		    NULL)
4343431Scarlsonj 			points++;
4353431Scarlsonj 	}
4363431Scarlsonj 
4373431Scarlsonj 	/*
4383431Scarlsonj 	 * Ten points for each point of "preference."  Note: the value 255 is
4393431Scarlsonj 	 * special.  It means "stop right now and select this server."
4403431Scarlsonj 	 */
4413431Scarlsonj 	d6o = dhcpv6_pkt_option(pkt, NULL, DHCPV6_OPT_PREFERENCE, &olen);
4423431Scarlsonj 	if (d6o != NULL && olen == sizeof (*d6o) + 1) {
4433431Scarlsonj 		int pref = *(const uchar_t *)(d6o + 1);
4443431Scarlsonj 
4453431Scarlsonj 		if (pref == 255)
4463431Scarlsonj 			return (-2);
4473431Scarlsonj 		points += 10 * pref;
4483431Scarlsonj 	}
4493431Scarlsonj 
4503431Scarlsonj 	return (points);
4513431Scarlsonj }
4523431Scarlsonj 
4533431Scarlsonj /*
4543431Scarlsonj  * compute_points_v4(): compute the number of "points" for a given v4 offer.
4553431Scarlsonj  *
4563431Scarlsonj  *   input: const PKT_LIST *: packet to inspect
4573431Scarlsonj  *	    const dhcp_smach_t *: state machine that received the packet
4583431Scarlsonj  *  output: int: -1 to discard, >=0 for preference.
4593431Scarlsonj  */
4603431Scarlsonj 
4613431Scarlsonj static int
4623431Scarlsonj compute_points_v4(const PKT_LIST *pkt)
4633431Scarlsonj {
4643431Scarlsonj 	int points = 0;
4653431Scarlsonj 
4663431Scarlsonj 	if (pkt->opts[CD_DHCP_TYPE] == NULL) {
4673431Scarlsonj 		dhcpmsg(MSG_VERBOSE, "compute_points_v4: valid BOOTP reply");
4683431Scarlsonj 		goto valid_offer;
4693431Scarlsonj 	}
4703431Scarlsonj 
4713431Scarlsonj 	if (pkt->opts[CD_LEASE_TIME] == NULL) {
4723431Scarlsonj 		dhcpmsg(MSG_WARNING, "compute_points_v4: OFFER without lease "
4733431Scarlsonj 		    "time");
4743431Scarlsonj 		return (-1);
4753431Scarlsonj 	}
4763431Scarlsonj 
4773431Scarlsonj 	if (pkt->opts[CD_LEASE_TIME]->len != sizeof (lease_t)) {
4783431Scarlsonj 		dhcpmsg(MSG_WARNING, "compute_points_v4: OFFER with garbled "
4793431Scarlsonj 		    "lease time");
4803431Scarlsonj 		return (-1);
4813431Scarlsonj 	}
4823431Scarlsonj 
4833431Scarlsonj 	if (pkt->opts[CD_SERVER_ID] == NULL) {
4843431Scarlsonj 		dhcpmsg(MSG_WARNING, "compute_points_v4: OFFER without server "
4853431Scarlsonj 		    "id");
4863431Scarlsonj 		return (-1);
4873431Scarlsonj 	}
4883431Scarlsonj 
4893431Scarlsonj 	if (pkt->opts[CD_SERVER_ID]->len != sizeof (ipaddr_t)) {
4903431Scarlsonj 		dhcpmsg(MSG_WARNING, "compute_points_v4: OFFER with garbled "
4913431Scarlsonj 		    "server id");
4923431Scarlsonj 		return (-1);
4933431Scarlsonj 	}
4943431Scarlsonj 
4953431Scarlsonj 	/* valid DHCP OFFER.  see if we got our parameters. */
4963431Scarlsonj 	dhcpmsg(MSG_VERBOSE, "compute_points_v4: valid OFFER packet");
4973431Scarlsonj 	points += 30;
4983431Scarlsonj 
4993431Scarlsonj valid_offer:
5003431Scarlsonj 	if (pkt->rfc1048)
5013431Scarlsonj 		points += 5;
5023431Scarlsonj 
5033431Scarlsonj 	/*
5043431Scarlsonj 	 * also could be faked, though more difficult because the encapsulation
5053431Scarlsonj 	 * is hard to encode on a BOOTP server; plus there's not as much real
5063431Scarlsonj 	 * estate in the packet for options, so it's likely this option would
5073431Scarlsonj 	 * get dropped.
5083431Scarlsonj 	 */
5093431Scarlsonj 
5103431Scarlsonj 	if (pkt->opts[CD_VENDOR_SPEC] != NULL)
5113431Scarlsonj 		points += 80;
5123431Scarlsonj 
5133431Scarlsonj 	if (pkt->opts[CD_SUBNETMASK] != NULL)
5143431Scarlsonj 		points++;
5153431Scarlsonj 
5163431Scarlsonj 	if (pkt->opts[CD_ROUTER] != NULL)
5173431Scarlsonj 		points++;
5183431Scarlsonj 
5193431Scarlsonj 	if (pkt->opts[CD_HOSTNAME] != NULL)
5203431Scarlsonj 		points += 5;
5213431Scarlsonj 
5223431Scarlsonj 	return (points);
5233431Scarlsonj }
5243431Scarlsonj 
5253431Scarlsonj /*
5263431Scarlsonj  * select_best(): selects the best offer from a list of IPv4 OFFER packets or
5273431Scarlsonj  *		  DHCPv6 Advertise packets.
5283431Scarlsonj  *
5293431Scarlsonj  *   input: dhcp_smach_t *: state machine with enqueued offers
5300Sstevel@tonic-gate  *  output: PKT_LIST *: the best packet, or NULL if none are acceptable
5310Sstevel@tonic-gate  */
5320Sstevel@tonic-gate 
5330Sstevel@tonic-gate static PKT_LIST *
5343431Scarlsonj select_best(dhcp_smach_t *dsmp)
5350Sstevel@tonic-gate {
5363431Scarlsonj 	PKT_LIST	*current = dsmp->dsm_recv_pkt_list;
5373431Scarlsonj 	PKT_LIST	*next, *best = NULL;
5383431Scarlsonj 	int		points, best_points = -1;
5390Sstevel@tonic-gate 
5400Sstevel@tonic-gate 	/*
5410Sstevel@tonic-gate 	 * pick out the best offer.  point system.
5423431Scarlsonj 	 * what's important for IPv4?
5430Sstevel@tonic-gate 	 *
5443431Scarlsonj 	 *	0) DHCP (30 points)
5450Sstevel@tonic-gate 	 *	1) no option overload
5463431Scarlsonj 	 *	2) encapsulated vendor option (80 points)
5470Sstevel@tonic-gate 	 *	3) non-null sname and siaddr fields
5480Sstevel@tonic-gate 	 *	4) non-null file field
5493431Scarlsonj 	 *	5) hostname (5 points)
5503431Scarlsonj 	 *	6) subnetmask (1 point)
5513431Scarlsonj 	 *	7) router (1 point)
5520Sstevel@tonic-gate 	 */
5530Sstevel@tonic-gate 
5543431Scarlsonj 	for (; current != NULL; current = next) {
5553431Scarlsonj 		next = current->next;
5560Sstevel@tonic-gate 
5573431Scarlsonj 		points = current->isv6 ?
5583431Scarlsonj 		    compute_points_v6(current, dsmp) :
5593431Scarlsonj 		    compute_points_v4(current);
5600Sstevel@tonic-gate 
5613431Scarlsonj 		/*
5623431Scarlsonj 		 * Just discard any unacceptable entries we encounter.
5633431Scarlsonj 		 */
5643431Scarlsonj 		if (points == -1) {
5653431Scarlsonj 			remque(current);
5663431Scarlsonj 			free_pkt_entry(current);
5670Sstevel@tonic-gate 			continue;
5680Sstevel@tonic-gate 		}
5690Sstevel@tonic-gate 
5703431Scarlsonj 		dhcpmsg(MSG_DEBUG, "select_best: OFFER had %d points", points);
5710Sstevel@tonic-gate 
5723431Scarlsonj 		/* Special case: stop now and select */
5733431Scarlsonj 		if (points == -2) {
5743431Scarlsonj 			best = current;
5753431Scarlsonj 			break;
5763431Scarlsonj 		}
5770Sstevel@tonic-gate 
5780Sstevel@tonic-gate 		if (points >= best_points) {
5790Sstevel@tonic-gate 			best_points = points;
5800Sstevel@tonic-gate 			best = current;
5810Sstevel@tonic-gate 		}
5820Sstevel@tonic-gate 	}
5830Sstevel@tonic-gate 
5840Sstevel@tonic-gate 	if (best != NULL) {
5850Sstevel@tonic-gate 		dhcpmsg(MSG_DEBUG, "select_best: most points: %d", best_points);
5863431Scarlsonj 		remque(best);
5873431Scarlsonj 	} else {
5880Sstevel@tonic-gate 		dhcpmsg(MSG_DEBUG, "select_best: no valid OFFER/BOOTP reply");
5893431Scarlsonj 	}
5900Sstevel@tonic-gate 
5910Sstevel@tonic-gate 	return (best);
5920Sstevel@tonic-gate }
5930Sstevel@tonic-gate 
5940Sstevel@tonic-gate /*
5953431Scarlsonj  * accept_v4_acknak(): determine what to do with a DHCPv4 ACK/NAK based on the
5963431Scarlsonj  *		       current state.  If we're renewing or rebinding, the ACK
5973431Scarlsonj  *		       must be for the same address and must have a new lease
5983431Scarlsonj  *		       time.  If it's a NAK, then our cache is garbage, and we
5993431Scarlsonj  *		       must restart.  Finally, call dhcp_bound on accepted
6003431Scarlsonj  *		       ACKs.
6010Sstevel@tonic-gate  *
6023431Scarlsonj  *   input: dhcp_smach_t *: the state machine to handle the ACK/NAK
6033431Scarlsonj  *	    PKT_LIST *: the ACK/NAK message
6040Sstevel@tonic-gate  *  output: void
6050Sstevel@tonic-gate  */
6060Sstevel@tonic-gate 
6073431Scarlsonj static void
6083431Scarlsonj accept_v4_acknak(dhcp_smach_t *dsmp, PKT_LIST *plp)
6090Sstevel@tonic-gate {
6104516Scarlsonj 	/* Account for received and processed messages */
6114516Scarlsonj 	dsmp->dsm_received++;
6124516Scarlsonj 
6130Sstevel@tonic-gate 	if (*plp->opts[CD_DHCP_TYPE]->value == ACK) {
6144106Scarlsonj 		if (dsmp->dsm_state != INFORM_SENT &&
6154106Scarlsonj 		    dsmp->dsm_state != INFORMATION &&
6164106Scarlsonj 		    (plp->opts[CD_LEASE_TIME] == NULL ||
6174106Scarlsonj 		    plp->opts[CD_LEASE_TIME]->len != sizeof (lease_t))) {
6183431Scarlsonj 			dhcpmsg(MSG_WARNING, "accept_v4_acknak: ACK packet on "
6193431Scarlsonj 			    "%s missing mandatory lease option, ignored",
6203431Scarlsonj 			    dsmp->dsm_name);
6213431Scarlsonj 			dsmp->dsm_bad_offers++;
6223431Scarlsonj 			free_pkt_entry(plp);
6230Sstevel@tonic-gate 			return;
6240Sstevel@tonic-gate 		}
6253431Scarlsonj 		if ((dsmp->dsm_state == RENEWING ||
6263431Scarlsonj 		    dsmp->dsm_state == REBINDING) &&
6273431Scarlsonj 		    dsmp->dsm_leases->dl_lifs->lif_addr !=
6283431Scarlsonj 		    plp->pkt->yiaddr.s_addr) {
6293431Scarlsonj 			dhcpmsg(MSG_WARNING, "accept_v4_acknak: renewal ACK "
6303431Scarlsonj 			    "packet has a different IP address (%s), ignored",
6313431Scarlsonj 			    inet_ntoa(plp->pkt->yiaddr));
6323431Scarlsonj 			dsmp->dsm_bad_offers++;
6333431Scarlsonj 			free_pkt_entry(plp);
6340Sstevel@tonic-gate 			return;
6350Sstevel@tonic-gate 		}
6360Sstevel@tonic-gate 	}
6370Sstevel@tonic-gate 
6380Sstevel@tonic-gate 	/*
6390Sstevel@tonic-gate 	 * looks good; cancel the retransmission timer and unregister
6400Sstevel@tonic-gate 	 * the acknak handler. ACK to BOUND, NAK back to SELECTING.
6410Sstevel@tonic-gate 	 */
6420Sstevel@tonic-gate 
6433431Scarlsonj 	stop_pkt_retransmission(dsmp);
6440Sstevel@tonic-gate 
6453431Scarlsonj 	if (*plp->opts[CD_DHCP_TYPE]->value == NAK) {
6463431Scarlsonj 		dhcpmsg(MSG_WARNING, "accept_v4_acknak: NAK on interface %s",
6473431Scarlsonj 		    dsmp->dsm_name);
6483431Scarlsonj 		dsmp->dsm_bad_offers++;
6493431Scarlsonj 		free_pkt_entry(plp);
6503431Scarlsonj 		dhcp_restart(dsmp);
6510Sstevel@tonic-gate 
6520Sstevel@tonic-gate 		/*
6530Sstevel@tonic-gate 		 * remove any bogus cached configuration we might have
6540Sstevel@tonic-gate 		 * around (right now would only happen if we got here
6550Sstevel@tonic-gate 		 * from INIT_REBOOT).
6560Sstevel@tonic-gate 		 */
6570Sstevel@tonic-gate 
6583431Scarlsonj 		(void) remove_hostconf(dsmp->dsm_name, dsmp->dsm_isv6);
6590Sstevel@tonic-gate 		return;
6600Sstevel@tonic-gate 	}
6610Sstevel@tonic-gate 
6620Sstevel@tonic-gate 	if (plp->opts[CD_SERVER_ID] == NULL ||
6630Sstevel@tonic-gate 	    plp->opts[CD_SERVER_ID]->len != sizeof (ipaddr_t)) {
6643431Scarlsonj 		dhcpmsg(MSG_ERROR, "accept_v4_acknak: ACK with no valid "
6654516Scarlsonj 		    "server id on %s", dsmp->dsm_name);
6663431Scarlsonj 		dsmp->dsm_bad_offers++;
6673431Scarlsonj 		free_pkt_entry(plp);
6683431Scarlsonj 		dhcp_restart(dsmp);
6693431Scarlsonj 		return;
6703431Scarlsonj 	}
6713431Scarlsonj 
6723431Scarlsonj 	if (plp->opts[CD_MESSAGE] != NULL) {
6733431Scarlsonj 		print_server_msg(dsmp, (char *)plp->opts[CD_MESSAGE]->value,
6743431Scarlsonj 		    plp->opts[CD_MESSAGE]->len);
6753431Scarlsonj 	}
6763431Scarlsonj 
6773431Scarlsonj 	dhcpmsg(MSG_VERBOSE, "accept_v4_acknak: ACK on %s", dsmp->dsm_name);
6783431Scarlsonj 	if (!dhcp_bound(dsmp, plp)) {
6793431Scarlsonj 		dhcpmsg(MSG_WARNING, "accept_v4_acknak: dhcp_bound failed "
6803431Scarlsonj 		    "for %s", dsmp->dsm_name);
6813431Scarlsonj 		dhcp_restart(dsmp);
6823431Scarlsonj 	}
6833431Scarlsonj }
6843431Scarlsonj 
6853431Scarlsonj /*
6863431Scarlsonj  * accept_v6_message(): determine what to do with a DHCPv6 message based on the
6873431Scarlsonj  *			current state.
6883431Scarlsonj  *
6893431Scarlsonj  *   input: dhcp_smach_t *: the state machine to handle the message
6903431Scarlsonj  *	    PKT_LIST *: the DHCPv6 message
6913431Scarlsonj  *	    const char *: type of message (for logging)
6923431Scarlsonj  *	    uchar_t: type of message (extracted from packet)
6933431Scarlsonj  *  output: void
6943431Scarlsonj  */
6953431Scarlsonj 
6963431Scarlsonj static void
6973431Scarlsonj accept_v6_message(dhcp_smach_t *dsmp, PKT_LIST *plp, const char *pname,
6983431Scarlsonj     uchar_t recv_type)
6993431Scarlsonj {
7003431Scarlsonj 	const dhcpv6_option_t *d6o;
7013431Scarlsonj 	uint_t olen;
7023431Scarlsonj 	const char *estr, *msg;
7033431Scarlsonj 	uint_t msglen;
7043431Scarlsonj 	int status;
7053431Scarlsonj 
7064516Scarlsonj 	/* Account for received and processed messages */
7074516Scarlsonj 	dsmp->dsm_received++;
7084516Scarlsonj 
7094516Scarlsonj 	/* We don't yet support Reconfigure at all. */
7104516Scarlsonj 	if (recv_type == DHCPV6_MSG_RECONFIGURE) {
7114516Scarlsonj 		dhcpmsg(MSG_VERBOSE, "accept_v6_message: ignored Reconfigure "
7124516Scarlsonj 		    "on %s", dsmp->dsm_name);
7134516Scarlsonj 		free_pkt_entry(plp);
7144516Scarlsonj 		return;
7154516Scarlsonj 	}
7164516Scarlsonj 
7173431Scarlsonj 	/*
7183431Scarlsonj 	 * All valid DHCPv6 messages must have our Client ID specified.
7193431Scarlsonj 	 */
7203431Scarlsonj 	d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_CLIENTID, &olen);
7213431Scarlsonj 	olen -= sizeof (*d6o);
7223431Scarlsonj 	if (d6o == NULL || olen != dsmp->dsm_cidlen ||
7233431Scarlsonj 	    memcmp(d6o + 1, dsmp->dsm_cid, olen) != 0) {
7243431Scarlsonj 		dhcpmsg(MSG_VERBOSE,
7253431Scarlsonj 		    "accept_v6_message: discarded %s on %s: %s Client ID",
7263431Scarlsonj 		    pname, dsmp->dsm_name, d6o == NULL ? "no" : "wrong");
7273431Scarlsonj 		free_pkt_entry(plp);
7280Sstevel@tonic-gate 		return;
7290Sstevel@tonic-gate 	}
7300Sstevel@tonic-gate 
7313431Scarlsonj 	/*
7323431Scarlsonj 	 * All valid DHCPv6 messages must have a Server ID specified.
7333431Scarlsonj 	 *
7343431Scarlsonj 	 * If this is a Reply and it's not in response to Solicit, Confirm,
7353431Scarlsonj 	 * Rebind, or Information-Request, then it must also match the Server
7363431Scarlsonj 	 * ID we're expecting.
7373431Scarlsonj 	 *
7383431Scarlsonj 	 * For Reply in the Solicit, Confirm, Rebind, and Information-Request
7393431Scarlsonj 	 * cases, the Server ID needs to be saved.  This is done inside of
7403431Scarlsonj 	 * dhcp_bound().
7413431Scarlsonj 	 */
7423431Scarlsonj 	d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_SERVERID, &olen);
7433431Scarlsonj 	if (d6o == NULL) {
7443431Scarlsonj 		dhcpmsg(MSG_DEBUG,
7453431Scarlsonj 		    "accept_v6_message: discarded %s on %s: no Server ID",
7463431Scarlsonj 		    pname, dsmp->dsm_name);
7473431Scarlsonj 		free_pkt_entry(plp);
7483431Scarlsonj 		return;
7493431Scarlsonj 	}
7503431Scarlsonj 	if (recv_type == DHCPV6_MSG_REPLY && dsmp->dsm_state != SELECTING &&
7513431Scarlsonj 	    dsmp->dsm_state != INIT_REBOOT && dsmp->dsm_state != REBINDING &&
7523431Scarlsonj 	    dsmp->dsm_state != INFORM_SENT) {
7533431Scarlsonj 		olen -= sizeof (*d6o);
7543431Scarlsonj 		if (olen != dsmp->dsm_serveridlen ||
7553431Scarlsonj 		    memcmp(d6o + 1, dsmp->dsm_serverid, olen) != 0) {
7563431Scarlsonj 			dhcpmsg(MSG_DEBUG, "accept_v6_message: discarded %s on "
7573431Scarlsonj 			    "%s: wrong Server ID", pname, dsmp->dsm_name);
7583431Scarlsonj 			free_pkt_entry(plp);
7593431Scarlsonj 			return;
7603431Scarlsonj 		}
7613431Scarlsonj 	}
7623431Scarlsonj 
7633431Scarlsonj 	/*
7643431Scarlsonj 	 * Break out of the switch if the input message needs to be discarded.
7653431Scarlsonj 	 * Return from the function if the message has been enqueued or
7663431Scarlsonj 	 * consumed.
7673431Scarlsonj 	 */
7683431Scarlsonj 	switch (dsmp->dsm_state) {
7693431Scarlsonj 	case SELECTING:
7703431Scarlsonj 		/* A Reply message signifies a Rapid-Commit. */
7713431Scarlsonj 		if (recv_type == DHCPV6_MSG_REPLY) {
7723431Scarlsonj 			if (dhcpv6_pkt_option(plp, NULL,
7733431Scarlsonj 			    DHCPV6_OPT_RAPID_COMMIT, &olen) == NULL) {
7743431Scarlsonj 				dhcpmsg(MSG_DEBUG, "accept_v6_message: Reply "
7753431Scarlsonj 				    "on %s lacks Rapid-Commit; ignoring",
7763431Scarlsonj 				    dsmp->dsm_name);
7773431Scarlsonj 				break;
7783431Scarlsonj 			}
7793431Scarlsonj 			dhcpmsg(MSG_VERBOSE,
7803431Scarlsonj 			    "accept_v6_message: rapid-commit Reply on %s",
7813431Scarlsonj 			    dsmp->dsm_name);
7823431Scarlsonj 			cancel_offer_timer(dsmp);
7833431Scarlsonj 			goto rapid_commit;
7843431Scarlsonj 		}
7853431Scarlsonj 
7863431Scarlsonj 		/* Otherwise, we're looking for Advertisements. */
7873431Scarlsonj 		if (recv_type != DHCPV6_MSG_ADVERTISE)
7883431Scarlsonj 			break;
7893431Scarlsonj 
7903431Scarlsonj 		/*
7913431Scarlsonj 		 * Special case: if this advertisement has preference 255, then
7923431Scarlsonj 		 * we must stop right now and select this server.
7933431Scarlsonj 		 */
7943431Scarlsonj 		d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_PREFERENCE,
7953431Scarlsonj 		    &olen);
7963431Scarlsonj 		if (d6o != NULL && olen == sizeof (*d6o) + 1 &&
7973431Scarlsonj 		    *(const uchar_t *)(d6o + 1) == 255) {
7983431Scarlsonj 			pkt_smach_enqueue(dsmp, plp);
7993431Scarlsonj 			dhcpmsg(MSG_DEBUG, "accept_v6_message: preference 255;"
8003431Scarlsonj 			    " immediate Request on %s", dsmp->dsm_name);
8013431Scarlsonj 			dhcp_requesting(NULL, dsmp);
8023431Scarlsonj 		} else {
8033431Scarlsonj 			pkt_smach_enqueue(dsmp, plp);
8043431Scarlsonj 		}
8053431Scarlsonj 		return;
8063431Scarlsonj 
8073431Scarlsonj 	case PRE_BOUND:
8083431Scarlsonj 	case BOUND:
8093431Scarlsonj 		/*
8103431Scarlsonj 		 * Not looking for anything in these states.  (If we
8113431Scarlsonj 		 * implemented reconfigure, that might go here.)
8123431Scarlsonj 		 */
8133431Scarlsonj 		break;
8140Sstevel@tonic-gate 
8153431Scarlsonj 	case REQUESTING:
8163431Scarlsonj 	case INIT_REBOOT:
8173431Scarlsonj 	case RENEWING:
8183431Scarlsonj 	case REBINDING:
8193431Scarlsonj 	case INFORM_SENT:
8203431Scarlsonj 		/*
8213431Scarlsonj 		 * We're looking for Reply messages.
8223431Scarlsonj 		 */
8233431Scarlsonj 		if (recv_type != DHCPV6_MSG_REPLY)
8243431Scarlsonj 			break;
8253431Scarlsonj 		dhcpmsg(MSG_VERBOSE,
8263431Scarlsonj 		    "accept_v6_message: received Reply message on %s",
8273431Scarlsonj 		    dsmp->dsm_name);
8283431Scarlsonj 	rapid_commit:
8293431Scarlsonj 		/*
8303431Scarlsonj 		 * Extract the status code option.  If one is present and the
8313431Scarlsonj 		 * request failed, then try to go to another advertisement in
8323431Scarlsonj 		 * the list or restart the selection machinery.
8333431Scarlsonj 		 */
8343431Scarlsonj 		d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_STATUS_CODE,
8353431Scarlsonj 		    &olen);
8363431Scarlsonj 		status = dhcpv6_status_code(d6o, olen, &estr, &msg, &msglen);
8373431Scarlsonj 		/*
8383431Scarlsonj 		 * Check for the UseMulticast status code.  If this is present,
8393431Scarlsonj 		 * and if we were actually using unicast, then drop back and
8403431Scarlsonj 		 * try again.  If we weren't using unicast, then just pretend
8413431Scarlsonj 		 * we never saw this message -- the peer is confused.  (TAHI
8423431Scarlsonj 		 * does this.)
8433431Scarlsonj 		 */
8443431Scarlsonj 		if (status == DHCPV6_STAT_USEMCAST) {
8453431Scarlsonj 			if (IN6_IS_ADDR_MULTICAST(
8463431Scarlsonj 			    &dsmp->dsm_send_dest.v6.sin6_addr)) {
8473431Scarlsonj 				break;
8483431Scarlsonj 			} else {
8493431Scarlsonj 				free_pkt_entry(plp);
8503431Scarlsonj 				dsmp->dsm_send_dest.v6.sin6_addr =
8513431Scarlsonj 				    ipv6_all_dhcp_relay_and_servers;
8523431Scarlsonj 				retransmit_now(dsmp);
8533431Scarlsonj 				return;
8543431Scarlsonj 			}
8553431Scarlsonj 		}
8563431Scarlsonj 		print_server_msg(dsmp, msg, msglen);
8573431Scarlsonj 		/*
8583431Scarlsonj 		 * We treat NoBinding at the top level as "success."  Granted,
8593431Scarlsonj 		 * this doesn't make much sense, but the TAHI test suite does
8603431Scarlsonj 		 * this.  NoBinding really only makes sense in the context of a
8613431Scarlsonj 		 * specific IA, as it refers to the GUID:IAID binding, so
8623431Scarlsonj 		 * ignoring it at the top level is safe.
8633431Scarlsonj 		 */
8643431Scarlsonj 		if (status == DHCPV6_STAT_SUCCESS ||
8653431Scarlsonj 		    status == DHCPV6_STAT_NOBINDING) {
8663431Scarlsonj 			if (dhcp_bound(dsmp, plp)) {
8673431Scarlsonj 				/*
8683431Scarlsonj 				 * dhcp_bound will stop retransmission on
8693431Scarlsonj 				 * success, if that's called for.
8703431Scarlsonj 				 */
8713431Scarlsonj 				server_unicast_option(dsmp, plp);
8723431Scarlsonj 			} else {
8733431Scarlsonj 				stop_pkt_retransmission(dsmp);
8743431Scarlsonj 				dhcpmsg(MSG_WARNING, "accept_v6_message: "
8753431Scarlsonj 				    "dhcp_bound failed for %s", dsmp->dsm_name);
8763431Scarlsonj 				(void) remove_hostconf(dsmp->dsm_name,
8773431Scarlsonj 				    dsmp->dsm_isv6);
8784516Scarlsonj 				dhcp_restart(dsmp);
8793431Scarlsonj 			}
8803431Scarlsonj 		} else {
8813431Scarlsonj 			dhcpmsg(MSG_WARNING, "accept_v6_message: Reply: %s",
8823431Scarlsonj 			    estr);
8833431Scarlsonj 			stop_pkt_retransmission(dsmp);
8843431Scarlsonj 			free_pkt_entry(plp);
8853431Scarlsonj 			if (dsmp->dsm_state == INFORM_SENT) {
8863431Scarlsonj 				(void) set_smach_state(dsmp, INIT);
8873431Scarlsonj 				ipc_action_finish(dsmp, DHCP_IPC_E_SRVFAILED);
8883431Scarlsonj 			} else {
8893431Scarlsonj 				(void) remove_hostconf(dsmp->dsm_name,
8903431Scarlsonj 				    dsmp->dsm_isv6);
8913431Scarlsonj 				request_failed(dsmp);
8923431Scarlsonj 			}
8933431Scarlsonj 		}
8943431Scarlsonj 		return;
8953431Scarlsonj 
8963431Scarlsonj 	case DECLINING:
8973431Scarlsonj 		/*
8983431Scarlsonj 		 * We're looking for Reply messages.
8993431Scarlsonj 		 */
9003431Scarlsonj 		if (recv_type != DHCPV6_MSG_REPLY)
9013431Scarlsonj 			break;
9023431Scarlsonj 		stop_pkt_retransmission(dsmp);
9033431Scarlsonj 		/*
9043431Scarlsonj 		 * Extract the status code option.  Note that it's not a
9053431Scarlsonj 		 * failure if the server reports an error.
9063431Scarlsonj 		 */
9073431Scarlsonj 		d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_STATUS_CODE,
9083431Scarlsonj 		    &olen);
9093431Scarlsonj 		if (dhcpv6_status_code(d6o, olen, &estr, &msg,
9103431Scarlsonj 		    &msglen) == DHCPV6_STAT_SUCCESS) {
9113431Scarlsonj 			print_server_msg(dsmp, msg, msglen);
9123431Scarlsonj 		} else {
9133431Scarlsonj 			dhcpmsg(MSG_WARNING, "accept_v6_message: Reply: %s",
9143431Scarlsonj 			    estr);
9153431Scarlsonj 		}
9163431Scarlsonj 		free_pkt_entry(plp);
9173431Scarlsonj 		if (dsmp->dsm_leases == NULL) {
9183431Scarlsonj 			dhcpmsg(MSG_VERBOSE, "accept_v6_message: %s has no "
9194516Scarlsonj 			    "leases left", dsmp->dsm_name);
9203431Scarlsonj 			dhcp_restart(dsmp);
9213431Scarlsonj 		} else if (dsmp->dsm_lif_wait == 0) {
9223431Scarlsonj 			(void) set_smach_state(dsmp, BOUND);
9233431Scarlsonj 		} else {
9243431Scarlsonj 			(void) set_smach_state(dsmp, PRE_BOUND);
9253431Scarlsonj 		}
9263431Scarlsonj 		return;
9273431Scarlsonj 
9283431Scarlsonj 	case RELEASING:
9293431Scarlsonj 		/*
9303431Scarlsonj 		 * We're looking for Reply messages.
9313431Scarlsonj 		 */
9323431Scarlsonj 		if (recv_type != DHCPV6_MSG_REPLY)
9333431Scarlsonj 			break;
9343431Scarlsonj 		stop_pkt_retransmission(dsmp);
9353431Scarlsonj 		/*
9363431Scarlsonj 		 * Extract the status code option.
9373431Scarlsonj 		 */
9383431Scarlsonj 		d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_STATUS_CODE,
9393431Scarlsonj 		    &olen);
9403431Scarlsonj 		if (dhcpv6_status_code(d6o, olen, &estr, &msg,
9413431Scarlsonj 		    &msglen) == DHCPV6_STAT_SUCCESS) {
9423431Scarlsonj 			print_server_msg(dsmp, msg, msglen);
9433431Scarlsonj 		} else {
9443431Scarlsonj 			dhcpmsg(MSG_WARNING, "accept_v6_message: Reply: %s",
9453431Scarlsonj 			    estr);
9463431Scarlsonj 		}
9473431Scarlsonj 		free_pkt_entry(plp);
9483431Scarlsonj 		finished_smach(dsmp, DHCP_IPC_SUCCESS);
9490Sstevel@tonic-gate 		return;
9500Sstevel@tonic-gate 	}
9510Sstevel@tonic-gate 
9523431Scarlsonj 	/*
9533431Scarlsonj 	 * Break from above switch means that the message must be discarded.
9543431Scarlsonj 	 */
9553431Scarlsonj 	dhcpmsg(MSG_VERBOSE,
9563431Scarlsonj 	    "accept_v6_message: discarded v6 %s on %s; state %s",
9573431Scarlsonj 	    pname, dsmp->dsm_name, dhcp_state_to_string(dsmp->dsm_state));
9583431Scarlsonj 	free_pkt_entry(plp);
9590Sstevel@tonic-gate }
9600Sstevel@tonic-gate 
9610Sstevel@tonic-gate /*
962*5381Smeem  * dhcp_acknak_global(): Processes reception of an ACK or NAK packet on the
9633431Scarlsonj  *			 global socket -- broadcast packets for IPv4, all
9643431Scarlsonj  *			 packets for DHCPv6.
9650Sstevel@tonic-gate  *
9663431Scarlsonj  *   input: iu_eh_t *: unused
9673431Scarlsonj  *	    int: the global file descriptor the ACK/NAK arrived on
9683431Scarlsonj  *	    short: unused
9693431Scarlsonj  *	    iu_event_id_t: unused
9703431Scarlsonj  *	    void *: unused
9713431Scarlsonj  *  output: void
9723431Scarlsonj  */
9733431Scarlsonj 
9743431Scarlsonj /* ARGSUSED */
9753431Scarlsonj void
976*5381Smeem dhcp_acknak_global(iu_eh_t *ehp, int fd, short events, iu_event_id_t id,
9773431Scarlsonj     void *arg)
9783431Scarlsonj {
9793431Scarlsonj 	PKT_LIST	*plp;
9803431Scarlsonj 	dhcp_pif_t	*pif;
9813431Scarlsonj 	uchar_t		recv_type;
9823431Scarlsonj 	const char	*pname;
9833431Scarlsonj 	uint_t		xid;
9843431Scarlsonj 	dhcp_smach_t	*dsmp;
9853431Scarlsonj 	boolean_t	isv6 = (fd == v6_sock_fd);
986*5381Smeem 	struct sockaddr_in sin;
987*5381Smeem 	const char	*reason;
988*5381Smeem 	size_t		sinlen = sizeof (sin);
989*5381Smeem 	int		sock;
9903431Scarlsonj 
991*5381Smeem 	plp = recv_pkt(fd, get_max_mtu(isv6), isv6);
9924456Sss150715 	if (plp == NULL)
9933431Scarlsonj 		return;
9943431Scarlsonj 
9953431Scarlsonj 	pif = lookup_pif_by_index(plp->ifindex, isv6);
9963431Scarlsonj 	if (pif == NULL) {
997*5381Smeem 		dhcpmsg(MSG_VERBOSE, "dhcp_acknak_global: ignored packet "
9983431Scarlsonj 		    "received on v%d ifIndex %d", isv6 ? 6 : 4, plp->ifindex);
9993431Scarlsonj 		free_pkt_entry(plp);
10003431Scarlsonj 		return;
10013431Scarlsonj 	}
10023431Scarlsonj 
10033431Scarlsonj 	recv_type = pkt_recv_type(plp);
10043431Scarlsonj 	pname = pkt_type_to_string(recv_type, isv6);
10053431Scarlsonj 
10063431Scarlsonj 	/*
1007*5381Smeem 	 * Find the corresponding state machine.
10083431Scarlsonj 	 *
10093431Scarlsonj 	 * Note that DHCPv6 Reconfigure would be special: it's not the reply to
10103431Scarlsonj 	 * any transaction, and thus we would need to search on transaction ID
1011*5381Smeem 	 * zero (all state machines) to find the match.	 However, Reconfigure
10123431Scarlsonj 	 * is not yet supported.
10133431Scarlsonj 	 */
10143431Scarlsonj 	xid = pkt_get_xid(plp->pkt, isv6);
1015*5381Smeem 	if (!isv6 && !pkt_v4_match(recv_type, DHCP_PACK|DHCP_PNAK)) {
1016*5381Smeem 		reason = "not ACK or NAK";
1017*5381Smeem 		goto drop;
1018*5381Smeem 	}
1019*5381Smeem 
10203431Scarlsonj 	for (dsmp = lookup_smach_by_xid(xid, NULL, isv6); dsmp != NULL;
10213431Scarlsonj 	    dsmp = lookup_smach_by_xid(xid, dsmp, isv6)) {
10223431Scarlsonj 		if (dsmp->dsm_lif->lif_pif == pif)
10233431Scarlsonj 			break;
10243431Scarlsonj 	}
1025*5381Smeem 
1026*5381Smeem 	if (dsmp == NULL) {
1027*5381Smeem 		reason = "unknown state machine";
1028*5381Smeem 		goto drop;
1029*5381Smeem 	}
1030*5381Smeem 
1031*5381Smeem 	/*
1032*5381Smeem 	 * For IPv4, most packets will be handled by dhcp_packet_lif().  The
1033*5381Smeem 	 * only exceptions are broadcast packets sent when lif_sock_ip_fd has
1034*5381Smeem 	 * bound to something other than INADDR_ANY.
1035*5381Smeem 	 */
1036*5381Smeem 	if (!isv6) {
1037*5381Smeem 		sock = dsmp->dsm_lif->lif_sock_ip_fd;
1038*5381Smeem 
1039*5381Smeem 		if (getsockname(sock, (struct sockaddr *)&sin, &sinlen) != -1 &&
1040*5381Smeem 		    sin.sin_addr.s_addr == INADDR_ANY) {
1041*5381Smeem 			reason = "handled by lif_sock_ip_fd";
1042*5381Smeem 			goto drop;
1043*5381Smeem 		}
10443431Scarlsonj 	}
10453431Scarlsonj 
10463431Scarlsonj 	/*
10473431Scarlsonj 	 * We've got a packet; make sure it's acceptable and cancel the REQUEST
10483431Scarlsonj 	 * retransmissions.
10493431Scarlsonj 	 */
10503431Scarlsonj 	if (isv6)
10513431Scarlsonj 		accept_v6_message(dsmp, plp, pname, recv_type);
10523431Scarlsonj 	else
10533431Scarlsonj 		accept_v4_acknak(dsmp, plp);
1054*5381Smeem 	return;
1055*5381Smeem drop:
1056*5381Smeem 	dhcpmsg(MSG_VERBOSE, "dhcp_acknak_global: ignored v%d %s packet for %s "
1057*5381Smeem 	    "received on global socket: %s", isv6 ? 6 : 4, pname, pif->pif_name,
1058*5381Smeem 	    reason);
1059*5381Smeem 	free_pkt_entry(plp);
10603431Scarlsonj }
10613431Scarlsonj 
10623431Scarlsonj /*
10633431Scarlsonj  * request_failed(): Attempt to request an address has failed.  Take an
10643431Scarlsonj  *		     appropriate action.
10653431Scarlsonj  *
10663431Scarlsonj  *   input: dhcp_smach_t *: state machine that has failed
10673431Scarlsonj  *  output: void
10683431Scarlsonj  */
10693431Scarlsonj 
10703431Scarlsonj static void
10713431Scarlsonj request_failed(dhcp_smach_t *dsmp)
10723431Scarlsonj {
10733431Scarlsonj 	PKT_LIST *offer;
10743431Scarlsonj 
10753431Scarlsonj 	dsmp->dsm_server = ipv6_all_dhcp_relay_and_servers;
10763431Scarlsonj 	if ((offer = select_best(dsmp)) != NULL) {
10773431Scarlsonj 		insque(offer, &dsmp->dsm_recv_pkt_list);
10783431Scarlsonj 		dhcp_requesting(NULL, dsmp);
10793431Scarlsonj 	} else {
10803431Scarlsonj 		dhcpmsg(MSG_INFO, "no offers left on %s; restarting",
10813431Scarlsonj 		    dsmp->dsm_name);
10823431Scarlsonj 		dhcp_selecting(dsmp);
10833431Scarlsonj 	}
10843431Scarlsonj }
10853431Scarlsonj 
10863431Scarlsonj /*
1087*5381Smeem  * dhcp_packet_lif(): Processes reception of an ACK, NAK, or OFFER packet on
1088*5381Smeem  *		      a given logical interface for IPv4 (only).
10893431Scarlsonj  *
10903431Scarlsonj  *   input: iu_eh_t *: unused
1091*5381Smeem  *	    int: the file descriptor the packet arrived on
10923431Scarlsonj  *	    short: unused
10933431Scarlsonj  *	    iu_event_id_t: the id of this event callback with the handler
10943431Scarlsonj  *	    void *: pointer to logical interface receiving message
10953431Scarlsonj  *  output: void
10963431Scarlsonj  */
10973431Scarlsonj 
10983431Scarlsonj /* ARGSUSED */
10993431Scarlsonj void
1100*5381Smeem dhcp_packet_lif(iu_eh_t *ehp, int fd, short events, iu_event_id_t id,
11013431Scarlsonj     void *arg)
11023431Scarlsonj {
11033431Scarlsonj 	dhcp_lif_t	*lif = arg;
11043431Scarlsonj 	PKT_LIST	*plp;
11053431Scarlsonj 	uchar_t		recv_type;
11063431Scarlsonj 	const char	*pname;
11073431Scarlsonj 	uint_t		xid;
11083431Scarlsonj 	dhcp_smach_t	*dsmp;
11093431Scarlsonj 
1110*5381Smeem 	if ((plp = recv_pkt(fd, lif->lif_max, B_FALSE)) == NULL)
11113431Scarlsonj 		return;
11123431Scarlsonj 
11133431Scarlsonj 	recv_type = pkt_recv_type(plp);
11143431Scarlsonj 	pname = pkt_type_to_string(recv_type, B_FALSE);
11153431Scarlsonj 
1116*5381Smeem 	if (!pkt_v4_match(recv_type,
1117*5381Smeem 	    DHCP_PACK | DHCP_PNAK | DHCP_PUNTYPED | DHCP_POFFER)) {
1118*5381Smeem 		dhcpmsg(MSG_VERBOSE, "dhcp_packet_lif: ignored v4 %s packet "
11193431Scarlsonj 		    "received via LIF %s", pname, lif->lif_name);
11203431Scarlsonj 		free_pkt_entry(plp);
11213431Scarlsonj 		return;
11223431Scarlsonj 	}
11233431Scarlsonj 
11243431Scarlsonj 	/*
1125*5381Smeem 	 * Find the corresponding state machine.
11263431Scarlsonj 	 */
11273431Scarlsonj 	xid = pkt_get_xid(plp->pkt, B_FALSE);
11283431Scarlsonj 	for (dsmp = lookup_smach_by_xid(xid, NULL, B_FALSE); dsmp != NULL;
11293431Scarlsonj 	    dsmp = lookup_smach_by_xid(xid, dsmp, B_FALSE)) {
11303431Scarlsonj 		if (dsmp->dsm_lif == lif)
11313431Scarlsonj 			break;
11323431Scarlsonj 	}
1133*5381Smeem 
1134*5381Smeem 	if (dsmp == NULL)
1135*5381Smeem 		goto drop;
1136*5381Smeem 
1137*5381Smeem 	if (pkt_v4_match(recv_type, DHCP_PACK|DHCP_PNAK)) {
1138*5381Smeem 		/*
1139*5381Smeem 		 * We've got an ACK/NAK; make sure it's acceptable and cancel
1140*5381Smeem 		 * the REQUEST retransmissions.
1141*5381Smeem 		 */
1142*5381Smeem 		accept_v4_acknak(dsmp, plp);
1143*5381Smeem 	} else {
1144*5381Smeem 		if (is_bound_state(dsmp->dsm_state))
1145*5381Smeem 			goto drop;
1146*5381Smeem 		/*
1147*5381Smeem 		 * Must be an OFFER or a BOOTP message: enqueue it for later
1148*5381Smeem 		 * processing by select_best().
1149*5381Smeem 		 */
1150*5381Smeem 		pkt_smach_enqueue(dsmp, plp);
11513431Scarlsonj 	}
1152*5381Smeem 	return;
1153*5381Smeem drop:
1154*5381Smeem 	dhcpmsg(MSG_VERBOSE, "dhcp_packet_lif: ignored %s packet xid "
1155*5381Smeem 	    "%x received via LIF %s; %s", pname, xid, lif->lif_name,
1156*5381Smeem 	    dsmp == NULL ? "unknown state machine" : "bound");
1157*5381Smeem 	free_pkt_entry(plp);
11583431Scarlsonj }
11593431Scarlsonj 
11603431Scarlsonj /*
11614516Scarlsonj  * dhcp_restart(): restarts DHCP (from INIT) on a given state machine, but only
11624516Scarlsonj  *		   if we're leasing addresses.  Doesn't restart for information-
11634516Scarlsonj  *		   only interfaces.
11643431Scarlsonj  *
11653431Scarlsonj  *   input: dhcp_smach_t *: the state machine to restart DHCP on
11660Sstevel@tonic-gate  *  output: void
11670Sstevel@tonic-gate  */
11680Sstevel@tonic-gate 
11692546Scarlsonj void
11703431Scarlsonj dhcp_restart(dhcp_smach_t *dsmp)
11710Sstevel@tonic-gate {
11724516Scarlsonj 	if (dsmp->dsm_state == INFORM_SENT || dsmp->dsm_state == INFORMATION)
11734516Scarlsonj 		return;
11744516Scarlsonj 
11753431Scarlsonj 	/*
11763431Scarlsonj 	 * As we're returning to INIT state, we need to discard any leases we
11773431Scarlsonj 	 * may have, and (for v4) canonize the LIF.  There's a bit of tension
11783431Scarlsonj 	 * between keeping around a possibly still working address, and obeying
11793431Scarlsonj 	 * the RFCs.  A more elaborate design would be to mark the addresses as
11803431Scarlsonj 	 * DEPRECATED, and then start a removal timer.  Such a design would
11813431Scarlsonj 	 * probably compromise testing.
11823431Scarlsonj 	 */
11833431Scarlsonj 	deprecate_leases(dsmp);
11840Sstevel@tonic-gate 
11854106Scarlsonj 	if (!set_start_timer(dsmp)) {
11862546Scarlsonj 		dhcpmsg(MSG_ERROR, "dhcp_restart: cannot schedule dhcp_start, "
11873431Scarlsonj 		    "reverting to INIT state on %s", dsmp->dsm_name);
11883431Scarlsonj 
11893431Scarlsonj 		(void) set_smach_state(dsmp, INIT);
11903431Scarlsonj 		dsmp->dsm_dflags |= DHCP_IF_FAILED;
11913431Scarlsonj 		ipc_action_finish(dsmp, DHCP_IPC_E_MEMORY);
11924516Scarlsonj 	} else {
11934516Scarlsonj 		dhcpmsg(MSG_DEBUG, "dhcp_restart: restarting DHCP on %s",
11944516Scarlsonj 		    dsmp->dsm_name);
11953431Scarlsonj 	}
11960Sstevel@tonic-gate }
11970Sstevel@tonic-gate 
11980Sstevel@tonic-gate /*
11990Sstevel@tonic-gate  * stop_requesting(): decides when to stop retransmitting REQUESTs
12000Sstevel@tonic-gate  *
12013431Scarlsonj  *   input: dhcp_smach_t *: the state machine REQUESTs are being sent from
12020Sstevel@tonic-gate  *	    unsigned int: the number of REQUESTs sent so far
12030Sstevel@tonic-gate  *  output: boolean_t: B_TRUE if retransmissions should stop
12040Sstevel@tonic-gate  */
12050Sstevel@tonic-gate 
12060Sstevel@tonic-gate static boolean_t
12073431Scarlsonj stop_requesting(dhcp_smach_t *dsmp, unsigned int n_requests)
12080Sstevel@tonic-gate {
12093431Scarlsonj 	uint_t maxreq;
12100Sstevel@tonic-gate 
12113431Scarlsonj 	maxreq = dsmp->dsm_isv6 ? DHCPV6_REQ_MAX_RC : DHCP_MAX_REQUESTS;
12123431Scarlsonj 	if (n_requests >= maxreq) {
12130Sstevel@tonic-gate 
12143431Scarlsonj 		dhcpmsg(MSG_INFO, "no ACK/NAK/Reply to REQUEST on %s",
12153431Scarlsonj 		    dsmp->dsm_name);
12160Sstevel@tonic-gate 
12173431Scarlsonj 		request_failed(dsmp);
12180Sstevel@tonic-gate 		return (B_TRUE);
12193431Scarlsonj 	} else {
12203431Scarlsonj 		return (B_FALSE);
12210Sstevel@tonic-gate 	}
12220Sstevel@tonic-gate }
1223