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