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