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