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-2017, 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_dhcp_domainname); 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_dhcp_domainname); 1234 dsmp->dsm_dhcp_domainname = 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 } 1634