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