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: 2182546Scarlsonj /* This is just a duplicate ack; silently ignore it */ 2193431Scarlsonj return (B_TRUE); 2200Sstevel@tonic-gate 2210Sstevel@tonic-gate case RENEWING: 2220Sstevel@tonic-gate case REBINDING: 2233431Scarlsonj 2243431Scarlsonj if (dsmp->dsm_isv6) { 2253431Scarlsonj if ((v6b = configure_v6_leases(dsmp)) != v6Done) 2263431Scarlsonj return (v6b == v6Resent); 2273431Scarlsonj } else { 2283431Scarlsonj if (!configure_v4_timers(dsmp)) 2293431Scarlsonj return (B_FALSE); 2303431Scarlsonj if (!clear_lif_deprecated(dsmp->dsm_lif)) 2313431Scarlsonj return (B_FALSE); 2323431Scarlsonj } 2330Sstevel@tonic-gate 2340Sstevel@tonic-gate /* 2353431Scarlsonj * If some or all of the leases were torn down by the server, 2363431Scarlsonj * then handle that as an expiry. When the script is done 2373431Scarlsonj * running for the LOSS6 event, we'll end up back here. 2380Sstevel@tonic-gate */ 2393431Scarlsonj if ((lif = find_expired_lif(dsmp)) != NULL) { 2403431Scarlsonj hold_lif(lif); 2413431Scarlsonj dhcp_expire(NULL, lif); 2423431Scarlsonj while ((lif = find_expired_lif(dsmp)) != NULL) { 2433431Scarlsonj dlp = lif->lif_lease; 2443431Scarlsonj unplumb_lif(lif); 2453431Scarlsonj if (dlp->dl_nlifs == 0) 2463431Scarlsonj remove_lease(dlp); 2473431Scarlsonj } 2483431Scarlsonj if (dsmp->dsm_leases == NULL) 2493431Scarlsonj return (B_FALSE); 2503431Scarlsonj } 2510Sstevel@tonic-gate 2523431Scarlsonj if (oldstate == REBINDING && dsmp->dsm_isv6 && 2533431Scarlsonj !save_server_id(dsmp, ack)) { 2543431Scarlsonj return (B_FALSE); 2550Sstevel@tonic-gate } 2560Sstevel@tonic-gate 2573431Scarlsonj /* 2583431Scarlsonj * Handle Renew/Rebind that fails to address one of our leases. 2593431Scarlsonj * (Should just never happen, but RFC 3315 section 18.1.8 2603431Scarlsonj * requires it, and TAHI tests for it.) 2613431Scarlsonj */ 2623431Scarlsonj for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) { 2633431Scarlsonj if (dlp->dl_stale && dlp->dl_nlifs > 0) 2643431Scarlsonj break; 2653431Scarlsonj } 2663431Scarlsonj if (dlp != NULL) { 2673431Scarlsonj dhcpmsg(MSG_DEBUG, "dhcp_bound: lease not updated; " 2683431Scarlsonj "allow retransmit"); 2693431Scarlsonj return (B_TRUE); 2703431Scarlsonj } 2710Sstevel@tonic-gate 2723431Scarlsonj if (!set_smach_state(dsmp, BOUND)) 2733431Scarlsonj return (B_FALSE); 2743431Scarlsonj 2753431Scarlsonj (void) script_start(dsmp, dsmp->dsm_isv6 ? EVENT_EXTEND6 : 2763431Scarlsonj EVENT_EXTEND, bound_event_cb, NULL, NULL); 2773431Scarlsonj 2783431Scarlsonj dsmp->dsm_curstart_monosec = dsmp->dsm_newstart_monosec; 2793431Scarlsonj 2803431Scarlsonj /* Stop sending requests now */ 2813431Scarlsonj stop_pkt_retransmission(dsmp); 2820Sstevel@tonic-gate break; 2830Sstevel@tonic-gate 2840Sstevel@tonic-gate case INFORM_SENT: 2850Sstevel@tonic-gate 2863431Scarlsonj if (dsmp->dsm_isv6 && !save_server_id(dsmp, ack)) { 2873431Scarlsonj return (B_FALSE); 2883431Scarlsonj } 2893431Scarlsonj 2903431Scarlsonj (void) bound_event_cb(dsmp, NULL); 2913431Scarlsonj if (!set_smach_state(dsmp, INFORMATION)) 2923431Scarlsonj return (B_FALSE); 2933431Scarlsonj 2943431Scarlsonj /* Stop sending requests now */ 2953431Scarlsonj stop_pkt_retransmission(dsmp); 2960Sstevel@tonic-gate break; 2970Sstevel@tonic-gate 2980Sstevel@tonic-gate default: 2990Sstevel@tonic-gate /* something is really bizarre... */ 3003431Scarlsonj dhcpmsg(MSG_DEBUG, 3013431Scarlsonj "dhcp_bound: called in unexpected state: %s", 3023431Scarlsonj dhcp_state_to_string(dsmp->dsm_state)); 3033431Scarlsonj return (B_FALSE); 3040Sstevel@tonic-gate } 3050Sstevel@tonic-gate 3060Sstevel@tonic-gate /* 3070Sstevel@tonic-gate * remove any stale hostconf file that might be lying around for 3083431Scarlsonj * this state machine. (in general, it's harmless, since we'll write a 3090Sstevel@tonic-gate * fresh one when we exit anyway, but just to reduce confusion..) 3100Sstevel@tonic-gate */ 3110Sstevel@tonic-gate 3123431Scarlsonj (void) remove_hostconf(dsmp->dsm_name, dsmp->dsm_isv6); 3133431Scarlsonj return (B_TRUE); 3140Sstevel@tonic-gate } 3150Sstevel@tonic-gate 3160Sstevel@tonic-gate /* 3172546Scarlsonj * dhcp_bound_complete(): complete interface configuration after DAD 3182546Scarlsonj * 3193431Scarlsonj * input: dhcp_smach_t *: the state machine now ready 3202546Scarlsonj * output: none 3212546Scarlsonj */ 3222546Scarlsonj 3232546Scarlsonj void 3243431Scarlsonj dhcp_bound_complete(dhcp_smach_t *dsmp) 3252546Scarlsonj { 3263431Scarlsonj PKT_LIST *ack; 3273431Scarlsonj DHCP_OPT *router_list; 3283431Scarlsonj int i; 3293431Scarlsonj DHCPSTATE oldstate; 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); 3383431Scarlsonj dhcpmsg(MSG_DEBUG, "configure_bound: 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 /* 3473431Scarlsonj * add each provided router; we'll clean them up when the 3483431Scarlsonj * state machine goes away or when our lease expires. 3493431Scarlsonj */ 3503431Scarlsonj 3513431Scarlsonj ack = dsmp->dsm_ack; 3523431Scarlsonj router_list = ack->opts[CD_ROUTER]; 3533431Scarlsonj if (router_list && (router_list->len % sizeof (ipaddr_t)) == 0) { 3543431Scarlsonj 3553431Scarlsonj dsmp->dsm_nrouters = router_list->len / sizeof (ipaddr_t); 3563431Scarlsonj dsmp->dsm_routers = malloc(router_list->len); 3573431Scarlsonj if (dsmp->dsm_routers == NULL) { 3583431Scarlsonj dhcpmsg(MSG_ERR, "configure_bound: cannot allocate " 3593431Scarlsonj "default router list, ignoring default routers"); 3603431Scarlsonj dsmp->dsm_nrouters = 0; 3613431Scarlsonj } 3623431Scarlsonj 3633431Scarlsonj for (i = 0; i < dsmp->dsm_nrouters; i++) { 3643431Scarlsonj 3653431Scarlsonj (void) memcpy(&dsmp->dsm_routers[i].s_addr, 3663431Scarlsonj router_list->value + (i * sizeof (ipaddr_t)), 3673431Scarlsonj sizeof (ipaddr_t)); 3683431Scarlsonj 3693431Scarlsonj if (!add_default_route(dsmp->dsm_name, 3703431Scarlsonj &dsmp->dsm_routers[i])) { 3713431Scarlsonj dhcpmsg(MSG_ERR, "configure_bound: cannot add " 3723431Scarlsonj "default router %s on %s", inet_ntoa( 3733431Scarlsonj dsmp->dsm_routers[i]), dsmp->dsm_name); 3743431Scarlsonj dsmp->dsm_routers[i].s_addr = htonl(INADDR_ANY); 3753431Scarlsonj continue; 3763431Scarlsonj } 3773431Scarlsonj 3783431Scarlsonj dhcpmsg(MSG_INFO, "added default router %s on %s", 3793431Scarlsonj inet_ntoa(dsmp->dsm_routers[i]), dsmp->dsm_name); 3803431Scarlsonj } 3813431Scarlsonj } 3823431Scarlsonj 3833431Scarlsonj oldstate = dsmp->dsm_state; 3843431Scarlsonj if (!set_smach_state(dsmp, BOUND)) { 3853431Scarlsonj dhcpmsg(MSG_ERR, 3863431Scarlsonj "configure_bound: cannot set bound state on %s", 3873431Scarlsonj dsmp->dsm_name); 3883431Scarlsonj return; 3893431Scarlsonj } 3903431Scarlsonj 3913431Scarlsonj dhcpmsg(MSG_DEBUG, "configure_bound: bound %s", dsmp->dsm_name); 3923431Scarlsonj 3933431Scarlsonj /* 3943431Scarlsonj * We're now committed to this binding, so if it came from BOOTP, set 3953431Scarlsonj * the flag. 3963431Scarlsonj */ 3973431Scarlsonj 3983431Scarlsonj if (ack->opts[CD_DHCP_TYPE] == NULL) 3993431Scarlsonj dsmp->dsm_dflags |= DHCP_IF_BOOTP; 4003431Scarlsonj 4013431Scarlsonj /* 4023431Scarlsonj * If the previous state was ADOPTING, event loop has not been started 4032612Scarlsonj * at this time; so don't run the EVENT_BOUND script. 4042612Scarlsonj */ 4053431Scarlsonj if (oldstate != ADOPTING) { 4063431Scarlsonj (void) script_start(dsmp, EVENT_BOUND, bound_event_cb, NULL, 4072612Scarlsonj NULL); 4082612Scarlsonj } 4092546Scarlsonj 4103431Scarlsonj dsmp->dsm_curstart_monosec = dsmp->dsm_newstart_monosec; 4112546Scarlsonj } 4122546Scarlsonj 4132546Scarlsonj /* 4143431Scarlsonj * fuzzify(): adds some "fuzz" to a t1/t2 time, in accordance with RFC2131. 4153431Scarlsonj * We use up to plus or minus 2% jitter in the time. This is a 4163431Scarlsonj * small value, but the timers involved are typically long. A 4173431Scarlsonj * common T1 value is one day, and the fuzz is up to 28.8 minutes; 4183431Scarlsonj * plenty of time to make sure that individual clients don't renew 4193431Scarlsonj * all at the same time. 4200Sstevel@tonic-gate * 4213431Scarlsonj * input: uint32_t: the number of seconds until lease expiration 4223431Scarlsonj * double: the approximate percentage of that time to return 4233431Scarlsonj * output: double: a number approximating (sec * pct) 4243431Scarlsonj */ 4253431Scarlsonj 4263431Scarlsonj static double 4273431Scarlsonj fuzzify(uint32_t sec, double pct) 4283431Scarlsonj { 4293431Scarlsonj return (sec * (pct + (drand48() - 0.5) / 25.0)); 4303431Scarlsonj } 4313431Scarlsonj 4323431Scarlsonj /* 4333431Scarlsonj * get_pkt_times(): pulls the lease times out of a v4 DHCP packet and stores 4343431Scarlsonj * them as host byte-order relative times in the passed in 4353431Scarlsonj * parameters. 4363431Scarlsonj * 4373431Scarlsonj * input: PKT_LIST *: the packet to pull the packet times from 4383431Scarlsonj * lease_t *: where to store the relative lease time in hbo 4393431Scarlsonj * lease_t *: where to store the relative t1 time in hbo 4403431Scarlsonj * lease_t *: where to store the relative t2 time in hbo 4413431Scarlsonj * output: void 4420Sstevel@tonic-gate */ 4430Sstevel@tonic-gate 4443431Scarlsonj static void 4453431Scarlsonj get_pkt_times(PKT_LIST *ack, lease_t *lease, lease_t *t1, lease_t *t2) 4460Sstevel@tonic-gate { 4473431Scarlsonj *lease = DHCP_PERM; 4483431Scarlsonj *t1 = DHCP_PERM; 4493431Scarlsonj *t2 = DHCP_PERM; 4500Sstevel@tonic-gate 4513431Scarlsonj if (ack->opts[CD_DHCP_TYPE] == NULL) { 4523431Scarlsonj dhcpmsg(MSG_VERBOSE, 4533431Scarlsonj "get_pkt_times: BOOTP response; infinite lease"); 4543431Scarlsonj return; 4553431Scarlsonj } 4563431Scarlsonj if (ack->opts[CD_LEASE_TIME] == NULL) { 4573431Scarlsonj dhcpmsg(MSG_VERBOSE, 4583431Scarlsonj "get_pkt_times: no lease option provided"); 4593431Scarlsonj return; 4603431Scarlsonj } 4613431Scarlsonj if (ack->opts[CD_LEASE_TIME]->len != sizeof (lease_t)) { 4623431Scarlsonj dhcpmsg(MSG_VERBOSE, "get_pkt_times: invalid lease option"); 4633431Scarlsonj } 4643431Scarlsonj 4653431Scarlsonj (void) memcpy(lease, ack->opts[CD_LEASE_TIME]->value, sizeof (lease_t)); 4663431Scarlsonj *lease = ntohl(*lease); 4673431Scarlsonj 4683431Scarlsonj if (*lease == DHCP_PERM) { 4693431Scarlsonj dhcpmsg(MSG_VERBOSE, "get_pkt_times: infinite lease granted"); 4703431Scarlsonj return; 4713431Scarlsonj } 4723431Scarlsonj 4733431Scarlsonj if (ack->opts[CD_T1_TIME] != NULL && 4743431Scarlsonj ack->opts[CD_T1_TIME]->len == sizeof (lease_t)) { 4753431Scarlsonj (void) memcpy(t1, ack->opts[CD_T1_TIME]->value, sizeof (*t1)); 4763431Scarlsonj *t1 = ntohl(*t1); 4773431Scarlsonj } 4783431Scarlsonj 4793431Scarlsonj if (ack->opts[CD_T2_TIME] != NULL && 4803431Scarlsonj ack->opts[CD_T2_TIME]->len == sizeof (lease_t)) { 4813431Scarlsonj (void) memcpy(t2, ack->opts[CD_T2_TIME]->value, sizeof (*t2)); 4823431Scarlsonj *t2 = ntohl(*t2); 4830Sstevel@tonic-gate } 4840Sstevel@tonic-gate 4853431Scarlsonj if ((*t1 == DHCP_PERM) || (*t1 >= *lease)) 4863431Scarlsonj *t1 = (lease_t)fuzzify(*lease, DHCP_T1_FACT); 4873431Scarlsonj 4883431Scarlsonj if ((*t2 == DHCP_PERM) || (*t2 > *lease) || (*t2 <= *t1)) 4893431Scarlsonj *t2 = (lease_t)fuzzify(*lease, DHCP_T2_FACT); 4903431Scarlsonj 4913431Scarlsonj dhcpmsg(MSG_VERBOSE, "get_pkt_times: lease %u t1 %u t2 %u", 4923431Scarlsonj *lease, *t1, *t2); 4933431Scarlsonj } 4943431Scarlsonj 4953431Scarlsonj /* 4963431Scarlsonj * configure_v4_timers(): configures the lease timers on a v4 state machine 4973431Scarlsonj * 4983431Scarlsonj * input: dhcp_smach_t *: the state machine to configure 4993431Scarlsonj * output: boolean_t: B_TRUE on success, B_FALSE on failure 5003431Scarlsonj */ 5013431Scarlsonj 5023431Scarlsonj static boolean_t 5033431Scarlsonj configure_v4_timers(dhcp_smach_t *dsmp) 5043431Scarlsonj { 5053431Scarlsonj PKT_LIST *ack = dsmp->dsm_ack; 5063431Scarlsonj lease_t lease, t1, t2; 5073431Scarlsonj dhcp_lease_t *dlp; 5083431Scarlsonj dhcp_lif_t *lif; 5093431Scarlsonj 5103431Scarlsonj /* v4 has just one lease per state machine, and one LIF */ 5113431Scarlsonj dlp = dsmp->dsm_leases; 5123431Scarlsonj lif = dlp->dl_lifs; 5133431Scarlsonj 5143431Scarlsonj /* 5153431Scarlsonj * If it's DHCP, but there's no valid lease time, then complain, 5163431Scarlsonj * decline the lease and return error. 5173431Scarlsonj */ 5183431Scarlsonj if (ack->opts[CD_DHCP_TYPE] != NULL && 5193431Scarlsonj (ack->opts[CD_LEASE_TIME] == NULL || 5203431Scarlsonj ack->opts[CD_LEASE_TIME]->len != sizeof (lease_t))) { 5213431Scarlsonj lif_mark_decline(lif, "Missing or corrupted lease time"); 5223431Scarlsonj send_declines(dsmp); 5233431Scarlsonj dhcpmsg(MSG_WARNING, "configure_v4_timers: %s lease time in " 5243431Scarlsonj "ACK on %s", ack->opts[CD_LEASE_TIME] == NULL ? "missing" : 5253431Scarlsonj "corrupt", dsmp->dsm_name); 5263431Scarlsonj return (B_FALSE); 5273431Scarlsonj } 5283431Scarlsonj 5293431Scarlsonj /* Stop the T1 and T2 timers */ 5303431Scarlsonj cancel_lease_timers(dlp); 5313431Scarlsonj 5323431Scarlsonj /* Stop the LEASE timer */ 5333431Scarlsonj cancel_lif_timers(lif); 5340Sstevel@tonic-gate 5350Sstevel@tonic-gate /* 5360Sstevel@tonic-gate * type has already been verified as ACK. if type is not set, 5370Sstevel@tonic-gate * then we got a BOOTP packet. we now fetch the t1, t2, and 5380Sstevel@tonic-gate * lease options out of the packet into variables. they are 5390Sstevel@tonic-gate * returned as relative host-byte-ordered times. 5400Sstevel@tonic-gate */ 5410Sstevel@tonic-gate 5423431Scarlsonj get_pkt_times(ack, &lease, &t1, &t2); 5433431Scarlsonj 5443431Scarlsonj /* 5453431Scarlsonj * if the current lease is mysteriously close to the new 5463431Scarlsonj * lease, warn the user. unless there's less than a minute 5473431Scarlsonj * left, round to the closest minute. 5483431Scarlsonj */ 5493431Scarlsonj 5503431Scarlsonj if (lif->lif_expire.dt_start != 0 && 5513431Scarlsonj abs((dsmp->dsm_newstart_monosec + lease) - 5523431Scarlsonj (dsmp->dsm_curstart_monosec + lif->lif_expire.dt_start)) < 5533431Scarlsonj DHCP_LEASE_EPS) { 5543431Scarlsonj const char *noext = "configure_v4_timers: lease renewed but " 5553431Scarlsonj "time not extended"; 5563431Scarlsonj int msg_level; 5573431Scarlsonj uint_t minleft; 5580Sstevel@tonic-gate 5593431Scarlsonj if (lif->lif_expire.dt_start < DHCP_LEASE_ERROR_THRESH) 5603431Scarlsonj msg_level = MSG_ERROR; 5613431Scarlsonj else 5623431Scarlsonj msg_level = MSG_VERBOSE; 5633431Scarlsonj 5643431Scarlsonj minleft = (lif->lif_expire.dt_start + 30) / 60; 5650Sstevel@tonic-gate 5663431Scarlsonj if (lif->lif_expire.dt_start < 60) { 5673431Scarlsonj dhcpmsg(msg_level, "%s; expires in %d seconds", 5683431Scarlsonj noext, lif->lif_expire.dt_start); 5693431Scarlsonj } else if (minleft == 1) { 5703431Scarlsonj dhcpmsg(msg_level, "%s; expires in 1 minute", noext); 5713431Scarlsonj } else if (minleft > 120) { 5723431Scarlsonj dhcpmsg(msg_level, "%s; expires in %d hours", 5733431Scarlsonj noext, (minleft + 30) / 60); 5743431Scarlsonj } else { 5753431Scarlsonj dhcpmsg(msg_level, "%s; expires in %d minutes", 5763431Scarlsonj noext, minleft); 5773431Scarlsonj } 5780Sstevel@tonic-gate } 5790Sstevel@tonic-gate 5803431Scarlsonj init_timer(&dlp->dl_t1, t1); 5813431Scarlsonj init_timer(&dlp->dl_t2, t2); 5823431Scarlsonj init_timer(&lif->lif_expire, lease); 5833431Scarlsonj 5843431Scarlsonj if (lease == DHCP_PERM) { 5853431Scarlsonj dhcpmsg(MSG_INFO, 5863431Scarlsonj "configure_v4_timers: %s acquired permanent lease", 5873431Scarlsonj dsmp->dsm_name); 5883431Scarlsonj return (B_TRUE); 5893431Scarlsonj } 5900Sstevel@tonic-gate 5913431Scarlsonj dhcpmsg(MSG_INFO, "configure_v4_timers: %s acquired lease, expires %s", 5923431Scarlsonj dsmp->dsm_name, 5933431Scarlsonj monosec_to_string(dsmp->dsm_newstart_monosec + lease)); 5940Sstevel@tonic-gate 5953431Scarlsonj dhcpmsg(MSG_INFO, "configure_v4_timers: %s begins renewal at %s", 5963431Scarlsonj dsmp->dsm_name, monosec_to_string(dsmp->dsm_newstart_monosec + 5973431Scarlsonj dlp->dl_t1.dt_start)); 5983431Scarlsonj 5993431Scarlsonj dhcpmsg(MSG_INFO, "configure_v4_timers: %s begins rebinding at %s", 6003431Scarlsonj dsmp->dsm_name, monosec_to_string(dsmp->dsm_newstart_monosec + 6013431Scarlsonj dlp->dl_t2.dt_start)); 6020Sstevel@tonic-gate 6030Sstevel@tonic-gate /* 6040Sstevel@tonic-gate * according to RFC2131, there is no minimum lease time, but don't 6050Sstevel@tonic-gate * set up renew/rebind timers if lease is shorter than DHCP_REBIND_MIN. 6060Sstevel@tonic-gate */ 6070Sstevel@tonic-gate 6083431Scarlsonj if (!schedule_lif_timer(lif, &lif->lif_expire, dhcp_expire)) 6090Sstevel@tonic-gate goto failure; 6100Sstevel@tonic-gate 6110Sstevel@tonic-gate if (lease < DHCP_REBIND_MIN) { 6123431Scarlsonj dhcpmsg(MSG_WARNING, "configure_v4_timers: lease on %s is for " 6133431Scarlsonj "less than %d seconds!", dsmp->dsm_name, DHCP_REBIND_MIN); 6143431Scarlsonj return (B_TRUE); 6150Sstevel@tonic-gate } 6160Sstevel@tonic-gate 6173431Scarlsonj if (!schedule_lease_timer(dlp, &dlp->dl_t1, dhcp_renew)) 6180Sstevel@tonic-gate goto failure; 6190Sstevel@tonic-gate 6203431Scarlsonj if (!schedule_lease_timer(dlp, &dlp->dl_t2, dhcp_rebind)) 6210Sstevel@tonic-gate goto failure; 6220Sstevel@tonic-gate 6233431Scarlsonj return (B_TRUE); 6240Sstevel@tonic-gate 6250Sstevel@tonic-gate failure: 6263431Scarlsonj cancel_lease_timers(dlp); 6273431Scarlsonj cancel_lif_timers(lif); 6283431Scarlsonj dhcpmsg(MSG_WARNING, 6293431Scarlsonj "configure_v4_timers: cannot schedule lease timers"); 6303431Scarlsonj return (B_FALSE); 6310Sstevel@tonic-gate } 6320Sstevel@tonic-gate 6330Sstevel@tonic-gate /* 6343431Scarlsonj * configure_v6_leases(): configures the IPv6 leases on a state machine from 6353431Scarlsonj * the current DHCPv6 ACK. We need to scan the ACK, 6363431Scarlsonj * create a lease for each IA_NA, and a new LIF for each 6373431Scarlsonj * IAADDR. 6380Sstevel@tonic-gate * 6393431Scarlsonj * input: dhcp_smach_t *: the machine to configure (with a valid dsm_ack) 6403431Scarlsonj * output: enum v6_bind_result: restart, resend, or done 6410Sstevel@tonic-gate */ 6420Sstevel@tonic-gate 6433431Scarlsonj static enum v6_bind_result 6443431Scarlsonj configure_v6_leases(dhcp_smach_t *dsmp) 6450Sstevel@tonic-gate { 6463431Scarlsonj const dhcpv6_option_t *d6o, *d6so, *d6sso; 6473431Scarlsonj const char *optbase, *estr, *msg; 6483431Scarlsonj uint_t olen, solen, ssolen, msglen; 6493431Scarlsonj dhcpv6_ia_na_t d6in; 6503431Scarlsonj dhcpv6_iaaddr_t d6ia; 6513431Scarlsonj dhcp_lease_t *dlp; 6523431Scarlsonj uint32_t shortest; 6533431Scarlsonj dhcp_lif_t *lif; 6543431Scarlsonj uint_t nlifs; 6553431Scarlsonj boolean_t got_iana = B_FALSE; 6563431Scarlsonj uint_t scode; 6573431Scarlsonj 6583431Scarlsonj for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) 6593431Scarlsonj dlp->dl_stale = B_TRUE; 6603431Scarlsonj 6613431Scarlsonj d6o = NULL; 6623431Scarlsonj while ((d6o = dhcpv6_pkt_option(dsmp->dsm_ack, d6o, DHCPV6_OPT_IA_NA, 6633431Scarlsonj &olen)) != NULL) { 6643431Scarlsonj if (olen < sizeof (d6in)) { 6653431Scarlsonj dhcpmsg(MSG_WARNING, 6663431Scarlsonj "configure_v6_leases: garbled IA_NA"); 6673431Scarlsonj continue; 6683431Scarlsonj } 6693431Scarlsonj 6703431Scarlsonj /* 6713431Scarlsonj * Check the IAID. It should be for our controlling LIF. If a 6723431Scarlsonj * single state machine needs to use multiple IAIDs, then this 6733431Scarlsonj * will need to change. 6743431Scarlsonj */ 6753431Scarlsonj (void) memcpy(&d6in, d6o, sizeof (d6in)); 6763431Scarlsonj d6in.d6in_iaid = ntohl(d6in.d6in_iaid); 6773431Scarlsonj if (d6in.d6in_iaid != dsmp->dsm_lif->lif_iaid) { 6783431Scarlsonj dhcpmsg(MSG_WARNING, "configure_v6_leases: ignored " 6793431Scarlsonj "IA_NA for IAID %x (not %x)", d6in.d6in_iaid, 6803431Scarlsonj dsmp->dsm_lif->lif_iaid); 6813431Scarlsonj continue; 6823431Scarlsonj } 6833431Scarlsonj 6843431Scarlsonj /* 6853431Scarlsonj * See notes below; there's only one IA_NA and a single IAID 6863431Scarlsonj * for now. 6873431Scarlsonj */ 6883431Scarlsonj if ((dlp = dsmp->dsm_leases) != NULL) 6893431Scarlsonj dlp->dl_stale = B_FALSE; 6903431Scarlsonj 6913431Scarlsonj /* 6923431Scarlsonj * Note that some bug-ridden servers will try to give us 6933431Scarlsonj * multiple IA_NA options for a single IAID. We ignore 6943431Scarlsonj * duplicates. 6953431Scarlsonj */ 6963431Scarlsonj if (got_iana) { 6973431Scarlsonj dhcpmsg(MSG_WARNING, "configure_v6_leases: unexpected " 6983431Scarlsonj "extra IA_NA ignored"); 6993431Scarlsonj continue; 7003431Scarlsonj } 7013431Scarlsonj 7023431Scarlsonj d6in.d6in_t1 = ntohl(d6in.d6in_t1); 7033431Scarlsonj d6in.d6in_t2 = ntohl(d6in.d6in_t2); 7043431Scarlsonj 7053431Scarlsonj /* RFC 3315 required check for invalid T1/T2 combinations */ 7063431Scarlsonj if (d6in.d6in_t1 > d6in.d6in_t2 && d6in.d6in_t2 != 0) { 7073431Scarlsonj dhcpmsg(MSG_WARNING, "configure_v6_leases: ignored " 7083431Scarlsonj "IA_NA with invalid T1 %u > T2 %u", d6in.d6in_t1, 7093431Scarlsonj d6in.d6in_t2); 7103431Scarlsonj continue; 7113431Scarlsonj } 7123431Scarlsonj 7133431Scarlsonj /* 7143431Scarlsonj * There may be a status code here. Process if present. 7153431Scarlsonj */ 7163431Scarlsonj optbase = (const char *)d6o + sizeof (d6in); 7173431Scarlsonj olen -= sizeof (d6in); 7183431Scarlsonj d6so = dhcpv6_find_option(optbase, olen, NULL, 7193431Scarlsonj DHCPV6_OPT_STATUS_CODE, &solen); 7203431Scarlsonj scode = dhcpv6_status_code(d6so, solen, &estr, &msg, &msglen); 7213431Scarlsonj if (scode != DHCPV6_STAT_SUCCESS) { 7223431Scarlsonj dhcpmsg(MSG_WARNING, 7233431Scarlsonj "configure_v6_leases: IA_NA: %s: %.*s", 7243431Scarlsonj estr, msglen, msg); 7253431Scarlsonj } 7263431Scarlsonj print_server_msg(dsmp, msg, msglen); 7273431Scarlsonj 7283431Scarlsonj /* 7293431Scarlsonj * Other errors are possible here. According to RFC 3315 7303431Scarlsonj * section 18.1.8, we ignore the entire IA if it gives the "no 7313431Scarlsonj * addresses" status code. We may try another server if we 7323431Scarlsonj * like -- we instead opt to allow the addresses to expire and 7333431Scarlsonj * then try a new server. 7343431Scarlsonj * 7353431Scarlsonj * If the status code is "no binding," then we must go back and 7363431Scarlsonj * redo the Request. Surprisingly, it doesn't matter if it's 7373431Scarlsonj * any other code. 7383431Scarlsonj */ 7393431Scarlsonj if (scode == DHCPV6_STAT_NOADDRS) { 7403431Scarlsonj dhcpmsg(MSG_DEBUG, "configure_v6_leases: ignoring " 7413431Scarlsonj "no-addrs status in IA_NA"); 7423431Scarlsonj continue; 7433431Scarlsonj } 7443431Scarlsonj 7453431Scarlsonj if (scode == DHCPV6_STAT_NOBINDING) { 7463431Scarlsonj send_v6_request(dsmp); 7473431Scarlsonj return (v6Resent); 7483431Scarlsonj } 7493431Scarlsonj 7503431Scarlsonj /* 7513431Scarlsonj * Find or create the lease structure. This part is simple, 7523431Scarlsonj * because we support only IA_NA and a single IAID. This means 7533431Scarlsonj * there's only one lease structure. The design supports 7543431Scarlsonj * multiple lease structures so that IA_TA and IA_PD can be 7553431Scarlsonj * added later. 7563431Scarlsonj */ 7573431Scarlsonj if ((dlp = dsmp->dsm_leases) == NULL && 7583431Scarlsonj (dlp = insert_lease(dsmp)) == NULL) { 7593431Scarlsonj dhcpmsg(MSG_ERROR, "configure_v6_leases: unable to " 7603431Scarlsonj "allocate memory for lease"); 7613431Scarlsonj return (v6Restart); 7623431Scarlsonj } 7633431Scarlsonj 7643431Scarlsonj /* 7653431Scarlsonj * Iterate over the IAADDR options contained within this IA_NA. 7663431Scarlsonj */ 7673431Scarlsonj shortest = DHCPV6_INFTIME; 7683431Scarlsonj d6so = NULL; 7693431Scarlsonj while ((d6so = dhcpv6_find_option(optbase, olen, d6so, 7703431Scarlsonj DHCPV6_OPT_IAADDR, &solen)) != NULL) { 7713431Scarlsonj if (solen < sizeof (d6ia)) { 7723431Scarlsonj dhcpmsg(MSG_WARNING, 7733431Scarlsonj "configure_v6_leases: garbled IAADDR"); 7743431Scarlsonj continue; 7753431Scarlsonj } 7763431Scarlsonj (void) memcpy(&d6ia, d6so, sizeof (d6ia)); 7773431Scarlsonj 7783431Scarlsonj d6ia.d6ia_preflife = ntohl(d6ia.d6ia_preflife); 7793431Scarlsonj d6ia.d6ia_vallife = ntohl(d6ia.d6ia_vallife); 7803431Scarlsonj 7813431Scarlsonj /* RFC 3315 required validity check */ 7823431Scarlsonj if (d6ia.d6ia_preflife > d6ia.d6ia_vallife) { 7833431Scarlsonj dhcpmsg(MSG_WARNING, 7843431Scarlsonj "configure_v6_leases: ignored IAADDR with " 7853431Scarlsonj "preferred lifetime %u > valid %u", 7863431Scarlsonj d6ia.d6ia_preflife, d6ia.d6ia_vallife); 7873431Scarlsonj continue; 7883431Scarlsonj } 7893431Scarlsonj 7903431Scarlsonj /* 7913431Scarlsonj * RFC 3315 allows a status code to be buried inside 7923431Scarlsonj * the IAADDR option. Look for it, and process if 7933431Scarlsonj * present. Process in a manner similar to that for 7943431Scarlsonj * the IA itself; TAHI checks for this. Real servers 7953431Scarlsonj * likely won't do this. 7963431Scarlsonj */ 7973431Scarlsonj d6sso = dhcpv6_find_option((const char *)d6so + 7983431Scarlsonj sizeof (d6ia), solen - sizeof (d6ia), NULL, 7993431Scarlsonj DHCPV6_OPT_STATUS_CODE, &ssolen); 8003431Scarlsonj scode = dhcpv6_status_code(d6sso, ssolen, &estr, &msg, 8013431Scarlsonj &msglen); 8023431Scarlsonj print_server_msg(dsmp, msg, msglen); 8033431Scarlsonj if (scode == DHCPV6_STAT_NOADDRS) { 8043431Scarlsonj dhcpmsg(MSG_DEBUG, "configure_v6_leases: " 8053431Scarlsonj "ignoring no-addrs status in IAADDR"); 8063431Scarlsonj continue; 8073431Scarlsonj } 8083431Scarlsonj if (scode == DHCPV6_STAT_NOBINDING) { 8093431Scarlsonj send_v6_request(dsmp); 8103431Scarlsonj return (v6Resent); 8113431Scarlsonj } 8123431Scarlsonj if (scode != DHCPV6_STAT_SUCCESS) { 8133431Scarlsonj dhcpmsg(MSG_WARNING, 8143431Scarlsonj "configure_v6_leases: IAADDR: %s", estr); 8153431Scarlsonj } 8163431Scarlsonj 8173431Scarlsonj /* 8183431Scarlsonj * Locate the existing LIF within the lease associated 8193431Scarlsonj * with this address, if any. 8203431Scarlsonj */ 8213431Scarlsonj lif = dlp->dl_lifs; 8223431Scarlsonj for (nlifs = dlp->dl_nlifs; nlifs > 0; 8233431Scarlsonj nlifs--, lif = lif->lif_next) { 8243431Scarlsonj if (IN6_ARE_ADDR_EQUAL(&d6ia.d6ia_addr, 8253431Scarlsonj &lif->lif_v6addr)) 8263431Scarlsonj break; 8273431Scarlsonj } 8283431Scarlsonj 8293431Scarlsonj /* 8303431Scarlsonj * If the server has set the lifetime to zero, then 8313431Scarlsonj * delete the LIF. Otherwise, set the new LIF expiry 8323431Scarlsonj * time, adding the LIF if necessary. 8333431Scarlsonj */ 8343431Scarlsonj if (d6ia.d6ia_vallife == 0) { 8353431Scarlsonj /* If it was found, then it's expired */ 8363431Scarlsonj if (nlifs != 0) { 8373431Scarlsonj dhcpmsg(MSG_DEBUG, 8383431Scarlsonj "configure_v6_leases: lif %s has " 8393431Scarlsonj "expired", lif->lif_name); 8403431Scarlsonj lif->lif_expired = B_TRUE; 8413431Scarlsonj } 8423431Scarlsonj continue; 8433431Scarlsonj } 8443431Scarlsonj 8453431Scarlsonj /* If it wasn't found, then create it now. */ 8463431Scarlsonj if (nlifs == 0) { 8473431Scarlsonj lif = plumb_lif(dsmp->dsm_lif->lif_pif, 8483431Scarlsonj &d6ia.d6ia_addr); 8493431Scarlsonj if (lif == NULL) 8503431Scarlsonj continue; 8513431Scarlsonj if (++dlp->dl_nlifs == 1) { 8523431Scarlsonj dlp->dl_lifs = lif; 8533431Scarlsonj } else { 8543431Scarlsonj remque(lif); 8553431Scarlsonj insque(lif, dlp->dl_lifs); 8563431Scarlsonj } 8573431Scarlsonj lif->lif_lease = dlp; 8583431Scarlsonj lif->lif_dad_wait = _B_TRUE; 8593431Scarlsonj dsmp->dsm_lif_wait++; 8603431Scarlsonj } else { 8613431Scarlsonj /* If it was found, cancel timer */ 8623431Scarlsonj cancel_lif_timers(lif); 8633431Scarlsonj if (d6ia.d6ia_preflife != 0 && 8643431Scarlsonj !clear_lif_deprecated(lif)) { 8653431Scarlsonj unplumb_lif(lif); 8663431Scarlsonj continue; 8673431Scarlsonj } 8683431Scarlsonj } 8693431Scarlsonj 8703431Scarlsonj /* Set the new expiry timers */ 8713431Scarlsonj init_timer(&lif->lif_preferred, d6ia.d6ia_preflife); 8723431Scarlsonj init_timer(&lif->lif_expire, d6ia.d6ia_vallife); 8733431Scarlsonj 8743431Scarlsonj /* 8753431Scarlsonj * If the preferred lifetime is over now, then the LIF 8763431Scarlsonj * is deprecated. If it's the same as the expiry time, 8773431Scarlsonj * then we don't need a separate timer for it. 8783431Scarlsonj */ 8793431Scarlsonj if (d6ia.d6ia_preflife == 0) { 8803431Scarlsonj set_lif_deprecated(lif); 8813431Scarlsonj } else if (d6ia.d6ia_preflife != DHCPV6_INFTIME && 8823431Scarlsonj d6ia.d6ia_preflife != d6ia.d6ia_vallife && 8833431Scarlsonj !schedule_lif_timer(lif, &lif->lif_preferred, 8843431Scarlsonj dhcp_deprecate)) { 8853431Scarlsonj unplumb_lif(lif); 8863431Scarlsonj continue; 8873431Scarlsonj } 8883431Scarlsonj 8893431Scarlsonj if (d6ia.d6ia_vallife != DHCPV6_INFTIME && 8903431Scarlsonj !schedule_lif_timer(lif, &lif->lif_expire, 8913431Scarlsonj dhcp_expire)) { 8923431Scarlsonj unplumb_lif(lif); 8933431Scarlsonj continue; 8943431Scarlsonj } 8953431Scarlsonj 8963431Scarlsonj if (d6ia.d6ia_preflife < shortest) 8973431Scarlsonj shortest = d6ia.d6ia_preflife; 8983431Scarlsonj } 8993431Scarlsonj 9003431Scarlsonj if (dlp->dl_nlifs == 0) { 9013431Scarlsonj dhcpmsg(MSG_WARNING, 9023431Scarlsonj "configure_v6_leases: no IAADDRs found in IA_NA"); 9033431Scarlsonj remove_lease(dlp); 9043431Scarlsonj continue; 9053431Scarlsonj } 9063431Scarlsonj 9073431Scarlsonj if (d6in.d6in_t1 == 0 && d6in.d6in_t2 == 0) { 9083431Scarlsonj /* Default values from RFC 3315: 0.5 and 0.8 */ 9093431Scarlsonj if ((d6in.d6in_t1 = shortest / 2) == 0) 9103431Scarlsonj d6in.d6in_t1 = 1; 9113431Scarlsonj d6in.d6in_t2 = shortest - shortest / 5; 9123431Scarlsonj } 9133431Scarlsonj 9143431Scarlsonj cancel_lease_timers(dlp); 9153431Scarlsonj init_timer(&dlp->dl_t1, d6in.d6in_t1); 9163431Scarlsonj init_timer(&dlp->dl_t2, d6in.d6in_t2); 9173431Scarlsonj 9183431Scarlsonj if ((d6in.d6in_t1 != DHCPV6_INFTIME && 9193431Scarlsonj !schedule_lease_timer(dlp, &dlp->dl_t1, dhcp_renew)) || 9203431Scarlsonj (d6in.d6in_t2 != DHCPV6_INFTIME && 9213431Scarlsonj !schedule_lease_timer(dlp, &dlp->dl_t2, dhcp_rebind))) { 9223431Scarlsonj dhcpmsg(MSG_WARNING, "configure_v6_leases: unable to " 9233431Scarlsonj "set renew/rebind timers"); 9243431Scarlsonj } else { 9253431Scarlsonj got_iana = B_TRUE; 9263431Scarlsonj } 9273431Scarlsonj } 9283431Scarlsonj 9293431Scarlsonj if (!got_iana) { 9303431Scarlsonj dhcpmsg(MSG_WARNING, 9313431Scarlsonj "configure_v6_leases: no usable IA_NA option found"); 9323431Scarlsonj } 9333431Scarlsonj 9343431Scarlsonj return (v6Done); 9353431Scarlsonj } 9363431Scarlsonj 9373431Scarlsonj /* 9383431Scarlsonj * configure_v4_lease(): configures the IPv4 lease on a state machine from 9393431Scarlsonj * the current DHCP ACK. There's only one lease and LIF 9403431Scarlsonj * per state machine in IPv4. 9413431Scarlsonj * 9423431Scarlsonj * input: dhcp_smach_t *: the machine to configure (with a valid dsm_ack) 9433431Scarlsonj * output: boolean_t: B_TRUE on success, B_FALSE on failure 9443431Scarlsonj */ 9453431Scarlsonj 9463431Scarlsonj static boolean_t 9473431Scarlsonj configure_v4_lease(dhcp_smach_t *dsmp) 9483431Scarlsonj { 9493431Scarlsonj struct lifreq lifr; 9500Sstevel@tonic-gate struct sockaddr_in *sin; 9513431Scarlsonj PKT_LIST *ack = dsmp->dsm_ack; 9523431Scarlsonj dhcp_lease_t *dlp; 9533431Scarlsonj dhcp_lif_t *lif; 9543431Scarlsonj uint32_t addrhbo; 9553431Scarlsonj struct in_addr inaddr; 9560Sstevel@tonic-gate 9570Sstevel@tonic-gate /* 9580Sstevel@tonic-gate * if we're using DHCP, then we'll have a valid CD_SERVER_ID 9590Sstevel@tonic-gate * (we checked in dhcp_acknak()); set it now so that 9603431Scarlsonj * dsmp->dsm_server is valid in case we need to send_decline(). 9610Sstevel@tonic-gate * note that we use comparisons against opts[CD_DHCP_TYPE] 9620Sstevel@tonic-gate * since we haven't set DHCP_IF_BOOTP yet (we don't do that 9630Sstevel@tonic-gate * until we're sure we want the offered address.) 9640Sstevel@tonic-gate */ 9650Sstevel@tonic-gate 9663431Scarlsonj if (ack->opts[CD_DHCP_TYPE] != NULL) { 9673431Scarlsonj (void) memcpy(&inaddr, ack->opts[CD_SERVER_ID]->value, 9683431Scarlsonj sizeof (inaddr)); 9693431Scarlsonj IN6_INADDR_TO_V4MAPPED(&inaddr, &dsmp->dsm_server); 9700Sstevel@tonic-gate } 9710Sstevel@tonic-gate 9723431Scarlsonj /* 9733431Scarlsonj * There needs to be exactly one lease for IPv4, and that lease 9743431Scarlsonj * controls the main LIF for the state machine. If it doesn't exist 9753431Scarlsonj * yet, then create it now. 9763431Scarlsonj */ 9773431Scarlsonj if ((dlp = dsmp->dsm_leases) == NULL && 9783431Scarlsonj (dlp = insert_lease(dsmp)) == NULL) { 9793431Scarlsonj dhcpmsg(MSG_ERROR, "configure_v4_lease: unable to allocate " 9803431Scarlsonj "memory for lease"); 9813431Scarlsonj return (B_FALSE); 9823431Scarlsonj } 9833431Scarlsonj if (dlp->dl_nlifs == 0) { 9843431Scarlsonj dlp->dl_lifs = dsmp->dsm_lif; 9853431Scarlsonj dlp->dl_nlifs = 1; 9863431Scarlsonj 9873431Scarlsonj /* The lease holds a reference on the LIF */ 9883431Scarlsonj hold_lif(dlp->dl_lifs); 9893431Scarlsonj dlp->dl_lifs->lif_lease = dlp; 9903431Scarlsonj } 9913431Scarlsonj 9923431Scarlsonj lif = dlp->dl_lifs; 9933431Scarlsonj 9943431Scarlsonj IN6_INADDR_TO_V4MAPPED(&ack->pkt->yiaddr, &lif->lif_v6addr); 9953431Scarlsonj addrhbo = ntohl(ack->pkt->yiaddr.s_addr); 9963431Scarlsonj if ((addrhbo & IN_CLASSA_NET) == 0 || 9973431Scarlsonj (addrhbo >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET || 9983431Scarlsonj IN_CLASSD(addrhbo)) { 9993431Scarlsonj dhcpmsg(MSG_ERROR, 10003431Scarlsonj "configure_v4_lease: got invalid IP address %s for %s", 10013431Scarlsonj inet_ntoa(ack->pkt->yiaddr), lif->lif_name); 10023431Scarlsonj return (B_FALSE); 10033431Scarlsonj } 10043431Scarlsonj 10053431Scarlsonj (void) memset(&lifr, 0, sizeof (struct lifreq)); 10063431Scarlsonj (void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ); 10070Sstevel@tonic-gate 10080Sstevel@tonic-gate /* 10090Sstevel@tonic-gate * bring the interface online. note that there is no optimal 10100Sstevel@tonic-gate * order here: it is considered bad taste (and in > solaris 7, 10110Sstevel@tonic-gate * likely illegal) to bring an interface up before it has an 10120Sstevel@tonic-gate * ip address. however, due to an apparent bug in sun fddi 10130Sstevel@tonic-gate * 5.0, fddi will not obtain a network routing entry unless 10140Sstevel@tonic-gate * the interface is brought up before it has an ip address. 10150Sstevel@tonic-gate * we take the lesser of the two evils; if fddi customers have 10160Sstevel@tonic-gate * problems, they can get a newer fddi distribution which 10170Sstevel@tonic-gate * fixes the problem. 10180Sstevel@tonic-gate */ 10190Sstevel@tonic-gate 10203431Scarlsonj sin = (struct sockaddr_in *)&lifr.lifr_addr; 10210Sstevel@tonic-gate sin->sin_family = AF_INET; 10220Sstevel@tonic-gate 10233431Scarlsonj (void) memset(&lif->lif_v6mask, 0xff, sizeof (lif->lif_v6mask)); 10240Sstevel@tonic-gate if (ack->opts[CD_SUBNETMASK] != NULL && 10253431Scarlsonj ack->opts[CD_SUBNETMASK]->len == sizeof (inaddr)) { 10260Sstevel@tonic-gate 10273431Scarlsonj (void) memcpy(&inaddr, ack->opts[CD_SUBNETMASK]->value, 10283431Scarlsonj sizeof (inaddr)); 10290Sstevel@tonic-gate 10300Sstevel@tonic-gate } else { 10310Sstevel@tonic-gate 10320Sstevel@tonic-gate if (ack->opts[CD_SUBNETMASK] != NULL && 10333431Scarlsonj ack->opts[CD_SUBNETMASK]->len != sizeof (inaddr)) { 10343431Scarlsonj dhcpmsg(MSG_WARNING, "configure_v4_lease: specified " 10353431Scarlsonj "subnet mask length is %d instead of %d, ignoring", 10360Sstevel@tonic-gate ack->opts[CD_SUBNETMASK]->len, sizeof (ipaddr_t)); 10373431Scarlsonj } else { 10383431Scarlsonj dhcpmsg(MSG_WARNING, "configure_v4_lease: no IP " 10393431Scarlsonj "netmask specified for %s, making best guess", 10403431Scarlsonj lif->lif_name); 10413431Scarlsonj } 10420Sstevel@tonic-gate 10430Sstevel@tonic-gate /* 10440Sstevel@tonic-gate * no legitimate IP subnet mask specified.. use best 10453431Scarlsonj * guess. recall that lif_addr is in network order, so 10460Sstevel@tonic-gate * imagine it's 0x11223344: then when it is read into 10470Sstevel@tonic-gate * a register on x86, it becomes 0x44332211, so we 10480Sstevel@tonic-gate * must ntohl() it to convert it to 0x11223344 in 10490Sstevel@tonic-gate * order to use the macros in <netinet/in.h>. 10500Sstevel@tonic-gate */ 10510Sstevel@tonic-gate 10523431Scarlsonj if (IN_CLASSA(addrhbo)) 10533431Scarlsonj inaddr.s_addr = htonl(IN_CLASSA_NET); 10543431Scarlsonj else if (IN_CLASSB(addrhbo)) 10553431Scarlsonj inaddr.s_addr = htonl(IN_CLASSB_NET); 10563431Scarlsonj else /* must be class c */ 10573431Scarlsonj inaddr.s_addr = htonl(IN_CLASSC_NET); 10583431Scarlsonj } 10593431Scarlsonj lif->lif_v6mask._S6_un._S6_u32[3] = inaddr.s_addr; 10600Sstevel@tonic-gate 10613431Scarlsonj sin->sin_addr = inaddr; 10623431Scarlsonj dhcpmsg(MSG_INFO, "configure_v4_lease: setting IP netmask to %s on %s", 10633431Scarlsonj inet_ntoa(sin->sin_addr), lif->lif_name); 10643431Scarlsonj 10653431Scarlsonj if (ioctl(v4_sock_fd, SIOCSLIFNETMASK, &lifr) == -1) { 10663431Scarlsonj dhcpmsg(MSG_ERR, "configure_v4_lease: cannot set IP netmask " 10673431Scarlsonj "on %s", lif->lif_name); 10683431Scarlsonj return (B_FALSE); 10690Sstevel@tonic-gate } 10700Sstevel@tonic-gate 10713431Scarlsonj IN6_V4MAPPED_TO_INADDR(&lif->lif_v6addr, &sin->sin_addr); 10723431Scarlsonj dhcpmsg(MSG_INFO, "configure_v4_lease: setting IP address to %s on %s", 10733431Scarlsonj inet_ntoa(sin->sin_addr), lif->lif_name); 10740Sstevel@tonic-gate 10753431Scarlsonj if (ioctl(v4_sock_fd, SIOCSLIFADDR, &lifr) == -1) { 10763431Scarlsonj dhcpmsg(MSG_ERR, "configure_v4_lease: cannot set IP address " 10773431Scarlsonj "on %s", lif->lif_name); 10783431Scarlsonj return (B_FALSE); 10793431Scarlsonj } 10803431Scarlsonj 10813431Scarlsonj if (ioctl(v4_sock_fd, SIOCGLIFFLAGS, &lifr) == -1) { 10823431Scarlsonj dhcpmsg(MSG_ERR, "configure_v4_lease: cannot get interface " 10833431Scarlsonj "flags for %s", lif->lif_name); 10843431Scarlsonj return (B_FALSE); 10850Sstevel@tonic-gate } 10860Sstevel@tonic-gate 10873431Scarlsonj lifr.lifr_flags |= IFF_UP; 10883431Scarlsonj if (ioctl(v4_sock_fd, SIOCSLIFFLAGS, &lifr) == -1) { 10893431Scarlsonj dhcpmsg(MSG_ERR, "configure_v4_lease: cannot set interface " 10903431Scarlsonj "flags for %s", lif->lif_name); 10913431Scarlsonj return (B_FALSE); 10923431Scarlsonj } 10933431Scarlsonj lif->lif_flags = lifr.lifr_flags; 10940Sstevel@tonic-gate 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 1149*3476Scarlsonj 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