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