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