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