xref: /onnv-gate/usr/src/cmd/cmd-inet/sbin/dhcpagent/bound.c (revision 12989:1e6b26d10a55)
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