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 /*
222157Sdh155122  * Copyright 2006 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 
300Sstevel@tonic-gate #include <sys/types.h>
310Sstevel@tonic-gate #include <sys/stropts.h>	/* FLUSHR/FLUSHW */
320Sstevel@tonic-gate #include <netinet/in.h>
330Sstevel@tonic-gate #include <netinet/dhcp.h>
340Sstevel@tonic-gate #include <netinet/udp.h>
350Sstevel@tonic-gate #include <netinet/ip_var.h>
360Sstevel@tonic-gate #include <netinet/udp_var.h>
370Sstevel@tonic-gate #include <dhcp_hostconf.h>
380Sstevel@tonic-gate #include <arpa/inet.h>
390Sstevel@tonic-gate #include <string.h>
400Sstevel@tonic-gate #include <unistd.h>
410Sstevel@tonic-gate #include <dhcpmsg.h>
420Sstevel@tonic-gate 
430Sstevel@tonic-gate #include "states.h"
440Sstevel@tonic-gate #include "util.h"
450Sstevel@tonic-gate #include "packet.h"
460Sstevel@tonic-gate #include "interface.h"
470Sstevel@tonic-gate #include "agent.h"
480Sstevel@tonic-gate 
490Sstevel@tonic-gate static PKT_LIST		*select_best(PKT_LIST **);
500Sstevel@tonic-gate static stop_func_t	stop_requesting;
510Sstevel@tonic-gate 
520Sstevel@tonic-gate /*
530Sstevel@tonic-gate  * dhcp_requesting(): checks if OFFER packets to come in from DHCP servers.
540Sstevel@tonic-gate  *		      if so, chooses the best one, sends a REQUEST to the
550Sstevel@tonic-gate  *		      server and registers an event handler to receive
560Sstevel@tonic-gate  *		      the ACK/NAK
570Sstevel@tonic-gate  *
580Sstevel@tonic-gate  *   input: iu_tq_t *: unused
590Sstevel@tonic-gate  *	    void *: the interface receiving OFFER packets
600Sstevel@tonic-gate  *  output: void
610Sstevel@tonic-gate  */
620Sstevel@tonic-gate 
630Sstevel@tonic-gate /* ARGSUSED */
640Sstevel@tonic-gate void
650Sstevel@tonic-gate dhcp_requesting(iu_tq_t *tqp, void *arg)
660Sstevel@tonic-gate {
670Sstevel@tonic-gate 	struct ifslist		*ifsp = (struct ifslist *)arg;
680Sstevel@tonic-gate 	dhcp_pkt_t		*dpkt;
690Sstevel@tonic-gate 	PKT_LIST		*offer;
700Sstevel@tonic-gate 	lease_t			lease;
710Sstevel@tonic-gate 
722157Sdh155122 	ifsp->if_offer_timer = -1;
732157Sdh155122 
740Sstevel@tonic-gate 	if (check_ifs(ifsp) == 0) {
750Sstevel@tonic-gate 		(void) release_ifs(ifsp);
760Sstevel@tonic-gate 		return;
770Sstevel@tonic-gate 	}
780Sstevel@tonic-gate 
790Sstevel@tonic-gate 	/*
800Sstevel@tonic-gate 	 * select the best OFFER; all others pitched.
810Sstevel@tonic-gate 	 */
820Sstevel@tonic-gate 
830Sstevel@tonic-gate 	offer = select_best(&ifsp->if_recv_pkt_list);
840Sstevel@tonic-gate 	if (offer == NULL) {
850Sstevel@tonic-gate 
860Sstevel@tonic-gate 		dhcpmsg(MSG_VERBOSE, "no OFFERs on %s, waiting...",
870Sstevel@tonic-gate 		    ifsp->if_name);
880Sstevel@tonic-gate 
890Sstevel@tonic-gate 		/*
900Sstevel@tonic-gate 		 * no acceptable OFFERs have come in.  reschedule
910Sstevel@tonic-gate 		 * ourselves for callback.
920Sstevel@tonic-gate 		 */
930Sstevel@tonic-gate 
942157Sdh155122 		if ((ifsp->if_offer_timer = iu_schedule_timer(tq,
952157Sdh155122 		    ifsp->if_offer_wait, dhcp_requesting, ifsp)) == -1) {
960Sstevel@tonic-gate 
970Sstevel@tonic-gate 			/*
980Sstevel@tonic-gate 			 * ugh.  the best we can do at this point is
990Sstevel@tonic-gate 			 * revert back to INIT and wait for a user to
1000Sstevel@tonic-gate 			 * restart us.
1010Sstevel@tonic-gate 			 */
1020Sstevel@tonic-gate 
1030Sstevel@tonic-gate 			ifsp->if_state	 = INIT;
1040Sstevel@tonic-gate 			ifsp->if_dflags |= DHCP_IF_FAILED;
1050Sstevel@tonic-gate 
1060Sstevel@tonic-gate 			stop_pkt_retransmission(ifsp);
1070Sstevel@tonic-gate 			ipc_action_finish(ifsp, DHCP_IPC_E_MEMORY);
1080Sstevel@tonic-gate 			async_finish(ifsp);
1090Sstevel@tonic-gate 
1100Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "dhcp_requesting: cannot "
1110Sstevel@tonic-gate 			    "reschedule callback, reverting to INIT state on "
1120Sstevel@tonic-gate 			    "%s", ifsp->if_name);
1130Sstevel@tonic-gate 		} else
1140Sstevel@tonic-gate 			hold_ifs(ifsp);
1150Sstevel@tonic-gate 
1160Sstevel@tonic-gate 		return;
1170Sstevel@tonic-gate 	}
1180Sstevel@tonic-gate 
1190Sstevel@tonic-gate 	stop_pkt_retransmission(ifsp);
1200Sstevel@tonic-gate 
1210Sstevel@tonic-gate 	/*
1220Sstevel@tonic-gate 	 * stop collecting packets.  check to see whether we got an
1230Sstevel@tonic-gate 	 * OFFER or a BOOTP packet.  if we got a BOOTP packet, go to
1240Sstevel@tonic-gate 	 * the BOUND state now.
1250Sstevel@tonic-gate 	 */
1260Sstevel@tonic-gate 
1270Sstevel@tonic-gate 	if (iu_unregister_event(eh, ifsp->if_offer_id, NULL) != 0) {
1280Sstevel@tonic-gate 		(void) release_ifs(ifsp);
1290Sstevel@tonic-gate 		ifsp->if_offer_id = -1;
1300Sstevel@tonic-gate 	}
1310Sstevel@tonic-gate 
1320Sstevel@tonic-gate 	if (offer->opts[CD_DHCP_TYPE] == NULL) {
1330Sstevel@tonic-gate 
1340Sstevel@tonic-gate 		ifsp->if_state = REQUESTING;
1350Sstevel@tonic-gate 
1360Sstevel@tonic-gate 		if (dhcp_bound(ifsp, offer) == 0) {
1370Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "dhcp_requesting: dhcp_bound "
1380Sstevel@tonic-gate 			    "failed for %s", ifsp->if_name);
139*2546Scarlsonj 			dhcp_restart(ifsp);
1400Sstevel@tonic-gate 			return;
1410Sstevel@tonic-gate 		}
1420Sstevel@tonic-gate 
1430Sstevel@tonic-gate 		return;
1440Sstevel@tonic-gate 	}
1450Sstevel@tonic-gate 
1460Sstevel@tonic-gate 	/*
1470Sstevel@tonic-gate 	 * if we got a message from the server, display it.
1480Sstevel@tonic-gate 	 */
1490Sstevel@tonic-gate 
1500Sstevel@tonic-gate 	if (offer->opts[CD_MESSAGE] != NULL)
1510Sstevel@tonic-gate 		print_server_msg(ifsp, offer->opts[CD_MESSAGE]);
1520Sstevel@tonic-gate 
1530Sstevel@tonic-gate 	/*
1540Sstevel@tonic-gate 	 * assemble a DHCPREQUEST, with the ciaddr field set to 0,
1550Sstevel@tonic-gate 	 * since we got here from the INIT state.
1560Sstevel@tonic-gate 	 */
1570Sstevel@tonic-gate 
1580Sstevel@tonic-gate 	dpkt = init_pkt(ifsp, REQUEST);
1590Sstevel@tonic-gate 
1600Sstevel@tonic-gate 	/*
1610Sstevel@tonic-gate 	 * grab the lease out of the OFFER; we know it's valid since
1620Sstevel@tonic-gate 	 * select_best() already checked.  The max dhcp message size
1630Sstevel@tonic-gate 	 * option is set to the interface max, minus the size of the udp and
1640Sstevel@tonic-gate 	 * ip headers.
1650Sstevel@tonic-gate 	 */
1660Sstevel@tonic-gate 
1670Sstevel@tonic-gate 	(void) memcpy(&lease, offer->opts[CD_LEASE_TIME]->value,
1680Sstevel@tonic-gate 	    sizeof (lease_t));
1690Sstevel@tonic-gate 
1700Sstevel@tonic-gate 	add_pkt_opt32(dpkt, CD_LEASE_TIME, lease);
1710Sstevel@tonic-gate 	add_pkt_opt16(dpkt, CD_MAX_DHCP_SIZE, htons(ifsp->if_max -
1720Sstevel@tonic-gate 			sizeof (struct udpiphdr)));
1730Sstevel@tonic-gate 	add_pkt_opt32(dpkt, CD_REQUESTED_IP_ADDR, offer->pkt->yiaddr.s_addr);
1740Sstevel@tonic-gate 	add_pkt_opt(dpkt, CD_SERVER_ID, offer->opts[CD_SERVER_ID]->value,
1750Sstevel@tonic-gate 	    offer->opts[CD_SERVER_ID]->len);
1760Sstevel@tonic-gate 
1770Sstevel@tonic-gate 	add_pkt_opt(dpkt, CD_CLASS_ID, class_id, class_id_len);
1780Sstevel@tonic-gate 	add_pkt_opt(dpkt, CD_REQUEST_LIST, ifsp->if_prl, ifsp->if_prllen);
1790Sstevel@tonic-gate 
1800Sstevel@tonic-gate 	/*
1810Sstevel@tonic-gate 	 * if_reqhost was set for this interface in dhcp_selecting()
1820Sstevel@tonic-gate 	 * if the DF_REQUEST_HOSTNAME option set and a host name was
1830Sstevel@tonic-gate 	 * found
1840Sstevel@tonic-gate 	 */
1850Sstevel@tonic-gate 	if (ifsp->if_reqhost != NULL) {
1860Sstevel@tonic-gate 		add_pkt_opt(dpkt, CD_HOSTNAME, ifsp->if_reqhost,
1870Sstevel@tonic-gate 		    strlen(ifsp->if_reqhost));
1880Sstevel@tonic-gate 	}
1890Sstevel@tonic-gate 	add_pkt_opt(dpkt, CD_END, NULL, 0);
1900Sstevel@tonic-gate 
1910Sstevel@tonic-gate 	/* all done with the offer */
1920Sstevel@tonic-gate 	free_pkt_list(&offer);
1930Sstevel@tonic-gate 
1940Sstevel@tonic-gate 	/*
1950Sstevel@tonic-gate 	 * send out the REQUEST, trying retransmissions.  either a NAK
1960Sstevel@tonic-gate 	 * or too many REQUEST attempts will revert us to SELECTING.
1970Sstevel@tonic-gate 	 */
1980Sstevel@tonic-gate 
1990Sstevel@tonic-gate 	ifsp->if_state = REQUESTING;
2000Sstevel@tonic-gate 	(void) send_pkt(ifsp, dpkt, htonl(INADDR_BROADCAST), stop_requesting);
2010Sstevel@tonic-gate 
2020Sstevel@tonic-gate 	/*
2030Sstevel@tonic-gate 	 * wait for an ACK or NAK to come back from the server.  if
2040Sstevel@tonic-gate 	 * we can't register this event handler, then we won't be able
2050Sstevel@tonic-gate 	 * to see the server's responses.  the best we can really do
2060Sstevel@tonic-gate 	 * in that case is drop back to INIT and hope someone notices.
2070Sstevel@tonic-gate 	 */
2080Sstevel@tonic-gate 
2090Sstevel@tonic-gate 	if (register_acknak(ifsp) == 0) {
2100Sstevel@tonic-gate 
2110Sstevel@tonic-gate 		ifsp->if_state	 = INIT;
2120Sstevel@tonic-gate 		ifsp->if_dflags |= DHCP_IF_FAILED;
2130Sstevel@tonic-gate 
2140Sstevel@tonic-gate 		ipc_action_finish(ifsp, DHCP_IPC_E_MEMORY);
2150Sstevel@tonic-gate 		async_finish(ifsp);
2160Sstevel@tonic-gate 
2170Sstevel@tonic-gate 		dhcpmsg(MSG_ERROR, "dhcp_requesting: cannot register to "
2180Sstevel@tonic-gate 		    "collect ACK/NAK packets, reverting to INIT on %s",
2190Sstevel@tonic-gate 		    ifsp->if_name);
2200Sstevel@tonic-gate 	}
2210Sstevel@tonic-gate }
2220Sstevel@tonic-gate 
2230Sstevel@tonic-gate /*
2240Sstevel@tonic-gate  * select_best(): selects the best OFFER packet from a list of OFFER packets
2250Sstevel@tonic-gate  *
2260Sstevel@tonic-gate  *   input: PKT_LIST **: a list of packets to select the best from
2270Sstevel@tonic-gate  *  output: PKT_LIST *: the best packet, or NULL if none are acceptable
2280Sstevel@tonic-gate  */
2290Sstevel@tonic-gate 
2300Sstevel@tonic-gate static PKT_LIST *
2310Sstevel@tonic-gate select_best(PKT_LIST **pkts)
2320Sstevel@tonic-gate {
2330Sstevel@tonic-gate 	PKT_LIST	*current, *best = NULL;
2340Sstevel@tonic-gate 	uint32_t	points, best_points = 0;
2350Sstevel@tonic-gate 
2360Sstevel@tonic-gate 	/*
2370Sstevel@tonic-gate 	 * pick out the best offer.  point system.
2380Sstevel@tonic-gate 	 * what's important?
2390Sstevel@tonic-gate 	 *
2400Sstevel@tonic-gate 	 *	0) DHCP
2410Sstevel@tonic-gate 	 *	1) no option overload
2420Sstevel@tonic-gate 	 *	2) encapsulated vendor option
2430Sstevel@tonic-gate 	 *	3) non-null sname and siaddr fields
2440Sstevel@tonic-gate 	 *	4) non-null file field
2450Sstevel@tonic-gate 	 *	5) hostname
2460Sstevel@tonic-gate 	 *	6) subnetmask
2470Sstevel@tonic-gate 	 *	7) router
2480Sstevel@tonic-gate 	 */
2490Sstevel@tonic-gate 
2500Sstevel@tonic-gate 	for (current = *pkts; current != NULL; current = current->next) {
2510Sstevel@tonic-gate 
2520Sstevel@tonic-gate 		points = 0;
2530Sstevel@tonic-gate 
2540Sstevel@tonic-gate 		if (current->opts[CD_DHCP_TYPE] == NULL) {
2550Sstevel@tonic-gate 			dhcpmsg(MSG_VERBOSE, "valid BOOTP reply");
2560Sstevel@tonic-gate 			goto valid_offer;
2570Sstevel@tonic-gate 		}
2580Sstevel@tonic-gate 
2590Sstevel@tonic-gate 		if (current->opts[CD_LEASE_TIME] == NULL) {
2600Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "select_best: OFFER without "
2610Sstevel@tonic-gate 			    "lease time");
2620Sstevel@tonic-gate 			continue;
2630Sstevel@tonic-gate 		}
2640Sstevel@tonic-gate 
2650Sstevel@tonic-gate 		if (current->opts[CD_LEASE_TIME]->len != sizeof (lease_t)) {
2660Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "select_best: OFFER with garbled "
2670Sstevel@tonic-gate 			    "lease time");
2680Sstevel@tonic-gate 			continue;
2690Sstevel@tonic-gate 		}
2700Sstevel@tonic-gate 
2710Sstevel@tonic-gate 		if (current->opts[CD_SERVER_ID] == NULL) {
2720Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "select_best: OFFER without "
2730Sstevel@tonic-gate 			    "server id");
2740Sstevel@tonic-gate 			continue;
2750Sstevel@tonic-gate 		}
2760Sstevel@tonic-gate 
2770Sstevel@tonic-gate 		if (current->opts[CD_SERVER_ID]->len != sizeof (ipaddr_t)) {
2780Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "select_best: OFFER with garbled "
2790Sstevel@tonic-gate 			    "server id");
2800Sstevel@tonic-gate 			continue;
2810Sstevel@tonic-gate 		}
2820Sstevel@tonic-gate 
2830Sstevel@tonic-gate 		/* valid DHCP OFFER.  see if we got our parameters. */
2840Sstevel@tonic-gate 		dhcpmsg(MSG_VERBOSE, "valid OFFER packet");
2850Sstevel@tonic-gate 		points += 30;
2860Sstevel@tonic-gate 
2870Sstevel@tonic-gate valid_offer:
2880Sstevel@tonic-gate 		if (current->rfc1048)
2890Sstevel@tonic-gate 			points += 5;
2900Sstevel@tonic-gate 
2910Sstevel@tonic-gate 		/*
2920Sstevel@tonic-gate 		 * also could be faked, though more difficult because
2930Sstevel@tonic-gate 		 * the encapsulation is hard to encode on a BOOTP
2940Sstevel@tonic-gate 		 * server; plus there's not as much real estate in the
2950Sstevel@tonic-gate 		 * packet for options, so it's likely this option
2960Sstevel@tonic-gate 		 * would get dropped.
2970Sstevel@tonic-gate 		 */
2980Sstevel@tonic-gate 
2990Sstevel@tonic-gate 		if (current->opts[CD_VENDOR_SPEC] != NULL)
3000Sstevel@tonic-gate 			points += 80;
3010Sstevel@tonic-gate 
3020Sstevel@tonic-gate 		if (current->opts[CD_SUBNETMASK] != NULL)
3030Sstevel@tonic-gate 			points++;
3040Sstevel@tonic-gate 
3050Sstevel@tonic-gate 		if (current->opts[CD_ROUTER] != NULL)
3060Sstevel@tonic-gate 			points++;
3070Sstevel@tonic-gate 
3080Sstevel@tonic-gate 		if (current->opts[CD_HOSTNAME] != NULL)
3090Sstevel@tonic-gate 			points += 5;
3100Sstevel@tonic-gate 
3110Sstevel@tonic-gate 		dhcpmsg(MSG_DEBUG, "select_best: OFFER had %d points", points);
3120Sstevel@tonic-gate 
3130Sstevel@tonic-gate 		if (points >= best_points) {
3140Sstevel@tonic-gate 			best_points = points;
3150Sstevel@tonic-gate 			best = current;
3160Sstevel@tonic-gate 		}
3170Sstevel@tonic-gate 	}
3180Sstevel@tonic-gate 
3190Sstevel@tonic-gate 	if (best != NULL) {
3200Sstevel@tonic-gate 		dhcpmsg(MSG_DEBUG, "select_best: most points: %d", best_points);
3210Sstevel@tonic-gate 		remove_from_pkt_list(pkts, best);
3220Sstevel@tonic-gate 	} else
3230Sstevel@tonic-gate 		dhcpmsg(MSG_DEBUG, "select_best: no valid OFFER/BOOTP reply");
3240Sstevel@tonic-gate 
3250Sstevel@tonic-gate 	free_pkt_list(pkts);
3260Sstevel@tonic-gate 	return (best);
3270Sstevel@tonic-gate }
3280Sstevel@tonic-gate 
3290Sstevel@tonic-gate /*
3300Sstevel@tonic-gate  * dhcp_acknak(): processes reception of an ACK or NAK packet on an interface
3310Sstevel@tonic-gate  *
3320Sstevel@tonic-gate  *   input: iu_eh_t *: unused
3330Sstevel@tonic-gate  *	    int: the file descriptor the ACK/NAK arrived on
3340Sstevel@tonic-gate  *	    short: unused
3350Sstevel@tonic-gate  *	    iu_event_id_t: the id of this event callback with the handler
3360Sstevel@tonic-gate  *	    void *: the interface that received the ACK or NAK
3370Sstevel@tonic-gate  *  output: void
3380Sstevel@tonic-gate  */
3390Sstevel@tonic-gate 
3400Sstevel@tonic-gate /* ARGSUSED */
3410Sstevel@tonic-gate void
3420Sstevel@tonic-gate dhcp_acknak(iu_eh_t *ehp, int fd, short events, iu_event_id_t id, void *arg)
3430Sstevel@tonic-gate {
3440Sstevel@tonic-gate 	struct ifslist		*ifsp = (struct ifslist *)arg;
3450Sstevel@tonic-gate 	PKT_LIST		*plp;
3460Sstevel@tonic-gate 
3470Sstevel@tonic-gate 	if (check_ifs(ifsp) == 0) {
3480Sstevel@tonic-gate 		/* unregister_acknak() does our release_ifs() */
3490Sstevel@tonic-gate 		(void) unregister_acknak(ifsp);
3500Sstevel@tonic-gate 		(void) ioctl(fd, I_FLUSH, FLUSHR|FLUSHW);
3510Sstevel@tonic-gate 		return;
3520Sstevel@tonic-gate 	}
3530Sstevel@tonic-gate 
3540Sstevel@tonic-gate 	/*
3550Sstevel@tonic-gate 	 * note that check_ifs() did our release_ifs() but we're not
3560Sstevel@tonic-gate 	 * sure we're done yet; call hold_ifs() to reacquire our hold;
3570Sstevel@tonic-gate 	 * if we're done, unregister_acknak() will release_ifs() below.
3580Sstevel@tonic-gate 	 */
3590Sstevel@tonic-gate 
3600Sstevel@tonic-gate 	hold_ifs(ifsp);
3610Sstevel@tonic-gate 
3620Sstevel@tonic-gate 	if (recv_pkt(ifsp, fd, DHCP_PACK|DHCP_PNAK, B_FALSE) == 0)
3630Sstevel@tonic-gate 		return;
3640Sstevel@tonic-gate 
3650Sstevel@tonic-gate 	/*
3660Sstevel@tonic-gate 	 * we've got a packet; make sure it's acceptable before
3670Sstevel@tonic-gate 	 * cancelling the REQUEST retransmissions.
3680Sstevel@tonic-gate 	 */
3690Sstevel@tonic-gate 
3700Sstevel@tonic-gate 	plp = ifsp->if_recv_pkt_list;
3710Sstevel@tonic-gate 	remove_from_pkt_list(&ifsp->if_recv_pkt_list, plp);
3720Sstevel@tonic-gate 
3730Sstevel@tonic-gate 	if (*plp->opts[CD_DHCP_TYPE]->value == ACK) {
3740Sstevel@tonic-gate 		if (plp->opts[CD_LEASE_TIME] == NULL ||
3750Sstevel@tonic-gate 		    plp->opts[CD_LEASE_TIME]->len != sizeof (lease_t)) {
3760Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "dhcp_acknak: ACK packet on %s "
3770Sstevel@tonic-gate 			    "missing mandatory lease option, ignored",
3780Sstevel@tonic-gate 			    ifsp->if_name);
3790Sstevel@tonic-gate 			ifsp->if_bad_offers++;
3800Sstevel@tonic-gate 			free_pkt_list(&plp);
3810Sstevel@tonic-gate 			return;
3820Sstevel@tonic-gate 		}
3830Sstevel@tonic-gate 		if ((ifsp->if_state == RENEWING ||
3840Sstevel@tonic-gate 			ifsp->if_state == REBINDING) &&
3850Sstevel@tonic-gate 			ifsp->if_addr.s_addr != plp->pkt->yiaddr.s_addr) {
3860Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "dhcp_acknak: renewal ACK packet "
3870Sstevel@tonic-gate 				"has a different IP address (%s), ignored",
3880Sstevel@tonic-gate 				inet_ntoa(plp->pkt->yiaddr));
3890Sstevel@tonic-gate 			ifsp->if_bad_offers++;
3900Sstevel@tonic-gate 			free_pkt_list(&plp);
3910Sstevel@tonic-gate 			return;
3920Sstevel@tonic-gate 		}
3930Sstevel@tonic-gate 	}
3940Sstevel@tonic-gate 
3950Sstevel@tonic-gate 	/*
3960Sstevel@tonic-gate 	 * looks good; cancel the retransmission timer and unregister
3970Sstevel@tonic-gate 	 * the acknak handler. ACK to BOUND, NAK back to SELECTING.
3980Sstevel@tonic-gate 	 */
3990Sstevel@tonic-gate 
4000Sstevel@tonic-gate 	stop_pkt_retransmission(ifsp);
4010Sstevel@tonic-gate 	(void) unregister_acknak(ifsp);
4020Sstevel@tonic-gate 
4030Sstevel@tonic-gate 	if (*(plp->opts[CD_DHCP_TYPE]->value) == NAK) {
4040Sstevel@tonic-gate 		dhcpmsg(MSG_WARNING, "dhcp_acknak: NAK on interface %s",
4050Sstevel@tonic-gate 		    ifsp->if_name);
4060Sstevel@tonic-gate 		ifsp->if_bad_offers++;
4070Sstevel@tonic-gate 		free_pkt_list(&plp);
408*2546Scarlsonj 		dhcp_restart(ifsp);
4090Sstevel@tonic-gate 
4100Sstevel@tonic-gate 		/*
4110Sstevel@tonic-gate 		 * remove any bogus cached configuration we might have
4120Sstevel@tonic-gate 		 * around (right now would only happen if we got here
4130Sstevel@tonic-gate 		 * from INIT_REBOOT).
4140Sstevel@tonic-gate 		 */
4150Sstevel@tonic-gate 
4160Sstevel@tonic-gate 		(void) remove_hostconf(ifsp->if_name);
4170Sstevel@tonic-gate 		return;
4180Sstevel@tonic-gate 	}
4190Sstevel@tonic-gate 
4200Sstevel@tonic-gate 	if (plp->opts[CD_SERVER_ID] == NULL ||
4210Sstevel@tonic-gate 	    plp->opts[CD_SERVER_ID]->len != sizeof (ipaddr_t)) {
4220Sstevel@tonic-gate 		dhcpmsg(MSG_ERROR, "dhcp_acknak: ACK with no valid server id, "
4230Sstevel@tonic-gate 		    "restarting DHCP on %s", ifsp->if_name);
4240Sstevel@tonic-gate 		ifsp->if_bad_offers++;
4250Sstevel@tonic-gate 		free_pkt_list(&plp);
426*2546Scarlsonj 		dhcp_restart(ifsp);
4270Sstevel@tonic-gate 		return;
4280Sstevel@tonic-gate 	}
4290Sstevel@tonic-gate 
4300Sstevel@tonic-gate 	if (plp->opts[CD_MESSAGE] != NULL)
4310Sstevel@tonic-gate 		print_server_msg(ifsp, plp->opts[CD_MESSAGE]);
4320Sstevel@tonic-gate 
4330Sstevel@tonic-gate 	if (dhcp_bound(ifsp, plp) == 0) {
4340Sstevel@tonic-gate 		dhcpmsg(MSG_WARNING, "dhcp_acknak: dhcp_bound failed "
4350Sstevel@tonic-gate 		    "for %s", ifsp->if_name);
436*2546Scarlsonj 		dhcp_restart(ifsp);
4370Sstevel@tonic-gate 		return;
4380Sstevel@tonic-gate 	}
4390Sstevel@tonic-gate 
4400Sstevel@tonic-gate 	dhcpmsg(MSG_VERBOSE, "ACK on interface %s", ifsp->if_name);
4410Sstevel@tonic-gate }
4420Sstevel@tonic-gate 
4430Sstevel@tonic-gate /*
444*2546Scarlsonj  * dhcp_restart(): restarts DHCP (from INIT) on a given interface
4450Sstevel@tonic-gate  *
4460Sstevel@tonic-gate  *   input: struct ifslist *: the interface to restart DHCP on
4470Sstevel@tonic-gate  *  output: void
4480Sstevel@tonic-gate  */
4490Sstevel@tonic-gate 
450*2546Scarlsonj void
451*2546Scarlsonj dhcp_restart(struct ifslist *ifsp)
4520Sstevel@tonic-gate {
4530Sstevel@tonic-gate 	if (iu_schedule_timer(tq, DHCP_RESTART_WAIT, dhcp_start, ifsp) == -1) {
4540Sstevel@tonic-gate 
4550Sstevel@tonic-gate 		ifsp->if_state	 = INIT;
4560Sstevel@tonic-gate 		ifsp->if_dflags |= DHCP_IF_FAILED;
4570Sstevel@tonic-gate 
4580Sstevel@tonic-gate 		ipc_action_finish(ifsp, DHCP_IPC_E_MEMORY);
4590Sstevel@tonic-gate 		async_finish(ifsp);
4600Sstevel@tonic-gate 
461*2546Scarlsonj 		dhcpmsg(MSG_ERROR, "dhcp_restart: cannot schedule dhcp_start, "
4620Sstevel@tonic-gate 		    "reverting to INIT state on %s", ifsp->if_name);
4630Sstevel@tonic-gate 	} else
4640Sstevel@tonic-gate 		hold_ifs(ifsp);
4650Sstevel@tonic-gate }
4660Sstevel@tonic-gate 
4670Sstevel@tonic-gate /*
4680Sstevel@tonic-gate  * stop_requesting(): decides when to stop retransmitting REQUESTs
4690Sstevel@tonic-gate  *
4700Sstevel@tonic-gate  *   input: struct ifslist *: the interface REQUESTs are being sent on
4710Sstevel@tonic-gate  *	    unsigned int: the number of REQUESTs sent so far
4720Sstevel@tonic-gate  *  output: boolean_t: B_TRUE if retransmissions should stop
4730Sstevel@tonic-gate  */
4740Sstevel@tonic-gate 
4750Sstevel@tonic-gate static boolean_t
4760Sstevel@tonic-gate stop_requesting(struct ifslist *ifsp, unsigned int n_requests)
4770Sstevel@tonic-gate {
4780Sstevel@tonic-gate 	if (n_requests >= DHCP_MAX_REQUESTS) {
4790Sstevel@tonic-gate 
4800Sstevel@tonic-gate 		(void) unregister_acknak(ifsp);
4810Sstevel@tonic-gate 
4820Sstevel@tonic-gate 		dhcpmsg(MSG_INFO, "no ACK/NAK to REQUESTING REQUEST, "
4830Sstevel@tonic-gate 		    "restarting DHCP on %s", ifsp->if_name);
4840Sstevel@tonic-gate 
4850Sstevel@tonic-gate 		dhcp_selecting(ifsp);
4860Sstevel@tonic-gate 		return (B_TRUE);
4870Sstevel@tonic-gate 	}
4880Sstevel@tonic-gate 
4890Sstevel@tonic-gate 	return (B_FALSE);
4900Sstevel@tonic-gate }
491