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 2010 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 dsmp->dsm_cidlen = client_id_len; 1042 (void) memcpy(dsmp->dsm_cid, value, client_id_len); 1043 return (DHCP_IPC_SUCCESS); 1044 } 1045 } 1046 no_specified_id: 1047 1048 /* 1049 * There was either no user-specified Client ID value, or we were 1050 * unable to parse it. We need to determine if a Client ID is required 1051 * and, if so, generate one. 1052 * 1053 * If it's IPv4, not in an IPMP group, and not a logical interface, 1054 * then we need to preserve backward-compatibility by avoiding 1055 * new-fangled DUID/IAID construction. (Note: even for IPMP test 1056 * addresses, we construct a DUID/IAID since we may renew a lease for 1057 * an IPMP test address on any functioning IP interface in the group.) 1058 */ 1059 if (!pif->pif_isv6 && pif->pif_grifname[0] == '\0' && 1060 strchr(dsmp->dsm_name, ':') == NULL) { 1061 if (pif->pif_hwtype == ARPHRD_IB) { 1062 /* 1063 * This comes from the DHCP over IPoIB specification. 1064 * In the absence of an user specified client id, IPoIB 1065 * automatically uses the required format, with the 1066 * unique 4 octet value set to 0 (since IPoIB driver 1067 * allows only a single interface on a port with a 1068 * specific GID to belong to an IP subnet (PSARC 1069 * 2001/289, FWARC 2002/702). 1070 * 1071 * Type Client-Identifier 1072 * +-----+-----+-----+-----+-----+----....----+ 1073 * | 0 | 0 (4 octets) | GID (16 octets)| 1074 * +-----+-----+-----+-----+-----+----....----+ 1075 */ 1076 dsmp->dsm_cidlen = 1 + 4 + 16; 1077 dsmp->dsm_cid = client_id = malloc(dsmp->dsm_cidlen); 1078 if (dsmp->dsm_cid == NULL) 1079 goto alloc_failure; 1080 1081 /* 1082 * Pick the GID from the mac address. The format 1083 * of the hardware address is: 1084 * +-----+-----+-----+-----+----....----+ 1085 * | QPN (4 octets) | GID (16 octets)| 1086 * +-----+-----+-----+-----+----....----+ 1087 */ 1088 (void) memcpy(client_id + 5, pif->pif_hwaddr + 4, 1089 pif->pif_hwlen - 4); 1090 (void) memset(client_id, 0, 5); 1091 } 1092 return (DHCP_IPC_SUCCESS); 1093 } 1094 1095 /* 1096 * Now check for a saved DUID. If there is one, then use it. If there 1097 * isn't, then generate a new one. For IPv4, we need to construct the 1098 * RFC 4361 Client ID with this value and the LIF's IAID. 1099 */ 1100 if (global_duid == NULL && 1101 (global_duid = read_stable_duid(&global_duidlen)) == NULL) { 1102 global_duid = make_stable_duid(pif->pif_name, &global_duidlen); 1103 if (global_duid == NULL) 1104 goto alloc_failure; 1105 duid_retry(NULL, NULL); 1106 } 1107 1108 if (pif->pif_isv6) { 1109 dsmp->dsm_cid = malloc(global_duidlen); 1110 if (dsmp->dsm_cid == NULL) 1111 goto alloc_failure; 1112 (void) memcpy(dsmp->dsm_cid, global_duid, global_duidlen); 1113 dsmp->dsm_cidlen = global_duidlen; 1114 } else { 1115 dsmp->dsm_cid = malloc(5 + global_duidlen); 1116 if (dsmp->dsm_cid == NULL) 1117 goto alloc_failure; 1118 dsmp->dsm_cid[0] = 255; 1119 dsmp->dsm_cid[1] = lif->lif_iaid >> 24; 1120 dsmp->dsm_cid[2] = lif->lif_iaid >> 16; 1121 dsmp->dsm_cid[3] = lif->lif_iaid >> 8; 1122 dsmp->dsm_cid[4] = lif->lif_iaid; 1123 (void) memcpy(dsmp->dsm_cid + 5, global_duid, global_duidlen); 1124 dsmp->dsm_cidlen = 5 + global_duidlen; 1125 } 1126 1127 return (DHCP_IPC_SUCCESS); 1128 1129 alloc_failure: 1130 dhcpmsg(MSG_ERR, "get_smach_cid: cannot allocate Client Id for %s", 1131 dsmp->dsm_name); 1132 return (DHCP_IPC_E_MEMORY); 1133 } 1134 1135 /* 1136 * smach_count(): returns the number of state machines running 1137 * 1138 * input: void 1139 * output: uint_t: the number of state machines 1140 */ 1141 1142 uint_t 1143 smach_count(void) 1144 { 1145 return (global_smach_count); 1146 } 1147 1148 /* 1149 * discard_default_routes(): removes a state machine's default routes alone. 1150 * 1151 * input: dhcp_smach_t *: the state machine whose default routes need to be 1152 * discarded 1153 * output: void 1154 */ 1155 1156 void 1157 discard_default_routes(dhcp_smach_t *dsmp) 1158 { 1159 free(dsmp->dsm_routers); 1160 dsmp->dsm_routers = NULL; 1161 dsmp->dsm_nrouters = 0; 1162 } 1163 1164 /* 1165 * remove_default_routes(): removes a state machine's default routes from the 1166 * kernel and from the state machine. 1167 * 1168 * input: dhcp_smach_t *: the state machine whose default routes need to be 1169 * removed 1170 * output: void 1171 */ 1172 1173 void 1174 remove_default_routes(dhcp_smach_t *dsmp) 1175 { 1176 int idx; 1177 uint32_t ifindex; 1178 1179 if (dsmp->dsm_routers != NULL) { 1180 ifindex = dsmp->dsm_lif->lif_pif->pif_index; 1181 for (idx = dsmp->dsm_nrouters - 1; idx >= 0; idx--) { 1182 if (del_default_route(ifindex, 1183 &dsmp->dsm_routers[idx])) { 1184 dhcpmsg(MSG_DEBUG, "remove_default_routes: " 1185 "removed %s from %s", 1186 inet_ntoa(dsmp->dsm_routers[idx]), 1187 dsmp->dsm_name); 1188 } else { 1189 dhcpmsg(MSG_INFO, "remove_default_routes: " 1190 "unable to remove %s from %s", 1191 inet_ntoa(dsmp->dsm_routers[idx]), 1192 dsmp->dsm_name); 1193 } 1194 } 1195 discard_default_routes(dsmp); 1196 } 1197 } 1198 1199 /* 1200 * reset_smach(): resets a state machine to its initial state 1201 * 1202 * input: dhcp_smach_t *: the state machine to reset 1203 * output: void 1204 */ 1205 1206 void 1207 reset_smach(dhcp_smach_t *dsmp) 1208 { 1209 dsmp->dsm_dflags &= ~DHCP_IF_FAILED; 1210 1211 remove_default_routes(dsmp); 1212 1213 free_pkt_list(&dsmp->dsm_recv_pkt_list); 1214 free_pkt_entry(dsmp->dsm_ack); 1215 if (dsmp->dsm_orig_ack != dsmp->dsm_ack) 1216 free_pkt_entry(dsmp->dsm_orig_ack); 1217 dsmp->dsm_ack = dsmp->dsm_orig_ack = NULL; 1218 1219 free(dsmp->dsm_reqhost); 1220 dsmp->dsm_reqhost = NULL; 1221 1222 cancel_smach_timers(dsmp); 1223 1224 (void) set_smach_state(dsmp, INIT); 1225 if (dsmp->dsm_isv6) { 1226 dsmp->dsm_server = ipv6_all_dhcp_relay_and_servers; 1227 } else { 1228 IN6_IPADDR_TO_V4MAPPED(htonl(INADDR_BROADCAST), 1229 &dsmp->dsm_server); 1230 } 1231 dsmp->dsm_neg_hrtime = gethrtime(); 1232 /* 1233 * We must never get here with a script running, since it means we're 1234 * resetting an smach that is still in the middle of another state 1235 * transition with a pending dsm_script_callback. 1236 */ 1237 assert(dsmp->dsm_script_pid == -1); 1238 } 1239 1240 /* 1241 * refresh_smach(): refreshes a given state machine, as though awakened from 1242 * hibernation or by lower layer "link up." 1243 * 1244 * input: dhcp_smach_t *: state machine to refresh 1245 * output: void 1246 */ 1247 1248 void 1249 refresh_smach(dhcp_smach_t *dsmp) 1250 { 1251 if (dsmp->dsm_state == BOUND || dsmp->dsm_state == RENEWING || 1252 dsmp->dsm_state == REBINDING || dsmp->dsm_state == INFORMATION) { 1253 dhcpmsg(MSG_WARNING, "refreshing state on %s", dsmp->dsm_name); 1254 cancel_smach_timers(dsmp); 1255 if (dsmp->dsm_state == INFORMATION) 1256 dhcp_inform(dsmp); 1257 else 1258 dhcp_init_reboot(dsmp); 1259 } 1260 } 1261 1262 /* 1263 * refresh_smachs(): refreshes all finite leases under DHCP control 1264 * 1265 * input: iu_eh_t *: unused 1266 * int: unused 1267 * void *: unused 1268 * output: void 1269 */ 1270 1271 /* ARGSUSED */ 1272 void 1273 refresh_smachs(iu_eh_t *eh, int sig, void *arg) 1274 { 1275 boolean_t isv6 = B_FALSE; 1276 dhcp_smach_t *dsmp; 1277 1278 for (;;) { 1279 for (dsmp = next_smach(NULL, isv6); dsmp != NULL; 1280 dsmp = next_smach(dsmp, isv6)) { 1281 refresh_smach(dsmp); 1282 } 1283 if (isv6) 1284 break; 1285 isv6 = B_TRUE; 1286 } 1287 } 1288 1289 /* 1290 * nuke_smach_list(): delete the state machine list. For use when the 1291 * dhcpagent is exiting. 1292 * 1293 * input: none 1294 * output: none 1295 */ 1296 1297 void 1298 nuke_smach_list(void) 1299 { 1300 boolean_t isv6 = B_FALSE; 1301 dhcp_smach_t *dsmp, *dsmp_next; 1302 1303 for (;;) { 1304 for (dsmp = next_smach(NULL, isv6); dsmp != NULL; 1305 dsmp = dsmp_next) { 1306 int status; 1307 1308 dsmp_next = next_smach(dsmp, isv6); 1309 1310 /* If we're already dropping or releasing, skip */ 1311 if (dsmp->dsm_droprelease) 1312 continue; 1313 dsmp->dsm_droprelease = B_TRUE; 1314 1315 cancel_smach_timers(dsmp); 1316 1317 /* 1318 * If the script is started by script_start, dhcp_drop 1319 * and dhcp_release should and will only be called 1320 * after the script exits. 1321 */ 1322 if (df_get_bool(dsmp->dsm_name, isv6, 1323 DF_RELEASE_ON_SIGTERM) || 1324 df_get_bool(dsmp->dsm_name, isv6, 1325 DF_VERIFIED_LEASE_ONLY)) { 1326 if (script_start(dsmp, isv6 ? EVENT_RELEASE6 : 1327 EVENT_RELEASE, dhcp_release, 1328 "DHCP agent is exiting", &status)) { 1329 continue; 1330 } 1331 if (status == 1) 1332 continue; 1333 } 1334 (void) script_start(dsmp, isv6 ? EVENT_DROP6 : 1335 EVENT_DROP, dhcp_drop, NULL, NULL); 1336 } 1337 if (isv6) 1338 break; 1339 isv6 = B_TRUE; 1340 } 1341 } 1342 1343 /* 1344 * insert_lease(): Create a lease structure on a given state machine. The 1345 * lease holds a reference to the state machine. 1346 * 1347 * input: dhcp_smach_t *: state machine 1348 * output: dhcp_lease_t *: newly-created lease 1349 */ 1350 1351 dhcp_lease_t * 1352 insert_lease(dhcp_smach_t *dsmp) 1353 { 1354 dhcp_lease_t *dlp; 1355 1356 if ((dlp = calloc(1, sizeof (*dlp))) == NULL) 1357 return (NULL); 1358 dlp->dl_smach = dsmp; 1359 dlp->dl_hold_count = 1; 1360 init_timer(&dlp->dl_t1, 0); 1361 init_timer(&dlp->dl_t2, 0); 1362 insque(dlp, &dsmp->dsm_leases); 1363 dhcpmsg(MSG_DEBUG2, "insert_lease: new lease for %s", dsmp->dsm_name); 1364 return (dlp); 1365 } 1366 1367 /* 1368 * hold_lease(): acquires a hold on a lease 1369 * 1370 * input: dhcp_lease_t *: the lease to acquire a hold on 1371 * output: void 1372 */ 1373 1374 void 1375 hold_lease(dhcp_lease_t *dlp) 1376 { 1377 dlp->dl_hold_count++; 1378 1379 dhcpmsg(MSG_DEBUG2, "hold_lease: hold count on lease for %s: %d", 1380 dlp->dl_smach->dsm_name, dlp->dl_hold_count); 1381 } 1382 1383 /* 1384 * release_lease(): releases a hold previously acquired on a lease. 1385 * If the hold count reaches 0, the lease is freed. 1386 * 1387 * input: dhcp_lease_t *: the lease to release the hold on 1388 * output: void 1389 */ 1390 1391 void 1392 release_lease(dhcp_lease_t *dlp) 1393 { 1394 if (dlp->dl_hold_count == 0) { 1395 dhcpmsg(MSG_CRIT, "release_lease: extraneous release"); 1396 return; 1397 } 1398 1399 if (dlp->dl_hold_count == 1 && !dlp->dl_removed) { 1400 dhcpmsg(MSG_CRIT, "release_lease: missing removal"); 1401 return; 1402 } 1403 1404 if (--dlp->dl_hold_count == 0) { 1405 dhcpmsg(MSG_DEBUG, 1406 "release_lease: freeing lease on state machine %s", 1407 dlp->dl_smach->dsm_name); 1408 free(dlp); 1409 } else { 1410 dhcpmsg(MSG_DEBUG2, 1411 "release_lease: hold count on lease for %s: %d", 1412 dlp->dl_smach->dsm_name, dlp->dl_hold_count); 1413 } 1414 } 1415 1416 /* 1417 * remove_lease(): removes a given lease from the state machine and drops the 1418 * state machine's hold on the lease. 1419 * 1420 * input: dhcp_lease_t *: the lease to remove 1421 * output: void 1422 */ 1423 1424 void 1425 remove_lease(dhcp_lease_t *dlp) 1426 { 1427 if (dlp->dl_removed) { 1428 dhcpmsg(MSG_CRIT, "remove_lease: extraneous removal"); 1429 } else { 1430 dhcp_lif_t *lif, *lifnext; 1431 uint_t nlifs; 1432 1433 dhcpmsg(MSG_DEBUG, 1434 "remove_lease: removed lease from state machine %s", 1435 dlp->dl_smach->dsm_name); 1436 dlp->dl_removed = B_TRUE; 1437 remque(dlp); 1438 1439 cancel_lease_timers(dlp); 1440 1441 lif = dlp->dl_lifs; 1442 nlifs = dlp->dl_nlifs; 1443 for (; nlifs > 0; nlifs--, lif = lifnext) { 1444 lifnext = lif->lif_next; 1445 unplumb_lif(lif); 1446 } 1447 1448 release_lease(dlp); 1449 } 1450 } 1451 1452 /* 1453 * cancel_lease_timer(): cancels a lease-related timer 1454 * 1455 * input: dhcp_lease_t *: the lease to operate on 1456 * dhcp_timer_t *: the timer to cancel 1457 * output: void 1458 */ 1459 1460 static void 1461 cancel_lease_timer(dhcp_lease_t *dlp, dhcp_timer_t *dt) 1462 { 1463 if (dt->dt_id == -1) 1464 return; 1465 if (cancel_timer(dt)) { 1466 release_lease(dlp); 1467 } else { 1468 dhcpmsg(MSG_WARNING, 1469 "cancel_lease_timer: cannot cancel timer"); 1470 } 1471 } 1472 1473 /* 1474 * cancel_lease_timers(): cancels an lease's pending timers 1475 * 1476 * input: dhcp_lease_t *: the lease to operate on 1477 * output: void 1478 */ 1479 1480 void 1481 cancel_lease_timers(dhcp_lease_t *dlp) 1482 { 1483 cancel_lease_timer(dlp, &dlp->dl_t1); 1484 cancel_lease_timer(dlp, &dlp->dl_t2); 1485 } 1486 1487 /* 1488 * schedule_lease_timer(): schedules a lease-related timer 1489 * 1490 * input: dhcp_lease_t *: the lease to operate on 1491 * dhcp_timer_t *: the timer to schedule 1492 * iu_tq_callback_t *: the callback to call upon firing 1493 * output: boolean_t: B_TRUE if the timer was scheduled successfully 1494 */ 1495 1496 boolean_t 1497 schedule_lease_timer(dhcp_lease_t *dlp, dhcp_timer_t *dt, 1498 iu_tq_callback_t *expire) 1499 { 1500 /* 1501 * If there's a timer running, cancel it and release its lease 1502 * reference. 1503 */ 1504 if (dt->dt_id != -1) { 1505 if (!cancel_timer(dt)) 1506 return (B_FALSE); 1507 release_lease(dlp); 1508 } 1509 1510 if (schedule_timer(dt, expire, dlp)) { 1511 hold_lease(dlp); 1512 return (B_TRUE); 1513 } else { 1514 dhcpmsg(MSG_WARNING, 1515 "schedule_lease_timer: cannot schedule timer"); 1516 return (B_FALSE); 1517 } 1518 } 1519 1520 /* 1521 * deprecate_leases(): remove all of the leases from a given state machine 1522 * 1523 * input: dhcp_smach_t *: the state machine 1524 * output: none 1525 */ 1526 1527 void 1528 deprecate_leases(dhcp_smach_t *dsmp) 1529 { 1530 dhcp_lease_t *dlp; 1531 1532 /* 1533 * note that due to infelicities in the routing code, any default 1534 * routes must be removed prior to canonizing or deprecating the LIF. 1535 */ 1536 1537 remove_default_routes(dsmp); 1538 1539 while ((dlp = dsmp->dsm_leases) != NULL) 1540 remove_lease(dlp); 1541 } 1542 1543 /* 1544 * verify_smach(): if the state machine is in a bound state, then verify the 1545 * standing of the configured interfaces. Abandon those that 1546 * the user has modified. If we end up with no valid leases, 1547 * then just terminate the state machine. 1548 * 1549 * input: dhcp_smach_t *: the state machine 1550 * output: boolean_t: B_TRUE if the state machine is still valid. 1551 * note: assumes caller holds a state machine reference; as with most 1552 * callback functions. 1553 */ 1554 1555 boolean_t 1556 verify_smach(dhcp_smach_t *dsmp) 1557 { 1558 dhcp_lease_t *dlp, *dlpn; 1559 1560 if (dsmp->dsm_dflags & DHCP_IF_REMOVED) { 1561 release_smach(dsmp); 1562 return (B_FALSE); 1563 } 1564 1565 if (!dsmp->dsm_isv6) { 1566 /* 1567 * If this is DHCPv4, then verify the main LIF. 1568 */ 1569 if (!verify_lif(dsmp->dsm_lif)) 1570 goto smach_terminate; 1571 } 1572 1573 /* 1574 * If we're not in one of the bound states, then there are no LIFs to 1575 * verify here. 1576 */ 1577 if (dsmp->dsm_state != BOUND && 1578 dsmp->dsm_state != RENEWING && 1579 dsmp->dsm_state != REBINDING) { 1580 release_smach(dsmp); 1581 return (B_TRUE); 1582 } 1583 1584 for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlpn) { 1585 dhcp_lif_t *lif, *lifnext; 1586 uint_t nlifs; 1587 1588 dlpn = dlp->dl_next; 1589 lif = dlp->dl_lifs; 1590 nlifs = dlp->dl_nlifs; 1591 for (; nlifs > 0; lif = lifnext, nlifs--) { 1592 lifnext = lif->lif_next; 1593 if (!verify_lif(lif)) { 1594 /* 1595 * User has manipulated the interface. Even 1596 * if we plumbed it, we must now disown it. 1597 */ 1598 lif->lif_plumbed = B_FALSE; 1599 remove_lif(lif); 1600 } 1601 } 1602 if (dlp->dl_nlifs == 0) 1603 remove_lease(dlp); 1604 } 1605 1606 /* 1607 * If there are leases left, then everything's ok. 1608 */ 1609 if (dsmp->dsm_leases != NULL) { 1610 release_smach(dsmp); 1611 return (B_TRUE); 1612 } 1613 1614 smach_terminate: 1615 finished_smach(dsmp, DHCP_IPC_E_UNKIF); 1616 release_smach(dsmp); 1617 1618 return (B_FALSE); 1619 } 1620