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