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