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