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