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