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 * Copyright (c) 2017, Chris Fraire <cfraire@me.com>. 24 * Copyright 2019 Joshua M. Clulow <josh@sysmgr.org> 25 * Copyright 2026 Oxide Computer Company 26 * 27 * BOUND state of the DHCP client state machine. 28 */ 29 30 #include <sys/socket.h> 31 #include <sys/types.h> 32 #include <sys/debug.h> 33 #include <string.h> 34 #include <netinet/in.h> 35 #include <sys/sockio.h> 36 #include <unistd.h> 37 #include <time.h> 38 #include <arpa/inet.h> 39 #include <stdlib.h> 40 #include <search.h> 41 #include <sys/sysmacros.h> 42 #include <dhcp_hostconf.h> 43 #include <dhcpagent_util.h> 44 #include <dhcpmsg.h> 45 46 #include "states.h" 47 #include "packet.h" 48 #include "util.h" 49 #include "agent.h" 50 #include "interface.h" 51 #include "script_handler.h" 52 #include "defaults.h" 53 54 /* 55 * Possible outcomes for IPv6 binding attempt. 56 */ 57 enum v6_bind_result { 58 v6Restart, /* report failure and restart state machine */ 59 v6Resent, /* new Request message has been sent */ 60 v6Done /* successful binding */ 61 }; 62 63 static enum v6_bind_result configure_v6_leases(dhcp_smach_t *); 64 static boolean_t configure_v4_lease(dhcp_smach_t *); 65 static boolean_t configure_v4_timers(dhcp_smach_t *); 66 67 /* 68 * bound_event_cb(): callback for script_start on the event EVENT_BOUND 69 * 70 * input: dhcp_smach_t *: the state machine configured 71 * void *: unused 72 * output: int: always 1 73 */ 74 75 /* ARGSUSED1 */ 76 static int 77 bound_event_cb(dhcp_smach_t *dsmp, void *arg) 78 { 79 if (dsmp->dsm_ia.ia_fd != -1) 80 ipc_action_finish(dsmp, DHCP_IPC_SUCCESS); 81 else 82 async_finish(dsmp); 83 return (1); 84 } 85 86 /* 87 * dhcp_bound(): configures an state machine and interfaces using information 88 * contained in the ACK/Reply packet and sets up lease timers. 89 * Before starting, the requested address is verified by 90 * Duplicate Address Detection to make sure it's not in use. 91 * 92 * input: dhcp_smach_t *: the state machine to move to bound 93 * PKT_LIST *: the ACK/Reply packet, or NULL to use dsmp->dsm_ack 94 * output: boolean_t: B_TRUE on success, B_FALSE on failure 95 */ 96 97 boolean_t 98 dhcp_bound(dhcp_smach_t *dsmp, PKT_LIST *ack) 99 { 100 DHCPSTATE oldstate; 101 lease_t new_lease; 102 dhcp_lif_t *lif; 103 dhcp_lease_t *dlp; 104 enum v6_bind_result v6b; 105 106 if (ack != NULL) { 107 /* If ack we're replacing is not the original, then free it */ 108 if (dsmp->dsm_ack != dsmp->dsm_orig_ack) 109 free_pkt_entry(dsmp->dsm_ack); 110 dsmp->dsm_ack = ack; 111 /* Save the first ack as the original */ 112 if (dsmp->dsm_orig_ack == NULL) 113 dsmp->dsm_orig_ack = ack; 114 115 save_domainname(dsmp, ack); 116 } 117 118 oldstate = dsmp->dsm_state; 119 switch (oldstate) { 120 121 case ADOPTING: 122 /* Note that adoption occurs only for IPv4 DHCP. */ 123 124 /* Ignore BOOTP */ 125 if (ack->opts[CD_DHCP_TYPE] == NULL) 126 return (B_FALSE); 127 128 /* 129 * if we're adopting a lease, the lease timers 130 * only provide an upper bound since we don't know 131 * from what time they are relative to. assume we 132 * have a lease time of at most DHCP_ADOPT_LEASE_MAX. 133 */ 134 (void) memcpy(&new_lease, ack->opts[CD_LEASE_TIME]->value, 135 sizeof (lease_t)); 136 137 new_lease = htonl(MIN(ntohl(new_lease), DHCP_ADOPT_LEASE_MAX)); 138 139 (void) memcpy(ack->opts[CD_LEASE_TIME]->value, &new_lease, 140 sizeof (lease_t)); 141 142 /* 143 * we have no idea when the REQUEST that generated 144 * this ACK was sent, but for diagnostic purposes 145 * we'll assume its close to the current time. 146 */ 147 dsmp->dsm_newstart_monosec = monosec(); 148 149 if (dsmp->dsm_isv6) { 150 if ((v6b = configure_v6_leases(dsmp)) != v6Done) 151 return (v6b == v6Resent); 152 } else { 153 if (!configure_v4_lease(dsmp)) 154 return (B_FALSE); 155 156 if (!configure_v4_timers(dsmp)) 157 return (B_FALSE); 158 } 159 160 dsmp->dsm_curstart_monosec = dsmp->dsm_newstart_monosec; 161 write_lease_to_hostconf(dsmp); 162 break; 163 164 case SELECTING: 165 case REQUESTING: 166 case INIT_REBOOT: 167 168 if (dsmp->dsm_isv6) { 169 if ((v6b = configure_v6_leases(dsmp)) != v6Done) 170 return (v6b == v6Resent); 171 } else { 172 if (!configure_v4_lease(dsmp)) 173 return (B_FALSE); 174 175 if (!configure_v4_timers(dsmp)) 176 return (B_FALSE); 177 178 if (!clear_lif_deprecated(dsmp->dsm_lif)) 179 return (B_FALSE); 180 } 181 182 /* Stop sending requests now */ 183 stop_pkt_retransmission(dsmp); 184 185 /* 186 * If we didn't end up with any usable leases, then we have a 187 * problem. 188 */ 189 if (dsmp->dsm_leases == NULL) { 190 dhcpmsg(MSG_WARNING, 191 "dhcp_bound: no address lease established"); 192 return (B_FALSE); 193 } 194 195 /* 196 * If this is a Rapid-Commit (selecting state) or if we're 197 * dealing with a reboot (init-reboot), then we will have a new 198 * server ID to save. 199 */ 200 if (ack != NULL && 201 (oldstate == SELECTING || oldstate == INIT_REBOOT) && 202 dsmp->dsm_isv6 && !save_server_id(dsmp, ack)) { 203 dhcpmsg(MSG_ERROR, 204 "dhcp_bound: unable to save server ID on %s", 205 dsmp->dsm_name); 206 return (B_FALSE); 207 } 208 209 /* 210 * We will continue configuring the interfaces via 211 * dhcp_bound_complete, once kernel DAD completes. If no new 212 * leases were created (which can happen on an init-reboot used 213 * for link-up confirmation), then go straight to bound state. 214 */ 215 if (!set_smach_state(dsmp, PRE_BOUND)) 216 return (B_FALSE); 217 if (dsmp->dsm_lif_wait == 0) 218 dhcp_bound_complete(dsmp); 219 break; 220 221 case PRE_BOUND: 222 case BOUND: 223 case INFORMATION: 224 /* This is just a duplicate ack; silently ignore it */ 225 return (B_TRUE); 226 227 case RENEWING: 228 case REBINDING: 229 230 if (dsmp->dsm_isv6) { 231 if ((v6b = configure_v6_leases(dsmp)) != v6Done) 232 return (v6b == v6Resent); 233 } else { 234 if (!configure_v4_timers(dsmp)) 235 return (B_FALSE); 236 if (!clear_lif_deprecated(dsmp->dsm_lif)) 237 return (B_FALSE); 238 } 239 240 /* 241 * If some or all of the leases were torn down by the server, 242 * then handle that as an expiry. When the script is done 243 * running for the LOSS6 event, we'll end up back here. 244 */ 245 if ((lif = find_expired_lif(dsmp)) != NULL) { 246 hold_lif(lif); 247 dhcp_expire(NULL, lif); 248 while ((lif = find_expired_lif(dsmp)) != NULL) { 249 dlp = lif->lif_lease; 250 unplumb_lif(lif); 251 if (dlp->dl_nlifs == 0) 252 remove_lease(dlp); 253 } 254 if (dsmp->dsm_leases == NULL) 255 return (B_FALSE); 256 } 257 258 if (oldstate == REBINDING && dsmp->dsm_isv6 && 259 !save_server_id(dsmp, ack)) { 260 return (B_FALSE); 261 } 262 263 /* 264 * Handle Renew/Rebind that fails to address one of our leases. 265 * (Should just never happen, but RFC 3315 section 18.1.8 266 * requires it, and TAHI tests for it.) 267 */ 268 for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) { 269 if (dlp->dl_stale && dlp->dl_nlifs > 0) 270 break; 271 } 272 if (dlp != NULL) { 273 dhcpmsg(MSG_DEBUG, "dhcp_bound: lease not updated; " 274 "allow retransmit"); 275 return (B_TRUE); 276 } 277 278 if (!set_smach_state(dsmp, BOUND)) 279 return (B_FALSE); 280 281 (void) script_start(dsmp, dsmp->dsm_isv6 ? EVENT_EXTEND6 : 282 EVENT_EXTEND, bound_event_cb, NULL, NULL); 283 284 dsmp->dsm_curstart_monosec = dsmp->dsm_newstart_monosec; 285 write_lease_to_hostconf(dsmp); 286 287 /* Stop sending requests now */ 288 stop_pkt_retransmission(dsmp); 289 break; 290 291 case INFORM_SENT: 292 293 if (dsmp->dsm_isv6 && !save_server_id(dsmp, ack)) { 294 return (B_FALSE); 295 } 296 297 (void) bound_event_cb(dsmp, NULL); 298 if (!set_smach_state(dsmp, INFORMATION)) 299 return (B_FALSE); 300 301 /* Stop sending requests now */ 302 stop_pkt_retransmission(dsmp); 303 break; 304 305 default: 306 /* something is really bizarre... */ 307 dhcpmsg(MSG_DEBUG, 308 "dhcp_bound: called in unexpected state: %s", 309 dhcp_state_to_string(dsmp->dsm_state)); 310 return (B_FALSE); 311 } 312 313 return (B_TRUE); 314 } 315 316 static int 317 dhcp_route_add(dhcp_smach_t *dsmp, struct in_addr *network, uint8_t prefix, 318 struct in_addr *nexthop, boolean_t interface, dhcp_route_source_t source) 319 { 320 dhcp_route_t *dhr, *found; 321 avl_index_t where; 322 323 if ((dhr = calloc(1, sizeof (*dhr))) == NULL) { 324 return (-1); 325 } 326 327 VERIFY3U(prefix, <=, 32); 328 if (prefix != 0) { 329 dhr->dhr_network = *network; 330 dhr->dhr_prefix = prefix; 331 } 332 dhr->dhr_nexthop = *nexthop; 333 dhr->dhr_installed = B_FALSE; 334 dhr->dhr_interface = interface; 335 dhr->dhr_source = source; 336 337 if ((found = avl_find(&dsmp->dsm_routes, dhr, &where)) != NULL) { 338 /* 339 * The route is already in the list. 340 */ 341 found->dhr_source |= source; 342 free(dhr); 343 return (0); 344 } 345 346 avl_insert(&dsmp->dsm_routes, dhr, where); 347 return (0); 348 } 349 350 /* 351 * Parse the "Classless Route" option from RFC3442. The option is formatted as 352 * a sequence of pairs of destination prefix and next hop router address. 353 */ 354 static int 355 dhcp_classless_routes_add(dhcp_smach_t *dsmp, DHCP_OPT *routes, ipaddr_t ifip) 356 { 357 uint_t pos = 0; 358 uint_t nsigocts = 0; 359 uint_t c = 0; 360 uint8_t network[4]; 361 uint8_t nexthop[4]; 362 uint_t pfx = 0; 363 enum { 364 RPST_REST = 1, 365 RPST_NETWORK, 366 RPST_NEXTHOP 367 } state = RPST_REST; 368 369 VERIFY3U(routes->code, ==, CD_CLASSLESS_ROUTES); 370 371 while (pos < routes->len) { 372 uint8_t b = routes->value[pos]; 373 374 switch (state) { 375 case RPST_REST: 376 /* 377 * The first octet of a destination descriptor is the 378 * number of prefix bits in the subnet mask. This also 379 * determines the number of octets of that will follow 380 * to completely specify the destination prefix. 381 */ 382 if (b == 0) { 383 nsigocts = 0; 384 } else if (b >= 1 && b <= 8) { 385 nsigocts = 1; 386 } else if (b >= 9 && b <= 16) { 387 nsigocts = 2; 388 } else if (b >= 17 && b <= 24) { 389 nsigocts = 3; 390 } else if (b >= 25 && b <= 32) { 391 nsigocts = 4; 392 } else { 393 errno = EPROTO; 394 return (-1); 395 } 396 pfx = b; 397 (void) memset(network, 0, sizeof (network)); 398 (void) memset(nexthop, 0, sizeof (nexthop)); 399 c = 0; 400 pos++; 401 state = RPST_NETWORK; 402 break; 403 404 case RPST_NETWORK: 405 if (c < nsigocts) { 406 network[c++] = b; 407 pos++; 408 break; 409 } 410 c = 0; 411 state = RPST_NEXTHOP; 412 break; 413 414 case RPST_NEXTHOP: 415 /* 416 * The next hop router address appears directly after 417 * the variable length destination descriptor. 418 */ 419 nexthop[c++] = b; 420 pos++; 421 if (c >= 4) { 422 boolean_t iface; 423 struct in_addr ia_net, ia_hop; 424 (void) memcpy(&ia_net.s_addr, network, 425 sizeof (ipaddr_t)); 426 if (nexthop[0] == 0 && nexthop[1] == 0 && 427 nexthop[2] == 0 && nexthop[3] == 0) { 428 /* 429 * A next hop address of all zeroes in 430 * the option value means this is an 431 * interface route and we need to use 432 * the address of the interface. 433 */ 434 iface = B_TRUE; 435 (void) memcpy(&ia_hop.s_addr, &ifip, 436 sizeof (ipaddr_t)); 437 } else { 438 iface = B_FALSE; 439 (void) memcpy(&ia_hop.s_addr, nexthop, 440 sizeof (ipaddr_t)); 441 } 442 443 (void) dhcp_route_add(dsmp, &ia_net, pfx, 444 &ia_hop, iface, DHR_SRC_CLASSLESS_ROUTES); 445 446 state = RPST_REST; 447 } 448 break; 449 } 450 } 451 452 if (state != RPST_REST) { 453 errno = EPROTO; 454 return (-1); 455 } 456 457 return (0); 458 } 459 460 /* 461 * dhcp_bound_complete(): complete interface configuration after DAD 462 * 463 * input: dhcp_smach_t *: the state machine now ready 464 * output: none 465 */ 466 467 void 468 dhcp_bound_complete(dhcp_smach_t *dsmp) 469 { 470 PKT_LIST *ack = dsmp->dsm_ack; 471 DHCP_OPT *routes; 472 DHCP_OPT *router_list; 473 DHCPSTATE oldstate; 474 dhcp_lif_t *lif = dsmp->dsm_lif; 475 boolean_t ignore_mtu = B_FALSE; 476 boolean_t ignore_routes = B_FALSE, ignore_router_list = B_FALSE; 477 boolean_t manage_mtu, manage_classless_routes; 478 479 /* 480 * Do bound state entry processing only if running IPv4. There's no 481 * need for this with DHCPv6 because link-locals are used for I/O and 482 * because DHCPv6 isn't entangled with routing. 483 */ 484 if (dsmp->dsm_isv6) { 485 (void) set_smach_state(dsmp, BOUND); 486 dhcpmsg(MSG_DEBUG, "dhcp_bound_complete: bound %s", 487 dsmp->dsm_name); 488 (void) script_start(dsmp, EVENT_BOUND6, bound_event_cb, NULL, 489 NULL); 490 dsmp->dsm_curstart_monosec = dsmp->dsm_newstart_monosec; 491 write_lease_to_hostconf(dsmp); 492 return; 493 } 494 495 manage_mtu = df_get_bool(dsmp->dsm_name, dsmp->dsm_isv6, DF_SET_MTU); 496 manage_classless_routes = df_get_bool(dsmp->dsm_name, dsmp->dsm_isv6, 497 DF_CLASSLESS_ROUTES); 498 499 /* 500 * Check to see if the default route, classless routes, or MTU options 501 * appear in the ignore list. 502 */ 503 for (int i = 0; i < dsmp->dsm_pillen; i++) { 504 switch (dsmp->dsm_pil[i]) { 505 case CD_MTU: 506 ignore_mtu = B_TRUE; 507 break; 508 case CD_ROUTER: 509 ignore_router_list = B_TRUE; 510 break; 511 case CD_CLASSLESS_ROUTES: 512 ignore_routes = B_TRUE; 513 break; 514 } 515 } 516 517 /* 518 * If the server provides a valid MTU option, and the operator has not 519 * disabled MTU management, configure the MTU on the interface now. 520 */ 521 if (manage_mtu) { 522 DHCP_OPT *mtu; 523 uint16_t mtuval = 0; 524 525 if (!ignore_mtu && (mtu = ack->opts[CD_MTU]) != NULL && 526 mtu->len == sizeof (uint16_t)) { 527 (void) memcpy(&mtuval, mtu->value, sizeof (mtuval)); 528 mtuval = ntohs(mtuval); 529 530 set_lif_mtu(lif, mtuval); 531 } else { 532 /* 533 * If no MTU value is provided, clear any value that 534 * might have been requested previously. 535 */ 536 clear_lif_mtu(lif); 537 } 538 } 539 540 VERIFY3U(avl_numnodes(&dsmp->dsm_routes), ==, 0); 541 542 /* 543 * Process the RFC3442 classless routes option, if present and enabled: 544 */ 545 if (!ignore_routes && 546 (routes = ack->opts[CD_CLASSLESS_ROUTES]) != NULL && 547 dhcp_classless_routes_add(dsmp, routes, lif->lif_addr) != 0) { 548 dhcpmsg(MSG_ERR, "dhcp_bound_complete: cannot read " 549 "classless route list, ignoring classless routes"); 550 551 /* 552 * We add routes incrementally while parsing the response. To 553 * avoid using a partially valid response from the server, 554 * remove any classless routes we may have just added: 555 */ 556 discard_routes(dsmp, DHR_SRC_CLASSLESS_ROUTES); 557 } 558 559 /* 560 * Process the traditional default gateway option, if present and 561 * enabled: 562 */ 563 if (!ignore_router_list && 564 (router_list = ack->opts[CD_ROUTER]) != NULL && 565 (router_list->len % sizeof (ipaddr_t)) == 0) { 566 for (int i = 0; i < router_list->len / sizeof (ipaddr_t); i++) { 567 struct in_addr gateway; 568 (void) memcpy(&gateway, 569 router_list->value + (i * sizeof (ipaddr_t)), 570 sizeof (ipaddr_t)); 571 if (dhcp_route_add(dsmp, NULL, 0, &gateway, 572 B_FALSE, DHR_SRC_DEFAULT_GATEWAY) != 0) { 573 dhcpmsg(MSG_ERR, "dhcp_bound_complete: " 574 "adding default router to list failed"); 575 } 576 } 577 } 578 579 /* 580 * RFC3442 requires us to ignore the router option if a valid classless 581 * routes option value is available and we are able to process it. In 582 * theory the server should only give us one or the other, but we 583 * cannot assume that is the case. 584 */ 585 dhcp_route_source_t use_source = DHR_SRC_DEFAULT_GATEWAY; 586 for (dhcp_route_t *dhr = avl_first(&dsmp->dsm_routes); 587 dhr != NULL; dhr = AVL_NEXT(&dsmp->dsm_routes, dhr)) { 588 if (!(dhr->dhr_source & DHR_SRC_CLASSLESS_ROUTES)) { 589 continue; 590 } 591 592 use_source = DHR_SRC_CLASSLESS_ROUTES; 593 dhcpmsg(MSG_INFO, "classless routes are available; " 594 "ignoring router option on %s", 595 dsmp->dsm_name); 596 break; 597 } 598 599 /* 600 * If management of classless routes is disabled through configuration 601 * but the option was requested and not explicitly ignored, we will not 602 * add any routes from any source. This allows the operator to collect 603 * the option and perform some site-specific handling. 604 */ 605 if (!manage_classless_routes && 606 use_source == DHR_SRC_CLASSLESS_ROUTES) { 607 goto skip_routes; 608 } 609 610 /* 611 * Add each provided router; we'll clean them up when the state machine 612 * goes away or when our lease expires. 613 * 614 * Note that we do not handle routes on IPv4 logicals; see README for 615 * details. 616 */ 617 if (strchr(lif->lif_name, ':') == NULL && 618 !lif->lif_pif->pif_under_ipmp) { 619 for (dhcp_route_t *dhr = avl_first(&dsmp->dsm_routes); 620 dhr != NULL; dhr = AVL_NEXT(&dsmp->dsm_routes, dhr)) { 621 char rt[INET_ADDRSTRLEN]; 622 const char *verb = "added"; 623 624 if ((dhr->dhr_source & use_source) != use_source) { 625 /* 626 * This route is not from the source we have 627 * selected. 628 */ 629 continue; 630 } 631 632 (void) strncpy(rt, inet_ntoa(dhr->dhr_network), 633 sizeof (rt)); 634 635 if (!add_route(lif->lif_pif->pif_index, dhr)) { 636 if (errno == EEXIST) { 637 /* 638 * This route already exists in the 639 * kernel. Adopt this route as if we 640 * were able to add it, just in case 641 * the DHCP agent crashed and was 642 * restarted. 643 */ 644 verb = "found"; 645 } else { 646 dhcpmsg(MSG_ERR, 647 "dhcp_bound_complete: cannot " 648 "add route %s/%u -> %s (%s) on %s", 649 rt, dhr->dhr_prefix, 650 inet_ntoa(dhr->dhr_nexthop), 651 dhr->dhr_interface ? "interface" : 652 "gateway", dsmp->dsm_name); 653 continue; 654 } 655 } 656 657 dhr->dhr_installed = B_TRUE; 658 dhcpmsg(MSG_INFO, "%s route %s/%u -> %s (%s) on %s", 659 verb, rt, dhr->dhr_prefix, 660 inet_ntoa(dhr->dhr_nexthop), 661 dhr->dhr_interface ? "interface" : "gateway", 662 dsmp->dsm_name); 663 } 664 } 665 666 skip_routes: 667 oldstate = dsmp->dsm_state; 668 if (!set_smach_state(dsmp, BOUND)) { 669 dhcpmsg(MSG_ERR, 670 "dhcp_bound_complete: cannot set bound state on %s", 671 dsmp->dsm_name); 672 return; 673 } 674 675 dhcpmsg(MSG_DEBUG, "dhcp_bound_complete: bound %s", dsmp->dsm_name); 676 677 /* 678 * We're now committed to this binding, so if it came from BOOTP, set 679 * the flag. 680 */ 681 682 if (ack->opts[CD_DHCP_TYPE] == NULL) 683 dsmp->dsm_dflags |= DHCP_IF_BOOTP; 684 685 /* 686 * If the previous state was ADOPTING, event loop has not been started 687 * at this time; so don't run the EVENT_BOUND script. 688 */ 689 if (oldstate != ADOPTING) { 690 (void) script_start(dsmp, EVENT_BOUND, bound_event_cb, NULL, 691 NULL); 692 } 693 694 dsmp->dsm_curstart_monosec = dsmp->dsm_newstart_monosec; 695 write_lease_to_hostconf(dsmp); 696 } 697 698 /* 699 * fuzzify(): adds some "fuzz" to a t1/t2 time, in accordance with RFC2131. 700 * We use up to plus or minus 2% jitter in the time. This is a 701 * small value, but the timers involved are typically long. A 702 * common T1 value is one day, and the fuzz is up to 28.8 minutes; 703 * plenty of time to make sure that individual clients don't renew 704 * all at the same time. 705 * 706 * input: uint32_t: the number of seconds until lease expiration 707 * double: the approximate percentage of that time to return 708 * output: double: a number approximating (sec * pct) 709 */ 710 711 static double 712 fuzzify(uint32_t sec, double pct) 713 { 714 return (sec * (pct + (drand48() - 0.5) / 25.0)); 715 } 716 717 /* 718 * get_pkt_times(): pulls the lease times out of a v4 DHCP packet and stores 719 * them as host byte-order relative times in the passed in 720 * parameters. 721 * 722 * input: PKT_LIST *: the packet to pull the packet times from 723 * lease_t *: where to store the relative lease time in hbo 724 * lease_t *: where to store the relative t1 time in hbo 725 * lease_t *: where to store the relative t2 time in hbo 726 * output: void 727 */ 728 729 static void 730 get_pkt_times(PKT_LIST *ack, lease_t *lease, lease_t *t1, lease_t *t2) 731 { 732 *lease = DHCP_PERM; 733 *t1 = DHCP_PERM; 734 *t2 = DHCP_PERM; 735 736 if (ack->opts[CD_DHCP_TYPE] == NULL) { 737 dhcpmsg(MSG_VERBOSE, 738 "get_pkt_times: BOOTP response; infinite lease"); 739 return; 740 } 741 if (ack->opts[CD_LEASE_TIME] == NULL) { 742 dhcpmsg(MSG_VERBOSE, 743 "get_pkt_times: no lease option provided"); 744 return; 745 } 746 if (ack->opts[CD_LEASE_TIME]->len != sizeof (lease_t)) { 747 dhcpmsg(MSG_VERBOSE, "get_pkt_times: invalid lease option"); 748 } 749 750 (void) memcpy(lease, ack->opts[CD_LEASE_TIME]->value, sizeof (lease_t)); 751 *lease = ntohl(*lease); 752 753 if (*lease == DHCP_PERM) { 754 dhcpmsg(MSG_VERBOSE, "get_pkt_times: infinite lease granted"); 755 return; 756 } 757 758 if (ack->opts[CD_T1_TIME] != NULL && 759 ack->opts[CD_T1_TIME]->len == sizeof (lease_t)) { 760 (void) memcpy(t1, ack->opts[CD_T1_TIME]->value, sizeof (*t1)); 761 *t1 = ntohl(*t1); 762 } 763 764 if (ack->opts[CD_T2_TIME] != NULL && 765 ack->opts[CD_T2_TIME]->len == sizeof (lease_t)) { 766 (void) memcpy(t2, ack->opts[CD_T2_TIME]->value, sizeof (*t2)); 767 *t2 = ntohl(*t2); 768 } 769 770 if ((*t1 == DHCP_PERM) || (*t1 >= *lease)) 771 *t1 = (lease_t)fuzzify(*lease, DHCP_T1_FACT); 772 773 if ((*t2 == DHCP_PERM) || (*t2 > *lease) || (*t2 <= *t1)) 774 *t2 = (lease_t)fuzzify(*lease, DHCP_T2_FACT); 775 776 dhcpmsg(MSG_VERBOSE, "get_pkt_times: lease %u t1 %u t2 %u", 777 *lease, *t1, *t2); 778 } 779 780 /* 781 * configure_v4_timers(): configures the lease timers on a v4 state machine 782 * 783 * input: dhcp_smach_t *: the state machine to configure 784 * output: boolean_t: B_TRUE on success, B_FALSE on failure 785 */ 786 787 static boolean_t 788 configure_v4_timers(dhcp_smach_t *dsmp) 789 { 790 PKT_LIST *ack = dsmp->dsm_ack; 791 lease_t lease, t1, t2; 792 dhcp_lease_t *dlp; 793 dhcp_lif_t *lif; 794 795 /* v4 has just one lease per state machine, and one LIF */ 796 dlp = dsmp->dsm_leases; 797 lif = dlp->dl_lifs; 798 799 /* 800 * If it's DHCP, but there's no valid lease time, then complain, 801 * decline the lease and return error. 802 */ 803 if (ack->opts[CD_DHCP_TYPE] != NULL && 804 (ack->opts[CD_LEASE_TIME] == NULL || 805 ack->opts[CD_LEASE_TIME]->len != sizeof (lease_t))) { 806 lif_mark_decline(lif, "Missing or corrupted lease time"); 807 send_declines(dsmp); 808 dhcpmsg(MSG_WARNING, "configure_v4_timers: %s lease time in " 809 "ACK on %s", ack->opts[CD_LEASE_TIME] == NULL ? "missing" : 810 "corrupt", dsmp->dsm_name); 811 return (B_FALSE); 812 } 813 814 /* Stop the T1 and T2 timers */ 815 cancel_lease_timers(dlp); 816 817 /* Stop the LEASE timer */ 818 cancel_lif_timers(lif); 819 820 /* 821 * type has already been verified as ACK. if type is not set, 822 * then we got a BOOTP packet. we now fetch the t1, t2, and 823 * lease options out of the packet into variables. they are 824 * returned as relative host-byte-ordered times. 825 */ 826 827 get_pkt_times(ack, &lease, &t1, &t2); 828 829 /* 830 * if the current lease is mysteriously close to the new 831 * lease, warn the user. unless there's less than a minute 832 * left, round to the closest minute. 833 */ 834 835 if (lif->lif_expire.dt_start != 0 && 836 abs((dsmp->dsm_newstart_monosec + lease) - 837 (dsmp->dsm_curstart_monosec + lif->lif_expire.dt_start)) < 838 DHCP_LEASE_EPS) { 839 const char *noext = "configure_v4_timers: lease renewed but " 840 "time not extended"; 841 int msg_level; 842 uint_t minleft; 843 844 if (lif->lif_expire.dt_start < DHCP_LEASE_ERROR_THRESH) 845 msg_level = MSG_ERROR; 846 else 847 msg_level = MSG_VERBOSE; 848 849 minleft = (lif->lif_expire.dt_start + 30) / 60; 850 851 if (lif->lif_expire.dt_start < 60) { 852 dhcpmsg(msg_level, "%s; expires in %d seconds", 853 noext, lif->lif_expire.dt_start); 854 } else if (minleft == 1) { 855 dhcpmsg(msg_level, "%s; expires in 1 minute", noext); 856 } else if (minleft > 120) { 857 dhcpmsg(msg_level, "%s; expires in %d hours", 858 noext, (minleft + 30) / 60); 859 } else { 860 dhcpmsg(msg_level, "%s; expires in %d minutes", 861 noext, minleft); 862 } 863 } 864 865 init_timer(&dlp->dl_t1, t1); 866 init_timer(&dlp->dl_t2, t2); 867 init_timer(&lif->lif_expire, lease); 868 869 if (lease == DHCP_PERM) { 870 dhcpmsg(MSG_INFO, 871 "configure_v4_timers: %s acquired permanent lease", 872 dsmp->dsm_name); 873 return (B_TRUE); 874 } 875 876 dhcpmsg(MSG_INFO, "configure_v4_timers: %s acquired lease, expires %s", 877 dsmp->dsm_name, 878 monosec_to_string(dsmp->dsm_newstart_monosec + lease)); 879 880 dhcpmsg(MSG_INFO, "configure_v4_timers: %s begins renewal at %s", 881 dsmp->dsm_name, monosec_to_string(dsmp->dsm_newstart_monosec + 882 dlp->dl_t1.dt_start)); 883 884 dhcpmsg(MSG_INFO, "configure_v4_timers: %s begins rebinding at %s", 885 dsmp->dsm_name, monosec_to_string(dsmp->dsm_newstart_monosec + 886 dlp->dl_t2.dt_start)); 887 888 /* 889 * according to RFC2131, there is no minimum lease time, but don't 890 * set up renew/rebind timers if lease is shorter than DHCP_REBIND_MIN. 891 */ 892 893 if (!schedule_lif_timer(lif, &lif->lif_expire, dhcp_expire)) 894 goto failure; 895 896 if (lease < DHCP_REBIND_MIN) { 897 dhcpmsg(MSG_WARNING, "configure_v4_timers: lease on %s is for " 898 "less than %d seconds!", dsmp->dsm_name, DHCP_REBIND_MIN); 899 return (B_TRUE); 900 } 901 902 if (!schedule_lease_timer(dlp, &dlp->dl_t1, dhcp_renew)) 903 goto failure; 904 905 if (!schedule_lease_timer(dlp, &dlp->dl_t2, dhcp_rebind)) 906 goto failure; 907 908 return (B_TRUE); 909 910 failure: 911 cancel_lease_timers(dlp); 912 cancel_lif_timers(lif); 913 dhcpmsg(MSG_WARNING, 914 "configure_v4_timers: cannot schedule lease timers"); 915 return (B_FALSE); 916 } 917 918 /* 919 * configure_v6_leases(): configures the IPv6 leases on a state machine from 920 * the current DHCPv6 ACK. We need to scan the ACK, 921 * create a lease for each IA_NA, and a new LIF for each 922 * IAADDR. 923 * 924 * input: dhcp_smach_t *: the machine to configure (with a valid dsm_ack) 925 * output: enum v6_bind_result: restart, resend, or done 926 */ 927 928 static enum v6_bind_result 929 configure_v6_leases(dhcp_smach_t *dsmp) 930 { 931 const dhcpv6_option_t *d6o, *d6so, *d6sso; 932 const char *optbase, *estr, *msg; 933 uint_t olen, solen, ssolen, msglen; 934 dhcpv6_ia_na_t d6in; 935 dhcpv6_iaaddr_t d6ia; 936 dhcp_lease_t *dlp; 937 uint32_t shortest; 938 dhcp_lif_t *lif; 939 uint_t nlifs; 940 boolean_t got_iana = B_FALSE; 941 uint_t scode; 942 943 for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) 944 dlp->dl_stale = B_TRUE; 945 946 d6o = NULL; 947 while ((d6o = dhcpv6_pkt_option(dsmp->dsm_ack, d6o, DHCPV6_OPT_IA_NA, 948 &olen)) != NULL) { 949 if (olen < sizeof (d6in)) { 950 dhcpmsg(MSG_WARNING, 951 "configure_v6_leases: garbled IA_NA"); 952 continue; 953 } 954 955 /* 956 * Check the IAID. It should be for our controlling LIF. If a 957 * single state machine needs to use multiple IAIDs, then this 958 * will need to change. 959 */ 960 (void) memcpy(&d6in, d6o, sizeof (d6in)); 961 d6in.d6in_iaid = ntohl(d6in.d6in_iaid); 962 if (d6in.d6in_iaid != dsmp->dsm_lif->lif_iaid) { 963 dhcpmsg(MSG_WARNING, "configure_v6_leases: ignored " 964 "IA_NA for IAID %x (not %x)", d6in.d6in_iaid, 965 dsmp->dsm_lif->lif_iaid); 966 continue; 967 } 968 969 /* 970 * See notes below; there's only one IA_NA and a single IAID 971 * for now. 972 */ 973 if ((dlp = dsmp->dsm_leases) != NULL) 974 dlp->dl_stale = B_FALSE; 975 976 /* 977 * Note that some bug-ridden servers will try to give us 978 * multiple IA_NA options for a single IAID. We ignore 979 * duplicates. 980 */ 981 if (got_iana) { 982 dhcpmsg(MSG_WARNING, "configure_v6_leases: unexpected " 983 "extra IA_NA ignored"); 984 continue; 985 } 986 987 d6in.d6in_t1 = ntohl(d6in.d6in_t1); 988 d6in.d6in_t2 = ntohl(d6in.d6in_t2); 989 990 /* RFC 3315 required check for invalid T1/T2 combinations */ 991 if (d6in.d6in_t1 > d6in.d6in_t2 && d6in.d6in_t2 != 0) { 992 dhcpmsg(MSG_WARNING, "configure_v6_leases: ignored " 993 "IA_NA with invalid T1 %u > T2 %u", d6in.d6in_t1, 994 d6in.d6in_t2); 995 continue; 996 } 997 998 /* 999 * There may be a status code here. Process if present. 1000 */ 1001 optbase = (const char *)d6o + sizeof (d6in); 1002 olen -= sizeof (d6in); 1003 d6so = dhcpv6_find_option(optbase, olen, NULL, 1004 DHCPV6_OPT_STATUS_CODE, &solen); 1005 scode = dhcpv6_status_code(d6so, solen, &estr, &msg, &msglen); 1006 if (scode != DHCPV6_STAT_SUCCESS) { 1007 dhcpmsg(MSG_WARNING, 1008 "configure_v6_leases: IA_NA: %s: %.*s", 1009 estr, msglen, msg); 1010 } 1011 print_server_msg(dsmp, msg, msglen); 1012 1013 /* 1014 * Other errors are possible here. According to RFC 3315 1015 * section 18.1.8, we ignore the entire IA if it gives the "no 1016 * addresses" status code. We may try another server if we 1017 * like -- we instead opt to allow the addresses to expire and 1018 * then try a new server. 1019 * 1020 * If the status code is "no binding," then we must go back and 1021 * redo the Request. Surprisingly, it doesn't matter if it's 1022 * any other code. 1023 */ 1024 if (scode == DHCPV6_STAT_NOADDRS) { 1025 dhcpmsg(MSG_DEBUG, "configure_v6_leases: ignoring " 1026 "no-addrs status in IA_NA"); 1027 continue; 1028 } 1029 1030 if (scode == DHCPV6_STAT_NOBINDING) { 1031 send_v6_request(dsmp); 1032 return (v6Resent); 1033 } 1034 1035 /* 1036 * Find or create the lease structure. This part is simple, 1037 * because we support only IA_NA and a single IAID. This means 1038 * there's only one lease structure. The design supports 1039 * multiple lease structures so that IA_TA and IA_PD can be 1040 * added later. 1041 */ 1042 if ((dlp = dsmp->dsm_leases) == NULL && 1043 (dlp = insert_lease(dsmp)) == NULL) { 1044 dhcpmsg(MSG_ERROR, "configure_v6_leases: unable to " 1045 "allocate memory for lease"); 1046 return (v6Restart); 1047 } 1048 1049 /* 1050 * Iterate over the IAADDR options contained within this IA_NA. 1051 */ 1052 shortest = DHCPV6_INFTIME; 1053 d6so = NULL; 1054 while ((d6so = dhcpv6_find_option(optbase, olen, d6so, 1055 DHCPV6_OPT_IAADDR, &solen)) != NULL) { 1056 if (solen < sizeof (d6ia)) { 1057 dhcpmsg(MSG_WARNING, 1058 "configure_v6_leases: garbled IAADDR"); 1059 continue; 1060 } 1061 (void) memcpy(&d6ia, d6so, sizeof (d6ia)); 1062 1063 d6ia.d6ia_preflife = ntohl(d6ia.d6ia_preflife); 1064 d6ia.d6ia_vallife = ntohl(d6ia.d6ia_vallife); 1065 1066 /* RFC 3315 required validity check */ 1067 if (d6ia.d6ia_preflife > d6ia.d6ia_vallife) { 1068 dhcpmsg(MSG_WARNING, 1069 "configure_v6_leases: ignored IAADDR with " 1070 "preferred lifetime %u > valid %u", 1071 d6ia.d6ia_preflife, d6ia.d6ia_vallife); 1072 continue; 1073 } 1074 1075 /* 1076 * RFC 3315 allows a status code to be buried inside 1077 * the IAADDR option. Look for it, and process if 1078 * present. Process in a manner similar to that for 1079 * the IA itself; TAHI checks for this. Real servers 1080 * likely won't do this. 1081 */ 1082 d6sso = dhcpv6_find_option((const char *)d6so + 1083 sizeof (d6ia), solen - sizeof (d6ia), NULL, 1084 DHCPV6_OPT_STATUS_CODE, &ssolen); 1085 scode = dhcpv6_status_code(d6sso, ssolen, &estr, &msg, 1086 &msglen); 1087 print_server_msg(dsmp, msg, msglen); 1088 if (scode == DHCPV6_STAT_NOADDRS) { 1089 dhcpmsg(MSG_DEBUG, "configure_v6_leases: " 1090 "ignoring no-addrs status in IAADDR"); 1091 continue; 1092 } 1093 if (scode == DHCPV6_STAT_NOBINDING) { 1094 send_v6_request(dsmp); 1095 return (v6Resent); 1096 } 1097 if (scode != DHCPV6_STAT_SUCCESS) { 1098 dhcpmsg(MSG_WARNING, 1099 "configure_v6_leases: IAADDR: %s", estr); 1100 } 1101 1102 /* 1103 * Locate the existing LIF within the lease associated 1104 * with this address, if any. 1105 */ 1106 lif = dlp->dl_lifs; 1107 for (nlifs = dlp->dl_nlifs; nlifs > 0; 1108 nlifs--, lif = lif->lif_next) { 1109 if (IN6_ARE_ADDR_EQUAL(&d6ia.d6ia_addr, 1110 &lif->lif_v6addr)) 1111 break; 1112 } 1113 1114 /* 1115 * If the server has set the lifetime to zero, then 1116 * delete the LIF. Otherwise, set the new LIF expiry 1117 * time, adding the LIF if necessary. 1118 */ 1119 if (d6ia.d6ia_vallife == 0) { 1120 /* If it was found, then it's expired */ 1121 if (nlifs != 0) { 1122 dhcpmsg(MSG_DEBUG, 1123 "configure_v6_leases: lif %s has " 1124 "expired", lif->lif_name); 1125 lif->lif_expired = B_TRUE; 1126 } 1127 continue; 1128 } 1129 1130 /* If it wasn't found, then create it now. */ 1131 if (nlifs == 0) { 1132 lif = plumb_lif(dsmp->dsm_lif->lif_pif, 1133 &d6ia.d6ia_addr); 1134 if (lif == NULL) 1135 continue; 1136 if (++dlp->dl_nlifs == 1) { 1137 dlp->dl_lifs = lif; 1138 } else { 1139 remque(lif); 1140 insque(lif, dlp->dl_lifs); 1141 } 1142 lif->lif_lease = dlp; 1143 lif->lif_dad_wait = _B_TRUE; 1144 dsmp->dsm_lif_wait++; 1145 } else { 1146 /* If it was found, cancel timer */ 1147 cancel_lif_timers(lif); 1148 if (d6ia.d6ia_preflife != 0 && 1149 !clear_lif_deprecated(lif)) { 1150 unplumb_lif(lif); 1151 continue; 1152 } 1153 } 1154 1155 /* Set the new expiry timers */ 1156 init_timer(&lif->lif_preferred, d6ia.d6ia_preflife); 1157 init_timer(&lif->lif_expire, d6ia.d6ia_vallife); 1158 1159 /* 1160 * If the preferred lifetime is over now, then the LIF 1161 * is deprecated. If it's the same as the expiry time, 1162 * then we don't need a separate timer for it. 1163 */ 1164 if (d6ia.d6ia_preflife == 0) { 1165 set_lif_deprecated(lif); 1166 } else if (d6ia.d6ia_preflife != DHCPV6_INFTIME && 1167 d6ia.d6ia_preflife != d6ia.d6ia_vallife && 1168 !schedule_lif_timer(lif, &lif->lif_preferred, 1169 dhcp_deprecate)) { 1170 unplumb_lif(lif); 1171 continue; 1172 } 1173 1174 if (d6ia.d6ia_vallife != DHCPV6_INFTIME && 1175 !schedule_lif_timer(lif, &lif->lif_expire, 1176 dhcp_expire)) { 1177 unplumb_lif(lif); 1178 continue; 1179 } 1180 1181 if (d6ia.d6ia_preflife < shortest) 1182 shortest = d6ia.d6ia_preflife; 1183 } 1184 1185 if (dlp->dl_nlifs == 0) { 1186 dhcpmsg(MSG_WARNING, 1187 "configure_v6_leases: no IAADDRs found in IA_NA"); 1188 remove_lease(dlp); 1189 continue; 1190 } 1191 1192 if (d6in.d6in_t1 == 0 && d6in.d6in_t2 == 0) { 1193 /* Default values from RFC 3315: 0.5 and 0.8 */ 1194 if ((d6in.d6in_t1 = shortest / 2) == 0) 1195 d6in.d6in_t1 = 1; 1196 d6in.d6in_t2 = shortest - shortest / 5; 1197 } 1198 1199 cancel_lease_timers(dlp); 1200 init_timer(&dlp->dl_t1, d6in.d6in_t1); 1201 init_timer(&dlp->dl_t2, d6in.d6in_t2); 1202 1203 if ((d6in.d6in_t1 != DHCPV6_INFTIME && 1204 !schedule_lease_timer(dlp, &dlp->dl_t1, dhcp_renew)) || 1205 (d6in.d6in_t2 != DHCPV6_INFTIME && 1206 !schedule_lease_timer(dlp, &dlp->dl_t2, dhcp_rebind))) { 1207 dhcpmsg(MSG_WARNING, "configure_v6_leases: unable to " 1208 "set renew/rebind timers"); 1209 } else { 1210 got_iana = B_TRUE; 1211 } 1212 } 1213 1214 if (!got_iana) { 1215 dhcpmsg(MSG_WARNING, 1216 "configure_v6_leases: no usable IA_NA option found"); 1217 } 1218 1219 return (v6Done); 1220 } 1221 1222 /* 1223 * configure_v4_lease(): configures the IPv4 lease on a state machine from 1224 * the current DHCP ACK. There's only one lease and LIF 1225 * per state machine in IPv4. 1226 * 1227 * input: dhcp_smach_t *: the machine to configure (with a valid dsm_ack) 1228 * output: boolean_t: B_TRUE on success, B_FALSE on failure 1229 */ 1230 1231 static boolean_t 1232 configure_v4_lease(dhcp_smach_t *dsmp) 1233 { 1234 struct lifreq lifr; 1235 struct sockaddr_in *sin; 1236 PKT_LIST *ack = dsmp->dsm_ack; 1237 dhcp_lease_t *dlp; 1238 dhcp_lif_t *lif; 1239 uint32_t addrhbo; 1240 struct in_addr inaddr; 1241 1242 /* 1243 * if we're using DHCP, then we'll have a valid CD_SERVER_ID 1244 * (we checked in dhcp_acknak()); set it now so that 1245 * dsmp->dsm_server is valid in case we need to send_decline(). 1246 * note that we use comparisons against opts[CD_DHCP_TYPE] 1247 * since we haven't set DHCP_IF_BOOTP yet (we don't do that 1248 * until we're sure we want the offered address.) 1249 */ 1250 1251 if (ack->opts[CD_DHCP_TYPE] != NULL) { 1252 (void) memcpy(&inaddr, ack->opts[CD_SERVER_ID]->value, 1253 sizeof (inaddr)); 1254 IN6_INADDR_TO_V4MAPPED(&inaddr, &dsmp->dsm_server); 1255 } 1256 1257 /* 1258 * There needs to be exactly one lease for IPv4, and that lease 1259 * controls the main LIF for the state machine. If it doesn't exist 1260 * yet, then create it now. 1261 */ 1262 if ((dlp = dsmp->dsm_leases) == NULL && 1263 (dlp = insert_lease(dsmp)) == NULL) { 1264 dhcpmsg(MSG_ERROR, "configure_v4_lease: unable to allocate " 1265 "memory for lease"); 1266 return (B_FALSE); 1267 } 1268 if (dlp->dl_nlifs == 0) { 1269 dlp->dl_lifs = dsmp->dsm_lif; 1270 dlp->dl_nlifs = 1; 1271 1272 /* The lease holds a reference on the LIF */ 1273 hold_lif(dlp->dl_lifs); 1274 dlp->dl_lifs->lif_lease = dlp; 1275 } 1276 1277 lif = dlp->dl_lifs; 1278 1279 IN6_INADDR_TO_V4MAPPED(&ack->pkt->yiaddr, &lif->lif_v6addr); 1280 addrhbo = ntohl(ack->pkt->yiaddr.s_addr); 1281 if ((addrhbo & IN_CLASSA_NET) == 0 || 1282 (addrhbo >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET || 1283 IN_CLASSD(addrhbo)) { 1284 dhcpmsg(MSG_ERROR, 1285 "configure_v4_lease: got invalid IP address %s for %s", 1286 inet_ntoa(ack->pkt->yiaddr), lif->lif_name); 1287 return (B_FALSE); 1288 } 1289 1290 (void) memset(&lifr, 0, sizeof (struct lifreq)); 1291 (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 1292 1293 /* 1294 * bring the interface online. note that there is no optimal 1295 * order here: it is considered bad taste (and in > solaris 7, 1296 * likely illegal) to bring an interface up before it has an 1297 * ip address. however, due to an apparent bug in sun fddi 1298 * 5.0, fddi will not obtain a network routing entry unless 1299 * the interface is brought up before it has an ip address. 1300 * we take the lesser of the two evils; if fddi customers have 1301 * problems, they can get a newer fddi distribution which 1302 * fixes the problem. 1303 */ 1304 1305 sin = (struct sockaddr_in *)&lifr.lifr_addr; 1306 sin->sin_family = AF_INET; 1307 1308 (void) memset(&lif->lif_v6mask, 0xff, sizeof (lif->lif_v6mask)); 1309 if (ack->opts[CD_SUBNETMASK] != NULL && 1310 ack->opts[CD_SUBNETMASK]->len == sizeof (inaddr)) { 1311 1312 (void) memcpy(&inaddr, ack->opts[CD_SUBNETMASK]->value, 1313 sizeof (inaddr)); 1314 1315 } else { 1316 1317 if (ack->opts[CD_SUBNETMASK] != NULL && 1318 ack->opts[CD_SUBNETMASK]->len != sizeof (inaddr)) { 1319 dhcpmsg(MSG_WARNING, "configure_v4_lease: specified " 1320 "subnet mask length is %d instead of %d, ignoring", 1321 ack->opts[CD_SUBNETMASK]->len, sizeof (ipaddr_t)); 1322 } else { 1323 dhcpmsg(MSG_WARNING, "configure_v4_lease: no IP " 1324 "netmask specified for %s, making best guess", 1325 lif->lif_name); 1326 } 1327 1328 /* 1329 * no legitimate IP subnet mask specified.. use best 1330 * guess. recall that lif_addr is in network order, so 1331 * imagine it's 0x11223344: then when it is read into 1332 * a register on x86, it becomes 0x44332211, so we 1333 * must ntohl() it to convert it to 0x11223344 in 1334 * order to use the macros in <netinet/in.h>. 1335 */ 1336 1337 if (IN_CLASSA(addrhbo)) 1338 inaddr.s_addr = htonl(IN_CLASSA_NET); 1339 else if (IN_CLASSB(addrhbo)) 1340 inaddr.s_addr = htonl(IN_CLASSB_NET); 1341 else if (IN_CLASSC(addrhbo)) 1342 inaddr.s_addr = htonl(IN_CLASSC_NET); 1343 else { 1344 /* 1345 * Cant be Class D as that is multicast 1346 * Must be Class E 1347 */ 1348 inaddr.s_addr = htonl(IN_CLASSE_NET); 1349 } 1350 } 1351 lif->lif_v6mask._S6_un._S6_u32[3] = inaddr.s_addr; 1352 1353 sin->sin_addr = inaddr; 1354 dhcpmsg(MSG_INFO, "configure_v4_lease: setting IP netmask to %s on %s", 1355 inet_ntoa(sin->sin_addr), lif->lif_name); 1356 1357 if (ioctl(v4_sock_fd, SIOCSLIFNETMASK, &lifr) == -1) { 1358 dhcpmsg(MSG_ERR, "configure_v4_lease: cannot set IP netmask " 1359 "on %s", lif->lif_name); 1360 return (B_FALSE); 1361 } 1362 1363 IN6_V4MAPPED_TO_INADDR(&lif->lif_v6addr, &sin->sin_addr); 1364 dhcpmsg(MSG_INFO, "configure_v4_lease: setting IP address to %s on %s", 1365 inet_ntoa(sin->sin_addr), lif->lif_name); 1366 1367 if (ioctl(v4_sock_fd, SIOCSLIFADDR, &lifr) == -1) { 1368 dhcpmsg(MSG_ERR, "configure_v4_lease: cannot set IP address " 1369 "on %s", lif->lif_name); 1370 return (B_FALSE); 1371 } 1372 1373 if (!lif->lif_dad_wait) { 1374 lif->lif_dad_wait = _B_TRUE; 1375 dsmp->dsm_lif_wait++; 1376 } 1377 1378 if (ack->opts[CD_BROADCASTADDR] != NULL && 1379 ack->opts[CD_BROADCASTADDR]->len == sizeof (inaddr)) { 1380 1381 (void) memcpy(&inaddr, ack->opts[CD_BROADCASTADDR]->value, 1382 sizeof (inaddr)); 1383 1384 } else { 1385 1386 if (ack->opts[CD_BROADCASTADDR] != NULL && 1387 ack->opts[CD_BROADCASTADDR]->len != sizeof (inaddr)) { 1388 dhcpmsg(MSG_WARNING, "configure_v4_lease: specified " 1389 "broadcast address length is %d instead of %d, " 1390 "ignoring", ack->opts[CD_BROADCASTADDR]->len, 1391 sizeof (inaddr)); 1392 } else { 1393 dhcpmsg(MSG_WARNING, "configure_v4_lease: no IP " 1394 "broadcast specified for %s, making best guess", 1395 lif->lif_name); 1396 } 1397 1398 /* 1399 * no legitimate IP broadcast specified. compute it 1400 * from the IP address and netmask. 1401 */ 1402 1403 IN6_V4MAPPED_TO_INADDR(&lif->lif_v6addr, &inaddr); 1404 inaddr.s_addr |= ~lif->lif_v6mask._S6_un._S6_u32[3]; 1405 } 1406 1407 /* 1408 * the kernel will set the broadcast address for us as part of 1409 * bringing the interface up. since experience has shown that dhcp 1410 * servers sometimes provide a bogus broadcast address, we let the 1411 * kernel set it so that it's guaranteed to be correct. 1412 * 1413 * also, note any inconsistencies and save the broadcast address the 1414 * kernel set so that we can watch for changes to it. 1415 */ 1416 1417 if (ioctl(v4_sock_fd, SIOCGLIFBRDADDR, &lifr) == -1) { 1418 dhcpmsg(MSG_ERR, "configure_v4_lease: cannot get broadcast " 1419 "address for %s", lif->lif_name); 1420 return (B_FALSE); 1421 } 1422 1423 if (inaddr.s_addr != sin->sin_addr.s_addr) { 1424 dhcpmsg(MSG_WARNING, "configure_v4_lease: incorrect broadcast " 1425 "address %s specified for %s; ignoring", inet_ntoa(inaddr), 1426 lif->lif_name); 1427 } 1428 1429 lif->lif_broadcast = sin->sin_addr.s_addr; 1430 dhcpmsg(MSG_INFO, 1431 "configure_v4_lease: using broadcast address %s on %s", 1432 inet_ntoa(inaddr), lif->lif_name); 1433 return (B_TRUE); 1434 } 1435 1436 /* 1437 * save_server_id(): save off the new DHCPv6 Server ID 1438 * 1439 * input: dhcp_smach_t *: the state machine to use 1440 * PKT_LIST *: the packet with the Reply message 1441 * output: boolean_t: B_TRUE on success, B_FALSE on failure 1442 */ 1443 1444 boolean_t 1445 save_server_id(dhcp_smach_t *dsmp, PKT_LIST *msg) 1446 { 1447 const dhcpv6_option_t *d6o; 1448 uint_t olen; 1449 1450 d6o = dhcpv6_pkt_option(msg, NULL, DHCPV6_OPT_SERVERID, &olen); 1451 if (d6o == NULL) 1452 return (B_FALSE); 1453 olen -= sizeof (*d6o); 1454 free(dsmp->dsm_serverid); 1455 if ((dsmp->dsm_serverid = malloc(olen)) == NULL) { 1456 return (B_FALSE); 1457 } else { 1458 dsmp->dsm_serveridlen = olen; 1459 (void) memcpy(dsmp->dsm_serverid, d6o + 1, olen); 1460 return (B_TRUE); 1461 } 1462 } 1463