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