xref: /titanic_50/usr/src/cmd/cmd-inet/sbin/dhcpagent/states.c (revision 4e4761498aa6990539820cfc2ee7b1c7c53b6bc3)
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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  *
25  * This module contains core functions for managing DHCP state machine
26  * instances.
27  */
28 
29 #pragma ident	"%Z%%M%	%I%	%E% SMI"
30 
31 #include <stdlib.h>
32 #include <search.h>
33 #include <string.h>
34 #include <ctype.h>
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <netinet/in.h>
38 #include <netinet/arp.h>
39 #include <arpa/inet.h>
40 #include <dhcpmsg.h>
41 #include <dhcpagent_util.h>
42 #include <dhcp_stable.h>
43 
44 #include "agent.h"
45 #include "states.h"
46 #include "interface.h"
47 #include "defaults.h"
48 #include "script_handler.h"
49 
50 static uint_t global_smach_count;
51 
52 static uchar_t *global_duid;
53 static size_t global_duidlen;
54 
55 /*
56  * iaid_retry(): attempt to write LIF IAID again
57  *
58  *   input: iu_tq_t *: ignored
59  *	    void *: pointer to LIF
60  *  output: none
61  */
62 
63 /* ARGSUSED */
64 static void
65 iaid_retry(iu_tq_t *tqp, void *arg)
66 {
67 	dhcp_lif_t *lif = arg;
68 
69 	if (write_stable_iaid(lif->lif_name, lif->lif_iaid) == -1) {
70 		if (errno != EROFS) {
71 			dhcpmsg(MSG_ERR,
72 			    "iaid_retry: unable to write out IAID for %s",
73 			    lif->lif_name);
74 			release_lif(lif);
75 		} else {
76 			lif->lif_iaid_id = iu_schedule_timer(tq, 60,
77 			    iaid_retry, lif);
78 		}
79 	} else {
80 		release_lif(lif);
81 	}
82 }
83 
84 /*
85  * insert_smach(): Create a state machine instance on a given logical
86  *		   interface.  The state machine holds the caller's LIF
87  *		   reference on success, and frees it on failure.
88  *
89  *   input: dhcp_lif_t *: logical interface name
90  *	    int *: set to DHCP_IPC_E_* if creation fails
91  *  output: dhcp_smach_t *: state machine instance
92  */
93 
94 dhcp_smach_t *
95 insert_smach(dhcp_lif_t *lif, int *error)
96 {
97 	dhcp_smach_t *dsmp, *alt_primary;
98 	boolean_t isv6;
99 	const char *prl;
100 
101 	if ((dsmp = calloc(1, sizeof (*dsmp))) == NULL) {
102 		dhcpmsg(MSG_ERR, "cannot allocate state machine entry for %s",
103 		    lif->lif_name);
104 		remove_lif(lif);
105 		release_lif(lif);
106 		*error = DHCP_IPC_E_MEMORY;
107 		return (NULL);
108 	}
109 	dsmp->dsm_name = lif->lif_name;
110 	dsmp->dsm_lif = lif;
111 	dsmp->dsm_hold_count = 1;
112 	dsmp->dsm_state = INIT;
113 	dsmp->dsm_dflags = DHCP_IF_REMOVED;	/* until added to list */
114 	isv6 = lif->lif_pif->pif_isv6;
115 
116 	/*
117 	 * Now that we have a controlling LIF, we need to assign an IAID to
118 	 * that LIF.
119 	 */
120 	if (lif->lif_iaid == 0 &&
121 	    (lif->lif_iaid = read_stable_iaid(lif->lif_name)) == 0) {
122 		static uint32_t iaidctr = 0x80000000u;
123 
124 		/*
125 		 * If this is a logical interface, then use an arbitrary seed
126 		 * value.  Otherwise, use the ifIndex.
127 		 */
128 		lif->lif_iaid = make_stable_iaid(lif->lif_name,
129 		    strchr(lif->lif_name, ':') != NULL ? iaidctr++ :
130 		    lif->lif_pif->pif_index);
131 		dhcpmsg(MSG_INFO,
132 		    "insert_smach: manufactured IAID %u for v%d %s",
133 		    lif->lif_iaid, isv6 ? 6 : 4, lif->lif_name);
134 		hold_lif(lif);
135 		iaid_retry(NULL, lif);
136 	}
137 
138 	if (isv6) {
139 		dsmp->dsm_dflags |= DHCP_IF_V6;
140 		dsmp->dsm_server = ipv6_all_dhcp_relay_and_servers;
141 
142 		/*
143 		 * With DHCPv6, we do all of our I/O using the common
144 		 * v6_sock_fd.  There's no need for per-interface file
145 		 * descriptors because we have IPV6_PKTINFO.
146 		 */
147 	} else {
148 		IN6_IPADDR_TO_V4MAPPED(htonl(INADDR_BROADCAST),
149 		    &dsmp->dsm_server);
150 
151 		/*
152 		 * With IPv4 DHCP, we start off doing our I/O via DLPI, so open
153 		 * that up now.
154 		 */
155 		if (!open_dlpi_pif(lif->lif_pif)) {
156 			dhcpmsg(MSG_ERR, "unable to open DLPI for %s",
157 			    lif->lif_name);
158 			/* This will also dispose of the LIF */
159 			release_smach(dsmp);
160 			*error = DHCP_IPC_E_SOCKET;
161 			return (NULL);
162 		}
163 		dsmp->dsm_using_dlpi = B_TRUE;
164 	}
165 	dsmp->dsm_retrans_timer		= -1;
166 	dsmp->dsm_offer_timer		= -1;
167 	dsmp->dsm_neg_hrtime		= gethrtime();
168 	dsmp->dsm_script_fd		= -1;
169 	dsmp->dsm_script_pid		= -1;
170 	dsmp->dsm_script_helper_pid	= -1;
171 	dsmp->dsm_script_event_id	= -1;
172 
173 	ipc_action_init(&dsmp->dsm_ia);
174 
175 	/*
176 	 * initialize the parameter request list, if there is one.
177 	 */
178 
179 	prl = df_get_string(dsmp->dsm_name, isv6, DF_PARAM_REQUEST_LIST);
180 	if (prl == NULL) {
181 		dsmp->dsm_prl = NULL;
182 	} else {
183 		int i;
184 
185 		for (dsmp->dsm_prllen = 1, i = 0; prl[i] != '\0'; i++) {
186 			if (prl[i] == ',')
187 				dsmp->dsm_prllen++;
188 		}
189 
190 		dsmp->dsm_prl = malloc(dsmp->dsm_prllen *
191 		    sizeof (*dsmp->dsm_prl));
192 		if (dsmp->dsm_prl == NULL) {
193 			dhcpmsg(MSG_WARNING, "insert_smach: cannot allocate "
194 			    "parameter request list for %s (continuing)",
195 			    dsmp->dsm_name);
196 		} else {
197 			for (i = 0; i < dsmp->dsm_prllen; prl++, i++) {
198 				dsmp->dsm_prl[i] = strtoul(prl, NULL, 0);
199 				while (*prl != ',' && *prl != '\0')
200 					prl++;
201 				if (*prl == '\0')
202 					break;
203 			}
204 		}
205 	}
206 
207 	dsmp->dsm_offer_wait = df_get_int(dsmp->dsm_name, isv6,
208 	    DF_OFFER_WAIT);
209 
210 	/*
211 	 * If there is no primary of this type, and there is one of the other,
212 	 * then make this one primary if it's on the same named PIF.
213 	 */
214 	if (primary_smach(isv6) == NULL &&
215 	    (alt_primary = primary_smach(!isv6)) != NULL) {
216 		if (strcmp(lif->lif_pif->pif_name,
217 		    alt_primary->dsm_lif->lif_pif->pif_name) == 0) {
218 			dhcpmsg(MSG_DEBUG,
219 			    "insert_smach: making %s primary for v%d",
220 			    dsmp->dsm_name, isv6 ? 6 : 4);
221 			dsmp->dsm_dflags |= DHCP_IF_PRIMARY;
222 		}
223 	}
224 
225 	/*
226 	 * We now have at least one state machine running, so cancel any
227 	 * running inactivity timer.
228 	 */
229 	if (inactivity_id != -1 &&
230 	    iu_cancel_timer(tq, inactivity_id, NULL) == 1)
231 		inactivity_id = -1;
232 
233 	dsmp->dsm_dflags &= ~DHCP_IF_REMOVED;
234 	insque(dsmp, &lif->lif_smachs);
235 	global_smach_count++;
236 	dhcpmsg(MSG_DEBUG2, "insert_smach: inserted %s", dsmp->dsm_name);
237 
238 	return (dsmp);
239 }
240 
241 /*
242  * hold_smach(): acquires a hold on a state machine
243  *
244  *   input: dhcp_smach_t *: the state machine to acquire a hold on
245  *  output: void
246  */
247 
248 void
249 hold_smach(dhcp_smach_t *dsmp)
250 {
251 	dsmp->dsm_hold_count++;
252 
253 	dhcpmsg(MSG_DEBUG2, "hold_smach: hold count on %s: %d",
254 	    dsmp->dsm_name, dsmp->dsm_hold_count);
255 }
256 
257 /*
258  * free_smach(): frees the memory occupied by a state machine
259  *
260  *   input: dhcp_smach_t *: the DHCP state machine to free
261  *  output: void
262  */
263 
264 static void
265 free_smach(dhcp_smach_t *dsmp)
266 {
267 	dhcpmsg(MSG_DEBUG, "free_smach: freeing state machine %s",
268 	    dsmp->dsm_name);
269 
270 	deprecate_leases(dsmp);
271 	remove_lif(dsmp->dsm_lif);
272 	release_lif(dsmp->dsm_lif);
273 	free_pkt_list(&dsmp->dsm_recv_pkt_list);
274 	if (dsmp->dsm_ack != dsmp->dsm_orig_ack)
275 		free_pkt_entry(dsmp->dsm_orig_ack);
276 	free_pkt_entry(dsmp->dsm_ack);
277 	free(dsmp->dsm_send_pkt.pkt);
278 	free(dsmp->dsm_cid);
279 	free(dsmp->dsm_prl);
280 	free(dsmp->dsm_routers);
281 	free(dsmp->dsm_reqhost);
282 	free(dsmp);
283 
284 	/* no big deal if this fails */
285 	if (global_smach_count == 0 && inactivity_id == -1) {
286 		inactivity_id = iu_schedule_timer(tq, DHCP_INACTIVITY_WAIT,
287 		    inactivity_shutdown, NULL);
288 	}
289 }
290 
291 /*
292  * release_smach(): releases a hold previously acquired on a state machine.
293  *		    If the hold count reaches 0, the state machine is freed.
294  *
295  *   input: dhcp_smach_t *: the state machine entry to release the hold on
296  *  output: void
297  */
298 
299 void
300 release_smach(dhcp_smach_t *dsmp)
301 {
302 	if (dsmp->dsm_hold_count == 0) {
303 		dhcpmsg(MSG_CRIT, "release_smach: extraneous release");
304 		return;
305 	}
306 
307 	if (dsmp->dsm_hold_count == 1 &&
308 	    !(dsmp->dsm_dflags & DHCP_IF_REMOVED)) {
309 		dhcpmsg(MSG_CRIT, "release_smach: missing removal");
310 		return;
311 	}
312 
313 	if (--dsmp->dsm_hold_count == 0) {
314 		free_smach(dsmp);
315 	} else {
316 		dhcpmsg(MSG_DEBUG2, "release_smach: hold count on %s: %d",
317 		    dsmp->dsm_name, dsmp->dsm_hold_count);
318 	}
319 }
320 
321 /*
322  * next_smach(): state machine iterator function
323  *
324  *   input: dhcp_smach_t *: current state machine (or NULL for list start)
325  *          boolean_t: B_TRUE if DHCPv6, B_FALSE otherwise
326  *  output: dhcp_smach_t *: next state machine in list
327  */
328 
329 dhcp_smach_t *
330 next_smach(dhcp_smach_t *dsmp, boolean_t isv6)
331 {
332 	dhcp_lif_t *lif;
333 	dhcp_pif_t *pif;
334 
335 	if (dsmp != NULL) {
336 		if (dsmp->dsm_next != NULL)
337 			return (dsmp->dsm_next);
338 
339 		if ((lif = dsmp->dsm_lif) != NULL)
340 			lif = lif->lif_next;
341 		for (; lif != NULL; lif = lif->lif_next) {
342 			if (lif->lif_smachs != NULL)
343 				return (lif->lif_smachs);
344 		}
345 
346 		if ((pif = dsmp->dsm_lif->lif_pif) != NULL)
347 			pif = pif->pif_next;
348 	} else {
349 		pif = isv6 ? v6root : v4root;
350 	}
351 	for (; pif != NULL; pif = pif->pif_next) {
352 		for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) {
353 			if (lif->lif_smachs != NULL)
354 				return (lif->lif_smachs);
355 		}
356 	}
357 	return (NULL);
358 }
359 
360 /*
361  * primary_smach(): loop through all state machines of the given type (v4 or
362  *		    v6) in the system, and locate the one that's primary.
363  *
364  *   input: boolean_t: B_TRUE for IPv6
365  *  output: dhcp_smach_t *: the primary state machine
366  */
367 
368 dhcp_smach_t *
369 primary_smach(boolean_t isv6)
370 {
371 	dhcp_smach_t *dsmp;
372 
373 	for (dsmp = next_smach(NULL, isv6); dsmp != NULL;
374 	    dsmp = next_smach(dsmp, isv6)) {
375 		if (dsmp->dsm_dflags & DHCP_IF_PRIMARY)
376 			break;
377 	}
378 	return (dsmp);
379 }
380 
381 /*
382  * make_primary(): designate a given state machine as being the primary
383  *		   instance on the primary interface.  Note that the user often
384  *		   thinks in terms of a primary "interface" (rather than just
385  *		   an instance), so we go to lengths here to keep v4 and v6 in
386  *		   sync.
387  *
388  *   input: dhcp_smach_t *: the primary state machine
389  *  output: none
390  */
391 
392 void
393 make_primary(dhcp_smach_t *dsmp)
394 {
395 	dhcp_smach_t *old_primary, *alt_primary;
396 	dhcp_pif_t *pif;
397 
398 	if ((old_primary = primary_smach(dsmp->dsm_isv6)) != NULL)
399 		old_primary->dsm_dflags &= ~DHCP_IF_PRIMARY;
400 	dsmp->dsm_dflags |= DHCP_IF_PRIMARY;
401 
402 	/*
403 	 * Find the primary for the other protocol.
404 	 */
405 	alt_primary = primary_smach(!dsmp->dsm_isv6);
406 
407 	/*
408 	 * If it's on a different interface, then cancel that.  If it's on the
409 	 * same interface, then we're done.
410 	 */
411 	if (alt_primary != NULL) {
412 		if (strcmp(alt_primary->dsm_lif->lif_pif->pif_name,
413 		    dsmp->dsm_lif->lif_pif->pif_name) == 0)
414 			return;
415 		alt_primary->dsm_dflags &= ~DHCP_IF_PRIMARY;
416 	}
417 
418 	/*
419 	 * We need a new primary for the other protocol.  If the PIF exists,
420 	 * there must be at least one state machine.  Just choose the first for
421 	 * consistency with insert_smach().
422 	 */
423 	if ((pif = lookup_pif_by_name(dsmp->dsm_lif->lif_pif->pif_name,
424 	    !dsmp->dsm_isv6)) != NULL) {
425 		pif->pif_lifs->lif_smachs->dsm_dflags |= DHCP_IF_PRIMARY;
426 	}
427 }
428 
429 /*
430  * lookup_smach(): finds a state machine by name and type; used for dispatching
431  *		   user commands.
432  *
433  *   input: const char *: the name of the state machine
434  *          boolean_t: B_TRUE if DHCPv6, B_FALSE otherwise
435  *  output: dhcp_smach_t *: the state machine found
436  */
437 
438 dhcp_smach_t *
439 lookup_smach(const char *smname, boolean_t isv6)
440 {
441 	dhcp_smach_t *dsmp;
442 
443 	for (dsmp = next_smach(NULL, isv6); dsmp != NULL;
444 	    dsmp = next_smach(dsmp, isv6)) {
445 		if (strcmp(dsmp->dsm_name, smname) == 0)
446 			break;
447 	}
448 	return (dsmp);
449 }
450 
451 /*
452  * lookup_smach_by_uindex(): iterate through running state machines by
453  *			     truncated interface index.
454  *
455  *   input: uint16_t: the interface index (truncated)
456  *	    dhcp_smach_t *: the previous state machine, or NULL for start
457  *	    boolean_t: B_TRUE for DHCPv6, B_FALSE for IPv4 DHCP
458  *  output: dhcp_smach_t *: next state machine, or NULL at end of list
459  */
460 
461 dhcp_smach_t *
462 lookup_smach_by_uindex(uint16_t ifindex, dhcp_smach_t *dsmp, boolean_t isv6)
463 {
464 	dhcp_pif_t *pif;
465 	dhcp_lif_t *lif;
466 
467 	/*
468 	 * If the user gives us a state machine, then check that the next one
469 	 * available is on the same physical interface.  If so, then go ahead
470 	 * and return that.
471 	 */
472 	if (dsmp != NULL) {
473 		pif = dsmp->dsm_lif->lif_pif;
474 		if ((dsmp = next_smach(dsmp, isv6)) == NULL)
475 			return (NULL);
476 		if (pif == dsmp->dsm_lif->lif_pif)
477 			return (dsmp);
478 	} else {
479 		/* Otherwise, start at the beginning of the list */
480 		pif = NULL;
481 	}
482 
483 	/*
484 	 * Find the next physical interface with the same truncated interface
485 	 * index, and return the first state machine on that.  If there are no
486 	 * more physical interfaces that match, then we're done.
487 	 */
488 	do {
489 		pif = lookup_pif_by_uindex(ifindex, pif, isv6);
490 		if (pif == NULL)
491 			return (NULL);
492 		for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) {
493 			if ((dsmp = lif->lif_smachs) != NULL)
494 				break;
495 		}
496 	} while (dsmp == NULL);
497 	return (dsmp);
498 }
499 
500 /*
501  * lookup_smach_by_xid(): iterate through running state machines by transaction
502  *			  id.  Transaction ID zero means "all state machines."
503  *
504  *   input: uint32_t: the transaction id to look up
505  *	    dhcp_smach_t *: the previous state machine, or NULL for start
506  *	    boolean_t: B_TRUE if DHCPv6, B_FALSE otherwise
507  *  output: dhcp_smach_t *: next state machine, or NULL at end of list
508  */
509 
510 dhcp_smach_t *
511 lookup_smach_by_xid(uint32_t xid, dhcp_smach_t *dsmp, boolean_t isv6)
512 {
513 	for (dsmp = next_smach(dsmp, isv6); dsmp != NULL;
514 	    dsmp = next_smach(dsmp, isv6)) {
515 		if (xid == 0 ||
516 		    pkt_get_xid(dsmp->dsm_send_pkt.pkt, isv6) == xid)
517 			break;
518 	}
519 
520 	return (dsmp);
521 }
522 
523 /*
524  * lookup_smach_by_event(): find a state machine busy with a particular event
525  *			    ID.  This is used only for error handling.
526  *
527  *   input: iu_event_id_t: the event id to look up
528  *  output: dhcp_smach_t *: matching state machine, or NULL if none
529  */
530 
531 dhcp_smach_t *
532 lookup_smach_by_event(iu_event_id_t eid)
533 {
534 	dhcp_smach_t *dsmp;
535 	boolean_t isv6 = B_FALSE;
536 
537 	for (;;) {
538 		for (dsmp = next_smach(NULL, isv6); dsmp != NULL;
539 		    dsmp = next_smach(dsmp, isv6)) {
540 			if ((dsmp->dsm_dflags & DHCP_IF_BUSY) &&
541 			    eid == dsmp->dsm_ia.ia_eid)
542 				return (dsmp);
543 		}
544 		if (isv6)
545 			break;
546 		isv6 = B_TRUE;
547 	}
548 
549 	return (dsmp);
550 }
551 
552 /*
553  * cancel_offer_timer(): stop the offer polling timer on a given state machine
554  *
555  *   input: dhcp_smach_t *: state machine on which to stop polling for offers
556  *  output: none
557  */
558 
559 void
560 cancel_offer_timer(dhcp_smach_t *dsmp)
561 {
562 	int retval;
563 
564 	if (dsmp->dsm_offer_timer != -1) {
565 		retval = iu_cancel_timer(tq, dsmp->dsm_offer_timer, NULL);
566 		dsmp->dsm_offer_timer = -1;
567 		if (retval == 1)
568 			release_smach(dsmp);
569 	}
570 }
571 
572 /*
573  * cancel_smach_timers(): stop all of the timers related to a given state
574  *			  machine, including lease and LIF expiry.
575  *
576  *   input: dhcp_smach_t *: state machine to cancel
577  *  output: none
578  */
579 
580 static void
581 cancel_smach_timers(dhcp_smach_t *dsmp)
582 {
583 	dhcp_lease_t *dlp;
584 	dhcp_lif_t *lif;
585 	uint_t nlifs;
586 
587 	for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
588 		cancel_lease_timers(dlp);
589 		lif = dlp->dl_lifs;
590 		nlifs = dlp->dl_nlifs;
591 		for (; nlifs > 0; nlifs--, lif = lif->lif_next)
592 			cancel_lif_timers(lif);
593 	}
594 
595 	cancel_offer_timer(dsmp);
596 	stop_pkt_retransmission(dsmp);
597 }
598 
599 /*
600  * remove_smach(): removes a given state machine from the system.  marks it
601  *		   for being freed (but may not actually free it).
602  *
603  *   input: dhcp_smach_t *: the state machine to remove
604  *  output: void
605  */
606 
607 void
608 remove_smach(dhcp_smach_t *dsmp)
609 {
610 	if (dsmp->dsm_dflags & DHCP_IF_REMOVED)
611 		return;
612 
613 	dhcpmsg(MSG_DEBUG2, "remove_smach: removing %s", dsmp->dsm_name);
614 	dsmp->dsm_dflags |= DHCP_IF_REMOVED;
615 	remque(dsmp);
616 	global_smach_count--;
617 
618 	/*
619 	 * if we have long term timers, cancel them so that state machine
620 	 * resources can be reclaimed in a reasonable amount of time.
621 	 */
622 	cancel_smach_timers(dsmp);
623 
624 	/* Drop the hold that the LIF's state machine list had on us */
625 	release_smach(dsmp);
626 }
627 
628 /*
629  * finished_smach(): we're finished with a given state machine; remove it from
630  *		     the system and tell the user (who may have initiated the
631  *		     removal process).  Note that we remove it from the system
632  *		     first to allow back-to-back drop and create invocations.
633  *
634  *   input: dhcp_smach_t *: the state machine to remove
635  *	    int: error for IPC
636  *  output: void
637  */
638 
639 void
640 finished_smach(dhcp_smach_t *dsmp, int error)
641 {
642 	hold_smach(dsmp);
643 	remove_smach(dsmp);
644 	if (dsmp->dsm_ia.ia_fd != -1)
645 		ipc_action_finish(dsmp, error);
646 	else
647 		(void) async_cancel(dsmp);
648 	release_smach(dsmp);
649 }
650 
651 /*
652  * set_smach_state(): changes state and updates I/O
653  *
654  *   input: dhcp_smach_t *: the state machine to change
655  *	    DHCPSTATE: the new state
656  *  output: boolean_t: B_TRUE on success, B_FALSE on failure
657  */
658 
659 boolean_t
660 set_smach_state(dhcp_smach_t *dsmp, DHCPSTATE state)
661 {
662 	if (dsmp->dsm_state != state) {
663 		boolean_t is_bound;
664 
665 		dhcpmsg(MSG_DEBUG,
666 		    "set_smach_state: changing from %s to %s on %s",
667 		    dhcp_state_to_string(dsmp->dsm_state),
668 		    dhcp_state_to_string(state), dsmp->dsm_name);
669 
670 		if (!dsmp->dsm_isv6) {
671 			/*
672 			 * When we're in a bound state for IPv4, we receive our
673 			 * packets through our LIF.  Otherwise, we receive them
674 			 * through DLPI.  Make sure the right one is connected.
675 			 * For IPv6, no such change is necessary.
676 			 */
677 			is_bound = (state == BOUND || state == REBINDING ||
678 			    state == RENEWING || state == RELEASING);
679 			if (dsmp->dsm_using_dlpi && is_bound) {
680 				if (!open_ip_lif(dsmp->dsm_lif))
681 					return (B_FALSE);
682 				dsmp->dsm_using_dlpi = B_FALSE;
683 				close_dlpi_pif(dsmp->dsm_lif->lif_pif);
684 			}
685 			if (!dsmp->dsm_using_dlpi && !is_bound) {
686 				if (!open_dlpi_pif(dsmp->dsm_lif->lif_pif))
687 					return (B_FALSE);
688 				dsmp->dsm_using_dlpi = B_TRUE;
689 				close_ip_lif(dsmp->dsm_lif);
690 			}
691 		}
692 
693 		dsmp->dsm_state = state;
694 	}
695 	return (B_TRUE);
696 }
697 
698 /*
699  * duid_retry(): attempt to write DUID again
700  *
701  *   input: iu_tq_t *: ignored
702  *	    void *: ignored
703  *  output: none
704  */
705 
706 /* ARGSUSED */
707 static void
708 duid_retry(iu_tq_t *tqp, void *arg)
709 {
710 	if (write_stable_duid(global_duid, global_duidlen) == -1) {
711 		if (errno != EROFS) {
712 			dhcpmsg(MSG_ERR,
713 			    "duid_retry: unable to write out DUID");
714 		} else {
715 			(void) iu_schedule_timer(tq, 60, duid_retry, NULL);
716 		}
717 	}
718 }
719 
720 /*
721  * get_smach_cid(): gets the client ID for a given state machine.
722  *
723  *   input: dhcp_smach_t *: the state machine to set up
724  *  output: int: DHCP_IPC_SUCCESS or one of DHCP_IPC_E_* on failure.
725  */
726 
727 int
728 get_smach_cid(dhcp_smach_t *dsmp)
729 {
730 	uchar_t *client_id;
731 	uint_t client_id_len;
732 	dhcp_lif_t *lif = dsmp->dsm_lif;
733 	dhcp_pif_t *pif = lif->lif_pif;
734 	const char *value;
735 	size_t slen;
736 
737 	/*
738 	 * Look in defaults file for the client-id.  If present, this takes
739 	 * precedence over all other forms of ID.
740 	 */
741 
742 	dhcpmsg(MSG_DEBUG, "get_smach_cid: getting default client-id "
743 	    "property on %s", dsmp->dsm_name);
744 	value = df_get_string(dsmp->dsm_name, pif->pif_isv6, DF_CLIENT_ID);
745 	if (value != NULL) {
746 		/*
747 		 * The Client ID string can have one of three basic forms:
748 		 *	<decimal>,<data...>
749 		 *	0x<hex...>
750 		 *	<string...>
751 		 *
752 		 * The first form is an RFC 3315 DUID.  This is legal for both
753 		 * IPv4 DHCP and DHCPv6.  For IPv4, an RFC 4361 Client ID is
754 		 * constructed from this value.
755 		 *
756 		 * The second and third forms are legal for IPv4 only.  This is
757 		 * a raw Client ID, in hex or ASCII string format.
758 		 */
759 
760 		if (isdigit(*value) &&
761 		    value[strspn(value, "0123456789")] == ',') {
762 			char *cp;
763 			ulong_t duidtype;
764 			ulong_t subtype;
765 
766 			errno = 0;
767 			duidtype = strtoul(value, &cp, 0);
768 			if (value == cp || errno != 0 || *cp != ',' ||
769 			    duidtype > 65535) {
770 				dhcpmsg(MSG_ERR, "get_smach_cid: cannot parse "
771 				    "DUID type in %s", value);
772 				goto no_specified_id;
773 			}
774 			value = cp + 1;
775 			switch (duidtype) {
776 			case DHCPV6_DUID_LL:
777 			case DHCPV6_DUID_LLT: {
778 				int num;
779 				char chr;
780 
781 				errno = 0;
782 				subtype = strtoul(value, &cp, 0);
783 				if (value == cp || errno != 0 || *cp != ',' ||
784 				    subtype > 65535) {
785 					dhcpmsg(MSG_ERR, "get_smach_cid: "
786 					    "cannot parse MAC type in %s",
787 					    value);
788 					goto no_specified_id;
789 				}
790 				value = cp + 1;
791 				client_id_len = pif->pif_isv6 ? 1 : 5;
792 				for (; *cp != '\0'; cp++) {
793 					if (*cp == ':')
794 						client_id_len++;
795 					else if (!isxdigit(*cp))
796 						break;
797 				}
798 				if (duidtype == DHCPV6_DUID_LL) {
799 					duid_llt_t *dllt;
800 					time_t now;
801 
802 					client_id_len += sizeof (*dllt);
803 					dllt = malloc(client_id_len);
804 					if (dllt == NULL)
805 						goto alloc_failure;
806 					dsmp->dsm_cid = (uchar_t *)dllt;
807 					dllt->dllt_dutype = htons(duidtype);
808 					dllt->dllt_hwtype = htons(subtype);
809 					now = time(NULL) - DUID_TIME_BASE;
810 					dllt->dllt_time = htonl(now);
811 					cp = (char *)(dllt + 1);
812 				} else {
813 					duid_ll_t *dll;
814 
815 					client_id_len += sizeof (*dll);
816 					dll = malloc(client_id_len);
817 					if (dll == NULL)
818 						goto alloc_failure;
819 					dsmp->dsm_cid = (uchar_t *)dll;
820 					dll->dll_dutype = htons(duidtype);
821 					dll->dll_hwtype = htons(subtype);
822 					cp = (char *)(dll + 1);
823 				}
824 				num = 0;
825 				while ((chr = *value) != '\0') {
826 					if (isdigit(chr)) {
827 						num = (num << 4) + chr - '0';
828 					} else if (isxdigit(chr)) {
829 						num = (num << 4) + 10 + chr -
830 						    (isupper(chr) ? 'A' : 'a');
831 					} else if (chr == ':') {
832 						*cp++ = num;
833 						num = 0;
834 					} else {
835 						break;
836 					}
837 				}
838 				break;
839 			}
840 			case DHCPV6_DUID_EN: {
841 				duid_en_t *den;
842 
843 				errno = 0;
844 				subtype = strtoul(value, &cp, 0);
845 				if (value == cp || errno != 0 || *cp != ',') {
846 					dhcpmsg(MSG_ERR, "get_smach_cid: "
847 					    "cannot parse enterprise in %s",
848 					    value);
849 					goto no_specified_id;
850 				}
851 				value = cp + 1;
852 				slen = strlen(value);
853 				client_id_len = (slen + 1) / 2;
854 				den = malloc(sizeof (*den) + client_id_len);
855 				if (den == NULL)
856 					goto alloc_failure;
857 				den->den_dutype = htons(duidtype);
858 				DHCPV6_SET_ENTNUM(den, subtype);
859 				if (hexascii_to_octet(value, slen, den + 1,
860 				    &client_id_len) != 0) {
861 					dhcpmsg(MSG_ERROR, "get_smach_cid: "
862 					    "cannot parse hex string in %s",
863 					    value);
864 					free(den);
865 					goto no_specified_id;
866 				}
867 				dsmp->dsm_cid = (uchar_t *)den;
868 				break;
869 			}
870 			default:
871 				slen = strlen(value);
872 				client_id_len = (slen + 1) / 2;
873 				cp = malloc(client_id_len);
874 				if (cp == NULL)
875 					goto alloc_failure;
876 				if (hexascii_to_octet(value, slen, cp,
877 				    &client_id_len) != 0) {
878 					dhcpmsg(MSG_ERROR, "get_smach_cid: "
879 					    "cannot parse hex string in %s",
880 					    value);
881 					free(cp);
882 					goto no_specified_id;
883 				}
884 				dsmp->dsm_cid = (uchar_t *)cp;
885 				break;
886 			}
887 			dsmp->dsm_cidlen = client_id_len;
888 			if (!pif->pif_isv6) {
889 				(void) memmove(dsmp->dsm_cid + 5,
890 				    dsmp->dsm_cid, client_id_len - 5);
891 				dsmp->dsm_cid[0] = 255;
892 				dsmp->dsm_cid[1] = lif->lif_iaid >> 24;
893 				dsmp->dsm_cid[2] = lif->lif_iaid >> 16;
894 				dsmp->dsm_cid[3] = lif->lif_iaid >> 8;
895 				dsmp->dsm_cid[4] = lif->lif_iaid;
896 			}
897 			return (DHCP_IPC_SUCCESS);
898 		}
899 
900 		if (pif->pif_isv6) {
901 			dhcpmsg(MSG_ERROR,
902 			    "get_smach_cid: client ID for %s invalid: %s",
903 			    dsmp->dsm_name, value);
904 		} else if (strncasecmp("0x", value, 2) == 0 &&
905 		    value[2] != '\0') {
906 			/* skip past the 0x and convert the value to binary */
907 			value += 2;
908 			slen = strlen(value);
909 			client_id_len = (slen + 1) / 2;
910 			dsmp->dsm_cid = malloc(client_id_len);
911 			if (dsmp->dsm_cid == NULL)
912 				goto alloc_failure;
913 			if (hexascii_to_octet(value, slen, dsmp->dsm_cid,
914 			    &client_id_len) == 0) {
915 				dsmp->dsm_cidlen = client_id_len;
916 				return (DHCP_IPC_SUCCESS);
917 			}
918 			dhcpmsg(MSG_WARNING, "get_smach_cid: cannot convert "
919 			    "hex value for Client ID on %s", dsmp->dsm_name);
920 		} else {
921 			client_id_len = strlen(value);
922 			dsmp->dsm_cid = malloc(client_id_len);
923 			if (dsmp->dsm_cid == NULL)
924 				goto alloc_failure;
925 			(void) memcpy(dsmp->dsm_cid, value, client_id_len);
926 			return (DHCP_IPC_SUCCESS);
927 		}
928 	}
929 no_specified_id:
930 
931 	/*
932 	 * There was either no user-specified Client ID value, or we were
933 	 * unable to parse it.  We need to determine if a Client ID is required
934 	 * and, if so, generate one.
935 	 *
936 	 * If it's IPv4 and not a logical interface, then we need to preserve
937 	 * backward-compatibility by avoiding new-fangled DUID/IAID
938 	 * construction.
939 	 */
940 	if (!pif->pif_isv6 && strchr(dsmp->dsm_name, ':') == NULL) {
941 		if (pif->pif_hwtype == ARPHRD_IB) {
942 			/*
943 			 * This comes from the DHCP over IPoIB specification.
944 			 * In the absence of an user specified client id, IPoIB
945 			 * automatically uses the required format, with the
946 			 * unique 4 octet value set to 0 (since IPoIB driver
947 			 * allows only a single interface on a port with a
948 			 * specific GID to belong to an IP subnet (PSARC
949 			 * 2001/289, FWARC 2002/702).
950 			 *
951 			 *   Type  Client-Identifier
952 			 * +-----+-----+-----+-----+-----+----....----+
953 			 * |  0  |  0 (4 octets)   |   GID (16 octets)|
954 			 * +-----+-----+-----+-----+-----+----....----+
955 			 */
956 			dsmp->dsm_cidlen = 1 + 4 + 16;
957 			dsmp->dsm_cid = client_id = malloc(dsmp->dsm_cidlen);
958 			if (dsmp->dsm_cid == NULL)
959 				goto alloc_failure;
960 
961 			/*
962 			 * Pick the GID from the mac address. The format
963 			 * of the hardware address is:
964 			 * +-----+-----+-----+-----+----....----+
965 			 * | QPN (4 octets)  |   GID (16 octets)|
966 			 * +-----+-----+-----+-----+----....----+
967 			 */
968 			(void) memcpy(client_id + 5, pif->pif_hwaddr + 4,
969 			    pif->pif_hwlen - 4);
970 			(void) memset(client_id, 0, 5);
971 		}
972 		return (DHCP_IPC_SUCCESS);
973 	}
974 
975 	/*
976 	 * Now check for a saved DUID.  If there is one, then use it.  If there
977 	 * isn't, then generate a new one.  For IPv4, we need to construct the
978 	 * RFC 4361 Client ID with this value and the LIF's IAID.
979 	 */
980 	if (global_duid == NULL &&
981 	    (global_duid = read_stable_duid(&global_duidlen)) == NULL) {
982 		global_duid = make_stable_duid(pif->pif_name, &global_duidlen);
983 		if (global_duid == NULL)
984 			goto alloc_failure;
985 		duid_retry(NULL, NULL);
986 	}
987 
988 	if (pif->pif_isv6) {
989 		dsmp->dsm_cid = malloc(global_duidlen);
990 		if (dsmp->dsm_cid == NULL)
991 			goto alloc_failure;
992 		(void) memcpy(dsmp->dsm_cid, global_duid, global_duidlen);
993 		dsmp->dsm_cidlen = global_duidlen;
994 	} else {
995 		dsmp->dsm_cid = malloc(5 + global_duidlen);
996 		if (dsmp->dsm_cid == NULL)
997 			goto alloc_failure;
998 		dsmp->dsm_cid[0] = 255;
999 		dsmp->dsm_cid[1] = lif->lif_iaid >> 24;
1000 		dsmp->dsm_cid[2] = lif->lif_iaid >> 16;
1001 		dsmp->dsm_cid[3] = lif->lif_iaid >> 8;
1002 		dsmp->dsm_cid[4] = lif->lif_iaid;
1003 		(void) memcpy(dsmp->dsm_cid + 5, global_duid, global_duidlen);
1004 		dsmp->dsm_cidlen = 5 + global_duidlen;
1005 	}
1006 
1007 	return (DHCP_IPC_SUCCESS);
1008 
1009 alloc_failure:
1010 	dhcpmsg(MSG_ERR, "get_smach_cid: cannot allocate Client Id for %s",
1011 	    dsmp->dsm_name);
1012 	return (DHCP_IPC_E_MEMORY);
1013 }
1014 
1015 /*
1016  * smach_count(): returns the number of state machines running
1017  *
1018  *   input: void
1019  *  output: uint_t: the number of state machines
1020  */
1021 
1022 uint_t
1023 smach_count(void)
1024 {
1025 	return (global_smach_count);
1026 }
1027 
1028 /*
1029  * remove_default_routes(): removes a state machine's default routes
1030  *
1031  *   input: dhcp_smach_t *: the state machine whose default routes need to be
1032  *			    removed
1033  *  output: void
1034  */
1035 
1036 void
1037 remove_default_routes(dhcp_smach_t *dsmp)
1038 {
1039 	int idx;
1040 
1041 	if (dsmp->dsm_routers != NULL) {
1042 		for (idx = dsmp->dsm_nrouters - 1; idx >= 0; idx--) {
1043 			if (del_default_route(dsmp->dsm_name,
1044 			    &dsmp->dsm_routers[idx])) {
1045 				dhcpmsg(MSG_DEBUG, "remove_default_routes: "
1046 				    "removed %s from %s",
1047 				    inet_ntoa(dsmp->dsm_routers[idx]),
1048 				    dsmp->dsm_name);
1049 			} else {
1050 				dhcpmsg(MSG_INFO, "remove_default_routes: "
1051 				    "unable to remove %s from %s",
1052 				    inet_ntoa(dsmp->dsm_routers[idx]),
1053 				    dsmp->dsm_name);
1054 			}
1055 		}
1056 		free(dsmp->dsm_routers);
1057 		dsmp->dsm_routers = NULL;
1058 		dsmp->dsm_nrouters = 0;
1059 	}
1060 }
1061 
1062 /*
1063  * reset_smach(): resets a state machine to its initial state
1064  *
1065  *   input: dhcp_smach_t *: the state machine to reset
1066  *  output: void
1067  */
1068 
1069 void
1070 reset_smach(dhcp_smach_t *dsmp)
1071 {
1072 	dsmp->dsm_dflags &= ~DHCP_IF_FAILED;
1073 
1074 	remove_default_routes(dsmp);
1075 
1076 	free_pkt_list(&dsmp->dsm_recv_pkt_list);
1077 
1078 	if (dsmp->dsm_orig_ack != dsmp->dsm_ack)
1079 		free_pkt_entry(dsmp->dsm_orig_ack);
1080 
1081 	free_pkt_entry(dsmp->dsm_ack);
1082 
1083 	dsmp->dsm_ack = dsmp->dsm_orig_ack = NULL;
1084 
1085 	cancel_smach_timers(dsmp);
1086 
1087 	(void) set_smach_state(dsmp, INIT);
1088 	if (dsmp->dsm_isv6) {
1089 		dsmp->dsm_server = ipv6_all_dhcp_relay_and_servers;
1090 	} else {
1091 		IN6_IPADDR_TO_V4MAPPED(htonl(INADDR_BROADCAST),
1092 		    &dsmp->dsm_server);
1093 	}
1094 	dsmp->dsm_neg_hrtime		= gethrtime();
1095 	dsmp->dsm_script_fd		= -1;
1096 	dsmp->dsm_script_pid		= -1;
1097 	dsmp->dsm_script_helper_pid	= -1;
1098 	dsmp->dsm_script_callback	= NULL;
1099 	dsmp->dsm_callback_arg		= NULL;
1100 	dsmp->dsm_script_event_id	= -1;
1101 	free(dsmp->dsm_reqhost);
1102 	dsmp->dsm_reqhost		= NULL;
1103 }
1104 
1105 /*
1106  * refresh_smach(): refreshes a given state machine, as though awakened from
1107  *		    hibernation or by lower layer "link up."
1108  *
1109  *   input: dhcp_smach_t *: state machine to refresh
1110  *  output: void
1111  */
1112 
1113 void
1114 refresh_smach(dhcp_smach_t *dsmp)
1115 {
1116 	if (dsmp->dsm_state == BOUND || dsmp->dsm_state == RENEWING ||
1117 	    dsmp->dsm_state == REBINDING) {
1118 		dhcpmsg(MSG_WARNING, "refreshing lease on %s", dsmp->dsm_name);
1119 		cancel_smach_timers(dsmp);
1120 		dhcp_init_reboot(dsmp);
1121 	}
1122 }
1123 
1124 /*
1125  * refresh_smachs(): refreshes all finite leases under DHCP control
1126  *
1127  *   input: iu_eh_t *: unused
1128  *	    int: unused
1129  *	    void *: unused
1130  *  output: void
1131  */
1132 
1133 /* ARGSUSED */
1134 void
1135 refresh_smachs(iu_eh_t *eh, int sig, void *arg)
1136 {
1137 	boolean_t isv6 = B_FALSE;
1138 	dhcp_smach_t *dsmp;
1139 
1140 	for (;;) {
1141 		for (dsmp = next_smach(NULL, isv6); dsmp != NULL;
1142 		    dsmp = next_smach(dsmp, isv6)) {
1143 			refresh_smach(dsmp);
1144 		}
1145 		if (isv6)
1146 			break;
1147 		isv6 = B_TRUE;
1148 	}
1149 }
1150 
1151 /*
1152  * nuke_smach_list(): delete the state machine list.  For use when the
1153  *		      dhcpagent is exiting.
1154  *
1155  *   input: none
1156  *  output: none
1157  */
1158 
1159 void
1160 nuke_smach_list(void)
1161 {
1162 	boolean_t isv6 = B_FALSE;
1163 	dhcp_smach_t *dsmp, *dsmp_next;
1164 
1165 	for (;;) {
1166 		for (dsmp = next_smach(NULL, isv6); dsmp != NULL;
1167 		    dsmp = dsmp_next) {
1168 			int	status;
1169 			const char *drop = isv6 ? EVENT_DROP6 : EVENT_DROP;
1170 			const char *release = isv6 ? EVENT_RELEASE6 :
1171 			    EVENT_RELEASE;
1172 
1173 			dsmp_next = next_smach(dsmp, isv6);
1174 
1175 			cancel_smach_timers(dsmp);
1176 			if (dsmp->dsm_script_pid != -1) {
1177 				/*
1178 				 * Stop a script if it is not for DROP or
1179 				 * RELEASE
1180 				 */
1181 				if (strcmp(dsmp->dsm_script_event, drop) == 0 ||
1182 				    strcmp(dsmp->dsm_script_event, release) ==
1183 				    0) {
1184 					continue;
1185 				}
1186 				script_stop(dsmp);
1187 			}
1188 
1189 			/*
1190 			 * If the script is started by script_start, dhcp_drop
1191 			 * and dhcp_release should and will only be called
1192 			 * after the script exits.
1193 			 */
1194 			if (df_get_bool(dsmp->dsm_name, isv6,
1195 			    DF_RELEASE_ON_SIGTERM)) {
1196 				if (script_start(dsmp, release, dhcp_release,
1197 				    "DHCP agent is exiting", &status) == 1) {
1198 					continue;
1199 				}
1200 				if (status == 1)
1201 					continue;
1202 			}
1203 			(void) script_start(dsmp, drop, dhcp_drop, NULL, NULL);
1204 		}
1205 		if (isv6)
1206 			break;
1207 		isv6 = B_TRUE;
1208 	}
1209 }
1210 
1211 /*
1212  * insert_lease(): Create a lease structure on a given state machine.  The
1213  *		   lease holds a reference to the state machine.
1214  *
1215  *   input: dhcp_smach_t *: state machine
1216  *  output: dhcp_lease_t *: newly-created lease
1217  */
1218 
1219 dhcp_lease_t *
1220 insert_lease(dhcp_smach_t *dsmp)
1221 {
1222 	dhcp_lease_t *dlp;
1223 
1224 	if ((dlp = calloc(1, sizeof (*dlp))) == NULL)
1225 		return (NULL);
1226 	dlp->dl_smach = dsmp;
1227 	dlp->dl_hold_count = 1;
1228 	init_timer(&dlp->dl_t1, 0);
1229 	init_timer(&dlp->dl_t2, 0);
1230 	insque(dlp, &dsmp->dsm_leases);
1231 	dhcpmsg(MSG_DEBUG2, "insert_lease: new lease for %s", dsmp->dsm_name);
1232 	return (dlp);
1233 }
1234 
1235 /*
1236  * hold_lease(): acquires a hold on a lease
1237  *
1238  *   input: dhcp_lease_t *: the lease to acquire a hold on
1239  *  output: void
1240  */
1241 
1242 void
1243 hold_lease(dhcp_lease_t *dlp)
1244 {
1245 	dlp->dl_hold_count++;
1246 
1247 	dhcpmsg(MSG_DEBUG2, "hold_lease: hold count on lease for %s: %d",
1248 	    dlp->dl_smach->dsm_name, dlp->dl_hold_count);
1249 }
1250 
1251 /*
1252  * release_lease(): releases a hold previously acquired on a lease.
1253  *		    If the hold count reaches 0, the lease is freed.
1254  *
1255  *   input: dhcp_lease_t *: the lease to release the hold on
1256  *  output: void
1257  */
1258 
1259 void
1260 release_lease(dhcp_lease_t *dlp)
1261 {
1262 	if (dlp->dl_hold_count == 0) {
1263 		dhcpmsg(MSG_CRIT, "release_lease: extraneous release");
1264 		return;
1265 	}
1266 
1267 	if (dlp->dl_hold_count == 1 && !dlp->dl_removed) {
1268 		dhcpmsg(MSG_CRIT, "release_lease: missing removal");
1269 		return;
1270 	}
1271 
1272 	if (--dlp->dl_hold_count == 0) {
1273 		dhcpmsg(MSG_DEBUG,
1274 		    "release_lease: freeing lease on state machine %s",
1275 		    dlp->dl_smach->dsm_name);
1276 		free(dlp);
1277 	} else {
1278 		dhcpmsg(MSG_DEBUG2,
1279 		    "release_lease: hold count on lease for %s: %d",
1280 		    dlp->dl_smach->dsm_name, dlp->dl_hold_count);
1281 	}
1282 }
1283 
1284 /*
1285  * remove_lease(): removes a given lease from the state machine and drops the
1286  *		   state machine's hold on the lease.
1287  *
1288  *   input: dhcp_lease_t *: the lease to remove
1289  *  output: void
1290  */
1291 
1292 void
1293 remove_lease(dhcp_lease_t *dlp)
1294 {
1295 	if (dlp->dl_removed) {
1296 		dhcpmsg(MSG_CRIT, "remove_lease: extraneous removal");
1297 	} else {
1298 		dhcp_lif_t *lif, *lifnext;
1299 		uint_t nlifs;
1300 
1301 		dhcpmsg(MSG_DEBUG,
1302 		    "remove_lease: removed lease from state machine %s",
1303 		    dlp->dl_smach->dsm_name);
1304 		dlp->dl_removed = B_TRUE;
1305 		remque(dlp);
1306 
1307 		cancel_lease_timers(dlp);
1308 
1309 		lif = dlp->dl_lifs;
1310 		nlifs = dlp->dl_nlifs;
1311 		for (; nlifs > 0; nlifs--, lif = lifnext) {
1312 			lifnext = lif->lif_next;
1313 			unplumb_lif(lif);
1314 		}
1315 
1316 		release_lease(dlp);
1317 	}
1318 }
1319 
1320 /*
1321  * cancel_lease_timer(): cancels a lease-related timer
1322  *
1323  *   input: dhcp_lease_t *: the lease to operate on
1324  *	    dhcp_timer_t *: the timer to cancel
1325  *  output: void
1326  */
1327 
1328 static void
1329 cancel_lease_timer(dhcp_lease_t *dlp, dhcp_timer_t *dt)
1330 {
1331 	if (dt->dt_id == -1)
1332 		return;
1333 	if (cancel_timer(dt)) {
1334 		release_lease(dlp);
1335 	} else {
1336 		dhcpmsg(MSG_WARNING,
1337 		    "cancel_lease_timer: cannot cancel timer");
1338 	}
1339 }
1340 
1341 /*
1342  * cancel_lease_timers(): cancels an lease's pending timers
1343  *
1344  *   input: dhcp_lease_t *: the lease to operate on
1345  *  output: void
1346  */
1347 
1348 void
1349 cancel_lease_timers(dhcp_lease_t *dlp)
1350 {
1351 	cancel_lease_timer(dlp, &dlp->dl_t1);
1352 	cancel_lease_timer(dlp, &dlp->dl_t2);
1353 }
1354 
1355 /*
1356  * schedule_lease_timer(): schedules a lease-related timer
1357  *
1358  *   input: dhcp_lease_t *: the lease to operate on
1359  *	    dhcp_timer_t *: the timer to schedule
1360  *	    iu_tq_callback_t *: the callback to call upon firing
1361  *  output: boolean_t: B_TRUE if the timer was scheduled successfully
1362  */
1363 
1364 boolean_t
1365 schedule_lease_timer(dhcp_lease_t *dlp, dhcp_timer_t *dt,
1366     iu_tq_callback_t *expire)
1367 {
1368 	/*
1369 	 * If there's a timer running, cancel it and release its lease
1370 	 * reference.
1371 	 */
1372 	if (dt->dt_id != -1) {
1373 		if (!cancel_timer(dt))
1374 			return (B_FALSE);
1375 		release_lease(dlp);
1376 	}
1377 
1378 	if (schedule_timer(dt, expire, dlp)) {
1379 		hold_lease(dlp);
1380 		return (B_TRUE);
1381 	} else {
1382 		dhcpmsg(MSG_WARNING,
1383 		    "schedule_lease_timer: cannot schedule timer");
1384 		return (B_FALSE);
1385 	}
1386 }
1387 
1388 /*
1389  * deprecate_leases(): remove all of the leases from a given state machine
1390  *
1391  *   input: dhcp_smach_t *: the state machine
1392  *  output: none
1393  */
1394 
1395 void
1396 deprecate_leases(dhcp_smach_t *dsmp)
1397 {
1398 	dhcp_lease_t *dlp;
1399 
1400 	/*
1401 	 * note that due to infelicities in the routing code, any default
1402 	 * routes must be removed prior to canonizing or deprecating the LIF.
1403 	 */
1404 
1405 	remove_default_routes(dsmp);
1406 
1407 	while ((dlp = dsmp->dsm_leases) != NULL)
1408 		remove_lease(dlp);
1409 }
1410 
1411 /*
1412  * verify_smach(): if the state machine is in a bound state, then verify the
1413  *		   standing of the configured interfaces.  Abandon those that
1414  *		   the user has modified.  If we end up with no valid leases,
1415  *		   then just terminate the state machine.
1416  *
1417  *   input: dhcp_smach_t *: the state machine
1418  *  output: boolean_t: B_TRUE if the state machine is still valid.
1419  *    note: assumes caller holds a state machine reference; as with most
1420  *	    callback functions.
1421  */
1422 
1423 boolean_t
1424 verify_smach(dhcp_smach_t *dsmp)
1425 {
1426 	dhcp_lease_t *dlp, *dlpn;
1427 
1428 	if (dsmp->dsm_dflags & DHCP_IF_REMOVED) {
1429 		release_smach(dsmp);
1430 		return (B_FALSE);
1431 	}
1432 
1433 	if (!dsmp->dsm_isv6) {
1434 		/*
1435 		 * If this is DHCPv4, then verify the main LIF.
1436 		 */
1437 		if (!verify_lif(dsmp->dsm_lif))
1438 			goto smach_terminate;
1439 	}
1440 
1441 	/*
1442 	 * If we're not in one of the bound states, then there are no LIFs to
1443 	 * verify here.
1444 	 */
1445 	if (dsmp->dsm_state != BOUND &&
1446 	    dsmp->dsm_state != RENEWING &&
1447 	    dsmp->dsm_state != REBINDING) {
1448 		release_smach(dsmp);
1449 		return (B_TRUE);
1450 	}
1451 
1452 	for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlpn) {
1453 		dhcp_lif_t *lif, *lifnext;
1454 		uint_t nlifs;
1455 
1456 		dlpn = dlp->dl_next;
1457 		lif = dlp->dl_lifs;
1458 		nlifs = dlp->dl_nlifs;
1459 		for (; nlifs > 0; lif = lifnext, nlifs--) {
1460 			lifnext = lif->lif_next;
1461 			if (!verify_lif(lif)) {
1462 				/*
1463 				 * User has manipulated the interface.  Even
1464 				 * if we plumbed it, we must now disown it.
1465 				 */
1466 				lif->lif_plumbed = B_FALSE;
1467 				remove_lif(lif);
1468 			}
1469 		}
1470 		if (dlp->dl_nlifs == 0)
1471 			remove_lease(dlp);
1472 	}
1473 
1474 	/*
1475 	 * If there are leases left, then everything's ok.
1476 	 */
1477 	if (dsmp->dsm_leases != NULL) {
1478 		release_smach(dsmp);
1479 		return (B_TRUE);
1480 	}
1481 
1482 smach_terminate:
1483 	finished_smach(dsmp, DHCP_IPC_E_UNKIF);
1484 	release_smach(dsmp);
1485 
1486 	return (B_FALSE);
1487 }
1488