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 (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. 23 * 24 * This module contains core functions for managing DHCP state machine 25 * instances. 26 */ 27 28 #include <assert.h> 29 #include <stdlib.h> 30 #include <search.h> 31 #include <string.h> 32 #include <ctype.h> 33 #include <sys/types.h> 34 #include <sys/socket.h> 35 #include <netinet/in.h> 36 #include <netinet/arp.h> 37 #include <arpa/inet.h> 38 #include <dhcpmsg.h> 39 #include <dhcpagent_util.h> 40 #include <dhcp_stable.h> 41 #include <dhcp_inittab.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 * parse_param_list(): parse a parameter list. 85 * 86 * input: const char *: parameter list string with comma-separated entries 87 * uint_t *: return parameter; number of entries decoded 88 * const char *: name of parameter list for logging purposes 89 * dhcp_smach_t *: smach pointer for logging 90 * output: uint16_t *: allocated array of parameters, or NULL if none. 91 */ 92 93 static uint16_t * 94 parse_param_list(const char *param_list, uint_t *param_cnt, 95 const char *param_name, dhcp_smach_t *dsmp) 96 { 97 int i, maxparam; 98 char tsym[DSYM_MAX_SYM_LEN + 1]; 99 uint16_t *params; 100 const char *cp; 101 dhcp_symbol_t *entry; 102 103 *param_cnt = 0; 104 105 if (param_list == NULL) 106 return (NULL); 107 108 for (maxparam = 1, i = 0; param_list[i] != '\0'; i++) { 109 if (param_list[i] == ',') 110 maxparam++; 111 } 112 113 params = malloc(maxparam * sizeof (*params)); 114 if (params == NULL) { 115 dhcpmsg(MSG_WARNING, 116 "cannot allocate parameter %s list for %s (continuing)", 117 param_name, dsmp->dsm_name); 118 return (NULL); 119 } 120 121 for (i = 0; i < maxparam; ) { 122 123 if (isspace(*param_list)) 124 param_list++; 125 126 /* extract the next element on the list */ 127 cp = strchr(param_list, ','); 128 if (cp == NULL || cp - param_list >= sizeof (tsym)) 129 (void) strlcpy(tsym, param_list, sizeof (tsym)); 130 else 131 (void) strlcpy(tsym, param_list, cp - param_list + 1); 132 133 /* LINTED -- do nothing with blanks on purpose */ 134 if (tsym[0] == '\0') { 135 ; 136 } else if (isalpha(tsym[0])) { 137 entry = inittab_getbyname(ITAB_CAT_SITE | 138 ITAB_CAT_STANDARD | 139 (dsmp->dsm_isv6 ? ITAB_CAT_V6 : 0), 140 ITAB_CONS_INFO, tsym); 141 if (entry == NULL) { 142 dhcpmsg(MSG_INFO, "ignored unknown %s list " 143 "entry '%s' for %s", param_name, tsym, 144 dsmp->dsm_name); 145 } else { 146 params[i++] = entry->ds_code; 147 free(entry); 148 } 149 } else { 150 params[i++] = strtoul(tsym, NULL, 0); 151 } 152 if (cp == NULL) 153 break; 154 param_list = cp + 1; 155 } 156 157 *param_cnt = i; 158 return (params); 159 } 160 161 /* 162 * insert_smach(): Create a state machine instance on a given logical 163 * interface. The state machine holds the caller's LIF 164 * reference on success, and frees it on failure. 165 * 166 * input: dhcp_lif_t *: logical interface name 167 * int *: set to DHCP_IPC_E_* if creation fails 168 * output: dhcp_smach_t *: state machine instance 169 */ 170 171 dhcp_smach_t * 172 insert_smach(dhcp_lif_t *lif, int *error) 173 { 174 dhcp_smach_t *dsmp, *alt_primary; 175 boolean_t isv6; 176 const char *plist; 177 178 if ((dsmp = calloc(1, sizeof (*dsmp))) == NULL) { 179 dhcpmsg(MSG_ERR, "cannot allocate state machine entry for %s", 180 lif->lif_name); 181 remove_lif(lif); 182 release_lif(lif); 183 *error = DHCP_IPC_E_MEMORY; 184 return (NULL); 185 } 186 dsmp->dsm_name = lif->lif_name; 187 dsmp->dsm_lif = lif; 188 dsmp->dsm_hold_count = 1; 189 dsmp->dsm_state = INIT; 190 dsmp->dsm_dflags = DHCP_IF_REMOVED; /* until added to list */ 191 isv6 = lif->lif_pif->pif_isv6; 192 193 /* 194 * Now that we have a controlling LIF, we need to assign an IAID to 195 * that LIF. 196 */ 197 if (lif->lif_iaid == 0 && 198 (lif->lif_iaid = read_stable_iaid(lif->lif_name)) == 0) { 199 static uint32_t iaidctr = 0x80000000u; 200 201 /* 202 * If this is a logical interface, then use an arbitrary seed 203 * value. Otherwise, use the ifIndex. 204 */ 205 lif->lif_iaid = make_stable_iaid(lif->lif_name, 206 strchr(lif->lif_name, ':') != NULL ? iaidctr++ : 207 lif->lif_pif->pif_index); 208 dhcpmsg(MSG_INFO, 209 "insert_smach: manufactured IAID %u for v%d %s", 210 lif->lif_iaid, isv6 ? 6 : 4, lif->lif_name); 211 hold_lif(lif); 212 iaid_retry(NULL, lif); 213 } 214 215 if (isv6) { 216 dsmp->dsm_dflags |= DHCP_IF_V6; 217 dsmp->dsm_server = ipv6_all_dhcp_relay_and_servers; 218 219 /* 220 * With DHCPv6, we do all of our I/O using the common 221 * v6_sock_fd. There's no need for per-interface file 222 * descriptors because we have IPV6_PKTINFO. 223 */ 224 } else { 225 IN6_IPADDR_TO_V4MAPPED(htonl(INADDR_BROADCAST), 226 &dsmp->dsm_server); 227 228 /* 229 * With IPv4 DHCP, we use a socket per lif. 230 */ 231 if (!open_ip_lif(lif, INADDR_ANY, B_TRUE)) { 232 dhcpmsg(MSG_ERR, "unable to open socket for %s", 233 lif->lif_name); 234 /* This will also dispose of the LIF */ 235 release_smach(dsmp); 236 *error = DHCP_IPC_E_SOCKET; 237 return (NULL); 238 } 239 } 240 241 script_init(dsmp); 242 ipc_action_init(&dsmp->dsm_ia); 243 244 dsmp->dsm_neg_hrtime = gethrtime(); 245 dsmp->dsm_offer_timer = -1; 246 dsmp->dsm_start_timer = -1; 247 dsmp->dsm_retrans_timer = -1; 248 249 /* 250 * Initialize the parameter request and ignore lists, if any. 251 */ 252 plist = df_get_string(dsmp->dsm_name, isv6, DF_PARAM_REQUEST_LIST); 253 dsmp->dsm_prl = parse_param_list(plist, &dsmp->dsm_prllen, "request", 254 dsmp); 255 plist = df_get_string(dsmp->dsm_name, isv6, DF_PARAM_IGNORE_LIST); 256 dsmp->dsm_pil = parse_param_list(plist, &dsmp->dsm_pillen, "ignore", 257 dsmp); 258 259 dsmp->dsm_offer_wait = df_get_int(dsmp->dsm_name, isv6, 260 DF_OFFER_WAIT); 261 262 /* 263 * If there is no primary of this type, and there is one of the other, 264 * then make this one primary if it's on the same named PIF. 265 */ 266 if (primary_smach(isv6) == NULL && 267 (alt_primary = primary_smach(!isv6)) != NULL) { 268 if (strcmp(lif->lif_pif->pif_name, 269 alt_primary->dsm_lif->lif_pif->pif_name) == 0) { 270 dhcpmsg(MSG_DEBUG, 271 "insert_smach: making %s primary for v%d", 272 dsmp->dsm_name, isv6 ? 6 : 4); 273 dsmp->dsm_dflags |= DHCP_IF_PRIMARY; 274 } 275 } 276 277 /* 278 * We now have at least one state machine running, so cancel any 279 * running inactivity timer. 280 */ 281 if (inactivity_id != -1 && 282 iu_cancel_timer(tq, inactivity_id, NULL) == 1) 283 inactivity_id = -1; 284 285 dsmp->dsm_dflags &= ~DHCP_IF_REMOVED; 286 insque(dsmp, &lif->lif_smachs); 287 global_smach_count++; 288 dhcpmsg(MSG_DEBUG2, "insert_smach: inserted %s", dsmp->dsm_name); 289 290 return (dsmp); 291 } 292 293 /* 294 * hold_smach(): acquires a hold on a state machine 295 * 296 * input: dhcp_smach_t *: the state machine to acquire a hold on 297 * output: void 298 */ 299 300 void 301 hold_smach(dhcp_smach_t *dsmp) 302 { 303 dsmp->dsm_hold_count++; 304 305 dhcpmsg(MSG_DEBUG2, "hold_smach: hold count on %s: %d", 306 dsmp->dsm_name, dsmp->dsm_hold_count); 307 } 308 309 /* 310 * free_smach(): frees the memory occupied by a state machine 311 * 312 * input: dhcp_smach_t *: the DHCP state machine to free 313 * output: void 314 */ 315 316 static void 317 free_smach(dhcp_smach_t *dsmp) 318 { 319 dhcpmsg(MSG_DEBUG, "free_smach: freeing state machine %s", 320 dsmp->dsm_name); 321 322 deprecate_leases(dsmp); 323 remove_lif(dsmp->dsm_lif); 324 release_lif(dsmp->dsm_lif); 325 free_pkt_list(&dsmp->dsm_recv_pkt_list); 326 if (dsmp->dsm_ack != dsmp->dsm_orig_ack) 327 free_pkt_entry(dsmp->dsm_orig_ack); 328 free_pkt_entry(dsmp->dsm_ack); 329 free(dsmp->dsm_send_pkt.pkt); 330 free(dsmp->dsm_cid); 331 free(dsmp->dsm_prl); 332 free(dsmp->dsm_pil); 333 free(dsmp->dsm_routers); 334 free(dsmp->dsm_reqhost); 335 free(dsmp); 336 337 /* no big deal if this fails */ 338 if (global_smach_count == 0 && inactivity_id == -1) { 339 inactivity_id = iu_schedule_timer(tq, DHCP_INACTIVITY_WAIT, 340 inactivity_shutdown, NULL); 341 } 342 } 343 344 /* 345 * release_smach(): releases a hold previously acquired on a state machine. 346 * If the hold count reaches 0, the state machine is freed. 347 * 348 * input: dhcp_smach_t *: the state machine entry to release the hold on 349 * output: void 350 */ 351 352 void 353 release_smach(dhcp_smach_t *dsmp) 354 { 355 if (dsmp->dsm_hold_count == 0) { 356 dhcpmsg(MSG_CRIT, "release_smach: extraneous release"); 357 return; 358 } 359 360 if (dsmp->dsm_hold_count == 1 && 361 !(dsmp->dsm_dflags & DHCP_IF_REMOVED)) { 362 dhcpmsg(MSG_CRIT, "release_smach: missing removal"); 363 return; 364 } 365 366 if (--dsmp->dsm_hold_count == 0) { 367 free_smach(dsmp); 368 } else { 369 dhcpmsg(MSG_DEBUG2, "release_smach: hold count on %s: %d", 370 dsmp->dsm_name, dsmp->dsm_hold_count); 371 } 372 } 373 374 /* 375 * next_smach(): state machine iterator function 376 * 377 * input: dhcp_smach_t *: current state machine (or NULL for list start) 378 * boolean_t: B_TRUE if DHCPv6, B_FALSE otherwise 379 * output: dhcp_smach_t *: next state machine in list 380 */ 381 382 dhcp_smach_t * 383 next_smach(dhcp_smach_t *dsmp, boolean_t isv6) 384 { 385 dhcp_lif_t *lif; 386 dhcp_pif_t *pif; 387 388 if (dsmp != NULL) { 389 if (dsmp->dsm_next != NULL) 390 return (dsmp->dsm_next); 391 392 if ((lif = dsmp->dsm_lif) != NULL) 393 lif = lif->lif_next; 394 for (; lif != NULL; lif = lif->lif_next) { 395 if (lif->lif_smachs != NULL) 396 return (lif->lif_smachs); 397 } 398 399 if ((pif = dsmp->dsm_lif->lif_pif) != NULL) 400 pif = pif->pif_next; 401 } else { 402 pif = isv6 ? v6root : v4root; 403 } 404 for (; pif != NULL; pif = pif->pif_next) { 405 for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) { 406 if (lif->lif_smachs != NULL) 407 return (lif->lif_smachs); 408 } 409 } 410 return (NULL); 411 } 412 413 /* 414 * primary_smach(): loop through all state machines of the given type (v4 or 415 * v6) in the system, and locate the one that's primary. 416 * 417 * input: boolean_t: B_TRUE for IPv6 418 * output: dhcp_smach_t *: the primary state machine 419 */ 420 421 dhcp_smach_t * 422 primary_smach(boolean_t isv6) 423 { 424 dhcp_smach_t *dsmp; 425 426 for (dsmp = next_smach(NULL, isv6); dsmp != NULL; 427 dsmp = next_smach(dsmp, isv6)) { 428 if (dsmp->dsm_dflags & DHCP_IF_PRIMARY) 429 break; 430 } 431 return (dsmp); 432 } 433 434 /* 435 * info_primary_smach(): loop through all state machines of the given type (v4 436 * or v6) in the system, and locate the one that should 437 * be considered "primary" for dhcpinfo. 438 * 439 * input: boolean_t: B_TRUE for IPv6 440 * output: dhcp_smach_t *: the dhcpinfo primary state machine 441 */ 442 443 dhcp_smach_t * 444 info_primary_smach(boolean_t isv6) 445 { 446 dhcp_smach_t *bestdsm = NULL; 447 dhcp_smach_t *dsmp; 448 449 for (dsmp = next_smach(NULL, isv6); dsmp != NULL; 450 dsmp = next_smach(dsmp, isv6)) { 451 /* 452 * If there is a primary, then something previously went wrong 453 * with verification, because the caller uses primary_smach() 454 * before calling this routine. There's nothing else we can do 455 * but return failure, as the designated primary must be bad. 456 */ 457 if (dsmp->dsm_dflags & DHCP_IF_PRIMARY) 458 return (NULL); 459 460 /* If we have no information, then we're not primary. */ 461 if (dsmp->dsm_ack == NULL) 462 continue; 463 464 /* 465 * Among those interfaces that have DHCP information, the 466 * "primary" is the one that sorts lexically first. 467 */ 468 if (bestdsm == NULL || 469 strcmp(dsmp->dsm_name, bestdsm->dsm_name) < 0) 470 bestdsm = dsmp; 471 } 472 return (bestdsm); 473 } 474 475 /* 476 * make_primary(): designate a given state machine as being the primary 477 * instance on the primary interface. Note that the user often 478 * thinks in terms of a primary "interface" (rather than just 479 * an instance), so we go to lengths here to keep v4 and v6 in 480 * sync. 481 * 482 * input: dhcp_smach_t *: the primary state machine 483 * output: none 484 */ 485 486 void 487 make_primary(dhcp_smach_t *dsmp) 488 { 489 dhcp_smach_t *old_primary, *alt_primary; 490 dhcp_pif_t *pif; 491 492 if ((old_primary = primary_smach(dsmp->dsm_isv6)) != NULL) 493 old_primary->dsm_dflags &= ~DHCP_IF_PRIMARY; 494 dsmp->dsm_dflags |= DHCP_IF_PRIMARY; 495 496 /* 497 * Find the primary for the other protocol. 498 */ 499 alt_primary = primary_smach(!dsmp->dsm_isv6); 500 501 /* 502 * If it's on a different interface, then cancel that. If it's on the 503 * same interface, then we're done. 504 */ 505 if (alt_primary != NULL) { 506 if (strcmp(alt_primary->dsm_lif->lif_pif->pif_name, 507 dsmp->dsm_lif->lif_pif->pif_name) == 0) 508 return; 509 alt_primary->dsm_dflags &= ~DHCP_IF_PRIMARY; 510 } 511 512 /* 513 * We need a new primary for the other protocol. If the PIF exists, 514 * there must be at least one state machine. Just choose the first for 515 * consistency with insert_smach(). 516 */ 517 if ((pif = lookup_pif_by_name(dsmp->dsm_lif->lif_pif->pif_name, 518 !dsmp->dsm_isv6)) != NULL) { 519 pif->pif_lifs->lif_smachs->dsm_dflags |= DHCP_IF_PRIMARY; 520 } 521 } 522 523 /* 524 * lookup_smach(): finds a state machine by name and type; used for dispatching 525 * user commands. 526 * 527 * input: const char *: the name of the state machine 528 * boolean_t: B_TRUE if DHCPv6, B_FALSE otherwise 529 * output: dhcp_smach_t *: the state machine found 530 */ 531 532 dhcp_smach_t * 533 lookup_smach(const char *smname, boolean_t isv6) 534 { 535 dhcp_smach_t *dsmp; 536 537 for (dsmp = next_smach(NULL, isv6); dsmp != NULL; 538 dsmp = next_smach(dsmp, isv6)) { 539 if (strcmp(dsmp->dsm_name, smname) == 0) 540 break; 541 } 542 return (dsmp); 543 } 544 545 /* 546 * lookup_smach_by_uindex(): iterate through running state machines by 547 * truncated interface index. 548 * 549 * input: uint16_t: the interface index (truncated) 550 * dhcp_smach_t *: the previous state machine, or NULL for start 551 * boolean_t: B_TRUE for DHCPv6, B_FALSE for IPv4 DHCP 552 * output: dhcp_smach_t *: next state machine, or NULL at end of list 553 */ 554 555 dhcp_smach_t * 556 lookup_smach_by_uindex(uint16_t ifindex, dhcp_smach_t *dsmp, boolean_t isv6) 557 { 558 dhcp_pif_t *pif; 559 dhcp_lif_t *lif; 560 561 /* 562 * If the user gives us a state machine, then check that the next one 563 * available is on the same physical interface. If so, then go ahead 564 * and return that. 565 */ 566 if (dsmp != NULL) { 567 pif = dsmp->dsm_lif->lif_pif; 568 if ((dsmp = next_smach(dsmp, isv6)) == NULL) 569 return (NULL); 570 if (pif == dsmp->dsm_lif->lif_pif) 571 return (dsmp); 572 } else { 573 /* Otherwise, start at the beginning of the list */ 574 pif = NULL; 575 } 576 577 /* 578 * Find the next physical interface with the same truncated interface 579 * index, and return the first state machine on that. If there are no 580 * more physical interfaces that match, then we're done. 581 */ 582 do { 583 pif = lookup_pif_by_uindex(ifindex, pif, isv6); 584 if (pif == NULL) 585 return (NULL); 586 for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) { 587 if ((dsmp = lif->lif_smachs) != NULL) 588 break; 589 } 590 } while (dsmp == NULL); 591 return (dsmp); 592 } 593 594 /* 595 * lookup_smach_by_xid(): iterate through running state machines by transaction 596 * id. Transaction ID zero means "all state machines." 597 * 598 * input: uint32_t: the transaction id to look up 599 * dhcp_smach_t *: the previous state machine, or NULL for start 600 * boolean_t: B_TRUE if DHCPv6, B_FALSE otherwise 601 * output: dhcp_smach_t *: next state machine, or NULL at end of list 602 */ 603 604 dhcp_smach_t * 605 lookup_smach_by_xid(uint32_t xid, dhcp_smach_t *dsmp, boolean_t isv6) 606 { 607 for (dsmp = next_smach(dsmp, isv6); dsmp != NULL; 608 dsmp = next_smach(dsmp, isv6)) { 609 if (xid == 0 || 610 pkt_get_xid(dsmp->dsm_send_pkt.pkt, isv6) == xid) 611 break; 612 } 613 614 return (dsmp); 615 } 616 617 /* 618 * lookup_smach_by_event(): find a state machine busy with a particular event 619 * ID. This is used only for error handling. 620 * 621 * input: iu_event_id_t: the event id to look up 622 * output: dhcp_smach_t *: matching state machine, or NULL if none 623 */ 624 625 dhcp_smach_t * 626 lookup_smach_by_event(iu_event_id_t eid) 627 { 628 dhcp_smach_t *dsmp; 629 boolean_t isv6 = B_FALSE; 630 631 for (;;) { 632 for (dsmp = next_smach(NULL, isv6); dsmp != NULL; 633 dsmp = next_smach(dsmp, isv6)) { 634 if ((dsmp->dsm_dflags & DHCP_IF_BUSY) && 635 eid == dsmp->dsm_ia.ia_eid) 636 return (dsmp); 637 } 638 if (isv6) 639 break; 640 isv6 = B_TRUE; 641 } 642 643 return (dsmp); 644 } 645 646 /* 647 * cancel_offer_timer(): stop the offer polling timer on a given state machine 648 * 649 * input: dhcp_smach_t *: state machine on which to stop polling for offers 650 * output: none 651 */ 652 653 void 654 cancel_offer_timer(dhcp_smach_t *dsmp) 655 { 656 int retval; 657 658 if (dsmp->dsm_offer_timer != -1) { 659 retval = iu_cancel_timer(tq, dsmp->dsm_offer_timer, NULL); 660 dsmp->dsm_offer_timer = -1; 661 if (retval == 1) 662 release_smach(dsmp); 663 } 664 } 665 666 /* 667 * cancel_smach_timers(): stop all of the timers related to a given state 668 * machine, including lease and LIF expiry. 669 * 670 * input: dhcp_smach_t *: state machine to cancel 671 * output: none 672 * note: this function assumes that the iu timer functions are synchronous 673 * and thus don't require any protection or ordering on cancellation. 674 */ 675 676 void 677 cancel_smach_timers(dhcp_smach_t *dsmp) 678 { 679 dhcp_lease_t *dlp; 680 dhcp_lif_t *lif; 681 uint_t nlifs; 682 683 for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) { 684 cancel_lease_timers(dlp); 685 lif = dlp->dl_lifs; 686 nlifs = dlp->dl_nlifs; 687 for (; nlifs > 0; nlifs--, lif = lif->lif_next) 688 cancel_lif_timers(lif); 689 } 690 691 cancel_offer_timer(dsmp); 692 stop_pkt_retransmission(dsmp); 693 if (dsmp->dsm_start_timer != -1) { 694 (void) iu_cancel_timer(tq, dsmp->dsm_start_timer, NULL); 695 dsmp->dsm_start_timer = -1; 696 release_smach(dsmp); 697 } 698 } 699 700 /* 701 * remove_smach(): removes a given state machine from the system. marks it 702 * for being freed (but may not actually free it). 703 * 704 * input: dhcp_smach_t *: the state machine to remove 705 * output: void 706 */ 707 708 void 709 remove_smach(dhcp_smach_t *dsmp) 710 { 711 if (dsmp->dsm_dflags & DHCP_IF_REMOVED) 712 return; 713 714 dhcpmsg(MSG_DEBUG2, "remove_smach: removing %s", dsmp->dsm_name); 715 dsmp->dsm_dflags |= DHCP_IF_REMOVED; 716 remque(dsmp); 717 global_smach_count--; 718 719 /* 720 * if we have long term timers, cancel them so that state machine 721 * resources can be reclaimed in a reasonable amount of time. 722 */ 723 cancel_smach_timers(dsmp); 724 725 /* Drop the hold that the LIF's state machine list had on us */ 726 release_smach(dsmp); 727 } 728 729 /* 730 * finished_smach(): we're finished with a given state machine; remove it from 731 * the system and tell the user (who may have initiated the 732 * removal process). Note that we remove it from the system 733 * first to allow back-to-back drop and create invocations. 734 * 735 * input: dhcp_smach_t *: the state machine to remove 736 * int: error for IPC 737 * output: void 738 */ 739 740 void 741 finished_smach(dhcp_smach_t *dsmp, int error) 742 { 743 hold_smach(dsmp); 744 remove_smach(dsmp); 745 if (dsmp->dsm_ia.ia_fd != -1) 746 ipc_action_finish(dsmp, error); 747 else 748 (void) async_cancel(dsmp); 749 release_smach(dsmp); 750 } 751 752 /* 753 * is_bound_state(): checks if a state indicates the client is bound 754 * 755 * input: DHCPSTATE: the state to check 756 * output: boolean_t: B_TRUE if the state is bound, B_FALSE if not 757 */ 758 759 boolean_t 760 is_bound_state(DHCPSTATE state) 761 { 762 return (state == BOUND || state == REBINDING || state == INFORMATION || 763 state == RELEASING || state == INFORM_SENT || state == RENEWING); 764 } 765 766 /* 767 * set_smach_state(): changes state and updates I/O 768 * 769 * input: dhcp_smach_t *: the state machine to change 770 * DHCPSTATE: the new state 771 * output: boolean_t: B_TRUE on success, B_FALSE on failure 772 */ 773 774 boolean_t 775 set_smach_state(dhcp_smach_t *dsmp, DHCPSTATE state) 776 { 777 dhcp_lif_t *lif = dsmp->dsm_lif; 778 779 if (dsmp->dsm_state != state) { 780 dhcpmsg(MSG_DEBUG, 781 "set_smach_state: changing from %s to %s on %s", 782 dhcp_state_to_string(dsmp->dsm_state), 783 dhcp_state_to_string(state), dsmp->dsm_name); 784 785 /* 786 * For IPv4, when we're in a bound state our socket must be 787 * bound to our address. Otherwise, our socket must be bound 788 * to INADDR_ANY. For IPv6, no such change is necessary. 789 */ 790 if (!dsmp->dsm_isv6) { 791 if (is_bound_state(dsmp->dsm_state)) { 792 if (!is_bound_state(state)) { 793 close_ip_lif(lif); 794 if (!open_ip_lif(lif, INADDR_ANY, 795 B_FALSE)) 796 return (B_FALSE); 797 } 798 } else { 799 if (is_bound_state(state)) { 800 close_ip_lif(lif); 801 if (!open_ip_lif(lif, 802 ntohl(lif->lif_addr), B_FALSE)) 803 return (B_FALSE); 804 } 805 } 806 } 807 808 dsmp->dsm_state = state; 809 } 810 return (B_TRUE); 811 } 812 813 /* 814 * duid_retry(): attempt to write DUID again 815 * 816 * input: iu_tq_t *: ignored 817 * void *: ignored 818 * output: none 819 */ 820 821 /* ARGSUSED */ 822 static void 823 duid_retry(iu_tq_t *tqp, void *arg) 824 { 825 if (write_stable_duid(global_duid, global_duidlen) == -1) { 826 if (errno != EROFS) { 827 dhcpmsg(MSG_ERR, 828 "duid_retry: unable to write out DUID"); 829 } else { 830 (void) iu_schedule_timer(tq, 60, duid_retry, NULL); 831 } 832 } 833 } 834 835 /* 836 * get_smach_cid(): gets the client ID for a given state machine. 837 * 838 * input: dhcp_smach_t *: the state machine to set up 839 * output: int: DHCP_IPC_SUCCESS or one of DHCP_IPC_E_* on failure. 840 */ 841 842 int 843 get_smach_cid(dhcp_smach_t *dsmp) 844 { 845 uchar_t *client_id; 846 uint_t client_id_len; 847 dhcp_lif_t *lif = dsmp->dsm_lif; 848 dhcp_pif_t *pif = lif->lif_pif; 849 const char *value; 850 size_t slen; 851 852 /* 853 * Look in defaults file for the client-id. If present, this takes 854 * precedence over all other forms of ID. 855 */ 856 857 dhcpmsg(MSG_DEBUG, "get_smach_cid: getting default client-id " 858 "property on %s", dsmp->dsm_name); 859 value = df_get_string(dsmp->dsm_name, pif->pif_isv6, DF_CLIENT_ID); 860 if (value != NULL) { 861 /* 862 * The Client ID string can have one of three basic forms: 863 * <decimal>,<data...> 864 * 0x<hex...> 865 * <string...> 866 * 867 * The first form is an RFC 3315 DUID. This is legal for both 868 * IPv4 DHCP and DHCPv6. For IPv4, an RFC 4361 Client ID is 869 * constructed from this value. 870 * 871 * The second and third forms are legal for IPv4 only. This is 872 * a raw Client ID, in hex or ASCII string format. 873 */ 874 875 if (isdigit(*value) && 876 value[strspn(value, "0123456789")] == ',') { 877 char *cp; 878 ulong_t duidtype; 879 ulong_t subtype; 880 881 errno = 0; 882 duidtype = strtoul(value, &cp, 0); 883 if (value == cp || errno != 0 || *cp != ',' || 884 duidtype > 65535) { 885 dhcpmsg(MSG_ERR, "get_smach_cid: cannot parse " 886 "DUID type in %s", value); 887 goto no_specified_id; 888 } 889 value = cp + 1; 890 switch (duidtype) { 891 case DHCPV6_DUID_LL: 892 case DHCPV6_DUID_LLT: { 893 int num; 894 char chr; 895 896 errno = 0; 897 subtype = strtoul(value, &cp, 0); 898 if (value == cp || errno != 0 || *cp != ',' || 899 subtype > 65535) { 900 dhcpmsg(MSG_ERR, "get_smach_cid: " 901 "cannot parse MAC type in %s", 902 value); 903 goto no_specified_id; 904 } 905 value = cp + 1; 906 client_id_len = pif->pif_isv6 ? 1 : 5; 907 for (; *cp != '\0'; cp++) { 908 if (*cp == ':') 909 client_id_len++; 910 else if (!isxdigit(*cp)) 911 break; 912 } 913 if (duidtype == DHCPV6_DUID_LL) { 914 duid_llt_t *dllt; 915 time_t now; 916 917 client_id_len += sizeof (*dllt); 918 dllt = malloc(client_id_len); 919 if (dllt == NULL) 920 goto alloc_failure; 921 dsmp->dsm_cid = (uchar_t *)dllt; 922 dllt->dllt_dutype = htons(duidtype); 923 dllt->dllt_hwtype = htons(subtype); 924 now = time(NULL) - DUID_TIME_BASE; 925 dllt->dllt_time = htonl(now); 926 cp = (char *)(dllt + 1); 927 } else { 928 duid_ll_t *dll; 929 930 client_id_len += sizeof (*dll); 931 dll = malloc(client_id_len); 932 if (dll == NULL) 933 goto alloc_failure; 934 dsmp->dsm_cid = (uchar_t *)dll; 935 dll->dll_dutype = htons(duidtype); 936 dll->dll_hwtype = htons(subtype); 937 cp = (char *)(dll + 1); 938 } 939 num = 0; 940 while ((chr = *value) != '\0') { 941 if (isdigit(chr)) { 942 num = (num << 4) + chr - '0'; 943 } else if (isxdigit(chr)) { 944 num = (num << 4) + 10 + chr - 945 (isupper(chr) ? 'A' : 'a'); 946 } else if (chr == ':') { 947 *cp++ = num; 948 num = 0; 949 } else { 950 break; 951 } 952 } 953 break; 954 } 955 case DHCPV6_DUID_EN: { 956 duid_en_t *den; 957 958 errno = 0; 959 subtype = strtoul(value, &cp, 0); 960 if (value == cp || errno != 0 || *cp != ',') { 961 dhcpmsg(MSG_ERR, "get_smach_cid: " 962 "cannot parse enterprise in %s", 963 value); 964 goto no_specified_id; 965 } 966 value = cp + 1; 967 slen = strlen(value); 968 client_id_len = (slen + 1) / 2; 969 den = malloc(sizeof (*den) + client_id_len); 970 if (den == NULL) 971 goto alloc_failure; 972 den->den_dutype = htons(duidtype); 973 DHCPV6_SET_ENTNUM(den, subtype); 974 if (hexascii_to_octet(value, slen, den + 1, 975 &client_id_len) != 0) { 976 dhcpmsg(MSG_ERROR, "get_smach_cid: " 977 "cannot parse hex string in %s", 978 value); 979 free(den); 980 goto no_specified_id; 981 } 982 dsmp->dsm_cid = (uchar_t *)den; 983 break; 984 } 985 default: 986 slen = strlen(value); 987 client_id_len = (slen + 1) / 2; 988 cp = malloc(client_id_len); 989 if (cp == NULL) 990 goto alloc_failure; 991 if (hexascii_to_octet(value, slen, cp, 992 &client_id_len) != 0) { 993 dhcpmsg(MSG_ERROR, "get_smach_cid: " 994 "cannot parse hex string in %s", 995 value); 996 free(cp); 997 goto no_specified_id; 998 } 999 dsmp->dsm_cid = (uchar_t *)cp; 1000 break; 1001 } 1002 dsmp->dsm_cidlen = client_id_len; 1003 if (!pif->pif_isv6) { 1004 (void) memmove(dsmp->dsm_cid + 5, 1005 dsmp->dsm_cid, client_id_len - 5); 1006 dsmp->dsm_cid[0] = 255; 1007 dsmp->dsm_cid[1] = lif->lif_iaid >> 24; 1008 dsmp->dsm_cid[2] = lif->lif_iaid >> 16; 1009 dsmp->dsm_cid[3] = lif->lif_iaid >> 8; 1010 dsmp->dsm_cid[4] = lif->lif_iaid; 1011 } 1012 return (DHCP_IPC_SUCCESS); 1013 } 1014 1015 if (pif->pif_isv6) { 1016 dhcpmsg(MSG_ERROR, 1017 "get_smach_cid: client ID for %s invalid: %s", 1018 dsmp->dsm_name, value); 1019 } else if (strncasecmp("0x", value, 2) == 0 && 1020 value[2] != '\0') { 1021 /* skip past the 0x and convert the value to binary */ 1022 value += 2; 1023 slen = strlen(value); 1024 client_id_len = (slen + 1) / 2; 1025 dsmp->dsm_cid = malloc(client_id_len); 1026 if (dsmp->dsm_cid == NULL) 1027 goto alloc_failure; 1028 if (hexascii_to_octet(value, slen, dsmp->dsm_cid, 1029 &client_id_len) == 0) { 1030 dsmp->dsm_cidlen = client_id_len; 1031 return (DHCP_IPC_SUCCESS); 1032 } 1033 dhcpmsg(MSG_WARNING, "get_smach_cid: cannot convert " 1034 "hex value for Client ID on %s", dsmp->dsm_name); 1035 } else { 1036 client_id_len = strlen(value); 1037 dsmp->dsm_cid = malloc(client_id_len); 1038 if (dsmp->dsm_cid == NULL) 1039 goto alloc_failure; 1040 dsmp->dsm_cidlen = client_id_len; 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_INVIF); 1615 release_smach(dsmp); 1616 1617 return (B_FALSE); 1618 } 1619