10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 52187Smeem * Common Development and Distribution License (the "License"). 62187Smeem * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 22*8485SPeter.Memishian@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate * 250Sstevel@tonic-gate * BOUND state of the DHCP client state machine. 260Sstevel@tonic-gate */ 270Sstevel@tonic-gate 280Sstevel@tonic-gate #include <sys/socket.h> 290Sstevel@tonic-gate #include <sys/types.h> 300Sstevel@tonic-gate #include <string.h> 310Sstevel@tonic-gate #include <netinet/in.h> 320Sstevel@tonic-gate #include <sys/sockio.h> 330Sstevel@tonic-gate #include <unistd.h> 340Sstevel@tonic-gate #include <time.h> 350Sstevel@tonic-gate #include <arpa/inet.h> 360Sstevel@tonic-gate #include <stdlib.h> 373431Scarlsonj #include <search.h> 380Sstevel@tonic-gate #include <sys/sysmacros.h> 390Sstevel@tonic-gate #include <dhcp_hostconf.h> 403431Scarlsonj #include <dhcpagent_util.h> 410Sstevel@tonic-gate #include <dhcpmsg.h> 420Sstevel@tonic-gate 430Sstevel@tonic-gate #include "states.h" 440Sstevel@tonic-gate #include "packet.h" 450Sstevel@tonic-gate #include "util.h" 460Sstevel@tonic-gate #include "agent.h" 470Sstevel@tonic-gate #include "interface.h" 480Sstevel@tonic-gate #include "script_handler.h" 490Sstevel@tonic-gate 503431Scarlsonj /* 513431Scarlsonj * Possible outcomes for IPv6 binding attempt. 523431Scarlsonj */ 533431Scarlsonj enum v6_bind_result { 543431Scarlsonj v6Restart, /* report failure and restart state machine */ 553431Scarlsonj v6Resent, /* new Request message has been sent */ 563431Scarlsonj v6Done /* successful binding */ 573431Scarlsonj }; 580Sstevel@tonic-gate 593431Scarlsonj static enum v6_bind_result configure_v6_leases(dhcp_smach_t *); 603431Scarlsonj static boolean_t configure_v4_lease(dhcp_smach_t *); 613431Scarlsonj static boolean_t configure_v4_timers(dhcp_smach_t *); 620Sstevel@tonic-gate 630Sstevel@tonic-gate /* 640Sstevel@tonic-gate * bound_event_cb(): callback for script_start on the event EVENT_BOUND 650Sstevel@tonic-gate * 663431Scarlsonj * input: dhcp_smach_t *: the state machine configured 673431Scarlsonj * void *: unused 680Sstevel@tonic-gate * output: int: always 1 690Sstevel@tonic-gate */ 700Sstevel@tonic-gate 713431Scarlsonj /* ARGSUSED1 */ 720Sstevel@tonic-gate static int 733431Scarlsonj bound_event_cb(dhcp_smach_t *dsmp, void *arg) 740Sstevel@tonic-gate { 753431Scarlsonj if (dsmp->dsm_ia.ia_fd != -1) 763431Scarlsonj ipc_action_finish(dsmp, DHCP_IPC_SUCCESS); 773431Scarlsonj else 783431Scarlsonj async_finish(dsmp); 790Sstevel@tonic-gate return (1); 800Sstevel@tonic-gate } 810Sstevel@tonic-gate 820Sstevel@tonic-gate /* 833431Scarlsonj * dhcp_bound(): configures an state machine and interfaces using information 843431Scarlsonj * contained in the ACK/Reply packet and sets up lease timers. 853431Scarlsonj * Before starting, the requested address is verified by 863431Scarlsonj * Duplicate Address Detection to make sure it's not in use. 870Sstevel@tonic-gate * 883431Scarlsonj * input: dhcp_smach_t *: the state machine to move to bound 893431Scarlsonj * PKT_LIST *: the ACK/Reply packet, or NULL to use dsmp->dsm_ack 903431Scarlsonj * output: boolean_t: B_TRUE on success, B_FALSE on failure 910Sstevel@tonic-gate */ 920Sstevel@tonic-gate 933431Scarlsonj boolean_t 943431Scarlsonj dhcp_bound(dhcp_smach_t *dsmp, PKT_LIST *ack) 950Sstevel@tonic-gate { 963431Scarlsonj DHCPSTATE oldstate; 973431Scarlsonj lease_t new_lease; 983431Scarlsonj dhcp_lif_t *lif; 993431Scarlsonj dhcp_lease_t *dlp; 1003431Scarlsonj enum v6_bind_result v6b; 1010Sstevel@tonic-gate 1020Sstevel@tonic-gate if (ack != NULL) { 1030Sstevel@tonic-gate /* If ack we're replacing is not the original, then free it */ 1043431Scarlsonj if (dsmp->dsm_ack != dsmp->dsm_orig_ack) 1053431Scarlsonj free_pkt_entry(dsmp->dsm_ack); 1063431Scarlsonj dsmp->dsm_ack = ack; 1070Sstevel@tonic-gate /* Save the first ack as the original */ 1083431Scarlsonj if (dsmp->dsm_orig_ack == NULL) 1093431Scarlsonj dsmp->dsm_orig_ack = ack; 1100Sstevel@tonic-gate } 1110Sstevel@tonic-gate 1123431Scarlsonj oldstate = dsmp->dsm_state; 1133431Scarlsonj switch (oldstate) { 1140Sstevel@tonic-gate 1150Sstevel@tonic-gate case ADOPTING: 1163431Scarlsonj /* Note that adoption occurs only for IPv4 DHCP. */ 1173431Scarlsonj 1183431Scarlsonj /* Ignore BOOTP */ 1193431Scarlsonj if (ack->opts[CD_DHCP_TYPE] == NULL) 1203431Scarlsonj return (B_FALSE); 1210Sstevel@tonic-gate 1220Sstevel@tonic-gate /* 1233431Scarlsonj * if we're adopting a lease, the lease timers 1240Sstevel@tonic-gate * only provide an upper bound since we don't know 1250Sstevel@tonic-gate * from what time they are relative to. assume we 1260Sstevel@tonic-gate * have a lease time of at most DHCP_ADOPT_LEASE_MAX. 1270Sstevel@tonic-gate */ 1283431Scarlsonj (void) memcpy(&new_lease, ack->opts[CD_LEASE_TIME]->value, 1293431Scarlsonj sizeof (lease_t)); 1300Sstevel@tonic-gate 1310Sstevel@tonic-gate new_lease = htonl(MIN(ntohl(new_lease), DHCP_ADOPT_LEASE_MAX)); 1320Sstevel@tonic-gate 1333431Scarlsonj (void) memcpy(ack->opts[CD_LEASE_TIME]->value, &new_lease, 1343431Scarlsonj sizeof (lease_t)); 1350Sstevel@tonic-gate 1360Sstevel@tonic-gate /* 1370Sstevel@tonic-gate * we have no idea when the REQUEST that generated 1380Sstevel@tonic-gate * this ACK was sent, but for diagnostic purposes 1390Sstevel@tonic-gate * we'll assume its close to the current time. 1400Sstevel@tonic-gate */ 1413431Scarlsonj dsmp->dsm_newstart_monosec = monosec(); 1422612Scarlsonj 1433431Scarlsonj if (dsmp->dsm_isv6) { 1443431Scarlsonj if ((v6b = configure_v6_leases(dsmp)) != v6Done) 1453431Scarlsonj return (v6b == v6Resent); 1463431Scarlsonj } else { 1473431Scarlsonj if (!configure_v4_lease(dsmp)) 1483431Scarlsonj return (B_FALSE); 1492546Scarlsonj 1503431Scarlsonj if (!configure_v4_timers(dsmp)) 1513431Scarlsonj return (B_FALSE); 1523431Scarlsonj } 1533431Scarlsonj 1543431Scarlsonj dsmp->dsm_curstart_monosec = dsmp->dsm_newstart_monosec; 1552546Scarlsonj break; 1560Sstevel@tonic-gate 1573431Scarlsonj case SELECTING: 1580Sstevel@tonic-gate case REQUESTING: 1590Sstevel@tonic-gate case INIT_REBOOT: 1600Sstevel@tonic-gate 1613431Scarlsonj if (dsmp->dsm_isv6) { 1623431Scarlsonj if ((v6b = configure_v6_leases(dsmp)) != v6Done) 1633431Scarlsonj return (v6b == v6Resent); 1643431Scarlsonj } else { 1653431Scarlsonj if (!configure_v4_lease(dsmp)) 1663431Scarlsonj return (B_FALSE); 1670Sstevel@tonic-gate 1683431Scarlsonj if (!configure_v4_timers(dsmp)) 1693431Scarlsonj return (B_FALSE); 1703431Scarlsonj 1713431Scarlsonj if (!clear_lif_deprecated(dsmp->dsm_lif)) 1723431Scarlsonj return (B_FALSE); 1733431Scarlsonj } 1743431Scarlsonj 1753431Scarlsonj /* Stop sending requests now */ 1763431Scarlsonj stop_pkt_retransmission(dsmp); 1770Sstevel@tonic-gate 1780Sstevel@tonic-gate /* 1793431Scarlsonj * If we didn't end up with any usable leases, then we have a 1803431Scarlsonj * problem. 1813431Scarlsonj */ 1823431Scarlsonj if (dsmp->dsm_leases == NULL) { 1833431Scarlsonj dhcpmsg(MSG_WARNING, 1843431Scarlsonj "dhcp_bound: no address lease established"); 1853431Scarlsonj return (B_FALSE); 1863431Scarlsonj } 1873431Scarlsonj 1883431Scarlsonj /* 1893431Scarlsonj * If this is a Rapid-Commit (selecting state) or if we're 1903431Scarlsonj * dealing with a reboot (init-reboot), then we will have a new 1913431Scarlsonj * server ID to save. 1920Sstevel@tonic-gate */ 1933431Scarlsonj if (ack != NULL && 1943431Scarlsonj (oldstate == SELECTING || oldstate == INIT_REBOOT) && 1953431Scarlsonj dsmp->dsm_isv6 && !save_server_id(dsmp, ack)) { 1963431Scarlsonj dhcpmsg(MSG_ERROR, 1973431Scarlsonj "dhcp_bound: unable to save server ID on %s", 1983431Scarlsonj dsmp->dsm_name); 1993431Scarlsonj return (B_FALSE); 2003431Scarlsonj } 2013431Scarlsonj 2023431Scarlsonj /* 2033431Scarlsonj * We will continue configuring the interfaces via 2043431Scarlsonj * dhcp_bound_complete, once kernel DAD completes. If no new 2053431Scarlsonj * leases were created (which can happen on an init-reboot used 2063431Scarlsonj * for link-up confirmation), then go straight to bound state. 2073431Scarlsonj */ 2083431Scarlsonj if (!set_smach_state(dsmp, PRE_BOUND)) 2093431Scarlsonj return (B_FALSE); 2103431Scarlsonj if (dsmp->dsm_lif_wait == 0) 2113431Scarlsonj dhcp_bound_complete(dsmp); 2122546Scarlsonj break; 2130Sstevel@tonic-gate 2142546Scarlsonj case PRE_BOUND: 2153431Scarlsonj case BOUND: 2164516Scarlsonj case INFORMATION: 2172546Scarlsonj /* This is just a duplicate ack; silently ignore it */ 2183431Scarlsonj return (B_TRUE); 2190Sstevel@tonic-gate 2200Sstevel@tonic-gate case RENEWING: 2210Sstevel@tonic-gate case REBINDING: 2223431Scarlsonj 2233431Scarlsonj if (dsmp->dsm_isv6) { 2243431Scarlsonj if ((v6b = configure_v6_leases(dsmp)) != v6Done) 2253431Scarlsonj return (v6b == v6Resent); 2263431Scarlsonj } else { 2273431Scarlsonj if (!configure_v4_timers(dsmp)) 2283431Scarlsonj return (B_FALSE); 2293431Scarlsonj if (!clear_lif_deprecated(dsmp->dsm_lif)) 2303431Scarlsonj return (B_FALSE); 2313431Scarlsonj } 2320Sstevel@tonic-gate 2330Sstevel@tonic-gate /* 2343431Scarlsonj * If some or all of the leases were torn down by the server, 2353431Scarlsonj * then handle that as an expiry. When the script is done 2363431Scarlsonj * running for the LOSS6 event, we'll end up back here. 2370Sstevel@tonic-gate */ 2383431Scarlsonj if ((lif = find_expired_lif(dsmp)) != NULL) { 2393431Scarlsonj hold_lif(lif); 2403431Scarlsonj dhcp_expire(NULL, lif); 2413431Scarlsonj while ((lif = find_expired_lif(dsmp)) != NULL) { 2423431Scarlsonj dlp = lif->lif_lease; 2433431Scarlsonj unplumb_lif(lif); 2443431Scarlsonj if (dlp->dl_nlifs == 0) 2453431Scarlsonj remove_lease(dlp); 2463431Scarlsonj } 2473431Scarlsonj if (dsmp->dsm_leases == NULL) 2483431Scarlsonj return (B_FALSE); 2493431Scarlsonj } 2500Sstevel@tonic-gate 2513431Scarlsonj if (oldstate == REBINDING && dsmp->dsm_isv6 && 2523431Scarlsonj !save_server_id(dsmp, ack)) { 2533431Scarlsonj return (B_FALSE); 2540Sstevel@tonic-gate } 2550Sstevel@tonic-gate 2563431Scarlsonj /* 2573431Scarlsonj * Handle Renew/Rebind that fails to address one of our leases. 2583431Scarlsonj * (Should just never happen, but RFC 3315 section 18.1.8 2593431Scarlsonj * requires it, and TAHI tests for it.) 2603431Scarlsonj */ 2613431Scarlsonj for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) { 2623431Scarlsonj if (dlp->dl_stale && dlp->dl_nlifs > 0) 2633431Scarlsonj break; 2643431Scarlsonj } 2653431Scarlsonj if (dlp != NULL) { 2663431Scarlsonj dhcpmsg(MSG_DEBUG, "dhcp_bound: lease not updated; " 2673431Scarlsonj "allow retransmit"); 2683431Scarlsonj return (B_TRUE); 2693431Scarlsonj } 2700Sstevel@tonic-gate 2713431Scarlsonj if (!set_smach_state(dsmp, BOUND)) 2723431Scarlsonj return (B_FALSE); 2733431Scarlsonj 2743431Scarlsonj (void) script_start(dsmp, dsmp->dsm_isv6 ? EVENT_EXTEND6 : 2753431Scarlsonj EVENT_EXTEND, bound_event_cb, NULL, NULL); 2763431Scarlsonj 2773431Scarlsonj dsmp->dsm_curstart_monosec = dsmp->dsm_newstart_monosec; 2783431Scarlsonj 2793431Scarlsonj /* Stop sending requests now */ 2803431Scarlsonj stop_pkt_retransmission(dsmp); 2810Sstevel@tonic-gate break; 2820Sstevel@tonic-gate 2830Sstevel@tonic-gate case INFORM_SENT: 2840Sstevel@tonic-gate 2853431Scarlsonj if (dsmp->dsm_isv6 && !save_server_id(dsmp, ack)) { 2863431Scarlsonj return (B_FALSE); 2873431Scarlsonj } 2883431Scarlsonj 2893431Scarlsonj (void) bound_event_cb(dsmp, NULL); 2903431Scarlsonj if (!set_smach_state(dsmp, INFORMATION)) 2913431Scarlsonj return (B_FALSE); 2923431Scarlsonj 2933431Scarlsonj /* Stop sending requests now */ 2943431Scarlsonj stop_pkt_retransmission(dsmp); 2950Sstevel@tonic-gate break; 2960Sstevel@tonic-gate 2970Sstevel@tonic-gate default: 2980Sstevel@tonic-gate /* something is really bizarre... */ 2993431Scarlsonj dhcpmsg(MSG_DEBUG, 3003431Scarlsonj "dhcp_bound: called in unexpected state: %s", 3013431Scarlsonj dhcp_state_to_string(dsmp->dsm_state)); 3023431Scarlsonj return (B_FALSE); 3030Sstevel@tonic-gate } 3040Sstevel@tonic-gate 3050Sstevel@tonic-gate /* 3060Sstevel@tonic-gate * remove any stale hostconf file that might be lying around for 3073431Scarlsonj * this state machine. (in general, it's harmless, since we'll write a 3080Sstevel@tonic-gate * fresh one when we exit anyway, but just to reduce confusion..) 3090Sstevel@tonic-gate */ 3100Sstevel@tonic-gate 3113431Scarlsonj (void) remove_hostconf(dsmp->dsm_name, dsmp->dsm_isv6); 3123431Scarlsonj return (B_TRUE); 3130Sstevel@tonic-gate } 3140Sstevel@tonic-gate 3150Sstevel@tonic-gate /* 3162546Scarlsonj * dhcp_bound_complete(): complete interface configuration after DAD 3172546Scarlsonj * 3183431Scarlsonj * input: dhcp_smach_t *: the state machine now ready 3192546Scarlsonj * output: none 3202546Scarlsonj */ 3212546Scarlsonj 3222546Scarlsonj void 3233431Scarlsonj dhcp_bound_complete(dhcp_smach_t *dsmp) 3242546Scarlsonj { 3253431Scarlsonj PKT_LIST *ack; 3263431Scarlsonj DHCP_OPT *router_list; 3273431Scarlsonj int i; 3283431Scarlsonj DHCPSTATE oldstate; 3294106Scarlsonj dhcp_lif_t *lif; 3303431Scarlsonj 3313431Scarlsonj /* 3323431Scarlsonj * Do bound state entry processing only if running IPv4. There's no 3333431Scarlsonj * need for this with DHCPv6 because link-locals are used for I/O and 3343431Scarlsonj * because DHCPv6 isn't entangled with routing. 3353431Scarlsonj */ 3363431Scarlsonj if (dsmp->dsm_isv6) { 3373431Scarlsonj (void) set_smach_state(dsmp, BOUND); 3385381Smeem dhcpmsg(MSG_DEBUG, "dhcp_bound_complete: bound %s", 3393431Scarlsonj dsmp->dsm_name); 3403431Scarlsonj (void) script_start(dsmp, EVENT_BOUND6, bound_event_cb, NULL, 3413431Scarlsonj NULL); 3423431Scarlsonj dsmp->dsm_curstart_monosec = dsmp->dsm_newstart_monosec; 3432546Scarlsonj return; 3443431Scarlsonj } 3452546Scarlsonj 3462612Scarlsonj /* 3474106Scarlsonj * Add each provided router; we'll clean them up when the 3483431Scarlsonj * state machine goes away or when our lease expires. 3494106Scarlsonj * 3504106Scarlsonj * Note that we do not handle default routers on IPv4 logicals; 3514106Scarlsonj * see README for details. 3523431Scarlsonj */ 3533431Scarlsonj 3543431Scarlsonj ack = dsmp->dsm_ack; 3553431Scarlsonj router_list = ack->opts[CD_ROUTER]; 3564106Scarlsonj lif = dsmp->dsm_lif; 3574106Scarlsonj if (router_list != NULL && 3584106Scarlsonj (router_list->len % sizeof (ipaddr_t)) == 0 && 359*8485SPeter.Memishian@Sun.COM strchr(lif->lif_name, ':') == NULL && 360*8485SPeter.Memishian@Sun.COM !lif->lif_pif->pif_under_ipmp) { 3613431Scarlsonj 3623431Scarlsonj dsmp->dsm_nrouters = router_list->len / sizeof (ipaddr_t); 3633431Scarlsonj dsmp->dsm_routers = malloc(router_list->len); 3643431Scarlsonj if (dsmp->dsm_routers == NULL) { 3655381Smeem dhcpmsg(MSG_ERR, "dhcp_bound_complete: cannot allocate " 3663431Scarlsonj "default router list, ignoring default routers"); 3673431Scarlsonj dsmp->dsm_nrouters = 0; 3683431Scarlsonj } 3693431Scarlsonj 3703431Scarlsonj for (i = 0; i < dsmp->dsm_nrouters; i++) { 3713431Scarlsonj 3723431Scarlsonj (void) memcpy(&dsmp->dsm_routers[i].s_addr, 3733431Scarlsonj router_list->value + (i * sizeof (ipaddr_t)), 3743431Scarlsonj sizeof (ipaddr_t)); 3753431Scarlsonj 3764106Scarlsonj if (!add_default_route(lif->lif_pif->pif_index, 3773431Scarlsonj &dsmp->dsm_routers[i])) { 3785381Smeem dhcpmsg(MSG_ERR, "dhcp_bound_complete: cannot " 3795381Smeem "add default router %s on %s", inet_ntoa( 3803431Scarlsonj dsmp->dsm_routers[i]), dsmp->dsm_name); 3813431Scarlsonj dsmp->dsm_routers[i].s_addr = htonl(INADDR_ANY); 3823431Scarlsonj continue; 3833431Scarlsonj } 3843431Scarlsonj 3853431Scarlsonj dhcpmsg(MSG_INFO, "added default router %s on %s", 3863431Scarlsonj inet_ntoa(dsmp->dsm_routers[i]), dsmp->dsm_name); 3873431Scarlsonj } 3883431Scarlsonj } 3893431Scarlsonj 3903431Scarlsonj oldstate = dsmp->dsm_state; 3913431Scarlsonj if (!set_smach_state(dsmp, BOUND)) { 3923431Scarlsonj dhcpmsg(MSG_ERR, 3935381Smeem "dhcp_bound_complete: cannot set bound state on %s", 3943431Scarlsonj dsmp->dsm_name); 3953431Scarlsonj return; 3963431Scarlsonj } 3973431Scarlsonj 3985381Smeem dhcpmsg(MSG_DEBUG, "dhcp_bound_complete: bound %s", dsmp->dsm_name); 3993431Scarlsonj 4003431Scarlsonj /* 4013431Scarlsonj * We're now committed to this binding, so if it came from BOOTP, set 4023431Scarlsonj * the flag. 4033431Scarlsonj */ 4043431Scarlsonj 4053431Scarlsonj if (ack->opts[CD_DHCP_TYPE] == NULL) 4063431Scarlsonj dsmp->dsm_dflags |= DHCP_IF_BOOTP; 4073431Scarlsonj 4083431Scarlsonj /* 4093431Scarlsonj * If the previous state was ADOPTING, event loop has not been started 4102612Scarlsonj * at this time; so don't run the EVENT_BOUND script. 4112612Scarlsonj */ 4123431Scarlsonj if (oldstate != ADOPTING) { 4133431Scarlsonj (void) script_start(dsmp, EVENT_BOUND, bound_event_cb, NULL, 4142612Scarlsonj NULL); 4152612Scarlsonj } 4162546Scarlsonj 4173431Scarlsonj dsmp->dsm_curstart_monosec = dsmp->dsm_newstart_monosec; 4182546Scarlsonj } 4192546Scarlsonj 4202546Scarlsonj /* 4213431Scarlsonj * fuzzify(): adds some "fuzz" to a t1/t2 time, in accordance with RFC2131. 4223431Scarlsonj * We use up to plus or minus 2% jitter in the time. This is a 4233431Scarlsonj * small value, but the timers involved are typically long. A 4243431Scarlsonj * common T1 value is one day, and the fuzz is up to 28.8 minutes; 4253431Scarlsonj * plenty of time to make sure that individual clients don't renew 4263431Scarlsonj * all at the same time. 4270Sstevel@tonic-gate * 4283431Scarlsonj * input: uint32_t: the number of seconds until lease expiration 4293431Scarlsonj * double: the approximate percentage of that time to return 4303431Scarlsonj * output: double: a number approximating (sec * pct) 4313431Scarlsonj */ 4323431Scarlsonj 4333431Scarlsonj static double 4343431Scarlsonj fuzzify(uint32_t sec, double pct) 4353431Scarlsonj { 4363431Scarlsonj return (sec * (pct + (drand48() - 0.5) / 25.0)); 4373431Scarlsonj } 4383431Scarlsonj 4393431Scarlsonj /* 4403431Scarlsonj * get_pkt_times(): pulls the lease times out of a v4 DHCP packet and stores 4413431Scarlsonj * them as host byte-order relative times in the passed in 4423431Scarlsonj * parameters. 4433431Scarlsonj * 4443431Scarlsonj * input: PKT_LIST *: the packet to pull the packet times from 4453431Scarlsonj * lease_t *: where to store the relative lease time in hbo 4463431Scarlsonj * lease_t *: where to store the relative t1 time in hbo 4473431Scarlsonj * lease_t *: where to store the relative t2 time in hbo 4483431Scarlsonj * output: void 4490Sstevel@tonic-gate */ 4500Sstevel@tonic-gate 4513431Scarlsonj static void 4523431Scarlsonj get_pkt_times(PKT_LIST *ack, lease_t *lease, lease_t *t1, lease_t *t2) 4530Sstevel@tonic-gate { 4543431Scarlsonj *lease = DHCP_PERM; 4553431Scarlsonj *t1 = DHCP_PERM; 4563431Scarlsonj *t2 = DHCP_PERM; 4570Sstevel@tonic-gate 4583431Scarlsonj if (ack->opts[CD_DHCP_TYPE] == NULL) { 4593431Scarlsonj dhcpmsg(MSG_VERBOSE, 4603431Scarlsonj "get_pkt_times: BOOTP response; infinite lease"); 4613431Scarlsonj return; 4623431Scarlsonj } 4633431Scarlsonj if (ack->opts[CD_LEASE_TIME] == NULL) { 4643431Scarlsonj dhcpmsg(MSG_VERBOSE, 4653431Scarlsonj "get_pkt_times: no lease option provided"); 4663431Scarlsonj return; 4673431Scarlsonj } 4683431Scarlsonj if (ack->opts[CD_LEASE_TIME]->len != sizeof (lease_t)) { 4693431Scarlsonj dhcpmsg(MSG_VERBOSE, "get_pkt_times: invalid lease option"); 4703431Scarlsonj } 4713431Scarlsonj 4723431Scarlsonj (void) memcpy(lease, ack->opts[CD_LEASE_TIME]->value, sizeof (lease_t)); 4733431Scarlsonj *lease = ntohl(*lease); 4743431Scarlsonj 4753431Scarlsonj if (*lease == DHCP_PERM) { 4763431Scarlsonj dhcpmsg(MSG_VERBOSE, "get_pkt_times: infinite lease granted"); 4773431Scarlsonj return; 4783431Scarlsonj } 4793431Scarlsonj 4803431Scarlsonj if (ack->opts[CD_T1_TIME] != NULL && 4813431Scarlsonj ack->opts[CD_T1_TIME]->len == sizeof (lease_t)) { 4823431Scarlsonj (void) memcpy(t1, ack->opts[CD_T1_TIME]->value, sizeof (*t1)); 4833431Scarlsonj *t1 = ntohl(*t1); 4843431Scarlsonj } 4853431Scarlsonj 4863431Scarlsonj if (ack->opts[CD_T2_TIME] != NULL && 4873431Scarlsonj ack->opts[CD_T2_TIME]->len == sizeof (lease_t)) { 4883431Scarlsonj (void) memcpy(t2, ack->opts[CD_T2_TIME]->value, sizeof (*t2)); 4893431Scarlsonj *t2 = ntohl(*t2); 4900Sstevel@tonic-gate } 4910Sstevel@tonic-gate 4923431Scarlsonj if ((*t1 == DHCP_PERM) || (*t1 >= *lease)) 4933431Scarlsonj *t1 = (lease_t)fuzzify(*lease, DHCP_T1_FACT); 4943431Scarlsonj 4953431Scarlsonj if ((*t2 == DHCP_PERM) || (*t2 > *lease) || (*t2 <= *t1)) 4963431Scarlsonj *t2 = (lease_t)fuzzify(*lease, DHCP_T2_FACT); 4973431Scarlsonj 4983431Scarlsonj dhcpmsg(MSG_VERBOSE, "get_pkt_times: lease %u t1 %u t2 %u", 4993431Scarlsonj *lease, *t1, *t2); 5003431Scarlsonj } 5013431Scarlsonj 5023431Scarlsonj /* 5033431Scarlsonj * configure_v4_timers(): configures the lease timers on a v4 state machine 5043431Scarlsonj * 5053431Scarlsonj * input: dhcp_smach_t *: the state machine to configure 5063431Scarlsonj * output: boolean_t: B_TRUE on success, B_FALSE on failure 5073431Scarlsonj */ 5083431Scarlsonj 5093431Scarlsonj static boolean_t 5103431Scarlsonj configure_v4_timers(dhcp_smach_t *dsmp) 5113431Scarlsonj { 5123431Scarlsonj PKT_LIST *ack = dsmp->dsm_ack; 5133431Scarlsonj lease_t lease, t1, t2; 5143431Scarlsonj dhcp_lease_t *dlp; 5153431Scarlsonj dhcp_lif_t *lif; 5163431Scarlsonj 5173431Scarlsonj /* v4 has just one lease per state machine, and one LIF */ 5183431Scarlsonj dlp = dsmp->dsm_leases; 5193431Scarlsonj lif = dlp->dl_lifs; 5203431Scarlsonj 5213431Scarlsonj /* 5223431Scarlsonj * If it's DHCP, but there's no valid lease time, then complain, 5233431Scarlsonj * decline the lease and return error. 5243431Scarlsonj */ 5253431Scarlsonj if (ack->opts[CD_DHCP_TYPE] != NULL && 5263431Scarlsonj (ack->opts[CD_LEASE_TIME] == NULL || 5273431Scarlsonj ack->opts[CD_LEASE_TIME]->len != sizeof (lease_t))) { 5283431Scarlsonj lif_mark_decline(lif, "Missing or corrupted lease time"); 5293431Scarlsonj send_declines(dsmp); 5303431Scarlsonj dhcpmsg(MSG_WARNING, "configure_v4_timers: %s lease time in " 5313431Scarlsonj "ACK on %s", ack->opts[CD_LEASE_TIME] == NULL ? "missing" : 5323431Scarlsonj "corrupt", dsmp->dsm_name); 5333431Scarlsonj return (B_FALSE); 5343431Scarlsonj } 5353431Scarlsonj 5363431Scarlsonj /* Stop the T1 and T2 timers */ 5373431Scarlsonj cancel_lease_timers(dlp); 5383431Scarlsonj 5393431Scarlsonj /* Stop the LEASE timer */ 5403431Scarlsonj cancel_lif_timers(lif); 5410Sstevel@tonic-gate 5420Sstevel@tonic-gate /* 5430Sstevel@tonic-gate * type has already been verified as ACK. if type is not set, 5440Sstevel@tonic-gate * then we got a BOOTP packet. we now fetch the t1, t2, and 5450Sstevel@tonic-gate * lease options out of the packet into variables. they are 5460Sstevel@tonic-gate * returned as relative host-byte-ordered times. 5470Sstevel@tonic-gate */ 5480Sstevel@tonic-gate 5493431Scarlsonj get_pkt_times(ack, &lease, &t1, &t2); 5503431Scarlsonj 5513431Scarlsonj /* 5523431Scarlsonj * if the current lease is mysteriously close to the new 5533431Scarlsonj * lease, warn the user. unless there's less than a minute 5543431Scarlsonj * left, round to the closest minute. 5553431Scarlsonj */ 5563431Scarlsonj 5573431Scarlsonj if (lif->lif_expire.dt_start != 0 && 5583431Scarlsonj abs((dsmp->dsm_newstart_monosec + lease) - 5593431Scarlsonj (dsmp->dsm_curstart_monosec + lif->lif_expire.dt_start)) < 5603431Scarlsonj DHCP_LEASE_EPS) { 5613431Scarlsonj const char *noext = "configure_v4_timers: lease renewed but " 5623431Scarlsonj "time not extended"; 5633431Scarlsonj int msg_level; 5643431Scarlsonj uint_t minleft; 5650Sstevel@tonic-gate 5663431Scarlsonj if (lif->lif_expire.dt_start < DHCP_LEASE_ERROR_THRESH) 5673431Scarlsonj msg_level = MSG_ERROR; 5683431Scarlsonj else 5693431Scarlsonj msg_level = MSG_VERBOSE; 5703431Scarlsonj 5713431Scarlsonj minleft = (lif->lif_expire.dt_start + 30) / 60; 5720Sstevel@tonic-gate 5733431Scarlsonj if (lif->lif_expire.dt_start < 60) { 5743431Scarlsonj dhcpmsg(msg_level, "%s; expires in %d seconds", 5753431Scarlsonj noext, lif->lif_expire.dt_start); 5763431Scarlsonj } else if (minleft == 1) { 5773431Scarlsonj dhcpmsg(msg_level, "%s; expires in 1 minute", noext); 5783431Scarlsonj } else if (minleft > 120) { 5793431Scarlsonj dhcpmsg(msg_level, "%s; expires in %d hours", 5803431Scarlsonj noext, (minleft + 30) / 60); 5813431Scarlsonj } else { 5823431Scarlsonj dhcpmsg(msg_level, "%s; expires in %d minutes", 5833431Scarlsonj noext, minleft); 5843431Scarlsonj } 5850Sstevel@tonic-gate } 5860Sstevel@tonic-gate 5873431Scarlsonj init_timer(&dlp->dl_t1, t1); 5883431Scarlsonj init_timer(&dlp->dl_t2, t2); 5893431Scarlsonj init_timer(&lif->lif_expire, lease); 5903431Scarlsonj 5913431Scarlsonj if (lease == DHCP_PERM) { 5923431Scarlsonj dhcpmsg(MSG_INFO, 5933431Scarlsonj "configure_v4_timers: %s acquired permanent lease", 5943431Scarlsonj dsmp->dsm_name); 5953431Scarlsonj return (B_TRUE); 5963431Scarlsonj } 5970Sstevel@tonic-gate 5983431Scarlsonj dhcpmsg(MSG_INFO, "configure_v4_timers: %s acquired lease, expires %s", 5993431Scarlsonj dsmp->dsm_name, 6003431Scarlsonj monosec_to_string(dsmp->dsm_newstart_monosec + lease)); 6010Sstevel@tonic-gate 6023431Scarlsonj dhcpmsg(MSG_INFO, "configure_v4_timers: %s begins renewal at %s", 6033431Scarlsonj dsmp->dsm_name, monosec_to_string(dsmp->dsm_newstart_monosec + 6043431Scarlsonj dlp->dl_t1.dt_start)); 6053431Scarlsonj 6063431Scarlsonj dhcpmsg(MSG_INFO, "configure_v4_timers: %s begins rebinding at %s", 6073431Scarlsonj dsmp->dsm_name, monosec_to_string(dsmp->dsm_newstart_monosec + 6083431Scarlsonj dlp->dl_t2.dt_start)); 6090Sstevel@tonic-gate 6100Sstevel@tonic-gate /* 6110Sstevel@tonic-gate * according to RFC2131, there is no minimum lease time, but don't 6120Sstevel@tonic-gate * set up renew/rebind timers if lease is shorter than DHCP_REBIND_MIN. 6130Sstevel@tonic-gate */ 6140Sstevel@tonic-gate 6153431Scarlsonj if (!schedule_lif_timer(lif, &lif->lif_expire, dhcp_expire)) 6160Sstevel@tonic-gate goto failure; 6170Sstevel@tonic-gate 6180Sstevel@tonic-gate if (lease < DHCP_REBIND_MIN) { 6193431Scarlsonj dhcpmsg(MSG_WARNING, "configure_v4_timers: lease on %s is for " 6203431Scarlsonj "less than %d seconds!", dsmp->dsm_name, DHCP_REBIND_MIN); 6213431Scarlsonj return (B_TRUE); 6220Sstevel@tonic-gate } 6230Sstevel@tonic-gate 6243431Scarlsonj if (!schedule_lease_timer(dlp, &dlp->dl_t1, dhcp_renew)) 6250Sstevel@tonic-gate goto failure; 6260Sstevel@tonic-gate 6273431Scarlsonj if (!schedule_lease_timer(dlp, &dlp->dl_t2, dhcp_rebind)) 6280Sstevel@tonic-gate goto failure; 6290Sstevel@tonic-gate 6303431Scarlsonj return (B_TRUE); 6310Sstevel@tonic-gate 6320Sstevel@tonic-gate failure: 6333431Scarlsonj cancel_lease_timers(dlp); 6343431Scarlsonj cancel_lif_timers(lif); 6353431Scarlsonj dhcpmsg(MSG_WARNING, 6363431Scarlsonj "configure_v4_timers: cannot schedule lease timers"); 6373431Scarlsonj return (B_FALSE); 6380Sstevel@tonic-gate } 6390Sstevel@tonic-gate 6400Sstevel@tonic-gate /* 6413431Scarlsonj * configure_v6_leases(): configures the IPv6 leases on a state machine from 6423431Scarlsonj * the current DHCPv6 ACK. We need to scan the ACK, 6433431Scarlsonj * create a lease for each IA_NA, and a new LIF for each 6443431Scarlsonj * IAADDR. 6450Sstevel@tonic-gate * 6463431Scarlsonj * input: dhcp_smach_t *: the machine to configure (with a valid dsm_ack) 6473431Scarlsonj * output: enum v6_bind_result: restart, resend, or done 6480Sstevel@tonic-gate */ 6490Sstevel@tonic-gate 6503431Scarlsonj static enum v6_bind_result 6513431Scarlsonj configure_v6_leases(dhcp_smach_t *dsmp) 6520Sstevel@tonic-gate { 6533431Scarlsonj const dhcpv6_option_t *d6o, *d6so, *d6sso; 6543431Scarlsonj const char *optbase, *estr, *msg; 6553431Scarlsonj uint_t olen, solen, ssolen, msglen; 6563431Scarlsonj dhcpv6_ia_na_t d6in; 6573431Scarlsonj dhcpv6_iaaddr_t d6ia; 6583431Scarlsonj dhcp_lease_t *dlp; 6593431Scarlsonj uint32_t shortest; 6603431Scarlsonj dhcp_lif_t *lif; 6613431Scarlsonj uint_t nlifs; 6623431Scarlsonj boolean_t got_iana = B_FALSE; 6633431Scarlsonj uint_t scode; 6643431Scarlsonj 6653431Scarlsonj for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) 6663431Scarlsonj dlp->dl_stale = B_TRUE; 6673431Scarlsonj 6683431Scarlsonj d6o = NULL; 6693431Scarlsonj while ((d6o = dhcpv6_pkt_option(dsmp->dsm_ack, d6o, DHCPV6_OPT_IA_NA, 6703431Scarlsonj &olen)) != NULL) { 6713431Scarlsonj if (olen < sizeof (d6in)) { 6723431Scarlsonj dhcpmsg(MSG_WARNING, 6733431Scarlsonj "configure_v6_leases: garbled IA_NA"); 6743431Scarlsonj continue; 6753431Scarlsonj } 6763431Scarlsonj 6773431Scarlsonj /* 6783431Scarlsonj * Check the IAID. It should be for our controlling LIF. If a 6793431Scarlsonj * single state machine needs to use multiple IAIDs, then this 6803431Scarlsonj * will need to change. 6813431Scarlsonj */ 6823431Scarlsonj (void) memcpy(&d6in, d6o, sizeof (d6in)); 6833431Scarlsonj d6in.d6in_iaid = ntohl(d6in.d6in_iaid); 6843431Scarlsonj if (d6in.d6in_iaid != dsmp->dsm_lif->lif_iaid) { 6853431Scarlsonj dhcpmsg(MSG_WARNING, "configure_v6_leases: ignored " 6863431Scarlsonj "IA_NA for IAID %x (not %x)", d6in.d6in_iaid, 6873431Scarlsonj dsmp->dsm_lif->lif_iaid); 6883431Scarlsonj continue; 6893431Scarlsonj } 6903431Scarlsonj 6913431Scarlsonj /* 6923431Scarlsonj * See notes below; there's only one IA_NA and a single IAID 6933431Scarlsonj * for now. 6943431Scarlsonj */ 6953431Scarlsonj if ((dlp = dsmp->dsm_leases) != NULL) 6963431Scarlsonj dlp->dl_stale = B_FALSE; 6973431Scarlsonj 6983431Scarlsonj /* 6993431Scarlsonj * Note that some bug-ridden servers will try to give us 7003431Scarlsonj * multiple IA_NA options for a single IAID. We ignore 7013431Scarlsonj * duplicates. 7023431Scarlsonj */ 7033431Scarlsonj if (got_iana) { 7043431Scarlsonj dhcpmsg(MSG_WARNING, "configure_v6_leases: unexpected " 7053431Scarlsonj "extra IA_NA ignored"); 7063431Scarlsonj continue; 7073431Scarlsonj } 7083431Scarlsonj 7093431Scarlsonj d6in.d6in_t1 = ntohl(d6in.d6in_t1); 7103431Scarlsonj d6in.d6in_t2 = ntohl(d6in.d6in_t2); 7113431Scarlsonj 7123431Scarlsonj /* RFC 3315 required check for invalid T1/T2 combinations */ 7133431Scarlsonj if (d6in.d6in_t1 > d6in.d6in_t2 && d6in.d6in_t2 != 0) { 7143431Scarlsonj dhcpmsg(MSG_WARNING, "configure_v6_leases: ignored " 7153431Scarlsonj "IA_NA with invalid T1 %u > T2 %u", d6in.d6in_t1, 7163431Scarlsonj d6in.d6in_t2); 7173431Scarlsonj continue; 7183431Scarlsonj } 7193431Scarlsonj 7203431Scarlsonj /* 7213431Scarlsonj * There may be a status code here. Process if present. 7223431Scarlsonj */ 7233431Scarlsonj optbase = (const char *)d6o + sizeof (d6in); 7243431Scarlsonj olen -= sizeof (d6in); 7253431Scarlsonj d6so = dhcpv6_find_option(optbase, olen, NULL, 7263431Scarlsonj DHCPV6_OPT_STATUS_CODE, &solen); 7273431Scarlsonj scode = dhcpv6_status_code(d6so, solen, &estr, &msg, &msglen); 7283431Scarlsonj if (scode != DHCPV6_STAT_SUCCESS) { 7293431Scarlsonj dhcpmsg(MSG_WARNING, 7303431Scarlsonj "configure_v6_leases: IA_NA: %s: %.*s", 7313431Scarlsonj estr, msglen, msg); 7323431Scarlsonj } 7333431Scarlsonj print_server_msg(dsmp, msg, msglen); 7343431Scarlsonj 7353431Scarlsonj /* 7363431Scarlsonj * Other errors are possible here. According to RFC 3315 7373431Scarlsonj * section 18.1.8, we ignore the entire IA if it gives the "no 7383431Scarlsonj * addresses" status code. We may try another server if we 7393431Scarlsonj * like -- we instead opt to allow the addresses to expire and 7403431Scarlsonj * then try a new server. 7413431Scarlsonj * 7423431Scarlsonj * If the status code is "no binding," then we must go back and 7433431Scarlsonj * redo the Request. Surprisingly, it doesn't matter if it's 7443431Scarlsonj * any other code. 7453431Scarlsonj */ 7463431Scarlsonj if (scode == DHCPV6_STAT_NOADDRS) { 7473431Scarlsonj dhcpmsg(MSG_DEBUG, "configure_v6_leases: ignoring " 7483431Scarlsonj "no-addrs status in IA_NA"); 7493431Scarlsonj continue; 7503431Scarlsonj } 7513431Scarlsonj 7523431Scarlsonj if (scode == DHCPV6_STAT_NOBINDING) { 7533431Scarlsonj send_v6_request(dsmp); 7543431Scarlsonj return (v6Resent); 7553431Scarlsonj } 7563431Scarlsonj 7573431Scarlsonj /* 7583431Scarlsonj * Find or create the lease structure. This part is simple, 7593431Scarlsonj * because we support only IA_NA and a single IAID. This means 7603431Scarlsonj * there's only one lease structure. The design supports 7613431Scarlsonj * multiple lease structures so that IA_TA and IA_PD can be 7623431Scarlsonj * added later. 7633431Scarlsonj */ 7643431Scarlsonj if ((dlp = dsmp->dsm_leases) == NULL && 7653431Scarlsonj (dlp = insert_lease(dsmp)) == NULL) { 7663431Scarlsonj dhcpmsg(MSG_ERROR, "configure_v6_leases: unable to " 7673431Scarlsonj "allocate memory for lease"); 7683431Scarlsonj return (v6Restart); 7693431Scarlsonj } 7703431Scarlsonj 7713431Scarlsonj /* 7723431Scarlsonj * Iterate over the IAADDR options contained within this IA_NA. 7733431Scarlsonj */ 7743431Scarlsonj shortest = DHCPV6_INFTIME; 7753431Scarlsonj d6so = NULL; 7763431Scarlsonj while ((d6so = dhcpv6_find_option(optbase, olen, d6so, 7773431Scarlsonj DHCPV6_OPT_IAADDR, &solen)) != NULL) { 7783431Scarlsonj if (solen < sizeof (d6ia)) { 7793431Scarlsonj dhcpmsg(MSG_WARNING, 7803431Scarlsonj "configure_v6_leases: garbled IAADDR"); 7813431Scarlsonj continue; 7823431Scarlsonj } 7833431Scarlsonj (void) memcpy(&d6ia, d6so, sizeof (d6ia)); 7843431Scarlsonj 7853431Scarlsonj d6ia.d6ia_preflife = ntohl(d6ia.d6ia_preflife); 7863431Scarlsonj d6ia.d6ia_vallife = ntohl(d6ia.d6ia_vallife); 7873431Scarlsonj 7883431Scarlsonj /* RFC 3315 required validity check */ 7893431Scarlsonj if (d6ia.d6ia_preflife > d6ia.d6ia_vallife) { 7903431Scarlsonj dhcpmsg(MSG_WARNING, 7913431Scarlsonj "configure_v6_leases: ignored IAADDR with " 7923431Scarlsonj "preferred lifetime %u > valid %u", 7933431Scarlsonj d6ia.d6ia_preflife, d6ia.d6ia_vallife); 7943431Scarlsonj continue; 7953431Scarlsonj } 7963431Scarlsonj 7973431Scarlsonj /* 7983431Scarlsonj * RFC 3315 allows a status code to be buried inside 7993431Scarlsonj * the IAADDR option. Look for it, and process if 8003431Scarlsonj * present. Process in a manner similar to that for 8013431Scarlsonj * the IA itself; TAHI checks for this. Real servers 8023431Scarlsonj * likely won't do this. 8033431Scarlsonj */ 8043431Scarlsonj d6sso = dhcpv6_find_option((const char *)d6so + 8053431Scarlsonj sizeof (d6ia), solen - sizeof (d6ia), NULL, 8063431Scarlsonj DHCPV6_OPT_STATUS_CODE, &ssolen); 8073431Scarlsonj scode = dhcpv6_status_code(d6sso, ssolen, &estr, &msg, 8083431Scarlsonj &msglen); 8093431Scarlsonj print_server_msg(dsmp, msg, msglen); 8103431Scarlsonj if (scode == DHCPV6_STAT_NOADDRS) { 8113431Scarlsonj dhcpmsg(MSG_DEBUG, "configure_v6_leases: " 8123431Scarlsonj "ignoring no-addrs status in IAADDR"); 8133431Scarlsonj continue; 8143431Scarlsonj } 8153431Scarlsonj if (scode == DHCPV6_STAT_NOBINDING) { 8163431Scarlsonj send_v6_request(dsmp); 8173431Scarlsonj return (v6Resent); 8183431Scarlsonj } 8193431Scarlsonj if (scode != DHCPV6_STAT_SUCCESS) { 8203431Scarlsonj dhcpmsg(MSG_WARNING, 8213431Scarlsonj "configure_v6_leases: IAADDR: %s", estr); 8223431Scarlsonj } 8233431Scarlsonj 8243431Scarlsonj /* 8253431Scarlsonj * Locate the existing LIF within the lease associated 8263431Scarlsonj * with this address, if any. 8273431Scarlsonj */ 8283431Scarlsonj lif = dlp->dl_lifs; 8293431Scarlsonj for (nlifs = dlp->dl_nlifs; nlifs > 0; 8303431Scarlsonj nlifs--, lif = lif->lif_next) { 8313431Scarlsonj if (IN6_ARE_ADDR_EQUAL(&d6ia.d6ia_addr, 8323431Scarlsonj &lif->lif_v6addr)) 8333431Scarlsonj break; 8343431Scarlsonj } 8353431Scarlsonj 8363431Scarlsonj /* 8373431Scarlsonj * If the server has set the lifetime to zero, then 8383431Scarlsonj * delete the LIF. Otherwise, set the new LIF expiry 8393431Scarlsonj * time, adding the LIF if necessary. 8403431Scarlsonj */ 8413431Scarlsonj if (d6ia.d6ia_vallife == 0) { 8423431Scarlsonj /* If it was found, then it's expired */ 8433431Scarlsonj if (nlifs != 0) { 8443431Scarlsonj dhcpmsg(MSG_DEBUG, 8453431Scarlsonj "configure_v6_leases: lif %s has " 8463431Scarlsonj "expired", lif->lif_name); 8473431Scarlsonj lif->lif_expired = B_TRUE; 8483431Scarlsonj } 8493431Scarlsonj continue; 8503431Scarlsonj } 8513431Scarlsonj 8523431Scarlsonj /* If it wasn't found, then create it now. */ 8533431Scarlsonj if (nlifs == 0) { 8543431Scarlsonj lif = plumb_lif(dsmp->dsm_lif->lif_pif, 8553431Scarlsonj &d6ia.d6ia_addr); 8563431Scarlsonj if (lif == NULL) 8573431Scarlsonj continue; 8583431Scarlsonj if (++dlp->dl_nlifs == 1) { 8593431Scarlsonj dlp->dl_lifs = lif; 8603431Scarlsonj } else { 8613431Scarlsonj remque(lif); 8623431Scarlsonj insque(lif, dlp->dl_lifs); 8633431Scarlsonj } 8643431Scarlsonj lif->lif_lease = dlp; 8653431Scarlsonj lif->lif_dad_wait = _B_TRUE; 8663431Scarlsonj dsmp->dsm_lif_wait++; 8673431Scarlsonj } else { 8683431Scarlsonj /* If it was found, cancel timer */ 8693431Scarlsonj cancel_lif_timers(lif); 8703431Scarlsonj if (d6ia.d6ia_preflife != 0 && 8713431Scarlsonj !clear_lif_deprecated(lif)) { 8723431Scarlsonj unplumb_lif(lif); 8733431Scarlsonj continue; 8743431Scarlsonj } 8753431Scarlsonj } 8763431Scarlsonj 8773431Scarlsonj /* Set the new expiry timers */ 8783431Scarlsonj init_timer(&lif->lif_preferred, d6ia.d6ia_preflife); 8793431Scarlsonj init_timer(&lif->lif_expire, d6ia.d6ia_vallife); 8803431Scarlsonj 8813431Scarlsonj /* 8823431Scarlsonj * If the preferred lifetime is over now, then the LIF 8833431Scarlsonj * is deprecated. If it's the same as the expiry time, 8843431Scarlsonj * then we don't need a separate timer for it. 8853431Scarlsonj */ 8863431Scarlsonj if (d6ia.d6ia_preflife == 0) { 8873431Scarlsonj set_lif_deprecated(lif); 8883431Scarlsonj } else if (d6ia.d6ia_preflife != DHCPV6_INFTIME && 8893431Scarlsonj d6ia.d6ia_preflife != d6ia.d6ia_vallife && 8903431Scarlsonj !schedule_lif_timer(lif, &lif->lif_preferred, 8913431Scarlsonj dhcp_deprecate)) { 8923431Scarlsonj unplumb_lif(lif); 8933431Scarlsonj continue; 8943431Scarlsonj } 8953431Scarlsonj 8963431Scarlsonj if (d6ia.d6ia_vallife != DHCPV6_INFTIME && 8973431Scarlsonj !schedule_lif_timer(lif, &lif->lif_expire, 8983431Scarlsonj dhcp_expire)) { 8993431Scarlsonj unplumb_lif(lif); 9003431Scarlsonj continue; 9013431Scarlsonj } 9023431Scarlsonj 9033431Scarlsonj if (d6ia.d6ia_preflife < shortest) 9043431Scarlsonj shortest = d6ia.d6ia_preflife; 9053431Scarlsonj } 9063431Scarlsonj 9073431Scarlsonj if (dlp->dl_nlifs == 0) { 9083431Scarlsonj dhcpmsg(MSG_WARNING, 9093431Scarlsonj "configure_v6_leases: no IAADDRs found in IA_NA"); 9103431Scarlsonj remove_lease(dlp); 9113431Scarlsonj continue; 9123431Scarlsonj } 9133431Scarlsonj 9143431Scarlsonj if (d6in.d6in_t1 == 0 && d6in.d6in_t2 == 0) { 9153431Scarlsonj /* Default values from RFC 3315: 0.5 and 0.8 */ 9163431Scarlsonj if ((d6in.d6in_t1 = shortest / 2) == 0) 9173431Scarlsonj d6in.d6in_t1 = 1; 9183431Scarlsonj d6in.d6in_t2 = shortest - shortest / 5; 9193431Scarlsonj } 9203431Scarlsonj 9213431Scarlsonj cancel_lease_timers(dlp); 9223431Scarlsonj init_timer(&dlp->dl_t1, d6in.d6in_t1); 9233431Scarlsonj init_timer(&dlp->dl_t2, d6in.d6in_t2); 9243431Scarlsonj 9253431Scarlsonj if ((d6in.d6in_t1 != DHCPV6_INFTIME && 9263431Scarlsonj !schedule_lease_timer(dlp, &dlp->dl_t1, dhcp_renew)) || 9273431Scarlsonj (d6in.d6in_t2 != DHCPV6_INFTIME && 9283431Scarlsonj !schedule_lease_timer(dlp, &dlp->dl_t2, dhcp_rebind))) { 9293431Scarlsonj dhcpmsg(MSG_WARNING, "configure_v6_leases: unable to " 9303431Scarlsonj "set renew/rebind timers"); 9313431Scarlsonj } else { 9323431Scarlsonj got_iana = B_TRUE; 9333431Scarlsonj } 9343431Scarlsonj } 9353431Scarlsonj 9363431Scarlsonj if (!got_iana) { 9373431Scarlsonj dhcpmsg(MSG_WARNING, 9383431Scarlsonj "configure_v6_leases: no usable IA_NA option found"); 9393431Scarlsonj } 9403431Scarlsonj 9413431Scarlsonj return (v6Done); 9423431Scarlsonj } 9433431Scarlsonj 9443431Scarlsonj /* 9453431Scarlsonj * configure_v4_lease(): configures the IPv4 lease on a state machine from 9463431Scarlsonj * the current DHCP ACK. There's only one lease and LIF 9473431Scarlsonj * per state machine in IPv4. 9483431Scarlsonj * 9493431Scarlsonj * input: dhcp_smach_t *: the machine to configure (with a valid dsm_ack) 9503431Scarlsonj * output: boolean_t: B_TRUE on success, B_FALSE on failure 9513431Scarlsonj */ 9523431Scarlsonj 9533431Scarlsonj static boolean_t 9543431Scarlsonj configure_v4_lease(dhcp_smach_t *dsmp) 9553431Scarlsonj { 9563431Scarlsonj struct lifreq lifr; 9570Sstevel@tonic-gate struct sockaddr_in *sin; 9583431Scarlsonj PKT_LIST *ack = dsmp->dsm_ack; 9593431Scarlsonj dhcp_lease_t *dlp; 9603431Scarlsonj dhcp_lif_t *lif; 9613431Scarlsonj uint32_t addrhbo; 9623431Scarlsonj struct in_addr inaddr; 9630Sstevel@tonic-gate 9640Sstevel@tonic-gate /* 9650Sstevel@tonic-gate * if we're using DHCP, then we'll have a valid CD_SERVER_ID 9660Sstevel@tonic-gate * (we checked in dhcp_acknak()); set it now so that 9673431Scarlsonj * dsmp->dsm_server is valid in case we need to send_decline(). 9680Sstevel@tonic-gate * note that we use comparisons against opts[CD_DHCP_TYPE] 9690Sstevel@tonic-gate * since we haven't set DHCP_IF_BOOTP yet (we don't do that 9700Sstevel@tonic-gate * until we're sure we want the offered address.) 9710Sstevel@tonic-gate */ 9720Sstevel@tonic-gate 9733431Scarlsonj if (ack->opts[CD_DHCP_TYPE] != NULL) { 9743431Scarlsonj (void) memcpy(&inaddr, ack->opts[CD_SERVER_ID]->value, 9753431Scarlsonj sizeof (inaddr)); 9763431Scarlsonj IN6_INADDR_TO_V4MAPPED(&inaddr, &dsmp->dsm_server); 9770Sstevel@tonic-gate } 9780Sstevel@tonic-gate 9793431Scarlsonj /* 9803431Scarlsonj * There needs to be exactly one lease for IPv4, and that lease 9813431Scarlsonj * controls the main LIF for the state machine. If it doesn't exist 9823431Scarlsonj * yet, then create it now. 9833431Scarlsonj */ 9843431Scarlsonj if ((dlp = dsmp->dsm_leases) == NULL && 9853431Scarlsonj (dlp = insert_lease(dsmp)) == NULL) { 9863431Scarlsonj dhcpmsg(MSG_ERROR, "configure_v4_lease: unable to allocate " 9873431Scarlsonj "memory for lease"); 9883431Scarlsonj return (B_FALSE); 9893431Scarlsonj } 9903431Scarlsonj if (dlp->dl_nlifs == 0) { 9913431Scarlsonj dlp->dl_lifs = dsmp->dsm_lif; 9923431Scarlsonj dlp->dl_nlifs = 1; 9933431Scarlsonj 9943431Scarlsonj /* The lease holds a reference on the LIF */ 9953431Scarlsonj hold_lif(dlp->dl_lifs); 9963431Scarlsonj dlp->dl_lifs->lif_lease = dlp; 9973431Scarlsonj } 9983431Scarlsonj 9993431Scarlsonj lif = dlp->dl_lifs; 10003431Scarlsonj 10013431Scarlsonj IN6_INADDR_TO_V4MAPPED(&ack->pkt->yiaddr, &lif->lif_v6addr); 10023431Scarlsonj addrhbo = ntohl(ack->pkt->yiaddr.s_addr); 10033431Scarlsonj if ((addrhbo & IN_CLASSA_NET) == 0 || 10043431Scarlsonj (addrhbo >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET || 10053431Scarlsonj IN_CLASSD(addrhbo)) { 10063431Scarlsonj dhcpmsg(MSG_ERROR, 10073431Scarlsonj "configure_v4_lease: got invalid IP address %s for %s", 10083431Scarlsonj inet_ntoa(ack->pkt->yiaddr), lif->lif_name); 10093431Scarlsonj return (B_FALSE); 10103431Scarlsonj } 10113431Scarlsonj 10123431Scarlsonj (void) memset(&lifr, 0, sizeof (struct lifreq)); 10133431Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 10140Sstevel@tonic-gate 10150Sstevel@tonic-gate /* 10160Sstevel@tonic-gate * bring the interface online. note that there is no optimal 10170Sstevel@tonic-gate * order here: it is considered bad taste (and in > solaris 7, 10180Sstevel@tonic-gate * likely illegal) to bring an interface up before it has an 10190Sstevel@tonic-gate * ip address. however, due to an apparent bug in sun fddi 10200Sstevel@tonic-gate * 5.0, fddi will not obtain a network routing entry unless 10210Sstevel@tonic-gate * the interface is brought up before it has an ip address. 10220Sstevel@tonic-gate * we take the lesser of the two evils; if fddi customers have 10230Sstevel@tonic-gate * problems, they can get a newer fddi distribution which 10240Sstevel@tonic-gate * fixes the problem. 10250Sstevel@tonic-gate */ 10260Sstevel@tonic-gate 10273431Scarlsonj sin = (struct sockaddr_in *)&lifr.lifr_addr; 10280Sstevel@tonic-gate sin->sin_family = AF_INET; 10290Sstevel@tonic-gate 10303431Scarlsonj (void) memset(&lif->lif_v6mask, 0xff, sizeof (lif->lif_v6mask)); 10310Sstevel@tonic-gate if (ack->opts[CD_SUBNETMASK] != NULL && 10323431Scarlsonj ack->opts[CD_SUBNETMASK]->len == sizeof (inaddr)) { 10330Sstevel@tonic-gate 10343431Scarlsonj (void) memcpy(&inaddr, ack->opts[CD_SUBNETMASK]->value, 10353431Scarlsonj sizeof (inaddr)); 10360Sstevel@tonic-gate 10370Sstevel@tonic-gate } else { 10380Sstevel@tonic-gate 10390Sstevel@tonic-gate if (ack->opts[CD_SUBNETMASK] != NULL && 10403431Scarlsonj ack->opts[CD_SUBNETMASK]->len != sizeof (inaddr)) { 10413431Scarlsonj dhcpmsg(MSG_WARNING, "configure_v4_lease: specified " 10423431Scarlsonj "subnet mask length is %d instead of %d, ignoring", 10430Sstevel@tonic-gate ack->opts[CD_SUBNETMASK]->len, sizeof (ipaddr_t)); 10443431Scarlsonj } else { 10453431Scarlsonj dhcpmsg(MSG_WARNING, "configure_v4_lease: no IP " 10463431Scarlsonj "netmask specified for %s, making best guess", 10473431Scarlsonj lif->lif_name); 10483431Scarlsonj } 10490Sstevel@tonic-gate 10500Sstevel@tonic-gate /* 10510Sstevel@tonic-gate * no legitimate IP subnet mask specified.. use best 10523431Scarlsonj * guess. recall that lif_addr is in network order, so 10530Sstevel@tonic-gate * imagine it's 0x11223344: then when it is read into 10540Sstevel@tonic-gate * a register on x86, it becomes 0x44332211, so we 10550Sstevel@tonic-gate * must ntohl() it to convert it to 0x11223344 in 10560Sstevel@tonic-gate * order to use the macros in <netinet/in.h>. 10570Sstevel@tonic-gate */ 10580Sstevel@tonic-gate 10593431Scarlsonj if (IN_CLASSA(addrhbo)) 10603431Scarlsonj inaddr.s_addr = htonl(IN_CLASSA_NET); 10613431Scarlsonj else if (IN_CLASSB(addrhbo)) 10623431Scarlsonj inaddr.s_addr = htonl(IN_CLASSB_NET); 10635577Ssangeeta else if (IN_CLASSC(addrhbo)) 10643431Scarlsonj inaddr.s_addr = htonl(IN_CLASSC_NET); 10655577Ssangeeta else { 10665577Ssangeeta /* 10675577Ssangeeta * Cant be Class D as that is multicast 10685577Ssangeeta * Must be Class E 10695577Ssangeeta */ 10705577Ssangeeta inaddr.s_addr = htonl(IN_CLASSE_NET); 10715577Ssangeeta } 10723431Scarlsonj } 10733431Scarlsonj lif->lif_v6mask._S6_un._S6_u32[3] = inaddr.s_addr; 10740Sstevel@tonic-gate 10753431Scarlsonj sin->sin_addr = inaddr; 10763431Scarlsonj dhcpmsg(MSG_INFO, "configure_v4_lease: setting IP netmask to %s on %s", 10773431Scarlsonj inet_ntoa(sin->sin_addr), lif->lif_name); 10783431Scarlsonj 10793431Scarlsonj if (ioctl(v4_sock_fd, SIOCSLIFNETMASK, &lifr) == -1) { 10803431Scarlsonj dhcpmsg(MSG_ERR, "configure_v4_lease: cannot set IP netmask " 10813431Scarlsonj "on %s", lif->lif_name); 10823431Scarlsonj return (B_FALSE); 10830Sstevel@tonic-gate } 10840Sstevel@tonic-gate 10853431Scarlsonj IN6_V4MAPPED_TO_INADDR(&lif->lif_v6addr, &sin->sin_addr); 10863431Scarlsonj dhcpmsg(MSG_INFO, "configure_v4_lease: setting IP address to %s on %s", 10873431Scarlsonj inet_ntoa(sin->sin_addr), lif->lif_name); 10880Sstevel@tonic-gate 10893431Scarlsonj if (ioctl(v4_sock_fd, SIOCSLIFADDR, &lifr) == -1) { 10903431Scarlsonj dhcpmsg(MSG_ERR, "configure_v4_lease: cannot set IP address " 10913431Scarlsonj "on %s", lif->lif_name); 10923431Scarlsonj return (B_FALSE); 10933431Scarlsonj } 10943431Scarlsonj 10953431Scarlsonj lif->lif_dad_wait = B_TRUE; 10963431Scarlsonj dsmp->dsm_lif_wait++; 10970Sstevel@tonic-gate 10980Sstevel@tonic-gate if (ack->opts[CD_BROADCASTADDR] != NULL && 10993431Scarlsonj ack->opts[CD_BROADCASTADDR]->len == sizeof (inaddr)) { 11000Sstevel@tonic-gate 11013431Scarlsonj (void) memcpy(&inaddr, ack->opts[CD_BROADCASTADDR]->value, 11023431Scarlsonj sizeof (inaddr)); 11030Sstevel@tonic-gate 11040Sstevel@tonic-gate } else { 11050Sstevel@tonic-gate 11060Sstevel@tonic-gate if (ack->opts[CD_BROADCASTADDR] != NULL && 11073431Scarlsonj ack->opts[CD_BROADCASTADDR]->len != sizeof (inaddr)) { 11083431Scarlsonj dhcpmsg(MSG_WARNING, "configure_v4_lease: specified " 11090Sstevel@tonic-gate "broadcast address length is %d instead of %d, " 11100Sstevel@tonic-gate "ignoring", ack->opts[CD_BROADCASTADDR]->len, 11113431Scarlsonj sizeof (inaddr)); 11123431Scarlsonj } else { 11133431Scarlsonj dhcpmsg(MSG_WARNING, "configure_v4_lease: no IP " 11143431Scarlsonj "broadcast specified for %s, making best guess", 11153431Scarlsonj lif->lif_name); 11163431Scarlsonj } 11170Sstevel@tonic-gate 11180Sstevel@tonic-gate /* 11190Sstevel@tonic-gate * no legitimate IP broadcast specified. compute it 11200Sstevel@tonic-gate * from the IP address and netmask. 11210Sstevel@tonic-gate */ 11220Sstevel@tonic-gate 11233431Scarlsonj IN6_V4MAPPED_TO_INADDR(&lif->lif_v6addr, &inaddr); 11243431Scarlsonj inaddr.s_addr |= ~lif->lif_v6mask._S6_un._S6_u32[3]; 11250Sstevel@tonic-gate } 11260Sstevel@tonic-gate 11270Sstevel@tonic-gate /* 11280Sstevel@tonic-gate * the kernel will set the broadcast address for us as part of 11290Sstevel@tonic-gate * bringing the interface up. since experience has shown that dhcp 11300Sstevel@tonic-gate * servers sometimes provide a bogus broadcast address, we let the 11310Sstevel@tonic-gate * kernel set it so that it's guaranteed to be correct. 11320Sstevel@tonic-gate * 11330Sstevel@tonic-gate * also, note any inconsistencies and save the broadcast address the 11340Sstevel@tonic-gate * kernel set so that we can watch for changes to it. 11350Sstevel@tonic-gate */ 11360Sstevel@tonic-gate 11373431Scarlsonj if (ioctl(v4_sock_fd, SIOCGLIFBRDADDR, &lifr) == -1) { 11383431Scarlsonj dhcpmsg(MSG_ERR, "configure_v4_lease: cannot get broadcast " 11393431Scarlsonj "address for %s", lif->lif_name); 11403431Scarlsonj return (B_FALSE); 11410Sstevel@tonic-gate } 11420Sstevel@tonic-gate 11433431Scarlsonj if (inaddr.s_addr != sin->sin_addr.s_addr) { 11443431Scarlsonj dhcpmsg(MSG_WARNING, "configure_v4_lease: incorrect broadcast " 11453431Scarlsonj "address %s specified for %s; ignoring", inet_ntoa(inaddr), 11463431Scarlsonj lif->lif_name); 11470Sstevel@tonic-gate } 11480Sstevel@tonic-gate 11493476Scarlsonj lif->lif_broadcast = sin->sin_addr.s_addr; 11503431Scarlsonj dhcpmsg(MSG_INFO, 11513431Scarlsonj "configure_v4_lease: using broadcast address %s on %s", 11523431Scarlsonj inet_ntoa(inaddr), lif->lif_name); 11533431Scarlsonj return (B_TRUE); 11542546Scarlsonj } 11552546Scarlsonj 11562546Scarlsonj /* 11573431Scarlsonj * save_server_id(): save off the new DHCPv6 Server ID 11582546Scarlsonj * 11593431Scarlsonj * input: dhcp_smach_t *: the state machine to use 11603431Scarlsonj * PKT_LIST *: the packet with the Reply message 11613431Scarlsonj * output: boolean_t: B_TRUE on success, B_FALSE on failure 11622546Scarlsonj */ 11632546Scarlsonj 11643431Scarlsonj boolean_t 11653431Scarlsonj save_server_id(dhcp_smach_t *dsmp, PKT_LIST *msg) 11662546Scarlsonj { 11673431Scarlsonj const dhcpv6_option_t *d6o; 11683431Scarlsonj uint_t olen; 11690Sstevel@tonic-gate 11703431Scarlsonj d6o = dhcpv6_pkt_option(msg, NULL, DHCPV6_OPT_SERVERID, &olen); 11713431Scarlsonj if (d6o == NULL) 11723431Scarlsonj return (B_FALSE); 11733431Scarlsonj olen -= sizeof (*d6o); 11743431Scarlsonj free(dsmp->dsm_serverid); 11753431Scarlsonj if ((dsmp->dsm_serverid = malloc(olen)) == NULL) { 11763431Scarlsonj return (B_FALSE); 11773431Scarlsonj } else { 11783431Scarlsonj dsmp->dsm_serveridlen = olen; 11793431Scarlsonj (void) memcpy(dsmp->dsm_serverid, d6o + 1, olen); 11803431Scarlsonj return (B_TRUE); 11810Sstevel@tonic-gate } 11820Sstevel@tonic-gate } 1183