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