xref: /onnv-gate/usr/src/cmd/cmd-inet/sbin/dhcpagent/renew.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
52546Scarlsonj  * Common Development and Distribution License (the "License").
62546Scarlsonj  * 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 
250Sstevel@tonic-gate #include <sys/types.h>
260Sstevel@tonic-gate #include <time.h>
270Sstevel@tonic-gate #include <netinet/in.h>
280Sstevel@tonic-gate #include <netinet/dhcp.h>
290Sstevel@tonic-gate #include <netinet/udp.h>
300Sstevel@tonic-gate #include <netinet/ip_var.h>
310Sstevel@tonic-gate #include <netinet/udp_var.h>
320Sstevel@tonic-gate #include <libinetutil.h>
330Sstevel@tonic-gate #include <dhcpmsg.h>
34*12989SVasumathi.Sundaram@oracle.COM #include <dhcp_hostconf.h>
350Sstevel@tonic-gate #include <string.h>
360Sstevel@tonic-gate 
370Sstevel@tonic-gate #include "packet.h"
380Sstevel@tonic-gate #include "agent.h"
390Sstevel@tonic-gate #include "script_handler.h"
400Sstevel@tonic-gate #include "interface.h"
410Sstevel@tonic-gate #include "states.h"
420Sstevel@tonic-gate #include "util.h"
430Sstevel@tonic-gate 
440Sstevel@tonic-gate /*
453431Scarlsonj  * Number of seconds to wait for a retry if the user is interacting with the
463431Scarlsonj  * daemon.
470Sstevel@tonic-gate  */
483431Scarlsonj #define	RETRY_DELAY	10
490Sstevel@tonic-gate 
503431Scarlsonj /*
513431Scarlsonj  * If the renew timer fires within this number of seconds of the rebind timer,
523431Scarlsonj  * then skip renew.  This prevents us from sending back-to-back renew and
533431Scarlsonj  * rebind messages -- a pointless activity.
543431Scarlsonj  */
553431Scarlsonj #define	TOO_CLOSE	2
560Sstevel@tonic-gate 
573431Scarlsonj static boolean_t stop_extending(dhcp_smach_t *, unsigned int);
580Sstevel@tonic-gate 
590Sstevel@tonic-gate /*
603431Scarlsonj  * dhcp_renew(): attempts to renew a DHCP lease on expiration of the T1 timer.
610Sstevel@tonic-gate  *
620Sstevel@tonic-gate  *   input: iu_tq_t *: unused
633431Scarlsonj  *	    void *: the lease to renew (dhcp_lease_t)
640Sstevel@tonic-gate  *  output: void
653431Scarlsonj  *
663431Scarlsonj  *   notes: The primary expense involved with DHCP (like most UDP protocols) is
673431Scarlsonj  *	    with the generation and handling of packets, not the contents of
683431Scarlsonj  *	    those packets.  Thus, we try to reduce the number of packets that
693431Scarlsonj  *	    are sent.  It would be nice to just renew all leases here (each one
703431Scarlsonj  *	    added has trivial added overhead), but the DHCPv6 RFC doesn't
713431Scarlsonj  *	    explicitly allow that behavior.  Rather than having that argument,
723431Scarlsonj  *	    we settle for ones that are close in expiry to the one that fired.
733431Scarlsonj  *	    For v4, we repeatedly reschedule the T1 timer to do the
743431Scarlsonj  *	    retransmissions.  For v6, we rely on the common timer computation
753431Scarlsonj  *	    in packet.c.
760Sstevel@tonic-gate  */
770Sstevel@tonic-gate 
780Sstevel@tonic-gate /* ARGSUSED */
790Sstevel@tonic-gate void
dhcp_renew(iu_tq_t * tqp,void * arg)800Sstevel@tonic-gate dhcp_renew(iu_tq_t *tqp, void *arg)
810Sstevel@tonic-gate {
823431Scarlsonj 	dhcp_lease_t *dlp = arg;
833431Scarlsonj 	dhcp_smach_t *dsmp = dlp->dl_smach;
843431Scarlsonj 	uint32_t	t2;
850Sstevel@tonic-gate 
863431Scarlsonj 	dhcpmsg(MSG_VERBOSE, "dhcp_renew: T1 timer expired on %s",
873431Scarlsonj 	    dsmp->dsm_name);
883431Scarlsonj 
893431Scarlsonj 	dlp->dl_t1.dt_id = -1;
900Sstevel@tonic-gate 
913431Scarlsonj 	if (dsmp->dsm_state == RENEWING || dsmp->dsm_state == REBINDING) {
923431Scarlsonj 		dhcpmsg(MSG_DEBUG, "dhcp_renew: already renewing");
933431Scarlsonj 		release_lease(dlp);
943431Scarlsonj 		return;
953431Scarlsonj 	}
960Sstevel@tonic-gate 
973431Scarlsonj 	/*
983431Scarlsonj 	 * Sanity check: don't send packets if we're past T2, or if we're
993431Scarlsonj 	 * extremely close.
1003431Scarlsonj 	 */
1013431Scarlsonj 
1023431Scarlsonj 	t2 = dsmp->dsm_curstart_monosec + dlp->dl_t2.dt_start;
1033431Scarlsonj 	if (monosec() + TOO_CLOSE >= t2) {
1043431Scarlsonj 		dhcpmsg(MSG_DEBUG, "dhcp_renew: %spast T2 on %s",
1053431Scarlsonj 		    monosec() > t2 ? "" : "almost ", dsmp->dsm_name);
1063431Scarlsonj 		release_lease(dlp);
1070Sstevel@tonic-gate 		return;
1080Sstevel@tonic-gate 	}
1090Sstevel@tonic-gate 
1100Sstevel@tonic-gate 	/*
1113431Scarlsonj 	 * If there isn't an async event pending, or if we can cancel the one
1123431Scarlsonj 	 * that's there, then try to renew by sending an extension request.  If
1133431Scarlsonj 	 * that fails, we'll try again when the next timer fires.
1140Sstevel@tonic-gate 	 */
1153431Scarlsonj 	if (!async_cancel(dsmp) || !async_start(dsmp, DHCP_EXTEND, B_FALSE) ||
1163431Scarlsonj 	    !dhcp_extending(dsmp)) {
1173431Scarlsonj 		if (monosec() + RETRY_DELAY < t2) {
1180Sstevel@tonic-gate 			/*
1193431Scarlsonj 			 * Try again in RETRY_DELAY seconds; user command
1203431Scarlsonj 			 * should be gone.
1210Sstevel@tonic-gate 			 */
1223431Scarlsonj 			init_timer(&dlp->dl_t1, RETRY_DELAY);
1233431Scarlsonj 			(void) set_smach_state(dsmp, BOUND);
1243431Scarlsonj 			if (!schedule_lease_timer(dlp, &dlp->dl_t1,
1253431Scarlsonj 			    dhcp_renew)) {
1263431Scarlsonj 				dhcpmsg(MSG_INFO, "dhcp_renew: unable to "
1273431Scarlsonj 				    "reschedule renewal around user command "
1283431Scarlsonj 				    "on %s; will wait for rebind",
1293431Scarlsonj 				    dsmp->dsm_name);
1303431Scarlsonj 			}
1313431Scarlsonj 		} else {
1323431Scarlsonj 			dhcpmsg(MSG_DEBUG, "dhcp_renew: user busy on %s; will "
1333431Scarlsonj 			    "wait for rebind", dsmp->dsm_name);
1343431Scarlsonj 		}
1353431Scarlsonj 	}
1363431Scarlsonj 	release_lease(dlp);
1370Sstevel@tonic-gate }
1380Sstevel@tonic-gate 
1390Sstevel@tonic-gate /*
1403431Scarlsonj  * dhcp_rebind(): attempts to renew a DHCP lease from the REBINDING state (T2
1413431Scarlsonj  *		  timer expiry).
1420Sstevel@tonic-gate  *
1430Sstevel@tonic-gate  *   input: iu_tq_t *: unused
1443431Scarlsonj  *	    void *: the lease to renew
1450Sstevel@tonic-gate  *  output: void
1463431Scarlsonj  *   notes: For v4, we repeatedly reschedule the T2 timer to do the
1473431Scarlsonj  *	    retransmissions.  For v6, we rely on the common timer computation
1483431Scarlsonj  *	    in packet.c.
1490Sstevel@tonic-gate  */
1500Sstevel@tonic-gate 
1510Sstevel@tonic-gate /* ARGSUSED */
1520Sstevel@tonic-gate void
dhcp_rebind(iu_tq_t * tqp,void * arg)1530Sstevel@tonic-gate dhcp_rebind(iu_tq_t *tqp, void *arg)
1540Sstevel@tonic-gate {
1553431Scarlsonj 	dhcp_lease_t	*dlp = arg;
1563431Scarlsonj 	dhcp_smach_t	*dsmp = dlp->dl_smach;
1573431Scarlsonj 	int		nlifs;
1583431Scarlsonj 	dhcp_lif_t	*lif;
1593431Scarlsonj 	boolean_t	some_valid;
1603431Scarlsonj 	uint32_t	expiremax;
1613431Scarlsonj 	DHCPSTATE	oldstate;
1623431Scarlsonj 
1633431Scarlsonj 	dhcpmsg(MSG_VERBOSE, "dhcp_rebind: T2 timer expired on %s",
1643431Scarlsonj 	    dsmp->dsm_name);
1653431Scarlsonj 
1663431Scarlsonj 	dlp->dl_t2.dt_id = -1;
1673431Scarlsonj 
1683431Scarlsonj 	if ((oldstate = dsmp->dsm_state) == REBINDING) {
1693431Scarlsonj 		dhcpmsg(MSG_DEBUG, "dhcp_renew: already rebinding");
1703431Scarlsonj 		release_lease(dlp);
1713431Scarlsonj 		return;
1723431Scarlsonj 	}
1730Sstevel@tonic-gate 
1743431Scarlsonj 	/*
1753431Scarlsonj 	 * Sanity check: don't send packets if we've already expired on all of
1763431Scarlsonj 	 * the addresses.  We compute the maximum expiration time here, because
1773431Scarlsonj 	 * it won't matter for v4 (there's only one lease) and for v6 we need
1783431Scarlsonj 	 * to know when the last lease ages away.
1793431Scarlsonj 	 */
1800Sstevel@tonic-gate 
1813431Scarlsonj 	some_valid = B_FALSE;
1823431Scarlsonj 	expiremax = monosec();
1833431Scarlsonj 	lif = dlp->dl_lifs;
1843431Scarlsonj 	for (nlifs = dlp->dl_nlifs; nlifs > 0; nlifs--, lif = lif->lif_next) {
1853431Scarlsonj 		uint32_t expire;
1863431Scarlsonj 
1873431Scarlsonj 		expire = dsmp->dsm_curstart_monosec + lif->lif_expire.dt_start;
1883431Scarlsonj 		if (expire > expiremax) {
1893431Scarlsonj 			expiremax = expire;
1903431Scarlsonj 			some_valid = B_TRUE;
1913431Scarlsonj 		}
1923431Scarlsonj 	}
1933431Scarlsonj 	if (!some_valid) {
1943431Scarlsonj 		dhcpmsg(MSG_DEBUG, "dhcp_rebind: all leases expired on %s",
1953431Scarlsonj 		    dsmp->dsm_name);
1963431Scarlsonj 		release_lease(dlp);
1970Sstevel@tonic-gate 		return;
1980Sstevel@tonic-gate 	}
1990Sstevel@tonic-gate 
2000Sstevel@tonic-gate 	/*
2013431Scarlsonj 	 * This is our first venture into the REBINDING state, so reset the
2023431Scarlsonj 	 * server address.  We know the renew timer has already been cancelled
2033431Scarlsonj 	 * (or we wouldn't be here).
2040Sstevel@tonic-gate 	 */
2053431Scarlsonj 	if (dsmp->dsm_isv6) {
2063431Scarlsonj 		dsmp->dsm_server = ipv6_all_dhcp_relay_and_servers;
2073431Scarlsonj 	} else {
2083431Scarlsonj 		IN6_IPADDR_TO_V4MAPPED(htonl(INADDR_BROADCAST),
2093431Scarlsonj 		    &dsmp->dsm_server);
2103431Scarlsonj 	}
2110Sstevel@tonic-gate 
2123431Scarlsonj 	/* {Bound,Renew}->rebind transitions cannot fail */
2133431Scarlsonj 	(void) set_smach_state(dsmp, REBINDING);
2140Sstevel@tonic-gate 
2150Sstevel@tonic-gate 	/*
2163431Scarlsonj 	 * If there isn't an async event pending, or if we can cancel the one
2173431Scarlsonj 	 * that's there, then try to rebind by sending an extension request.
2183431Scarlsonj 	 * If that fails, we'll clean up when the lease expires.
2190Sstevel@tonic-gate 	 */
2203431Scarlsonj 	if (!async_cancel(dsmp) || !async_start(dsmp, DHCP_EXTEND, B_FALSE) ||
2213431Scarlsonj 	    !dhcp_extending(dsmp)) {
2223431Scarlsonj 		if (monosec() + RETRY_DELAY < expiremax) {
2230Sstevel@tonic-gate 			/*
2243431Scarlsonj 			 * Try again in RETRY_DELAY seconds; user command
2253431Scarlsonj 			 * should be gone.
2260Sstevel@tonic-gate 			 */
2273431Scarlsonj 			init_timer(&dlp->dl_t2, RETRY_DELAY);
2283431Scarlsonj 			(void) set_smach_state(dsmp, oldstate);
2293431Scarlsonj 			if (!schedule_lease_timer(dlp, &dlp->dl_t2,
2303431Scarlsonj 			    dhcp_rebind)) {
2313431Scarlsonj 				dhcpmsg(MSG_INFO, "dhcp_rebind: unable to "
2323431Scarlsonj 				    "reschedule rebind around user command on "
2333431Scarlsonj 				    "%s; lease may expire", dsmp->dsm_name);
2343431Scarlsonj 			}
2353431Scarlsonj 		} else {
2363431Scarlsonj 			dhcpmsg(MSG_WARNING, "dhcp_rebind: user busy on %s; "
2373431Scarlsonj 			    "will expire", dsmp->dsm_name);
2383431Scarlsonj 		}
2390Sstevel@tonic-gate 	}
2403431Scarlsonj 	release_lease(dlp);
2410Sstevel@tonic-gate }
2420Sstevel@tonic-gate 
2430Sstevel@tonic-gate /*
2443431Scarlsonj  * dhcp_finish_expire(): finish expiration of a lease after the user script
2453431Scarlsonj  *			 runs.  If this is the last lease, then restart DHCP.
2463431Scarlsonj  *			 The caller has a reference to the LIF, which will be
2473431Scarlsonj  *			 dropped.
2480Sstevel@tonic-gate  *
2493431Scarlsonj  *   input: dhcp_smach_t *: the state machine to be restarted
2503431Scarlsonj  *	    void *: logical interface that has expired
2510Sstevel@tonic-gate  *  output: int: always 1
2520Sstevel@tonic-gate  */
2530Sstevel@tonic-gate 
2540Sstevel@tonic-gate static int
dhcp_finish_expire(dhcp_smach_t * dsmp,void * arg)2553431Scarlsonj dhcp_finish_expire(dhcp_smach_t *dsmp, void *arg)
2560Sstevel@tonic-gate {
2573431Scarlsonj 	dhcp_lif_t *lif = arg;
2583431Scarlsonj 	dhcp_lease_t *dlp;
2593431Scarlsonj 
2603431Scarlsonj 	dhcpmsg(MSG_DEBUG, "lease expired on %s; removing", lif->lif_name);
2613431Scarlsonj 
2623431Scarlsonj 	dlp = lif->lif_lease;
2633431Scarlsonj 	unplumb_lif(lif);
2643431Scarlsonj 	if (dlp->dl_nlifs == 0)
2653431Scarlsonj 		remove_lease(dlp);
2663431Scarlsonj 	release_lif(lif);
2673431Scarlsonj 
2683431Scarlsonj 	/* If some valid leases remain, then drive on */
2693431Scarlsonj 	if (dsmp->dsm_leases != NULL) {
2703431Scarlsonj 		dhcpmsg(MSG_DEBUG,
2713431Scarlsonj 		    "dhcp_finish_expire: some leases remain on %s",
2723431Scarlsonj 		    dsmp->dsm_name);
2733431Scarlsonj 		return (1);
2743431Scarlsonj 	}
2753431Scarlsonj 
276*12989SVasumathi.Sundaram@oracle.COM 	(void) remove_hostconf(dsmp->dsm_name, dsmp->dsm_isv6);
277*12989SVasumathi.Sundaram@oracle.COM 
2783431Scarlsonj 	dhcpmsg(MSG_INFO, "last lease expired on %s -- restarting DHCP",
2793431Scarlsonj 	    dsmp->dsm_name);
2800Sstevel@tonic-gate 
2810Sstevel@tonic-gate 	/*
2820Sstevel@tonic-gate 	 * in the case where the lease is less than DHCP_REBIND_MIN
2830Sstevel@tonic-gate 	 * seconds, we will never enter dhcp_renew() and thus the packet
2840Sstevel@tonic-gate 	 * counters will not be reset.  in that case, reset them here.
2850Sstevel@tonic-gate 	 */
2860Sstevel@tonic-gate 
2873431Scarlsonj 	if (dsmp->dsm_state == BOUND) {
2883431Scarlsonj 		dsmp->dsm_bad_offers	= 0;
2893431Scarlsonj 		dsmp->dsm_sent		= 0;
2903431Scarlsonj 		dsmp->dsm_received	= 0;
2910Sstevel@tonic-gate 	}
2920Sstevel@tonic-gate 
2933431Scarlsonj 	deprecate_leases(dsmp);
2940Sstevel@tonic-gate 
2953431Scarlsonj 	/* reset_smach() in dhcp_selecting() will clean up any leftover state */
2963431Scarlsonj 	dhcp_selecting(dsmp);
2973431Scarlsonj 
2980Sstevel@tonic-gate 	return (1);
2990Sstevel@tonic-gate }
3000Sstevel@tonic-gate 
3010Sstevel@tonic-gate /*
3023431Scarlsonj  * dhcp_deprecate(): deprecates an address on a given logical interface when
3033431Scarlsonj  *		     the preferred lifetime expires.
3040Sstevel@tonic-gate  *
3050Sstevel@tonic-gate  *   input: iu_tq_t *: unused
3063431Scarlsonj  *	    void *: the logical interface whose lease is expiring
3073431Scarlsonj  *  output: void
3083431Scarlsonj  */
3093431Scarlsonj 
3103431Scarlsonj /* ARGSUSED */
3113431Scarlsonj void
dhcp_deprecate(iu_tq_t * tqp,void * arg)3123431Scarlsonj dhcp_deprecate(iu_tq_t *tqp, void *arg)
3133431Scarlsonj {
3143431Scarlsonj 	dhcp_lif_t *lif = arg;
3153431Scarlsonj 
3163431Scarlsonj 	set_lif_deprecated(lif);
3173431Scarlsonj 	release_lif(lif);
3183431Scarlsonj }
3193431Scarlsonj 
3203431Scarlsonj /*
3213431Scarlsonj  * dhcp_expire(): expires a lease on a given logical interface and, if there
3223431Scarlsonj  *		  are no more leases, restarts DHCP.
3233431Scarlsonj  *
3243431Scarlsonj  *   input: iu_tq_t *: unused
3253431Scarlsonj  *	    void *: the logical interface whose lease has expired
3260Sstevel@tonic-gate  *  output: void
3270Sstevel@tonic-gate  */
3280Sstevel@tonic-gate 
3290Sstevel@tonic-gate /* ARGSUSED */
3300Sstevel@tonic-gate void
dhcp_expire(iu_tq_t * tqp,void * arg)3310Sstevel@tonic-gate dhcp_expire(iu_tq_t *tqp, void *arg)
3320Sstevel@tonic-gate {
3333431Scarlsonj 	dhcp_lif_t	*lif = arg;
3343431Scarlsonj 	dhcp_smach_t	*dsmp;
3353431Scarlsonj 	const char	*event;
3360Sstevel@tonic-gate 
3373431Scarlsonj 	dhcpmsg(MSG_VERBOSE, "dhcp_expire: lease timer expired on %s",
3383431Scarlsonj 	    lif->lif_name);
3390Sstevel@tonic-gate 
3403431Scarlsonj 	lif->lif_expire.dt_id = -1;
3413431Scarlsonj 	if (lif->lif_lease == NULL) {
3423431Scarlsonj 		release_lif(lif);
3430Sstevel@tonic-gate 		return;
3440Sstevel@tonic-gate 	}
3450Sstevel@tonic-gate 
3463431Scarlsonj 	set_lif_deprecated(lif);
3470Sstevel@tonic-gate 
3483431Scarlsonj 	dsmp = lif->lif_lease->dl_smach;
3493431Scarlsonj 
3503431Scarlsonj 	if (!async_cancel(dsmp)) {
3510Sstevel@tonic-gate 
3523431Scarlsonj 		dhcpmsg(MSG_WARNING,
3533431Scarlsonj 		    "dhcp_expire: cannot cancel current asynchronous command "
3543431Scarlsonj 		    "on %s", dsmp->dsm_name);
3553431Scarlsonj 
3563431Scarlsonj 		/*
3573431Scarlsonj 		 * Try to schedule ourselves for callback.  We're really
3583431Scarlsonj 		 * situation-critical here; there's not much hope for us if
3593431Scarlsonj 		 * this fails.
3603431Scarlsonj 		 */
3613431Scarlsonj 		init_timer(&lif->lif_expire, DHCP_EXPIRE_WAIT);
3623431Scarlsonj 		if (schedule_lif_timer(lif, &lif->lif_expire, dhcp_expire))
3633431Scarlsonj 			return;
3640Sstevel@tonic-gate 
3653431Scarlsonj 		dhcpmsg(MSG_CRIT, "dhcp_expire: cannot reschedule dhcp_expire "
3663431Scarlsonj 		    "to get called back, proceeding...");
3673431Scarlsonj 	}
3683431Scarlsonj 
3693431Scarlsonj 	if (!async_start(dsmp, DHCP_START, B_FALSE))
3703431Scarlsonj 		dhcpmsg(MSG_WARNING, "dhcp_expire: cannot start asynchronous "
3713431Scarlsonj 		    "transaction on %s, continuing...", dsmp->dsm_name);
3720Sstevel@tonic-gate 
3733431Scarlsonj 	/*
3743431Scarlsonj 	 * Determine if this state machine has any non-expired LIFs left in it.
3753431Scarlsonj 	 * If it doesn't, then this is an "expire" event.  Otherwise, if some
3763431Scarlsonj 	 * valid leases remain, it's a "loss" event.  The SOMEEXP case can
3773431Scarlsonj 	 * occur only with DHCPv6.
3783431Scarlsonj 	 */
3793431Scarlsonj 	if (expired_lif_state(dsmp) == DHCP_EXP_SOMEEXP)
3803431Scarlsonj 		event = EVENT_LOSS6;
3813431Scarlsonj 	else if (dsmp->dsm_isv6)
3823431Scarlsonj 		event = EVENT_EXPIRE6;
3833431Scarlsonj 	else
3843431Scarlsonj 		event = EVENT_EXPIRE;
3850Sstevel@tonic-gate 
3860Sstevel@tonic-gate 	/*
3870Sstevel@tonic-gate 	 * just march on if this fails; at worst someone will be able
3880Sstevel@tonic-gate 	 * to async_start() while we're actually busy with our own
3890Sstevel@tonic-gate 	 * asynchronous transaction.  better than not having a lease.
3900Sstevel@tonic-gate 	 */
3910Sstevel@tonic-gate 
3923431Scarlsonj 	(void) script_start(dsmp, event, dhcp_finish_expire, lif, NULL);
3930Sstevel@tonic-gate }
3940Sstevel@tonic-gate 
3950Sstevel@tonic-gate /*
3963431Scarlsonj  * dhcp_extending(): sends a REQUEST (IPv4 DHCP) or Rebind/Renew (DHCPv6) to
3973431Scarlsonj  *		     extend a lease on a given state machine
3980Sstevel@tonic-gate  *
3993431Scarlsonj  *   input: dhcp_smach_t *: the state machine to send the message from
4003431Scarlsonj  *  output: boolean_t: B_TRUE if the extension request was sent
4010Sstevel@tonic-gate  */
4020Sstevel@tonic-gate 
4033431Scarlsonj boolean_t
dhcp_extending(dhcp_smach_t * dsmp)4043431Scarlsonj dhcp_extending(dhcp_smach_t *dsmp)
4050Sstevel@tonic-gate {
4060Sstevel@tonic-gate 	dhcp_pkt_t		*dpkt;
4070Sstevel@tonic-gate 
4083431Scarlsonj 	stop_pkt_retransmission(dsmp);
4093431Scarlsonj 
4103431Scarlsonj 	/*
4113431Scarlsonj 	 * We change state here because this function is also called when
4123431Scarlsonj 	 * adopting a lease and on demand by the user.
4133431Scarlsonj 	 */
4143431Scarlsonj 	if (dsmp->dsm_state == BOUND) {
4153431Scarlsonj 		dsmp->dsm_neg_hrtime	= gethrtime();
4163431Scarlsonj 		dsmp->dsm_bad_offers	= 0;
4173431Scarlsonj 		dsmp->dsm_sent		= 0;
4183431Scarlsonj 		dsmp->dsm_received	= 0;
4193431Scarlsonj 		/* Bound->renew can't fail */
4203431Scarlsonj 		(void) set_smach_state(dsmp, RENEWING);
4210Sstevel@tonic-gate 	}
4220Sstevel@tonic-gate 
4233431Scarlsonj 	dhcpmsg(MSG_DEBUG, "dhcp_extending: sending request on %s",
4243431Scarlsonj 	    dsmp->dsm_name);
4253431Scarlsonj 
4263431Scarlsonj 	if (dsmp->dsm_isv6) {
4273431Scarlsonj 		dhcp_lease_t *dlp;
4283431Scarlsonj 		dhcp_lif_t *lif;
4293431Scarlsonj 		uint_t nlifs;
4303431Scarlsonj 		uint_t irt, mrt;
4313431Scarlsonj 
4323431Scarlsonj 		/*
4333431Scarlsonj 		 * Start constructing the Renew/Rebind message.  Only Renew has
4343431Scarlsonj 		 * a server ID, as we still think our server might be
4353431Scarlsonj 		 * reachable.
4363431Scarlsonj 		 */
4373431Scarlsonj 		if (dsmp->dsm_state == RENEWING) {
4383431Scarlsonj 			dpkt = init_pkt(dsmp, DHCPV6_MSG_RENEW);
4393431Scarlsonj 			(void) add_pkt_opt(dpkt, DHCPV6_OPT_SERVERID,
4403431Scarlsonj 			    dsmp->dsm_serverid, dsmp->dsm_serveridlen);
4413431Scarlsonj 			irt = DHCPV6_REN_TIMEOUT;
4423431Scarlsonj 			mrt = DHCPV6_REN_MAX_RT;
4433431Scarlsonj 		} else {
4443431Scarlsonj 			dpkt = init_pkt(dsmp, DHCPV6_MSG_REBIND);
4453431Scarlsonj 			irt = DHCPV6_REB_TIMEOUT;
4463431Scarlsonj 			mrt = DHCPV6_REB_MAX_RT;
4473431Scarlsonj 		}
4480Sstevel@tonic-gate 
4493431Scarlsonj 		/*
4503431Scarlsonj 		 * Loop over the leases, and add an IA_NA for each and an
4513431Scarlsonj 		 * IAADDR for each address.
4523431Scarlsonj 		 */
4533431Scarlsonj 		for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
4543431Scarlsonj 			lif = dlp->dl_lifs;
4553431Scarlsonj 			for (nlifs = dlp->dl_nlifs; nlifs > 0;
4563431Scarlsonj 			    nlifs--, lif = lif->lif_next) {
4573431Scarlsonj 				(void) add_pkt_lif(dpkt, lif,
4583431Scarlsonj 				    DHCPV6_STAT_SUCCESS, NULL);
4593431Scarlsonj 			}
4603431Scarlsonj 		}
4613431Scarlsonj 
4623431Scarlsonj 		/* Add required Option Request option */
4633431Scarlsonj 		(void) add_pkt_prl(dpkt, dsmp);
4643431Scarlsonj 
4653431Scarlsonj 		return (send_pkt_v6(dsmp, dpkt, dsmp->dsm_server,
4663431Scarlsonj 		    stop_extending, irt, mrt));
4673431Scarlsonj 	} else {
4683431Scarlsonj 		dhcp_lif_t *lif = dsmp->dsm_lif;
4693431Scarlsonj 		ipaddr_t server;
4703431Scarlsonj 
4713431Scarlsonj 		/* assemble the DHCPREQUEST message. */
4723431Scarlsonj 		dpkt = init_pkt(dsmp, REQUEST);
4733431Scarlsonj 		dpkt->pkt->ciaddr.s_addr = lif->lif_addr;
4740Sstevel@tonic-gate 
4753431Scarlsonj 		/*
4763431Scarlsonj 		 * The max dhcp message size option is set to the interface
4773431Scarlsonj 		 * max, minus the size of the udp and ip headers.
4783431Scarlsonj 		 */
4793431Scarlsonj 		(void) add_pkt_opt16(dpkt, CD_MAX_DHCP_SIZE,
4803431Scarlsonj 		    htons(lif->lif_max - sizeof (struct udpiphdr)));
4813431Scarlsonj 		(void) add_pkt_opt32(dpkt, CD_LEASE_TIME, htonl(DHCP_PERM));
4823431Scarlsonj 
4833448Sdh155122 		if (class_id_len != 0) {
4843448Sdh155122 			(void) add_pkt_opt(dpkt, CD_CLASS_ID, class_id,
4853448Sdh155122 			    class_id_len);
4863448Sdh155122 		}
4873431Scarlsonj 		(void) add_pkt_prl(dpkt, dsmp);
4883431Scarlsonj 		/*
4893431Scarlsonj 		 * dsm_reqhost was set for this state machine in
4903431Scarlsonj 		 * dhcp_selecting() if the REQUEST_HOSTNAME option was set and
4913431Scarlsonj 		 * a host name was found.
4923431Scarlsonj 		 */
4933431Scarlsonj 		if (dsmp->dsm_reqhost != NULL) {
4943431Scarlsonj 			(void) add_pkt_opt(dpkt, CD_HOSTNAME, dsmp->dsm_reqhost,
4953431Scarlsonj 			    strlen(dsmp->dsm_reqhost));
4963431Scarlsonj 		}
4973431Scarlsonj 		(void) add_pkt_opt(dpkt, CD_END, NULL, 0);
4983431Scarlsonj 
4993431Scarlsonj 		IN6_V4MAPPED_TO_IPADDR(&dsmp->dsm_server, server);
5003431Scarlsonj 		return (send_pkt(dsmp, dpkt, server, stop_extending));
5013431Scarlsonj 	}
5023431Scarlsonj }
5030Sstevel@tonic-gate 
5043431Scarlsonj /*
5053431Scarlsonj  * stop_extending(): decides when to stop retransmitting v4 REQUEST or v6
5063431Scarlsonj  *		     Renew/Rebind messages.  If we're renewing, then stop if
5073431Scarlsonj  *		     T2 is soon approaching.
5083431Scarlsonj  *
5093431Scarlsonj  *   input: dhcp_smach_t *: the state machine REQUESTs are being sent from
5103431Scarlsonj  *	    unsigned int: the number of REQUESTs sent so far
5113431Scarlsonj  *  output: boolean_t: B_TRUE if retransmissions should stop
5123431Scarlsonj  */
5133431Scarlsonj 
5143431Scarlsonj /* ARGSUSED */
5153431Scarlsonj static boolean_t
stop_extending(dhcp_smach_t * dsmp,unsigned int n_requests)5163431Scarlsonj stop_extending(dhcp_smach_t *dsmp, unsigned int n_requests)
5173431Scarlsonj {
5183431Scarlsonj 	dhcp_lease_t *dlp;
5190Sstevel@tonic-gate 
5203431Scarlsonj 	/*
5213431Scarlsonj 	 * If we're renewing and rebind time is soon approaching, then don't
5223431Scarlsonj 	 * schedule
5233431Scarlsonj 	 */
5243431Scarlsonj 	if (dsmp->dsm_state == RENEWING) {
5253431Scarlsonj 		monosec_t t2;
5263431Scarlsonj 
5273431Scarlsonj 		t2 = 0;
5283431Scarlsonj 		for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
5293431Scarlsonj 			if (dlp->dl_t2.dt_start > t2)
5303431Scarlsonj 				t2 = dlp->dl_t2.dt_start;
5313431Scarlsonj 		}
5323431Scarlsonj 		t2 += dsmp->dsm_curstart_monosec;
5333431Scarlsonj 		if (monosec() + TOO_CLOSE >= t2) {
5343431Scarlsonj 			dhcpmsg(MSG_DEBUG, "stop_extending: %spast T2 on %s",
5353431Scarlsonj 			    monosec() > t2 ? "" : "almost ", dsmp->dsm_name);
5363431Scarlsonj 			return (B_TRUE);
5373431Scarlsonj 		}
5380Sstevel@tonic-gate 	}
5390Sstevel@tonic-gate 
5400Sstevel@tonic-gate 	/*
5413431Scarlsonj 	 * Note that returning B_TRUE cancels both this transmission and the
5423431Scarlsonj 	 * one that would occur at dsm_send_timeout, and that for v4 we cut the
5433431Scarlsonj 	 * time in half for each retransmission.  Thus we check here against
5443431Scarlsonj 	 * half of the minimum.
5450Sstevel@tonic-gate 	 */
5463431Scarlsonj 	if (!dsmp->dsm_isv6 &&
5473431Scarlsonj 	    dsmp->dsm_send_timeout < DHCP_REBIND_MIN * MILLISEC / 2) {
5483431Scarlsonj 		dhcpmsg(MSG_DEBUG, "stop_extending: next retry would be in "
5493431Scarlsonj 		    "%d.%03d; stopping", dsmp->dsm_send_timeout / MILLISEC,
5503431Scarlsonj 		    dsmp->dsm_send_timeout % MILLISEC);
5513431Scarlsonj 		return (B_TRUE);
5523431Scarlsonj 	}
5530Sstevel@tonic-gate 
5543431Scarlsonj 	/* Otherwise, w stop only when the next timer (rebind, expire) fires */
5553431Scarlsonj 	return (B_FALSE);
5560Sstevel@tonic-gate }
557