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 /* 228485SPeter.Memishian@Sun.COM * Copyright 2009 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 299508SPeter.Memishian@Sun.COM #include <assert.h> 303431Scarlsonj #include <stdlib.h> 313431Scarlsonj #include <search.h> 323431Scarlsonj #include <string.h> 333431Scarlsonj #include <ctype.h> 343431Scarlsonj #include <sys/types.h> 353431Scarlsonj #include <sys/socket.h> 363431Scarlsonj #include <netinet/in.h> 373431Scarlsonj #include <netinet/arp.h> 383431Scarlsonj #include <arpa/inet.h> 393431Scarlsonj #include <dhcpmsg.h> 403431Scarlsonj #include <dhcpagent_util.h> 413431Scarlsonj #include <dhcp_stable.h> 42*9633Sjames.d.carlson@sun.com #include <dhcp_inittab.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 /* 85*9633Sjames.d.carlson@sun.com * parse_param_list(): parse a parameter list. 86*9633Sjames.d.carlson@sun.com * 87*9633Sjames.d.carlson@sun.com * input: const char *: parameter list string with comma-separated entries 88*9633Sjames.d.carlson@sun.com * uint_t *: return parameter; number of entries decoded 89*9633Sjames.d.carlson@sun.com * const char *: name of parameter list for logging purposes 90*9633Sjames.d.carlson@sun.com * dhcp_smach_t *: smach pointer for logging 91*9633Sjames.d.carlson@sun.com * output: uint16_t *: allocated array of parameters, or NULL if none. 92*9633Sjames.d.carlson@sun.com */ 93*9633Sjames.d.carlson@sun.com 94*9633Sjames.d.carlson@sun.com static uint16_t * 95*9633Sjames.d.carlson@sun.com parse_param_list(const char *param_list, uint_t *param_cnt, 96*9633Sjames.d.carlson@sun.com const char *param_name, dhcp_smach_t *dsmp) 97*9633Sjames.d.carlson@sun.com { 98*9633Sjames.d.carlson@sun.com int i, maxparam; 99*9633Sjames.d.carlson@sun.com char tsym[DSYM_MAX_SYM_LEN + 1]; 100*9633Sjames.d.carlson@sun.com uint16_t *params; 101*9633Sjames.d.carlson@sun.com const char *cp; 102*9633Sjames.d.carlson@sun.com dhcp_symbol_t *entry; 103*9633Sjames.d.carlson@sun.com 104*9633Sjames.d.carlson@sun.com *param_cnt = 0; 105*9633Sjames.d.carlson@sun.com 106*9633Sjames.d.carlson@sun.com if (param_list == NULL) 107*9633Sjames.d.carlson@sun.com return (NULL); 108*9633Sjames.d.carlson@sun.com 109*9633Sjames.d.carlson@sun.com for (maxparam = 1, i = 0; param_list[i] != '\0'; i++) { 110*9633Sjames.d.carlson@sun.com if (param_list[i] == ',') 111*9633Sjames.d.carlson@sun.com maxparam++; 112*9633Sjames.d.carlson@sun.com } 113*9633Sjames.d.carlson@sun.com 114*9633Sjames.d.carlson@sun.com params = malloc(maxparam * sizeof (*params)); 115*9633Sjames.d.carlson@sun.com if (params == NULL) { 116*9633Sjames.d.carlson@sun.com dhcpmsg(MSG_WARNING, 117*9633Sjames.d.carlson@sun.com "cannot allocate parameter %s list for %s (continuing)", 118*9633Sjames.d.carlson@sun.com param_name, dsmp->dsm_name); 119*9633Sjames.d.carlson@sun.com return (NULL); 120*9633Sjames.d.carlson@sun.com } 121*9633Sjames.d.carlson@sun.com 122*9633Sjames.d.carlson@sun.com for (i = 0; i < maxparam; ) { 123*9633Sjames.d.carlson@sun.com 124*9633Sjames.d.carlson@sun.com if (isspace(*param_list)) 125*9633Sjames.d.carlson@sun.com param_list++; 126*9633Sjames.d.carlson@sun.com 127*9633Sjames.d.carlson@sun.com /* extract the next element on the list */ 128*9633Sjames.d.carlson@sun.com cp = strchr(param_list, ','); 129*9633Sjames.d.carlson@sun.com if (cp == NULL || cp - param_list >= sizeof (tsym)) 130*9633Sjames.d.carlson@sun.com (void) strlcpy(tsym, param_list, sizeof (tsym)); 131*9633Sjames.d.carlson@sun.com else 132*9633Sjames.d.carlson@sun.com (void) strlcpy(tsym, param_list, cp - param_list + 1); 133*9633Sjames.d.carlson@sun.com 134*9633Sjames.d.carlson@sun.com /* LINTED -- do nothing with blanks on purpose */ 135*9633Sjames.d.carlson@sun.com if (tsym[0] == '\0') { 136*9633Sjames.d.carlson@sun.com ; 137*9633Sjames.d.carlson@sun.com } else if (isalpha(tsym[0])) { 138*9633Sjames.d.carlson@sun.com entry = inittab_getbyname(ITAB_CAT_SITE | 139*9633Sjames.d.carlson@sun.com ITAB_CAT_STANDARD | 140*9633Sjames.d.carlson@sun.com (dsmp->dsm_isv6 ? ITAB_CAT_V6 : 0), 141*9633Sjames.d.carlson@sun.com ITAB_CONS_INFO, tsym); 142*9633Sjames.d.carlson@sun.com if (entry == NULL) { 143*9633Sjames.d.carlson@sun.com dhcpmsg(MSG_INFO, "ignored unknown %s list " 144*9633Sjames.d.carlson@sun.com "entry '%s' for %s", param_name, tsym, 145*9633Sjames.d.carlson@sun.com dsmp->dsm_name); 146*9633Sjames.d.carlson@sun.com } else { 147*9633Sjames.d.carlson@sun.com params[i++] = entry->ds_code; 148*9633Sjames.d.carlson@sun.com free(entry); 149*9633Sjames.d.carlson@sun.com } 150*9633Sjames.d.carlson@sun.com } else { 151*9633Sjames.d.carlson@sun.com params[i++] = strtoul(tsym, NULL, 0); 152*9633Sjames.d.carlson@sun.com } 153*9633Sjames.d.carlson@sun.com if (cp == NULL) 154*9633Sjames.d.carlson@sun.com break; 155*9633Sjames.d.carlson@sun.com param_list = cp + 1; 156*9633Sjames.d.carlson@sun.com } 157*9633Sjames.d.carlson@sun.com 158*9633Sjames.d.carlson@sun.com *param_cnt = i; 159*9633Sjames.d.carlson@sun.com return (params); 160*9633Sjames.d.carlson@sun.com } 161*9633Sjames.d.carlson@sun.com 162*9633Sjames.d.carlson@sun.com /* 1633431Scarlsonj * insert_smach(): Create a state machine instance on a given logical 1643431Scarlsonj * interface. The state machine holds the caller's LIF 1653431Scarlsonj * reference on success, and frees it on failure. 1663431Scarlsonj * 1673431Scarlsonj * input: dhcp_lif_t *: logical interface name 1683431Scarlsonj * int *: set to DHCP_IPC_E_* if creation fails 1693431Scarlsonj * output: dhcp_smach_t *: state machine instance 1703431Scarlsonj */ 1713431Scarlsonj 1723431Scarlsonj dhcp_smach_t * 1733431Scarlsonj insert_smach(dhcp_lif_t *lif, int *error) 1743431Scarlsonj { 1753431Scarlsonj dhcp_smach_t *dsmp, *alt_primary; 1763431Scarlsonj boolean_t isv6; 177*9633Sjames.d.carlson@sun.com const char *plist; 1783431Scarlsonj 1793431Scarlsonj if ((dsmp = calloc(1, sizeof (*dsmp))) == NULL) { 1803431Scarlsonj dhcpmsg(MSG_ERR, "cannot allocate state machine entry for %s", 1813431Scarlsonj lif->lif_name); 1823431Scarlsonj remove_lif(lif); 1833431Scarlsonj release_lif(lif); 1843431Scarlsonj *error = DHCP_IPC_E_MEMORY; 1853431Scarlsonj return (NULL); 1863431Scarlsonj } 1873431Scarlsonj dsmp->dsm_name = lif->lif_name; 1883431Scarlsonj dsmp->dsm_lif = lif; 1893431Scarlsonj dsmp->dsm_hold_count = 1; 1903431Scarlsonj dsmp->dsm_state = INIT; 1913431Scarlsonj dsmp->dsm_dflags = DHCP_IF_REMOVED; /* until added to list */ 1923431Scarlsonj isv6 = lif->lif_pif->pif_isv6; 1933431Scarlsonj 1943431Scarlsonj /* 1953431Scarlsonj * Now that we have a controlling LIF, we need to assign an IAID to 1963431Scarlsonj * that LIF. 1973431Scarlsonj */ 1983431Scarlsonj if (lif->lif_iaid == 0 && 1993431Scarlsonj (lif->lif_iaid = read_stable_iaid(lif->lif_name)) == 0) { 2003431Scarlsonj static uint32_t iaidctr = 0x80000000u; 2013431Scarlsonj 2023431Scarlsonj /* 2033431Scarlsonj * If this is a logical interface, then use an arbitrary seed 2043431Scarlsonj * value. Otherwise, use the ifIndex. 2053431Scarlsonj */ 2063431Scarlsonj lif->lif_iaid = make_stable_iaid(lif->lif_name, 2073431Scarlsonj strchr(lif->lif_name, ':') != NULL ? iaidctr++ : 2083431Scarlsonj lif->lif_pif->pif_index); 2093431Scarlsonj dhcpmsg(MSG_INFO, 2103431Scarlsonj "insert_smach: manufactured IAID %u for v%d %s", 2113431Scarlsonj lif->lif_iaid, isv6 ? 6 : 4, lif->lif_name); 2123431Scarlsonj hold_lif(lif); 2133431Scarlsonj iaid_retry(NULL, lif); 2143431Scarlsonj } 2153431Scarlsonj 2163431Scarlsonj if (isv6) { 2173431Scarlsonj dsmp->dsm_dflags |= DHCP_IF_V6; 2183431Scarlsonj dsmp->dsm_server = ipv6_all_dhcp_relay_and_servers; 2193431Scarlsonj 2203431Scarlsonj /* 2213431Scarlsonj * With DHCPv6, we do all of our I/O using the common 2223431Scarlsonj * v6_sock_fd. There's no need for per-interface file 2233431Scarlsonj * descriptors because we have IPV6_PKTINFO. 2243431Scarlsonj */ 2253431Scarlsonj } else { 2263431Scarlsonj IN6_IPADDR_TO_V4MAPPED(htonl(INADDR_BROADCAST), 2273431Scarlsonj &dsmp->dsm_server); 2283431Scarlsonj 2293431Scarlsonj /* 2305381Smeem * With IPv4 DHCP, we use a socket per lif. 2313431Scarlsonj */ 2328485SPeter.Memishian@Sun.COM if (!open_ip_lif(lif, INADDR_ANY, B_TRUE)) { 2335381Smeem dhcpmsg(MSG_ERR, "unable to open socket for %s", 2343431Scarlsonj lif->lif_name); 2353431Scarlsonj /* This will also dispose of the LIF */ 2363431Scarlsonj release_smach(dsmp); 2373431Scarlsonj *error = DHCP_IPC_E_SOCKET; 2383431Scarlsonj return (NULL); 2393431Scarlsonj } 2403431Scarlsonj } 2419508SPeter.Memishian@Sun.COM 2429508SPeter.Memishian@Sun.COM script_init(dsmp); 2439508SPeter.Memishian@Sun.COM ipc_action_init(&dsmp->dsm_ia); 2443431Scarlsonj 2459508SPeter.Memishian@Sun.COM dsmp->dsm_neg_hrtime = gethrtime(); 2469508SPeter.Memishian@Sun.COM dsmp->dsm_offer_timer = -1; 2479508SPeter.Memishian@Sun.COM dsmp->dsm_start_timer = -1; 2489508SPeter.Memishian@Sun.COM dsmp->dsm_retrans_timer = -1; 2493431Scarlsonj 2503431Scarlsonj /* 251*9633Sjames.d.carlson@sun.com * Initialize the parameter request and ignore lists, if any. 2523431Scarlsonj */ 253*9633Sjames.d.carlson@sun.com plist = df_get_string(dsmp->dsm_name, isv6, DF_PARAM_REQUEST_LIST); 254*9633Sjames.d.carlson@sun.com dsmp->dsm_prl = parse_param_list(plist, &dsmp->dsm_prllen, "request", 255*9633Sjames.d.carlson@sun.com dsmp); 256*9633Sjames.d.carlson@sun.com plist = df_get_string(dsmp->dsm_name, isv6, DF_PARAM_IGNORE_LIST); 257*9633Sjames.d.carlson@sun.com dsmp->dsm_pil = parse_param_list(plist, &dsmp->dsm_pillen, "ignore", 258*9633Sjames.d.carlson@sun.com dsmp); 2593431Scarlsonj 2603431Scarlsonj dsmp->dsm_offer_wait = df_get_int(dsmp->dsm_name, isv6, 2613431Scarlsonj DF_OFFER_WAIT); 2623431Scarlsonj 2633431Scarlsonj /* 2643431Scarlsonj * If there is no primary of this type, and there is one of the other, 2653431Scarlsonj * then make this one primary if it's on the same named PIF. 2663431Scarlsonj */ 2673431Scarlsonj if (primary_smach(isv6) == NULL && 2683431Scarlsonj (alt_primary = primary_smach(!isv6)) != NULL) { 2693431Scarlsonj if (strcmp(lif->lif_pif->pif_name, 2703431Scarlsonj alt_primary->dsm_lif->lif_pif->pif_name) == 0) { 2713431Scarlsonj dhcpmsg(MSG_DEBUG, 2723431Scarlsonj "insert_smach: making %s primary for v%d", 2733431Scarlsonj dsmp->dsm_name, isv6 ? 6 : 4); 2743431Scarlsonj dsmp->dsm_dflags |= DHCP_IF_PRIMARY; 2753431Scarlsonj } 2763431Scarlsonj } 2773431Scarlsonj 2783431Scarlsonj /* 2793431Scarlsonj * We now have at least one state machine running, so cancel any 2803431Scarlsonj * running inactivity timer. 2813431Scarlsonj */ 2823431Scarlsonj if (inactivity_id != -1 && 2833431Scarlsonj iu_cancel_timer(tq, inactivity_id, NULL) == 1) 2843431Scarlsonj inactivity_id = -1; 2853431Scarlsonj 2863431Scarlsonj dsmp->dsm_dflags &= ~DHCP_IF_REMOVED; 2873431Scarlsonj insque(dsmp, &lif->lif_smachs); 2883431Scarlsonj global_smach_count++; 2893431Scarlsonj dhcpmsg(MSG_DEBUG2, "insert_smach: inserted %s", dsmp->dsm_name); 2903431Scarlsonj 2913431Scarlsonj return (dsmp); 2923431Scarlsonj } 2933431Scarlsonj 2943431Scarlsonj /* 2953431Scarlsonj * hold_smach(): acquires a hold on a state machine 2963431Scarlsonj * 2973431Scarlsonj * input: dhcp_smach_t *: the state machine to acquire a hold on 2983431Scarlsonj * output: void 2993431Scarlsonj */ 3003431Scarlsonj 3013431Scarlsonj void 3023431Scarlsonj hold_smach(dhcp_smach_t *dsmp) 3033431Scarlsonj { 3043431Scarlsonj dsmp->dsm_hold_count++; 3053431Scarlsonj 3063431Scarlsonj dhcpmsg(MSG_DEBUG2, "hold_smach: hold count on %s: %d", 3073431Scarlsonj dsmp->dsm_name, dsmp->dsm_hold_count); 3083431Scarlsonj } 3093431Scarlsonj 3103431Scarlsonj /* 3113431Scarlsonj * free_smach(): frees the memory occupied by a state machine 3123431Scarlsonj * 3133431Scarlsonj * input: dhcp_smach_t *: the DHCP state machine to free 3143431Scarlsonj * output: void 3153431Scarlsonj */ 3163431Scarlsonj 3173431Scarlsonj static void 3183431Scarlsonj free_smach(dhcp_smach_t *dsmp) 3193431Scarlsonj { 3203431Scarlsonj dhcpmsg(MSG_DEBUG, "free_smach: freeing state machine %s", 3213431Scarlsonj dsmp->dsm_name); 3223431Scarlsonj 3233431Scarlsonj deprecate_leases(dsmp); 3243431Scarlsonj remove_lif(dsmp->dsm_lif); 3253431Scarlsonj release_lif(dsmp->dsm_lif); 3263431Scarlsonj free_pkt_list(&dsmp->dsm_recv_pkt_list); 3273431Scarlsonj if (dsmp->dsm_ack != dsmp->dsm_orig_ack) 3283431Scarlsonj free_pkt_entry(dsmp->dsm_orig_ack); 3293431Scarlsonj free_pkt_entry(dsmp->dsm_ack); 3303431Scarlsonj free(dsmp->dsm_send_pkt.pkt); 3313431Scarlsonj free(dsmp->dsm_cid); 3323431Scarlsonj free(dsmp->dsm_prl); 333*9633Sjames.d.carlson@sun.com free(dsmp->dsm_pil); 3343431Scarlsonj free(dsmp->dsm_routers); 3353431Scarlsonj free(dsmp->dsm_reqhost); 3363431Scarlsonj free(dsmp); 3373431Scarlsonj 3383431Scarlsonj /* no big deal if this fails */ 3393431Scarlsonj if (global_smach_count == 0 && inactivity_id == -1) { 3403431Scarlsonj inactivity_id = iu_schedule_timer(tq, DHCP_INACTIVITY_WAIT, 3413431Scarlsonj inactivity_shutdown, NULL); 3423431Scarlsonj } 3433431Scarlsonj } 3443431Scarlsonj 3453431Scarlsonj /* 3463431Scarlsonj * release_smach(): releases a hold previously acquired on a state machine. 3473431Scarlsonj * If the hold count reaches 0, the state machine is freed. 3483431Scarlsonj * 3493431Scarlsonj * input: dhcp_smach_t *: the state machine entry to release the hold on 3503431Scarlsonj * output: void 3513431Scarlsonj */ 3523431Scarlsonj 3533431Scarlsonj void 3543431Scarlsonj release_smach(dhcp_smach_t *dsmp) 3553431Scarlsonj { 3563431Scarlsonj if (dsmp->dsm_hold_count == 0) { 3573431Scarlsonj dhcpmsg(MSG_CRIT, "release_smach: extraneous release"); 3583431Scarlsonj return; 3593431Scarlsonj } 3603431Scarlsonj 3613431Scarlsonj if (dsmp->dsm_hold_count == 1 && 3623431Scarlsonj !(dsmp->dsm_dflags & DHCP_IF_REMOVED)) { 3633431Scarlsonj dhcpmsg(MSG_CRIT, "release_smach: missing removal"); 3643431Scarlsonj return; 3653431Scarlsonj } 3663431Scarlsonj 3673431Scarlsonj if (--dsmp->dsm_hold_count == 0) { 3683431Scarlsonj free_smach(dsmp); 3693431Scarlsonj } else { 3703431Scarlsonj dhcpmsg(MSG_DEBUG2, "release_smach: hold count on %s: %d", 3713431Scarlsonj dsmp->dsm_name, dsmp->dsm_hold_count); 3723431Scarlsonj } 3733431Scarlsonj } 3743431Scarlsonj 3753431Scarlsonj /* 3763431Scarlsonj * next_smach(): state machine iterator function 3773431Scarlsonj * 3783431Scarlsonj * input: dhcp_smach_t *: current state machine (or NULL for list start) 3793431Scarlsonj * boolean_t: B_TRUE if DHCPv6, B_FALSE otherwise 3803431Scarlsonj * output: dhcp_smach_t *: next state machine in list 3813431Scarlsonj */ 3823431Scarlsonj 3833431Scarlsonj dhcp_smach_t * 3843431Scarlsonj next_smach(dhcp_smach_t *dsmp, boolean_t isv6) 3853431Scarlsonj { 3863431Scarlsonj dhcp_lif_t *lif; 3873431Scarlsonj dhcp_pif_t *pif; 3883431Scarlsonj 3893431Scarlsonj if (dsmp != NULL) { 3903431Scarlsonj if (dsmp->dsm_next != NULL) 3913431Scarlsonj return (dsmp->dsm_next); 3923431Scarlsonj 3933431Scarlsonj if ((lif = dsmp->dsm_lif) != NULL) 3943431Scarlsonj lif = lif->lif_next; 3953431Scarlsonj for (; lif != NULL; lif = lif->lif_next) { 3963431Scarlsonj if (lif->lif_smachs != NULL) 3973431Scarlsonj return (lif->lif_smachs); 3983431Scarlsonj } 3993431Scarlsonj 4003431Scarlsonj if ((pif = dsmp->dsm_lif->lif_pif) != NULL) 4013431Scarlsonj pif = pif->pif_next; 4023431Scarlsonj } else { 4033431Scarlsonj pif = isv6 ? v6root : v4root; 4043431Scarlsonj } 4053431Scarlsonj for (; pif != NULL; pif = pif->pif_next) { 4063431Scarlsonj for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) { 4073431Scarlsonj if (lif->lif_smachs != NULL) 4083431Scarlsonj return (lif->lif_smachs); 4093431Scarlsonj } 4103431Scarlsonj } 4113431Scarlsonj return (NULL); 4123431Scarlsonj } 4133431Scarlsonj 4143431Scarlsonj /* 4153431Scarlsonj * primary_smach(): loop through all state machines of the given type (v4 or 4163431Scarlsonj * v6) in the system, and locate the one that's primary. 4173431Scarlsonj * 4183431Scarlsonj * input: boolean_t: B_TRUE for IPv6 4193431Scarlsonj * output: dhcp_smach_t *: the primary state machine 4203431Scarlsonj */ 4213431Scarlsonj 4223431Scarlsonj dhcp_smach_t * 4233431Scarlsonj primary_smach(boolean_t isv6) 4243431Scarlsonj { 4253431Scarlsonj dhcp_smach_t *dsmp; 4263431Scarlsonj 4273431Scarlsonj for (dsmp = next_smach(NULL, isv6); dsmp != NULL; 4283431Scarlsonj dsmp = next_smach(dsmp, isv6)) { 4293431Scarlsonj if (dsmp->dsm_dflags & DHCP_IF_PRIMARY) 4303431Scarlsonj break; 4313431Scarlsonj } 4323431Scarlsonj return (dsmp); 4333431Scarlsonj } 4343431Scarlsonj 4353431Scarlsonj /* 436*9633Sjames.d.carlson@sun.com * info_primary_smach(): loop through all state machines of the given type (v4 437*9633Sjames.d.carlson@sun.com * or v6) in the system, and locate the one that should 438*9633Sjames.d.carlson@sun.com * be considered "primary" for dhcpinfo. 439*9633Sjames.d.carlson@sun.com * 440*9633Sjames.d.carlson@sun.com * input: boolean_t: B_TRUE for IPv6 441*9633Sjames.d.carlson@sun.com * output: dhcp_smach_t *: the dhcpinfo primary state machine 442*9633Sjames.d.carlson@sun.com */ 443*9633Sjames.d.carlson@sun.com 444*9633Sjames.d.carlson@sun.com dhcp_smach_t * 445*9633Sjames.d.carlson@sun.com info_primary_smach(boolean_t isv6) 446*9633Sjames.d.carlson@sun.com { 447*9633Sjames.d.carlson@sun.com dhcp_smach_t *bestdsm = NULL; 448*9633Sjames.d.carlson@sun.com dhcp_smach_t *dsmp; 449*9633Sjames.d.carlson@sun.com 450*9633Sjames.d.carlson@sun.com for (dsmp = next_smach(NULL, isv6); dsmp != NULL; 451*9633Sjames.d.carlson@sun.com dsmp = next_smach(dsmp, isv6)) { 452*9633Sjames.d.carlson@sun.com /* 453*9633Sjames.d.carlson@sun.com * If there is a primary, then something previously went wrong 454*9633Sjames.d.carlson@sun.com * with verification, because the caller uses primary_smach() 455*9633Sjames.d.carlson@sun.com * before calling this routine. There's nothing else we can do 456*9633Sjames.d.carlson@sun.com * but return failure, as the designated primary must be bad. 457*9633Sjames.d.carlson@sun.com */ 458*9633Sjames.d.carlson@sun.com if (dsmp->dsm_dflags & DHCP_IF_PRIMARY) 459*9633Sjames.d.carlson@sun.com return (NULL); 460*9633Sjames.d.carlson@sun.com 461*9633Sjames.d.carlson@sun.com /* If we have no information, then we're not primary. */ 462*9633Sjames.d.carlson@sun.com if (dsmp->dsm_ack == NULL) 463*9633Sjames.d.carlson@sun.com continue; 464*9633Sjames.d.carlson@sun.com 465*9633Sjames.d.carlson@sun.com /* 466*9633Sjames.d.carlson@sun.com * Among those interfaces that have DHCP information, the 467*9633Sjames.d.carlson@sun.com * "primary" is the one that sorts lexically first. 468*9633Sjames.d.carlson@sun.com */ 469*9633Sjames.d.carlson@sun.com if (bestdsm == NULL || 470*9633Sjames.d.carlson@sun.com strcmp(dsmp->dsm_name, bestdsm->dsm_name) < 0) 471*9633Sjames.d.carlson@sun.com bestdsm = dsmp; 472*9633Sjames.d.carlson@sun.com } 473*9633Sjames.d.carlson@sun.com return (bestdsm); 474*9633Sjames.d.carlson@sun.com } 475*9633Sjames.d.carlson@sun.com 476*9633Sjames.d.carlson@sun.com /* 4773431Scarlsonj * make_primary(): designate a given state machine as being the primary 4783431Scarlsonj * instance on the primary interface. Note that the user often 4793431Scarlsonj * thinks in terms of a primary "interface" (rather than just 4803431Scarlsonj * an instance), so we go to lengths here to keep v4 and v6 in 4813431Scarlsonj * sync. 4823431Scarlsonj * 4833431Scarlsonj * input: dhcp_smach_t *: the primary state machine 4843431Scarlsonj * output: none 4853431Scarlsonj */ 4863431Scarlsonj 4873431Scarlsonj void 4883431Scarlsonj make_primary(dhcp_smach_t *dsmp) 4893431Scarlsonj { 4903431Scarlsonj dhcp_smach_t *old_primary, *alt_primary; 4913431Scarlsonj dhcp_pif_t *pif; 4923431Scarlsonj 4933431Scarlsonj if ((old_primary = primary_smach(dsmp->dsm_isv6)) != NULL) 4943431Scarlsonj old_primary->dsm_dflags &= ~DHCP_IF_PRIMARY; 4953431Scarlsonj dsmp->dsm_dflags |= DHCP_IF_PRIMARY; 4963431Scarlsonj 4973431Scarlsonj /* 4983431Scarlsonj * Find the primary for the other protocol. 4993431Scarlsonj */ 5003431Scarlsonj alt_primary = primary_smach(!dsmp->dsm_isv6); 5013431Scarlsonj 5023431Scarlsonj /* 5033431Scarlsonj * If it's on a different interface, then cancel that. If it's on the 5043431Scarlsonj * same interface, then we're done. 5053431Scarlsonj */ 5063431Scarlsonj if (alt_primary != NULL) { 5073431Scarlsonj if (strcmp(alt_primary->dsm_lif->lif_pif->pif_name, 5083431Scarlsonj dsmp->dsm_lif->lif_pif->pif_name) == 0) 5093431Scarlsonj return; 5103431Scarlsonj alt_primary->dsm_dflags &= ~DHCP_IF_PRIMARY; 5113431Scarlsonj } 5123431Scarlsonj 5133431Scarlsonj /* 5143431Scarlsonj * We need a new primary for the other protocol. If the PIF exists, 5153431Scarlsonj * there must be at least one state machine. Just choose the first for 5163431Scarlsonj * consistency with insert_smach(). 5173431Scarlsonj */ 5183431Scarlsonj if ((pif = lookup_pif_by_name(dsmp->dsm_lif->lif_pif->pif_name, 5193431Scarlsonj !dsmp->dsm_isv6)) != NULL) { 5203431Scarlsonj pif->pif_lifs->lif_smachs->dsm_dflags |= DHCP_IF_PRIMARY; 5213431Scarlsonj } 5223431Scarlsonj } 5233431Scarlsonj 5243431Scarlsonj /* 5253431Scarlsonj * lookup_smach(): finds a state machine by name and type; used for dispatching 5263431Scarlsonj * user commands. 5273431Scarlsonj * 5283431Scarlsonj * input: const char *: the name of the state machine 5293431Scarlsonj * boolean_t: B_TRUE if DHCPv6, B_FALSE otherwise 5303431Scarlsonj * output: dhcp_smach_t *: the state machine found 5313431Scarlsonj */ 5323431Scarlsonj 5333431Scarlsonj dhcp_smach_t * 5343431Scarlsonj lookup_smach(const char *smname, boolean_t isv6) 5353431Scarlsonj { 5363431Scarlsonj dhcp_smach_t *dsmp; 5373431Scarlsonj 5383431Scarlsonj for (dsmp = next_smach(NULL, isv6); dsmp != NULL; 5393431Scarlsonj dsmp = next_smach(dsmp, isv6)) { 5403431Scarlsonj if (strcmp(dsmp->dsm_name, smname) == 0) 5413431Scarlsonj break; 5423431Scarlsonj } 5433431Scarlsonj return (dsmp); 5443431Scarlsonj } 5453431Scarlsonj 5463431Scarlsonj /* 5473431Scarlsonj * lookup_smach_by_uindex(): iterate through running state machines by 5483431Scarlsonj * truncated interface index. 5493431Scarlsonj * 5503431Scarlsonj * input: uint16_t: the interface index (truncated) 5513431Scarlsonj * dhcp_smach_t *: the previous state machine, or NULL for start 5523431Scarlsonj * boolean_t: B_TRUE for DHCPv6, B_FALSE for IPv4 DHCP 5533431Scarlsonj * output: dhcp_smach_t *: next state machine, or NULL at end of list 5543431Scarlsonj */ 5553431Scarlsonj 5563431Scarlsonj dhcp_smach_t * 5573431Scarlsonj lookup_smach_by_uindex(uint16_t ifindex, dhcp_smach_t *dsmp, boolean_t isv6) 5583431Scarlsonj { 5593431Scarlsonj dhcp_pif_t *pif; 5603431Scarlsonj dhcp_lif_t *lif; 5613431Scarlsonj 5623431Scarlsonj /* 5633431Scarlsonj * If the user gives us a state machine, then check that the next one 5643431Scarlsonj * available is on the same physical interface. If so, then go ahead 5653431Scarlsonj * and return that. 5663431Scarlsonj */ 5673431Scarlsonj if (dsmp != NULL) { 5683431Scarlsonj pif = dsmp->dsm_lif->lif_pif; 5693431Scarlsonj if ((dsmp = next_smach(dsmp, isv6)) == NULL) 5703431Scarlsonj return (NULL); 5713431Scarlsonj if (pif == dsmp->dsm_lif->lif_pif) 5723431Scarlsonj return (dsmp); 5733431Scarlsonj } else { 5743431Scarlsonj /* Otherwise, start at the beginning of the list */ 5753431Scarlsonj pif = NULL; 5763431Scarlsonj } 5773431Scarlsonj 5783431Scarlsonj /* 5793431Scarlsonj * Find the next physical interface with the same truncated interface 5803431Scarlsonj * index, and return the first state machine on that. If there are no 5813431Scarlsonj * more physical interfaces that match, then we're done. 5823431Scarlsonj */ 5833431Scarlsonj do { 5843431Scarlsonj pif = lookup_pif_by_uindex(ifindex, pif, isv6); 5853431Scarlsonj if (pif == NULL) 5863431Scarlsonj return (NULL); 5873431Scarlsonj for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) { 5883431Scarlsonj if ((dsmp = lif->lif_smachs) != NULL) 5893431Scarlsonj break; 5903431Scarlsonj } 5913431Scarlsonj } while (dsmp == NULL); 5923431Scarlsonj return (dsmp); 5933431Scarlsonj } 5943431Scarlsonj 5953431Scarlsonj /* 5963431Scarlsonj * lookup_smach_by_xid(): iterate through running state machines by transaction 5973431Scarlsonj * id. Transaction ID zero means "all state machines." 5983431Scarlsonj * 5993431Scarlsonj * input: uint32_t: the transaction id to look up 6003431Scarlsonj * dhcp_smach_t *: the previous state machine, or NULL for start 6013431Scarlsonj * boolean_t: B_TRUE if DHCPv6, B_FALSE otherwise 6023431Scarlsonj * output: dhcp_smach_t *: next state machine, or NULL at end of list 6033431Scarlsonj */ 6043431Scarlsonj 6053431Scarlsonj dhcp_smach_t * 6063431Scarlsonj lookup_smach_by_xid(uint32_t xid, dhcp_smach_t *dsmp, boolean_t isv6) 6073431Scarlsonj { 6083431Scarlsonj for (dsmp = next_smach(dsmp, isv6); dsmp != NULL; 6093431Scarlsonj dsmp = next_smach(dsmp, isv6)) { 6103431Scarlsonj if (xid == 0 || 6113431Scarlsonj pkt_get_xid(dsmp->dsm_send_pkt.pkt, isv6) == xid) 6123431Scarlsonj break; 6133431Scarlsonj } 6143431Scarlsonj 6153431Scarlsonj return (dsmp); 6163431Scarlsonj } 6173431Scarlsonj 6183431Scarlsonj /* 6193431Scarlsonj * lookup_smach_by_event(): find a state machine busy with a particular event 6203431Scarlsonj * ID. This is used only for error handling. 6213431Scarlsonj * 6223431Scarlsonj * input: iu_event_id_t: the event id to look up 6233431Scarlsonj * output: dhcp_smach_t *: matching state machine, or NULL if none 6243431Scarlsonj */ 6253431Scarlsonj 6263431Scarlsonj dhcp_smach_t * 6273431Scarlsonj lookup_smach_by_event(iu_event_id_t eid) 6283431Scarlsonj { 6293431Scarlsonj dhcp_smach_t *dsmp; 6303431Scarlsonj boolean_t isv6 = B_FALSE; 6313431Scarlsonj 6323431Scarlsonj for (;;) { 6333431Scarlsonj for (dsmp = next_smach(NULL, isv6); dsmp != NULL; 6343431Scarlsonj dsmp = next_smach(dsmp, isv6)) { 6353431Scarlsonj if ((dsmp->dsm_dflags & DHCP_IF_BUSY) && 6363431Scarlsonj eid == dsmp->dsm_ia.ia_eid) 6373431Scarlsonj return (dsmp); 6383431Scarlsonj } 6393431Scarlsonj if (isv6) 6403431Scarlsonj break; 6413431Scarlsonj isv6 = B_TRUE; 6423431Scarlsonj } 6433431Scarlsonj 6443431Scarlsonj return (dsmp); 6453431Scarlsonj } 6463431Scarlsonj 6473431Scarlsonj /* 6483431Scarlsonj * cancel_offer_timer(): stop the offer polling timer on a given state machine 6493431Scarlsonj * 6503431Scarlsonj * input: dhcp_smach_t *: state machine on which to stop polling for offers 6513431Scarlsonj * output: none 6523431Scarlsonj */ 6533431Scarlsonj 6543431Scarlsonj void 6553431Scarlsonj cancel_offer_timer(dhcp_smach_t *dsmp) 6563431Scarlsonj { 6573431Scarlsonj int retval; 6583431Scarlsonj 6593431Scarlsonj if (dsmp->dsm_offer_timer != -1) { 6603431Scarlsonj retval = iu_cancel_timer(tq, dsmp->dsm_offer_timer, NULL); 6613431Scarlsonj dsmp->dsm_offer_timer = -1; 6623431Scarlsonj if (retval == 1) 6633431Scarlsonj release_smach(dsmp); 6643431Scarlsonj } 6653431Scarlsonj } 6663431Scarlsonj 6673431Scarlsonj /* 6683431Scarlsonj * cancel_smach_timers(): stop all of the timers related to a given state 6693431Scarlsonj * machine, including lease and LIF expiry. 6703431Scarlsonj * 6713431Scarlsonj * input: dhcp_smach_t *: state machine to cancel 6723431Scarlsonj * output: none 6734106Scarlsonj * note: this function assumes that the iu timer functions are synchronous 6744106Scarlsonj * and thus don't require any protection or ordering on cancellation. 6753431Scarlsonj */ 6763431Scarlsonj 6779508SPeter.Memishian@Sun.COM void 6783431Scarlsonj cancel_smach_timers(dhcp_smach_t *dsmp) 6793431Scarlsonj { 6803431Scarlsonj dhcp_lease_t *dlp; 6813431Scarlsonj dhcp_lif_t *lif; 6823431Scarlsonj uint_t nlifs; 6833431Scarlsonj 6843431Scarlsonj for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) { 6853431Scarlsonj cancel_lease_timers(dlp); 6863431Scarlsonj lif = dlp->dl_lifs; 6873431Scarlsonj nlifs = dlp->dl_nlifs; 6883431Scarlsonj for (; nlifs > 0; nlifs--, lif = lif->lif_next) 6893431Scarlsonj cancel_lif_timers(lif); 6903431Scarlsonj } 6913431Scarlsonj 6923431Scarlsonj cancel_offer_timer(dsmp); 6933431Scarlsonj stop_pkt_retransmission(dsmp); 6944106Scarlsonj if (dsmp->dsm_start_timer != -1) { 6954106Scarlsonj (void) iu_cancel_timer(tq, dsmp->dsm_start_timer, NULL); 6964106Scarlsonj dsmp->dsm_start_timer = -1; 6974106Scarlsonj release_smach(dsmp); 6984106Scarlsonj } 6993431Scarlsonj } 7003431Scarlsonj 7013431Scarlsonj /* 7023431Scarlsonj * remove_smach(): removes a given state machine from the system. marks it 7033431Scarlsonj * for being freed (but may not actually free it). 7043431Scarlsonj * 7053431Scarlsonj * input: dhcp_smach_t *: the state machine to remove 7063431Scarlsonj * output: void 7073431Scarlsonj */ 7083431Scarlsonj 7093522Scarlsonj void 7103431Scarlsonj remove_smach(dhcp_smach_t *dsmp) 7113431Scarlsonj { 7123431Scarlsonj if (dsmp->dsm_dflags & DHCP_IF_REMOVED) 7133431Scarlsonj return; 7143431Scarlsonj 7153431Scarlsonj dhcpmsg(MSG_DEBUG2, "remove_smach: removing %s", dsmp->dsm_name); 7163431Scarlsonj dsmp->dsm_dflags |= DHCP_IF_REMOVED; 7173431Scarlsonj remque(dsmp); 7183431Scarlsonj global_smach_count--; 7193431Scarlsonj 7203431Scarlsonj /* 7213431Scarlsonj * if we have long term timers, cancel them so that state machine 7223431Scarlsonj * resources can be reclaimed in a reasonable amount of time. 7233431Scarlsonj */ 7243431Scarlsonj cancel_smach_timers(dsmp); 7253431Scarlsonj 7263431Scarlsonj /* Drop the hold that the LIF's state machine list had on us */ 7273431Scarlsonj release_smach(dsmp); 7283431Scarlsonj } 7293431Scarlsonj 7303431Scarlsonj /* 7313431Scarlsonj * finished_smach(): we're finished with a given state machine; remove it from 7323431Scarlsonj * the system and tell the user (who may have initiated the 7333431Scarlsonj * removal process). Note that we remove it from the system 7343431Scarlsonj * first to allow back-to-back drop and create invocations. 7353431Scarlsonj * 7363431Scarlsonj * input: dhcp_smach_t *: the state machine to remove 7373431Scarlsonj * int: error for IPC 7383431Scarlsonj * output: void 7393431Scarlsonj */ 7403431Scarlsonj 7413431Scarlsonj void 7423431Scarlsonj finished_smach(dhcp_smach_t *dsmp, int error) 7433431Scarlsonj { 7443431Scarlsonj hold_smach(dsmp); 7453431Scarlsonj remove_smach(dsmp); 7463431Scarlsonj if (dsmp->dsm_ia.ia_fd != -1) 7473431Scarlsonj ipc_action_finish(dsmp, error); 7483431Scarlsonj else 7493431Scarlsonj (void) async_cancel(dsmp); 7503431Scarlsonj release_smach(dsmp); 7513431Scarlsonj } 7523431Scarlsonj 7533431Scarlsonj /* 7545381Smeem * is_bound_state(): checks if a state indicates the client is bound 7555381Smeem * 7565381Smeem * input: DHCPSTATE: the state to check 7575381Smeem * output: boolean_t: B_TRUE if the state is bound, B_FALSE if not 7585381Smeem */ 7595381Smeem 7605381Smeem boolean_t 7615381Smeem is_bound_state(DHCPSTATE state) 7625381Smeem { 7635381Smeem return (state == BOUND || state == REBINDING || state == INFORMATION || 7645381Smeem state == RELEASING || state == INFORM_SENT || state == RENEWING); 7655381Smeem } 7665381Smeem 7675381Smeem /* 7683431Scarlsonj * set_smach_state(): changes state and updates I/O 7693431Scarlsonj * 7703431Scarlsonj * input: dhcp_smach_t *: the state machine to change 7713431Scarlsonj * DHCPSTATE: the new state 7723431Scarlsonj * output: boolean_t: B_TRUE on success, B_FALSE on failure 7733431Scarlsonj */ 7743431Scarlsonj 7753431Scarlsonj boolean_t 7763431Scarlsonj set_smach_state(dhcp_smach_t *dsmp, DHCPSTATE state) 7773431Scarlsonj { 7785381Smeem dhcp_lif_t *lif = dsmp->dsm_lif; 7795381Smeem 7803431Scarlsonj if (dsmp->dsm_state != state) { 7813431Scarlsonj dhcpmsg(MSG_DEBUG, 7823431Scarlsonj "set_smach_state: changing from %s to %s on %s", 7833431Scarlsonj dhcp_state_to_string(dsmp->dsm_state), 7843431Scarlsonj dhcp_state_to_string(state), dsmp->dsm_name); 7853431Scarlsonj 7865381Smeem /* 7875381Smeem * For IPv4, when we're in a bound state our socket must be 7885381Smeem * bound to our address. Otherwise, our socket must be bound 7895381Smeem * to INADDR_ANY. For IPv6, no such change is necessary. 7905381Smeem */ 7913431Scarlsonj if (!dsmp->dsm_isv6) { 7925381Smeem if (is_bound_state(dsmp->dsm_state)) { 7935381Smeem if (!is_bound_state(state)) { 7945381Smeem close_ip_lif(lif); 7958485SPeter.Memishian@Sun.COM if (!open_ip_lif(lif, INADDR_ANY, 7968485SPeter.Memishian@Sun.COM B_FALSE)) 7975381Smeem return (B_FALSE); 7985381Smeem } 7995381Smeem } else { 8005381Smeem if (is_bound_state(state)) { 8015381Smeem close_ip_lif(lif); 8025381Smeem if (!open_ip_lif(lif, 8038485SPeter.Memishian@Sun.COM ntohl(lif->lif_addr), B_FALSE)) 8045381Smeem return (B_FALSE); 8055381Smeem } 8063431Scarlsonj } 8073431Scarlsonj } 8083431Scarlsonj 8093431Scarlsonj dsmp->dsm_state = state; 8103431Scarlsonj } 8113431Scarlsonj return (B_TRUE); 8123431Scarlsonj } 8133431Scarlsonj 8143431Scarlsonj /* 8153431Scarlsonj * duid_retry(): attempt to write DUID again 8163431Scarlsonj * 8173431Scarlsonj * input: iu_tq_t *: ignored 8183431Scarlsonj * void *: ignored 8193431Scarlsonj * output: none 8203431Scarlsonj */ 8213431Scarlsonj 8223431Scarlsonj /* ARGSUSED */ 8233431Scarlsonj static void 8243431Scarlsonj duid_retry(iu_tq_t *tqp, void *arg) 8253431Scarlsonj { 8263431Scarlsonj if (write_stable_duid(global_duid, global_duidlen) == -1) { 8273431Scarlsonj if (errno != EROFS) { 8283431Scarlsonj dhcpmsg(MSG_ERR, 8293431Scarlsonj "duid_retry: unable to write out DUID"); 8303431Scarlsonj } else { 8313431Scarlsonj (void) iu_schedule_timer(tq, 60, duid_retry, NULL); 8323431Scarlsonj } 8333431Scarlsonj } 8343431Scarlsonj } 8353431Scarlsonj 8363431Scarlsonj /* 8373431Scarlsonj * get_smach_cid(): gets the client ID for a given state machine. 8383431Scarlsonj * 8393431Scarlsonj * input: dhcp_smach_t *: the state machine to set up 8403431Scarlsonj * output: int: DHCP_IPC_SUCCESS or one of DHCP_IPC_E_* on failure. 8413431Scarlsonj */ 8423431Scarlsonj 8433431Scarlsonj int 8443431Scarlsonj get_smach_cid(dhcp_smach_t *dsmp) 8453431Scarlsonj { 8463431Scarlsonj uchar_t *client_id; 8473431Scarlsonj uint_t client_id_len; 8483431Scarlsonj dhcp_lif_t *lif = dsmp->dsm_lif; 8493431Scarlsonj dhcp_pif_t *pif = lif->lif_pif; 8503431Scarlsonj const char *value; 8513431Scarlsonj size_t slen; 8523431Scarlsonj 8533431Scarlsonj /* 8543431Scarlsonj * Look in defaults file for the client-id. If present, this takes 8553431Scarlsonj * precedence over all other forms of ID. 8563431Scarlsonj */ 8573431Scarlsonj 8583431Scarlsonj dhcpmsg(MSG_DEBUG, "get_smach_cid: getting default client-id " 8593431Scarlsonj "property on %s", dsmp->dsm_name); 8603431Scarlsonj value = df_get_string(dsmp->dsm_name, pif->pif_isv6, DF_CLIENT_ID); 8613431Scarlsonj if (value != NULL) { 8623431Scarlsonj /* 8633431Scarlsonj * The Client ID string can have one of three basic forms: 8643431Scarlsonj * <decimal>,<data...> 8653431Scarlsonj * 0x<hex...> 8663431Scarlsonj * <string...> 8673431Scarlsonj * 8683431Scarlsonj * The first form is an RFC 3315 DUID. This is legal for both 8693431Scarlsonj * IPv4 DHCP and DHCPv6. For IPv4, an RFC 4361 Client ID is 8703431Scarlsonj * constructed from this value. 8713431Scarlsonj * 8723431Scarlsonj * The second and third forms are legal for IPv4 only. This is 8733431Scarlsonj * a raw Client ID, in hex or ASCII string format. 8743431Scarlsonj */ 8753431Scarlsonj 8763431Scarlsonj if (isdigit(*value) && 8773431Scarlsonj value[strspn(value, "0123456789")] == ',') { 8783431Scarlsonj char *cp; 8793431Scarlsonj ulong_t duidtype; 8803431Scarlsonj ulong_t subtype; 8813431Scarlsonj 8823431Scarlsonj errno = 0; 8833431Scarlsonj duidtype = strtoul(value, &cp, 0); 8843431Scarlsonj if (value == cp || errno != 0 || *cp != ',' || 8853431Scarlsonj duidtype > 65535) { 8863431Scarlsonj dhcpmsg(MSG_ERR, "get_smach_cid: cannot parse " 8873431Scarlsonj "DUID type in %s", value); 8883431Scarlsonj goto no_specified_id; 8893431Scarlsonj } 8903431Scarlsonj value = cp + 1; 8913431Scarlsonj switch (duidtype) { 8923431Scarlsonj case DHCPV6_DUID_LL: 8933431Scarlsonj case DHCPV6_DUID_LLT: { 8943431Scarlsonj int num; 8953431Scarlsonj char chr; 8963431Scarlsonj 8973431Scarlsonj errno = 0; 8983431Scarlsonj subtype = strtoul(value, &cp, 0); 8993431Scarlsonj if (value == cp || errno != 0 || *cp != ',' || 9003431Scarlsonj subtype > 65535) { 9013431Scarlsonj dhcpmsg(MSG_ERR, "get_smach_cid: " 9023431Scarlsonj "cannot parse MAC type in %s", 9033431Scarlsonj value); 9043431Scarlsonj goto no_specified_id; 9053431Scarlsonj } 9063431Scarlsonj value = cp + 1; 9073431Scarlsonj client_id_len = pif->pif_isv6 ? 1 : 5; 9083431Scarlsonj for (; *cp != '\0'; cp++) { 9093431Scarlsonj if (*cp == ':') 9103431Scarlsonj client_id_len++; 9113431Scarlsonj else if (!isxdigit(*cp)) 9123431Scarlsonj break; 9133431Scarlsonj } 9143431Scarlsonj if (duidtype == DHCPV6_DUID_LL) { 9153431Scarlsonj duid_llt_t *dllt; 9163431Scarlsonj time_t now; 9173431Scarlsonj 9183431Scarlsonj client_id_len += sizeof (*dllt); 9193431Scarlsonj dllt = malloc(client_id_len); 9203431Scarlsonj if (dllt == NULL) 9213431Scarlsonj goto alloc_failure; 9223431Scarlsonj dsmp->dsm_cid = (uchar_t *)dllt; 9233431Scarlsonj dllt->dllt_dutype = htons(duidtype); 9243431Scarlsonj dllt->dllt_hwtype = htons(subtype); 9253431Scarlsonj now = time(NULL) - DUID_TIME_BASE; 9263431Scarlsonj dllt->dllt_time = htonl(now); 9273431Scarlsonj cp = (char *)(dllt + 1); 9283431Scarlsonj } else { 9293431Scarlsonj duid_ll_t *dll; 9303431Scarlsonj 9313431Scarlsonj client_id_len += sizeof (*dll); 9323431Scarlsonj dll = malloc(client_id_len); 9333431Scarlsonj if (dll == NULL) 9343431Scarlsonj goto alloc_failure; 9353431Scarlsonj dsmp->dsm_cid = (uchar_t *)dll; 9363431Scarlsonj dll->dll_dutype = htons(duidtype); 9373431Scarlsonj dll->dll_hwtype = htons(subtype); 9383431Scarlsonj cp = (char *)(dll + 1); 9393431Scarlsonj } 9403431Scarlsonj num = 0; 9413431Scarlsonj while ((chr = *value) != '\0') { 9423431Scarlsonj if (isdigit(chr)) { 9433431Scarlsonj num = (num << 4) + chr - '0'; 9443431Scarlsonj } else if (isxdigit(chr)) { 9453431Scarlsonj num = (num << 4) + 10 + chr - 9463431Scarlsonj (isupper(chr) ? 'A' : 'a'); 9473431Scarlsonj } else if (chr == ':') { 9483431Scarlsonj *cp++ = num; 9493431Scarlsonj num = 0; 9503431Scarlsonj } else { 9513431Scarlsonj break; 9523431Scarlsonj } 9533431Scarlsonj } 9543431Scarlsonj break; 9553431Scarlsonj } 9563431Scarlsonj case DHCPV6_DUID_EN: { 9573431Scarlsonj duid_en_t *den; 9583431Scarlsonj 9593431Scarlsonj errno = 0; 9603431Scarlsonj subtype = strtoul(value, &cp, 0); 9613431Scarlsonj if (value == cp || errno != 0 || *cp != ',') { 9623431Scarlsonj dhcpmsg(MSG_ERR, "get_smach_cid: " 9633431Scarlsonj "cannot parse enterprise in %s", 9643431Scarlsonj value); 9653431Scarlsonj goto no_specified_id; 9663431Scarlsonj } 9673431Scarlsonj value = cp + 1; 9683431Scarlsonj slen = strlen(value); 9693431Scarlsonj client_id_len = (slen + 1) / 2; 9703431Scarlsonj den = malloc(sizeof (*den) + client_id_len); 9713431Scarlsonj if (den == NULL) 9723431Scarlsonj goto alloc_failure; 9733431Scarlsonj den->den_dutype = htons(duidtype); 9743431Scarlsonj DHCPV6_SET_ENTNUM(den, subtype); 9753431Scarlsonj if (hexascii_to_octet(value, slen, den + 1, 9763431Scarlsonj &client_id_len) != 0) { 9773431Scarlsonj dhcpmsg(MSG_ERROR, "get_smach_cid: " 9783431Scarlsonj "cannot parse hex string in %s", 9793431Scarlsonj value); 9803431Scarlsonj free(den); 9813431Scarlsonj goto no_specified_id; 9823431Scarlsonj } 9833431Scarlsonj dsmp->dsm_cid = (uchar_t *)den; 9843431Scarlsonj break; 9853431Scarlsonj } 9863431Scarlsonj default: 9873431Scarlsonj slen = strlen(value); 9883431Scarlsonj client_id_len = (slen + 1) / 2; 9893431Scarlsonj cp = malloc(client_id_len); 9903431Scarlsonj if (cp == NULL) 9913431Scarlsonj goto alloc_failure; 9923431Scarlsonj if (hexascii_to_octet(value, slen, cp, 9933431Scarlsonj &client_id_len) != 0) { 9943431Scarlsonj dhcpmsg(MSG_ERROR, "get_smach_cid: " 9953431Scarlsonj "cannot parse hex string in %s", 9963431Scarlsonj value); 9973431Scarlsonj free(cp); 9983431Scarlsonj goto no_specified_id; 9993431Scarlsonj } 10003431Scarlsonj dsmp->dsm_cid = (uchar_t *)cp; 10013431Scarlsonj break; 10023431Scarlsonj } 10033431Scarlsonj dsmp->dsm_cidlen = client_id_len; 10043431Scarlsonj if (!pif->pif_isv6) { 10053431Scarlsonj (void) memmove(dsmp->dsm_cid + 5, 10063431Scarlsonj dsmp->dsm_cid, client_id_len - 5); 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 } 10133431Scarlsonj return (DHCP_IPC_SUCCESS); 10143431Scarlsonj } 10153431Scarlsonj 10163431Scarlsonj if (pif->pif_isv6) { 10173431Scarlsonj dhcpmsg(MSG_ERROR, 10183431Scarlsonj "get_smach_cid: client ID for %s invalid: %s", 10193431Scarlsonj dsmp->dsm_name, value); 10203431Scarlsonj } else if (strncasecmp("0x", value, 2) == 0 && 10213431Scarlsonj value[2] != '\0') { 10223431Scarlsonj /* skip past the 0x and convert the value to binary */ 10233431Scarlsonj value += 2; 10243431Scarlsonj slen = strlen(value); 10253431Scarlsonj client_id_len = (slen + 1) / 2; 10263431Scarlsonj dsmp->dsm_cid = malloc(client_id_len); 10273431Scarlsonj if (dsmp->dsm_cid == NULL) 10283431Scarlsonj goto alloc_failure; 10293431Scarlsonj if (hexascii_to_octet(value, slen, dsmp->dsm_cid, 10303431Scarlsonj &client_id_len) == 0) { 10313431Scarlsonj dsmp->dsm_cidlen = client_id_len; 10323431Scarlsonj return (DHCP_IPC_SUCCESS); 10333431Scarlsonj } 10343431Scarlsonj dhcpmsg(MSG_WARNING, "get_smach_cid: cannot convert " 10353431Scarlsonj "hex value for Client ID on %s", dsmp->dsm_name); 10363431Scarlsonj } else { 10373431Scarlsonj client_id_len = strlen(value); 10383431Scarlsonj dsmp->dsm_cid = malloc(client_id_len); 10393431Scarlsonj if (dsmp->dsm_cid == NULL) 10403431Scarlsonj goto alloc_failure; 10413431Scarlsonj (void) memcpy(dsmp->dsm_cid, value, client_id_len); 10423431Scarlsonj return (DHCP_IPC_SUCCESS); 10433431Scarlsonj } 10443431Scarlsonj } 10453431Scarlsonj no_specified_id: 10463431Scarlsonj 10473431Scarlsonj /* 10483431Scarlsonj * There was either no user-specified Client ID value, or we were 10493431Scarlsonj * unable to parse it. We need to determine if a Client ID is required 10503431Scarlsonj * and, if so, generate one. 10513431Scarlsonj * 10528485SPeter.Memishian@Sun.COM * If it's IPv4, not in an IPMP group, and not a logical interface, 10538485SPeter.Memishian@Sun.COM * then we need to preserve backward-compatibility by avoiding 10548485SPeter.Memishian@Sun.COM * new-fangled DUID/IAID construction. (Note: even for IPMP test 10558485SPeter.Memishian@Sun.COM * addresses, we construct a DUID/IAID since we may renew a lease for 10568485SPeter.Memishian@Sun.COM * an IPMP test address on any functioning IP interface in the group.) 10573431Scarlsonj */ 10588485SPeter.Memishian@Sun.COM if (!pif->pif_isv6 && pif->pif_grifname[0] == '\0' && 10598485SPeter.Memishian@Sun.COM strchr(dsmp->dsm_name, ':') == NULL) { 10603431Scarlsonj if (pif->pif_hwtype == ARPHRD_IB) { 10613431Scarlsonj /* 10623431Scarlsonj * This comes from the DHCP over IPoIB specification. 10633431Scarlsonj * In the absence of an user specified client id, IPoIB 10643431Scarlsonj * automatically uses the required format, with the 10653431Scarlsonj * unique 4 octet value set to 0 (since IPoIB driver 10663431Scarlsonj * allows only a single interface on a port with a 10673431Scarlsonj * specific GID to belong to an IP subnet (PSARC 10683431Scarlsonj * 2001/289, FWARC 2002/702). 10693431Scarlsonj * 10703431Scarlsonj * Type Client-Identifier 10713431Scarlsonj * +-----+-----+-----+-----+-----+----....----+ 10723431Scarlsonj * | 0 | 0 (4 octets) | GID (16 octets)| 10733431Scarlsonj * +-----+-----+-----+-----+-----+----....----+ 10743431Scarlsonj */ 10753431Scarlsonj dsmp->dsm_cidlen = 1 + 4 + 16; 10763431Scarlsonj dsmp->dsm_cid = client_id = malloc(dsmp->dsm_cidlen); 10773431Scarlsonj if (dsmp->dsm_cid == NULL) 10783431Scarlsonj goto alloc_failure; 10793431Scarlsonj 10803431Scarlsonj /* 10813431Scarlsonj * Pick the GID from the mac address. The format 10823431Scarlsonj * of the hardware address is: 10833431Scarlsonj * +-----+-----+-----+-----+----....----+ 10843431Scarlsonj * | QPN (4 octets) | GID (16 octets)| 10853431Scarlsonj * +-----+-----+-----+-----+----....----+ 10863431Scarlsonj */ 10873431Scarlsonj (void) memcpy(client_id + 5, pif->pif_hwaddr + 4, 10883431Scarlsonj pif->pif_hwlen - 4); 10893431Scarlsonj (void) memset(client_id, 0, 5); 10903431Scarlsonj } 10913431Scarlsonj return (DHCP_IPC_SUCCESS); 10923431Scarlsonj } 10933431Scarlsonj 10943431Scarlsonj /* 10953431Scarlsonj * Now check for a saved DUID. If there is one, then use it. If there 10963431Scarlsonj * isn't, then generate a new one. For IPv4, we need to construct the 10973431Scarlsonj * RFC 4361 Client ID with this value and the LIF's IAID. 10983431Scarlsonj */ 10993431Scarlsonj if (global_duid == NULL && 11003431Scarlsonj (global_duid = read_stable_duid(&global_duidlen)) == NULL) { 11013431Scarlsonj global_duid = make_stable_duid(pif->pif_name, &global_duidlen); 11023431Scarlsonj if (global_duid == NULL) 11033431Scarlsonj goto alloc_failure; 11043431Scarlsonj duid_retry(NULL, NULL); 11053431Scarlsonj } 11063431Scarlsonj 11073431Scarlsonj if (pif->pif_isv6) { 11083431Scarlsonj dsmp->dsm_cid = malloc(global_duidlen); 11093431Scarlsonj if (dsmp->dsm_cid == NULL) 11103431Scarlsonj goto alloc_failure; 11113431Scarlsonj (void) memcpy(dsmp->dsm_cid, global_duid, global_duidlen); 11123431Scarlsonj dsmp->dsm_cidlen = global_duidlen; 11133431Scarlsonj } else { 11143431Scarlsonj dsmp->dsm_cid = malloc(5 + global_duidlen); 11153431Scarlsonj if (dsmp->dsm_cid == NULL) 11163431Scarlsonj goto alloc_failure; 11173431Scarlsonj dsmp->dsm_cid[0] = 255; 11183431Scarlsonj dsmp->dsm_cid[1] = lif->lif_iaid >> 24; 11193431Scarlsonj dsmp->dsm_cid[2] = lif->lif_iaid >> 16; 11203431Scarlsonj dsmp->dsm_cid[3] = lif->lif_iaid >> 8; 11213431Scarlsonj dsmp->dsm_cid[4] = lif->lif_iaid; 11223431Scarlsonj (void) memcpy(dsmp->dsm_cid + 5, global_duid, global_duidlen); 11233431Scarlsonj dsmp->dsm_cidlen = 5 + global_duidlen; 11243431Scarlsonj } 11253431Scarlsonj 11263431Scarlsonj return (DHCP_IPC_SUCCESS); 11273431Scarlsonj 11283431Scarlsonj alloc_failure: 11293431Scarlsonj dhcpmsg(MSG_ERR, "get_smach_cid: cannot allocate Client Id for %s", 11303431Scarlsonj dsmp->dsm_name); 11313431Scarlsonj return (DHCP_IPC_E_MEMORY); 11323431Scarlsonj } 11333431Scarlsonj 11343431Scarlsonj /* 11353431Scarlsonj * smach_count(): returns the number of state machines running 11363431Scarlsonj * 11373431Scarlsonj * input: void 11383431Scarlsonj * output: uint_t: the number of state machines 11393431Scarlsonj */ 11403431Scarlsonj 11413431Scarlsonj uint_t 11423431Scarlsonj smach_count(void) 11433431Scarlsonj { 11443431Scarlsonj return (global_smach_count); 11453431Scarlsonj } 11463431Scarlsonj 11473431Scarlsonj /* 11484106Scarlsonj * discard_default_routes(): removes a state machine's default routes alone. 11494106Scarlsonj * 11504106Scarlsonj * input: dhcp_smach_t *: the state machine whose default routes need to be 11514106Scarlsonj * discarded 11524106Scarlsonj * output: void 11534106Scarlsonj */ 11544106Scarlsonj 11554106Scarlsonj void 11564106Scarlsonj discard_default_routes(dhcp_smach_t *dsmp) 11574106Scarlsonj { 11584106Scarlsonj free(dsmp->dsm_routers); 11594106Scarlsonj dsmp->dsm_routers = NULL; 11604106Scarlsonj dsmp->dsm_nrouters = 0; 11614106Scarlsonj } 11624106Scarlsonj 11634106Scarlsonj /* 11644106Scarlsonj * remove_default_routes(): removes a state machine's default routes from the 11654106Scarlsonj * kernel and from the state machine. 11663431Scarlsonj * 11673431Scarlsonj * input: dhcp_smach_t *: the state machine whose default routes need to be 11683431Scarlsonj * removed 11693431Scarlsonj * output: void 11703431Scarlsonj */ 11713431Scarlsonj 11723431Scarlsonj void 11733431Scarlsonj remove_default_routes(dhcp_smach_t *dsmp) 11743431Scarlsonj { 11753431Scarlsonj int idx; 11764106Scarlsonj uint32_t ifindex; 11773431Scarlsonj 11783431Scarlsonj if (dsmp->dsm_routers != NULL) { 11794106Scarlsonj ifindex = dsmp->dsm_lif->lif_pif->pif_index; 11803431Scarlsonj for (idx = dsmp->dsm_nrouters - 1; idx >= 0; idx--) { 11814106Scarlsonj if (del_default_route(ifindex, 11823431Scarlsonj &dsmp->dsm_routers[idx])) { 11833431Scarlsonj dhcpmsg(MSG_DEBUG, "remove_default_routes: " 11843431Scarlsonj "removed %s from %s", 11853431Scarlsonj inet_ntoa(dsmp->dsm_routers[idx]), 11863431Scarlsonj dsmp->dsm_name); 11873431Scarlsonj } else { 11883431Scarlsonj dhcpmsg(MSG_INFO, "remove_default_routes: " 11893431Scarlsonj "unable to remove %s from %s", 11903431Scarlsonj inet_ntoa(dsmp->dsm_routers[idx]), 11913431Scarlsonj dsmp->dsm_name); 11923431Scarlsonj } 11933431Scarlsonj } 11944106Scarlsonj discard_default_routes(dsmp); 11953431Scarlsonj } 11963431Scarlsonj } 11973431Scarlsonj 11983431Scarlsonj /* 11993431Scarlsonj * reset_smach(): resets a state machine to its initial state 12003431Scarlsonj * 12013431Scarlsonj * input: dhcp_smach_t *: the state machine to reset 12023431Scarlsonj * output: void 12033431Scarlsonj */ 12043431Scarlsonj 12053431Scarlsonj void 12063431Scarlsonj reset_smach(dhcp_smach_t *dsmp) 12073431Scarlsonj { 12083431Scarlsonj dsmp->dsm_dflags &= ~DHCP_IF_FAILED; 12093431Scarlsonj 12103431Scarlsonj remove_default_routes(dsmp); 12113431Scarlsonj 12123431Scarlsonj free_pkt_list(&dsmp->dsm_recv_pkt_list); 12139508SPeter.Memishian@Sun.COM free_pkt_entry(dsmp->dsm_ack); 12143431Scarlsonj if (dsmp->dsm_orig_ack != dsmp->dsm_ack) 12153431Scarlsonj free_pkt_entry(dsmp->dsm_orig_ack); 12169508SPeter.Memishian@Sun.COM dsmp->dsm_ack = dsmp->dsm_orig_ack = NULL; 12173431Scarlsonj 12189508SPeter.Memishian@Sun.COM free(dsmp->dsm_reqhost); 12199508SPeter.Memishian@Sun.COM dsmp->dsm_reqhost = NULL; 12203431Scarlsonj 12213431Scarlsonj cancel_smach_timers(dsmp); 12223431Scarlsonj 12233431Scarlsonj (void) set_smach_state(dsmp, INIT); 12243431Scarlsonj if (dsmp->dsm_isv6) { 12253431Scarlsonj dsmp->dsm_server = ipv6_all_dhcp_relay_and_servers; 12263431Scarlsonj } else { 12273431Scarlsonj IN6_IPADDR_TO_V4MAPPED(htonl(INADDR_BROADCAST), 12283431Scarlsonj &dsmp->dsm_server); 12293431Scarlsonj } 12309508SPeter.Memishian@Sun.COM dsmp->dsm_neg_hrtime = gethrtime(); 12319508SPeter.Memishian@Sun.COM /* 12329508SPeter.Memishian@Sun.COM * We must never get here with a script running, since it means we're 12339508SPeter.Memishian@Sun.COM * resetting an smach that is still in the middle of another state 12349508SPeter.Memishian@Sun.COM * transition with a pending dsm_script_callback. 12359508SPeter.Memishian@Sun.COM */ 12369508SPeter.Memishian@Sun.COM assert(dsmp->dsm_script_pid == -1); 12373431Scarlsonj } 12383431Scarlsonj 12393431Scarlsonj /* 12403431Scarlsonj * refresh_smach(): refreshes a given state machine, as though awakened from 12413431Scarlsonj * hibernation or by lower layer "link up." 12423431Scarlsonj * 12433431Scarlsonj * input: dhcp_smach_t *: state machine to refresh 12443431Scarlsonj * output: void 12453431Scarlsonj */ 12463431Scarlsonj 12473431Scarlsonj void 12483431Scarlsonj refresh_smach(dhcp_smach_t *dsmp) 12493431Scarlsonj { 12503431Scarlsonj if (dsmp->dsm_state == BOUND || dsmp->dsm_state == RENEWING || 12514106Scarlsonj dsmp->dsm_state == REBINDING || dsmp->dsm_state == INFORMATION) { 12524106Scarlsonj dhcpmsg(MSG_WARNING, "refreshing state on %s", dsmp->dsm_name); 12533431Scarlsonj cancel_smach_timers(dsmp); 12544106Scarlsonj if (dsmp->dsm_state == INFORMATION) 12554106Scarlsonj dhcp_inform(dsmp); 12564106Scarlsonj else 12574106Scarlsonj dhcp_init_reboot(dsmp); 12583431Scarlsonj } 12593431Scarlsonj } 12603431Scarlsonj 12613431Scarlsonj /* 12623431Scarlsonj * refresh_smachs(): refreshes all finite leases under DHCP control 12633431Scarlsonj * 12643431Scarlsonj * input: iu_eh_t *: unused 12653431Scarlsonj * int: unused 12663431Scarlsonj * void *: unused 12673431Scarlsonj * output: void 12683431Scarlsonj */ 12693431Scarlsonj 12703431Scarlsonj /* ARGSUSED */ 12713431Scarlsonj void 12723431Scarlsonj refresh_smachs(iu_eh_t *eh, int sig, void *arg) 12733431Scarlsonj { 12743431Scarlsonj boolean_t isv6 = B_FALSE; 12753431Scarlsonj dhcp_smach_t *dsmp; 12763431Scarlsonj 12773431Scarlsonj for (;;) { 12783431Scarlsonj for (dsmp = next_smach(NULL, isv6); dsmp != NULL; 12793431Scarlsonj dsmp = next_smach(dsmp, isv6)) { 12803431Scarlsonj refresh_smach(dsmp); 12813431Scarlsonj } 12823431Scarlsonj if (isv6) 12833431Scarlsonj break; 12843431Scarlsonj isv6 = B_TRUE; 12853431Scarlsonj } 12863431Scarlsonj } 12873431Scarlsonj 12883431Scarlsonj /* 12893431Scarlsonj * nuke_smach_list(): delete the state machine list. For use when the 12903431Scarlsonj * dhcpagent is exiting. 12913431Scarlsonj * 12923431Scarlsonj * input: none 12933431Scarlsonj * output: none 12943431Scarlsonj */ 12953431Scarlsonj 12963431Scarlsonj void 12973431Scarlsonj nuke_smach_list(void) 12983431Scarlsonj { 12993431Scarlsonj boolean_t isv6 = B_FALSE; 13003431Scarlsonj dhcp_smach_t *dsmp, *dsmp_next; 13013431Scarlsonj 13023431Scarlsonj for (;;) { 13033431Scarlsonj for (dsmp = next_smach(NULL, isv6); dsmp != NULL; 13043431Scarlsonj dsmp = dsmp_next) { 13053431Scarlsonj int status; 13063431Scarlsonj 13073431Scarlsonj dsmp_next = next_smach(dsmp, isv6); 13083431Scarlsonj 13096508Scarlsonj /* If we're already dropping or releasing, skip */ 13106508Scarlsonj if (dsmp->dsm_droprelease) 13116508Scarlsonj continue; 13126508Scarlsonj dsmp->dsm_droprelease = B_TRUE; 13136508Scarlsonj 13143431Scarlsonj cancel_smach_timers(dsmp); 13153431Scarlsonj 13163431Scarlsonj /* 13173431Scarlsonj * If the script is started by script_start, dhcp_drop 13183431Scarlsonj * and dhcp_release should and will only be called 13193431Scarlsonj * after the script exits. 13203431Scarlsonj */ 13213431Scarlsonj if (df_get_bool(dsmp->dsm_name, isv6, 1322*9633Sjames.d.carlson@sun.com DF_RELEASE_ON_SIGTERM) || 1323*9633Sjames.d.carlson@sun.com df_get_bool(dsmp->dsm_name, isv6, 1324*9633Sjames.d.carlson@sun.com DF_VERIFIED_LEASE_ONLY)) { 13256508Scarlsonj if (script_start(dsmp, isv6 ? EVENT_RELEASE6 : 13266508Scarlsonj EVENT_RELEASE, dhcp_release, 13276508Scarlsonj "DHCP agent is exiting", &status)) { 13283431Scarlsonj continue; 13293431Scarlsonj } 13303431Scarlsonj if (status == 1) 13313431Scarlsonj continue; 13323431Scarlsonj } 13336508Scarlsonj (void) script_start(dsmp, isv6 ? EVENT_DROP6 : 13346508Scarlsonj EVENT_DROP, dhcp_drop, NULL, NULL); 13353431Scarlsonj } 13363431Scarlsonj if (isv6) 13373431Scarlsonj break; 13383431Scarlsonj isv6 = B_TRUE; 13393431Scarlsonj } 13403431Scarlsonj } 13413431Scarlsonj 13423431Scarlsonj /* 13433431Scarlsonj * insert_lease(): Create a lease structure on a given state machine. The 13443431Scarlsonj * lease holds a reference to the state machine. 13453431Scarlsonj * 13463431Scarlsonj * input: dhcp_smach_t *: state machine 13473431Scarlsonj * output: dhcp_lease_t *: newly-created lease 13483431Scarlsonj */ 13493431Scarlsonj 13503431Scarlsonj dhcp_lease_t * 13513431Scarlsonj insert_lease(dhcp_smach_t *dsmp) 13523431Scarlsonj { 13533431Scarlsonj dhcp_lease_t *dlp; 13543431Scarlsonj 13553431Scarlsonj if ((dlp = calloc(1, sizeof (*dlp))) == NULL) 13563431Scarlsonj return (NULL); 13573431Scarlsonj dlp->dl_smach = dsmp; 13583431Scarlsonj dlp->dl_hold_count = 1; 13593431Scarlsonj init_timer(&dlp->dl_t1, 0); 13603431Scarlsonj init_timer(&dlp->dl_t2, 0); 13613431Scarlsonj insque(dlp, &dsmp->dsm_leases); 13623431Scarlsonj dhcpmsg(MSG_DEBUG2, "insert_lease: new lease for %s", dsmp->dsm_name); 13633431Scarlsonj return (dlp); 13643431Scarlsonj } 13653431Scarlsonj 13663431Scarlsonj /* 13673431Scarlsonj * hold_lease(): acquires a hold on a lease 13683431Scarlsonj * 13693431Scarlsonj * input: dhcp_lease_t *: the lease to acquire a hold on 13703431Scarlsonj * output: void 13713431Scarlsonj */ 13723431Scarlsonj 13733431Scarlsonj void 13743431Scarlsonj hold_lease(dhcp_lease_t *dlp) 13753431Scarlsonj { 13763431Scarlsonj dlp->dl_hold_count++; 13773431Scarlsonj 13783431Scarlsonj dhcpmsg(MSG_DEBUG2, "hold_lease: hold count on lease for %s: %d", 13793431Scarlsonj dlp->dl_smach->dsm_name, dlp->dl_hold_count); 13803431Scarlsonj } 13813431Scarlsonj 13823431Scarlsonj /* 13833431Scarlsonj * release_lease(): releases a hold previously acquired on a lease. 13843431Scarlsonj * If the hold count reaches 0, the lease is freed. 13853431Scarlsonj * 13863431Scarlsonj * input: dhcp_lease_t *: the lease to release the hold on 13873431Scarlsonj * output: void 13883431Scarlsonj */ 13893431Scarlsonj 13903431Scarlsonj void 13913431Scarlsonj release_lease(dhcp_lease_t *dlp) 13923431Scarlsonj { 13933431Scarlsonj if (dlp->dl_hold_count == 0) { 13943431Scarlsonj dhcpmsg(MSG_CRIT, "release_lease: extraneous release"); 13953431Scarlsonj return; 13963431Scarlsonj } 13973431Scarlsonj 13983431Scarlsonj if (dlp->dl_hold_count == 1 && !dlp->dl_removed) { 13993431Scarlsonj dhcpmsg(MSG_CRIT, "release_lease: missing removal"); 14003431Scarlsonj return; 14013431Scarlsonj } 14023431Scarlsonj 14033431Scarlsonj if (--dlp->dl_hold_count == 0) { 14043431Scarlsonj dhcpmsg(MSG_DEBUG, 14053431Scarlsonj "release_lease: freeing lease on state machine %s", 14063431Scarlsonj dlp->dl_smach->dsm_name); 14073431Scarlsonj free(dlp); 14083431Scarlsonj } else { 14093431Scarlsonj dhcpmsg(MSG_DEBUG2, 14103431Scarlsonj "release_lease: hold count on lease for %s: %d", 14113431Scarlsonj dlp->dl_smach->dsm_name, dlp->dl_hold_count); 14123431Scarlsonj } 14133431Scarlsonj } 14143431Scarlsonj 14153431Scarlsonj /* 14163431Scarlsonj * remove_lease(): removes a given lease from the state machine and drops the 14173431Scarlsonj * state machine's hold on the lease. 14183431Scarlsonj * 14193431Scarlsonj * input: dhcp_lease_t *: the lease to remove 14203431Scarlsonj * output: void 14213431Scarlsonj */ 14223431Scarlsonj 14233431Scarlsonj void 14243431Scarlsonj remove_lease(dhcp_lease_t *dlp) 14253431Scarlsonj { 14263431Scarlsonj if (dlp->dl_removed) { 14273431Scarlsonj dhcpmsg(MSG_CRIT, "remove_lease: extraneous removal"); 14283431Scarlsonj } else { 14293431Scarlsonj dhcp_lif_t *lif, *lifnext; 14303431Scarlsonj uint_t nlifs; 14313431Scarlsonj 14323431Scarlsonj dhcpmsg(MSG_DEBUG, 14333431Scarlsonj "remove_lease: removed lease from state machine %s", 14343431Scarlsonj dlp->dl_smach->dsm_name); 14353431Scarlsonj dlp->dl_removed = B_TRUE; 14363431Scarlsonj remque(dlp); 14373431Scarlsonj 14383431Scarlsonj cancel_lease_timers(dlp); 14393431Scarlsonj 14403431Scarlsonj lif = dlp->dl_lifs; 14413431Scarlsonj nlifs = dlp->dl_nlifs; 14423431Scarlsonj for (; nlifs > 0; nlifs--, lif = lifnext) { 14433431Scarlsonj lifnext = lif->lif_next; 14443431Scarlsonj unplumb_lif(lif); 14453431Scarlsonj } 14463431Scarlsonj 14473431Scarlsonj release_lease(dlp); 14483431Scarlsonj } 14493431Scarlsonj } 14503431Scarlsonj 14513431Scarlsonj /* 14523431Scarlsonj * cancel_lease_timer(): cancels a lease-related timer 14533431Scarlsonj * 14543431Scarlsonj * input: dhcp_lease_t *: the lease to operate on 14553431Scarlsonj * dhcp_timer_t *: the timer to cancel 14563431Scarlsonj * output: void 14573431Scarlsonj */ 14583431Scarlsonj 14593431Scarlsonj static void 14603431Scarlsonj cancel_lease_timer(dhcp_lease_t *dlp, dhcp_timer_t *dt) 14613431Scarlsonj { 14623431Scarlsonj if (dt->dt_id == -1) 14633431Scarlsonj return; 14643431Scarlsonj if (cancel_timer(dt)) { 14653431Scarlsonj release_lease(dlp); 14663431Scarlsonj } else { 14673431Scarlsonj dhcpmsg(MSG_WARNING, 14683431Scarlsonj "cancel_lease_timer: cannot cancel timer"); 14693431Scarlsonj } 14703431Scarlsonj } 14713431Scarlsonj 14723431Scarlsonj /* 14733431Scarlsonj * cancel_lease_timers(): cancels an lease's pending timers 14743431Scarlsonj * 14753431Scarlsonj * input: dhcp_lease_t *: the lease to operate on 14763431Scarlsonj * output: void 14773431Scarlsonj */ 14783431Scarlsonj 14793431Scarlsonj void 14803431Scarlsonj cancel_lease_timers(dhcp_lease_t *dlp) 14813431Scarlsonj { 14823431Scarlsonj cancel_lease_timer(dlp, &dlp->dl_t1); 14833431Scarlsonj cancel_lease_timer(dlp, &dlp->dl_t2); 14843431Scarlsonj } 14853431Scarlsonj 14863431Scarlsonj /* 14873431Scarlsonj * schedule_lease_timer(): schedules a lease-related timer 14883431Scarlsonj * 14893431Scarlsonj * input: dhcp_lease_t *: the lease to operate on 14903431Scarlsonj * dhcp_timer_t *: the timer to schedule 14913431Scarlsonj * iu_tq_callback_t *: the callback to call upon firing 14923431Scarlsonj * output: boolean_t: B_TRUE if the timer was scheduled successfully 14933431Scarlsonj */ 14943431Scarlsonj 14953431Scarlsonj boolean_t 14963431Scarlsonj schedule_lease_timer(dhcp_lease_t *dlp, dhcp_timer_t *dt, 14973431Scarlsonj iu_tq_callback_t *expire) 14983431Scarlsonj { 14993431Scarlsonj /* 15003431Scarlsonj * If there's a timer running, cancel it and release its lease 15013431Scarlsonj * reference. 15023431Scarlsonj */ 15033431Scarlsonj if (dt->dt_id != -1) { 15043431Scarlsonj if (!cancel_timer(dt)) 15053431Scarlsonj return (B_FALSE); 15063431Scarlsonj release_lease(dlp); 15073431Scarlsonj } 15083431Scarlsonj 15093431Scarlsonj if (schedule_timer(dt, expire, dlp)) { 15103431Scarlsonj hold_lease(dlp); 15113431Scarlsonj return (B_TRUE); 15123431Scarlsonj } else { 15133431Scarlsonj dhcpmsg(MSG_WARNING, 15143431Scarlsonj "schedule_lease_timer: cannot schedule timer"); 15153431Scarlsonj return (B_FALSE); 15163431Scarlsonj } 15173431Scarlsonj } 15183431Scarlsonj 15193431Scarlsonj /* 15203431Scarlsonj * deprecate_leases(): remove all of the leases from a given state machine 15213431Scarlsonj * 15223431Scarlsonj * input: dhcp_smach_t *: the state machine 15233431Scarlsonj * output: none 15243431Scarlsonj */ 15253431Scarlsonj 15263431Scarlsonj void 15273431Scarlsonj deprecate_leases(dhcp_smach_t *dsmp) 15283431Scarlsonj { 15293431Scarlsonj dhcp_lease_t *dlp; 15303431Scarlsonj 15313431Scarlsonj /* 15323431Scarlsonj * note that due to infelicities in the routing code, any default 15333431Scarlsonj * routes must be removed prior to canonizing or deprecating the LIF. 15343431Scarlsonj */ 15353431Scarlsonj 15363431Scarlsonj remove_default_routes(dsmp); 15373431Scarlsonj 15383431Scarlsonj while ((dlp = dsmp->dsm_leases) != NULL) 15393431Scarlsonj remove_lease(dlp); 15403431Scarlsonj } 15413431Scarlsonj 15423431Scarlsonj /* 15433431Scarlsonj * verify_smach(): if the state machine is in a bound state, then verify the 15443431Scarlsonj * standing of the configured interfaces. Abandon those that 15453431Scarlsonj * the user has modified. If we end up with no valid leases, 15463431Scarlsonj * then just terminate the state machine. 15473431Scarlsonj * 15483431Scarlsonj * input: dhcp_smach_t *: the state machine 15493431Scarlsonj * output: boolean_t: B_TRUE if the state machine is still valid. 15503431Scarlsonj * note: assumes caller holds a state machine reference; as with most 15513431Scarlsonj * callback functions. 15523431Scarlsonj */ 15533431Scarlsonj 15543431Scarlsonj boolean_t 15553431Scarlsonj verify_smach(dhcp_smach_t *dsmp) 15563431Scarlsonj { 15573431Scarlsonj dhcp_lease_t *dlp, *dlpn; 15583431Scarlsonj 15593431Scarlsonj if (dsmp->dsm_dflags & DHCP_IF_REMOVED) { 15603431Scarlsonj release_smach(dsmp); 15613431Scarlsonj return (B_FALSE); 15623431Scarlsonj } 15633431Scarlsonj 15643431Scarlsonj if (!dsmp->dsm_isv6) { 15653431Scarlsonj /* 15663431Scarlsonj * If this is DHCPv4, then verify the main LIF. 15673431Scarlsonj */ 15683431Scarlsonj if (!verify_lif(dsmp->dsm_lif)) 15693431Scarlsonj goto smach_terminate; 15703431Scarlsonj } 15713431Scarlsonj 15723431Scarlsonj /* 15733431Scarlsonj * If we're not in one of the bound states, then there are no LIFs to 15743431Scarlsonj * verify here. 15753431Scarlsonj */ 15763431Scarlsonj if (dsmp->dsm_state != BOUND && 15773431Scarlsonj dsmp->dsm_state != RENEWING && 15783431Scarlsonj dsmp->dsm_state != REBINDING) { 15793431Scarlsonj release_smach(dsmp); 15803431Scarlsonj return (B_TRUE); 15813431Scarlsonj } 15823431Scarlsonj 15833431Scarlsonj for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlpn) { 15843431Scarlsonj dhcp_lif_t *lif, *lifnext; 15853431Scarlsonj uint_t nlifs; 15863431Scarlsonj 15873431Scarlsonj dlpn = dlp->dl_next; 15883431Scarlsonj lif = dlp->dl_lifs; 15893431Scarlsonj nlifs = dlp->dl_nlifs; 15903431Scarlsonj for (; nlifs > 0; lif = lifnext, nlifs--) { 15913431Scarlsonj lifnext = lif->lif_next; 15923431Scarlsonj if (!verify_lif(lif)) { 15933431Scarlsonj /* 15943431Scarlsonj * User has manipulated the interface. Even 15953431Scarlsonj * if we plumbed it, we must now disown it. 15963431Scarlsonj */ 15973431Scarlsonj lif->lif_plumbed = B_FALSE; 15983431Scarlsonj remove_lif(lif); 15993431Scarlsonj } 16003431Scarlsonj } 16013431Scarlsonj if (dlp->dl_nlifs == 0) 16023431Scarlsonj remove_lease(dlp); 16033431Scarlsonj } 16043431Scarlsonj 16053431Scarlsonj /* 16063431Scarlsonj * If there are leases left, then everything's ok. 16073431Scarlsonj */ 16083431Scarlsonj if (dsmp->dsm_leases != NULL) { 16093431Scarlsonj release_smach(dsmp); 16103431Scarlsonj return (B_TRUE); 16113431Scarlsonj } 16123431Scarlsonj 16133431Scarlsonj smach_terminate: 16143431Scarlsonj finished_smach(dsmp, DHCP_IPC_E_UNKIF); 16153431Scarlsonj release_smach(dsmp); 16163431Scarlsonj 16173431Scarlsonj return (B_FALSE); 16183431Scarlsonj } 1619