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