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