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 /* 223431Scarlsonj * Copyright 2007 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 #pragma ident "%Z%%M% %I% %E% SMI" 290Sstevel@tonic-gate 300Sstevel@tonic-gate #include <sys/socket.h> 310Sstevel@tonic-gate #include <sys/types.h> 320Sstevel@tonic-gate #include <string.h> 330Sstevel@tonic-gate #include <netinet/in.h> 340Sstevel@tonic-gate #include <sys/sockio.h> 350Sstevel@tonic-gate #include <unistd.h> 360Sstevel@tonic-gate #include <time.h> 370Sstevel@tonic-gate #include <arpa/inet.h> 380Sstevel@tonic-gate #include <stdlib.h> 393431Scarlsonj #include <search.h> 400Sstevel@tonic-gate #include <sys/sysmacros.h> 410Sstevel@tonic-gate #include <dhcp_hostconf.h> 423431Scarlsonj #include <dhcpagent_util.h> 430Sstevel@tonic-gate #include <dhcpmsg.h> 440Sstevel@tonic-gate 450Sstevel@tonic-gate #include "states.h" 460Sstevel@tonic-gate #include "packet.h" 470Sstevel@tonic-gate #include "util.h" 480Sstevel@tonic-gate #include "agent.h" 490Sstevel@tonic-gate #include "interface.h" 500Sstevel@tonic-gate #include "script_handler.h" 510Sstevel@tonic-gate 523431Scarlsonj /* 533431Scarlsonj * Possible outcomes for IPv6 binding attempt. 543431Scarlsonj */ 553431Scarlsonj enum v6_bind_result { 563431Scarlsonj v6Restart, /* report failure and restart state machine */ 573431Scarlsonj v6Resent, /* new Request message has been sent */ 583431Scarlsonj v6Done /* successful binding */ 593431Scarlsonj }; 600Sstevel@tonic-gate 613431Scarlsonj static enum v6_bind_result configure_v6_leases(dhcp_smach_t *); 623431Scarlsonj static boolean_t configure_v4_lease(dhcp_smach_t *); 633431Scarlsonj static boolean_t configure_v4_timers(dhcp_smach_t *); 640Sstevel@tonic-gate 650Sstevel@tonic-gate /* 660Sstevel@tonic-gate * bound_event_cb(): callback for script_start on the event EVENT_BOUND 670Sstevel@tonic-gate * 683431Scarlsonj * input: dhcp_smach_t *: the state machine configured 693431Scarlsonj * void *: unused 700Sstevel@tonic-gate * output: int: always 1 710Sstevel@tonic-gate */ 720Sstevel@tonic-gate 733431Scarlsonj /* ARGSUSED1 */ 740Sstevel@tonic-gate static int 753431Scarlsonj bound_event_cb(dhcp_smach_t *dsmp, void *arg) 760Sstevel@tonic-gate { 773431Scarlsonj if (dsmp->dsm_ia.ia_fd != -1) 783431Scarlsonj ipc_action_finish(dsmp, DHCP_IPC_SUCCESS); 793431Scarlsonj else 803431Scarlsonj async_finish(dsmp); 810Sstevel@tonic-gate return (1); 820Sstevel@tonic-gate } 830Sstevel@tonic-gate 840Sstevel@tonic-gate /* 853431Scarlsonj * dhcp_bound(): configures an state machine and interfaces using information 863431Scarlsonj * contained in the ACK/Reply packet and sets up lease timers. 873431Scarlsonj * Before starting, the requested address is verified by 883431Scarlsonj * Duplicate Address Detection to make sure it's not in use. 890Sstevel@tonic-gate * 903431Scarlsonj * input: dhcp_smach_t *: the state machine to move to bound 913431Scarlsonj * PKT_LIST *: the ACK/Reply packet, or NULL to use dsmp->dsm_ack 923431Scarlsonj * output: boolean_t: B_TRUE on success, B_FALSE on failure 930Sstevel@tonic-gate */ 940Sstevel@tonic-gate 953431Scarlsonj boolean_t 963431Scarlsonj dhcp_bound(dhcp_smach_t *dsmp, PKT_LIST *ack) 970Sstevel@tonic-gate { 983431Scarlsonj DHCPSTATE oldstate; 993431Scarlsonj lease_t new_lease; 1003431Scarlsonj dhcp_lif_t *lif; 1013431Scarlsonj dhcp_lease_t *dlp; 1023431Scarlsonj enum v6_bind_result v6b; 1030Sstevel@tonic-gate 1040Sstevel@tonic-gate if (ack != NULL) { 1050Sstevel@tonic-gate /* If ack we're replacing is not the original, then free it */ 1063431Scarlsonj if (dsmp->dsm_ack != dsmp->dsm_orig_ack) 1073431Scarlsonj free_pkt_entry(dsmp->dsm_ack); 1083431Scarlsonj dsmp->dsm_ack = ack; 1090Sstevel@tonic-gate /* Save the first ack as the original */ 1103431Scarlsonj if (dsmp->dsm_orig_ack == NULL) 1113431Scarlsonj dsmp->dsm_orig_ack = ack; 1120Sstevel@tonic-gate } 1130Sstevel@tonic-gate 1143431Scarlsonj oldstate = dsmp->dsm_state; 1153431Scarlsonj switch (oldstate) { 1160Sstevel@tonic-gate 1170Sstevel@tonic-gate case ADOPTING: 1183431Scarlsonj /* Note that adoption occurs only for IPv4 DHCP. */ 1193431Scarlsonj 1203431Scarlsonj /* Ignore BOOTP */ 1213431Scarlsonj if (ack->opts[CD_DHCP_TYPE] == NULL) 1223431Scarlsonj return (B_FALSE); 1230Sstevel@tonic-gate 1240Sstevel@tonic-gate /* 1253431Scarlsonj * if we're adopting a lease, the lease timers 1260Sstevel@tonic-gate * only provide an upper bound since we don't know 1270Sstevel@tonic-gate * from what time they are relative to. assume we 1280Sstevel@tonic-gate * have a lease time of at most DHCP_ADOPT_LEASE_MAX. 1290Sstevel@tonic-gate */ 1303431Scarlsonj (void) memcpy(&new_lease, ack->opts[CD_LEASE_TIME]->value, 1313431Scarlsonj sizeof (lease_t)); 1320Sstevel@tonic-gate 1330Sstevel@tonic-gate new_lease = htonl(MIN(ntohl(new_lease), DHCP_ADOPT_LEASE_MAX)); 1340Sstevel@tonic-gate 1353431Scarlsonj (void) memcpy(ack->opts[CD_LEASE_TIME]->value, &new_lease, 1363431Scarlsonj sizeof (lease_t)); 1370Sstevel@tonic-gate 1380Sstevel@tonic-gate /* 1390Sstevel@tonic-gate * we have no idea when the REQUEST that generated 1400Sstevel@tonic-gate * this ACK was sent, but for diagnostic purposes 1410Sstevel@tonic-gate * we'll assume its close to the current time. 1420Sstevel@tonic-gate */ 1433431Scarlsonj dsmp->dsm_newstart_monosec = monosec(); 1442612Scarlsonj 1453431Scarlsonj if (dsmp->dsm_isv6) { 1463431Scarlsonj if ((v6b = configure_v6_leases(dsmp)) != v6Done) 1473431Scarlsonj return (v6b == v6Resent); 1483431Scarlsonj } else { 1493431Scarlsonj if (!configure_v4_lease(dsmp)) 1503431Scarlsonj return (B_FALSE); 1512546Scarlsonj 1523431Scarlsonj if (!configure_v4_timers(dsmp)) 1533431Scarlsonj return (B_FALSE); 1543431Scarlsonj } 1553431Scarlsonj 1563431Scarlsonj dsmp->dsm_curstart_monosec = dsmp->dsm_newstart_monosec; 1572546Scarlsonj break; 1580Sstevel@tonic-gate 1593431Scarlsonj case SELECTING: 1600Sstevel@tonic-gate case REQUESTING: 1610Sstevel@tonic-gate case INIT_REBOOT: 1620Sstevel@tonic-gate 1633431Scarlsonj if (dsmp->dsm_isv6) { 1643431Scarlsonj if ((v6b = configure_v6_leases(dsmp)) != v6Done) 1653431Scarlsonj return (v6b == v6Resent); 1663431Scarlsonj } else { 1673431Scarlsonj if (!configure_v4_lease(dsmp)) 1683431Scarlsonj return (B_FALSE); 1690Sstevel@tonic-gate 1703431Scarlsonj if (!configure_v4_timers(dsmp)) 1713431Scarlsonj return (B_FALSE); 1723431Scarlsonj 1733431Scarlsonj if (!clear_lif_deprecated(dsmp->dsm_lif)) 1743431Scarlsonj return (B_FALSE); 1753431Scarlsonj } 1763431Scarlsonj 1773431Scarlsonj /* Stop sending requests now */ 1783431Scarlsonj stop_pkt_retransmission(dsmp); 1790Sstevel@tonic-gate 1800Sstevel@tonic-gate /* 1813431Scarlsonj * If we didn't end up with any usable leases, then we have a 1823431Scarlsonj * problem. 1833431Scarlsonj */ 1843431Scarlsonj if (dsmp->dsm_leases == NULL) { 1853431Scarlsonj dhcpmsg(MSG_WARNING, 1863431Scarlsonj "dhcp_bound: no address lease established"); 1873431Scarlsonj return (B_FALSE); 1883431Scarlsonj } 1893431Scarlsonj 1903431Scarlsonj /* 1913431Scarlsonj * If this is a Rapid-Commit (selecting state) or if we're 1923431Scarlsonj * dealing with a reboot (init-reboot), then we will have a new 1933431Scarlsonj * server ID to save. 1940Sstevel@tonic-gate */ 1953431Scarlsonj if (ack != NULL && 1963431Scarlsonj (oldstate == SELECTING || oldstate == INIT_REBOOT) && 1973431Scarlsonj dsmp->dsm_isv6 && !save_server_id(dsmp, ack)) { 1983431Scarlsonj dhcpmsg(MSG_ERROR, 1993431Scarlsonj "dhcp_bound: unable to save server ID on %s", 2003431Scarlsonj dsmp->dsm_name); 2013431Scarlsonj return (B_FALSE); 2023431Scarlsonj } 2033431Scarlsonj 2043431Scarlsonj /* 2053431Scarlsonj * We will continue configuring the interfaces via 2063431Scarlsonj * dhcp_bound_complete, once kernel DAD completes. If no new 2073431Scarlsonj * leases were created (which can happen on an init-reboot used 2083431Scarlsonj * for link-up confirmation), then go straight to bound state. 2093431Scarlsonj */ 2103431Scarlsonj if (!set_smach_state(dsmp, PRE_BOUND)) 2113431Scarlsonj return (B_FALSE); 2123431Scarlsonj if (dsmp->dsm_lif_wait == 0) 2133431Scarlsonj dhcp_bound_complete(dsmp); 2142546Scarlsonj break; 2150Sstevel@tonic-gate 2162546Scarlsonj case PRE_BOUND: 2173431Scarlsonj case BOUND: 2184516Scarlsonj case INFORMATION: 2192546Scarlsonj /* This is just a duplicate ack; silently ignore it */ 2203431Scarlsonj return (B_TRUE); 2210Sstevel@tonic-gate 2220Sstevel@tonic-gate case RENEWING: 2230Sstevel@tonic-gate case REBINDING: 2243431Scarlsonj 2253431Scarlsonj if (dsmp->dsm_isv6) { 2263431Scarlsonj if ((v6b = configure_v6_leases(dsmp)) != v6Done) 2273431Scarlsonj return (v6b == v6Resent); 2283431Scarlsonj } else { 2293431Scarlsonj if (!configure_v4_timers(dsmp)) 2303431Scarlsonj return (B_FALSE); 2313431Scarlsonj if (!clear_lif_deprecated(dsmp->dsm_lif)) 2323431Scarlsonj return (B_FALSE); 2333431Scarlsonj } 2340Sstevel@tonic-gate 2350Sstevel@tonic-gate /* 2363431Scarlsonj * If some or all of the leases were torn down by the server, 2373431Scarlsonj * then handle that as an expiry. When the script is done 2383431Scarlsonj * running for the LOSS6 event, we'll end up back here. 2390Sstevel@tonic-gate */ 2403431Scarlsonj if ((lif = find_expired_lif(dsmp)) != NULL) { 2413431Scarlsonj hold_lif(lif); 2423431Scarlsonj dhcp_expire(NULL, lif); 2433431Scarlsonj while ((lif = find_expired_lif(dsmp)) != NULL) { 2443431Scarlsonj dlp = lif->lif_lease; 2453431Scarlsonj unplumb_lif(lif); 2463431Scarlsonj if (dlp->dl_nlifs == 0) 2473431Scarlsonj remove_lease(dlp); 2483431Scarlsonj } 2493431Scarlsonj if (dsmp->dsm_leases == NULL) 2503431Scarlsonj return (B_FALSE); 2513431Scarlsonj } 2520Sstevel@tonic-gate 2533431Scarlsonj if (oldstate == REBINDING && dsmp->dsm_isv6 && 2543431Scarlsonj !save_server_id(dsmp, ack)) { 2553431Scarlsonj return (B_FALSE); 2560Sstevel@tonic-gate } 2570Sstevel@tonic-gate 2583431Scarlsonj /* 2593431Scarlsonj * Handle Renew/Rebind that fails to address one of our leases. 2603431Scarlsonj * (Should just never happen, but RFC 3315 section 18.1.8 2613431Scarlsonj * requires it, and TAHI tests for it.) 2623431Scarlsonj */ 2633431Scarlsonj for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) { 2643431Scarlsonj if (dlp->dl_stale && dlp->dl_nlifs > 0) 2653431Scarlsonj break; 2663431Scarlsonj } 2673431Scarlsonj if (dlp != NULL) { 2683431Scarlsonj dhcpmsg(MSG_DEBUG, "dhcp_bound: lease not updated; " 2693431Scarlsonj "allow retransmit"); 2703431Scarlsonj return (B_TRUE); 2713431Scarlsonj } 2720Sstevel@tonic-gate 2733431Scarlsonj if (!set_smach_state(dsmp, BOUND)) 2743431Scarlsonj return (B_FALSE); 2753431Scarlsonj 2763431Scarlsonj (void) script_start(dsmp, dsmp->dsm_isv6 ? EVENT_EXTEND6 : 2773431Scarlsonj EVENT_EXTEND, bound_event_cb, NULL, NULL); 2783431Scarlsonj 2793431Scarlsonj dsmp->dsm_curstart_monosec = dsmp->dsm_newstart_monosec; 2803431Scarlsonj 2813431Scarlsonj /* Stop sending requests now */ 2823431Scarlsonj stop_pkt_retransmission(dsmp); 2830Sstevel@tonic-gate break; 2840Sstevel@tonic-gate 2850Sstevel@tonic-gate case INFORM_SENT: 2860Sstevel@tonic-gate 2873431Scarlsonj if (dsmp->dsm_isv6 && !save_server_id(dsmp, ack)) { 2883431Scarlsonj return (B_FALSE); 2893431Scarlsonj } 2903431Scarlsonj 2913431Scarlsonj (void) bound_event_cb(dsmp, NULL); 2923431Scarlsonj if (!set_smach_state(dsmp, INFORMATION)) 2933431Scarlsonj return (B_FALSE); 2943431Scarlsonj 2953431Scarlsonj /* Stop sending requests now */ 2963431Scarlsonj stop_pkt_retransmission(dsmp); 2970Sstevel@tonic-gate break; 2980Sstevel@tonic-gate 2990Sstevel@tonic-gate default: 3000Sstevel@tonic-gate /* something is really bizarre... */ 3013431Scarlsonj dhcpmsg(MSG_DEBUG, 3023431Scarlsonj "dhcp_bound: called in unexpected state: %s", 3033431Scarlsonj dhcp_state_to_string(dsmp->dsm_state)); 3043431Scarlsonj return (B_FALSE); 3050Sstevel@tonic-gate } 3060Sstevel@tonic-gate 3070Sstevel@tonic-gate /* 3080Sstevel@tonic-gate * remove any stale hostconf file that might be lying around for 3093431Scarlsonj * this state machine. (in general, it's harmless, since we'll write a 3100Sstevel@tonic-gate * fresh one when we exit anyway, but just to reduce confusion..) 3110Sstevel@tonic-gate */ 3120Sstevel@tonic-gate 3133431Scarlsonj (void) remove_hostconf(dsmp->dsm_name, dsmp->dsm_isv6); 3143431Scarlsonj return (B_TRUE); 3150Sstevel@tonic-gate } 3160Sstevel@tonic-gate 3170Sstevel@tonic-gate /* 3182546Scarlsonj * dhcp_bound_complete(): complete interface configuration after DAD 3192546Scarlsonj * 3203431Scarlsonj * input: dhcp_smach_t *: the state machine now ready 3212546Scarlsonj * output: none 3222546Scarlsonj */ 3232546Scarlsonj 3242546Scarlsonj void 3253431Scarlsonj dhcp_bound_complete(dhcp_smach_t *dsmp) 3262546Scarlsonj { 3273431Scarlsonj PKT_LIST *ack; 3283431Scarlsonj DHCP_OPT *router_list; 3293431Scarlsonj int i; 3303431Scarlsonj DHCPSTATE oldstate; 3314106Scarlsonj dhcp_lif_t *lif; 3323431Scarlsonj 3333431Scarlsonj /* 3343431Scarlsonj * Do bound state entry processing only if running IPv4. There's no 3353431Scarlsonj * need for this with DHCPv6 because link-locals are used for I/O and 3363431Scarlsonj * because DHCPv6 isn't entangled with routing. 3373431Scarlsonj */ 3383431Scarlsonj if (dsmp->dsm_isv6) { 3393431Scarlsonj (void) set_smach_state(dsmp, BOUND); 3405381Smeem dhcpmsg(MSG_DEBUG, "dhcp_bound_complete: bound %s", 3413431Scarlsonj dsmp->dsm_name); 3423431Scarlsonj (void) script_start(dsmp, EVENT_BOUND6, bound_event_cb, NULL, 3433431Scarlsonj NULL); 3443431Scarlsonj dsmp->dsm_curstart_monosec = dsmp->dsm_newstart_monosec; 3452546Scarlsonj return; 3463431Scarlsonj } 3472546Scarlsonj 3482612Scarlsonj /* 3494106Scarlsonj * Add each provided router; we'll clean them up when the 3503431Scarlsonj * state machine goes away or when our lease expires. 3514106Scarlsonj * 3524106Scarlsonj * Note that we do not handle default routers on IPv4 logicals; 3534106Scarlsonj * see README for details. 3543431Scarlsonj */ 3553431Scarlsonj 3563431Scarlsonj ack = dsmp->dsm_ack; 3573431Scarlsonj router_list = ack->opts[CD_ROUTER]; 3584106Scarlsonj lif = dsmp->dsm_lif; 3594106Scarlsonj if (router_list != NULL && 3604106Scarlsonj (router_list->len % sizeof (ipaddr_t)) == 0 && 3614106Scarlsonj strchr(lif->lif_name, ':') == NULL) { 3623431Scarlsonj 3633431Scarlsonj dsmp->dsm_nrouters = router_list->len / sizeof (ipaddr_t); 3643431Scarlsonj dsmp->dsm_routers = malloc(router_list->len); 3653431Scarlsonj if (dsmp->dsm_routers == NULL) { 3665381Smeem dhcpmsg(MSG_ERR, "dhcp_bound_complete: cannot allocate " 3673431Scarlsonj "default router list, ignoring default routers"); 3683431Scarlsonj dsmp->dsm_nrouters = 0; 3693431Scarlsonj } 3703431Scarlsonj 3713431Scarlsonj for (i = 0; i < dsmp->dsm_nrouters; i++) { 3723431Scarlsonj 3733431Scarlsonj (void) memcpy(&dsmp->dsm_routers[i].s_addr, 3743431Scarlsonj router_list->value + (i * sizeof (ipaddr_t)), 3753431Scarlsonj sizeof (ipaddr_t)); 3763431Scarlsonj 3774106Scarlsonj if (!add_default_route(lif->lif_pif->pif_index, 3783431Scarlsonj &dsmp->dsm_routers[i])) { 3795381Smeem dhcpmsg(MSG_ERR, "dhcp_bound_complete: cannot " 3805381Smeem "add default router %s on %s", inet_ntoa( 3813431Scarlsonj dsmp->dsm_routers[i]), dsmp->dsm_name); 3823431Scarlsonj dsmp->dsm_routers[i].s_addr = htonl(INADDR_ANY); 3833431Scarlsonj continue; 3843431Scarlsonj } 3853431Scarlsonj 3863431Scarlsonj dhcpmsg(MSG_INFO, "added default router %s on %s", 3873431Scarlsonj inet_ntoa(dsmp->dsm_routers[i]), dsmp->dsm_name); 3883431Scarlsonj } 3893431Scarlsonj } 3903431Scarlsonj 3913431Scarlsonj oldstate = dsmp->dsm_state; 3923431Scarlsonj if (!set_smach_state(dsmp, BOUND)) { 3933431Scarlsonj dhcpmsg(MSG_ERR, 3945381Smeem "dhcp_bound_complete: cannot set bound state on %s", 3953431Scarlsonj dsmp->dsm_name); 3963431Scarlsonj return; 3973431Scarlsonj } 3983431Scarlsonj 3995381Smeem dhcpmsg(MSG_DEBUG, "dhcp_bound_complete: bound %s", dsmp->dsm_name); 4003431Scarlsonj 4013431Scarlsonj /* 4023431Scarlsonj * We're now committed to this binding, so if it came from BOOTP, set 4033431Scarlsonj * the flag. 4043431Scarlsonj */ 4053431Scarlsonj 4063431Scarlsonj if (ack->opts[CD_DHCP_TYPE] == NULL) 4073431Scarlsonj dsmp->dsm_dflags |= DHCP_IF_BOOTP; 4083431Scarlsonj 4093431Scarlsonj /* 4103431Scarlsonj * If the previous state was ADOPTING, event loop has not been started 4112612Scarlsonj * at this time; so don't run the EVENT_BOUND script. 4122612Scarlsonj */ 4133431Scarlsonj if (oldstate != ADOPTING) { 4143431Scarlsonj (void) script_start(dsmp, EVENT_BOUND, bound_event_cb, NULL, 4152612Scarlsonj NULL); 4162612Scarlsonj } 4172546Scarlsonj 4183431Scarlsonj dsmp->dsm_curstart_monosec = dsmp->dsm_newstart_monosec; 4192546Scarlsonj } 4202546Scarlsonj 4212546Scarlsonj /* 4223431Scarlsonj * fuzzify(): adds some "fuzz" to a t1/t2 time, in accordance with RFC2131. 4233431Scarlsonj * We use up to plus or minus 2% jitter in the time. This is a 4243431Scarlsonj * small value, but the timers involved are typically long. A 4253431Scarlsonj * common T1 value is one day, and the fuzz is up to 28.8 minutes; 4263431Scarlsonj * plenty of time to make sure that individual clients don't renew 4273431Scarlsonj * all at the same time. 4280Sstevel@tonic-gate * 4293431Scarlsonj * input: uint32_t: the number of seconds until lease expiration 4303431Scarlsonj * double: the approximate percentage of that time to return 4313431Scarlsonj * output: double: a number approximating (sec * pct) 4323431Scarlsonj */ 4333431Scarlsonj 4343431Scarlsonj static double 4353431Scarlsonj fuzzify(uint32_t sec, double pct) 4363431Scarlsonj { 4373431Scarlsonj return (sec * (pct + (drand48() - 0.5) / 25.0)); 4383431Scarlsonj } 4393431Scarlsonj 4403431Scarlsonj /* 4413431Scarlsonj * get_pkt_times(): pulls the lease times out of a v4 DHCP packet and stores 4423431Scarlsonj * them as host byte-order relative times in the passed in 4433431Scarlsonj * parameters. 4443431Scarlsonj * 4453431Scarlsonj * input: PKT_LIST *: the packet to pull the packet times from 4463431Scarlsonj * lease_t *: where to store the relative lease time in hbo 4473431Scarlsonj * lease_t *: where to store the relative t1 time in hbo 4483431Scarlsonj * lease_t *: where to store the relative t2 time in hbo 4493431Scarlsonj * output: void 4500Sstevel@tonic-gate */ 4510Sstevel@tonic-gate 4523431Scarlsonj static void 4533431Scarlsonj get_pkt_times(PKT_LIST *ack, lease_t *lease, lease_t *t1, lease_t *t2) 4540Sstevel@tonic-gate { 4553431Scarlsonj *lease = DHCP_PERM; 4563431Scarlsonj *t1 = DHCP_PERM; 4573431Scarlsonj *t2 = DHCP_PERM; 4580Sstevel@tonic-gate 4593431Scarlsonj if (ack->opts[CD_DHCP_TYPE] == NULL) { 4603431Scarlsonj dhcpmsg(MSG_VERBOSE, 4613431Scarlsonj "get_pkt_times: BOOTP response; infinite lease"); 4623431Scarlsonj return; 4633431Scarlsonj } 4643431Scarlsonj if (ack->opts[CD_LEASE_TIME] == NULL) { 4653431Scarlsonj dhcpmsg(MSG_VERBOSE, 4663431Scarlsonj "get_pkt_times: no lease option provided"); 4673431Scarlsonj return; 4683431Scarlsonj } 4693431Scarlsonj if (ack->opts[CD_LEASE_TIME]->len != sizeof (lease_t)) { 4703431Scarlsonj dhcpmsg(MSG_VERBOSE, "get_pkt_times: invalid lease option"); 4713431Scarlsonj } 4723431Scarlsonj 4733431Scarlsonj (void) memcpy(lease, ack->opts[CD_LEASE_TIME]->value, sizeof (lease_t)); 4743431Scarlsonj *lease = ntohl(*lease); 4753431Scarlsonj 4763431Scarlsonj if (*lease == DHCP_PERM) { 4773431Scarlsonj dhcpmsg(MSG_VERBOSE, "get_pkt_times: infinite lease granted"); 4783431Scarlsonj return; 4793431Scarlsonj } 4803431Scarlsonj 4813431Scarlsonj if (ack->opts[CD_T1_TIME] != NULL && 4823431Scarlsonj ack->opts[CD_T1_TIME]->len == sizeof (lease_t)) { 4833431Scarlsonj (void) memcpy(t1, ack->opts[CD_T1_TIME]->value, sizeof (*t1)); 4843431Scarlsonj *t1 = ntohl(*t1); 4853431Scarlsonj } 4863431Scarlsonj 4873431Scarlsonj if (ack->opts[CD_T2_TIME] != NULL && 4883431Scarlsonj ack->opts[CD_T2_TIME]->len == sizeof (lease_t)) { 4893431Scarlsonj (void) memcpy(t2, ack->opts[CD_T2_TIME]->value, sizeof (*t2)); 4903431Scarlsonj *t2 = ntohl(*t2); 4910Sstevel@tonic-gate } 4920Sstevel@tonic-gate 4933431Scarlsonj if ((*t1 == DHCP_PERM) || (*t1 >= *lease)) 4943431Scarlsonj *t1 = (lease_t)fuzzify(*lease, DHCP_T1_FACT); 4953431Scarlsonj 4963431Scarlsonj if ((*t2 == DHCP_PERM) || (*t2 > *lease) || (*t2 <= *t1)) 4973431Scarlsonj *t2 = (lease_t)fuzzify(*lease, DHCP_T2_FACT); 4983431Scarlsonj 4993431Scarlsonj dhcpmsg(MSG_VERBOSE, "get_pkt_times: lease %u t1 %u t2 %u", 5003431Scarlsonj *lease, *t1, *t2); 5013431Scarlsonj } 5023431Scarlsonj 5033431Scarlsonj /* 5043431Scarlsonj * configure_v4_timers(): configures the lease timers on a v4 state machine 5053431Scarlsonj * 5063431Scarlsonj * input: dhcp_smach_t *: the state machine to configure 5073431Scarlsonj * output: boolean_t: B_TRUE on success, B_FALSE on failure 5083431Scarlsonj */ 5093431Scarlsonj 5103431Scarlsonj static boolean_t 5113431Scarlsonj configure_v4_timers(dhcp_smach_t *dsmp) 5123431Scarlsonj { 5133431Scarlsonj PKT_LIST *ack = dsmp->dsm_ack; 5143431Scarlsonj lease_t lease, t1, t2; 5153431Scarlsonj dhcp_lease_t *dlp; 5163431Scarlsonj dhcp_lif_t *lif; 5173431Scarlsonj 5183431Scarlsonj /* v4 has just one lease per state machine, and one LIF */ 5193431Scarlsonj dlp = dsmp->dsm_leases; 5203431Scarlsonj lif = dlp->dl_lifs; 5213431Scarlsonj 5223431Scarlsonj /* 5233431Scarlsonj * If it's DHCP, but there's no valid lease time, then complain, 5243431Scarlsonj * decline the lease and return error. 5253431Scarlsonj */ 5263431Scarlsonj if (ack->opts[CD_DHCP_TYPE] != NULL && 5273431Scarlsonj (ack->opts[CD_LEASE_TIME] == NULL || 5283431Scarlsonj ack->opts[CD_LEASE_TIME]->len != sizeof (lease_t))) { 5293431Scarlsonj lif_mark_decline(lif, "Missing or corrupted lease time"); 5303431Scarlsonj send_declines(dsmp); 5313431Scarlsonj dhcpmsg(MSG_WARNING, "configure_v4_timers: %s lease time in " 5323431Scarlsonj "ACK on %s", ack->opts[CD_LEASE_TIME] == NULL ? "missing" : 5333431Scarlsonj "corrupt", dsmp->dsm_name); 5343431Scarlsonj return (B_FALSE); 5353431Scarlsonj } 5363431Scarlsonj 5373431Scarlsonj /* Stop the T1 and T2 timers */ 5383431Scarlsonj cancel_lease_timers(dlp); 5393431Scarlsonj 5403431Scarlsonj /* Stop the LEASE timer */ 5413431Scarlsonj cancel_lif_timers(lif); 5420Sstevel@tonic-gate 5430Sstevel@tonic-gate /* 5440Sstevel@tonic-gate * type has already been verified as ACK. if type is not set, 5450Sstevel@tonic-gate * then we got a BOOTP packet. we now fetch the t1, t2, and 5460Sstevel@tonic-gate * lease options out of the packet into variables. they are 5470Sstevel@tonic-gate * returned as relative host-byte-ordered times. 5480Sstevel@tonic-gate */ 5490Sstevel@tonic-gate 5503431Scarlsonj get_pkt_times(ack, &lease, &t1, &t2); 5513431Scarlsonj 5523431Scarlsonj /* 5533431Scarlsonj * if the current lease is mysteriously close to the new 5543431Scarlsonj * lease, warn the user. unless there's less than a minute 5553431Scarlsonj * left, round to the closest minute. 5563431Scarlsonj */ 5573431Scarlsonj 5583431Scarlsonj if (lif->lif_expire.dt_start != 0 && 5593431Scarlsonj abs((dsmp->dsm_newstart_monosec + lease) - 5603431Scarlsonj (dsmp->dsm_curstart_monosec + lif->lif_expire.dt_start)) < 5613431Scarlsonj DHCP_LEASE_EPS) { 5623431Scarlsonj const char *noext = "configure_v4_timers: lease renewed but " 5633431Scarlsonj "time not extended"; 5643431Scarlsonj int msg_level; 5653431Scarlsonj uint_t minleft; 5660Sstevel@tonic-gate 5673431Scarlsonj if (lif->lif_expire.dt_start < DHCP_LEASE_ERROR_THRESH) 5683431Scarlsonj msg_level = MSG_ERROR; 5693431Scarlsonj else 5703431Scarlsonj msg_level = MSG_VERBOSE; 5713431Scarlsonj 5723431Scarlsonj minleft = (lif->lif_expire.dt_start + 30) / 60; 5730Sstevel@tonic-gate 5743431Scarlsonj if (lif->lif_expire.dt_start < 60) { 5753431Scarlsonj dhcpmsg(msg_level, "%s; expires in %d seconds", 5763431Scarlsonj noext, lif->lif_expire.dt_start); 5773431Scarlsonj } else if (minleft == 1) { 5783431Scarlsonj dhcpmsg(msg_level, "%s; expires in 1 minute", noext); 5793431Scarlsonj } else if (minleft > 120) { 5803431Scarlsonj dhcpmsg(msg_level, "%s; expires in %d hours", 5813431Scarlsonj noext, (minleft + 30) / 60); 5823431Scarlsonj } else { 5833431Scarlsonj dhcpmsg(msg_level, "%s; expires in %d minutes", 5843431Scarlsonj noext, minleft); 5853431Scarlsonj } 5860Sstevel@tonic-gate } 5870Sstevel@tonic-gate 5883431Scarlsonj init_timer(&dlp->dl_t1, t1); 5893431Scarlsonj init_timer(&dlp->dl_t2, t2); 5903431Scarlsonj init_timer(&lif->lif_expire, lease); 5913431Scarlsonj 5923431Scarlsonj if (lease == DHCP_PERM) { 5933431Scarlsonj dhcpmsg(MSG_INFO, 5943431Scarlsonj "configure_v4_timers: %s acquired permanent lease", 5953431Scarlsonj dsmp->dsm_name); 5963431Scarlsonj return (B_TRUE); 5973431Scarlsonj } 5980Sstevel@tonic-gate 5993431Scarlsonj dhcpmsg(MSG_INFO, "configure_v4_timers: %s acquired lease, expires %s", 6003431Scarlsonj dsmp->dsm_name, 6013431Scarlsonj monosec_to_string(dsmp->dsm_newstart_monosec + lease)); 6020Sstevel@tonic-gate 6033431Scarlsonj dhcpmsg(MSG_INFO, "configure_v4_timers: %s begins renewal at %s", 6043431Scarlsonj dsmp->dsm_name, monosec_to_string(dsmp->dsm_newstart_monosec + 6053431Scarlsonj dlp->dl_t1.dt_start)); 6063431Scarlsonj 6073431Scarlsonj dhcpmsg(MSG_INFO, "configure_v4_timers: %s begins rebinding at %s", 6083431Scarlsonj dsmp->dsm_name, monosec_to_string(dsmp->dsm_newstart_monosec + 6093431Scarlsonj dlp->dl_t2.dt_start)); 6100Sstevel@tonic-gate 6110Sstevel@tonic-gate /* 6120Sstevel@tonic-gate * according to RFC2131, there is no minimum lease time, but don't 6130Sstevel@tonic-gate * set up renew/rebind timers if lease is shorter than DHCP_REBIND_MIN. 6140Sstevel@tonic-gate */ 6150Sstevel@tonic-gate 6163431Scarlsonj if (!schedule_lif_timer(lif, &lif->lif_expire, dhcp_expire)) 6170Sstevel@tonic-gate goto failure; 6180Sstevel@tonic-gate 6190Sstevel@tonic-gate if (lease < DHCP_REBIND_MIN) { 6203431Scarlsonj dhcpmsg(MSG_WARNING, "configure_v4_timers: lease on %s is for " 6213431Scarlsonj "less than %d seconds!", dsmp->dsm_name, DHCP_REBIND_MIN); 6223431Scarlsonj return (B_TRUE); 6230Sstevel@tonic-gate } 6240Sstevel@tonic-gate 6253431Scarlsonj if (!schedule_lease_timer(dlp, &dlp->dl_t1, dhcp_renew)) 6260Sstevel@tonic-gate goto failure; 6270Sstevel@tonic-gate 6283431Scarlsonj if (!schedule_lease_timer(dlp, &dlp->dl_t2, dhcp_rebind)) 6290Sstevel@tonic-gate goto failure; 6300Sstevel@tonic-gate 6313431Scarlsonj return (B_TRUE); 6320Sstevel@tonic-gate 6330Sstevel@tonic-gate failure: 6343431Scarlsonj cancel_lease_timers(dlp); 6353431Scarlsonj cancel_lif_timers(lif); 6363431Scarlsonj dhcpmsg(MSG_WARNING, 6373431Scarlsonj "configure_v4_timers: cannot schedule lease timers"); 6383431Scarlsonj return (B_FALSE); 6390Sstevel@tonic-gate } 6400Sstevel@tonic-gate 6410Sstevel@tonic-gate /* 6423431Scarlsonj * configure_v6_leases(): configures the IPv6 leases on a state machine from 6433431Scarlsonj * the current DHCPv6 ACK. We need to scan the ACK, 6443431Scarlsonj * create a lease for each IA_NA, and a new LIF for each 6453431Scarlsonj * IAADDR. 6460Sstevel@tonic-gate * 6473431Scarlsonj * input: dhcp_smach_t *: the machine to configure (with a valid dsm_ack) 6483431Scarlsonj * output: enum v6_bind_result: restart, resend, or done 6490Sstevel@tonic-gate */ 6500Sstevel@tonic-gate 6513431Scarlsonj static enum v6_bind_result 6523431Scarlsonj configure_v6_leases(dhcp_smach_t *dsmp) 6530Sstevel@tonic-gate { 6543431Scarlsonj const dhcpv6_option_t *d6o, *d6so, *d6sso; 6553431Scarlsonj const char *optbase, *estr, *msg; 6563431Scarlsonj uint_t olen, solen, ssolen, msglen; 6573431Scarlsonj dhcpv6_ia_na_t d6in; 6583431Scarlsonj dhcpv6_iaaddr_t d6ia; 6593431Scarlsonj dhcp_lease_t *dlp; 6603431Scarlsonj uint32_t shortest; 6613431Scarlsonj dhcp_lif_t *lif; 6623431Scarlsonj uint_t nlifs; 6633431Scarlsonj boolean_t got_iana = B_FALSE; 6643431Scarlsonj uint_t scode; 6653431Scarlsonj 6663431Scarlsonj for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) 6673431Scarlsonj dlp->dl_stale = B_TRUE; 6683431Scarlsonj 6693431Scarlsonj d6o = NULL; 6703431Scarlsonj while ((d6o = dhcpv6_pkt_option(dsmp->dsm_ack, d6o, DHCPV6_OPT_IA_NA, 6713431Scarlsonj &olen)) != NULL) { 6723431Scarlsonj if (olen < sizeof (d6in)) { 6733431Scarlsonj dhcpmsg(MSG_WARNING, 6743431Scarlsonj "configure_v6_leases: garbled IA_NA"); 6753431Scarlsonj continue; 6763431Scarlsonj } 6773431Scarlsonj 6783431Scarlsonj /* 6793431Scarlsonj * Check the IAID. It should be for our controlling LIF. If a 6803431Scarlsonj * single state machine needs to use multiple IAIDs, then this 6813431Scarlsonj * will need to change. 6823431Scarlsonj */ 6833431Scarlsonj (void) memcpy(&d6in, d6o, sizeof (d6in)); 6843431Scarlsonj d6in.d6in_iaid = ntohl(d6in.d6in_iaid); 6853431Scarlsonj if (d6in.d6in_iaid != dsmp->dsm_lif->lif_iaid) { 6863431Scarlsonj dhcpmsg(MSG_WARNING, "configure_v6_leases: ignored " 6873431Scarlsonj "IA_NA for IAID %x (not %x)", d6in.d6in_iaid, 6883431Scarlsonj dsmp->dsm_lif->lif_iaid); 6893431Scarlsonj continue; 6903431Scarlsonj } 6913431Scarlsonj 6923431Scarlsonj /* 6933431Scarlsonj * See notes below; there's only one IA_NA and a single IAID 6943431Scarlsonj * for now. 6953431Scarlsonj */ 6963431Scarlsonj if ((dlp = dsmp->dsm_leases) != NULL) 6973431Scarlsonj dlp->dl_stale = B_FALSE; 6983431Scarlsonj 6993431Scarlsonj /* 7003431Scarlsonj * Note that some bug-ridden servers will try to give us 7013431Scarlsonj * multiple IA_NA options for a single IAID. We ignore 7023431Scarlsonj * duplicates. 7033431Scarlsonj */ 7043431Scarlsonj if (got_iana) { 7053431Scarlsonj dhcpmsg(MSG_WARNING, "configure_v6_leases: unexpected " 7063431Scarlsonj "extra IA_NA ignored"); 7073431Scarlsonj continue; 7083431Scarlsonj } 7093431Scarlsonj 7103431Scarlsonj d6in.d6in_t1 = ntohl(d6in.d6in_t1); 7113431Scarlsonj d6in.d6in_t2 = ntohl(d6in.d6in_t2); 7123431Scarlsonj 7133431Scarlsonj /* RFC 3315 required check for invalid T1/T2 combinations */ 7143431Scarlsonj if (d6in.d6in_t1 > d6in.d6in_t2 && d6in.d6in_t2 != 0) { 7153431Scarlsonj dhcpmsg(MSG_WARNING, "configure_v6_leases: ignored " 7163431Scarlsonj "IA_NA with invalid T1 %u > T2 %u", d6in.d6in_t1, 7173431Scarlsonj d6in.d6in_t2); 7183431Scarlsonj continue; 7193431Scarlsonj } 7203431Scarlsonj 7213431Scarlsonj /* 7223431Scarlsonj * There may be a status code here. Process if present. 7233431Scarlsonj */ 7243431Scarlsonj optbase = (const char *)d6o + sizeof (d6in); 7253431Scarlsonj olen -= sizeof (d6in); 7263431Scarlsonj d6so = dhcpv6_find_option(optbase, olen, NULL, 7273431Scarlsonj DHCPV6_OPT_STATUS_CODE, &solen); 7283431Scarlsonj scode = dhcpv6_status_code(d6so, solen, &estr, &msg, &msglen); 7293431Scarlsonj if (scode != DHCPV6_STAT_SUCCESS) { 7303431Scarlsonj dhcpmsg(MSG_WARNING, 7313431Scarlsonj "configure_v6_leases: IA_NA: %s: %.*s", 7323431Scarlsonj estr, msglen, msg); 7333431Scarlsonj } 7343431Scarlsonj print_server_msg(dsmp, msg, msglen); 7353431Scarlsonj 7363431Scarlsonj /* 7373431Scarlsonj * Other errors are possible here. According to RFC 3315 7383431Scarlsonj * section 18.1.8, we ignore the entire IA if it gives the "no 7393431Scarlsonj * addresses" status code. We may try another server if we 7403431Scarlsonj * like -- we instead opt to allow the addresses to expire and 7413431Scarlsonj * then try a new server. 7423431Scarlsonj * 7433431Scarlsonj * If the status code is "no binding," then we must go back and 7443431Scarlsonj * redo the Request. Surprisingly, it doesn't matter if it's 7453431Scarlsonj * any other code. 7463431Scarlsonj */ 7473431Scarlsonj if (scode == DHCPV6_STAT_NOADDRS) { 7483431Scarlsonj dhcpmsg(MSG_DEBUG, "configure_v6_leases: ignoring " 7493431Scarlsonj "no-addrs status in IA_NA"); 7503431Scarlsonj continue; 7513431Scarlsonj } 7523431Scarlsonj 7533431Scarlsonj if (scode == DHCPV6_STAT_NOBINDING) { 7543431Scarlsonj send_v6_request(dsmp); 7553431Scarlsonj return (v6Resent); 7563431Scarlsonj } 7573431Scarlsonj 7583431Scarlsonj /* 7593431Scarlsonj * Find or create the lease structure. This part is simple, 7603431Scarlsonj * because we support only IA_NA and a single IAID. This means 7613431Scarlsonj * there's only one lease structure. The design supports 7623431Scarlsonj * multiple lease structures so that IA_TA and IA_PD can be 7633431Scarlsonj * added later. 7643431Scarlsonj */ 7653431Scarlsonj if ((dlp = dsmp->dsm_leases) == NULL && 7663431Scarlsonj (dlp = insert_lease(dsmp)) == NULL) { 7673431Scarlsonj dhcpmsg(MSG_ERROR, "configure_v6_leases: unable to " 7683431Scarlsonj "allocate memory for lease"); 7693431Scarlsonj return (v6Restart); 7703431Scarlsonj } 7713431Scarlsonj 7723431Scarlsonj /* 7733431Scarlsonj * Iterate over the IAADDR options contained within this IA_NA. 7743431Scarlsonj */ 7753431Scarlsonj shortest = DHCPV6_INFTIME; 7763431Scarlsonj d6so = NULL; 7773431Scarlsonj while ((d6so = dhcpv6_find_option(optbase, olen, d6so, 7783431Scarlsonj DHCPV6_OPT_IAADDR, &solen)) != NULL) { 7793431Scarlsonj if (solen < sizeof (d6ia)) { 7803431Scarlsonj dhcpmsg(MSG_WARNING, 7813431Scarlsonj "configure_v6_leases: garbled IAADDR"); 7823431Scarlsonj continue; 7833431Scarlsonj } 7843431Scarlsonj (void) memcpy(&d6ia, d6so, sizeof (d6ia)); 7853431Scarlsonj 7863431Scarlsonj d6ia.d6ia_preflife = ntohl(d6ia.d6ia_preflife); 7873431Scarlsonj d6ia.d6ia_vallife = ntohl(d6ia.d6ia_vallife); 7883431Scarlsonj 7893431Scarlsonj /* RFC 3315 required validity check */ 7903431Scarlsonj if (d6ia.d6ia_preflife > d6ia.d6ia_vallife) { 7913431Scarlsonj dhcpmsg(MSG_WARNING, 7923431Scarlsonj "configure_v6_leases: ignored IAADDR with " 7933431Scarlsonj "preferred lifetime %u > valid %u", 7943431Scarlsonj d6ia.d6ia_preflife, d6ia.d6ia_vallife); 7953431Scarlsonj continue; 7963431Scarlsonj } 7973431Scarlsonj 7983431Scarlsonj /* 7993431Scarlsonj * RFC 3315 allows a status code to be buried inside 8003431Scarlsonj * the IAADDR option. Look for it, and process if 8013431Scarlsonj * present. Process in a manner similar to that for 8023431Scarlsonj * the IA itself; TAHI checks for this. Real servers 8033431Scarlsonj * likely won't do this. 8043431Scarlsonj */ 8053431Scarlsonj d6sso = dhcpv6_find_option((const char *)d6so + 8063431Scarlsonj sizeof (d6ia), solen - sizeof (d6ia), NULL, 8073431Scarlsonj DHCPV6_OPT_STATUS_CODE, &ssolen); 8083431Scarlsonj scode = dhcpv6_status_code(d6sso, ssolen, &estr, &msg, 8093431Scarlsonj &msglen); 8103431Scarlsonj print_server_msg(dsmp, msg, msglen); 8113431Scarlsonj if (scode == DHCPV6_STAT_NOADDRS) { 8123431Scarlsonj dhcpmsg(MSG_DEBUG, "configure_v6_leases: " 8133431Scarlsonj "ignoring no-addrs status in IAADDR"); 8143431Scarlsonj continue; 8153431Scarlsonj } 8163431Scarlsonj if (scode == DHCPV6_STAT_NOBINDING) { 8173431Scarlsonj send_v6_request(dsmp); 8183431Scarlsonj return (v6Resent); 8193431Scarlsonj } 8203431Scarlsonj if (scode != DHCPV6_STAT_SUCCESS) { 8213431Scarlsonj dhcpmsg(MSG_WARNING, 8223431Scarlsonj "configure_v6_leases: IAADDR: %s", estr); 8233431Scarlsonj } 8243431Scarlsonj 8253431Scarlsonj /* 8263431Scarlsonj * Locate the existing LIF within the lease associated 8273431Scarlsonj * with this address, if any. 8283431Scarlsonj */ 8293431Scarlsonj lif = dlp->dl_lifs; 8303431Scarlsonj for (nlifs = dlp->dl_nlifs; nlifs > 0; 8313431Scarlsonj nlifs--, lif = lif->lif_next) { 8323431Scarlsonj if (IN6_ARE_ADDR_EQUAL(&d6ia.d6ia_addr, 8333431Scarlsonj &lif->lif_v6addr)) 8343431Scarlsonj break; 8353431Scarlsonj } 8363431Scarlsonj 8373431Scarlsonj /* 8383431Scarlsonj * If the server has set the lifetime to zero, then 8393431Scarlsonj * delete the LIF. Otherwise, set the new LIF expiry 8403431Scarlsonj * time, adding the LIF if necessary. 8413431Scarlsonj */ 8423431Scarlsonj if (d6ia.d6ia_vallife == 0) { 8433431Scarlsonj /* If it was found, then it's expired */ 8443431Scarlsonj if (nlifs != 0) { 8453431Scarlsonj dhcpmsg(MSG_DEBUG, 8463431Scarlsonj "configure_v6_leases: lif %s has " 8473431Scarlsonj "expired", lif->lif_name); 8483431Scarlsonj lif->lif_expired = B_TRUE; 8493431Scarlsonj } 8503431Scarlsonj continue; 8513431Scarlsonj } 8523431Scarlsonj 8533431Scarlsonj /* If it wasn't found, then create it now. */ 8543431Scarlsonj if (nlifs == 0) { 8553431Scarlsonj lif = plumb_lif(dsmp->dsm_lif->lif_pif, 8563431Scarlsonj &d6ia.d6ia_addr); 8573431Scarlsonj if (lif == NULL) 8583431Scarlsonj continue; 8593431Scarlsonj if (++dlp->dl_nlifs == 1) { 8603431Scarlsonj dlp->dl_lifs = lif; 8613431Scarlsonj } else { 8623431Scarlsonj remque(lif); 8633431Scarlsonj insque(lif, dlp->dl_lifs); 8643431Scarlsonj } 8653431Scarlsonj lif->lif_lease = dlp; 8663431Scarlsonj lif->lif_dad_wait = _B_TRUE; 8673431Scarlsonj dsmp->dsm_lif_wait++; 8683431Scarlsonj } else { 8693431Scarlsonj /* If it was found, cancel timer */ 8703431Scarlsonj cancel_lif_timers(lif); 8713431Scarlsonj if (d6ia.d6ia_preflife != 0 && 8723431Scarlsonj !clear_lif_deprecated(lif)) { 8733431Scarlsonj unplumb_lif(lif); 8743431Scarlsonj continue; 8753431Scarlsonj } 8763431Scarlsonj } 8773431Scarlsonj 8783431Scarlsonj /* Set the new expiry timers */ 8793431Scarlsonj init_timer(&lif->lif_preferred, d6ia.d6ia_preflife); 8803431Scarlsonj init_timer(&lif->lif_expire, d6ia.d6ia_vallife); 8813431Scarlsonj 8823431Scarlsonj /* 8833431Scarlsonj * If the preferred lifetime is over now, then the LIF 8843431Scarlsonj * is deprecated. If it's the same as the expiry time, 8853431Scarlsonj * then we don't need a separate timer for it. 8863431Scarlsonj */ 8873431Scarlsonj if (d6ia.d6ia_preflife == 0) { 8883431Scarlsonj set_lif_deprecated(lif); 8893431Scarlsonj } else if (d6ia.d6ia_preflife != DHCPV6_INFTIME && 8903431Scarlsonj d6ia.d6ia_preflife != d6ia.d6ia_vallife && 8913431Scarlsonj !schedule_lif_timer(lif, &lif->lif_preferred, 8923431Scarlsonj dhcp_deprecate)) { 8933431Scarlsonj unplumb_lif(lif); 8943431Scarlsonj continue; 8953431Scarlsonj } 8963431Scarlsonj 8973431Scarlsonj if (d6ia.d6ia_vallife != DHCPV6_INFTIME && 8983431Scarlsonj !schedule_lif_timer(lif, &lif->lif_expire, 8993431Scarlsonj dhcp_expire)) { 9003431Scarlsonj unplumb_lif(lif); 9013431Scarlsonj continue; 9023431Scarlsonj } 9033431Scarlsonj 9043431Scarlsonj if (d6ia.d6ia_preflife < shortest) 9053431Scarlsonj shortest = d6ia.d6ia_preflife; 9063431Scarlsonj } 9073431Scarlsonj 9083431Scarlsonj if (dlp->dl_nlifs == 0) { 9093431Scarlsonj dhcpmsg(MSG_WARNING, 9103431Scarlsonj "configure_v6_leases: no IAADDRs found in IA_NA"); 9113431Scarlsonj remove_lease(dlp); 9123431Scarlsonj continue; 9133431Scarlsonj } 9143431Scarlsonj 9153431Scarlsonj if (d6in.d6in_t1 == 0 && d6in.d6in_t2 == 0) { 9163431Scarlsonj /* Default values from RFC 3315: 0.5 and 0.8 */ 9173431Scarlsonj if ((d6in.d6in_t1 = shortest / 2) == 0) 9183431Scarlsonj d6in.d6in_t1 = 1; 9193431Scarlsonj d6in.d6in_t2 = shortest - shortest / 5; 9203431Scarlsonj } 9213431Scarlsonj 9223431Scarlsonj cancel_lease_timers(dlp); 9233431Scarlsonj init_timer(&dlp->dl_t1, d6in.d6in_t1); 9243431Scarlsonj init_timer(&dlp->dl_t2, d6in.d6in_t2); 9253431Scarlsonj 9263431Scarlsonj if ((d6in.d6in_t1 != DHCPV6_INFTIME && 9273431Scarlsonj !schedule_lease_timer(dlp, &dlp->dl_t1, dhcp_renew)) || 9283431Scarlsonj (d6in.d6in_t2 != DHCPV6_INFTIME && 9293431Scarlsonj !schedule_lease_timer(dlp, &dlp->dl_t2, dhcp_rebind))) { 9303431Scarlsonj dhcpmsg(MSG_WARNING, "configure_v6_leases: unable to " 9313431Scarlsonj "set renew/rebind timers"); 9323431Scarlsonj } else { 9333431Scarlsonj got_iana = B_TRUE; 9343431Scarlsonj } 9353431Scarlsonj } 9363431Scarlsonj 9373431Scarlsonj if (!got_iana) { 9383431Scarlsonj dhcpmsg(MSG_WARNING, 9393431Scarlsonj "configure_v6_leases: no usable IA_NA option found"); 9403431Scarlsonj } 9413431Scarlsonj 9423431Scarlsonj return (v6Done); 9433431Scarlsonj } 9443431Scarlsonj 9453431Scarlsonj /* 9463431Scarlsonj * configure_v4_lease(): configures the IPv4 lease on a state machine from 9473431Scarlsonj * the current DHCP ACK. There's only one lease and LIF 9483431Scarlsonj * per state machine in IPv4. 9493431Scarlsonj * 9503431Scarlsonj * input: dhcp_smach_t *: the machine to configure (with a valid dsm_ack) 9513431Scarlsonj * output: boolean_t: B_TRUE on success, B_FALSE on failure 9523431Scarlsonj */ 9533431Scarlsonj 9543431Scarlsonj static boolean_t 9553431Scarlsonj configure_v4_lease(dhcp_smach_t *dsmp) 9563431Scarlsonj { 9573431Scarlsonj struct lifreq lifr; 9580Sstevel@tonic-gate struct sockaddr_in *sin; 9593431Scarlsonj PKT_LIST *ack = dsmp->dsm_ack; 9603431Scarlsonj dhcp_lease_t *dlp; 9613431Scarlsonj dhcp_lif_t *lif; 9623431Scarlsonj uint32_t addrhbo; 9633431Scarlsonj struct in_addr inaddr; 9640Sstevel@tonic-gate 9650Sstevel@tonic-gate /* 9660Sstevel@tonic-gate * if we're using DHCP, then we'll have a valid CD_SERVER_ID 9670Sstevel@tonic-gate * (we checked in dhcp_acknak()); set it now so that 9683431Scarlsonj * dsmp->dsm_server is valid in case we need to send_decline(). 9690Sstevel@tonic-gate * note that we use comparisons against opts[CD_DHCP_TYPE] 9700Sstevel@tonic-gate * since we haven't set DHCP_IF_BOOTP yet (we don't do that 9710Sstevel@tonic-gate * until we're sure we want the offered address.) 9720Sstevel@tonic-gate */ 9730Sstevel@tonic-gate 9743431Scarlsonj if (ack->opts[CD_DHCP_TYPE] != NULL) { 9753431Scarlsonj (void) memcpy(&inaddr, ack->opts[CD_SERVER_ID]->value, 9763431Scarlsonj sizeof (inaddr)); 9773431Scarlsonj IN6_INADDR_TO_V4MAPPED(&inaddr, &dsmp->dsm_server); 9780Sstevel@tonic-gate } 9790Sstevel@tonic-gate 9803431Scarlsonj /* 9813431Scarlsonj * There needs to be exactly one lease for IPv4, and that lease 9823431Scarlsonj * controls the main LIF for the state machine. If it doesn't exist 9833431Scarlsonj * yet, then create it now. 9843431Scarlsonj */ 9853431Scarlsonj if ((dlp = dsmp->dsm_leases) == NULL && 9863431Scarlsonj (dlp = insert_lease(dsmp)) == NULL) { 9873431Scarlsonj dhcpmsg(MSG_ERROR, "configure_v4_lease: unable to allocate " 9883431Scarlsonj "memory for lease"); 9893431Scarlsonj return (B_FALSE); 9903431Scarlsonj } 9913431Scarlsonj if (dlp->dl_nlifs == 0) { 9923431Scarlsonj dlp->dl_lifs = dsmp->dsm_lif; 9933431Scarlsonj dlp->dl_nlifs = 1; 9943431Scarlsonj 9953431Scarlsonj /* The lease holds a reference on the LIF */ 9963431Scarlsonj hold_lif(dlp->dl_lifs); 9973431Scarlsonj dlp->dl_lifs->lif_lease = dlp; 9983431Scarlsonj } 9993431Scarlsonj 10003431Scarlsonj lif = dlp->dl_lifs; 10013431Scarlsonj 10023431Scarlsonj IN6_INADDR_TO_V4MAPPED(&ack->pkt->yiaddr, &lif->lif_v6addr); 10033431Scarlsonj addrhbo = ntohl(ack->pkt->yiaddr.s_addr); 10043431Scarlsonj if ((addrhbo & IN_CLASSA_NET) == 0 || 10053431Scarlsonj (addrhbo >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET || 10063431Scarlsonj IN_CLASSD(addrhbo)) { 10073431Scarlsonj dhcpmsg(MSG_ERROR, 10083431Scarlsonj "configure_v4_lease: got invalid IP address %s for %s", 10093431Scarlsonj inet_ntoa(ack->pkt->yiaddr), lif->lif_name); 10103431Scarlsonj return (B_FALSE); 10113431Scarlsonj } 10123431Scarlsonj 10133431Scarlsonj (void) memset(&lifr, 0, sizeof (struct lifreq)); 10143431Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 10150Sstevel@tonic-gate 10160Sstevel@tonic-gate /* 10170Sstevel@tonic-gate * bring the interface online. note that there is no optimal 10180Sstevel@tonic-gate * order here: it is considered bad taste (and in > solaris 7, 10190Sstevel@tonic-gate * likely illegal) to bring an interface up before it has an 10200Sstevel@tonic-gate * ip address. however, due to an apparent bug in sun fddi 10210Sstevel@tonic-gate * 5.0, fddi will not obtain a network routing entry unless 10220Sstevel@tonic-gate * the interface is brought up before it has an ip address. 10230Sstevel@tonic-gate * we take the lesser of the two evils; if fddi customers have 10240Sstevel@tonic-gate * problems, they can get a newer fddi distribution which 10250Sstevel@tonic-gate * fixes the problem. 10260Sstevel@tonic-gate */ 10270Sstevel@tonic-gate 10283431Scarlsonj sin = (struct sockaddr_in *)&lifr.lifr_addr; 10290Sstevel@tonic-gate sin->sin_family = AF_INET; 10300Sstevel@tonic-gate 10313431Scarlsonj (void) memset(&lif->lif_v6mask, 0xff, sizeof (lif->lif_v6mask)); 10320Sstevel@tonic-gate if (ack->opts[CD_SUBNETMASK] != NULL && 10333431Scarlsonj ack->opts[CD_SUBNETMASK]->len == sizeof (inaddr)) { 10340Sstevel@tonic-gate 10353431Scarlsonj (void) memcpy(&inaddr, ack->opts[CD_SUBNETMASK]->value, 10363431Scarlsonj sizeof (inaddr)); 10370Sstevel@tonic-gate 10380Sstevel@tonic-gate } else { 10390Sstevel@tonic-gate 10400Sstevel@tonic-gate if (ack->opts[CD_SUBNETMASK] != NULL && 10413431Scarlsonj ack->opts[CD_SUBNETMASK]->len != sizeof (inaddr)) { 10423431Scarlsonj dhcpmsg(MSG_WARNING, "configure_v4_lease: specified " 10433431Scarlsonj "subnet mask length is %d instead of %d, ignoring", 10440Sstevel@tonic-gate ack->opts[CD_SUBNETMASK]->len, sizeof (ipaddr_t)); 10453431Scarlsonj } else { 10463431Scarlsonj dhcpmsg(MSG_WARNING, "configure_v4_lease: no IP " 10473431Scarlsonj "netmask specified for %s, making best guess", 10483431Scarlsonj lif->lif_name); 10493431Scarlsonj } 10500Sstevel@tonic-gate 10510Sstevel@tonic-gate /* 10520Sstevel@tonic-gate * no legitimate IP subnet mask specified.. use best 10533431Scarlsonj * guess. recall that lif_addr is in network order, so 10540Sstevel@tonic-gate * imagine it's 0x11223344: then when it is read into 10550Sstevel@tonic-gate * a register on x86, it becomes 0x44332211, so we 10560Sstevel@tonic-gate * must ntohl() it to convert it to 0x11223344 in 10570Sstevel@tonic-gate * order to use the macros in <netinet/in.h>. 10580Sstevel@tonic-gate */ 10590Sstevel@tonic-gate 10603431Scarlsonj if (IN_CLASSA(addrhbo)) 10613431Scarlsonj inaddr.s_addr = htonl(IN_CLASSA_NET); 10623431Scarlsonj else if (IN_CLASSB(addrhbo)) 10633431Scarlsonj inaddr.s_addr = htonl(IN_CLASSB_NET); 1064*5577Ssangeeta else if (IN_CLASSC(addrhbo)) 10653431Scarlsonj inaddr.s_addr = htonl(IN_CLASSC_NET); 1066*5577Ssangeeta else { 1067*5577Ssangeeta /* 1068*5577Ssangeeta * Cant be Class D as that is multicast 1069*5577Ssangeeta * Must be Class E 1070*5577Ssangeeta */ 1071*5577Ssangeeta inaddr.s_addr = htonl(IN_CLASSE_NET); 1072*5577Ssangeeta } 10733431Scarlsonj } 10743431Scarlsonj lif->lif_v6mask._S6_un._S6_u32[3] = inaddr.s_addr; 10750Sstevel@tonic-gate 10763431Scarlsonj sin->sin_addr = inaddr; 10773431Scarlsonj dhcpmsg(MSG_INFO, "configure_v4_lease: setting IP netmask to %s on %s", 10783431Scarlsonj inet_ntoa(sin->sin_addr), lif->lif_name); 10793431Scarlsonj 10803431Scarlsonj if (ioctl(v4_sock_fd, SIOCSLIFNETMASK, &lifr) == -1) { 10813431Scarlsonj dhcpmsg(MSG_ERR, "configure_v4_lease: cannot set IP netmask " 10823431Scarlsonj "on %s", lif->lif_name); 10833431Scarlsonj return (B_FALSE); 10840Sstevel@tonic-gate } 10850Sstevel@tonic-gate 10863431Scarlsonj IN6_V4MAPPED_TO_INADDR(&lif->lif_v6addr, &sin->sin_addr); 10873431Scarlsonj dhcpmsg(MSG_INFO, "configure_v4_lease: setting IP address to %s on %s", 10883431Scarlsonj inet_ntoa(sin->sin_addr), lif->lif_name); 10890Sstevel@tonic-gate 10903431Scarlsonj if (ioctl(v4_sock_fd, SIOCSLIFADDR, &lifr) == -1) { 10913431Scarlsonj dhcpmsg(MSG_ERR, "configure_v4_lease: cannot set IP address " 10923431Scarlsonj "on %s", lif->lif_name); 10933431Scarlsonj return (B_FALSE); 10943431Scarlsonj } 10953431Scarlsonj 10963431Scarlsonj lif->lif_dad_wait = B_TRUE; 10973431Scarlsonj dsmp->dsm_lif_wait++; 10980Sstevel@tonic-gate 10990Sstevel@tonic-gate if (ack->opts[CD_BROADCASTADDR] != NULL && 11003431Scarlsonj ack->opts[CD_BROADCASTADDR]->len == sizeof (inaddr)) { 11010Sstevel@tonic-gate 11023431Scarlsonj (void) memcpy(&inaddr, ack->opts[CD_BROADCASTADDR]->value, 11033431Scarlsonj sizeof (inaddr)); 11040Sstevel@tonic-gate 11050Sstevel@tonic-gate } else { 11060Sstevel@tonic-gate 11070Sstevel@tonic-gate if (ack->opts[CD_BROADCASTADDR] != NULL && 11083431Scarlsonj ack->opts[CD_BROADCASTADDR]->len != sizeof (inaddr)) { 11093431Scarlsonj dhcpmsg(MSG_WARNING, "configure_v4_lease: specified " 11100Sstevel@tonic-gate "broadcast address length is %d instead of %d, " 11110Sstevel@tonic-gate "ignoring", ack->opts[CD_BROADCASTADDR]->len, 11123431Scarlsonj sizeof (inaddr)); 11133431Scarlsonj } else { 11143431Scarlsonj dhcpmsg(MSG_WARNING, "configure_v4_lease: no IP " 11153431Scarlsonj "broadcast specified for %s, making best guess", 11163431Scarlsonj lif->lif_name); 11173431Scarlsonj } 11180Sstevel@tonic-gate 11190Sstevel@tonic-gate /* 11200Sstevel@tonic-gate * no legitimate IP broadcast specified. compute it 11210Sstevel@tonic-gate * from the IP address and netmask. 11220Sstevel@tonic-gate */ 11230Sstevel@tonic-gate 11243431Scarlsonj IN6_V4MAPPED_TO_INADDR(&lif->lif_v6addr, &inaddr); 11253431Scarlsonj inaddr.s_addr |= ~lif->lif_v6mask._S6_un._S6_u32[3]; 11260Sstevel@tonic-gate } 11270Sstevel@tonic-gate 11280Sstevel@tonic-gate /* 11290Sstevel@tonic-gate * the kernel will set the broadcast address for us as part of 11300Sstevel@tonic-gate * bringing the interface up. since experience has shown that dhcp 11310Sstevel@tonic-gate * servers sometimes provide a bogus broadcast address, we let the 11320Sstevel@tonic-gate * kernel set it so that it's guaranteed to be correct. 11330Sstevel@tonic-gate * 11340Sstevel@tonic-gate * also, note any inconsistencies and save the broadcast address the 11350Sstevel@tonic-gate * kernel set so that we can watch for changes to it. 11360Sstevel@tonic-gate */ 11370Sstevel@tonic-gate 11383431Scarlsonj if (ioctl(v4_sock_fd, SIOCGLIFBRDADDR, &lifr) == -1) { 11393431Scarlsonj dhcpmsg(MSG_ERR, "configure_v4_lease: cannot get broadcast " 11403431Scarlsonj "address for %s", lif->lif_name); 11413431Scarlsonj return (B_FALSE); 11420Sstevel@tonic-gate } 11430Sstevel@tonic-gate 11443431Scarlsonj if (inaddr.s_addr != sin->sin_addr.s_addr) { 11453431Scarlsonj dhcpmsg(MSG_WARNING, "configure_v4_lease: incorrect broadcast " 11463431Scarlsonj "address %s specified for %s; ignoring", inet_ntoa(inaddr), 11473431Scarlsonj lif->lif_name); 11480Sstevel@tonic-gate } 11490Sstevel@tonic-gate 11503476Scarlsonj lif->lif_broadcast = sin->sin_addr.s_addr; 11513431Scarlsonj dhcpmsg(MSG_INFO, 11523431Scarlsonj "configure_v4_lease: using broadcast address %s on %s", 11533431Scarlsonj inet_ntoa(inaddr), lif->lif_name); 11543431Scarlsonj return (B_TRUE); 11552546Scarlsonj } 11562546Scarlsonj 11572546Scarlsonj /* 11583431Scarlsonj * save_server_id(): save off the new DHCPv6 Server ID 11592546Scarlsonj * 11603431Scarlsonj * input: dhcp_smach_t *: the state machine to use 11613431Scarlsonj * PKT_LIST *: the packet with the Reply message 11623431Scarlsonj * output: boolean_t: B_TRUE on success, B_FALSE on failure 11632546Scarlsonj */ 11642546Scarlsonj 11653431Scarlsonj boolean_t 11663431Scarlsonj save_server_id(dhcp_smach_t *dsmp, PKT_LIST *msg) 11672546Scarlsonj { 11683431Scarlsonj const dhcpv6_option_t *d6o; 11693431Scarlsonj uint_t olen; 11700Sstevel@tonic-gate 11713431Scarlsonj d6o = dhcpv6_pkt_option(msg, NULL, DHCPV6_OPT_SERVERID, &olen); 11723431Scarlsonj if (d6o == NULL) 11733431Scarlsonj return (B_FALSE); 11743431Scarlsonj olen -= sizeof (*d6o); 11753431Scarlsonj free(dsmp->dsm_serverid); 11763431Scarlsonj if ((dsmp->dsm_serverid = malloc(olen)) == NULL) { 11773431Scarlsonj return (B_FALSE); 11783431Scarlsonj } else { 11793431Scarlsonj dsmp->dsm_serveridlen = olen; 11803431Scarlsonj (void) memcpy(dsmp->dsm_serverid, d6o + 1, olen); 11813431Scarlsonj return (B_TRUE); 11820Sstevel@tonic-gate } 11830Sstevel@tonic-gate } 1184