Home | History | Annotate | Download | only in dhcpagent
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  *
     25  * REQUESTING state of the client state machine.
     26  */
     27 
     28 #include <stdlib.h>
     29 #include <string.h>
     30 #include <search.h>
     31 #include <sys/types.h>
     32 #include <netinet/in.h>
     33 #include <netinet/dhcp.h>
     34 #include <netinet/udp.h>
     35 #include <netinet/ip_var.h>
     36 #include <netinet/udp_var.h>
     37 #include <arpa/inet.h>
     38 #include <dhcp_hostconf.h>
     39 #include <dhcpagent_util.h>
     40 #include <dhcpmsg.h>
     41 
     42 #include "states.h"
     43 #include "util.h"
     44 #include "packet.h"
     45 #include "interface.h"
     46 #include "agent.h"
     47 
     48 static PKT_LIST		*select_best(dhcp_smach_t *);
     49 static void		request_failed(dhcp_smach_t *);
     50 static stop_func_t	stop_requesting;
     51 
     52 /*
     53  * send_v6_request(): sends a DHCPv6 Request message and switches to REQUESTING
     54  *		      state.  This is a separate function because a NoBinding
     55  *		      response can also cause us to do this.
     56  *
     57  *   input: dhcp_smach_t *: the state machine
     58  *  output: none
     59  */
     60 
     61 void
     62 send_v6_request(dhcp_smach_t *dsmp)
     63 {
     64 	dhcp_pkt_t *dpkt;
     65 	dhcpv6_ia_na_t d6in;
     66 
     67 	dpkt = init_pkt(dsmp, DHCPV6_MSG_REQUEST);
     68 	(void) add_pkt_opt(dpkt, DHCPV6_OPT_SERVERID, dsmp->dsm_serverid,
     69 	    dsmp->dsm_serveridlen);
     70 
     71 	/* Add an IA_NA option for our controlling LIF */
     72 	d6in.d6in_iaid = htonl(dsmp->dsm_lif->lif_iaid);
     73 	d6in.d6in_t1 = htonl(0);
     74 	d6in.d6in_t2 = htonl(0);
     75 	(void) add_pkt_opt(dpkt, DHCPV6_OPT_IA_NA,
     76 	    (dhcpv6_option_t *)&d6in + 1,
     77 	    sizeof (d6in) - sizeof (dhcpv6_option_t));
     78 
     79 	/* Add required Option Request option */
     80 	(void) add_pkt_prl(dpkt, dsmp);
     81 
     82 	(void) send_pkt_v6(dsmp, dpkt, dsmp->dsm_server, stop_requesting,
     83 	    DHCPV6_REQ_TIMEOUT, DHCPV6_REQ_MAX_RT);
     84 
     85 	/* For DHCPv6, state switch cannot fail */
     86 	(void) set_smach_state(dsmp, REQUESTING);
     87 }
     88 
     89 /*
     90  * server_unicast_option(): determines the server address to use based on the
     91  *			    DHCPv6 Server Unicast option present in the given
     92  *			    packet.
     93  *
     94  *   input: dhcp_smach_t *: the state machine
     95  *	    PKT_LIST *: received packet (Advertisement or Reply)
     96  *  output: none
     97  */
     98 
     99 void
    100 server_unicast_option(dhcp_smach_t *dsmp, PKT_LIST *plp)
    101 {
    102 	const dhcpv6_option_t *d6o;
    103 	uint_t olen;
    104 
    105 	d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_UNICAST, &olen);
    106 	olen -= sizeof (*d6o);
    107 	/* LINTED: no consequent */
    108 	if (d6o == NULL) {
    109 		/* No Server Unicast option specified */
    110 	} else if (olen != sizeof (dsmp->dsm_server)) {
    111 		dhcpmsg(MSG_WARNING, "server_unicast_option: %s has Server "
    112 		    "Unicast option with bad length",
    113 		    pkt_type_to_string(pkt_recv_type(plp), B_TRUE));
    114 	} else {
    115 		in6_addr_t addr;
    116 
    117 		(void) memcpy(&addr, d6o + 1, olen);
    118 		if (IN6_IS_ADDR_UNSPECIFIED(&addr)) {
    119 			dhcpmsg(MSG_WARNING, "server_unicast_option: unicast "
    120 			    "to unspecified address ignored");
    121 		} else if (IN6_IS_ADDR_MULTICAST(&addr)) {
    122 			dhcpmsg(MSG_WARNING, "server_unicast_option: unicast "
    123 			    "to multicast address ignored");
    124 		} else if (IN6_IS_ADDR_V4COMPAT(&addr) ||
    125 		    IN6_IS_ADDR_V4MAPPED(&addr)) {
    126 			dhcpmsg(MSG_WARNING, "server_unicast_option: unicast "
    127 			    "to invalid address ignored");
    128 		} else {
    129 			dsmp->dsm_server = addr;
    130 		}
    131 	}
    132 }
    133 
    134 /*
    135  * dhcp_requesting(): checks if OFFER packets to come in from DHCP servers.
    136  *		      if so, chooses the best one, sends a REQUEST to the
    137  *		      server and registers an event handler to receive
    138  *		      the ACK/NAK.  This may be called by the offer timer or
    139  *		      by any function that wants to check for offers after
    140  *		      canceling that timer.
    141  *
    142  *   input: iu_tq_t *: timer queue; non-NULL if this is a timer callback
    143  *	    void *: the state machine receiving OFFER packets
    144  *  output: void
    145  */
    146 
    147 void
    148 dhcp_requesting(iu_tq_t *tqp, void *arg)
    149 {
    150 	dhcp_smach_t		*dsmp = arg;
    151 	dhcp_pkt_t		*dpkt;
    152 	PKT_LIST		*offer;
    153 	lease_t			lease;
    154 	boolean_t		isv6 = dsmp->dsm_isv6;
    155 
    156 	/*
    157 	 * We assume here that if tqp is set, then this means we're being
    158 	 * called back by the offer wait timer.  If so, then drop our hold
    159 	 * on the state machine.  Otherwise, cancel the timer if it's running.
    160 	 */
    161 	if (tqp != NULL) {
    162 		dhcpmsg(MSG_VERBOSE,
    163 		    "dhcp_requesting: offer wait timer on v%d %s",
    164 		    isv6 ? 6 : 4, dsmp->dsm_name);
    165 		dsmp->dsm_offer_timer = -1;
    166 		if (!verify_smach(dsmp))
    167 			return;
    168 	} else {
    169 		cancel_offer_timer(dsmp);
    170 	}
    171 
    172 	/*
    173 	 * select the best OFFER; all others pitched.
    174 	 */
    175 
    176 	offer = select_best(dsmp);
    177 	if (offer == NULL) {
    178 
    179 		dhcpmsg(MSG_VERBOSE,
    180 		    "no OFFERs/Advertisements on %s, waiting...",
    181 		    dsmp->dsm_name);
    182 
    183 		/*
    184 		 * no acceptable OFFERs have come in.  reschedule
    185 		 * ourself for callback.
    186 		 */
    187 
    188 		if ((dsmp->dsm_offer_timer = iu_schedule_timer(tq,
    189 		    dsmp->dsm_offer_wait, dhcp_requesting, dsmp)) == -1) {
    190 
    191 			/*
    192 			 * ugh.  the best we can do at this point is
    193 			 * revert back to INIT and wait for a user to
    194 			 * restart us.
    195 			 */
    196 
    197 			dhcpmsg(MSG_WARNING, "dhcp_requesting: cannot "
    198 			    "reschedule callback, reverting to INIT state on "
    199 			    "%s", dsmp->dsm_name);
    200 
    201 			stop_pkt_retransmission(dsmp);
    202 			(void) set_smach_state(dsmp, INIT);
    203 			dsmp->dsm_dflags |= DHCP_IF_FAILED;
    204 			ipc_action_finish(dsmp, DHCP_IPC_E_MEMORY);
    205 		} else {
    206 			hold_smach(dsmp);
    207 		}
    208 
    209 		return;
    210 	}
    211 
    212 	/*
    213 	 * With IPv4, the DHCPREQUEST packet we're about to transmit implicitly
    214 	 * declines all other offers we've received.  We can no longer use any
    215 	 * cached offers, so we must discard them now.  With DHCPv6, though,
    216 	 * we're permitted to hang onto the advertisements (offers) and try
    217 	 * them if the preferred one doesn't pan out.
    218 	 */
    219 	if (!isv6)
    220 		free_pkt_list(&dsmp->dsm_recv_pkt_list);
    221 
    222 	/* stop collecting packets. */
    223 
    224 	stop_pkt_retransmission(dsmp);
    225 
    226 	/*
    227 	 * For IPv4, check to see whether we got an OFFER or a BOOTP packet.
    228 	 * If we got a BOOTP packet, go to the BOUND state now.
    229 	 */
    230 	if (!isv6 && offer->opts[CD_DHCP_TYPE] == NULL) {
    231 		free_pkt_list(&dsmp->dsm_recv_pkt_list);
    232 
    233 		if (!set_smach_state(dsmp, REQUESTING)) {
    234 			dhcp_restart(dsmp);
    235 			return;
    236 		}
    237 
    238 		if (!dhcp_bound(dsmp, offer)) {
    239 			dhcpmsg(MSG_WARNING, "dhcp_requesting: dhcp_bound "
    240 			    "failed for %s", dsmp->dsm_name);
    241 			dhcp_restart(dsmp);
    242 			return;
    243 		}
    244 
    245 		return;
    246 	}
    247 
    248 	if (isv6) {
    249 		const char *estr, *msg;
    250 		const dhcpv6_option_t *d6o;
    251 		uint_t olen, msglen;
    252 
    253 		/* If there's a Status Code option, print the message */
    254 		d6o = dhcpv6_pkt_option(offer, NULL, DHCPV6_OPT_STATUS_CODE,
    255 		    &olen);
    256 		(void) dhcpv6_status_code(d6o, olen, &estr, &msg, &msglen);
    257 		print_server_msg(dsmp, msg, msglen);
    258 
    259 		/* Copy in the Server ID (guaranteed to be present now) */
    260 		if (!save_server_id(dsmp, offer))
    261 			goto failure;
    262 
    263 		/*
    264 		 * Determine how to send this message.  If the Advertisement
    265 		 * (offer) has the unicast option, then use the address
    266 		 * specified in the option.  Otherwise, send via multicast.
    267 		 */
    268 		server_unicast_option(dsmp, offer);
    269 
    270 		send_v6_request(dsmp);
    271 	} else {
    272 		/* if we got a message from the server, display it. */
    273 		if (offer->opts[CD_MESSAGE] != NULL) {
    274 			print_server_msg(dsmp,
    275 			    (char *)offer->opts[CD_MESSAGE]->value,
    276 			    offer->opts[CD_MESSAGE]->len);
    277 		}
    278 
    279 		/*
    280 		 * assemble a DHCPREQUEST, with the ciaddr field set to 0,
    281 		 * since we got here from the INIT state.
    282 		 */
    283 
    284 		dpkt = init_pkt(dsmp, REQUEST);
    285 
    286 		/*
    287 		 * Grab the lease out of the OFFER; we know it's valid because
    288 		 * select_best() already checked.  The max dhcp message size
    289 		 * option is set to the interface max, minus the size of the
    290 		 * udp and ip headers.
    291 		 */
    292 
    293 		(void) memcpy(&lease, offer->opts[CD_LEASE_TIME]->value,
    294 		    sizeof (lease_t));
    295 
    296 		(void) add_pkt_opt32(dpkt, CD_LEASE_TIME, lease);
    297 		(void) add_pkt_opt16(dpkt, CD_MAX_DHCP_SIZE,
    298 		    htons(dsmp->dsm_lif->lif_max - sizeof (struct udpiphdr)));
    299 		(void) add_pkt_opt32(dpkt, CD_REQUESTED_IP_ADDR,
    300 		    offer->pkt->yiaddr.s_addr);
    301 		(void) add_pkt_opt(dpkt, CD_SERVER_ID,
    302 		    offer->opts[CD_SERVER_ID]->value,
    303 		    offer->opts[CD_SERVER_ID]->len);
    304 
    305 		if (class_id_len != 0) {
    306 			(void) add_pkt_opt(dpkt, CD_CLASS_ID, class_id,
    307 			    class_id_len);
    308 		}
    309 		(void) add_pkt_prl(dpkt, dsmp);
    310 
    311 		/*
    312 		 * dsm_reqhost was set for this state machine in
    313 		 * dhcp_selecting() if the DF_REQUEST_HOSTNAME option set and a
    314 		 * host name was found
    315 		 */
    316 		if (dsmp->dsm_reqhost != NULL) {
    317 			(void) add_pkt_opt(dpkt, CD_HOSTNAME, dsmp->dsm_reqhost,
    318 			    strlen(dsmp->dsm_reqhost));
    319 		}
    320 		(void) add_pkt_opt(dpkt, CD_END, NULL, 0);
    321 
    322 		/*
    323 		 * send out the REQUEST, trying retransmissions.  either a NAK
    324 		 * or too many REQUEST attempts will revert us to SELECTING.
    325 		 */
    326 
    327 		if (!set_smach_state(dsmp, REQUESTING)) {
    328 			dhcpmsg(MSG_ERROR, "dhcp_requesting: cannot switch to "
    329 			    "REQUESTING state; reverting to INIT on %s",
    330 			    dsmp->dsm_name);
    331 			goto failure;
    332 		}
    333 
    334 		(void) send_pkt(dsmp, dpkt, htonl(INADDR_BROADCAST),
    335 		    stop_requesting);
    336 	}
    337 
    338 	/* all done with the offer */
    339 	free_pkt_entry(offer);
    340 
    341 	return;
    342 
    343 failure:
    344 	dsmp->dsm_dflags |= DHCP_IF_FAILED;
    345 	(void) set_smach_state(dsmp, INIT);
    346 	ipc_action_finish(dsmp, DHCP_IPC_E_MEMORY);
    347 	free_pkt_list(&dsmp->dsm_recv_pkt_list);
    348 }
    349 
    350 /*
    351  * compute_points_v6(): compute the number of "points" for a given v6
    352  *			advertisement.
    353  *
    354  *   input: const PKT_LIST *: packet to inspect
    355  *	    const dhcp_smach_t *: state machine that received the packet
    356  *  output: int: -1 to discard, -2 to accept immediately, >=0 for preference.
    357  */
    358 
    359 static int
    360 compute_points_v6(const PKT_LIST *pkt, const dhcp_smach_t *dsmp)
    361 {
    362 	char abuf[INET6_ADDRSTRLEN];
    363 	int points = 0;
    364 	const dhcpv6_option_t *d6o, *d6so;
    365 	uint_t olen, solen;
    366 	int i;
    367 	const char *estr, *msg;
    368 	uint_t msglen;
    369 
    370 	/*
    371 	 * Look through the packet contents.  Valid packets must have our
    372 	 * client ID and a server ID, which has already been checked by
    373 	 * dhcp_packet_lif.  Bonus points for each option.
    374 	 */
    375 
    376 	/* One point for having a valid message. */
    377 	points++;
    378 
    379 	/*
    380 	 * Per RFC 3315, if the Advertise message says, "yes, we have no
    381 	 * bananas today," then ignore the entire message.  (Why it's just
    382 	 * _this_ error and no other is a bit of a mystery, but a standard is a
    383 	 * standard.)
    384 	 */
    385 	d6o = dhcpv6_pkt_option(pkt, NULL, DHCPV6_OPT_STATUS_CODE, &olen);
    386 	if (dhcpv6_status_code(d6o, olen, &estr, &msg, &msglen) ==
    387 	    DHCPV6_STAT_NOADDRS) {
    388 		dhcpmsg(MSG_INFO,
    389 		    "discard advertisement from %s on %s: no address status",
    390 		    inet_ntop(AF_INET6,
    391 		    &((struct sockaddr_in6 *)&pkt->pktfrom)->sin6_addr,
    392 		    abuf, sizeof (abuf)), dsmp->dsm_name);
    393 		return (-1);
    394 	}
    395 
    396 	/* Two points for each batch of offered IP addresses */
    397 	d6o = NULL;
    398 	while ((d6o = dhcpv6_pkt_option(pkt, d6o, DHCPV6_OPT_IA_NA,
    399 	    &olen)) != NULL) {
    400 
    401 		/*
    402 		 * Note that it's possible to have "no bananas" on an
    403 		 * individual IA.  We must look for that here.
    404 		 *
    405 		 * RFC 3315 section 17.1.3 does not refer to the status code
    406 		 * embedded in the IA itself.  However, the TAHI test suite
    407 		 * checks for this specific case.  Because it's extremely
    408 		 * unlikely that any usable server is going to report that it
    409 		 * has no addresses on a network using DHCP for address
    410 		 * assignment, we allow such messages to be dropped.
    411 		 */
    412 		d6so = dhcpv6_find_option(
    413 		    (const char *)d6o + sizeof (dhcpv6_ia_na_t),
    414 		    olen - sizeof (dhcpv6_ia_na_t), NULL,
    415 		    DHCPV6_OPT_STATUS_CODE, &solen);
    416 		if (dhcpv6_status_code(d6so, solen, &estr, &msg, &msglen) ==
    417 		    DHCPV6_STAT_NOADDRS)
    418 			return (-1);
    419 		points += 2;
    420 	}
    421 
    422 	/*
    423 	 * Note that we drive on in the case where there are no addresses.  The
    424 	 * hope here is that we'll at least get some useful configuration
    425 	 * information.
    426 	 */
    427 
    428 	/* One point for each requested option */
    429 	for (i = 0; i < dsmp->dsm_prllen; i++) {
    430 		if (dhcpv6_pkt_option(pkt, NULL, dsmp->dsm_prl[i], NULL) !=
    431 		    NULL)
    432 			points++;
    433 	}
    434 
    435 	/*
    436 	 * Ten points for each point of "preference."  Note: the value 255 is
    437 	 * special.  It means "stop right now and select this server."
    438 	 */
    439 	d6o = dhcpv6_pkt_option(pkt, NULL, DHCPV6_OPT_PREFERENCE, &olen);
    440 	if (d6o != NULL && olen == sizeof (*d6o) + 1) {
    441 		int pref = *(const uchar_t *)(d6o + 1);
    442 
    443 		if (pref == 255)
    444 			return (-2);
    445 		points += 10 * pref;
    446 	}
    447 
    448 	return (points);
    449 }
    450 
    451 /*
    452  * compute_points_v4(): compute the number of "points" for a given v4 offer.
    453  *
    454  *   input: const PKT_LIST *: packet to inspect
    455  *	    const dhcp_smach_t *: state machine that received the packet
    456  *  output: int: -1 to discard, >=0 for preference.
    457  */
    458 
    459 static int
    460 compute_points_v4(const PKT_LIST *pkt)
    461 {
    462 	int points = 0;
    463 
    464 	if (pkt->opts[CD_DHCP_TYPE] == NULL) {
    465 		dhcpmsg(MSG_VERBOSE, "compute_points_v4: valid BOOTP reply");
    466 		goto valid_offer;
    467 	}
    468 
    469 	if (pkt->opts[CD_LEASE_TIME] == NULL) {
    470 		dhcpmsg(MSG_WARNING, "compute_points_v4: OFFER without lease "
    471 		    "time");
    472 		return (-1);
    473 	}
    474 
    475 	if (pkt->opts[CD_LEASE_TIME]->len != sizeof (lease_t)) {
    476 		dhcpmsg(MSG_WARNING, "compute_points_v4: OFFER with garbled "
    477 		    "lease time");
    478 		return (-1);
    479 	}
    480 
    481 	if (pkt->opts[CD_SERVER_ID] == NULL) {
    482 		dhcpmsg(MSG_WARNING, "compute_points_v4: OFFER without server "
    483 		    "id");
    484 		return (-1);
    485 	}
    486 
    487 	if (pkt->opts[CD_SERVER_ID]->len != sizeof (ipaddr_t)) {
    488 		dhcpmsg(MSG_WARNING, "compute_points_v4: OFFER with garbled "
    489 		    "server id");
    490 		return (-1);
    491 	}
    492 
    493 	/* valid DHCP OFFER.  see if we got our parameters. */
    494 	dhcpmsg(MSG_VERBOSE, "compute_points_v4: valid OFFER packet");
    495 	points += 30;
    496 
    497 valid_offer:
    498 	if (pkt->rfc1048)
    499 		points += 5;
    500 
    501 	/*
    502 	 * also could be faked, though more difficult because the encapsulation
    503 	 * is hard to encode on a BOOTP server; plus there's not as much real
    504 	 * estate in the packet for options, so it's likely this option would
    505 	 * get dropped.
    506 	 */
    507 
    508 	if (pkt->opts[CD_VENDOR_SPEC] != NULL)
    509 		points += 80;
    510 
    511 	if (pkt->opts[CD_SUBNETMASK] != NULL)
    512 		points++;
    513 
    514 	if (pkt->opts[CD_ROUTER] != NULL)
    515 		points++;
    516 
    517 	if (pkt->opts[CD_HOSTNAME] != NULL)
    518 		points += 5;
    519 
    520 	return (points);
    521 }
    522 
    523 /*
    524  * select_best(): selects the best offer from a list of IPv4 OFFER packets or
    525  *		  DHCPv6 Advertise packets.
    526  *
    527  *   input: dhcp_smach_t *: state machine with enqueued offers
    528  *  output: PKT_LIST *: the best packet, or NULL if none are acceptable
    529  */
    530 
    531 static PKT_LIST *
    532 select_best(dhcp_smach_t *dsmp)
    533 {
    534 	PKT_LIST	*current = dsmp->dsm_recv_pkt_list;
    535 	PKT_LIST	*next, *best = NULL;
    536 	int		points, best_points = -1;
    537 
    538 	/*
    539 	 * pick out the best offer.  point system.
    540 	 * what's important for IPv4?
    541 	 *
    542 	 *	0) DHCP (30 points)
    543 	 *	1) no option overload
    544 	 *	2) encapsulated vendor option (80 points)
    545 	 *	3) non-null sname and siaddr fields
    546 	 *	4) non-null file field
    547 	 *	5) hostname (5 points)
    548 	 *	6) subnetmask (1 point)
    549 	 *	7) router (1 point)
    550 	 */
    551 
    552 	for (; current != NULL; current = next) {
    553 		next = current->next;
    554 
    555 		points = current->isv6 ?
    556 		    compute_points_v6(current, dsmp) :
    557 		    compute_points_v4(current);
    558 
    559 		/*
    560 		 * Just discard any unacceptable entries we encounter.
    561 		 */
    562 		if (points == -1) {
    563 			remque(current);
    564 			free_pkt_entry(current);
    565 			continue;
    566 		}
    567 
    568 		dhcpmsg(MSG_DEBUG, "select_best: OFFER had %d points", points);
    569 
    570 		/* Special case: stop now and select */
    571 		if (points == -2) {
    572 			best = current;
    573 			break;
    574 		}
    575 
    576 		if (points >= best_points) {
    577 			best_points = points;
    578 			best = current;
    579 		}
    580 	}
    581 
    582 	if (best != NULL) {
    583 		dhcpmsg(MSG_DEBUG, "select_best: most points: %d", best_points);
    584 		remque(best);
    585 	} else {
    586 		dhcpmsg(MSG_DEBUG, "select_best: no valid OFFER/BOOTP reply");
    587 	}
    588 
    589 	return (best);
    590 }
    591 
    592 /*
    593  * accept_v4_acknak(): determine what to do with a DHCPv4 ACK/NAK based on the
    594  *		       current state.  If we're renewing or rebinding, the ACK
    595  *		       must be for the same address and must have a new lease
    596  *		       time.  If it's a NAK, then our cache is garbage, and we
    597  *		       must restart.  Finally, call dhcp_bound on accepted
    598  *		       ACKs.
    599  *
    600  *   input: dhcp_smach_t *: the state machine to handle the ACK/NAK
    601  *	    PKT_LIST *: the ACK/NAK message
    602  *  output: void
    603  */
    604 
    605 static void
    606 accept_v4_acknak(dhcp_smach_t *dsmp, PKT_LIST *plp)
    607 {
    608 	/* Account for received and processed messages */
    609 	dsmp->dsm_received++;
    610 
    611 	if (*plp->opts[CD_DHCP_TYPE]->value == ACK) {
    612 		if (dsmp->dsm_state != INFORM_SENT &&
    613 		    dsmp->dsm_state != INFORMATION &&
    614 		    (plp->opts[CD_LEASE_TIME] == NULL ||
    615 		    plp->opts[CD_LEASE_TIME]->len != sizeof (lease_t))) {
    616 			dhcpmsg(MSG_WARNING, "accept_v4_acknak: ACK packet on "
    617 			    "%s missing mandatory lease option, ignored",
    618 			    dsmp->dsm_name);
    619 			dsmp->dsm_bad_offers++;
    620 			free_pkt_entry(plp);
    621 			return;
    622 		}
    623 		if ((dsmp->dsm_state == RENEWING ||
    624 		    dsmp->dsm_state == REBINDING) &&
    625 		    dsmp->dsm_leases->dl_lifs->lif_addr !=
    626 		    plp->pkt->yiaddr.s_addr) {
    627 			dhcpmsg(MSG_WARNING, "accept_v4_acknak: renewal ACK "
    628 			    "packet has a different IP address (%s), ignored",
    629 			    inet_ntoa(plp->pkt->yiaddr));
    630 			dsmp->dsm_bad_offers++;
    631 			free_pkt_entry(plp);
    632 			return;
    633 		}
    634 	}
    635 
    636 	/*
    637 	 * looks good; cancel the retransmission timer and unregister
    638 	 * the acknak handler. ACK to BOUND, NAK back to SELECTING.
    639 	 */
    640 
    641 	stop_pkt_retransmission(dsmp);
    642 
    643 	if (*plp->opts[CD_DHCP_TYPE]->value == NAK) {
    644 		dhcpmsg(MSG_WARNING, "accept_v4_acknak: NAK on interface %s",
    645 		    dsmp->dsm_name);
    646 		dsmp->dsm_bad_offers++;
    647 		free_pkt_entry(plp);
    648 		dhcp_restart(dsmp);
    649 
    650 		/*
    651 		 * remove any bogus cached configuration we might have
    652 		 * around (right now would only happen if we got here
    653 		 * from INIT_REBOOT).
    654 		 */
    655 
    656 		(void) remove_hostconf(dsmp->dsm_name, dsmp->dsm_isv6);
    657 		return;
    658 	}
    659 
    660 	if (plp->opts[CD_SERVER_ID] == NULL ||
    661 	    plp->opts[CD_SERVER_ID]->len != sizeof (ipaddr_t)) {
    662 		dhcpmsg(MSG_ERROR, "accept_v4_acknak: ACK with no valid "
    663 		    "server id on %s", dsmp->dsm_name);
    664 		dsmp->dsm_bad_offers++;
    665 		free_pkt_entry(plp);
    666 		dhcp_restart(dsmp);
    667 		return;
    668 	}
    669 
    670 	if (plp->opts[CD_MESSAGE] != NULL) {
    671 		print_server_msg(dsmp, (char *)plp->opts[CD_MESSAGE]->value,
    672 		    plp->opts[CD_MESSAGE]->len);
    673 	}
    674 
    675 	dhcpmsg(MSG_VERBOSE, "accept_v4_acknak: ACK on %s", dsmp->dsm_name);
    676 	if (!dhcp_bound(dsmp, plp)) {
    677 		dhcpmsg(MSG_WARNING, "accept_v4_acknak: dhcp_bound failed "
    678 		    "for %s", dsmp->dsm_name);
    679 		dhcp_restart(dsmp);
    680 	}
    681 }
    682 
    683 /*
    684  * accept_v6_message(): determine what to do with a DHCPv6 message based on the
    685  *			current state.
    686  *
    687  *   input: dhcp_smach_t *: the state machine to handle the message
    688  *	    PKT_LIST *: the DHCPv6 message
    689  *	    const char *: type of message (for logging)
    690  *	    uchar_t: type of message (extracted from packet)
    691  *  output: void
    692  */
    693 
    694 static void
    695 accept_v6_message(dhcp_smach_t *dsmp, PKT_LIST *plp, const char *pname,
    696     uchar_t recv_type)
    697 {
    698 	const dhcpv6_option_t *d6o;
    699 	uint_t olen;
    700 	const char *estr, *msg;
    701 	uint_t msglen;
    702 	int status;
    703 
    704 	/* Account for received and processed messages */
    705 	dsmp->dsm_received++;
    706 
    707 	/* We don't yet support Reconfigure at all. */
    708 	if (recv_type == DHCPV6_MSG_RECONFIGURE) {
    709 		dhcpmsg(MSG_VERBOSE, "accept_v6_message: ignored Reconfigure "
    710 		    "on %s", dsmp->dsm_name);
    711 		free_pkt_entry(plp);
    712 		return;
    713 	}
    714 
    715 	/*
    716 	 * All valid DHCPv6 messages must have our Client ID specified.
    717 	 */
    718 	d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_CLIENTID, &olen);
    719 	olen -= sizeof (*d6o);
    720 	if (d6o == NULL || olen != dsmp->dsm_cidlen ||
    721 	    memcmp(d6o + 1, dsmp->dsm_cid, olen) != 0) {
    722 		dhcpmsg(MSG_VERBOSE,
    723 		    "accept_v6_message: discarded %s on %s: %s Client ID",
    724 		    pname, dsmp->dsm_name, d6o == NULL ? "no" : "wrong");
    725 		free_pkt_entry(plp);
    726 		return;
    727 	}
    728 
    729 	/*
    730 	 * All valid DHCPv6 messages must have a Server ID specified.
    731 	 *
    732 	 * If this is a Reply and it's not in response to Solicit, Confirm,
    733 	 * Rebind, or Information-Request, then it must also match the Server
    734 	 * ID we're expecting.
    735 	 *
    736 	 * For Reply in the Solicit, Confirm, Rebind, and Information-Request
    737 	 * cases, the Server ID needs to be saved.  This is done inside of
    738 	 * dhcp_bound().
    739 	 */
    740 	d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_SERVERID, &olen);
    741 	if (d6o == NULL) {
    742 		dhcpmsg(MSG_DEBUG,
    743 		    "accept_v6_message: discarded %s on %s: no Server ID",
    744 		    pname, dsmp->dsm_name);
    745 		free_pkt_entry(plp);
    746 		return;
    747 	}
    748 	if (recv_type == DHCPV6_MSG_REPLY && dsmp->dsm_state != SELECTING &&
    749 	    dsmp->dsm_state != INIT_REBOOT && dsmp->dsm_state != REBINDING &&
    750 	    dsmp->dsm_state != INFORM_SENT) {
    751 		olen -= sizeof (*d6o);
    752 		if (olen != dsmp->dsm_serveridlen ||
    753 		    memcmp(d6o + 1, dsmp->dsm_serverid, olen) != 0) {
    754 			dhcpmsg(MSG_DEBUG, "accept_v6_message: discarded %s on "
    755 			    "%s: wrong Server ID", pname, dsmp->dsm_name);
    756 			free_pkt_entry(plp);
    757 			return;
    758 		}
    759 	}
    760 
    761 	/*
    762 	 * Break out of the switch if the input message needs to be discarded.
    763 	 * Return from the function if the message has been enqueued or
    764 	 * consumed.
    765 	 */
    766 	switch (dsmp->dsm_state) {
    767 	case SELECTING:
    768 		/* A Reply message signifies a Rapid-Commit. */
    769 		if (recv_type == DHCPV6_MSG_REPLY) {
    770 			if (dhcpv6_pkt_option(plp, NULL,
    771 			    DHCPV6_OPT_RAPID_COMMIT, &olen) == NULL) {
    772 				dhcpmsg(MSG_DEBUG, "accept_v6_message: Reply "
    773 				    "on %s lacks Rapid-Commit; ignoring",
    774 				    dsmp->dsm_name);
    775 				break;
    776 			}
    777 			dhcpmsg(MSG_VERBOSE,
    778 			    "accept_v6_message: rapid-commit Reply on %s",
    779 			    dsmp->dsm_name);
    780 			cancel_offer_timer(dsmp);
    781 			goto rapid_commit;
    782 		}
    783 
    784 		/* Otherwise, we're looking for Advertisements. */
    785 		if (recv_type != DHCPV6_MSG_ADVERTISE)
    786 			break;
    787 
    788 		/*
    789 		 * Special case: if this advertisement has preference 255, then
    790 		 * we must stop right now and select this server.
    791 		 */
    792 		d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_PREFERENCE,
    793 		    &olen);
    794 		if (d6o != NULL && olen == sizeof (*d6o) + 1 &&
    795 		    *(const uchar_t *)(d6o + 1) == 255) {
    796 			pkt_smach_enqueue(dsmp, plp);
    797 			dhcpmsg(MSG_DEBUG, "accept_v6_message: preference 255;"
    798 			    " immediate Request on %s", dsmp->dsm_name);
    799 			dhcp_requesting(NULL, dsmp);
    800 		} else {
    801 			pkt_smach_enqueue(dsmp, plp);
    802 		}
    803 		return;
    804 
    805 	case PRE_BOUND:
    806 	case BOUND:
    807 		/*
    808 		 * Not looking for anything in these states.  (If we
    809 		 * implemented reconfigure, that might go here.)
    810 		 */
    811 		break;
    812 
    813 	case REQUESTING:
    814 	case INIT_REBOOT:
    815 	case RENEWING:
    816 	case REBINDING:
    817 	case INFORM_SENT:
    818 		/*
    819 		 * We're looking for Reply messages.
    820 		 */
    821 		if (recv_type != DHCPV6_MSG_REPLY)
    822 			break;
    823 		dhcpmsg(MSG_VERBOSE,
    824 		    "accept_v6_message: received Reply message on %s",
    825 		    dsmp->dsm_name);
    826 	rapid_commit:
    827 		/*
    828 		 * Extract the status code option.  If one is present and the
    829 		 * request failed, then try to go to another advertisement in
    830 		 * the list or restart the selection machinery.
    831 		 */
    832 		d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_STATUS_CODE,
    833 		    &olen);
    834 		status = dhcpv6_status_code(d6o, olen, &estr, &msg, &msglen);
    835 		/*
    836 		 * Check for the UseMulticast status code.  If this is present,
    837 		 * and if we were actually using unicast, then drop back and
    838 		 * try again.  If we weren't using unicast, then just pretend
    839 		 * we never saw this message -- the peer is confused.  (TAHI
    840 		 * does this.)
    841 		 */
    842 		if (status == DHCPV6_STAT_USEMCAST) {
    843 			if (IN6_IS_ADDR_MULTICAST(
    844 			    &dsmp->dsm_send_dest.v6.sin6_addr)) {
    845 				break;
    846 			} else {
    847 				free_pkt_entry(plp);
    848 				dsmp->dsm_send_dest.v6.sin6_addr =
    849 				    ipv6_all_dhcp_relay_and_servers;
    850 				retransmit_now(dsmp);
    851 				return;
    852 			}
    853 		}
    854 		print_server_msg(dsmp, msg, msglen);
    855 		/*
    856 		 * We treat NoBinding at the top level as "success."  Granted,
    857 		 * this doesn't make much sense, but the TAHI test suite does
    858 		 * this.  NoBinding really only makes sense in the context of a
    859 		 * specific IA, as it refers to the GUID:IAID binding, so
    860 		 * ignoring it at the top level is safe.
    861 		 */
    862 		if (status == DHCPV6_STAT_SUCCESS ||
    863 		    status == DHCPV6_STAT_NOBINDING) {
    864 			if (dhcp_bound(dsmp, plp)) {
    865 				/*
    866 				 * dhcp_bound will stop retransmission on
    867 				 * success, if that's called for.
    868 				 */
    869 				server_unicast_option(dsmp, plp);
    870 			} else {
    871 				stop_pkt_retransmission(dsmp);
    872 				dhcpmsg(MSG_WARNING, "accept_v6_message: "
    873 				    "dhcp_bound failed for %s", dsmp->dsm_name);
    874 				(void) remove_hostconf(dsmp->dsm_name,
    875 				    dsmp->dsm_isv6);
    876 				dhcp_restart(dsmp);
    877 			}
    878 		} else {
    879 			dhcpmsg(MSG_WARNING, "accept_v6_message: Reply: %s",
    880 			    estr);
    881 			stop_pkt_retransmission(dsmp);
    882 			free_pkt_entry(plp);
    883 			if (dsmp->dsm_state == INFORM_SENT) {
    884 				(void) set_smach_state(dsmp, INIT);
    885 				ipc_action_finish(dsmp, DHCP_IPC_E_SRVFAILED);
    886 			} else {
    887 				(void) remove_hostconf(dsmp->dsm_name,
    888 				    dsmp->dsm_isv6);
    889 				request_failed(dsmp);
    890 			}
    891 		}
    892 		return;
    893 
    894 	case DECLINING:
    895 		/*
    896 		 * We're looking for Reply messages.
    897 		 */
    898 		if (recv_type != DHCPV6_MSG_REPLY)
    899 			break;
    900 		stop_pkt_retransmission(dsmp);
    901 		/*
    902 		 * Extract the status code option.  Note that it's not a
    903 		 * failure if the server reports an error.
    904 		 */
    905 		d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_STATUS_CODE,
    906 		    &olen);
    907 		if (dhcpv6_status_code(d6o, olen, &estr, &msg,
    908 		    &msglen) == DHCPV6_STAT_SUCCESS) {
    909 			print_server_msg(dsmp, msg, msglen);
    910 		} else {
    911 			dhcpmsg(MSG_WARNING, "accept_v6_message: Reply: %s",
    912 			    estr);
    913 		}
    914 		free_pkt_entry(plp);
    915 		if (dsmp->dsm_leases == NULL) {
    916 			dhcpmsg(MSG_VERBOSE, "accept_v6_message: %s has no "
    917 			    "leases left", dsmp->dsm_name);
    918 			dhcp_restart(dsmp);
    919 		} else if (dsmp->dsm_lif_wait == 0) {
    920 			(void) set_smach_state(dsmp, BOUND);
    921 		} else {
    922 			(void) set_smach_state(dsmp, PRE_BOUND);
    923 		}
    924 		return;
    925 
    926 	case RELEASING:
    927 		/*
    928 		 * We're looking for Reply messages.
    929 		 */
    930 		if (recv_type != DHCPV6_MSG_REPLY)
    931 			break;
    932 		stop_pkt_retransmission(dsmp);
    933 		/*
    934 		 * Extract the status code option.
    935 		 */
    936 		d6o = dhcpv6_pkt_option(plp, NULL, DHCPV6_OPT_STATUS_CODE,
    937 		    &olen);
    938 		if (dhcpv6_status_code(d6o, olen, &estr, &msg,
    939 		    &msglen) == DHCPV6_STAT_SUCCESS) {
    940 			print_server_msg(dsmp, msg, msglen);
    941 		} else {
    942 			dhcpmsg(MSG_WARNING, "accept_v6_message: Reply: %s",
    943 			    estr);
    944 		}
    945 		free_pkt_entry(plp);
    946 		finished_smach(dsmp, DHCP_IPC_SUCCESS);
    947 		return;
    948 	}
    949 
    950 	/*
    951 	 * Break from above switch means that the message must be discarded.
    952 	 */
    953 	dhcpmsg(MSG_VERBOSE,
    954 	    "accept_v6_message: discarded v6 %s on %s; state %s",
    955 	    pname, dsmp->dsm_name, dhcp_state_to_string(dsmp->dsm_state));
    956 	free_pkt_entry(plp);
    957 }
    958 
    959 /*
    960  * dhcp_acknak_global(): Processes reception of an ACK or NAK packet on the
    961  *			 global socket -- broadcast packets for IPv4, all
    962  *			 packets for DHCPv6.
    963  *
    964  *   input: iu_eh_t *: unused
    965  *	    int: the global file descriptor the ACK/NAK arrived on
    966  *	    short: unused
    967  *	    iu_event_id_t: unused
    968  *	    void *: unused
    969  *  output: void
    970  */
    971 
    972 /* ARGSUSED */
    973 void
    974 dhcp_acknak_global(iu_eh_t *ehp, int fd, short events, iu_event_id_t id,
    975     void *arg)
    976 {
    977 	PKT_LIST	*plp;
    978 	dhcp_pif_t	*pif;
    979 	uchar_t		recv_type;
    980 	const char	*pname;
    981 	uint_t		xid;
    982 	dhcp_smach_t	*dsmp;
    983 	boolean_t	isv6 = (fd == v6_sock_fd);
    984 	struct sockaddr_in sin;
    985 	const char	*reason;
    986 	size_t		sinlen = sizeof (sin);
    987 	int		sock;
    988 
    989 	plp = recv_pkt(fd, get_max_mtu(isv6), isv6);
    990 	if (plp == NULL)
    991 		return;
    992 
    993 	recv_type = pkt_recv_type(plp);
    994 	pname = pkt_type_to_string(recv_type, isv6);
    995 
    996 	/*
    997 	 * Find the corresponding state machine and pif.
    998 	 *
    999 	 * Note that DHCPv6 Reconfigure would be special: it's not the reply to
   1000 	 * any transaction, and thus we would need to search on transaction ID
   1001 	 * zero (all state machines) to find the match.	 However, Reconfigure
   1002 	 * is not yet supported.
   1003 	 */
   1004 	xid = pkt_get_xid(plp->pkt, isv6);
   1005 
   1006 	for (dsmp = lookup_smach_by_xid(xid, NULL, isv6); dsmp != NULL;
   1007 	    dsmp = lookup_smach_by_xid(xid, dsmp, isv6)) {
   1008 		pif = dsmp->dsm_lif->lif_pif;
   1009 		if (pif->pif_index == plp->ifindex ||
   1010 		    pif->pif_under_ipmp && pif->pif_grindex == plp->ifindex)
   1011 			break;
   1012 	}
   1013 
   1014 	if (dsmp == NULL) {
   1015 		dhcpmsg(MSG_VERBOSE, "dhcp_acknak_global: ignored v%d %s packet"
   1016 		    " on ifindex %d: unknown state machine", isv6 ? 6 : 4,
   1017 		    pname, plp->ifindex);
   1018 		free_pkt_entry(plp);
   1019 		return;
   1020 	}
   1021 
   1022 	if (!isv6 && !pkt_v4_match(recv_type, DHCP_PACK|DHCP_PNAK)) {
   1023 		reason = "not ACK or NAK";
   1024 		goto drop;
   1025 	}
   1026 
   1027 	/*
   1028 	 * For IPv4, most packets will be handled by dhcp_packet_lif().  The
   1029 	 * only exceptions are broadcast packets sent when lif_sock_ip_fd has
   1030 	 * bound to something other than INADDR_ANY.
   1031 	 */
   1032 	if (!isv6) {
   1033 		sock = dsmp->dsm_lif->lif_sock_ip_fd;
   1034 
   1035 		if (getsockname(sock, (struct sockaddr *)&sin, &sinlen) != -1 &&
   1036 		    sin.sin_addr.s_addr == INADDR_ANY) {
   1037 			reason = "handled by lif_sock_ip_fd";
   1038 			goto drop;
   1039 		}
   1040 	}
   1041 
   1042 	/*
   1043 	 * We've got a packet; make sure it's acceptable and cancel the REQUEST
   1044 	 * retransmissions.
   1045 	 */
   1046 	if (isv6)
   1047 		accept_v6_message(dsmp, plp, pname, recv_type);
   1048 	else
   1049 		accept_v4_acknak(dsmp, plp);
   1050 	return;
   1051 drop:
   1052 	dhcpmsg(MSG_VERBOSE, "dhcp_acknak_global: ignored v%d %s packet for %s "
   1053 	    "received on global socket: %s", isv6 ? 6 : 4, pname, pif->pif_name,
   1054 	    reason);
   1055 	free_pkt_entry(plp);
   1056 }
   1057 
   1058 /*
   1059  * request_failed(): Attempt to request an address has failed.  Take an
   1060  *		     appropriate action.
   1061  *
   1062  *   input: dhcp_smach_t *: state machine that has failed
   1063  *  output: void
   1064  */
   1065 
   1066 static void
   1067 request_failed(dhcp_smach_t *dsmp)
   1068 {
   1069 	PKT_LIST *offer;
   1070 
   1071 	dsmp->dsm_server = ipv6_all_dhcp_relay_and_servers;
   1072 	if ((offer = select_best(dsmp)) != NULL) {
   1073 		insque(offer, &dsmp->dsm_recv_pkt_list);
   1074 		dhcp_requesting(NULL, dsmp);
   1075 	} else {
   1076 		dhcpmsg(MSG_INFO, "no offers left on %s; restarting",
   1077 		    dsmp->dsm_name);
   1078 		dhcp_selecting(dsmp);
   1079 	}
   1080 }
   1081 
   1082 /*
   1083  * dhcp_packet_lif(): Processes reception of an ACK, NAK, or OFFER packet on
   1084  *		      a given logical interface for IPv4 (only).
   1085  *
   1086  *   input: iu_eh_t *: unused
   1087  *	    int: the file descriptor the packet arrived on
   1088  *	    short: unused
   1089  *	    iu_event_id_t: the id of this event callback with the handler
   1090  *	    void *: pointer to logical interface receiving message
   1091  *  output: void
   1092  */
   1093 
   1094 /* ARGSUSED */
   1095 void
   1096 dhcp_packet_lif(iu_eh_t *ehp, int fd, short events, iu_event_id_t id,
   1097     void *arg)
   1098 {
   1099 	dhcp_lif_t	*lif = arg;
   1100 	PKT_LIST	*plp;
   1101 	uchar_t		recv_type;
   1102 	const char	*pname;
   1103 	uint_t		xid;
   1104 	dhcp_smach_t	*dsmp;
   1105 
   1106 	if ((plp = recv_pkt(fd, lif->lif_max, B_FALSE)) == NULL)
   1107 		return;
   1108 
   1109 	recv_type = pkt_recv_type(plp);
   1110 	pname = pkt_type_to_string(recv_type, B_FALSE);
   1111 
   1112 	if (!pkt_v4_match(recv_type,
   1113 	    DHCP_PACK | DHCP_PNAK | DHCP_PUNTYPED | DHCP_POFFER)) {
   1114 		dhcpmsg(MSG_VERBOSE, "dhcp_packet_lif: ignored v4 %s packet "
   1115 		    "received via LIF %s", pname, lif->lif_name);
   1116 		free_pkt_entry(plp);
   1117 		return;
   1118 	}
   1119 
   1120 	/*
   1121 	 * Find the corresponding state machine.
   1122 	 */
   1123 	xid = pkt_get_xid(plp->pkt, B_FALSE);
   1124 	for (dsmp = lookup_smach_by_xid(xid, NULL, B_FALSE); dsmp != NULL;
   1125 	    dsmp = lookup_smach_by_xid(xid, dsmp, B_FALSE)) {
   1126 		if (dsmp->dsm_lif == lif)
   1127 			break;
   1128 	}
   1129 
   1130 	if (dsmp == NULL)
   1131 		goto drop;
   1132 
   1133 	if (pkt_v4_match(recv_type, DHCP_PACK|DHCP_PNAK)) {
   1134 		/*
   1135 		 * We've got an ACK/NAK; make sure it's acceptable and cancel
   1136 		 * the REQUEST retransmissions.
   1137 		 */
   1138 		accept_v4_acknak(dsmp, plp);
   1139 	} else {
   1140 		if (is_bound_state(dsmp->dsm_state))
   1141 			goto drop;
   1142 		/*
   1143 		 * Must be an OFFER or a BOOTP message: enqueue it for later
   1144 		 * processing by select_best().
   1145 		 */
   1146 		pkt_smach_enqueue(dsmp, plp);
   1147 	}
   1148 	return;
   1149 drop:
   1150 	dhcpmsg(MSG_VERBOSE, "dhcp_packet_lif: ignored %s packet xid "
   1151 	    "%x received via LIF %s; %s", pname, xid, lif->lif_name,
   1152 	    dsmp == NULL ? "unknown state machine" : "bound");
   1153 	free_pkt_entry(plp);
   1154 }
   1155 
   1156 /*
   1157  * dhcp_restart(): restarts DHCP (from INIT) on a given state machine, but only
   1158  *		   if we're leasing addresses.  Doesn't restart for information-
   1159  *		   only interfaces.
   1160  *
   1161  *   input: dhcp_smach_t *: the state machine to restart DHCP on
   1162  *  output: void
   1163  */
   1164 
   1165 void
   1166 dhcp_restart(dhcp_smach_t *dsmp)
   1167 {
   1168 	if (dsmp->dsm_state == INFORM_SENT || dsmp->dsm_state == INFORMATION)
   1169 		return;
   1170 
   1171 	/*
   1172 	 * As we're returning to INIT state, we need to discard any leases we
   1173 	 * may have, and (for v4) canonize the LIF.  There's a bit of tension
   1174 	 * between keeping around a possibly still working address, and obeying
   1175 	 * the RFCs.  A more elaborate design would be to mark the addresses as
   1176 	 * DEPRECATED, and then start a removal timer.  Such a design would
   1177 	 * probably compromise testing.
   1178 	 */
   1179 	deprecate_leases(dsmp);
   1180 
   1181 	if (!set_start_timer(dsmp)) {
   1182 		dhcpmsg(MSG_ERROR, "dhcp_restart: cannot schedule dhcp_start, "
   1183 		    "reverting to INIT state on %s", dsmp->dsm_name);
   1184 
   1185 		(void) set_smach_state(dsmp, INIT);
   1186 		dsmp->dsm_dflags |= DHCP_IF_FAILED;
   1187 		ipc_action_finish(dsmp, DHCP_IPC_E_MEMORY);
   1188 	} else {
   1189 		dhcpmsg(MSG_DEBUG, "dhcp_restart: restarting DHCP on %s",
   1190 		    dsmp->dsm_name);
   1191 	}
   1192 }
   1193 
   1194 /*
   1195  * stop_requesting(): decides when to stop retransmitting REQUESTs
   1196  *
   1197  *   input: dhcp_smach_t *: the state machine REQUESTs are being sent from
   1198  *	    unsigned int: the number of REQUESTs sent so far
   1199  *  output: boolean_t: B_TRUE if retransmissions should stop
   1200  */
   1201 
   1202 static boolean_t
   1203 stop_requesting(dhcp_smach_t *dsmp, unsigned int n_requests)
   1204 {
   1205 	uint_t maxreq;
   1206 
   1207 	maxreq = dsmp->dsm_isv6 ? DHCPV6_REQ_MAX_RC : DHCP_MAX_REQUESTS;
   1208 	if (n_requests >= maxreq) {
   1209 
   1210 		dhcpmsg(MSG_INFO, "no ACK/NAK/Reply to REQUEST on %s",
   1211 		    dsmp->dsm_name);
   1212 
   1213 		request_failed(dsmp);
   1214 		return (B_TRUE);
   1215 	} else {
   1216 		return (B_FALSE);
   1217 	}
   1218 }
   1219