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 /*
22*12989SVasumathi.Sundaram@oracle.COM * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
233431Scarlsonj *
243431Scarlsonj * This module contains core functions for managing DHCP state machine
253431Scarlsonj * instances.
263431Scarlsonj */
273431Scarlsonj
289508SPeter.Memishian@Sun.COM #include <assert.h>
293431Scarlsonj #include <stdlib.h>
303431Scarlsonj #include <search.h>
313431Scarlsonj #include <string.h>
323431Scarlsonj #include <ctype.h>
333431Scarlsonj #include <sys/types.h>
343431Scarlsonj #include <sys/socket.h>
353431Scarlsonj #include <netinet/in.h>
363431Scarlsonj #include <netinet/arp.h>
373431Scarlsonj #include <arpa/inet.h>
383431Scarlsonj #include <dhcpmsg.h>
393431Scarlsonj #include <dhcpagent_util.h>
403431Scarlsonj #include <dhcp_stable.h>
419633Sjames.d.carlson@sun.com #include <dhcp_inittab.h>
423431Scarlsonj
433431Scarlsonj #include "agent.h"
443431Scarlsonj #include "states.h"
453431Scarlsonj #include "interface.h"
463431Scarlsonj #include "defaults.h"
473431Scarlsonj #include "script_handler.h"
483431Scarlsonj
493431Scarlsonj static uint_t global_smach_count;
503431Scarlsonj
513431Scarlsonj static uchar_t *global_duid;
523431Scarlsonj static size_t global_duidlen;
533431Scarlsonj
543431Scarlsonj /*
553431Scarlsonj * iaid_retry(): attempt to write LIF IAID again
563431Scarlsonj *
573431Scarlsonj * input: iu_tq_t *: ignored
583431Scarlsonj * void *: pointer to LIF
593431Scarlsonj * output: none
603431Scarlsonj */
613431Scarlsonj
623431Scarlsonj /* ARGSUSED */
633431Scarlsonj static void
iaid_retry(iu_tq_t * tqp,void * arg)643431Scarlsonj iaid_retry(iu_tq_t *tqp, void *arg)
653431Scarlsonj {
663431Scarlsonj dhcp_lif_t *lif = arg;
673431Scarlsonj
683431Scarlsonj if (write_stable_iaid(lif->lif_name, lif->lif_iaid) == -1) {
693431Scarlsonj if (errno != EROFS) {
703431Scarlsonj dhcpmsg(MSG_ERR,
713431Scarlsonj "iaid_retry: unable to write out IAID for %s",
723431Scarlsonj lif->lif_name);
733431Scarlsonj release_lif(lif);
743431Scarlsonj } else {
753431Scarlsonj lif->lif_iaid_id = iu_schedule_timer(tq, 60,
763431Scarlsonj iaid_retry, lif);
773431Scarlsonj }
783431Scarlsonj } else {
793431Scarlsonj release_lif(lif);
803431Scarlsonj }
813431Scarlsonj }
823431Scarlsonj
833431Scarlsonj /*
849633Sjames.d.carlson@sun.com * parse_param_list(): parse a parameter list.
859633Sjames.d.carlson@sun.com *
869633Sjames.d.carlson@sun.com * input: const char *: parameter list string with comma-separated entries
879633Sjames.d.carlson@sun.com * uint_t *: return parameter; number of entries decoded
889633Sjames.d.carlson@sun.com * const char *: name of parameter list for logging purposes
899633Sjames.d.carlson@sun.com * dhcp_smach_t *: smach pointer for logging
909633Sjames.d.carlson@sun.com * output: uint16_t *: allocated array of parameters, or NULL if none.
919633Sjames.d.carlson@sun.com */
929633Sjames.d.carlson@sun.com
939633Sjames.d.carlson@sun.com static uint16_t *
parse_param_list(const char * param_list,uint_t * param_cnt,const char * param_name,dhcp_smach_t * dsmp)949633Sjames.d.carlson@sun.com parse_param_list(const char *param_list, uint_t *param_cnt,
959633Sjames.d.carlson@sun.com const char *param_name, dhcp_smach_t *dsmp)
969633Sjames.d.carlson@sun.com {
979633Sjames.d.carlson@sun.com int i, maxparam;
989633Sjames.d.carlson@sun.com char tsym[DSYM_MAX_SYM_LEN + 1];
999633Sjames.d.carlson@sun.com uint16_t *params;
1009633Sjames.d.carlson@sun.com const char *cp;
1019633Sjames.d.carlson@sun.com dhcp_symbol_t *entry;
1029633Sjames.d.carlson@sun.com
1039633Sjames.d.carlson@sun.com *param_cnt = 0;
1049633Sjames.d.carlson@sun.com
1059633Sjames.d.carlson@sun.com if (param_list == NULL)
1069633Sjames.d.carlson@sun.com return (NULL);
1079633Sjames.d.carlson@sun.com
1089633Sjames.d.carlson@sun.com for (maxparam = 1, i = 0; param_list[i] != '\0'; i++) {
1099633Sjames.d.carlson@sun.com if (param_list[i] == ',')
1109633Sjames.d.carlson@sun.com maxparam++;
1119633Sjames.d.carlson@sun.com }
1129633Sjames.d.carlson@sun.com
1139633Sjames.d.carlson@sun.com params = malloc(maxparam * sizeof (*params));
1149633Sjames.d.carlson@sun.com if (params == NULL) {
1159633Sjames.d.carlson@sun.com dhcpmsg(MSG_WARNING,
1169633Sjames.d.carlson@sun.com "cannot allocate parameter %s list for %s (continuing)",
1179633Sjames.d.carlson@sun.com param_name, dsmp->dsm_name);
1189633Sjames.d.carlson@sun.com return (NULL);
1199633Sjames.d.carlson@sun.com }
1209633Sjames.d.carlson@sun.com
1219633Sjames.d.carlson@sun.com for (i = 0; i < maxparam; ) {
1229633Sjames.d.carlson@sun.com
1239633Sjames.d.carlson@sun.com if (isspace(*param_list))
1249633Sjames.d.carlson@sun.com param_list++;
1259633Sjames.d.carlson@sun.com
1269633Sjames.d.carlson@sun.com /* extract the next element on the list */
1279633Sjames.d.carlson@sun.com cp = strchr(param_list, ',');
1289633Sjames.d.carlson@sun.com if (cp == NULL || cp - param_list >= sizeof (tsym))
1299633Sjames.d.carlson@sun.com (void) strlcpy(tsym, param_list, sizeof (tsym));
1309633Sjames.d.carlson@sun.com else
1319633Sjames.d.carlson@sun.com (void) strlcpy(tsym, param_list, cp - param_list + 1);
1329633Sjames.d.carlson@sun.com
1339633Sjames.d.carlson@sun.com /* LINTED -- do nothing with blanks on purpose */
1349633Sjames.d.carlson@sun.com if (tsym[0] == '\0') {
1359633Sjames.d.carlson@sun.com ;
1369633Sjames.d.carlson@sun.com } else if (isalpha(tsym[0])) {
1379633Sjames.d.carlson@sun.com entry = inittab_getbyname(ITAB_CAT_SITE |
1389633Sjames.d.carlson@sun.com ITAB_CAT_STANDARD |
1399633Sjames.d.carlson@sun.com (dsmp->dsm_isv6 ? ITAB_CAT_V6 : 0),
1409633Sjames.d.carlson@sun.com ITAB_CONS_INFO, tsym);
1419633Sjames.d.carlson@sun.com if (entry == NULL) {
1429633Sjames.d.carlson@sun.com dhcpmsg(MSG_INFO, "ignored unknown %s list "
1439633Sjames.d.carlson@sun.com "entry '%s' for %s", param_name, tsym,
1449633Sjames.d.carlson@sun.com dsmp->dsm_name);
1459633Sjames.d.carlson@sun.com } else {
1469633Sjames.d.carlson@sun.com params[i++] = entry->ds_code;
1479633Sjames.d.carlson@sun.com free(entry);
1489633Sjames.d.carlson@sun.com }
1499633Sjames.d.carlson@sun.com } else {
1509633Sjames.d.carlson@sun.com params[i++] = strtoul(tsym, NULL, 0);
1519633Sjames.d.carlson@sun.com }
1529633Sjames.d.carlson@sun.com if (cp == NULL)
1539633Sjames.d.carlson@sun.com break;
1549633Sjames.d.carlson@sun.com param_list = cp + 1;
1559633Sjames.d.carlson@sun.com }
1569633Sjames.d.carlson@sun.com
1579633Sjames.d.carlson@sun.com *param_cnt = i;
1589633Sjames.d.carlson@sun.com return (params);
1599633Sjames.d.carlson@sun.com }
1609633Sjames.d.carlson@sun.com
1619633Sjames.d.carlson@sun.com /*
1623431Scarlsonj * insert_smach(): Create a state machine instance on a given logical
1633431Scarlsonj * interface. The state machine holds the caller's LIF
1643431Scarlsonj * reference on success, and frees it on failure.
1653431Scarlsonj *
1663431Scarlsonj * input: dhcp_lif_t *: logical interface name
1673431Scarlsonj * int *: set to DHCP_IPC_E_* if creation fails
1683431Scarlsonj * output: dhcp_smach_t *: state machine instance
1693431Scarlsonj */
1703431Scarlsonj
1713431Scarlsonj dhcp_smach_t *
insert_smach(dhcp_lif_t * lif,int * error)1723431Scarlsonj insert_smach(dhcp_lif_t *lif, int *error)
1733431Scarlsonj {
1743431Scarlsonj dhcp_smach_t *dsmp, *alt_primary;
1753431Scarlsonj boolean_t isv6;
1769633Sjames.d.carlson@sun.com const char *plist;
1773431Scarlsonj
1783431Scarlsonj if ((dsmp = calloc(1, sizeof (*dsmp))) == NULL) {
1793431Scarlsonj dhcpmsg(MSG_ERR, "cannot allocate state machine entry for %s",
1803431Scarlsonj lif->lif_name);
1813431Scarlsonj remove_lif(lif);
1823431Scarlsonj release_lif(lif);
1833431Scarlsonj *error = DHCP_IPC_E_MEMORY;
1843431Scarlsonj return (NULL);
1853431Scarlsonj }
1863431Scarlsonj dsmp->dsm_name = lif->lif_name;
1873431Scarlsonj dsmp->dsm_lif = lif;
1883431Scarlsonj dsmp->dsm_hold_count = 1;
1893431Scarlsonj dsmp->dsm_state = INIT;
1903431Scarlsonj dsmp->dsm_dflags = DHCP_IF_REMOVED; /* until added to list */
1913431Scarlsonj isv6 = lif->lif_pif->pif_isv6;
1923431Scarlsonj
1933431Scarlsonj /*
1943431Scarlsonj * Now that we have a controlling LIF, we need to assign an IAID to
1953431Scarlsonj * that LIF.
1963431Scarlsonj */
1973431Scarlsonj if (lif->lif_iaid == 0 &&
1983431Scarlsonj (lif->lif_iaid = read_stable_iaid(lif->lif_name)) == 0) {
1993431Scarlsonj static uint32_t iaidctr = 0x80000000u;
2003431Scarlsonj
2013431Scarlsonj /*
2023431Scarlsonj * If this is a logical interface, then use an arbitrary seed
2033431Scarlsonj * value. Otherwise, use the ifIndex.
2043431Scarlsonj */
2053431Scarlsonj lif->lif_iaid = make_stable_iaid(lif->lif_name,
2063431Scarlsonj strchr(lif->lif_name, ':') != NULL ? iaidctr++ :
2073431Scarlsonj lif->lif_pif->pif_index);
2083431Scarlsonj dhcpmsg(MSG_INFO,
2093431Scarlsonj "insert_smach: manufactured IAID %u for v%d %s",
2103431Scarlsonj lif->lif_iaid, isv6 ? 6 : 4, lif->lif_name);
2113431Scarlsonj hold_lif(lif);
2123431Scarlsonj iaid_retry(NULL, lif);
2133431Scarlsonj }
2143431Scarlsonj
2153431Scarlsonj if (isv6) {
2163431Scarlsonj dsmp->dsm_dflags |= DHCP_IF_V6;
2173431Scarlsonj dsmp->dsm_server = ipv6_all_dhcp_relay_and_servers;
2183431Scarlsonj
2193431Scarlsonj /*
2203431Scarlsonj * With DHCPv6, we do all of our I/O using the common
2213431Scarlsonj * v6_sock_fd. There's no need for per-interface file
2223431Scarlsonj * descriptors because we have IPV6_PKTINFO.
2233431Scarlsonj */
2243431Scarlsonj } else {
2253431Scarlsonj IN6_IPADDR_TO_V4MAPPED(htonl(INADDR_BROADCAST),
2263431Scarlsonj &dsmp->dsm_server);
2273431Scarlsonj
2283431Scarlsonj /*
2295381Smeem * With IPv4 DHCP, we use a socket per lif.
2303431Scarlsonj */
2318485SPeter.Memishian@Sun.COM if (!open_ip_lif(lif, INADDR_ANY, B_TRUE)) {
2325381Smeem dhcpmsg(MSG_ERR, "unable to open socket for %s",
2333431Scarlsonj lif->lif_name);
2343431Scarlsonj /* This will also dispose of the LIF */
2353431Scarlsonj release_smach(dsmp);
2363431Scarlsonj *error = DHCP_IPC_E_SOCKET;
2373431Scarlsonj return (NULL);
2383431Scarlsonj }
2393431Scarlsonj }
2409508SPeter.Memishian@Sun.COM
2419508SPeter.Memishian@Sun.COM script_init(dsmp);
2429508SPeter.Memishian@Sun.COM ipc_action_init(&dsmp->dsm_ia);
2433431Scarlsonj
2449508SPeter.Memishian@Sun.COM dsmp->dsm_neg_hrtime = gethrtime();
2459508SPeter.Memishian@Sun.COM dsmp->dsm_offer_timer = -1;
2469508SPeter.Memishian@Sun.COM dsmp->dsm_start_timer = -1;
2479508SPeter.Memishian@Sun.COM dsmp->dsm_retrans_timer = -1;
2483431Scarlsonj
2493431Scarlsonj /*
2509633Sjames.d.carlson@sun.com * Initialize the parameter request and ignore lists, if any.
2513431Scarlsonj */
2529633Sjames.d.carlson@sun.com plist = df_get_string(dsmp->dsm_name, isv6, DF_PARAM_REQUEST_LIST);
2539633Sjames.d.carlson@sun.com dsmp->dsm_prl = parse_param_list(plist, &dsmp->dsm_prllen, "request",
2549633Sjames.d.carlson@sun.com dsmp);
2559633Sjames.d.carlson@sun.com plist = df_get_string(dsmp->dsm_name, isv6, DF_PARAM_IGNORE_LIST);
2569633Sjames.d.carlson@sun.com dsmp->dsm_pil = parse_param_list(plist, &dsmp->dsm_pillen, "ignore",
2579633Sjames.d.carlson@sun.com dsmp);
2583431Scarlsonj
2593431Scarlsonj dsmp->dsm_offer_wait = df_get_int(dsmp->dsm_name, isv6,
2603431Scarlsonj DF_OFFER_WAIT);
2613431Scarlsonj
2623431Scarlsonj /*
2633431Scarlsonj * If there is no primary of this type, and there is one of the other,
2643431Scarlsonj * then make this one primary if it's on the same named PIF.
2653431Scarlsonj */
2663431Scarlsonj if (primary_smach(isv6) == NULL &&
2673431Scarlsonj (alt_primary = primary_smach(!isv6)) != NULL) {
2683431Scarlsonj if (strcmp(lif->lif_pif->pif_name,
2693431Scarlsonj alt_primary->dsm_lif->lif_pif->pif_name) == 0) {
2703431Scarlsonj dhcpmsg(MSG_DEBUG,
2713431Scarlsonj "insert_smach: making %s primary for v%d",
2723431Scarlsonj dsmp->dsm_name, isv6 ? 6 : 4);
2733431Scarlsonj dsmp->dsm_dflags |= DHCP_IF_PRIMARY;
2743431Scarlsonj }
2753431Scarlsonj }
2763431Scarlsonj
2773431Scarlsonj /*
2783431Scarlsonj * We now have at least one state machine running, so cancel any
2793431Scarlsonj * running inactivity timer.
2803431Scarlsonj */
2813431Scarlsonj if (inactivity_id != -1 &&
2823431Scarlsonj iu_cancel_timer(tq, inactivity_id, NULL) == 1)
2833431Scarlsonj inactivity_id = -1;
2843431Scarlsonj
2853431Scarlsonj dsmp->dsm_dflags &= ~DHCP_IF_REMOVED;
2863431Scarlsonj insque(dsmp, &lif->lif_smachs);
2873431Scarlsonj global_smach_count++;
2883431Scarlsonj dhcpmsg(MSG_DEBUG2, "insert_smach: inserted %s", dsmp->dsm_name);
2893431Scarlsonj
2903431Scarlsonj return (dsmp);
2913431Scarlsonj }
2923431Scarlsonj
2933431Scarlsonj /*
2943431Scarlsonj * hold_smach(): acquires a hold on a state machine
2953431Scarlsonj *
2963431Scarlsonj * input: dhcp_smach_t *: the state machine to acquire a hold on
2973431Scarlsonj * output: void
2983431Scarlsonj */
2993431Scarlsonj
3003431Scarlsonj void
hold_smach(dhcp_smach_t * dsmp)3013431Scarlsonj hold_smach(dhcp_smach_t *dsmp)
3023431Scarlsonj {
3033431Scarlsonj dsmp->dsm_hold_count++;
3043431Scarlsonj
3053431Scarlsonj dhcpmsg(MSG_DEBUG2, "hold_smach: hold count on %s: %d",
3063431Scarlsonj dsmp->dsm_name, dsmp->dsm_hold_count);
3073431Scarlsonj }
3083431Scarlsonj
3093431Scarlsonj /*
3103431Scarlsonj * free_smach(): frees the memory occupied by a state machine
3113431Scarlsonj *
3123431Scarlsonj * input: dhcp_smach_t *: the DHCP state machine to free
3133431Scarlsonj * output: void
3143431Scarlsonj */
3153431Scarlsonj
3163431Scarlsonj static void
free_smach(dhcp_smach_t * dsmp)3173431Scarlsonj free_smach(dhcp_smach_t *dsmp)
3183431Scarlsonj {
3193431Scarlsonj dhcpmsg(MSG_DEBUG, "free_smach: freeing state machine %s",
3203431Scarlsonj dsmp->dsm_name);
3213431Scarlsonj
3223431Scarlsonj deprecate_leases(dsmp);
3233431Scarlsonj remove_lif(dsmp->dsm_lif);
3243431Scarlsonj release_lif(dsmp->dsm_lif);
3253431Scarlsonj free_pkt_list(&dsmp->dsm_recv_pkt_list);
3263431Scarlsonj if (dsmp->dsm_ack != dsmp->dsm_orig_ack)
3273431Scarlsonj free_pkt_entry(dsmp->dsm_orig_ack);
3283431Scarlsonj free_pkt_entry(dsmp->dsm_ack);
3293431Scarlsonj free(dsmp->dsm_send_pkt.pkt);
3303431Scarlsonj free(dsmp->dsm_cid);
3313431Scarlsonj free(dsmp->dsm_prl);
3329633Sjames.d.carlson@sun.com free(dsmp->dsm_pil);
3333431Scarlsonj free(dsmp->dsm_routers);
3343431Scarlsonj free(dsmp->dsm_reqhost);
3353431Scarlsonj free(dsmp);
3363431Scarlsonj
3373431Scarlsonj /* no big deal if this fails */
3383431Scarlsonj if (global_smach_count == 0 && inactivity_id == -1) {
3393431Scarlsonj inactivity_id = iu_schedule_timer(tq, DHCP_INACTIVITY_WAIT,
3403431Scarlsonj inactivity_shutdown, NULL);
3413431Scarlsonj }
3423431Scarlsonj }
3433431Scarlsonj
3443431Scarlsonj /*
3453431Scarlsonj * release_smach(): releases a hold previously acquired on a state machine.
3463431Scarlsonj * If the hold count reaches 0, the state machine is freed.
3473431Scarlsonj *
3483431Scarlsonj * input: dhcp_smach_t *: the state machine entry to release the hold on
3493431Scarlsonj * output: void
3503431Scarlsonj */
3513431Scarlsonj
3523431Scarlsonj void
release_smach(dhcp_smach_t * dsmp)3533431Scarlsonj release_smach(dhcp_smach_t *dsmp)
3543431Scarlsonj {
3553431Scarlsonj if (dsmp->dsm_hold_count == 0) {
3563431Scarlsonj dhcpmsg(MSG_CRIT, "release_smach: extraneous release");
3573431Scarlsonj return;
3583431Scarlsonj }
3593431Scarlsonj
3603431Scarlsonj if (dsmp->dsm_hold_count == 1 &&
3613431Scarlsonj !(dsmp->dsm_dflags & DHCP_IF_REMOVED)) {
3623431Scarlsonj dhcpmsg(MSG_CRIT, "release_smach: missing removal");
3633431Scarlsonj return;
3643431Scarlsonj }
3653431Scarlsonj
3663431Scarlsonj if (--dsmp->dsm_hold_count == 0) {
3673431Scarlsonj free_smach(dsmp);
3683431Scarlsonj } else {
3693431Scarlsonj dhcpmsg(MSG_DEBUG2, "release_smach: hold count on %s: %d",
3703431Scarlsonj dsmp->dsm_name, dsmp->dsm_hold_count);
3713431Scarlsonj }
3723431Scarlsonj }
3733431Scarlsonj
3743431Scarlsonj /*
3753431Scarlsonj * next_smach(): state machine iterator function
3763431Scarlsonj *
3773431Scarlsonj * input: dhcp_smach_t *: current state machine (or NULL for list start)
3783431Scarlsonj * boolean_t: B_TRUE if DHCPv6, B_FALSE otherwise
3793431Scarlsonj * output: dhcp_smach_t *: next state machine in list
3803431Scarlsonj */
3813431Scarlsonj
3823431Scarlsonj dhcp_smach_t *
next_smach(dhcp_smach_t * dsmp,boolean_t isv6)3833431Scarlsonj next_smach(dhcp_smach_t *dsmp, boolean_t isv6)
3843431Scarlsonj {
3853431Scarlsonj dhcp_lif_t *lif;
3863431Scarlsonj dhcp_pif_t *pif;
3873431Scarlsonj
3883431Scarlsonj if (dsmp != NULL) {
3893431Scarlsonj if (dsmp->dsm_next != NULL)
3903431Scarlsonj return (dsmp->dsm_next);
3913431Scarlsonj
3923431Scarlsonj if ((lif = dsmp->dsm_lif) != NULL)
3933431Scarlsonj lif = lif->lif_next;
3943431Scarlsonj for (; lif != NULL; lif = lif->lif_next) {
3953431Scarlsonj if (lif->lif_smachs != NULL)
3963431Scarlsonj return (lif->lif_smachs);
3973431Scarlsonj }
3983431Scarlsonj
3993431Scarlsonj if ((pif = dsmp->dsm_lif->lif_pif) != NULL)
4003431Scarlsonj pif = pif->pif_next;
4013431Scarlsonj } else {
4023431Scarlsonj pif = isv6 ? v6root : v4root;
4033431Scarlsonj }
4043431Scarlsonj for (; pif != NULL; pif = pif->pif_next) {
4053431Scarlsonj for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) {
4063431Scarlsonj if (lif->lif_smachs != NULL)
4073431Scarlsonj return (lif->lif_smachs);
4083431Scarlsonj }
4093431Scarlsonj }
4103431Scarlsonj return (NULL);
4113431Scarlsonj }
4123431Scarlsonj
4133431Scarlsonj /*
4143431Scarlsonj * primary_smach(): loop through all state machines of the given type (v4 or
4153431Scarlsonj * v6) in the system, and locate the one that's primary.
4163431Scarlsonj *
4173431Scarlsonj * input: boolean_t: B_TRUE for IPv6
4183431Scarlsonj * output: dhcp_smach_t *: the primary state machine
4193431Scarlsonj */
4203431Scarlsonj
4213431Scarlsonj dhcp_smach_t *
primary_smach(boolean_t isv6)4223431Scarlsonj primary_smach(boolean_t isv6)
4233431Scarlsonj {
4243431Scarlsonj dhcp_smach_t *dsmp;
4253431Scarlsonj
4263431Scarlsonj for (dsmp = next_smach(NULL, isv6); dsmp != NULL;
4273431Scarlsonj dsmp = next_smach(dsmp, isv6)) {
4283431Scarlsonj if (dsmp->dsm_dflags & DHCP_IF_PRIMARY)
4293431Scarlsonj break;
4303431Scarlsonj }
4313431Scarlsonj return (dsmp);
4323431Scarlsonj }
4333431Scarlsonj
4343431Scarlsonj /*
4359633Sjames.d.carlson@sun.com * info_primary_smach(): loop through all state machines of the given type (v4
4369633Sjames.d.carlson@sun.com * or v6) in the system, and locate the one that should
4379633Sjames.d.carlson@sun.com * be considered "primary" for dhcpinfo.
4389633Sjames.d.carlson@sun.com *
4399633Sjames.d.carlson@sun.com * input: boolean_t: B_TRUE for IPv6
4409633Sjames.d.carlson@sun.com * output: dhcp_smach_t *: the dhcpinfo primary state machine
4419633Sjames.d.carlson@sun.com */
4429633Sjames.d.carlson@sun.com
4439633Sjames.d.carlson@sun.com dhcp_smach_t *
info_primary_smach(boolean_t isv6)4449633Sjames.d.carlson@sun.com info_primary_smach(boolean_t isv6)
4459633Sjames.d.carlson@sun.com {
4469633Sjames.d.carlson@sun.com dhcp_smach_t *bestdsm = NULL;
4479633Sjames.d.carlson@sun.com dhcp_smach_t *dsmp;
4489633Sjames.d.carlson@sun.com
4499633Sjames.d.carlson@sun.com for (dsmp = next_smach(NULL, isv6); dsmp != NULL;
4509633Sjames.d.carlson@sun.com dsmp = next_smach(dsmp, isv6)) {
4519633Sjames.d.carlson@sun.com /*
4529633Sjames.d.carlson@sun.com * If there is a primary, then something previously went wrong
4539633Sjames.d.carlson@sun.com * with verification, because the caller uses primary_smach()
4549633Sjames.d.carlson@sun.com * before calling this routine. There's nothing else we can do
4559633Sjames.d.carlson@sun.com * but return failure, as the designated primary must be bad.
4569633Sjames.d.carlson@sun.com */
4579633Sjames.d.carlson@sun.com if (dsmp->dsm_dflags & DHCP_IF_PRIMARY)
4589633Sjames.d.carlson@sun.com return (NULL);
4599633Sjames.d.carlson@sun.com
4609633Sjames.d.carlson@sun.com /* If we have no information, then we're not primary. */
4619633Sjames.d.carlson@sun.com if (dsmp->dsm_ack == NULL)
4629633Sjames.d.carlson@sun.com continue;
4639633Sjames.d.carlson@sun.com
4649633Sjames.d.carlson@sun.com /*
4659633Sjames.d.carlson@sun.com * Among those interfaces that have DHCP information, the
4669633Sjames.d.carlson@sun.com * "primary" is the one that sorts lexically first.
4679633Sjames.d.carlson@sun.com */
4689633Sjames.d.carlson@sun.com if (bestdsm == NULL ||
4699633Sjames.d.carlson@sun.com strcmp(dsmp->dsm_name, bestdsm->dsm_name) < 0)
4709633Sjames.d.carlson@sun.com bestdsm = dsmp;
4719633Sjames.d.carlson@sun.com }
4729633Sjames.d.carlson@sun.com return (bestdsm);
4739633Sjames.d.carlson@sun.com }
4749633Sjames.d.carlson@sun.com
4759633Sjames.d.carlson@sun.com /*
4763431Scarlsonj * make_primary(): designate a given state machine as being the primary
4773431Scarlsonj * instance on the primary interface. Note that the user often
4783431Scarlsonj * thinks in terms of a primary "interface" (rather than just
4793431Scarlsonj * an instance), so we go to lengths here to keep v4 and v6 in
4803431Scarlsonj * sync.
4813431Scarlsonj *
4823431Scarlsonj * input: dhcp_smach_t *: the primary state machine
4833431Scarlsonj * output: none
4843431Scarlsonj */
4853431Scarlsonj
4863431Scarlsonj void
make_primary(dhcp_smach_t * dsmp)4873431Scarlsonj make_primary(dhcp_smach_t *dsmp)
4883431Scarlsonj {
4893431Scarlsonj dhcp_smach_t *old_primary, *alt_primary;
4903431Scarlsonj dhcp_pif_t *pif;
4913431Scarlsonj
4923431Scarlsonj if ((old_primary = primary_smach(dsmp->dsm_isv6)) != NULL)
4933431Scarlsonj old_primary->dsm_dflags &= ~DHCP_IF_PRIMARY;
4943431Scarlsonj dsmp->dsm_dflags |= DHCP_IF_PRIMARY;
4953431Scarlsonj
4963431Scarlsonj /*
4973431Scarlsonj * Find the primary for the other protocol.
4983431Scarlsonj */
4993431Scarlsonj alt_primary = primary_smach(!dsmp->dsm_isv6);
5003431Scarlsonj
5013431Scarlsonj /*
5023431Scarlsonj * If it's on a different interface, then cancel that. If it's on the
5033431Scarlsonj * same interface, then we're done.
5043431Scarlsonj */
5053431Scarlsonj if (alt_primary != NULL) {
5063431Scarlsonj if (strcmp(alt_primary->dsm_lif->lif_pif->pif_name,
5073431Scarlsonj dsmp->dsm_lif->lif_pif->pif_name) == 0)
5083431Scarlsonj return;
5093431Scarlsonj alt_primary->dsm_dflags &= ~DHCP_IF_PRIMARY;
5103431Scarlsonj }
5113431Scarlsonj
5123431Scarlsonj /*
5133431Scarlsonj * We need a new primary for the other protocol. If the PIF exists,
5143431Scarlsonj * there must be at least one state machine. Just choose the first for
5153431Scarlsonj * consistency with insert_smach().
5163431Scarlsonj */
5173431Scarlsonj if ((pif = lookup_pif_by_name(dsmp->dsm_lif->lif_pif->pif_name,
5183431Scarlsonj !dsmp->dsm_isv6)) != NULL) {
5193431Scarlsonj pif->pif_lifs->lif_smachs->dsm_dflags |= DHCP_IF_PRIMARY;
5203431Scarlsonj }
5213431Scarlsonj }
5223431Scarlsonj
5233431Scarlsonj /*
5243431Scarlsonj * lookup_smach(): finds a state machine by name and type; used for dispatching
5253431Scarlsonj * user commands.
5263431Scarlsonj *
5273431Scarlsonj * input: const char *: the name of the state machine
5283431Scarlsonj * boolean_t: B_TRUE if DHCPv6, B_FALSE otherwise
5293431Scarlsonj * output: dhcp_smach_t *: the state machine found
5303431Scarlsonj */
5313431Scarlsonj
5323431Scarlsonj dhcp_smach_t *
lookup_smach(const char * smname,boolean_t isv6)5333431Scarlsonj lookup_smach(const char *smname, boolean_t isv6)
5343431Scarlsonj {
5353431Scarlsonj dhcp_smach_t *dsmp;
5363431Scarlsonj
5373431Scarlsonj for (dsmp = next_smach(NULL, isv6); dsmp != NULL;
5383431Scarlsonj dsmp = next_smach(dsmp, isv6)) {
5393431Scarlsonj if (strcmp(dsmp->dsm_name, smname) == 0)
5403431Scarlsonj break;
5413431Scarlsonj }
5423431Scarlsonj return (dsmp);
5433431Scarlsonj }
5443431Scarlsonj
5453431Scarlsonj /*
5463431Scarlsonj * lookup_smach_by_uindex(): iterate through running state machines by
5473431Scarlsonj * truncated interface index.
5483431Scarlsonj *
5493431Scarlsonj * input: uint16_t: the interface index (truncated)
5503431Scarlsonj * dhcp_smach_t *: the previous state machine, or NULL for start
5513431Scarlsonj * boolean_t: B_TRUE for DHCPv6, B_FALSE for IPv4 DHCP
5523431Scarlsonj * output: dhcp_smach_t *: next state machine, or NULL at end of list
5533431Scarlsonj */
5543431Scarlsonj
5553431Scarlsonj dhcp_smach_t *
lookup_smach_by_uindex(uint16_t ifindex,dhcp_smach_t * dsmp,boolean_t isv6)5563431Scarlsonj lookup_smach_by_uindex(uint16_t ifindex, dhcp_smach_t *dsmp, boolean_t isv6)
5573431Scarlsonj {
5583431Scarlsonj dhcp_pif_t *pif;
5593431Scarlsonj dhcp_lif_t *lif;
5603431Scarlsonj
5613431Scarlsonj /*
5623431Scarlsonj * If the user gives us a state machine, then check that the next one
5633431Scarlsonj * available is on the same physical interface. If so, then go ahead
5643431Scarlsonj * and return that.
5653431Scarlsonj */
5663431Scarlsonj if (dsmp != NULL) {
5673431Scarlsonj pif = dsmp->dsm_lif->lif_pif;
5683431Scarlsonj if ((dsmp = next_smach(dsmp, isv6)) == NULL)
5693431Scarlsonj return (NULL);
5703431Scarlsonj if (pif == dsmp->dsm_lif->lif_pif)
5713431Scarlsonj return (dsmp);
5723431Scarlsonj } else {
5733431Scarlsonj /* Otherwise, start at the beginning of the list */
5743431Scarlsonj pif = NULL;
5753431Scarlsonj }
5763431Scarlsonj
5773431Scarlsonj /*
5783431Scarlsonj * Find the next physical interface with the same truncated interface
5793431Scarlsonj * index, and return the first state machine on that. If there are no
5803431Scarlsonj * more physical interfaces that match, then we're done.
5813431Scarlsonj */
5823431Scarlsonj do {
5833431Scarlsonj pif = lookup_pif_by_uindex(ifindex, pif, isv6);
5843431Scarlsonj if (pif == NULL)
5853431Scarlsonj return (NULL);
5863431Scarlsonj for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) {
5873431Scarlsonj if ((dsmp = lif->lif_smachs) != NULL)
5883431Scarlsonj break;
5893431Scarlsonj }
5903431Scarlsonj } while (dsmp == NULL);
5913431Scarlsonj return (dsmp);
5923431Scarlsonj }
5933431Scarlsonj
5943431Scarlsonj /*
5953431Scarlsonj * lookup_smach_by_xid(): iterate through running state machines by transaction
5963431Scarlsonj * id. Transaction ID zero means "all state machines."
5973431Scarlsonj *
5983431Scarlsonj * input: uint32_t: the transaction id to look up
5993431Scarlsonj * dhcp_smach_t *: the previous state machine, or NULL for start
6003431Scarlsonj * boolean_t: B_TRUE if DHCPv6, B_FALSE otherwise
6013431Scarlsonj * output: dhcp_smach_t *: next state machine, or NULL at end of list
6023431Scarlsonj */
6033431Scarlsonj
6043431Scarlsonj dhcp_smach_t *
lookup_smach_by_xid(uint32_t xid,dhcp_smach_t * dsmp,boolean_t isv6)6053431Scarlsonj lookup_smach_by_xid(uint32_t xid, dhcp_smach_t *dsmp, boolean_t isv6)
6063431Scarlsonj {
6073431Scarlsonj for (dsmp = next_smach(dsmp, isv6); dsmp != NULL;
6083431Scarlsonj dsmp = next_smach(dsmp, isv6)) {
6093431Scarlsonj if (xid == 0 ||
6103431Scarlsonj pkt_get_xid(dsmp->dsm_send_pkt.pkt, isv6) == xid)
6113431Scarlsonj break;
6123431Scarlsonj }
6133431Scarlsonj
6143431Scarlsonj return (dsmp);
6153431Scarlsonj }
6163431Scarlsonj
6173431Scarlsonj /*
6183431Scarlsonj * lookup_smach_by_event(): find a state machine busy with a particular event
6193431Scarlsonj * ID. This is used only for error handling.
6203431Scarlsonj *
6213431Scarlsonj * input: iu_event_id_t: the event id to look up
6223431Scarlsonj * output: dhcp_smach_t *: matching state machine, or NULL if none
6233431Scarlsonj */
6243431Scarlsonj
6253431Scarlsonj dhcp_smach_t *
lookup_smach_by_event(iu_event_id_t eid)6263431Scarlsonj lookup_smach_by_event(iu_event_id_t eid)
6273431Scarlsonj {
6283431Scarlsonj dhcp_smach_t *dsmp;
6293431Scarlsonj boolean_t isv6 = B_FALSE;
6303431Scarlsonj
6313431Scarlsonj for (;;) {
6323431Scarlsonj for (dsmp = next_smach(NULL, isv6); dsmp != NULL;
6333431Scarlsonj dsmp = next_smach(dsmp, isv6)) {
6343431Scarlsonj if ((dsmp->dsm_dflags & DHCP_IF_BUSY) &&
6353431Scarlsonj eid == dsmp->dsm_ia.ia_eid)
6363431Scarlsonj return (dsmp);
6373431Scarlsonj }
6383431Scarlsonj if (isv6)
6393431Scarlsonj break;
6403431Scarlsonj isv6 = B_TRUE;
6413431Scarlsonj }
6423431Scarlsonj
6433431Scarlsonj return (dsmp);
6443431Scarlsonj }
6453431Scarlsonj
6463431Scarlsonj /*
6473431Scarlsonj * cancel_offer_timer(): stop the offer polling timer on a given state machine
6483431Scarlsonj *
6493431Scarlsonj * input: dhcp_smach_t *: state machine on which to stop polling for offers
6503431Scarlsonj * output: none
6513431Scarlsonj */
6523431Scarlsonj
6533431Scarlsonj void
cancel_offer_timer(dhcp_smach_t * dsmp)6543431Scarlsonj cancel_offer_timer(dhcp_smach_t *dsmp)
6553431Scarlsonj {
6563431Scarlsonj int retval;
6573431Scarlsonj
6583431Scarlsonj if (dsmp->dsm_offer_timer != -1) {
6593431Scarlsonj retval = iu_cancel_timer(tq, dsmp->dsm_offer_timer, NULL);
6603431Scarlsonj dsmp->dsm_offer_timer = -1;
6613431Scarlsonj if (retval == 1)
6623431Scarlsonj release_smach(dsmp);
6633431Scarlsonj }
6643431Scarlsonj }
6653431Scarlsonj
6663431Scarlsonj /*
6673431Scarlsonj * cancel_smach_timers(): stop all of the timers related to a given state
6683431Scarlsonj * machine, including lease and LIF expiry.
6693431Scarlsonj *
6703431Scarlsonj * input: dhcp_smach_t *: state machine to cancel
6713431Scarlsonj * output: none
6724106Scarlsonj * note: this function assumes that the iu timer functions are synchronous
6734106Scarlsonj * and thus don't require any protection or ordering on cancellation.
6743431Scarlsonj */
6753431Scarlsonj
6769508SPeter.Memishian@Sun.COM void
cancel_smach_timers(dhcp_smach_t * dsmp)6773431Scarlsonj cancel_smach_timers(dhcp_smach_t *dsmp)
6783431Scarlsonj {
6793431Scarlsonj dhcp_lease_t *dlp;
6803431Scarlsonj dhcp_lif_t *lif;
6813431Scarlsonj uint_t nlifs;
6823431Scarlsonj
6833431Scarlsonj for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
6843431Scarlsonj cancel_lease_timers(dlp);
6853431Scarlsonj lif = dlp->dl_lifs;
6863431Scarlsonj nlifs = dlp->dl_nlifs;
6873431Scarlsonj for (; nlifs > 0; nlifs--, lif = lif->lif_next)
6883431Scarlsonj cancel_lif_timers(lif);
6893431Scarlsonj }
6903431Scarlsonj
6913431Scarlsonj cancel_offer_timer(dsmp);
6923431Scarlsonj stop_pkt_retransmission(dsmp);
6934106Scarlsonj if (dsmp->dsm_start_timer != -1) {
6944106Scarlsonj (void) iu_cancel_timer(tq, dsmp->dsm_start_timer, NULL);
6954106Scarlsonj dsmp->dsm_start_timer = -1;
6964106Scarlsonj release_smach(dsmp);
6974106Scarlsonj }
6983431Scarlsonj }
6993431Scarlsonj
7003431Scarlsonj /*
7013431Scarlsonj * remove_smach(): removes a given state machine from the system. marks it
7023431Scarlsonj * for being freed (but may not actually free it).
7033431Scarlsonj *
7043431Scarlsonj * input: dhcp_smach_t *: the state machine to remove
7053431Scarlsonj * output: void
7063431Scarlsonj */
7073431Scarlsonj
7083522Scarlsonj void
remove_smach(dhcp_smach_t * dsmp)7093431Scarlsonj remove_smach(dhcp_smach_t *dsmp)
7103431Scarlsonj {
7113431Scarlsonj if (dsmp->dsm_dflags & DHCP_IF_REMOVED)
7123431Scarlsonj return;
7133431Scarlsonj
7143431Scarlsonj dhcpmsg(MSG_DEBUG2, "remove_smach: removing %s", dsmp->dsm_name);
7153431Scarlsonj dsmp->dsm_dflags |= DHCP_IF_REMOVED;
7163431Scarlsonj remque(dsmp);
7173431Scarlsonj global_smach_count--;
7183431Scarlsonj
7193431Scarlsonj /*
7203431Scarlsonj * if we have long term timers, cancel them so that state machine
7213431Scarlsonj * resources can be reclaimed in a reasonable amount of time.
7223431Scarlsonj */
7233431Scarlsonj cancel_smach_timers(dsmp);
7243431Scarlsonj
7253431Scarlsonj /* Drop the hold that the LIF's state machine list had on us */
7263431Scarlsonj release_smach(dsmp);
7273431Scarlsonj }
7283431Scarlsonj
7293431Scarlsonj /*
7303431Scarlsonj * finished_smach(): we're finished with a given state machine; remove it from
7313431Scarlsonj * the system and tell the user (who may have initiated the
7323431Scarlsonj * removal process). Note that we remove it from the system
7333431Scarlsonj * first to allow back-to-back drop and create invocations.
7343431Scarlsonj *
7353431Scarlsonj * input: dhcp_smach_t *: the state machine to remove
7363431Scarlsonj * int: error for IPC
7373431Scarlsonj * output: void
7383431Scarlsonj */
7393431Scarlsonj
7403431Scarlsonj void
finished_smach(dhcp_smach_t * dsmp,int error)7413431Scarlsonj finished_smach(dhcp_smach_t *dsmp, int error)
7423431Scarlsonj {
7433431Scarlsonj hold_smach(dsmp);
7443431Scarlsonj remove_smach(dsmp);
7453431Scarlsonj if (dsmp->dsm_ia.ia_fd != -1)
7463431Scarlsonj ipc_action_finish(dsmp, error);
7473431Scarlsonj else
7483431Scarlsonj (void) async_cancel(dsmp);
7493431Scarlsonj release_smach(dsmp);
7503431Scarlsonj }
7513431Scarlsonj
7523431Scarlsonj /*
7535381Smeem * is_bound_state(): checks if a state indicates the client is bound
7545381Smeem *
7555381Smeem * input: DHCPSTATE: the state to check
7565381Smeem * output: boolean_t: B_TRUE if the state is bound, B_FALSE if not
7575381Smeem */
7585381Smeem
7595381Smeem boolean_t
is_bound_state(DHCPSTATE state)7605381Smeem is_bound_state(DHCPSTATE state)
7615381Smeem {
7625381Smeem return (state == BOUND || state == REBINDING || state == INFORMATION ||
7635381Smeem state == RELEASING || state == INFORM_SENT || state == RENEWING);
7645381Smeem }
7655381Smeem
7665381Smeem /*
7673431Scarlsonj * set_smach_state(): changes state and updates I/O
7683431Scarlsonj *
7693431Scarlsonj * input: dhcp_smach_t *: the state machine to change
7703431Scarlsonj * DHCPSTATE: the new state
7713431Scarlsonj * output: boolean_t: B_TRUE on success, B_FALSE on failure
7723431Scarlsonj */
7733431Scarlsonj
7743431Scarlsonj boolean_t
set_smach_state(dhcp_smach_t * dsmp,DHCPSTATE state)7753431Scarlsonj set_smach_state(dhcp_smach_t *dsmp, DHCPSTATE state)
7763431Scarlsonj {
7775381Smeem dhcp_lif_t *lif = dsmp->dsm_lif;
7785381Smeem
7793431Scarlsonj if (dsmp->dsm_state != state) {
7803431Scarlsonj dhcpmsg(MSG_DEBUG,
7813431Scarlsonj "set_smach_state: changing from %s to %s on %s",
7823431Scarlsonj dhcp_state_to_string(dsmp->dsm_state),
7833431Scarlsonj dhcp_state_to_string(state), dsmp->dsm_name);
7843431Scarlsonj
7855381Smeem /*
7865381Smeem * For IPv4, when we're in a bound state our socket must be
7875381Smeem * bound to our address. Otherwise, our socket must be bound
7885381Smeem * to INADDR_ANY. For IPv6, no such change is necessary.
7895381Smeem */
7903431Scarlsonj if (!dsmp->dsm_isv6) {
7915381Smeem if (is_bound_state(dsmp->dsm_state)) {
7925381Smeem if (!is_bound_state(state)) {
7935381Smeem close_ip_lif(lif);
7948485SPeter.Memishian@Sun.COM if (!open_ip_lif(lif, INADDR_ANY,
7958485SPeter.Memishian@Sun.COM B_FALSE))
7965381Smeem return (B_FALSE);
7975381Smeem }
7985381Smeem } else {
7995381Smeem if (is_bound_state(state)) {
8005381Smeem close_ip_lif(lif);
8015381Smeem if (!open_ip_lif(lif,
8028485SPeter.Memishian@Sun.COM ntohl(lif->lif_addr), B_FALSE))
8035381Smeem return (B_FALSE);
8045381Smeem }
8053431Scarlsonj }
8063431Scarlsonj }
8073431Scarlsonj
8083431Scarlsonj dsmp->dsm_state = state;
8093431Scarlsonj }
8103431Scarlsonj return (B_TRUE);
8113431Scarlsonj }
8123431Scarlsonj
8133431Scarlsonj /*
8143431Scarlsonj * duid_retry(): attempt to write DUID again
8153431Scarlsonj *
8163431Scarlsonj * input: iu_tq_t *: ignored
8173431Scarlsonj * void *: ignored
8183431Scarlsonj * output: none
8193431Scarlsonj */
8203431Scarlsonj
8213431Scarlsonj /* ARGSUSED */
8223431Scarlsonj static void
duid_retry(iu_tq_t * tqp,void * arg)8233431Scarlsonj duid_retry(iu_tq_t *tqp, void *arg)
8243431Scarlsonj {
8253431Scarlsonj if (write_stable_duid(global_duid, global_duidlen) == -1) {
8263431Scarlsonj if (errno != EROFS) {
8273431Scarlsonj dhcpmsg(MSG_ERR,
8283431Scarlsonj "duid_retry: unable to write out DUID");
8293431Scarlsonj } else {
8303431Scarlsonj (void) iu_schedule_timer(tq, 60, duid_retry, NULL);
8313431Scarlsonj }
8323431Scarlsonj }
8333431Scarlsonj }
8343431Scarlsonj
8353431Scarlsonj /*
8363431Scarlsonj * get_smach_cid(): gets the client ID for a given state machine.
8373431Scarlsonj *
8383431Scarlsonj * input: dhcp_smach_t *: the state machine to set up
8393431Scarlsonj * output: int: DHCP_IPC_SUCCESS or one of DHCP_IPC_E_* on failure.
8403431Scarlsonj */
8413431Scarlsonj
8423431Scarlsonj int
get_smach_cid(dhcp_smach_t * dsmp)8433431Scarlsonj get_smach_cid(dhcp_smach_t *dsmp)
8443431Scarlsonj {
8453431Scarlsonj uchar_t *client_id;
8463431Scarlsonj uint_t client_id_len;
8473431Scarlsonj dhcp_lif_t *lif = dsmp->dsm_lif;
8483431Scarlsonj dhcp_pif_t *pif = lif->lif_pif;
8493431Scarlsonj const char *value;
8503431Scarlsonj size_t slen;
8513431Scarlsonj
8523431Scarlsonj /*
8533431Scarlsonj * Look in defaults file for the client-id. If present, this takes
8543431Scarlsonj * precedence over all other forms of ID.
8553431Scarlsonj */
8563431Scarlsonj
8573431Scarlsonj dhcpmsg(MSG_DEBUG, "get_smach_cid: getting default client-id "
8583431Scarlsonj "property on %s", dsmp->dsm_name);
8593431Scarlsonj value = df_get_string(dsmp->dsm_name, pif->pif_isv6, DF_CLIENT_ID);
8603431Scarlsonj if (value != NULL) {
8613431Scarlsonj /*
8623431Scarlsonj * The Client ID string can have one of three basic forms:
8633431Scarlsonj * <decimal>,<data...>
8643431Scarlsonj * 0x<hex...>
8653431Scarlsonj * <string...>
8663431Scarlsonj *
8673431Scarlsonj * The first form is an RFC 3315 DUID. This is legal for both
8683431Scarlsonj * IPv4 DHCP and DHCPv6. For IPv4, an RFC 4361 Client ID is
8693431Scarlsonj * constructed from this value.
8703431Scarlsonj *
8713431Scarlsonj * The second and third forms are legal for IPv4 only. This is
8723431Scarlsonj * a raw Client ID, in hex or ASCII string format.
8733431Scarlsonj */
8743431Scarlsonj
8753431Scarlsonj if (isdigit(*value) &&
8763431Scarlsonj value[strspn(value, "0123456789")] == ',') {
8773431Scarlsonj char *cp;
8783431Scarlsonj ulong_t duidtype;
8793431Scarlsonj ulong_t subtype;
8803431Scarlsonj
8813431Scarlsonj errno = 0;
8823431Scarlsonj duidtype = strtoul(value, &cp, 0);
8833431Scarlsonj if (value == cp || errno != 0 || *cp != ',' ||
8843431Scarlsonj duidtype > 65535) {
8853431Scarlsonj dhcpmsg(MSG_ERR, "get_smach_cid: cannot parse "
8863431Scarlsonj "DUID type in %s", value);
8873431Scarlsonj goto no_specified_id;
8883431Scarlsonj }
8893431Scarlsonj value = cp + 1;
8903431Scarlsonj switch (duidtype) {
8913431Scarlsonj case DHCPV6_DUID_LL:
8923431Scarlsonj case DHCPV6_DUID_LLT: {
8933431Scarlsonj int num;
8943431Scarlsonj char chr;
8953431Scarlsonj
8963431Scarlsonj errno = 0;
8973431Scarlsonj subtype = strtoul(value, &cp, 0);
8983431Scarlsonj if (value == cp || errno != 0 || *cp != ',' ||
8993431Scarlsonj subtype > 65535) {
9003431Scarlsonj dhcpmsg(MSG_ERR, "get_smach_cid: "
9013431Scarlsonj "cannot parse MAC type in %s",
9023431Scarlsonj value);
9033431Scarlsonj goto no_specified_id;
9043431Scarlsonj }
9053431Scarlsonj value = cp + 1;
9063431Scarlsonj client_id_len = pif->pif_isv6 ? 1 : 5;
9073431Scarlsonj for (; *cp != '\0'; cp++) {
9083431Scarlsonj if (*cp == ':')
9093431Scarlsonj client_id_len++;
9103431Scarlsonj else if (!isxdigit(*cp))
9113431Scarlsonj break;
9123431Scarlsonj }
9133431Scarlsonj if (duidtype == DHCPV6_DUID_LL) {
9143431Scarlsonj duid_llt_t *dllt;
9153431Scarlsonj time_t now;
9163431Scarlsonj
9173431Scarlsonj client_id_len += sizeof (*dllt);
9183431Scarlsonj dllt = malloc(client_id_len);
9193431Scarlsonj if (dllt == NULL)
9203431Scarlsonj goto alloc_failure;
9213431Scarlsonj dsmp->dsm_cid = (uchar_t *)dllt;
9223431Scarlsonj dllt->dllt_dutype = htons(duidtype);
9233431Scarlsonj dllt->dllt_hwtype = htons(subtype);
9243431Scarlsonj now = time(NULL) - DUID_TIME_BASE;
9253431Scarlsonj dllt->dllt_time = htonl(now);
9263431Scarlsonj cp = (char *)(dllt + 1);
9273431Scarlsonj } else {
9283431Scarlsonj duid_ll_t *dll;
9293431Scarlsonj
9303431Scarlsonj client_id_len += sizeof (*dll);
9313431Scarlsonj dll = malloc(client_id_len);
9323431Scarlsonj if (dll == NULL)
9333431Scarlsonj goto alloc_failure;
9343431Scarlsonj dsmp->dsm_cid = (uchar_t *)dll;
9353431Scarlsonj dll->dll_dutype = htons(duidtype);
9363431Scarlsonj dll->dll_hwtype = htons(subtype);
9373431Scarlsonj cp = (char *)(dll + 1);
9383431Scarlsonj }
9393431Scarlsonj num = 0;
9403431Scarlsonj while ((chr = *value) != '\0') {
9413431Scarlsonj if (isdigit(chr)) {
9423431Scarlsonj num = (num << 4) + chr - '0';
9433431Scarlsonj } else if (isxdigit(chr)) {
9443431Scarlsonj num = (num << 4) + 10 + chr -
9453431Scarlsonj (isupper(chr) ? 'A' : 'a');
9463431Scarlsonj } else if (chr == ':') {
9473431Scarlsonj *cp++ = num;
9483431Scarlsonj num = 0;
9493431Scarlsonj } else {
9503431Scarlsonj break;
9513431Scarlsonj }
9523431Scarlsonj }
9533431Scarlsonj break;
9543431Scarlsonj }
9553431Scarlsonj case DHCPV6_DUID_EN: {
9563431Scarlsonj duid_en_t *den;
9573431Scarlsonj
9583431Scarlsonj errno = 0;
9593431Scarlsonj subtype = strtoul(value, &cp, 0);
9603431Scarlsonj if (value == cp || errno != 0 || *cp != ',') {
9613431Scarlsonj dhcpmsg(MSG_ERR, "get_smach_cid: "
9623431Scarlsonj "cannot parse enterprise in %s",
9633431Scarlsonj value);
9643431Scarlsonj goto no_specified_id;
9653431Scarlsonj }
9663431Scarlsonj value = cp + 1;
9673431Scarlsonj slen = strlen(value);
9683431Scarlsonj client_id_len = (slen + 1) / 2;
9693431Scarlsonj den = malloc(sizeof (*den) + client_id_len);
9703431Scarlsonj if (den == NULL)
9713431Scarlsonj goto alloc_failure;
9723431Scarlsonj den->den_dutype = htons(duidtype);
9733431Scarlsonj DHCPV6_SET_ENTNUM(den, subtype);
9743431Scarlsonj if (hexascii_to_octet(value, slen, den + 1,
9753431Scarlsonj &client_id_len) != 0) {
9763431Scarlsonj dhcpmsg(MSG_ERROR, "get_smach_cid: "
9773431Scarlsonj "cannot parse hex string in %s",
9783431Scarlsonj value);
9793431Scarlsonj free(den);
9803431Scarlsonj goto no_specified_id;
9813431Scarlsonj }
9823431Scarlsonj dsmp->dsm_cid = (uchar_t *)den;
9833431Scarlsonj break;
9843431Scarlsonj }
9853431Scarlsonj default:
9863431Scarlsonj slen = strlen(value);
9873431Scarlsonj client_id_len = (slen + 1) / 2;
9883431Scarlsonj cp = malloc(client_id_len);
9893431Scarlsonj if (cp == NULL)
9903431Scarlsonj goto alloc_failure;
9913431Scarlsonj if (hexascii_to_octet(value, slen, cp,
9923431Scarlsonj &client_id_len) != 0) {
9933431Scarlsonj dhcpmsg(MSG_ERROR, "get_smach_cid: "
9943431Scarlsonj "cannot parse hex string in %s",
9953431Scarlsonj value);
9963431Scarlsonj free(cp);
9973431Scarlsonj goto no_specified_id;
9983431Scarlsonj }
9993431Scarlsonj dsmp->dsm_cid = (uchar_t *)cp;
10003431Scarlsonj break;
10013431Scarlsonj }
10023431Scarlsonj dsmp->dsm_cidlen = client_id_len;
10033431Scarlsonj if (!pif->pif_isv6) {
10043431Scarlsonj (void) memmove(dsmp->dsm_cid + 5,
10053431Scarlsonj dsmp->dsm_cid, client_id_len - 5);
10063431Scarlsonj dsmp->dsm_cid[0] = 255;
10073431Scarlsonj dsmp->dsm_cid[1] = lif->lif_iaid >> 24;
10083431Scarlsonj dsmp->dsm_cid[2] = lif->lif_iaid >> 16;
10093431Scarlsonj dsmp->dsm_cid[3] = lif->lif_iaid >> 8;
10103431Scarlsonj dsmp->dsm_cid[4] = lif->lif_iaid;
10113431Scarlsonj }
10123431Scarlsonj return (DHCP_IPC_SUCCESS);
10133431Scarlsonj }
10143431Scarlsonj
10153431Scarlsonj if (pif->pif_isv6) {
10163431Scarlsonj dhcpmsg(MSG_ERROR,
10173431Scarlsonj "get_smach_cid: client ID for %s invalid: %s",
10183431Scarlsonj dsmp->dsm_name, value);
10193431Scarlsonj } else if (strncasecmp("0x", value, 2) == 0 &&
10203431Scarlsonj value[2] != '\0') {
10213431Scarlsonj /* skip past the 0x and convert the value to binary */
10223431Scarlsonj value += 2;
10233431Scarlsonj slen = strlen(value);
10243431Scarlsonj client_id_len = (slen + 1) / 2;
10253431Scarlsonj dsmp->dsm_cid = malloc(client_id_len);
10263431Scarlsonj if (dsmp->dsm_cid == NULL)
10273431Scarlsonj goto alloc_failure;
10283431Scarlsonj if (hexascii_to_octet(value, slen, dsmp->dsm_cid,
10293431Scarlsonj &client_id_len) == 0) {
10303431Scarlsonj dsmp->dsm_cidlen = client_id_len;
10313431Scarlsonj return (DHCP_IPC_SUCCESS);
10323431Scarlsonj }
10333431Scarlsonj dhcpmsg(MSG_WARNING, "get_smach_cid: cannot convert "
10343431Scarlsonj "hex value for Client ID on %s", dsmp->dsm_name);
10353431Scarlsonj } else {
10363431Scarlsonj client_id_len = strlen(value);
10373431Scarlsonj dsmp->dsm_cid = malloc(client_id_len);
10383431Scarlsonj if (dsmp->dsm_cid == NULL)
10393431Scarlsonj goto alloc_failure;
104011739SY.Zhang@Sun.COM dsmp->dsm_cidlen = client_id_len;
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
smach_count(void)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
discard_default_routes(dhcp_smach_t * dsmp)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
remove_default_routes(dhcp_smach_t * dsmp)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
reset_smach(dhcp_smach_t * dsmp)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
refresh_smach(dhcp_smach_t * dsmp)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
refresh_smachs(iu_eh_t * eh,int sig,void * arg)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
nuke_smach_list(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,
13229633Sjames.d.carlson@sun.com DF_RELEASE_ON_SIGTERM) ||
13239633Sjames.d.carlson@sun.com df_get_bool(dsmp->dsm_name, isv6,
13249633Sjames.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 *
insert_lease(dhcp_smach_t * dsmp)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
hold_lease(dhcp_lease_t * dlp)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
release_lease(dhcp_lease_t * dlp)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
remove_lease(dhcp_lease_t * dlp)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
cancel_lease_timer(dhcp_lease_t * dlp,dhcp_timer_t * dt)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
cancel_lease_timers(dhcp_lease_t * dlp)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
schedule_lease_timer(dhcp_lease_t * dlp,dhcp_timer_t * dt,iu_tq_callback_t * expire)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
deprecate_leases(dhcp_smach_t * dsmp)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
verify_smach(dhcp_smach_t * dsmp)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:
1614*12989SVasumathi.Sundaram@oracle.COM finished_smach(dsmp, DHCP_IPC_E_INVIF);
16153431Scarlsonj release_smach(dsmp);
16163431Scarlsonj
16173431Scarlsonj return (B_FALSE);
16183431Scarlsonj }
1619