13431Scarlsonj /* 23431Scarlsonj * CDDL HEADER START 33431Scarlsonj * 43431Scarlsonj * The contents of this file are subject to the terms of the 53431Scarlsonj * Common Development and Distribution License (the "License"). 63431Scarlsonj * You may not use this file except in compliance with the License. 73431Scarlsonj * 83431Scarlsonj * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 93431Scarlsonj * or http://www.opensolaris.org/os/licensing. 103431Scarlsonj * See the License for the specific language governing permissions 113431Scarlsonj * and limitations under the License. 123431Scarlsonj * 133431Scarlsonj * When distributing Covered Code, include this CDDL HEADER in each 143431Scarlsonj * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 153431Scarlsonj * If applicable, add the following below this CDDL HEADER, with the 163431Scarlsonj * fields enclosed by brackets "[]" replaced with your own identifying 173431Scarlsonj * information: Portions Copyright [yyyy] [name of copyright owner] 183431Scarlsonj * 193431Scarlsonj * CDDL HEADER END 203431Scarlsonj */ 213431Scarlsonj /* 223431Scarlsonj * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 233431Scarlsonj * Use is subject to license terms. 243431Scarlsonj * 253431Scarlsonj * This module contains core functions for managing DHCP state machine 263431Scarlsonj * instances. 273431Scarlsonj */ 283431Scarlsonj 293431Scarlsonj #pragma ident "%Z%%M% %I% %E% SMI" 303431Scarlsonj 313431Scarlsonj #include <stdlib.h> 323431Scarlsonj #include <search.h> 333431Scarlsonj #include <string.h> 343431Scarlsonj #include <ctype.h> 353431Scarlsonj #include <sys/types.h> 363431Scarlsonj #include <sys/socket.h> 373431Scarlsonj #include <netinet/in.h> 383431Scarlsonj #include <netinet/arp.h> 393431Scarlsonj #include <arpa/inet.h> 403431Scarlsonj #include <dhcpmsg.h> 413431Scarlsonj #include <dhcpagent_util.h> 423431Scarlsonj #include <dhcp_stable.h> 433431Scarlsonj 443431Scarlsonj #include "agent.h" 453431Scarlsonj #include "states.h" 463431Scarlsonj #include "interface.h" 473431Scarlsonj #include "defaults.h" 483431Scarlsonj #include "script_handler.h" 493431Scarlsonj 503431Scarlsonj static uint_t global_smach_count; 513431Scarlsonj 523431Scarlsonj static uchar_t *global_duid; 533431Scarlsonj static size_t global_duidlen; 543431Scarlsonj 553431Scarlsonj /* 563431Scarlsonj * iaid_retry(): attempt to write LIF IAID again 573431Scarlsonj * 583431Scarlsonj * input: iu_tq_t *: ignored 593431Scarlsonj * void *: pointer to LIF 603431Scarlsonj * output: none 613431Scarlsonj */ 623431Scarlsonj 633431Scarlsonj /* ARGSUSED */ 643431Scarlsonj static void 653431Scarlsonj iaid_retry(iu_tq_t *tqp, void *arg) 663431Scarlsonj { 673431Scarlsonj dhcp_lif_t *lif = arg; 683431Scarlsonj 693431Scarlsonj if (write_stable_iaid(lif->lif_name, lif->lif_iaid) == -1) { 703431Scarlsonj if (errno != EROFS) { 713431Scarlsonj dhcpmsg(MSG_ERR, 723431Scarlsonj "iaid_retry: unable to write out IAID for %s", 733431Scarlsonj lif->lif_name); 743431Scarlsonj release_lif(lif); 753431Scarlsonj } else { 763431Scarlsonj lif->lif_iaid_id = iu_schedule_timer(tq, 60, 773431Scarlsonj iaid_retry, lif); 783431Scarlsonj } 793431Scarlsonj } else { 803431Scarlsonj release_lif(lif); 813431Scarlsonj } 823431Scarlsonj } 833431Scarlsonj 843431Scarlsonj /* 853431Scarlsonj * insert_smach(): Create a state machine instance on a given logical 863431Scarlsonj * interface. The state machine holds the caller's LIF 873431Scarlsonj * reference on success, and frees it on failure. 883431Scarlsonj * 893431Scarlsonj * input: dhcp_lif_t *: logical interface name 903431Scarlsonj * int *: set to DHCP_IPC_E_* if creation fails 913431Scarlsonj * output: dhcp_smach_t *: state machine instance 923431Scarlsonj */ 933431Scarlsonj 943431Scarlsonj dhcp_smach_t * 953431Scarlsonj insert_smach(dhcp_lif_t *lif, int *error) 963431Scarlsonj { 973431Scarlsonj dhcp_smach_t *dsmp, *alt_primary; 983431Scarlsonj boolean_t isv6; 993431Scarlsonj const char *prl; 1003431Scarlsonj 1013431Scarlsonj if ((dsmp = calloc(1, sizeof (*dsmp))) == NULL) { 1023431Scarlsonj dhcpmsg(MSG_ERR, "cannot allocate state machine entry for %s", 1033431Scarlsonj lif->lif_name); 1043431Scarlsonj remove_lif(lif); 1053431Scarlsonj release_lif(lif); 1063431Scarlsonj *error = DHCP_IPC_E_MEMORY; 1073431Scarlsonj return (NULL); 1083431Scarlsonj } 1093431Scarlsonj dsmp->dsm_name = lif->lif_name; 1103431Scarlsonj dsmp->dsm_lif = lif; 1113431Scarlsonj dsmp->dsm_hold_count = 1; 1123431Scarlsonj dsmp->dsm_state = INIT; 1133431Scarlsonj dsmp->dsm_dflags = DHCP_IF_REMOVED; /* until added to list */ 1143431Scarlsonj isv6 = lif->lif_pif->pif_isv6; 1153431Scarlsonj 1163431Scarlsonj /* 1173431Scarlsonj * Now that we have a controlling LIF, we need to assign an IAID to 1183431Scarlsonj * that LIF. 1193431Scarlsonj */ 1203431Scarlsonj if (lif->lif_iaid == 0 && 1213431Scarlsonj (lif->lif_iaid = read_stable_iaid(lif->lif_name)) == 0) { 1223431Scarlsonj static uint32_t iaidctr = 0x80000000u; 1233431Scarlsonj 1243431Scarlsonj /* 1253431Scarlsonj * If this is a logical interface, then use an arbitrary seed 1263431Scarlsonj * value. Otherwise, use the ifIndex. 1273431Scarlsonj */ 1283431Scarlsonj lif->lif_iaid = make_stable_iaid(lif->lif_name, 1293431Scarlsonj strchr(lif->lif_name, ':') != NULL ? iaidctr++ : 1303431Scarlsonj lif->lif_pif->pif_index); 1313431Scarlsonj dhcpmsg(MSG_INFO, 1323431Scarlsonj "insert_smach: manufactured IAID %u for v%d %s", 1333431Scarlsonj lif->lif_iaid, isv6 ? 6 : 4, lif->lif_name); 1343431Scarlsonj hold_lif(lif); 1353431Scarlsonj iaid_retry(NULL, lif); 1363431Scarlsonj } 1373431Scarlsonj 1383431Scarlsonj if (isv6) { 1393431Scarlsonj dsmp->dsm_dflags |= DHCP_IF_V6; 1403431Scarlsonj dsmp->dsm_server = ipv6_all_dhcp_relay_and_servers; 1413431Scarlsonj 1423431Scarlsonj /* 1433431Scarlsonj * With DHCPv6, we do all of our I/O using the common 1443431Scarlsonj * v6_sock_fd. There's no need for per-interface file 1453431Scarlsonj * descriptors because we have IPV6_PKTINFO. 1463431Scarlsonj */ 1473431Scarlsonj } else { 1483431Scarlsonj IN6_IPADDR_TO_V4MAPPED(htonl(INADDR_BROADCAST), 1493431Scarlsonj &dsmp->dsm_server); 1503431Scarlsonj 1513431Scarlsonj /* 1523431Scarlsonj * With IPv4 DHCP, we start off doing our I/O via DLPI, so open 1533431Scarlsonj * that up now. 1543431Scarlsonj */ 1553431Scarlsonj if (!open_dlpi_pif(lif->lif_pif)) { 1563431Scarlsonj dhcpmsg(MSG_ERR, "unable to open DLPI for %s", 1573431Scarlsonj lif->lif_name); 1583431Scarlsonj /* This will also dispose of the LIF */ 1593431Scarlsonj release_smach(dsmp); 1603431Scarlsonj *error = DHCP_IPC_E_SOCKET; 1613431Scarlsonj return (NULL); 1623431Scarlsonj } 1633431Scarlsonj dsmp->dsm_using_dlpi = B_TRUE; 1643431Scarlsonj } 1653431Scarlsonj dsmp->dsm_retrans_timer = -1; 1663431Scarlsonj dsmp->dsm_offer_timer = -1; 1673431Scarlsonj dsmp->dsm_neg_hrtime = gethrtime(); 1683431Scarlsonj dsmp->dsm_script_fd = -1; 1693431Scarlsonj dsmp->dsm_script_pid = -1; 1703431Scarlsonj dsmp->dsm_script_helper_pid = -1; 1713431Scarlsonj dsmp->dsm_script_event_id = -1; 1723431Scarlsonj 1733431Scarlsonj ipc_action_init(&dsmp->dsm_ia); 1743431Scarlsonj 1753431Scarlsonj /* 1763431Scarlsonj * initialize the parameter request list, if there is one. 1773431Scarlsonj */ 1783431Scarlsonj 1793431Scarlsonj prl = df_get_string(dsmp->dsm_name, isv6, DF_PARAM_REQUEST_LIST); 1803431Scarlsonj if (prl == NULL) { 1813431Scarlsonj dsmp->dsm_prl = NULL; 1823431Scarlsonj } else { 1833431Scarlsonj int i; 1843431Scarlsonj 1853431Scarlsonj for (dsmp->dsm_prllen = 1, i = 0; prl[i] != '\0'; i++) { 1863431Scarlsonj if (prl[i] == ',') 1873431Scarlsonj dsmp->dsm_prllen++; 1883431Scarlsonj } 1893431Scarlsonj 1903431Scarlsonj dsmp->dsm_prl = malloc(dsmp->dsm_prllen * 1913431Scarlsonj sizeof (*dsmp->dsm_prl)); 1923431Scarlsonj if (dsmp->dsm_prl == NULL) { 1933431Scarlsonj dhcpmsg(MSG_WARNING, "insert_smach: cannot allocate " 1943431Scarlsonj "parameter request list for %s (continuing)", 1953431Scarlsonj dsmp->dsm_name); 1963431Scarlsonj } else { 1973431Scarlsonj for (i = 0; i < dsmp->dsm_prllen; prl++, i++) { 1983431Scarlsonj dsmp->dsm_prl[i] = strtoul(prl, NULL, 0); 1993431Scarlsonj while (*prl != ',' && *prl != '\0') 2003431Scarlsonj prl++; 2013431Scarlsonj if (*prl == '\0') 2023431Scarlsonj break; 2033431Scarlsonj } 2043431Scarlsonj } 2053431Scarlsonj } 2063431Scarlsonj 2073431Scarlsonj dsmp->dsm_offer_wait = df_get_int(dsmp->dsm_name, isv6, 2083431Scarlsonj DF_OFFER_WAIT); 2093431Scarlsonj 2103431Scarlsonj /* 2113431Scarlsonj * If there is no primary of this type, and there is one of the other, 2123431Scarlsonj * then make this one primary if it's on the same named PIF. 2133431Scarlsonj */ 2143431Scarlsonj if (primary_smach(isv6) == NULL && 2153431Scarlsonj (alt_primary = primary_smach(!isv6)) != NULL) { 2163431Scarlsonj if (strcmp(lif->lif_pif->pif_name, 2173431Scarlsonj alt_primary->dsm_lif->lif_pif->pif_name) == 0) { 2183431Scarlsonj dhcpmsg(MSG_DEBUG, 2193431Scarlsonj "insert_smach: making %s primary for v%d", 2203431Scarlsonj dsmp->dsm_name, isv6 ? 6 : 4); 2213431Scarlsonj dsmp->dsm_dflags |= DHCP_IF_PRIMARY; 2223431Scarlsonj } 2233431Scarlsonj } 2243431Scarlsonj 2253431Scarlsonj /* 2263431Scarlsonj * We now have at least one state machine running, so cancel any 2273431Scarlsonj * running inactivity timer. 2283431Scarlsonj */ 2293431Scarlsonj if (inactivity_id != -1 && 2303431Scarlsonj iu_cancel_timer(tq, inactivity_id, NULL) == 1) 2313431Scarlsonj inactivity_id = -1; 2323431Scarlsonj 2333431Scarlsonj dsmp->dsm_dflags &= ~DHCP_IF_REMOVED; 2343431Scarlsonj insque(dsmp, &lif->lif_smachs); 2353431Scarlsonj global_smach_count++; 2363431Scarlsonj dhcpmsg(MSG_DEBUG2, "insert_smach: inserted %s", dsmp->dsm_name); 2373431Scarlsonj 2383431Scarlsonj return (dsmp); 2393431Scarlsonj } 2403431Scarlsonj 2413431Scarlsonj /* 2423431Scarlsonj * hold_smach(): acquires a hold on a state machine 2433431Scarlsonj * 2443431Scarlsonj * input: dhcp_smach_t *: the state machine to acquire a hold on 2453431Scarlsonj * output: void 2463431Scarlsonj */ 2473431Scarlsonj 2483431Scarlsonj void 2493431Scarlsonj hold_smach(dhcp_smach_t *dsmp) 2503431Scarlsonj { 2513431Scarlsonj dsmp->dsm_hold_count++; 2523431Scarlsonj 2533431Scarlsonj dhcpmsg(MSG_DEBUG2, "hold_smach: hold count on %s: %d", 2543431Scarlsonj dsmp->dsm_name, dsmp->dsm_hold_count); 2553431Scarlsonj } 2563431Scarlsonj 2573431Scarlsonj /* 2583431Scarlsonj * free_smach(): frees the memory occupied by a state machine 2593431Scarlsonj * 2603431Scarlsonj * input: dhcp_smach_t *: the DHCP state machine to free 2613431Scarlsonj * output: void 2623431Scarlsonj */ 2633431Scarlsonj 2643431Scarlsonj static void 2653431Scarlsonj free_smach(dhcp_smach_t *dsmp) 2663431Scarlsonj { 2673431Scarlsonj dhcpmsg(MSG_DEBUG, "free_smach: freeing state machine %s", 2683431Scarlsonj dsmp->dsm_name); 2693431Scarlsonj 2703431Scarlsonj deprecate_leases(dsmp); 2713431Scarlsonj remove_lif(dsmp->dsm_lif); 2723431Scarlsonj release_lif(dsmp->dsm_lif); 2733431Scarlsonj free_pkt_list(&dsmp->dsm_recv_pkt_list); 2743431Scarlsonj if (dsmp->dsm_ack != dsmp->dsm_orig_ack) 2753431Scarlsonj free_pkt_entry(dsmp->dsm_orig_ack); 2763431Scarlsonj free_pkt_entry(dsmp->dsm_ack); 2773431Scarlsonj free(dsmp->dsm_send_pkt.pkt); 2783431Scarlsonj free(dsmp->dsm_cid); 2793431Scarlsonj free(dsmp->dsm_prl); 2803431Scarlsonj free(dsmp->dsm_routers); 2813431Scarlsonj free(dsmp->dsm_reqhost); 2823431Scarlsonj free(dsmp); 2833431Scarlsonj 2843431Scarlsonj /* no big deal if this fails */ 2853431Scarlsonj if (global_smach_count == 0 && inactivity_id == -1) { 2863431Scarlsonj inactivity_id = iu_schedule_timer(tq, DHCP_INACTIVITY_WAIT, 2873431Scarlsonj inactivity_shutdown, NULL); 2883431Scarlsonj } 2893431Scarlsonj } 2903431Scarlsonj 2913431Scarlsonj /* 2923431Scarlsonj * release_smach(): releases a hold previously acquired on a state machine. 2933431Scarlsonj * If the hold count reaches 0, the state machine is freed. 2943431Scarlsonj * 2953431Scarlsonj * input: dhcp_smach_t *: the state machine entry to release the hold on 2963431Scarlsonj * output: void 2973431Scarlsonj */ 2983431Scarlsonj 2993431Scarlsonj void 3003431Scarlsonj release_smach(dhcp_smach_t *dsmp) 3013431Scarlsonj { 3023431Scarlsonj if (dsmp->dsm_hold_count == 0) { 3033431Scarlsonj dhcpmsg(MSG_CRIT, "release_smach: extraneous release"); 3043431Scarlsonj return; 3053431Scarlsonj } 3063431Scarlsonj 3073431Scarlsonj if (dsmp->dsm_hold_count == 1 && 3083431Scarlsonj !(dsmp->dsm_dflags & DHCP_IF_REMOVED)) { 3093431Scarlsonj dhcpmsg(MSG_CRIT, "release_smach: missing removal"); 3103431Scarlsonj return; 3113431Scarlsonj } 3123431Scarlsonj 3133431Scarlsonj if (--dsmp->dsm_hold_count == 0) { 3143431Scarlsonj free_smach(dsmp); 3153431Scarlsonj } else { 3163431Scarlsonj dhcpmsg(MSG_DEBUG2, "release_smach: hold count on %s: %d", 3173431Scarlsonj dsmp->dsm_name, dsmp->dsm_hold_count); 3183431Scarlsonj } 3193431Scarlsonj } 3203431Scarlsonj 3213431Scarlsonj /* 3223431Scarlsonj * next_smach(): state machine iterator function 3233431Scarlsonj * 3243431Scarlsonj * input: dhcp_smach_t *: current state machine (or NULL for list start) 3253431Scarlsonj * boolean_t: B_TRUE if DHCPv6, B_FALSE otherwise 3263431Scarlsonj * output: dhcp_smach_t *: next state machine in list 3273431Scarlsonj */ 3283431Scarlsonj 3293431Scarlsonj dhcp_smach_t * 3303431Scarlsonj next_smach(dhcp_smach_t *dsmp, boolean_t isv6) 3313431Scarlsonj { 3323431Scarlsonj dhcp_lif_t *lif; 3333431Scarlsonj dhcp_pif_t *pif; 3343431Scarlsonj 3353431Scarlsonj if (dsmp != NULL) { 3363431Scarlsonj if (dsmp->dsm_next != NULL) 3373431Scarlsonj return (dsmp->dsm_next); 3383431Scarlsonj 3393431Scarlsonj if ((lif = dsmp->dsm_lif) != NULL) 3403431Scarlsonj lif = lif->lif_next; 3413431Scarlsonj for (; lif != NULL; lif = lif->lif_next) { 3423431Scarlsonj if (lif->lif_smachs != NULL) 3433431Scarlsonj return (lif->lif_smachs); 3443431Scarlsonj } 3453431Scarlsonj 3463431Scarlsonj if ((pif = dsmp->dsm_lif->lif_pif) != NULL) 3473431Scarlsonj pif = pif->pif_next; 3483431Scarlsonj } else { 3493431Scarlsonj pif = isv6 ? v6root : v4root; 3503431Scarlsonj } 3513431Scarlsonj for (; pif != NULL; pif = pif->pif_next) { 3523431Scarlsonj for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) { 3533431Scarlsonj if (lif->lif_smachs != NULL) 3543431Scarlsonj return (lif->lif_smachs); 3553431Scarlsonj } 3563431Scarlsonj } 3573431Scarlsonj return (NULL); 3583431Scarlsonj } 3593431Scarlsonj 3603431Scarlsonj /* 3613431Scarlsonj * primary_smach(): loop through all state machines of the given type (v4 or 3623431Scarlsonj * v6) in the system, and locate the one that's primary. 3633431Scarlsonj * 3643431Scarlsonj * input: boolean_t: B_TRUE for IPv6 3653431Scarlsonj * output: dhcp_smach_t *: the primary state machine 3663431Scarlsonj */ 3673431Scarlsonj 3683431Scarlsonj dhcp_smach_t * 3693431Scarlsonj primary_smach(boolean_t isv6) 3703431Scarlsonj { 3713431Scarlsonj dhcp_smach_t *dsmp; 3723431Scarlsonj 3733431Scarlsonj for (dsmp = next_smach(NULL, isv6); dsmp != NULL; 3743431Scarlsonj dsmp = next_smach(dsmp, isv6)) { 3753431Scarlsonj if (dsmp->dsm_dflags & DHCP_IF_PRIMARY) 3763431Scarlsonj break; 3773431Scarlsonj } 3783431Scarlsonj return (dsmp); 3793431Scarlsonj } 3803431Scarlsonj 3813431Scarlsonj /* 3823431Scarlsonj * make_primary(): designate a given state machine as being the primary 3833431Scarlsonj * instance on the primary interface. Note that the user often 3843431Scarlsonj * thinks in terms of a primary "interface" (rather than just 3853431Scarlsonj * an instance), so we go to lengths here to keep v4 and v6 in 3863431Scarlsonj * sync. 3873431Scarlsonj * 3883431Scarlsonj * input: dhcp_smach_t *: the primary state machine 3893431Scarlsonj * output: none 3903431Scarlsonj */ 3913431Scarlsonj 3923431Scarlsonj void 3933431Scarlsonj make_primary(dhcp_smach_t *dsmp) 3943431Scarlsonj { 3953431Scarlsonj dhcp_smach_t *old_primary, *alt_primary; 3963431Scarlsonj dhcp_pif_t *pif; 3973431Scarlsonj 3983431Scarlsonj if ((old_primary = primary_smach(dsmp->dsm_isv6)) != NULL) 3993431Scarlsonj old_primary->dsm_dflags &= ~DHCP_IF_PRIMARY; 4003431Scarlsonj dsmp->dsm_dflags |= DHCP_IF_PRIMARY; 4013431Scarlsonj 4023431Scarlsonj /* 4033431Scarlsonj * Find the primary for the other protocol. 4043431Scarlsonj */ 4053431Scarlsonj alt_primary = primary_smach(!dsmp->dsm_isv6); 4063431Scarlsonj 4073431Scarlsonj /* 4083431Scarlsonj * If it's on a different interface, then cancel that. If it's on the 4093431Scarlsonj * same interface, then we're done. 4103431Scarlsonj */ 4113431Scarlsonj if (alt_primary != NULL) { 4123431Scarlsonj if (strcmp(alt_primary->dsm_lif->lif_pif->pif_name, 4133431Scarlsonj dsmp->dsm_lif->lif_pif->pif_name) == 0) 4143431Scarlsonj return; 4153431Scarlsonj alt_primary->dsm_dflags &= ~DHCP_IF_PRIMARY; 4163431Scarlsonj } 4173431Scarlsonj 4183431Scarlsonj /* 4193431Scarlsonj * We need a new primary for the other protocol. If the PIF exists, 4203431Scarlsonj * there must be at least one state machine. Just choose the first for 4213431Scarlsonj * consistency with insert_smach(). 4223431Scarlsonj */ 4233431Scarlsonj if ((pif = lookup_pif_by_name(dsmp->dsm_lif->lif_pif->pif_name, 4243431Scarlsonj !dsmp->dsm_isv6)) != NULL) { 4253431Scarlsonj pif->pif_lifs->lif_smachs->dsm_dflags |= DHCP_IF_PRIMARY; 4263431Scarlsonj } 4273431Scarlsonj } 4283431Scarlsonj 4293431Scarlsonj /* 4303431Scarlsonj * lookup_smach(): finds a state machine by name and type; used for dispatching 4313431Scarlsonj * user commands. 4323431Scarlsonj * 4333431Scarlsonj * input: const char *: the name of the state machine 4343431Scarlsonj * boolean_t: B_TRUE if DHCPv6, B_FALSE otherwise 4353431Scarlsonj * output: dhcp_smach_t *: the state machine found 4363431Scarlsonj */ 4373431Scarlsonj 4383431Scarlsonj dhcp_smach_t * 4393431Scarlsonj lookup_smach(const char *smname, boolean_t isv6) 4403431Scarlsonj { 4413431Scarlsonj dhcp_smach_t *dsmp; 4423431Scarlsonj 4433431Scarlsonj for (dsmp = next_smach(NULL, isv6); dsmp != NULL; 4443431Scarlsonj dsmp = next_smach(dsmp, isv6)) { 4453431Scarlsonj if (strcmp(dsmp->dsm_name, smname) == 0) 4463431Scarlsonj break; 4473431Scarlsonj } 4483431Scarlsonj return (dsmp); 4493431Scarlsonj } 4503431Scarlsonj 4513431Scarlsonj /* 4523431Scarlsonj * lookup_smach_by_uindex(): iterate through running state machines by 4533431Scarlsonj * truncated interface index. 4543431Scarlsonj * 4553431Scarlsonj * input: uint16_t: the interface index (truncated) 4563431Scarlsonj * dhcp_smach_t *: the previous state machine, or NULL for start 4573431Scarlsonj * boolean_t: B_TRUE for DHCPv6, B_FALSE for IPv4 DHCP 4583431Scarlsonj * output: dhcp_smach_t *: next state machine, or NULL at end of list 4593431Scarlsonj */ 4603431Scarlsonj 4613431Scarlsonj dhcp_smach_t * 4623431Scarlsonj lookup_smach_by_uindex(uint16_t ifindex, dhcp_smach_t *dsmp, boolean_t isv6) 4633431Scarlsonj { 4643431Scarlsonj dhcp_pif_t *pif; 4653431Scarlsonj dhcp_lif_t *lif; 4663431Scarlsonj 4673431Scarlsonj /* 4683431Scarlsonj * If the user gives us a state machine, then check that the next one 4693431Scarlsonj * available is on the same physical interface. If so, then go ahead 4703431Scarlsonj * and return that. 4713431Scarlsonj */ 4723431Scarlsonj if (dsmp != NULL) { 4733431Scarlsonj pif = dsmp->dsm_lif->lif_pif; 4743431Scarlsonj if ((dsmp = next_smach(dsmp, isv6)) == NULL) 4753431Scarlsonj return (NULL); 4763431Scarlsonj if (pif == dsmp->dsm_lif->lif_pif) 4773431Scarlsonj return (dsmp); 4783431Scarlsonj } else { 4793431Scarlsonj /* Otherwise, start at the beginning of the list */ 4803431Scarlsonj pif = NULL; 4813431Scarlsonj } 4823431Scarlsonj 4833431Scarlsonj /* 4843431Scarlsonj * Find the next physical interface with the same truncated interface 4853431Scarlsonj * index, and return the first state machine on that. If there are no 4863431Scarlsonj * more physical interfaces that match, then we're done. 4873431Scarlsonj */ 4883431Scarlsonj do { 4893431Scarlsonj pif = lookup_pif_by_uindex(ifindex, pif, isv6); 4903431Scarlsonj if (pif == NULL) 4913431Scarlsonj return (NULL); 4923431Scarlsonj for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) { 4933431Scarlsonj if ((dsmp = lif->lif_smachs) != NULL) 4943431Scarlsonj break; 4953431Scarlsonj } 4963431Scarlsonj } while (dsmp == NULL); 4973431Scarlsonj return (dsmp); 4983431Scarlsonj } 4993431Scarlsonj 5003431Scarlsonj /* 5013431Scarlsonj * lookup_smach_by_xid(): iterate through running state machines by transaction 5023431Scarlsonj * id. Transaction ID zero means "all state machines." 5033431Scarlsonj * 5043431Scarlsonj * input: uint32_t: the transaction id to look up 5053431Scarlsonj * dhcp_smach_t *: the previous state machine, or NULL for start 5063431Scarlsonj * boolean_t: B_TRUE if DHCPv6, B_FALSE otherwise 5073431Scarlsonj * output: dhcp_smach_t *: next state machine, or NULL at end of list 5083431Scarlsonj */ 5093431Scarlsonj 5103431Scarlsonj dhcp_smach_t * 5113431Scarlsonj lookup_smach_by_xid(uint32_t xid, dhcp_smach_t *dsmp, boolean_t isv6) 5123431Scarlsonj { 5133431Scarlsonj for (dsmp = next_smach(dsmp, isv6); dsmp != NULL; 5143431Scarlsonj dsmp = next_smach(dsmp, isv6)) { 5153431Scarlsonj if (xid == 0 || 5163431Scarlsonj pkt_get_xid(dsmp->dsm_send_pkt.pkt, isv6) == xid) 5173431Scarlsonj break; 5183431Scarlsonj } 5193431Scarlsonj 5203431Scarlsonj return (dsmp); 5213431Scarlsonj } 5223431Scarlsonj 5233431Scarlsonj /* 5243431Scarlsonj * lookup_smach_by_event(): find a state machine busy with a particular event 5253431Scarlsonj * ID. This is used only for error handling. 5263431Scarlsonj * 5273431Scarlsonj * input: iu_event_id_t: the event id to look up 5283431Scarlsonj * output: dhcp_smach_t *: matching state machine, or NULL if none 5293431Scarlsonj */ 5303431Scarlsonj 5313431Scarlsonj dhcp_smach_t * 5323431Scarlsonj lookup_smach_by_event(iu_event_id_t eid) 5333431Scarlsonj { 5343431Scarlsonj dhcp_smach_t *dsmp; 5353431Scarlsonj boolean_t isv6 = B_FALSE; 5363431Scarlsonj 5373431Scarlsonj for (;;) { 5383431Scarlsonj for (dsmp = next_smach(NULL, isv6); dsmp != NULL; 5393431Scarlsonj dsmp = next_smach(dsmp, isv6)) { 5403431Scarlsonj if ((dsmp->dsm_dflags & DHCP_IF_BUSY) && 5413431Scarlsonj eid == dsmp->dsm_ia.ia_eid) 5423431Scarlsonj return (dsmp); 5433431Scarlsonj } 5443431Scarlsonj if (isv6) 5453431Scarlsonj break; 5463431Scarlsonj isv6 = B_TRUE; 5473431Scarlsonj } 5483431Scarlsonj 5493431Scarlsonj return (dsmp); 5503431Scarlsonj } 5513431Scarlsonj 5523431Scarlsonj /* 5533431Scarlsonj * cancel_offer_timer(): stop the offer polling timer on a given state machine 5543431Scarlsonj * 5553431Scarlsonj * input: dhcp_smach_t *: state machine on which to stop polling for offers 5563431Scarlsonj * output: none 5573431Scarlsonj */ 5583431Scarlsonj 5593431Scarlsonj void 5603431Scarlsonj cancel_offer_timer(dhcp_smach_t *dsmp) 5613431Scarlsonj { 5623431Scarlsonj int retval; 5633431Scarlsonj 5643431Scarlsonj if (dsmp->dsm_offer_timer != -1) { 5653431Scarlsonj retval = iu_cancel_timer(tq, dsmp->dsm_offer_timer, NULL); 5663431Scarlsonj dsmp->dsm_offer_timer = -1; 5673431Scarlsonj if (retval == 1) 5683431Scarlsonj release_smach(dsmp); 5693431Scarlsonj } 5703431Scarlsonj } 5713431Scarlsonj 5723431Scarlsonj /* 5733431Scarlsonj * cancel_smach_timers(): stop all of the timers related to a given state 5743431Scarlsonj * machine, including lease and LIF expiry. 5753431Scarlsonj * 5763431Scarlsonj * input: dhcp_smach_t *: state machine to cancel 5773431Scarlsonj * output: none 5783431Scarlsonj */ 5793431Scarlsonj 5803431Scarlsonj static void 5813431Scarlsonj cancel_smach_timers(dhcp_smach_t *dsmp) 5823431Scarlsonj { 5833431Scarlsonj dhcp_lease_t *dlp; 5843431Scarlsonj dhcp_lif_t *lif; 5853431Scarlsonj uint_t nlifs; 5863431Scarlsonj 5873431Scarlsonj for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) { 5883431Scarlsonj cancel_lease_timers(dlp); 5893431Scarlsonj lif = dlp->dl_lifs; 5903431Scarlsonj nlifs = dlp->dl_nlifs; 5913431Scarlsonj for (; nlifs > 0; nlifs--, lif = lif->lif_next) 5923431Scarlsonj cancel_lif_timers(lif); 5933431Scarlsonj } 5943431Scarlsonj 5953431Scarlsonj cancel_offer_timer(dsmp); 5963431Scarlsonj stop_pkt_retransmission(dsmp); 5973431Scarlsonj } 5983431Scarlsonj 5993431Scarlsonj /* 6003431Scarlsonj * remove_smach(): removes a given state machine from the system. marks it 6013431Scarlsonj * for being freed (but may not actually free it). 6023431Scarlsonj * 6033431Scarlsonj * input: dhcp_smach_t *: the state machine to remove 6043431Scarlsonj * output: void 6053431Scarlsonj */ 6063431Scarlsonj 607*3522Scarlsonj void 6083431Scarlsonj remove_smach(dhcp_smach_t *dsmp) 6093431Scarlsonj { 6103431Scarlsonj if (dsmp->dsm_dflags & DHCP_IF_REMOVED) 6113431Scarlsonj return; 6123431Scarlsonj 6133431Scarlsonj dhcpmsg(MSG_DEBUG2, "remove_smach: removing %s", dsmp->dsm_name); 6143431Scarlsonj dsmp->dsm_dflags |= DHCP_IF_REMOVED; 6153431Scarlsonj remque(dsmp); 6163431Scarlsonj global_smach_count--; 6173431Scarlsonj 6183431Scarlsonj /* 6193431Scarlsonj * if we have long term timers, cancel them so that state machine 6203431Scarlsonj * resources can be reclaimed in a reasonable amount of time. 6213431Scarlsonj */ 6223431Scarlsonj cancel_smach_timers(dsmp); 6233431Scarlsonj 6243431Scarlsonj /* Drop the hold that the LIF's state machine list had on us */ 6253431Scarlsonj release_smach(dsmp); 6263431Scarlsonj } 6273431Scarlsonj 6283431Scarlsonj /* 6293431Scarlsonj * finished_smach(): we're finished with a given state machine; remove it from 6303431Scarlsonj * the system and tell the user (who may have initiated the 6313431Scarlsonj * removal process). Note that we remove it from the system 6323431Scarlsonj * first to allow back-to-back drop and create invocations. 6333431Scarlsonj * 6343431Scarlsonj * input: dhcp_smach_t *: the state machine to remove 6353431Scarlsonj * int: error for IPC 6363431Scarlsonj * output: void 6373431Scarlsonj */ 6383431Scarlsonj 6393431Scarlsonj void 6403431Scarlsonj finished_smach(dhcp_smach_t *dsmp, int error) 6413431Scarlsonj { 6423431Scarlsonj hold_smach(dsmp); 6433431Scarlsonj remove_smach(dsmp); 6443431Scarlsonj if (dsmp->dsm_ia.ia_fd != -1) 6453431Scarlsonj ipc_action_finish(dsmp, error); 6463431Scarlsonj else 6473431Scarlsonj (void) async_cancel(dsmp); 6483431Scarlsonj release_smach(dsmp); 6493431Scarlsonj } 6503431Scarlsonj 6513431Scarlsonj /* 6523431Scarlsonj * set_smach_state(): changes state and updates I/O 6533431Scarlsonj * 6543431Scarlsonj * input: dhcp_smach_t *: the state machine to change 6553431Scarlsonj * DHCPSTATE: the new state 6563431Scarlsonj * output: boolean_t: B_TRUE on success, B_FALSE on failure 6573431Scarlsonj */ 6583431Scarlsonj 6593431Scarlsonj boolean_t 6603431Scarlsonj set_smach_state(dhcp_smach_t *dsmp, DHCPSTATE state) 6613431Scarlsonj { 6623431Scarlsonj if (dsmp->dsm_state != state) { 6633431Scarlsonj boolean_t is_bound; 6643431Scarlsonj 6653431Scarlsonj dhcpmsg(MSG_DEBUG, 6663431Scarlsonj "set_smach_state: changing from %s to %s on %s", 6673431Scarlsonj dhcp_state_to_string(dsmp->dsm_state), 6683431Scarlsonj dhcp_state_to_string(state), dsmp->dsm_name); 6693431Scarlsonj 6703431Scarlsonj if (!dsmp->dsm_isv6) { 6713431Scarlsonj /* 6723431Scarlsonj * When we're in a bound state for IPv4, we receive our 6733431Scarlsonj * packets through our LIF. Otherwise, we receive them 6743431Scarlsonj * through DLPI. Make sure the right one is connected. 6753431Scarlsonj * For IPv6, no such change is necessary. 6763431Scarlsonj */ 6773431Scarlsonj is_bound = (state == BOUND || state == REBINDING || 6783431Scarlsonj state == RENEWING || state == RELEASING); 6793431Scarlsonj if (dsmp->dsm_using_dlpi && is_bound) { 6803431Scarlsonj if (!open_ip_lif(dsmp->dsm_lif)) 6813431Scarlsonj return (B_FALSE); 6823431Scarlsonj dsmp->dsm_using_dlpi = B_FALSE; 6833431Scarlsonj close_dlpi_pif(dsmp->dsm_lif->lif_pif); 6843431Scarlsonj } 6853431Scarlsonj if (!dsmp->dsm_using_dlpi && !is_bound) { 6863431Scarlsonj if (!open_dlpi_pif(dsmp->dsm_lif->lif_pif)) 6873431Scarlsonj return (B_FALSE); 6883431Scarlsonj dsmp->dsm_using_dlpi = B_TRUE; 6893431Scarlsonj close_ip_lif(dsmp->dsm_lif); 6903431Scarlsonj } 6913431Scarlsonj } 6923431Scarlsonj 6933431Scarlsonj dsmp->dsm_state = state; 6943431Scarlsonj } 6953431Scarlsonj return (B_TRUE); 6963431Scarlsonj } 6973431Scarlsonj 6983431Scarlsonj /* 6993431Scarlsonj * duid_retry(): attempt to write DUID again 7003431Scarlsonj * 7013431Scarlsonj * input: iu_tq_t *: ignored 7023431Scarlsonj * void *: ignored 7033431Scarlsonj * output: none 7043431Scarlsonj */ 7053431Scarlsonj 7063431Scarlsonj /* ARGSUSED */ 7073431Scarlsonj static void 7083431Scarlsonj duid_retry(iu_tq_t *tqp, void *arg) 7093431Scarlsonj { 7103431Scarlsonj if (write_stable_duid(global_duid, global_duidlen) == -1) { 7113431Scarlsonj if (errno != EROFS) { 7123431Scarlsonj dhcpmsg(MSG_ERR, 7133431Scarlsonj "duid_retry: unable to write out DUID"); 7143431Scarlsonj } else { 7153431Scarlsonj (void) iu_schedule_timer(tq, 60, duid_retry, NULL); 7163431Scarlsonj } 7173431Scarlsonj } 7183431Scarlsonj } 7193431Scarlsonj 7203431Scarlsonj /* 7213431Scarlsonj * get_smach_cid(): gets the client ID for a given state machine. 7223431Scarlsonj * 7233431Scarlsonj * input: dhcp_smach_t *: the state machine to set up 7243431Scarlsonj * output: int: DHCP_IPC_SUCCESS or one of DHCP_IPC_E_* on failure. 7253431Scarlsonj */ 7263431Scarlsonj 7273431Scarlsonj int 7283431Scarlsonj get_smach_cid(dhcp_smach_t *dsmp) 7293431Scarlsonj { 7303431Scarlsonj uchar_t *client_id; 7313431Scarlsonj uint_t client_id_len; 7323431Scarlsonj dhcp_lif_t *lif = dsmp->dsm_lif; 7333431Scarlsonj dhcp_pif_t *pif = lif->lif_pif; 7343431Scarlsonj const char *value; 7353431Scarlsonj size_t slen; 7363431Scarlsonj 7373431Scarlsonj /* 7383431Scarlsonj * Look in defaults file for the client-id. If present, this takes 7393431Scarlsonj * precedence over all other forms of ID. 7403431Scarlsonj */ 7413431Scarlsonj 7423431Scarlsonj dhcpmsg(MSG_DEBUG, "get_smach_cid: getting default client-id " 7433431Scarlsonj "property on %s", dsmp->dsm_name); 7443431Scarlsonj value = df_get_string(dsmp->dsm_name, pif->pif_isv6, DF_CLIENT_ID); 7453431Scarlsonj if (value != NULL) { 7463431Scarlsonj /* 7473431Scarlsonj * The Client ID string can have one of three basic forms: 7483431Scarlsonj * <decimal>,<data...> 7493431Scarlsonj * 0x<hex...> 7503431Scarlsonj * <string...> 7513431Scarlsonj * 7523431Scarlsonj * The first form is an RFC 3315 DUID. This is legal for both 7533431Scarlsonj * IPv4 DHCP and DHCPv6. For IPv4, an RFC 4361 Client ID is 7543431Scarlsonj * constructed from this value. 7553431Scarlsonj * 7563431Scarlsonj * The second and third forms are legal for IPv4 only. This is 7573431Scarlsonj * a raw Client ID, in hex or ASCII string format. 7583431Scarlsonj */ 7593431Scarlsonj 7603431Scarlsonj if (isdigit(*value) && 7613431Scarlsonj value[strspn(value, "0123456789")] == ',') { 7623431Scarlsonj char *cp; 7633431Scarlsonj ulong_t duidtype; 7643431Scarlsonj ulong_t subtype; 7653431Scarlsonj 7663431Scarlsonj errno = 0; 7673431Scarlsonj duidtype = strtoul(value, &cp, 0); 7683431Scarlsonj if (value == cp || errno != 0 || *cp != ',' || 7693431Scarlsonj duidtype > 65535) { 7703431Scarlsonj dhcpmsg(MSG_ERR, "get_smach_cid: cannot parse " 7713431Scarlsonj "DUID type in %s", value); 7723431Scarlsonj goto no_specified_id; 7733431Scarlsonj } 7743431Scarlsonj value = cp + 1; 7753431Scarlsonj switch (duidtype) { 7763431Scarlsonj case DHCPV6_DUID_LL: 7773431Scarlsonj case DHCPV6_DUID_LLT: { 7783431Scarlsonj int num; 7793431Scarlsonj char chr; 7803431Scarlsonj 7813431Scarlsonj errno = 0; 7823431Scarlsonj subtype = strtoul(value, &cp, 0); 7833431Scarlsonj if (value == cp || errno != 0 || *cp != ',' || 7843431Scarlsonj subtype > 65535) { 7853431Scarlsonj dhcpmsg(MSG_ERR, "get_smach_cid: " 7863431Scarlsonj "cannot parse MAC type in %s", 7873431Scarlsonj value); 7883431Scarlsonj goto no_specified_id; 7893431Scarlsonj } 7903431Scarlsonj value = cp + 1; 7913431Scarlsonj client_id_len = pif->pif_isv6 ? 1 : 5; 7923431Scarlsonj for (; *cp != '\0'; cp++) { 7933431Scarlsonj if (*cp == ':') 7943431Scarlsonj client_id_len++; 7953431Scarlsonj else if (!isxdigit(*cp)) 7963431Scarlsonj break; 7973431Scarlsonj } 7983431Scarlsonj if (duidtype == DHCPV6_DUID_LL) { 7993431Scarlsonj duid_llt_t *dllt; 8003431Scarlsonj time_t now; 8013431Scarlsonj 8023431Scarlsonj client_id_len += sizeof (*dllt); 8033431Scarlsonj dllt = malloc(client_id_len); 8043431Scarlsonj if (dllt == NULL) 8053431Scarlsonj goto alloc_failure; 8063431Scarlsonj dsmp->dsm_cid = (uchar_t *)dllt; 8073431Scarlsonj dllt->dllt_dutype = htons(duidtype); 8083431Scarlsonj dllt->dllt_hwtype = htons(subtype); 8093431Scarlsonj now = time(NULL) - DUID_TIME_BASE; 8103431Scarlsonj dllt->dllt_time = htonl(now); 8113431Scarlsonj cp = (char *)(dllt + 1); 8123431Scarlsonj } else { 8133431Scarlsonj duid_ll_t *dll; 8143431Scarlsonj 8153431Scarlsonj client_id_len += sizeof (*dll); 8163431Scarlsonj dll = malloc(client_id_len); 8173431Scarlsonj if (dll == NULL) 8183431Scarlsonj goto alloc_failure; 8193431Scarlsonj dsmp->dsm_cid = (uchar_t *)dll; 8203431Scarlsonj dll->dll_dutype = htons(duidtype); 8213431Scarlsonj dll->dll_hwtype = htons(subtype); 8223431Scarlsonj cp = (char *)(dll + 1); 8233431Scarlsonj } 8243431Scarlsonj num = 0; 8253431Scarlsonj while ((chr = *value) != '\0') { 8263431Scarlsonj if (isdigit(chr)) { 8273431Scarlsonj num = (num << 4) + chr - '0'; 8283431Scarlsonj } else if (isxdigit(chr)) { 8293431Scarlsonj num = (num << 4) + 10 + chr - 8303431Scarlsonj (isupper(chr) ? 'A' : 'a'); 8313431Scarlsonj } else if (chr == ':') { 8323431Scarlsonj *cp++ = num; 8333431Scarlsonj num = 0; 8343431Scarlsonj } else { 8353431Scarlsonj break; 8363431Scarlsonj } 8373431Scarlsonj } 8383431Scarlsonj break; 8393431Scarlsonj } 8403431Scarlsonj case DHCPV6_DUID_EN: { 8413431Scarlsonj duid_en_t *den; 8423431Scarlsonj 8433431Scarlsonj errno = 0; 8443431Scarlsonj subtype = strtoul(value, &cp, 0); 8453431Scarlsonj if (value == cp || errno != 0 || *cp != ',') { 8463431Scarlsonj dhcpmsg(MSG_ERR, "get_smach_cid: " 8473431Scarlsonj "cannot parse enterprise in %s", 8483431Scarlsonj value); 8493431Scarlsonj goto no_specified_id; 8503431Scarlsonj } 8513431Scarlsonj value = cp + 1; 8523431Scarlsonj slen = strlen(value); 8533431Scarlsonj client_id_len = (slen + 1) / 2; 8543431Scarlsonj den = malloc(sizeof (*den) + client_id_len); 8553431Scarlsonj if (den == NULL) 8563431Scarlsonj goto alloc_failure; 8573431Scarlsonj den->den_dutype = htons(duidtype); 8583431Scarlsonj DHCPV6_SET_ENTNUM(den, subtype); 8593431Scarlsonj if (hexascii_to_octet(value, slen, den + 1, 8603431Scarlsonj &client_id_len) != 0) { 8613431Scarlsonj dhcpmsg(MSG_ERROR, "get_smach_cid: " 8623431Scarlsonj "cannot parse hex string in %s", 8633431Scarlsonj value); 8643431Scarlsonj free(den); 8653431Scarlsonj goto no_specified_id; 8663431Scarlsonj } 8673431Scarlsonj dsmp->dsm_cid = (uchar_t *)den; 8683431Scarlsonj break; 8693431Scarlsonj } 8703431Scarlsonj default: 8713431Scarlsonj slen = strlen(value); 8723431Scarlsonj client_id_len = (slen + 1) / 2; 8733431Scarlsonj cp = malloc(client_id_len); 8743431Scarlsonj if (cp == NULL) 8753431Scarlsonj goto alloc_failure; 8763431Scarlsonj if (hexascii_to_octet(value, slen, cp, 8773431Scarlsonj &client_id_len) != 0) { 8783431Scarlsonj dhcpmsg(MSG_ERROR, "get_smach_cid: " 8793431Scarlsonj "cannot parse hex string in %s", 8803431Scarlsonj value); 8813431Scarlsonj free(cp); 8823431Scarlsonj goto no_specified_id; 8833431Scarlsonj } 8843431Scarlsonj dsmp->dsm_cid = (uchar_t *)cp; 8853431Scarlsonj break; 8863431Scarlsonj } 8873431Scarlsonj dsmp->dsm_cidlen = client_id_len; 8883431Scarlsonj if (!pif->pif_isv6) { 8893431Scarlsonj (void) memmove(dsmp->dsm_cid + 5, 8903431Scarlsonj dsmp->dsm_cid, client_id_len - 5); 8913431Scarlsonj dsmp->dsm_cid[0] = 255; 8923431Scarlsonj dsmp->dsm_cid[1] = lif->lif_iaid >> 24; 8933431Scarlsonj dsmp->dsm_cid[2] = lif->lif_iaid >> 16; 8943431Scarlsonj dsmp->dsm_cid[3] = lif->lif_iaid >> 8; 8953431Scarlsonj dsmp->dsm_cid[4] = lif->lif_iaid; 8963431Scarlsonj } 8973431Scarlsonj return (DHCP_IPC_SUCCESS); 8983431Scarlsonj } 8993431Scarlsonj 9003431Scarlsonj if (pif->pif_isv6) { 9013431Scarlsonj dhcpmsg(MSG_ERROR, 9023431Scarlsonj "get_smach_cid: client ID for %s invalid: %s", 9033431Scarlsonj dsmp->dsm_name, value); 9043431Scarlsonj } else if (strncasecmp("0x", value, 2) == 0 && 9053431Scarlsonj value[2] != '\0') { 9063431Scarlsonj /* skip past the 0x and convert the value to binary */ 9073431Scarlsonj value += 2; 9083431Scarlsonj slen = strlen(value); 9093431Scarlsonj client_id_len = (slen + 1) / 2; 9103431Scarlsonj dsmp->dsm_cid = malloc(client_id_len); 9113431Scarlsonj if (dsmp->dsm_cid == NULL) 9123431Scarlsonj goto alloc_failure; 9133431Scarlsonj if (hexascii_to_octet(value, slen, dsmp->dsm_cid, 9143431Scarlsonj &client_id_len) == 0) { 9153431Scarlsonj dsmp->dsm_cidlen = client_id_len; 9163431Scarlsonj return (DHCP_IPC_SUCCESS); 9173431Scarlsonj } 9183431Scarlsonj dhcpmsg(MSG_WARNING, "get_smach_cid: cannot convert " 9193431Scarlsonj "hex value for Client ID on %s", dsmp->dsm_name); 9203431Scarlsonj } else { 9213431Scarlsonj client_id_len = strlen(value); 9223431Scarlsonj dsmp->dsm_cid = malloc(client_id_len); 9233431Scarlsonj if (dsmp->dsm_cid == NULL) 9243431Scarlsonj goto alloc_failure; 9253431Scarlsonj (void) memcpy(dsmp->dsm_cid, value, client_id_len); 9263431Scarlsonj return (DHCP_IPC_SUCCESS); 9273431Scarlsonj } 9283431Scarlsonj } 9293431Scarlsonj no_specified_id: 9303431Scarlsonj 9313431Scarlsonj /* 9323431Scarlsonj * There was either no user-specified Client ID value, or we were 9333431Scarlsonj * unable to parse it. We need to determine if a Client ID is required 9343431Scarlsonj * and, if so, generate one. 9353431Scarlsonj * 9363431Scarlsonj * If it's IPv4 and not a logical interface, then we need to preserve 9373431Scarlsonj * backward-compatibility by avoiding new-fangled DUID/IAID 9383431Scarlsonj * construction. 9393431Scarlsonj */ 9403431Scarlsonj if (!pif->pif_isv6 && strchr(dsmp->dsm_name, ':') == NULL) { 9413431Scarlsonj if (pif->pif_hwtype == ARPHRD_IB) { 9423431Scarlsonj /* 9433431Scarlsonj * This comes from the DHCP over IPoIB specification. 9443431Scarlsonj * In the absence of an user specified client id, IPoIB 9453431Scarlsonj * automatically uses the required format, with the 9463431Scarlsonj * unique 4 octet value set to 0 (since IPoIB driver 9473431Scarlsonj * allows only a single interface on a port with a 9483431Scarlsonj * specific GID to belong to an IP subnet (PSARC 9493431Scarlsonj * 2001/289, FWARC 2002/702). 9503431Scarlsonj * 9513431Scarlsonj * Type Client-Identifier 9523431Scarlsonj * +-----+-----+-----+-----+-----+----....----+ 9533431Scarlsonj * | 0 | 0 (4 octets) | GID (16 octets)| 9543431Scarlsonj * +-----+-----+-----+-----+-----+----....----+ 9553431Scarlsonj */ 9563431Scarlsonj dsmp->dsm_cidlen = 1 + 4 + 16; 9573431Scarlsonj dsmp->dsm_cid = client_id = malloc(dsmp->dsm_cidlen); 9583431Scarlsonj if (dsmp->dsm_cid == NULL) 9593431Scarlsonj goto alloc_failure; 9603431Scarlsonj 9613431Scarlsonj /* 9623431Scarlsonj * Pick the GID from the mac address. The format 9633431Scarlsonj * of the hardware address is: 9643431Scarlsonj * +-----+-----+-----+-----+----....----+ 9653431Scarlsonj * | QPN (4 octets) | GID (16 octets)| 9663431Scarlsonj * +-----+-----+-----+-----+----....----+ 9673431Scarlsonj */ 9683431Scarlsonj (void) memcpy(client_id + 5, pif->pif_hwaddr + 4, 9693431Scarlsonj pif->pif_hwlen - 4); 9703431Scarlsonj (void) memset(client_id, 0, 5); 9713431Scarlsonj } 9723431Scarlsonj return (DHCP_IPC_SUCCESS); 9733431Scarlsonj } 9743431Scarlsonj 9753431Scarlsonj /* 9763431Scarlsonj * Now check for a saved DUID. If there is one, then use it. If there 9773431Scarlsonj * isn't, then generate a new one. For IPv4, we need to construct the 9783431Scarlsonj * RFC 4361 Client ID with this value and the LIF's IAID. 9793431Scarlsonj */ 9803431Scarlsonj if (global_duid == NULL && 9813431Scarlsonj (global_duid = read_stable_duid(&global_duidlen)) == NULL) { 9823431Scarlsonj global_duid = make_stable_duid(pif->pif_name, &global_duidlen); 9833431Scarlsonj if (global_duid == NULL) 9843431Scarlsonj goto alloc_failure; 9853431Scarlsonj duid_retry(NULL, NULL); 9863431Scarlsonj } 9873431Scarlsonj 9883431Scarlsonj if (pif->pif_isv6) { 9893431Scarlsonj dsmp->dsm_cid = malloc(global_duidlen); 9903431Scarlsonj if (dsmp->dsm_cid == NULL) 9913431Scarlsonj goto alloc_failure; 9923431Scarlsonj (void) memcpy(dsmp->dsm_cid, global_duid, global_duidlen); 9933431Scarlsonj dsmp->dsm_cidlen = global_duidlen; 9943431Scarlsonj } else { 9953431Scarlsonj dsmp->dsm_cid = malloc(5 + global_duidlen); 9963431Scarlsonj if (dsmp->dsm_cid == NULL) 9973431Scarlsonj goto alloc_failure; 9983431Scarlsonj dsmp->dsm_cid[0] = 255; 9993431Scarlsonj dsmp->dsm_cid[1] = lif->lif_iaid >> 24; 10003431Scarlsonj dsmp->dsm_cid[2] = lif->lif_iaid >> 16; 10013431Scarlsonj dsmp->dsm_cid[3] = lif->lif_iaid >> 8; 10023431Scarlsonj dsmp->dsm_cid[4] = lif->lif_iaid; 10033431Scarlsonj (void) memcpy(dsmp->dsm_cid + 5, global_duid, global_duidlen); 10043431Scarlsonj dsmp->dsm_cidlen = 5 + global_duidlen; 10053431Scarlsonj } 10063431Scarlsonj 10073431Scarlsonj return (DHCP_IPC_SUCCESS); 10083431Scarlsonj 10093431Scarlsonj alloc_failure: 10103431Scarlsonj dhcpmsg(MSG_ERR, "get_smach_cid: cannot allocate Client Id for %s", 10113431Scarlsonj dsmp->dsm_name); 10123431Scarlsonj return (DHCP_IPC_E_MEMORY); 10133431Scarlsonj } 10143431Scarlsonj 10153431Scarlsonj /* 10163431Scarlsonj * smach_count(): returns the number of state machines running 10173431Scarlsonj * 10183431Scarlsonj * input: void 10193431Scarlsonj * output: uint_t: the number of state machines 10203431Scarlsonj */ 10213431Scarlsonj 10223431Scarlsonj uint_t 10233431Scarlsonj smach_count(void) 10243431Scarlsonj { 10253431Scarlsonj return (global_smach_count); 10263431Scarlsonj } 10273431Scarlsonj 10283431Scarlsonj /* 10293431Scarlsonj * remove_default_routes(): removes a state machine's default routes 10303431Scarlsonj * 10313431Scarlsonj * input: dhcp_smach_t *: the state machine whose default routes need to be 10323431Scarlsonj * removed 10333431Scarlsonj * output: void 10343431Scarlsonj */ 10353431Scarlsonj 10363431Scarlsonj void 10373431Scarlsonj remove_default_routes(dhcp_smach_t *dsmp) 10383431Scarlsonj { 10393431Scarlsonj int idx; 10403431Scarlsonj 10413431Scarlsonj if (dsmp->dsm_routers != NULL) { 10423431Scarlsonj for (idx = dsmp->dsm_nrouters - 1; idx >= 0; idx--) { 10433431Scarlsonj if (del_default_route(dsmp->dsm_name, 10443431Scarlsonj &dsmp->dsm_routers[idx])) { 10453431Scarlsonj dhcpmsg(MSG_DEBUG, "remove_default_routes: " 10463431Scarlsonj "removed %s from %s", 10473431Scarlsonj inet_ntoa(dsmp->dsm_routers[idx]), 10483431Scarlsonj dsmp->dsm_name); 10493431Scarlsonj } else { 10503431Scarlsonj dhcpmsg(MSG_INFO, "remove_default_routes: " 10513431Scarlsonj "unable to remove %s from %s", 10523431Scarlsonj inet_ntoa(dsmp->dsm_routers[idx]), 10533431Scarlsonj dsmp->dsm_name); 10543431Scarlsonj } 10553431Scarlsonj } 10563431Scarlsonj free(dsmp->dsm_routers); 10573431Scarlsonj dsmp->dsm_routers = NULL; 10583431Scarlsonj dsmp->dsm_nrouters = 0; 10593431Scarlsonj } 10603431Scarlsonj } 10613431Scarlsonj 10623431Scarlsonj /* 10633431Scarlsonj * reset_smach(): resets a state machine to its initial state 10643431Scarlsonj * 10653431Scarlsonj * input: dhcp_smach_t *: the state machine to reset 10663431Scarlsonj * output: void 10673431Scarlsonj */ 10683431Scarlsonj 10693431Scarlsonj void 10703431Scarlsonj reset_smach(dhcp_smach_t *dsmp) 10713431Scarlsonj { 10723431Scarlsonj dsmp->dsm_dflags &= ~DHCP_IF_FAILED; 10733431Scarlsonj 10743431Scarlsonj remove_default_routes(dsmp); 10753431Scarlsonj 10763431Scarlsonj free_pkt_list(&dsmp->dsm_recv_pkt_list); 10773431Scarlsonj 10783431Scarlsonj if (dsmp->dsm_orig_ack != dsmp->dsm_ack) 10793431Scarlsonj free_pkt_entry(dsmp->dsm_orig_ack); 10803431Scarlsonj 10813431Scarlsonj free_pkt_entry(dsmp->dsm_ack); 10823431Scarlsonj 10833431Scarlsonj dsmp->dsm_ack = dsmp->dsm_orig_ack = NULL; 10843431Scarlsonj 10853431Scarlsonj cancel_smach_timers(dsmp); 10863431Scarlsonj 10873431Scarlsonj (void) set_smach_state(dsmp, INIT); 10883431Scarlsonj if (dsmp->dsm_isv6) { 10893431Scarlsonj dsmp->dsm_server = ipv6_all_dhcp_relay_and_servers; 10903431Scarlsonj } else { 10913431Scarlsonj IN6_IPADDR_TO_V4MAPPED(htonl(INADDR_BROADCAST), 10923431Scarlsonj &dsmp->dsm_server); 10933431Scarlsonj } 10943431Scarlsonj dsmp->dsm_neg_hrtime = gethrtime(); 10953431Scarlsonj dsmp->dsm_script_fd = -1; 10963431Scarlsonj dsmp->dsm_script_pid = -1; 10973431Scarlsonj dsmp->dsm_script_helper_pid = -1; 10983431Scarlsonj dsmp->dsm_script_callback = NULL; 10993431Scarlsonj dsmp->dsm_callback_arg = NULL; 11003431Scarlsonj dsmp->dsm_script_event_id = -1; 11013431Scarlsonj free(dsmp->dsm_reqhost); 11023431Scarlsonj dsmp->dsm_reqhost = NULL; 11033431Scarlsonj } 11043431Scarlsonj 11053431Scarlsonj /* 11063431Scarlsonj * refresh_smach(): refreshes a given state machine, as though awakened from 11073431Scarlsonj * hibernation or by lower layer "link up." 11083431Scarlsonj * 11093431Scarlsonj * input: dhcp_smach_t *: state machine to refresh 11103431Scarlsonj * output: void 11113431Scarlsonj */ 11123431Scarlsonj 11133431Scarlsonj void 11143431Scarlsonj refresh_smach(dhcp_smach_t *dsmp) 11153431Scarlsonj { 11163431Scarlsonj if (dsmp->dsm_state == BOUND || dsmp->dsm_state == RENEWING || 11173431Scarlsonj dsmp->dsm_state == REBINDING) { 11183431Scarlsonj dhcpmsg(MSG_WARNING, "refreshing lease on %s", dsmp->dsm_name); 11193431Scarlsonj cancel_smach_timers(dsmp); 11203431Scarlsonj dhcp_init_reboot(dsmp); 11213431Scarlsonj } 11223431Scarlsonj } 11233431Scarlsonj 11243431Scarlsonj /* 11253431Scarlsonj * refresh_smachs(): refreshes all finite leases under DHCP control 11263431Scarlsonj * 11273431Scarlsonj * input: iu_eh_t *: unused 11283431Scarlsonj * int: unused 11293431Scarlsonj * void *: unused 11303431Scarlsonj * output: void 11313431Scarlsonj */ 11323431Scarlsonj 11333431Scarlsonj /* ARGSUSED */ 11343431Scarlsonj void 11353431Scarlsonj refresh_smachs(iu_eh_t *eh, int sig, void *arg) 11363431Scarlsonj { 11373431Scarlsonj boolean_t isv6 = B_FALSE; 11383431Scarlsonj dhcp_smach_t *dsmp; 11393431Scarlsonj 11403431Scarlsonj for (;;) { 11413431Scarlsonj for (dsmp = next_smach(NULL, isv6); dsmp != NULL; 11423431Scarlsonj dsmp = next_smach(dsmp, isv6)) { 11433431Scarlsonj refresh_smach(dsmp); 11443431Scarlsonj } 11453431Scarlsonj if (isv6) 11463431Scarlsonj break; 11473431Scarlsonj isv6 = B_TRUE; 11483431Scarlsonj } 11493431Scarlsonj } 11503431Scarlsonj 11513431Scarlsonj /* 11523431Scarlsonj * nuke_smach_list(): delete the state machine list. For use when the 11533431Scarlsonj * dhcpagent is exiting. 11543431Scarlsonj * 11553431Scarlsonj * input: none 11563431Scarlsonj * output: none 11573431Scarlsonj */ 11583431Scarlsonj 11593431Scarlsonj void 11603431Scarlsonj nuke_smach_list(void) 11613431Scarlsonj { 11623431Scarlsonj boolean_t isv6 = B_FALSE; 11633431Scarlsonj dhcp_smach_t *dsmp, *dsmp_next; 11643431Scarlsonj 11653431Scarlsonj for (;;) { 11663431Scarlsonj for (dsmp = next_smach(NULL, isv6); dsmp != NULL; 11673431Scarlsonj dsmp = dsmp_next) { 11683431Scarlsonj int status; 11693431Scarlsonj const char *drop = isv6 ? EVENT_DROP6 : EVENT_DROP; 11703431Scarlsonj const char *release = isv6 ? EVENT_RELEASE6 : 11713431Scarlsonj EVENT_RELEASE; 11723431Scarlsonj 11733431Scarlsonj dsmp_next = next_smach(dsmp, isv6); 11743431Scarlsonj 11753431Scarlsonj cancel_smach_timers(dsmp); 11763431Scarlsonj if (dsmp->dsm_script_pid != -1) { 11773431Scarlsonj /* 11783431Scarlsonj * Stop a script if it is not for DROP or 11793431Scarlsonj * RELEASE 11803431Scarlsonj */ 11813431Scarlsonj if (strcmp(dsmp->dsm_script_event, drop) == 0 || 11823431Scarlsonj strcmp(dsmp->dsm_script_event, release) == 11833431Scarlsonj 0) { 11843431Scarlsonj continue; 11853431Scarlsonj } 11863431Scarlsonj script_stop(dsmp); 11873431Scarlsonj } 11883431Scarlsonj 11893431Scarlsonj /* 11903431Scarlsonj * If the script is started by script_start, dhcp_drop 11913431Scarlsonj * and dhcp_release should and will only be called 11923431Scarlsonj * after the script exits. 11933431Scarlsonj */ 11943431Scarlsonj if (df_get_bool(dsmp->dsm_name, isv6, 11953431Scarlsonj DF_RELEASE_ON_SIGTERM)) { 11963431Scarlsonj if (script_start(dsmp, release, dhcp_release, 11973431Scarlsonj "DHCP agent is exiting", &status) == 1) { 11983431Scarlsonj continue; 11993431Scarlsonj } 12003431Scarlsonj if (status == 1) 12013431Scarlsonj continue; 12023431Scarlsonj } 12033431Scarlsonj (void) script_start(dsmp, drop, dhcp_drop, NULL, NULL); 12043431Scarlsonj } 12053431Scarlsonj if (isv6) 12063431Scarlsonj break; 12073431Scarlsonj isv6 = B_TRUE; 12083431Scarlsonj } 12093431Scarlsonj } 12103431Scarlsonj 12113431Scarlsonj /* 12123431Scarlsonj * insert_lease(): Create a lease structure on a given state machine. The 12133431Scarlsonj * lease holds a reference to the state machine. 12143431Scarlsonj * 12153431Scarlsonj * input: dhcp_smach_t *: state machine 12163431Scarlsonj * output: dhcp_lease_t *: newly-created lease 12173431Scarlsonj */ 12183431Scarlsonj 12193431Scarlsonj dhcp_lease_t * 12203431Scarlsonj insert_lease(dhcp_smach_t *dsmp) 12213431Scarlsonj { 12223431Scarlsonj dhcp_lease_t *dlp; 12233431Scarlsonj 12243431Scarlsonj if ((dlp = calloc(1, sizeof (*dlp))) == NULL) 12253431Scarlsonj return (NULL); 12263431Scarlsonj dlp->dl_smach = dsmp; 12273431Scarlsonj dlp->dl_hold_count = 1; 12283431Scarlsonj init_timer(&dlp->dl_t1, 0); 12293431Scarlsonj init_timer(&dlp->dl_t2, 0); 12303431Scarlsonj insque(dlp, &dsmp->dsm_leases); 12313431Scarlsonj dhcpmsg(MSG_DEBUG2, "insert_lease: new lease for %s", dsmp->dsm_name); 12323431Scarlsonj return (dlp); 12333431Scarlsonj } 12343431Scarlsonj 12353431Scarlsonj /* 12363431Scarlsonj * hold_lease(): acquires a hold on a lease 12373431Scarlsonj * 12383431Scarlsonj * input: dhcp_lease_t *: the lease to acquire a hold on 12393431Scarlsonj * output: void 12403431Scarlsonj */ 12413431Scarlsonj 12423431Scarlsonj void 12433431Scarlsonj hold_lease(dhcp_lease_t *dlp) 12443431Scarlsonj { 12453431Scarlsonj dlp->dl_hold_count++; 12463431Scarlsonj 12473431Scarlsonj dhcpmsg(MSG_DEBUG2, "hold_lease: hold count on lease for %s: %d", 12483431Scarlsonj dlp->dl_smach->dsm_name, dlp->dl_hold_count); 12493431Scarlsonj } 12503431Scarlsonj 12513431Scarlsonj /* 12523431Scarlsonj * release_lease(): releases a hold previously acquired on a lease. 12533431Scarlsonj * If the hold count reaches 0, the lease is freed. 12543431Scarlsonj * 12553431Scarlsonj * input: dhcp_lease_t *: the lease to release the hold on 12563431Scarlsonj * output: void 12573431Scarlsonj */ 12583431Scarlsonj 12593431Scarlsonj void 12603431Scarlsonj release_lease(dhcp_lease_t *dlp) 12613431Scarlsonj { 12623431Scarlsonj if (dlp->dl_hold_count == 0) { 12633431Scarlsonj dhcpmsg(MSG_CRIT, "release_lease: extraneous release"); 12643431Scarlsonj return; 12653431Scarlsonj } 12663431Scarlsonj 12673431Scarlsonj if (dlp->dl_hold_count == 1 && !dlp->dl_removed) { 12683431Scarlsonj dhcpmsg(MSG_CRIT, "release_lease: missing removal"); 12693431Scarlsonj return; 12703431Scarlsonj } 12713431Scarlsonj 12723431Scarlsonj if (--dlp->dl_hold_count == 0) { 12733431Scarlsonj dhcpmsg(MSG_DEBUG, 12743431Scarlsonj "release_lease: freeing lease on state machine %s", 12753431Scarlsonj dlp->dl_smach->dsm_name); 12763431Scarlsonj free(dlp); 12773431Scarlsonj } else { 12783431Scarlsonj dhcpmsg(MSG_DEBUG2, 12793431Scarlsonj "release_lease: hold count on lease for %s: %d", 12803431Scarlsonj dlp->dl_smach->dsm_name, dlp->dl_hold_count); 12813431Scarlsonj } 12823431Scarlsonj } 12833431Scarlsonj 12843431Scarlsonj /* 12853431Scarlsonj * remove_lease(): removes a given lease from the state machine and drops the 12863431Scarlsonj * state machine's hold on the lease. 12873431Scarlsonj * 12883431Scarlsonj * input: dhcp_lease_t *: the lease to remove 12893431Scarlsonj * output: void 12903431Scarlsonj */ 12913431Scarlsonj 12923431Scarlsonj void 12933431Scarlsonj remove_lease(dhcp_lease_t *dlp) 12943431Scarlsonj { 12953431Scarlsonj if (dlp->dl_removed) { 12963431Scarlsonj dhcpmsg(MSG_CRIT, "remove_lease: extraneous removal"); 12973431Scarlsonj } else { 12983431Scarlsonj dhcp_lif_t *lif, *lifnext; 12993431Scarlsonj uint_t nlifs; 13003431Scarlsonj 13013431Scarlsonj dhcpmsg(MSG_DEBUG, 13023431Scarlsonj "remove_lease: removed lease from state machine %s", 13033431Scarlsonj dlp->dl_smach->dsm_name); 13043431Scarlsonj dlp->dl_removed = B_TRUE; 13053431Scarlsonj remque(dlp); 13063431Scarlsonj 13073431Scarlsonj cancel_lease_timers(dlp); 13083431Scarlsonj 13093431Scarlsonj lif = dlp->dl_lifs; 13103431Scarlsonj nlifs = dlp->dl_nlifs; 13113431Scarlsonj for (; nlifs > 0; nlifs--, lif = lifnext) { 13123431Scarlsonj lifnext = lif->lif_next; 13133431Scarlsonj unplumb_lif(lif); 13143431Scarlsonj } 13153431Scarlsonj 13163431Scarlsonj release_lease(dlp); 13173431Scarlsonj } 13183431Scarlsonj } 13193431Scarlsonj 13203431Scarlsonj /* 13213431Scarlsonj * cancel_lease_timer(): cancels a lease-related timer 13223431Scarlsonj * 13233431Scarlsonj * input: dhcp_lease_t *: the lease to operate on 13243431Scarlsonj * dhcp_timer_t *: the timer to cancel 13253431Scarlsonj * output: void 13263431Scarlsonj */ 13273431Scarlsonj 13283431Scarlsonj static void 13293431Scarlsonj cancel_lease_timer(dhcp_lease_t *dlp, dhcp_timer_t *dt) 13303431Scarlsonj { 13313431Scarlsonj if (dt->dt_id == -1) 13323431Scarlsonj return; 13333431Scarlsonj if (cancel_timer(dt)) { 13343431Scarlsonj release_lease(dlp); 13353431Scarlsonj } else { 13363431Scarlsonj dhcpmsg(MSG_WARNING, 13373431Scarlsonj "cancel_lease_timer: cannot cancel timer"); 13383431Scarlsonj } 13393431Scarlsonj } 13403431Scarlsonj 13413431Scarlsonj /* 13423431Scarlsonj * cancel_lease_timers(): cancels an lease's pending timers 13433431Scarlsonj * 13443431Scarlsonj * input: dhcp_lease_t *: the lease to operate on 13453431Scarlsonj * output: void 13463431Scarlsonj */ 13473431Scarlsonj 13483431Scarlsonj void 13493431Scarlsonj cancel_lease_timers(dhcp_lease_t *dlp) 13503431Scarlsonj { 13513431Scarlsonj cancel_lease_timer(dlp, &dlp->dl_t1); 13523431Scarlsonj cancel_lease_timer(dlp, &dlp->dl_t2); 13533431Scarlsonj } 13543431Scarlsonj 13553431Scarlsonj /* 13563431Scarlsonj * schedule_lease_timer(): schedules a lease-related timer 13573431Scarlsonj * 13583431Scarlsonj * input: dhcp_lease_t *: the lease to operate on 13593431Scarlsonj * dhcp_timer_t *: the timer to schedule 13603431Scarlsonj * iu_tq_callback_t *: the callback to call upon firing 13613431Scarlsonj * output: boolean_t: B_TRUE if the timer was scheduled successfully 13623431Scarlsonj */ 13633431Scarlsonj 13643431Scarlsonj boolean_t 13653431Scarlsonj schedule_lease_timer(dhcp_lease_t *dlp, dhcp_timer_t *dt, 13663431Scarlsonj iu_tq_callback_t *expire) 13673431Scarlsonj { 13683431Scarlsonj /* 13693431Scarlsonj * If there's a timer running, cancel it and release its lease 13703431Scarlsonj * reference. 13713431Scarlsonj */ 13723431Scarlsonj if (dt->dt_id != -1) { 13733431Scarlsonj if (!cancel_timer(dt)) 13743431Scarlsonj return (B_FALSE); 13753431Scarlsonj release_lease(dlp); 13763431Scarlsonj } 13773431Scarlsonj 13783431Scarlsonj if (schedule_timer(dt, expire, dlp)) { 13793431Scarlsonj hold_lease(dlp); 13803431Scarlsonj return (B_TRUE); 13813431Scarlsonj } else { 13823431Scarlsonj dhcpmsg(MSG_WARNING, 13833431Scarlsonj "schedule_lease_timer: cannot schedule timer"); 13843431Scarlsonj return (B_FALSE); 13853431Scarlsonj } 13863431Scarlsonj } 13873431Scarlsonj 13883431Scarlsonj /* 13893431Scarlsonj * deprecate_leases(): remove all of the leases from a given state machine 13903431Scarlsonj * 13913431Scarlsonj * input: dhcp_smach_t *: the state machine 13923431Scarlsonj * output: none 13933431Scarlsonj */ 13943431Scarlsonj 13953431Scarlsonj void 13963431Scarlsonj deprecate_leases(dhcp_smach_t *dsmp) 13973431Scarlsonj { 13983431Scarlsonj dhcp_lease_t *dlp; 13993431Scarlsonj 14003431Scarlsonj /* 14013431Scarlsonj * note that due to infelicities in the routing code, any default 14023431Scarlsonj * routes must be removed prior to canonizing or deprecating the LIF. 14033431Scarlsonj */ 14043431Scarlsonj 14053431Scarlsonj remove_default_routes(dsmp); 14063431Scarlsonj 14073431Scarlsonj while ((dlp = dsmp->dsm_leases) != NULL) 14083431Scarlsonj remove_lease(dlp); 14093431Scarlsonj } 14103431Scarlsonj 14113431Scarlsonj /* 14123431Scarlsonj * verify_smach(): if the state machine is in a bound state, then verify the 14133431Scarlsonj * standing of the configured interfaces. Abandon those that 14143431Scarlsonj * the user has modified. If we end up with no valid leases, 14153431Scarlsonj * then just terminate the state machine. 14163431Scarlsonj * 14173431Scarlsonj * input: dhcp_smach_t *: the state machine 14183431Scarlsonj * output: boolean_t: B_TRUE if the state machine is still valid. 14193431Scarlsonj * note: assumes caller holds a state machine reference; as with most 14203431Scarlsonj * callback functions. 14213431Scarlsonj */ 14223431Scarlsonj 14233431Scarlsonj boolean_t 14243431Scarlsonj verify_smach(dhcp_smach_t *dsmp) 14253431Scarlsonj { 14263431Scarlsonj dhcp_lease_t *dlp, *dlpn; 14273431Scarlsonj 14283431Scarlsonj if (dsmp->dsm_dflags & DHCP_IF_REMOVED) { 14293431Scarlsonj release_smach(dsmp); 14303431Scarlsonj return (B_FALSE); 14313431Scarlsonj } 14323431Scarlsonj 14333431Scarlsonj if (!dsmp->dsm_isv6) { 14343431Scarlsonj /* 14353431Scarlsonj * If this is DHCPv4, then verify the main LIF. 14363431Scarlsonj */ 14373431Scarlsonj if (!verify_lif(dsmp->dsm_lif)) 14383431Scarlsonj goto smach_terminate; 14393431Scarlsonj } 14403431Scarlsonj 14413431Scarlsonj /* 14423431Scarlsonj * If we're not in one of the bound states, then there are no LIFs to 14433431Scarlsonj * verify here. 14443431Scarlsonj */ 14453431Scarlsonj if (dsmp->dsm_state != BOUND && 14463431Scarlsonj dsmp->dsm_state != RENEWING && 14473431Scarlsonj dsmp->dsm_state != REBINDING) { 14483431Scarlsonj release_smach(dsmp); 14493431Scarlsonj return (B_TRUE); 14503431Scarlsonj } 14513431Scarlsonj 14523431Scarlsonj for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlpn) { 14533431Scarlsonj dhcp_lif_t *lif, *lifnext; 14543431Scarlsonj uint_t nlifs; 14553431Scarlsonj 14563431Scarlsonj dlpn = dlp->dl_next; 14573431Scarlsonj lif = dlp->dl_lifs; 14583431Scarlsonj nlifs = dlp->dl_nlifs; 14593431Scarlsonj for (; nlifs > 0; lif = lifnext, nlifs--) { 14603431Scarlsonj lifnext = lif->lif_next; 14613431Scarlsonj if (!verify_lif(lif)) { 14623431Scarlsonj /* 14633431Scarlsonj * User has manipulated the interface. Even 14643431Scarlsonj * if we plumbed it, we must now disown it. 14653431Scarlsonj */ 14663431Scarlsonj lif->lif_plumbed = B_FALSE; 14673431Scarlsonj remove_lif(lif); 14683431Scarlsonj } 14693431Scarlsonj } 14703431Scarlsonj if (dlp->dl_nlifs == 0) 14713431Scarlsonj remove_lease(dlp); 14723431Scarlsonj } 14733431Scarlsonj 14743431Scarlsonj /* 14753431Scarlsonj * If there are leases left, then everything's ok. 14763431Scarlsonj */ 14773431Scarlsonj if (dsmp->dsm_leases != NULL) { 14783431Scarlsonj release_smach(dsmp); 14793431Scarlsonj return (B_TRUE); 14803431Scarlsonj } 14813431Scarlsonj 14823431Scarlsonj smach_terminate: 14833431Scarlsonj finished_smach(dsmp, DHCP_IPC_E_UNKIF); 14843431Scarlsonj release_smach(dsmp); 14853431Scarlsonj 14863431Scarlsonj return (B_FALSE); 14873431Scarlsonj } 1488