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