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