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