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