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