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