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