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