Home | History | Annotate | Download | only in dhcpagent
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  *
     25  * This module contains core functions for managing DHCP state machine
     26  * instances.
     27  */
     28 
     29 #include <assert.h>
     30 #include <stdlib.h>
     31 #include <search.h>
     32 #include <string.h>
     33 #include <ctype.h>
     34 #include <sys/types.h>
     35 #include <sys/socket.h>
     36 #include <netinet/in.h>
     37 #include <netinet/arp.h>
     38 #include <arpa/inet.h>
     39 #include <dhcpmsg.h>
     40 #include <dhcpagent_util.h>
     41 #include <dhcp_stable.h>
     42 #include <dhcp_inittab.h>
     43 
     44 #include "agent.h"
     45 #include "states.h"
     46 #include "interface.h"
     47 #include "defaults.h"
     48 #include "script_handler.h"
     49 
     50 static uint_t global_smach_count;
     51 
     52 static uchar_t *global_duid;
     53 static size_t global_duidlen;
     54 
     55 /*
     56  * iaid_retry(): attempt to write LIF IAID again
     57  *
     58  *   input: iu_tq_t *: ignored
     59  *	    void *: pointer to LIF
     60  *  output: none
     61  */
     62 
     63 /* ARGSUSED */
     64 static void
     65 iaid_retry(iu_tq_t *tqp, void *arg)
     66 {
     67 	dhcp_lif_t *lif = arg;
     68 
     69 	if (write_stable_iaid(lif->lif_name, lif->lif_iaid) == -1) {
     70 		if (errno != EROFS) {
     71 			dhcpmsg(MSG_ERR,
     72 			    "iaid_retry: unable to write out IAID for %s",
     73 			    lif->lif_name);
     74 			release_lif(lif);
     75 		} else {
     76 			lif->lif_iaid_id = iu_schedule_timer(tq, 60,
     77 			    iaid_retry, lif);
     78 		}
     79 	} else {
     80 		release_lif(lif);
     81 	}
     82 }
     83 
     84 /*
     85  * parse_param_list(): parse a parameter list.
     86  *
     87  *   input: const char *: parameter list string with comma-separated entries
     88  *	    uint_t *: return parameter; number of entries decoded
     89  *	    const char *: name of parameter list for logging purposes
     90  *	    dhcp_smach_t *: smach pointer for logging
     91  *  output: uint16_t *: allocated array of parameters, or NULL if none.
     92  */
     93 
     94 static uint16_t *
     95 parse_param_list(const char *param_list, uint_t *param_cnt,
     96     const char *param_name, dhcp_smach_t *dsmp)
     97 {
     98 	int i, maxparam;
     99 	char tsym[DSYM_MAX_SYM_LEN + 1];
    100 	uint16_t *params;
    101 	const char *cp;
    102 	dhcp_symbol_t *entry;
    103 
    104 	*param_cnt = 0;
    105 
    106 	if (param_list == NULL)
    107 		return (NULL);
    108 
    109 	for (maxparam = 1, i = 0; param_list[i] != '\0'; i++) {
    110 		if (param_list[i] == ',')
    111 			maxparam++;
    112 	}
    113 
    114 	params = malloc(maxparam * sizeof (*params));
    115 	if (params == NULL) {
    116 		dhcpmsg(MSG_WARNING,
    117 		    "cannot allocate parameter %s list for %s (continuing)",
    118 		    param_name, dsmp->dsm_name);
    119 		return (NULL);
    120 	}
    121 
    122 	for (i = 0; i < maxparam; ) {
    123 
    124 		if (isspace(*param_list))
    125 			param_list++;
    126 
    127 		/* extract the next element on the list */
    128 		cp = strchr(param_list, ',');
    129 		if (cp == NULL || cp - param_list >= sizeof (tsym))
    130 			(void) strlcpy(tsym, param_list, sizeof (tsym));
    131 		else
    132 			(void) strlcpy(tsym, param_list, cp - param_list + 1);
    133 
    134 		/* LINTED -- do nothing with blanks on purpose */
    135 		if (tsym[0] == '\0') {
    136 			;
    137 		} else if (isalpha(tsym[0])) {
    138 			entry = inittab_getbyname(ITAB_CAT_SITE |
    139 			    ITAB_CAT_STANDARD |
    140 			    (dsmp->dsm_isv6 ? ITAB_CAT_V6 : 0),
    141 			    ITAB_CONS_INFO, tsym);
    142 			if (entry == NULL) {
    143 				dhcpmsg(MSG_INFO, "ignored unknown %s list "
    144 				    "entry '%s' for %s", param_name, tsym,
    145 				    dsmp->dsm_name);
    146 			} else {
    147 				params[i++] = entry->ds_code;
    148 				free(entry);
    149 			}
    150 		} else {
    151 			params[i++] = strtoul(tsym, NULL, 0);
    152 		}
    153 		if (cp == NULL)
    154 			break;
    155 		param_list = cp + 1;
    156 	}
    157 
    158 	*param_cnt = i;
    159 	return (params);
    160 }
    161 
    162 /*
    163  * insert_smach(): Create a state machine instance on a given logical
    164  *		   interface.  The state machine holds the caller's LIF
    165  *		   reference on success, and frees it on failure.
    166  *
    167  *   input: dhcp_lif_t *: logical interface name
    168  *	    int *: set to DHCP_IPC_E_* if creation fails
    169  *  output: dhcp_smach_t *: state machine instance
    170  */
    171 
    172 dhcp_smach_t *
    173 insert_smach(dhcp_lif_t *lif, int *error)
    174 {
    175 	dhcp_smach_t *dsmp, *alt_primary;
    176 	boolean_t isv6;
    177 	const char *plist;
    178 
    179 	if ((dsmp = calloc(1, sizeof (*dsmp))) == NULL) {
    180 		dhcpmsg(MSG_ERR, "cannot allocate state machine entry for %s",
    181 		    lif->lif_name);
    182 		remove_lif(lif);
    183 		release_lif(lif);
    184 		*error = DHCP_IPC_E_MEMORY;
    185 		return (NULL);
    186 	}
    187 	dsmp->dsm_name = lif->lif_name;
    188 	dsmp->dsm_lif = lif;
    189 	dsmp->dsm_hold_count = 1;
    190 	dsmp->dsm_state = INIT;
    191 	dsmp->dsm_dflags = DHCP_IF_REMOVED;	/* until added to list */
    192 	isv6 = lif->lif_pif->pif_isv6;
    193 
    194 	/*
    195 	 * Now that we have a controlling LIF, we need to assign an IAID to
    196 	 * that LIF.
    197 	 */
    198 	if (lif->lif_iaid == 0 &&
    199 	    (lif->lif_iaid = read_stable_iaid(lif->lif_name)) == 0) {
    200 		static uint32_t iaidctr = 0x80000000u;
    201 
    202 		/*
    203 		 * If this is a logical interface, then use an arbitrary seed
    204 		 * value.  Otherwise, use the ifIndex.
    205 		 */
    206 		lif->lif_iaid = make_stable_iaid(lif->lif_name,
    207 		    strchr(lif->lif_name, ':') != NULL ? iaidctr++ :
    208 		    lif->lif_pif->pif_index);
    209 		dhcpmsg(MSG_INFO,
    210 		    "insert_smach: manufactured IAID %u for v%d %s",
    211 		    lif->lif_iaid, isv6 ? 6 : 4, lif->lif_name);
    212 		hold_lif(lif);
    213 		iaid_retry(NULL, lif);
    214 	}
    215 
    216 	if (isv6) {
    217 		dsmp->dsm_dflags |= DHCP_IF_V6;
    218 		dsmp->dsm_server = ipv6_all_dhcp_relay_and_servers;
    219 
    220 		/*
    221 		 * With DHCPv6, we do all of our I/O using the common
    222 		 * v6_sock_fd.  There's no need for per-interface file
    223 		 * descriptors because we have IPV6_PKTINFO.
    224 		 */
    225 	} else {
    226 		IN6_IPADDR_TO_V4MAPPED(htonl(INADDR_BROADCAST),
    227 		    &dsmp->dsm_server);
    228 
    229 		/*
    230 		 * With IPv4 DHCP, we use a socket per lif.
    231 		 */
    232 		if (!open_ip_lif(lif, INADDR_ANY, B_TRUE)) {
    233 			dhcpmsg(MSG_ERR, "unable to open socket for %s",
    234 			    lif->lif_name);
    235 			/* This will also dispose of the LIF */
    236 			release_smach(dsmp);
    237 			*error = DHCP_IPC_E_SOCKET;
    238 			return (NULL);
    239 		}
    240 	}
    241 
    242 	script_init(dsmp);
    243 	ipc_action_init(&dsmp->dsm_ia);
    244 
    245 	dsmp->dsm_neg_hrtime = gethrtime();
    246 	dsmp->dsm_offer_timer = -1;
    247 	dsmp->dsm_start_timer = -1;
    248 	dsmp->dsm_retrans_timer = -1;
    249 
    250 	/*
    251 	 * Initialize the parameter request and ignore lists, if any.
    252 	 */
    253 	plist = df_get_string(dsmp->dsm_name, isv6, DF_PARAM_REQUEST_LIST);
    254 	dsmp->dsm_prl = parse_param_list(plist, &dsmp->dsm_prllen, "request",
    255 	    dsmp);
    256 	plist = df_get_string(dsmp->dsm_name, isv6, DF_PARAM_IGNORE_LIST);
    257 	dsmp->dsm_pil = parse_param_list(plist, &dsmp->dsm_pillen, "ignore",
    258 	    dsmp);
    259 
    260 	dsmp->dsm_offer_wait = df_get_int(dsmp->dsm_name, isv6,
    261 	    DF_OFFER_WAIT);
    262 
    263 	/*
    264 	 * If there is no primary of this type, and there is one of the other,
    265 	 * then make this one primary if it's on the same named PIF.
    266 	 */
    267 	if (primary_smach(isv6) == NULL &&
    268 	    (alt_primary = primary_smach(!isv6)) != NULL) {
    269 		if (strcmp(lif->lif_pif->pif_name,
    270 		    alt_primary->dsm_lif->lif_pif->pif_name) == 0) {
    271 			dhcpmsg(MSG_DEBUG,
    272 			    "insert_smach: making %s primary for v%d",
    273 			    dsmp->dsm_name, isv6 ? 6 : 4);
    274 			dsmp->dsm_dflags |= DHCP_IF_PRIMARY;
    275 		}
    276 	}
    277 
    278 	/*
    279 	 * We now have at least one state machine running, so cancel any
    280 	 * running inactivity timer.
    281 	 */
    282 	if (inactivity_id != -1 &&
    283 	    iu_cancel_timer(tq, inactivity_id, NULL) == 1)
    284 		inactivity_id = -1;
    285 
    286 	dsmp->dsm_dflags &= ~DHCP_IF_REMOVED;
    287 	insque(dsmp, &lif->lif_smachs);
    288 	global_smach_count++;
    289 	dhcpmsg(MSG_DEBUG2, "insert_smach: inserted %s", dsmp->dsm_name);
    290 
    291 	return (dsmp);
    292 }
    293 
    294 /*
    295  * hold_smach(): acquires a hold on a state machine
    296  *
    297  *   input: dhcp_smach_t *: the state machine to acquire a hold on
    298  *  output: void
    299  */
    300 
    301 void
    302 hold_smach(dhcp_smach_t *dsmp)
    303 {
    304 	dsmp->dsm_hold_count++;
    305 
    306 	dhcpmsg(MSG_DEBUG2, "hold_smach: hold count on %s: %d",
    307 	    dsmp->dsm_name, dsmp->dsm_hold_count);
    308 }
    309 
    310 /*
    311  * free_smach(): frees the memory occupied by a state machine
    312  *
    313  *   input: dhcp_smach_t *: the DHCP state machine to free
    314  *  output: void
    315  */
    316 
    317 static void
    318 free_smach(dhcp_smach_t *dsmp)
    319 {
    320 	dhcpmsg(MSG_DEBUG, "free_smach: freeing state machine %s",
    321 	    dsmp->dsm_name);
    322 
    323 	deprecate_leases(dsmp);
    324 	remove_lif(dsmp->dsm_lif);
    325 	release_lif(dsmp->dsm_lif);
    326 	free_pkt_list(&dsmp->dsm_recv_pkt_list);
    327 	if (dsmp->dsm_ack != dsmp->dsm_orig_ack)
    328 		free_pkt_entry(dsmp->dsm_orig_ack);
    329 	free_pkt_entry(dsmp->dsm_ack);
    330 	free(dsmp->dsm_send_pkt.pkt);
    331 	free(dsmp->dsm_cid);
    332 	free(dsmp->dsm_prl);
    333 	free(dsmp->dsm_pil);
    334 	free(dsmp->dsm_routers);
    335 	free(dsmp->dsm_reqhost);
    336 	free(dsmp);
    337 
    338 	/* no big deal if this fails */
    339 	if (global_smach_count == 0 && inactivity_id == -1) {
    340 		inactivity_id = iu_schedule_timer(tq, DHCP_INACTIVITY_WAIT,
    341 		    inactivity_shutdown, NULL);
    342 	}
    343 }
    344 
    345 /*
    346  * release_smach(): releases a hold previously acquired on a state machine.
    347  *		    If the hold count reaches 0, the state machine is freed.
    348  *
    349  *   input: dhcp_smach_t *: the state machine entry to release the hold on
    350  *  output: void
    351  */
    352 
    353 void
    354 release_smach(dhcp_smach_t *dsmp)
    355 {
    356 	if (dsmp->dsm_hold_count == 0) {
    357 		dhcpmsg(MSG_CRIT, "release_smach: extraneous release");
    358 		return;
    359 	}
    360 
    361 	if (dsmp->dsm_hold_count == 1 &&
    362 	    !(dsmp->dsm_dflags & DHCP_IF_REMOVED)) {
    363 		dhcpmsg(MSG_CRIT, "release_smach: missing removal");
    364 		return;
    365 	}
    366 
    367 	if (--dsmp->dsm_hold_count == 0) {
    368 		free_smach(dsmp);
    369 	} else {
    370 		dhcpmsg(MSG_DEBUG2, "release_smach: hold count on %s: %d",
    371 		    dsmp->dsm_name, dsmp->dsm_hold_count);
    372 	}
    373 }
    374 
    375 /*
    376  * next_smach(): state machine iterator function
    377  *
    378  *   input: dhcp_smach_t *: current state machine (or NULL for list start)
    379  *          boolean_t: B_TRUE if DHCPv6, B_FALSE otherwise
    380  *  output: dhcp_smach_t *: next state machine in list
    381  */
    382 
    383 dhcp_smach_t *
    384 next_smach(dhcp_smach_t *dsmp, boolean_t isv6)
    385 {
    386 	dhcp_lif_t *lif;
    387 	dhcp_pif_t *pif;
    388 
    389 	if (dsmp != NULL) {
    390 		if (dsmp->dsm_next != NULL)
    391 			return (dsmp->dsm_next);
    392 
    393 		if ((lif = dsmp->dsm_lif) != NULL)
    394 			lif = lif->lif_next;
    395 		for (; lif != NULL; lif = lif->lif_next) {
    396 			if (lif->lif_smachs != NULL)
    397 				return (lif->lif_smachs);
    398 		}
    399 
    400 		if ((pif = dsmp->dsm_lif->lif_pif) != NULL)
    401 			pif = pif->pif_next;
    402 	} else {
    403 		pif = isv6 ? v6root : v4root;
    404 	}
    405 	for (; pif != NULL; pif = pif->pif_next) {
    406 		for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) {
    407 			if (lif->lif_smachs != NULL)
    408 				return (lif->lif_smachs);
    409 		}
    410 	}
    411 	return (NULL);
    412 }
    413 
    414 /*
    415  * primary_smach(): loop through all state machines of the given type (v4 or
    416  *		    v6) in the system, and locate the one that's primary.
    417  *
    418  *   input: boolean_t: B_TRUE for IPv6
    419  *  output: dhcp_smach_t *: the primary state machine
    420  */
    421 
    422 dhcp_smach_t *
    423 primary_smach(boolean_t isv6)
    424 {
    425 	dhcp_smach_t *dsmp;
    426 
    427 	for (dsmp = next_smach(NULL, isv6); dsmp != NULL;
    428 	    dsmp = next_smach(dsmp, isv6)) {
    429 		if (dsmp->dsm_dflags & DHCP_IF_PRIMARY)
    430 			break;
    431 	}
    432 	return (dsmp);
    433 }
    434 
    435 /*
    436  * info_primary_smach(): loop through all state machines of the given type (v4
    437  *			 or v6) in the system, and locate the one that should
    438  *			 be considered "primary" for dhcpinfo.
    439  *
    440  *   input: boolean_t: B_TRUE for IPv6
    441  *  output: dhcp_smach_t *: the dhcpinfo primary state machine
    442  */
    443 
    444 dhcp_smach_t *
    445 info_primary_smach(boolean_t isv6)
    446 {
    447 	dhcp_smach_t *bestdsm = NULL;
    448 	dhcp_smach_t *dsmp;
    449 
    450 	for (dsmp = next_smach(NULL, isv6); dsmp != NULL;
    451 	    dsmp = next_smach(dsmp, isv6)) {
    452 		/*
    453 		 * If there is a primary, then something previously went wrong
    454 		 * with verification, because the caller uses primary_smach()
    455 		 * before calling this routine.  There's nothing else we can do
    456 		 * but return failure, as the designated primary must be bad.
    457 		 */
    458 		if (dsmp->dsm_dflags & DHCP_IF_PRIMARY)
    459 			return (NULL);
    460 
    461 		/* If we have no information, then we're not primary. */
    462 		if (dsmp->dsm_ack == NULL)
    463 			continue;
    464 
    465 		/*
    466 		 * Among those interfaces that have DHCP information, the
    467 		 * "primary" is the one that sorts lexically first.
    468 		 */
    469 		if (bestdsm == NULL ||
    470 		    strcmp(dsmp->dsm_name, bestdsm->dsm_name) < 0)
    471 			bestdsm = dsmp;
    472 	}
    473 	return (bestdsm);
    474 }
    475 
    476 /*
    477  * make_primary(): designate a given state machine as being the primary
    478  *		   instance on the primary interface.  Note that the user often
    479  *		   thinks in terms of a primary "interface" (rather than just
    480  *		   an instance), so we go to lengths here to keep v4 and v6 in
    481  *		   sync.
    482  *
    483  *   input: dhcp_smach_t *: the primary state machine
    484  *  output: none
    485  */
    486 
    487 void
    488 make_primary(dhcp_smach_t *dsmp)
    489 {
    490 	dhcp_smach_t *old_primary, *alt_primary;
    491 	dhcp_pif_t *pif;
    492 
    493 	if ((old_primary = primary_smach(dsmp->dsm_isv6)) != NULL)
    494 		old_primary->dsm_dflags &= ~DHCP_IF_PRIMARY;
    495 	dsmp->dsm_dflags |= DHCP_IF_PRIMARY;
    496 
    497 	/*
    498 	 * Find the primary for the other protocol.
    499 	 */
    500 	alt_primary = primary_smach(!dsmp->dsm_isv6);
    501 
    502 	/*
    503 	 * If it's on a different interface, then cancel that.  If it's on the
    504 	 * same interface, then we're done.
    505 	 */
    506 	if (alt_primary != NULL) {
    507 		if (strcmp(alt_primary->dsm_lif->lif_pif->pif_name,
    508 		    dsmp->dsm_lif->lif_pif->pif_name) == 0)
    509 			return;
    510 		alt_primary->dsm_dflags &= ~DHCP_IF_PRIMARY;
    511 	}
    512 
    513 	/*
    514 	 * We need a new primary for the other protocol.  If the PIF exists,
    515 	 * there must be at least one state machine.  Just choose the first for
    516 	 * consistency with insert_smach().
    517 	 */
    518 	if ((pif = lookup_pif_by_name(dsmp->dsm_lif->lif_pif->pif_name,
    519 	    !dsmp->dsm_isv6)) != NULL) {
    520 		pif->pif_lifs->lif_smachs->dsm_dflags |= DHCP_IF_PRIMARY;
    521 	}
    522 }
    523 
    524 /*
    525  * lookup_smach(): finds a state machine by name and type; used for dispatching
    526  *		   user commands.
    527  *
    528  *   input: const char *: the name of the state machine
    529  *          boolean_t: B_TRUE if DHCPv6, B_FALSE otherwise
    530  *  output: dhcp_smach_t *: the state machine found
    531  */
    532 
    533 dhcp_smach_t *
    534 lookup_smach(const char *smname, boolean_t isv6)
    535 {
    536 	dhcp_smach_t *dsmp;
    537 
    538 	for (dsmp = next_smach(NULL, isv6); dsmp != NULL;
    539 	    dsmp = next_smach(dsmp, isv6)) {
    540 		if (strcmp(dsmp->dsm_name, smname) == 0)
    541 			break;
    542 	}
    543 	return (dsmp);
    544 }
    545 
    546 /*
    547  * lookup_smach_by_uindex(): iterate through running state machines by
    548  *			     truncated interface index.
    549  *
    550  *   input: uint16_t: the interface index (truncated)
    551  *	    dhcp_smach_t *: the previous state machine, or NULL for start
    552  *	    boolean_t: B_TRUE for DHCPv6, B_FALSE for IPv4 DHCP
    553  *  output: dhcp_smach_t *: next state machine, or NULL at end of list
    554  */
    555 
    556 dhcp_smach_t *
    557 lookup_smach_by_uindex(uint16_t ifindex, dhcp_smach_t *dsmp, boolean_t isv6)
    558 {
    559 	dhcp_pif_t *pif;
    560 	dhcp_lif_t *lif;
    561 
    562 	/*
    563 	 * If the user gives us a state machine, then check that the next one
    564 	 * available is on the same physical interface.  If so, then go ahead
    565 	 * and return that.
    566 	 */
    567 	if (dsmp != NULL) {
    568 		pif = dsmp->dsm_lif->lif_pif;
    569 		if ((dsmp = next_smach(dsmp, isv6)) == NULL)
    570 			return (NULL);
    571 		if (pif == dsmp->dsm_lif->lif_pif)
    572 			return (dsmp);
    573 	} else {
    574 		/* Otherwise, start at the beginning of the list */
    575 		pif = NULL;
    576 	}
    577 
    578 	/*
    579 	 * Find the next physical interface with the same truncated interface
    580 	 * index, and return the first state machine on that.  If there are no
    581 	 * more physical interfaces that match, then we're done.
    582 	 */
    583 	do {
    584 		pif = lookup_pif_by_uindex(ifindex, pif, isv6);
    585 		if (pif == NULL)
    586 			return (NULL);
    587 		for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) {
    588 			if ((dsmp = lif->lif_smachs) != NULL)
    589 				break;
    590 		}
    591 	} while (dsmp == NULL);
    592 	return (dsmp);
    593 }
    594 
    595 /*
    596  * lookup_smach_by_xid(): iterate through running state machines by transaction
    597  *			  id.  Transaction ID zero means "all state machines."
    598  *
    599  *   input: uint32_t: the transaction id to look up
    600  *	    dhcp_smach_t *: the previous state machine, or NULL for start
    601  *	    boolean_t: B_TRUE if DHCPv6, B_FALSE otherwise
    602  *  output: dhcp_smach_t *: next state machine, or NULL at end of list
    603  */
    604 
    605 dhcp_smach_t *
    606 lookup_smach_by_xid(uint32_t xid, dhcp_smach_t *dsmp, boolean_t isv6)
    607 {
    608 	for (dsmp = next_smach(dsmp, isv6); dsmp != NULL;
    609 	    dsmp = next_smach(dsmp, isv6)) {
    610 		if (xid == 0 ||
    611 		    pkt_get_xid(dsmp->dsm_send_pkt.pkt, isv6) == xid)
    612 			break;
    613 	}
    614 
    615 	return (dsmp);
    616 }
    617 
    618 /*
    619  * lookup_smach_by_event(): find a state machine busy with a particular event
    620  *			    ID.  This is used only for error handling.
    621  *
    622  *   input: iu_event_id_t: the event id to look up
    623  *  output: dhcp_smach_t *: matching state machine, or NULL if none
    624  */
    625 
    626 dhcp_smach_t *
    627 lookup_smach_by_event(iu_event_id_t eid)
    628 {
    629 	dhcp_smach_t *dsmp;
    630 	boolean_t isv6 = B_FALSE;
    631 
    632 	for (;;) {
    633 		for (dsmp = next_smach(NULL, isv6); dsmp != NULL;
    634 		    dsmp = next_smach(dsmp, isv6)) {
    635 			if ((dsmp->dsm_dflags & DHCP_IF_BUSY) &&
    636 			    eid == dsmp->dsm_ia.ia_eid)
    637 				return (dsmp);
    638 		}
    639 		if (isv6)
    640 			break;
    641 		isv6 = B_TRUE;
    642 	}
    643 
    644 	return (dsmp);
    645 }
    646 
    647 /*
    648  * cancel_offer_timer(): stop the offer polling timer on a given state machine
    649  *
    650  *   input: dhcp_smach_t *: state machine on which to stop polling for offers
    651  *  output: none
    652  */
    653 
    654 void
    655 cancel_offer_timer(dhcp_smach_t *dsmp)
    656 {
    657 	int retval;
    658 
    659 	if (dsmp->dsm_offer_timer != -1) {
    660 		retval = iu_cancel_timer(tq, dsmp->dsm_offer_timer, NULL);
    661 		dsmp->dsm_offer_timer = -1;
    662 		if (retval == 1)
    663 			release_smach(dsmp);
    664 	}
    665 }
    666 
    667 /*
    668  * cancel_smach_timers(): stop all of the timers related to a given state
    669  *			  machine, including lease and LIF expiry.
    670  *
    671  *   input: dhcp_smach_t *: state machine to cancel
    672  *  output: none
    673  *    note: this function assumes that the iu timer functions are synchronous
    674  *	    and thus don't require any protection or ordering on cancellation.
    675  */
    676 
    677 void
    678 cancel_smach_timers(dhcp_smach_t *dsmp)
    679 {
    680 	dhcp_lease_t *dlp;
    681 	dhcp_lif_t *lif;
    682 	uint_t nlifs;
    683 
    684 	for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
    685 		cancel_lease_timers(dlp);
    686 		lif = dlp->dl_lifs;
    687 		nlifs = dlp->dl_nlifs;
    688 		for (; nlifs > 0; nlifs--, lif = lif->lif_next)
    689 			cancel_lif_timers(lif);
    690 	}
    691 
    692 	cancel_offer_timer(dsmp);
    693 	stop_pkt_retransmission(dsmp);
    694 	if (dsmp->dsm_start_timer != -1) {
    695 		(void) iu_cancel_timer(tq, dsmp->dsm_start_timer, NULL);
    696 		dsmp->dsm_start_timer = -1;
    697 		release_smach(dsmp);
    698 	}
    699 }
    700 
    701 /*
    702  * remove_smach(): removes a given state machine from the system.  marks it
    703  *		   for being freed (but may not actually free it).
    704  *
    705  *   input: dhcp_smach_t *: the state machine to remove
    706  *  output: void
    707  */
    708 
    709 void
    710 remove_smach(dhcp_smach_t *dsmp)
    711 {
    712 	if (dsmp->dsm_dflags & DHCP_IF_REMOVED)
    713 		return;
    714 
    715 	dhcpmsg(MSG_DEBUG2, "remove_smach: removing %s", dsmp->dsm_name);
    716 	dsmp->dsm_dflags |= DHCP_IF_REMOVED;
    717 	remque(dsmp);
    718 	global_smach_count--;
    719 
    720 	/*
    721 	 * if we have long term timers, cancel them so that state machine
    722 	 * resources can be reclaimed in a reasonable amount of time.
    723 	 */
    724 	cancel_smach_timers(dsmp);
    725 
    726 	/* Drop the hold that the LIF's state machine list had on us */
    727 	release_smach(dsmp);
    728 }
    729 
    730 /*
    731  * finished_smach(): we're finished with a given state machine; remove it from
    732  *		     the system and tell the user (who may have initiated the
    733  *		     removal process).  Note that we remove it from the system
    734  *		     first to allow back-to-back drop and create invocations.
    735  *
    736  *   input: dhcp_smach_t *: the state machine to remove
    737  *	    int: error for IPC
    738  *  output: void
    739  */
    740 
    741 void
    742 finished_smach(dhcp_smach_t *dsmp, int error)
    743 {
    744 	hold_smach(dsmp);
    745 	remove_smach(dsmp);
    746 	if (dsmp->dsm_ia.ia_fd != -1)
    747 		ipc_action_finish(dsmp, error);
    748 	else
    749 		(void) async_cancel(dsmp);
    750 	release_smach(dsmp);
    751 }
    752 
    753 /*
    754  * is_bound_state(): checks if a state indicates the client is bound
    755  *
    756  *   input: DHCPSTATE: the state to check
    757  *  output: boolean_t: B_TRUE if the state is bound, B_FALSE if not
    758  */
    759 
    760 boolean_t
    761 is_bound_state(DHCPSTATE state)
    762 {
    763 	return (state == BOUND || state == REBINDING || state == INFORMATION ||
    764 	    state == RELEASING || state == INFORM_SENT || state == RENEWING);
    765 }
    766 
    767 /*
    768  * set_smach_state(): changes state and updates I/O
    769  *
    770  *   input: dhcp_smach_t *: the state machine to change
    771  *	    DHCPSTATE: the new state
    772  *  output: boolean_t: B_TRUE on success, B_FALSE on failure
    773  */
    774 
    775 boolean_t
    776 set_smach_state(dhcp_smach_t *dsmp, DHCPSTATE state)
    777 {
    778 	dhcp_lif_t *lif = dsmp->dsm_lif;
    779 
    780 	if (dsmp->dsm_state != state) {
    781 		dhcpmsg(MSG_DEBUG,
    782 		    "set_smach_state: changing from %s to %s on %s",
    783 		    dhcp_state_to_string(dsmp->dsm_state),
    784 		    dhcp_state_to_string(state), dsmp->dsm_name);
    785 
    786 		/*
    787 		 * For IPv4, when we're in a bound state our socket must be
    788 		 * bound to our address.  Otherwise, our socket must be bound
    789 		 * to INADDR_ANY.  For IPv6, no such change is necessary.
    790 		 */
    791 		if (!dsmp->dsm_isv6) {
    792 			if (is_bound_state(dsmp->dsm_state)) {
    793 				if (!is_bound_state(state)) {
    794 					close_ip_lif(lif);
    795 					if (!open_ip_lif(lif, INADDR_ANY,
    796 					    B_FALSE))
    797 						return (B_FALSE);
    798 				}
    799 			} else {
    800 				if (is_bound_state(state)) {
    801 					close_ip_lif(lif);
    802 					if (!open_ip_lif(lif,
    803 					    ntohl(lif->lif_addr), B_FALSE))
    804 						return (B_FALSE);
    805 				}
    806 			}
    807 		}
    808 
    809 		dsmp->dsm_state = state;
    810 	}
    811 	return (B_TRUE);
    812 }
    813 
    814 /*
    815  * duid_retry(): attempt to write DUID again
    816  *
    817  *   input: iu_tq_t *: ignored
    818  *	    void *: ignored
    819  *  output: none
    820  */
    821 
    822 /* ARGSUSED */
    823 static void
    824 duid_retry(iu_tq_t *tqp, void *arg)
    825 {
    826 	if (write_stable_duid(global_duid, global_duidlen) == -1) {
    827 		if (errno != EROFS) {
    828 			dhcpmsg(MSG_ERR,
    829 			    "duid_retry: unable to write out DUID");
    830 		} else {
    831 			(void) iu_schedule_timer(tq, 60, duid_retry, NULL);
    832 		}
    833 	}
    834 }
    835 
    836 /*
    837  * get_smach_cid(): gets the client ID for a given state machine.
    838  *
    839  *   input: dhcp_smach_t *: the state machine to set up
    840  *  output: int: DHCP_IPC_SUCCESS or one of DHCP_IPC_E_* on failure.
    841  */
    842 
    843 int
    844 get_smach_cid(dhcp_smach_t *dsmp)
    845 {
    846 	uchar_t *client_id;
    847 	uint_t client_id_len;
    848 	dhcp_lif_t *lif = dsmp->dsm_lif;
    849 	dhcp_pif_t *pif = lif->lif_pif;
    850 	const char *value;
    851 	size_t slen;
    852 
    853 	/*
    854 	 * Look in defaults file for the client-id.  If present, this takes
    855 	 * precedence over all other forms of ID.
    856 	 */
    857 
    858 	dhcpmsg(MSG_DEBUG, "get_smach_cid: getting default client-id "
    859 	    "property on %s", dsmp->dsm_name);
    860 	value = df_get_string(dsmp->dsm_name, pif->pif_isv6, DF_CLIENT_ID);
    861 	if (value != NULL) {
    862 		/*
    863 		 * The Client ID string can have one of three basic forms:
    864 		 *	<decimal>,<data...>
    865 		 *	0x<hex...>
    866 		 *	<string...>
    867 		 *
    868 		 * The first form is an RFC 3315 DUID.  This is legal for both
    869 		 * IPv4 DHCP and DHCPv6.  For IPv4, an RFC 4361 Client ID is
    870 		 * constructed from this value.
    871 		 *
    872 		 * The second and third forms are legal for IPv4 only.  This is
    873 		 * a raw Client ID, in hex or ASCII string format.
    874 		 */
    875 
    876 		if (isdigit(*value) &&
    877 		    value[strspn(value, "0123456789")] == ',') {
    878 			char *cp;
    879 			ulong_t duidtype;
    880 			ulong_t subtype;
    881 
    882 			errno = 0;
    883 			duidtype = strtoul(value, &cp, 0);
    884 			if (value == cp || errno != 0 || *cp != ',' ||
    885 			    duidtype > 65535) {
    886 				dhcpmsg(MSG_ERR, "get_smach_cid: cannot parse "
    887 				    "DUID type in %s", value);
    888 				goto no_specified_id;
    889 			}
    890 			value = cp + 1;
    891 			switch (duidtype) {
    892 			case DHCPV6_DUID_LL:
    893 			case DHCPV6_DUID_LLT: {
    894 				int num;
    895 				char chr;
    896 
    897 				errno = 0;
    898 				subtype = strtoul(value, &cp, 0);
    899 				if (value == cp || errno != 0 || *cp != ',' ||
    900 				    subtype > 65535) {
    901 					dhcpmsg(MSG_ERR, "get_smach_cid: "
    902 					    "cannot parse MAC type in %s",
    903 					    value);
    904 					goto no_specified_id;
    905 				}
    906 				value = cp + 1;
    907 				client_id_len = pif->pif_isv6 ? 1 : 5;
    908 				for (; *cp != '\0'; cp++) {
    909 					if (*cp == ':')
    910 						client_id_len++;
    911 					else if (!isxdigit(*cp))
    912 						break;
    913 				}
    914 				if (duidtype == DHCPV6_DUID_LL) {
    915 					duid_llt_t *dllt;
    916 					time_t now;
    917 
    918 					client_id_len += sizeof (*dllt);
    919 					dllt = malloc(client_id_len);
    920 					if (dllt == NULL)
    921 						goto alloc_failure;
    922 					dsmp->dsm_cid = (uchar_t *)dllt;
    923 					dllt->dllt_dutype = htons(duidtype);
    924 					dllt->dllt_hwtype = htons(subtype);
    925 					now = time(NULL) - DUID_TIME_BASE;
    926 					dllt->dllt_time = htonl(now);
    927 					cp = (char *)(dllt + 1);
    928 				} else {
    929 					duid_ll_t *dll;
    930 
    931 					client_id_len += sizeof (*dll);
    932 					dll = malloc(client_id_len);
    933 					if (dll == NULL)
    934 						goto alloc_failure;
    935 					dsmp->dsm_cid = (uchar_t *)dll;
    936 					dll->dll_dutype = htons(duidtype);
    937 					dll->dll_hwtype = htons(subtype);
    938 					cp = (char *)(dll + 1);
    939 				}
    940 				num = 0;
    941 				while ((chr = *value) != '\0') {
    942 					if (isdigit(chr)) {
    943 						num = (num << 4) + chr - '0';
    944 					} else if (isxdigit(chr)) {
    945 						num = (num << 4) + 10 + chr -
    946 						    (isupper(chr) ? 'A' : 'a');
    947 					} else if (chr == ':') {
    948 						*cp++ = num;
    949 						num = 0;
    950 					} else {
    951 						break;
    952 					}
    953 				}
    954 				break;
    955 			}
    956 			case DHCPV6_DUID_EN: {
    957 				duid_en_t *den;
    958 
    959 				errno = 0;
    960 				subtype = strtoul(value, &cp, 0);
    961 				if (value == cp || errno != 0 || *cp != ',') {
    962 					dhcpmsg(MSG_ERR, "get_smach_cid: "
    963 					    "cannot parse enterprise in %s",
    964 					    value);
    965 					goto no_specified_id;
    966 				}
    967 				value = cp + 1;
    968 				slen = strlen(value);
    969 				client_id_len = (slen + 1) / 2;
    970 				den = malloc(sizeof (*den) + client_id_len);
    971 				if (den == NULL)
    972 					goto alloc_failure;
    973 				den->den_dutype = htons(duidtype);
    974 				DHCPV6_SET_ENTNUM(den, subtype);
    975 				if (hexascii_to_octet(value, slen, den + 1,
    976 				    &client_id_len) != 0) {
    977 					dhcpmsg(MSG_ERROR, "get_smach_cid: "
    978 					    "cannot parse hex string in %s",
    979 					    value);
    980 					free(den);
    981 					goto no_specified_id;
    982 				}
    983 				dsmp->dsm_cid = (uchar_t *)den;
    984 				break;
    985 			}
    986 			default:
    987 				slen = strlen(value);
    988 				client_id_len = (slen + 1) / 2;
    989 				cp = malloc(client_id_len);
    990 				if (cp == NULL)
    991 					goto alloc_failure;
    992 				if (hexascii_to_octet(value, slen, cp,
    993 				    &client_id_len) != 0) {
    994 					dhcpmsg(MSG_ERROR, "get_smach_cid: "
    995 					    "cannot parse hex string in %s",
    996 					    value);
    997 					free(cp);
    998 					goto no_specified_id;
    999 				}
   1000 				dsmp->dsm_cid = (uchar_t *)cp;
   1001 				break;
   1002 			}
   1003 			dsmp->dsm_cidlen = client_id_len;
   1004 			if (!pif->pif_isv6) {
   1005 				(void) memmove(dsmp->dsm_cid + 5,
   1006 				    dsmp->dsm_cid, client_id_len - 5);
   1007 				dsmp->dsm_cid[0] = 255;
   1008 				dsmp->dsm_cid[1] = lif->lif_iaid >> 24;
   1009 				dsmp->dsm_cid[2] = lif->lif_iaid >> 16;
   1010 				dsmp->dsm_cid[3] = lif->lif_iaid >> 8;
   1011 				dsmp->dsm_cid[4] = lif->lif_iaid;
   1012 			}
   1013 			return (DHCP_IPC_SUCCESS);
   1014 		}
   1015 
   1016 		if (pif->pif_isv6) {
   1017 			dhcpmsg(MSG_ERROR,
   1018 			    "get_smach_cid: client ID for %s invalid: %s",
   1019 			    dsmp->dsm_name, value);
   1020 		} else if (strncasecmp("0x", value, 2) == 0 &&
   1021 		    value[2] != '\0') {
   1022 			/* skip past the 0x and convert the value to binary */
   1023 			value += 2;
   1024 			slen = strlen(value);
   1025 			client_id_len = (slen + 1) / 2;
   1026 			dsmp->dsm_cid = malloc(client_id_len);
   1027 			if (dsmp->dsm_cid == NULL)
   1028 				goto alloc_failure;
   1029 			if (hexascii_to_octet(value, slen, dsmp->dsm_cid,
   1030 			    &client_id_len) == 0) {
   1031 				dsmp->dsm_cidlen = client_id_len;
   1032 				return (DHCP_IPC_SUCCESS);
   1033 			}
   1034 			dhcpmsg(MSG_WARNING, "get_smach_cid: cannot convert "
   1035 			    "hex value for Client ID on %s", dsmp->dsm_name);
   1036 		} else {
   1037 			client_id_len = strlen(value);
   1038 			dsmp->dsm_cid = malloc(client_id_len);
   1039 			if (dsmp->dsm_cid == NULL)
   1040 				goto alloc_failure;
   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
   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
   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
   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
   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
   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
   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
   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 *
   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
   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
   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
   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
   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
   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
   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
   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
   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_UNKIF);
   1615 	release_smach(dsmp);
   1616 
   1617 	return (B_FALSE);
   1618 }
   1619