17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
569bb4bb4Scarlsonj  * Common Development and Distribution License (the "License").
669bb4bb4Scarlsonj  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
220a3e1f6cSVasumathi Sundaram  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
23*b31320a7SChris Fraire  * Copyright (c) 2016-2017, Chris Fraire <cfraire@me.com>.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #include <sys/types.h>
277c478bd9Sstevel@tonic-gate #include <time.h>
287c478bd9Sstevel@tonic-gate #include <netinet/in.h>
297c478bd9Sstevel@tonic-gate #include <netinet/dhcp.h>
307c478bd9Sstevel@tonic-gate #include <netinet/udp.h>
317c478bd9Sstevel@tonic-gate #include <netinet/ip_var.h>
327c478bd9Sstevel@tonic-gate #include <netinet/udp_var.h>
337c478bd9Sstevel@tonic-gate #include <libinetutil.h>
347c478bd9Sstevel@tonic-gate #include <dhcpmsg.h>
350a3e1f6cSVasumathi Sundaram #include <dhcp_hostconf.h>
367c478bd9Sstevel@tonic-gate #include <string.h>
377c478bd9Sstevel@tonic-gate 
387c478bd9Sstevel@tonic-gate #include "packet.h"
397c478bd9Sstevel@tonic-gate #include "agent.h"
407c478bd9Sstevel@tonic-gate #include "script_handler.h"
417c478bd9Sstevel@tonic-gate #include "interface.h"
427c478bd9Sstevel@tonic-gate #include "states.h"
437c478bd9Sstevel@tonic-gate #include "util.h"
447c478bd9Sstevel@tonic-gate 
457c478bd9Sstevel@tonic-gate /*
46d04ccbb3Scarlsonj  * Number of seconds to wait for a retry if the user is interacting with the
47d04ccbb3Scarlsonj  * daemon.
487c478bd9Sstevel@tonic-gate  */
49d04ccbb3Scarlsonj #define	RETRY_DELAY	10
507c478bd9Sstevel@tonic-gate 
51d04ccbb3Scarlsonj /*
52d04ccbb3Scarlsonj  * If the renew timer fires within this number of seconds of the rebind timer,
53d04ccbb3Scarlsonj  * then skip renew.  This prevents us from sending back-to-back renew and
54d04ccbb3Scarlsonj  * rebind messages -- a pointless activity.
55d04ccbb3Scarlsonj  */
56d04ccbb3Scarlsonj #define	TOO_CLOSE	2
577c478bd9Sstevel@tonic-gate 
58d04ccbb3Scarlsonj static boolean_t stop_extending(dhcp_smach_t *, unsigned int);
597c478bd9Sstevel@tonic-gate 
607c478bd9Sstevel@tonic-gate /*
61d04ccbb3Scarlsonj  * dhcp_renew(): attempts to renew a DHCP lease on expiration of the T1 timer.
627c478bd9Sstevel@tonic-gate  *
637c478bd9Sstevel@tonic-gate  *   input: iu_tq_t *: unused
64d04ccbb3Scarlsonj  *	    void *: the lease to renew (dhcp_lease_t)
657c478bd9Sstevel@tonic-gate  *  output: void
66d04ccbb3Scarlsonj  *
67d04ccbb3Scarlsonj  *   notes: The primary expense involved with DHCP (like most UDP protocols) is
68d04ccbb3Scarlsonj  *	    with the generation and handling of packets, not the contents of
69d04ccbb3Scarlsonj  *	    those packets.  Thus, we try to reduce the number of packets that
70d04ccbb3Scarlsonj  *	    are sent.  It would be nice to just renew all leases here (each one
71d04ccbb3Scarlsonj  *	    added has trivial added overhead), but the DHCPv6 RFC doesn't
72d04ccbb3Scarlsonj  *	    explicitly allow that behavior.  Rather than having that argument,
73d04ccbb3Scarlsonj  *	    we settle for ones that are close in expiry to the one that fired.
74d04ccbb3Scarlsonj  *	    For v4, we repeatedly reschedule the T1 timer to do the
75d04ccbb3Scarlsonj  *	    retransmissions.  For v6, we rely on the common timer computation
76d04ccbb3Scarlsonj  *	    in packet.c.
777c478bd9Sstevel@tonic-gate  */
787c478bd9Sstevel@tonic-gate 
797c478bd9Sstevel@tonic-gate /* ARGSUSED */
807c478bd9Sstevel@tonic-gate void
dhcp_renew(iu_tq_t * tqp,void * arg)817c478bd9Sstevel@tonic-gate dhcp_renew(iu_tq_t *tqp, void *arg)
827c478bd9Sstevel@tonic-gate {
83d04ccbb3Scarlsonj 	dhcp_lease_t *dlp = arg;
84d04ccbb3Scarlsonj 	dhcp_smach_t *dsmp = dlp->dl_smach;
85d04ccbb3Scarlsonj 	uint32_t	t2;
867c478bd9Sstevel@tonic-gate 
87d04ccbb3Scarlsonj 	dhcpmsg(MSG_VERBOSE, "dhcp_renew: T1 timer expired on %s",
88d04ccbb3Scarlsonj 	    dsmp->dsm_name);
897c478bd9Sstevel@tonic-gate 
90d04ccbb3Scarlsonj 	dlp->dl_t1.dt_id = -1;
917c478bd9Sstevel@tonic-gate 
92d04ccbb3Scarlsonj 	if (dsmp->dsm_state == RENEWING || dsmp->dsm_state == REBINDING) {
93d04ccbb3Scarlsonj 		dhcpmsg(MSG_DEBUG, "dhcp_renew: already renewing");
94d04ccbb3Scarlsonj 		release_lease(dlp);
957c478bd9Sstevel@tonic-gate 		return;
967c478bd9Sstevel@tonic-gate 	}
977c478bd9Sstevel@tonic-gate 
987c478bd9Sstevel@tonic-gate 	/*
99d04ccbb3Scarlsonj 	 * Sanity check: don't send packets if we're past T2, or if we're
100d04ccbb3Scarlsonj 	 * extremely close.
1017c478bd9Sstevel@tonic-gate 	 */
1027c478bd9Sstevel@tonic-gate 
103d04ccbb3Scarlsonj 	t2 = dsmp->dsm_curstart_monosec + dlp->dl_t2.dt_start;
104d04ccbb3Scarlsonj 	if (monosec() + TOO_CLOSE >= t2) {
105d04ccbb3Scarlsonj 		dhcpmsg(MSG_DEBUG, "dhcp_renew: %spast T2 on %s",
106d04ccbb3Scarlsonj 		    monosec() > t2 ? "" : "almost ", dsmp->dsm_name);
107d04ccbb3Scarlsonj 		release_lease(dlp);
1087c478bd9Sstevel@tonic-gate 		return;
109d04ccbb3Scarlsonj 	}
1107c478bd9Sstevel@tonic-gate 
1117c478bd9Sstevel@tonic-gate 	/*
112d04ccbb3Scarlsonj 	 * If there isn't an async event pending, or if we can cancel the one
113d04ccbb3Scarlsonj 	 * that's there, then try to renew by sending an extension request.  If
114d04ccbb3Scarlsonj 	 * that fails, we'll try again when the next timer fires.
1157c478bd9Sstevel@tonic-gate 	 */
116d04ccbb3Scarlsonj 	if (!async_cancel(dsmp) || !async_start(dsmp, DHCP_EXTEND, B_FALSE) ||
117d04ccbb3Scarlsonj 	    !dhcp_extending(dsmp)) {
118d04ccbb3Scarlsonj 		if (monosec() + RETRY_DELAY < t2) {
1197c478bd9Sstevel@tonic-gate 			/*
120d04ccbb3Scarlsonj 			 * Try again in RETRY_DELAY seconds; user command
121d04ccbb3Scarlsonj 			 * should be gone.
1227c478bd9Sstevel@tonic-gate 			 */
123d04ccbb3Scarlsonj 			init_timer(&dlp->dl_t1, RETRY_DELAY);
124d04ccbb3Scarlsonj 			(void) set_smach_state(dsmp, BOUND);
125d04ccbb3Scarlsonj 			if (!schedule_lease_timer(dlp, &dlp->dl_t1,
126d04ccbb3Scarlsonj 			    dhcp_renew)) {
127d04ccbb3Scarlsonj 				dhcpmsg(MSG_INFO, "dhcp_renew: unable to "
128d04ccbb3Scarlsonj 				    "reschedule renewal around user command "
129d04ccbb3Scarlsonj 				    "on %s; will wait for rebind",
130d04ccbb3Scarlsonj 				    dsmp->dsm_name);
131d04ccbb3Scarlsonj 			}
132d04ccbb3Scarlsonj 		} else {
133d04ccbb3Scarlsonj 			dhcpmsg(MSG_DEBUG, "dhcp_renew: user busy on %s; will "
134d04ccbb3Scarlsonj 			    "wait for rebind", dsmp->dsm_name);
135d04ccbb3Scarlsonj 		}
136d04ccbb3Scarlsonj 	}
137d04ccbb3Scarlsonj 	release_lease(dlp);
1387c478bd9Sstevel@tonic-gate }
1397c478bd9Sstevel@tonic-gate 
1407c478bd9Sstevel@tonic-gate /*
141d04ccbb3Scarlsonj  * dhcp_rebind(): attempts to renew a DHCP lease from the REBINDING state (T2
142d04ccbb3Scarlsonj  *		  timer expiry).
1437c478bd9Sstevel@tonic-gate  *
1447c478bd9Sstevel@tonic-gate  *   input: iu_tq_t *: unused
145d04ccbb3Scarlsonj  *	    void *: the lease to renew
1467c478bd9Sstevel@tonic-gate  *  output: void
147d04ccbb3Scarlsonj  *   notes: For v4, we repeatedly reschedule the T2 timer to do the
148d04ccbb3Scarlsonj  *	    retransmissions.  For v6, we rely on the common timer computation
149d04ccbb3Scarlsonj  *	    in packet.c.
1507c478bd9Sstevel@tonic-gate  */
1517c478bd9Sstevel@tonic-gate 
1527c478bd9Sstevel@tonic-gate /* ARGSUSED */
1537c478bd9Sstevel@tonic-gate void
dhcp_rebind(iu_tq_t * tqp,void * arg)1547c478bd9Sstevel@tonic-gate dhcp_rebind(iu_tq_t *tqp, void *arg)
1557c478bd9Sstevel@tonic-gate {
156d04ccbb3Scarlsonj 	dhcp_lease_t	*dlp = arg;
157d04ccbb3Scarlsonj 	dhcp_smach_t	*dsmp = dlp->dl_smach;
158d04ccbb3Scarlsonj 	int		nlifs;
159d04ccbb3Scarlsonj 	dhcp_lif_t	*lif;
160d04ccbb3Scarlsonj 	boolean_t	some_valid;
161d04ccbb3Scarlsonj 	uint32_t	expiremax;
162d04ccbb3Scarlsonj 	DHCPSTATE	oldstate;
163d04ccbb3Scarlsonj 
164d04ccbb3Scarlsonj 	dhcpmsg(MSG_VERBOSE, "dhcp_rebind: T2 timer expired on %s",
165d04ccbb3Scarlsonj 	    dsmp->dsm_name);
166d04ccbb3Scarlsonj 
167d04ccbb3Scarlsonj 	dlp->dl_t2.dt_id = -1;
168d04ccbb3Scarlsonj 
169d04ccbb3Scarlsonj 	if ((oldstate = dsmp->dsm_state) == REBINDING) {
170d04ccbb3Scarlsonj 		dhcpmsg(MSG_DEBUG, "dhcp_renew: already rebinding");
171d04ccbb3Scarlsonj 		release_lease(dlp);
1727c478bd9Sstevel@tonic-gate 		return;
1737c478bd9Sstevel@tonic-gate 	}
1747c478bd9Sstevel@tonic-gate 
1757c478bd9Sstevel@tonic-gate 	/*
176d04ccbb3Scarlsonj 	 * Sanity check: don't send packets if we've already expired on all of
177d04ccbb3Scarlsonj 	 * the addresses.  We compute the maximum expiration time here, because
178d04ccbb3Scarlsonj 	 * it won't matter for v4 (there's only one lease) and for v6 we need
179d04ccbb3Scarlsonj 	 * to know when the last lease ages away.
1807c478bd9Sstevel@tonic-gate 	 */
1817c478bd9Sstevel@tonic-gate 
182d04ccbb3Scarlsonj 	some_valid = B_FALSE;
183d04ccbb3Scarlsonj 	expiremax = monosec();
184d04ccbb3Scarlsonj 	lif = dlp->dl_lifs;
185d04ccbb3Scarlsonj 	for (nlifs = dlp->dl_nlifs; nlifs > 0; nlifs--, lif = lif->lif_next) {
186d04ccbb3Scarlsonj 		uint32_t expire;
1877c478bd9Sstevel@tonic-gate 
188d04ccbb3Scarlsonj 		expire = dsmp->dsm_curstart_monosec + lif->lif_expire.dt_start;
189d04ccbb3Scarlsonj 		if (expire > expiremax) {
190d04ccbb3Scarlsonj 			expiremax = expire;
191d04ccbb3Scarlsonj 			some_valid = B_TRUE;
192d04ccbb3Scarlsonj 		}
193d04ccbb3Scarlsonj 	}
194d04ccbb3Scarlsonj 	if (!some_valid) {
195d04ccbb3Scarlsonj 		dhcpmsg(MSG_DEBUG, "dhcp_rebind: all leases expired on %s",
196d04ccbb3Scarlsonj 		    dsmp->dsm_name);
197d04ccbb3Scarlsonj 		release_lease(dlp);
198d04ccbb3Scarlsonj 		return;
199d04ccbb3Scarlsonj 	}
2007c478bd9Sstevel@tonic-gate 
2017c478bd9Sstevel@tonic-gate 	/*
202d04ccbb3Scarlsonj 	 * This is our first venture into the REBINDING state, so reset the
203d04ccbb3Scarlsonj 	 * server address.  We know the renew timer has already been cancelled
204d04ccbb3Scarlsonj 	 * (or we wouldn't be here).
2057c478bd9Sstevel@tonic-gate 	 */
206d04ccbb3Scarlsonj 	if (dsmp->dsm_isv6) {
207d04ccbb3Scarlsonj 		dsmp->dsm_server = ipv6_all_dhcp_relay_and_servers;
208d04ccbb3Scarlsonj 	} else {
209d04ccbb3Scarlsonj 		IN6_IPADDR_TO_V4MAPPED(htonl(INADDR_BROADCAST),
210d04ccbb3Scarlsonj 		    &dsmp->dsm_server);
2117c478bd9Sstevel@tonic-gate 	}
2127c478bd9Sstevel@tonic-gate 
213d04ccbb3Scarlsonj 	/* {Bound,Renew}->rebind transitions cannot fail */
214d04ccbb3Scarlsonj 	(void) set_smach_state(dsmp, REBINDING);
215d04ccbb3Scarlsonj 
2167c478bd9Sstevel@tonic-gate 	/*
217d04ccbb3Scarlsonj 	 * If there isn't an async event pending, or if we can cancel the one
218d04ccbb3Scarlsonj 	 * that's there, then try to rebind by sending an extension request.
219d04ccbb3Scarlsonj 	 * If that fails, we'll clean up when the lease expires.
2207c478bd9Sstevel@tonic-gate 	 */
221d04ccbb3Scarlsonj 	if (!async_cancel(dsmp) || !async_start(dsmp, DHCP_EXTEND, B_FALSE) ||
222d04ccbb3Scarlsonj 	    !dhcp_extending(dsmp)) {
223d04ccbb3Scarlsonj 		if (monosec() + RETRY_DELAY < expiremax) {
2247c478bd9Sstevel@tonic-gate 			/*
225d04ccbb3Scarlsonj 			 * Try again in RETRY_DELAY seconds; user command
226d04ccbb3Scarlsonj 			 * should be gone.
2277c478bd9Sstevel@tonic-gate 			 */
228d04ccbb3Scarlsonj 			init_timer(&dlp->dl_t2, RETRY_DELAY);
229d04ccbb3Scarlsonj 			(void) set_smach_state(dsmp, oldstate);
230d04ccbb3Scarlsonj 			if (!schedule_lease_timer(dlp, &dlp->dl_t2,
231d04ccbb3Scarlsonj 			    dhcp_rebind)) {
232d04ccbb3Scarlsonj 				dhcpmsg(MSG_INFO, "dhcp_rebind: unable to "
233d04ccbb3Scarlsonj 				    "reschedule rebind around user command on "
234d04ccbb3Scarlsonj 				    "%s; lease may expire", dsmp->dsm_name);
235d04ccbb3Scarlsonj 			}
236d04ccbb3Scarlsonj 		} else {
237d04ccbb3Scarlsonj 			dhcpmsg(MSG_WARNING, "dhcp_rebind: user busy on %s; "
238d04ccbb3Scarlsonj 			    "will expire", dsmp->dsm_name);
239d04ccbb3Scarlsonj 		}
2407c478bd9Sstevel@tonic-gate 	}
241d04ccbb3Scarlsonj 	release_lease(dlp);
2427c478bd9Sstevel@tonic-gate }
2437c478bd9Sstevel@tonic-gate 
2447c478bd9Sstevel@tonic-gate /*
245d04ccbb3Scarlsonj  * dhcp_finish_expire(): finish expiration of a lease after the user script
246d04ccbb3Scarlsonj  *			 runs.  If this is the last lease, then restart DHCP.
247d04ccbb3Scarlsonj  *			 The caller has a reference to the LIF, which will be
248d04ccbb3Scarlsonj  *			 dropped.
2497c478bd9Sstevel@tonic-gate  *
250d04ccbb3Scarlsonj  *   input: dhcp_smach_t *: the state machine to be restarted
251d04ccbb3Scarlsonj  *	    void *: logical interface that has expired
2527c478bd9Sstevel@tonic-gate  *  output: int: always 1
2537c478bd9Sstevel@tonic-gate  */
2547c478bd9Sstevel@tonic-gate 
2557c478bd9Sstevel@tonic-gate static int
dhcp_finish_expire(dhcp_smach_t * dsmp,void * arg)256d04ccbb3Scarlsonj dhcp_finish_expire(dhcp_smach_t *dsmp, void *arg)
2577c478bd9Sstevel@tonic-gate {
258d04ccbb3Scarlsonj 	dhcp_lif_t *lif = arg;
259d04ccbb3Scarlsonj 	dhcp_lease_t *dlp;
260d04ccbb3Scarlsonj 
261d04ccbb3Scarlsonj 	dhcpmsg(MSG_DEBUG, "lease expired on %s; removing", lif->lif_name);
262d04ccbb3Scarlsonj 
263d04ccbb3Scarlsonj 	dlp = lif->lif_lease;
264d04ccbb3Scarlsonj 	unplumb_lif(lif);
265d04ccbb3Scarlsonj 	if (dlp->dl_nlifs == 0)
266d04ccbb3Scarlsonj 		remove_lease(dlp);
267d04ccbb3Scarlsonj 	release_lif(lif);
268d04ccbb3Scarlsonj 
269d04ccbb3Scarlsonj 	/* If some valid leases remain, then drive on */
270d04ccbb3Scarlsonj 	if (dsmp->dsm_leases != NULL) {
271d04ccbb3Scarlsonj 		dhcpmsg(MSG_DEBUG,
272d04ccbb3Scarlsonj 		    "dhcp_finish_expire: some leases remain on %s",
273d04ccbb3Scarlsonj 		    dsmp->dsm_name);
274d04ccbb3Scarlsonj 		return (1);
275d04ccbb3Scarlsonj 	}
276d04ccbb3Scarlsonj 
2770a3e1f6cSVasumathi Sundaram 	(void) remove_hostconf(dsmp->dsm_name, dsmp->dsm_isv6);
2780a3e1f6cSVasumathi Sundaram 
279d04ccbb3Scarlsonj 	dhcpmsg(MSG_INFO, "last lease expired on %s -- restarting DHCP",
280d04ccbb3Scarlsonj 	    dsmp->dsm_name);
2817c478bd9Sstevel@tonic-gate 
2827c478bd9Sstevel@tonic-gate 	/*
2837c478bd9Sstevel@tonic-gate 	 * in the case where the lease is less than DHCP_REBIND_MIN
2847c478bd9Sstevel@tonic-gate 	 * seconds, we will never enter dhcp_renew() and thus the packet
2857c478bd9Sstevel@tonic-gate 	 * counters will not be reset.  in that case, reset them here.
2867c478bd9Sstevel@tonic-gate 	 */
2877c478bd9Sstevel@tonic-gate 
288d04ccbb3Scarlsonj 	if (dsmp->dsm_state == BOUND) {
289d04ccbb3Scarlsonj 		dsmp->dsm_bad_offers	= 0;
290d04ccbb3Scarlsonj 		dsmp->dsm_sent		= 0;
291d04ccbb3Scarlsonj 		dsmp->dsm_received	= 0;
2927c478bd9Sstevel@tonic-gate 	}
2937c478bd9Sstevel@tonic-gate 
294d04ccbb3Scarlsonj 	deprecate_leases(dsmp);
295d04ccbb3Scarlsonj 
296d04ccbb3Scarlsonj 	/* reset_smach() in dhcp_selecting() will clean up any leftover state */
297d04ccbb3Scarlsonj 	dhcp_selecting(dsmp);
2987c478bd9Sstevel@tonic-gate 
2997c478bd9Sstevel@tonic-gate 	return (1);
3007c478bd9Sstevel@tonic-gate }
3017c478bd9Sstevel@tonic-gate 
3027c478bd9Sstevel@tonic-gate /*
303d04ccbb3Scarlsonj  * dhcp_deprecate(): deprecates an address on a given logical interface when
304d04ccbb3Scarlsonj  *		     the preferred lifetime expires.
3057c478bd9Sstevel@tonic-gate  *
3067c478bd9Sstevel@tonic-gate  *   input: iu_tq_t *: unused
307d04ccbb3Scarlsonj  *	    void *: the logical interface whose lease is expiring
308d04ccbb3Scarlsonj  *  output: void
309d04ccbb3Scarlsonj  */
310d04ccbb3Scarlsonj 
311d04ccbb3Scarlsonj /* ARGSUSED */
312d04ccbb3Scarlsonj void
dhcp_deprecate(iu_tq_t * tqp,void * arg)313d04ccbb3Scarlsonj dhcp_deprecate(iu_tq_t *tqp, void *arg)
314d04ccbb3Scarlsonj {
315d04ccbb3Scarlsonj 	dhcp_lif_t *lif = arg;
316d04ccbb3Scarlsonj 
317d04ccbb3Scarlsonj 	set_lif_deprecated(lif);
318d04ccbb3Scarlsonj 	release_lif(lif);
319d04ccbb3Scarlsonj }
320d04ccbb3Scarlsonj 
321d04ccbb3Scarlsonj /*
322d04ccbb3Scarlsonj  * dhcp_expire(): expires a lease on a given logical interface and, if there
323d04ccbb3Scarlsonj  *		  are no more leases, restarts DHCP.
324d04ccbb3Scarlsonj  *
325d04ccbb3Scarlsonj  *   input: iu_tq_t *: unused
326d04ccbb3Scarlsonj  *	    void *: the logical interface whose lease has expired
3277c478bd9Sstevel@tonic-gate  *  output: void
3287c478bd9Sstevel@tonic-gate  */
3297c478bd9Sstevel@tonic-gate 
3307c478bd9Sstevel@tonic-gate /* ARGSUSED */
3317c478bd9Sstevel@tonic-gate void
dhcp_expire(iu_tq_t * tqp,void * arg)3327c478bd9Sstevel@tonic-gate dhcp_expire(iu_tq_t *tqp, void *arg)
3337c478bd9Sstevel@tonic-gate {
334d04ccbb3Scarlsonj 	dhcp_lif_t	*lif = arg;
335d04ccbb3Scarlsonj 	dhcp_smach_t	*dsmp;
336d04ccbb3Scarlsonj 	const char	*event;
3377c478bd9Sstevel@tonic-gate 
338d04ccbb3Scarlsonj 	dhcpmsg(MSG_VERBOSE, "dhcp_expire: lease timer expired on %s",
339d04ccbb3Scarlsonj 	    lif->lif_name);
3407c478bd9Sstevel@tonic-gate 
341d04ccbb3Scarlsonj 	lif->lif_expire.dt_id = -1;
342d04ccbb3Scarlsonj 	if (lif->lif_lease == NULL) {
343d04ccbb3Scarlsonj 		release_lif(lif);
3447c478bd9Sstevel@tonic-gate 		return;
3457c478bd9Sstevel@tonic-gate 	}
3467c478bd9Sstevel@tonic-gate 
347d04ccbb3Scarlsonj 	set_lif_deprecated(lif);
3487c478bd9Sstevel@tonic-gate 
349d04ccbb3Scarlsonj 	dsmp = lif->lif_lease->dl_smach;
3507c478bd9Sstevel@tonic-gate 
351d04ccbb3Scarlsonj 	if (!async_cancel(dsmp)) {
3527c478bd9Sstevel@tonic-gate 
353d04ccbb3Scarlsonj 		dhcpmsg(MSG_WARNING,
354d04ccbb3Scarlsonj 		    "dhcp_expire: cannot cancel current asynchronous command "
355d04ccbb3Scarlsonj 		    "on %s", dsmp->dsm_name);
3567c478bd9Sstevel@tonic-gate 
357d04ccbb3Scarlsonj 		/*
358d04ccbb3Scarlsonj 		 * Try to schedule ourselves for callback.  We're really
359d04ccbb3Scarlsonj 		 * situation-critical here; there's not much hope for us if
360d04ccbb3Scarlsonj 		 * this fails.
361d04ccbb3Scarlsonj 		 */
362d04ccbb3Scarlsonj 		init_timer(&lif->lif_expire, DHCP_EXPIRE_WAIT);
363d04ccbb3Scarlsonj 		if (schedule_lif_timer(lif, &lif->lif_expire, dhcp_expire))
364d04ccbb3Scarlsonj 			return;
3657c478bd9Sstevel@tonic-gate 
366d04ccbb3Scarlsonj 		dhcpmsg(MSG_CRIT, "dhcp_expire: cannot reschedule dhcp_expire "
367d04ccbb3Scarlsonj 		    "to get called back, proceeding...");
368d04ccbb3Scarlsonj 	}
369d04ccbb3Scarlsonj 
370d04ccbb3Scarlsonj 	if (!async_start(dsmp, DHCP_START, B_FALSE))
371d04ccbb3Scarlsonj 		dhcpmsg(MSG_WARNING, "dhcp_expire: cannot start asynchronous "
372d04ccbb3Scarlsonj 		    "transaction on %s, continuing...", dsmp->dsm_name);
373d04ccbb3Scarlsonj 
374d04ccbb3Scarlsonj 	/*
375d04ccbb3Scarlsonj 	 * Determine if this state machine has any non-expired LIFs left in it.
376d04ccbb3Scarlsonj 	 * If it doesn't, then this is an "expire" event.  Otherwise, if some
377d04ccbb3Scarlsonj 	 * valid leases remain, it's a "loss" event.  The SOMEEXP case can
378d04ccbb3Scarlsonj 	 * occur only with DHCPv6.
379d04ccbb3Scarlsonj 	 */
380d04ccbb3Scarlsonj 	if (expired_lif_state(dsmp) == DHCP_EXP_SOMEEXP)
381d04ccbb3Scarlsonj 		event = EVENT_LOSS6;
382d04ccbb3Scarlsonj 	else if (dsmp->dsm_isv6)
383d04ccbb3Scarlsonj 		event = EVENT_EXPIRE6;
384d04ccbb3Scarlsonj 	else
385d04ccbb3Scarlsonj 		event = EVENT_EXPIRE;
3867c478bd9Sstevel@tonic-gate 
3877c478bd9Sstevel@tonic-gate 	/*
3887c478bd9Sstevel@tonic-gate 	 * just march on if this fails; at worst someone will be able
3897c478bd9Sstevel@tonic-gate 	 * to async_start() while we're actually busy with our own
3907c478bd9Sstevel@tonic-gate 	 * asynchronous transaction.  better than not having a lease.
3917c478bd9Sstevel@tonic-gate 	 */
3927c478bd9Sstevel@tonic-gate 
393d04ccbb3Scarlsonj 	(void) script_start(dsmp, event, dhcp_finish_expire, lif, NULL);
3947c478bd9Sstevel@tonic-gate }
3957c478bd9Sstevel@tonic-gate 
3967c478bd9Sstevel@tonic-gate /*
397d04ccbb3Scarlsonj  * dhcp_extending(): sends a REQUEST (IPv4 DHCP) or Rebind/Renew (DHCPv6) to
398d04ccbb3Scarlsonj  *		     extend a lease on a given state machine
3997c478bd9Sstevel@tonic-gate  *
400d04ccbb3Scarlsonj  *   input: dhcp_smach_t *: the state machine to send the message from
401d04ccbb3Scarlsonj  *  output: boolean_t: B_TRUE if the extension request was sent
4027c478bd9Sstevel@tonic-gate  */
4037c478bd9Sstevel@tonic-gate 
404d04ccbb3Scarlsonj boolean_t
dhcp_extending(dhcp_smach_t * dsmp)405d04ccbb3Scarlsonj dhcp_extending(dhcp_smach_t *dsmp)
4067c478bd9Sstevel@tonic-gate {
4077c478bd9Sstevel@tonic-gate 	dhcp_pkt_t		*dpkt;
4087c478bd9Sstevel@tonic-gate 
409d04ccbb3Scarlsonj 	stop_pkt_retransmission(dsmp);
410d04ccbb3Scarlsonj 
411d04ccbb3Scarlsonj 	/*
412d04ccbb3Scarlsonj 	 * We change state here because this function is also called when
413d04ccbb3Scarlsonj 	 * adopting a lease and on demand by the user.
414d04ccbb3Scarlsonj 	 */
415d04ccbb3Scarlsonj 	if (dsmp->dsm_state == BOUND) {
416d04ccbb3Scarlsonj 		dsmp->dsm_neg_hrtime	= gethrtime();
417d04ccbb3Scarlsonj 		dsmp->dsm_bad_offers	= 0;
418d04ccbb3Scarlsonj 		dsmp->dsm_sent		= 0;
419d04ccbb3Scarlsonj 		dsmp->dsm_received	= 0;
420d04ccbb3Scarlsonj 		/* Bound->renew can't fail */
421d04ccbb3Scarlsonj 		(void) set_smach_state(dsmp, RENEWING);
4227c478bd9Sstevel@tonic-gate 	}
4237c478bd9Sstevel@tonic-gate 
424d04ccbb3Scarlsonj 	dhcpmsg(MSG_DEBUG, "dhcp_extending: sending request on %s",
425d04ccbb3Scarlsonj 	    dsmp->dsm_name);
4267c478bd9Sstevel@tonic-gate 
427d04ccbb3Scarlsonj 	if (dsmp->dsm_isv6) {
428d04ccbb3Scarlsonj 		dhcp_lease_t *dlp;
429d04ccbb3Scarlsonj 		dhcp_lif_t *lif;
430d04ccbb3Scarlsonj 		uint_t nlifs;
431d04ccbb3Scarlsonj 		uint_t irt, mrt;
4327c478bd9Sstevel@tonic-gate 
433d04ccbb3Scarlsonj 		/*
434d04ccbb3Scarlsonj 		 * Start constructing the Renew/Rebind message.  Only Renew has
435d04ccbb3Scarlsonj 		 * a server ID, as we still think our server might be
436d04ccbb3Scarlsonj 		 * reachable.
437d04ccbb3Scarlsonj 		 */
438d04ccbb3Scarlsonj 		if (dsmp->dsm_state == RENEWING) {
439d04ccbb3Scarlsonj 			dpkt = init_pkt(dsmp, DHCPV6_MSG_RENEW);
440d04ccbb3Scarlsonj 			(void) add_pkt_opt(dpkt, DHCPV6_OPT_SERVERID,
441d04ccbb3Scarlsonj 			    dsmp->dsm_serverid, dsmp->dsm_serveridlen);
442d04ccbb3Scarlsonj 			irt = DHCPV6_REN_TIMEOUT;
443d04ccbb3Scarlsonj 			mrt = DHCPV6_REN_MAX_RT;
444d04ccbb3Scarlsonj 		} else {
445d04ccbb3Scarlsonj 			dpkt = init_pkt(dsmp, DHCPV6_MSG_REBIND);
446d04ccbb3Scarlsonj 			irt = DHCPV6_REB_TIMEOUT;
447d04ccbb3Scarlsonj 			mrt = DHCPV6_REB_MAX_RT;
448d04ccbb3Scarlsonj 		}
4497c478bd9Sstevel@tonic-gate 
450d04ccbb3Scarlsonj 		/*
451d04ccbb3Scarlsonj 		 * Loop over the leases, and add an IA_NA for each and an
452d04ccbb3Scarlsonj 		 * IAADDR for each address.
453d04ccbb3Scarlsonj 		 */
454d04ccbb3Scarlsonj 		for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
455d04ccbb3Scarlsonj 			lif = dlp->dl_lifs;
456d04ccbb3Scarlsonj 			for (nlifs = dlp->dl_nlifs; nlifs > 0;
457d04ccbb3Scarlsonj 			    nlifs--, lif = lif->lif_next) {
458d04ccbb3Scarlsonj 				(void) add_pkt_lif(dpkt, lif,
459d04ccbb3Scarlsonj 				    DHCPV6_STAT_SUCCESS, NULL);
460d04ccbb3Scarlsonj 			}
461d04ccbb3Scarlsonj 		}
4627c478bd9Sstevel@tonic-gate 
463d04ccbb3Scarlsonj 		/* Add required Option Request option */
464d04ccbb3Scarlsonj 		(void) add_pkt_prl(dpkt, dsmp);
4657c478bd9Sstevel@tonic-gate 
466d04ccbb3Scarlsonj 		return (send_pkt_v6(dsmp, dpkt, dsmp->dsm_server,
467d04ccbb3Scarlsonj 		    stop_extending, irt, mrt));
468d04ccbb3Scarlsonj 	} else {
469d04ccbb3Scarlsonj 		dhcp_lif_t *lif = dsmp->dsm_lif;
470d04ccbb3Scarlsonj 		ipaddr_t server;
4717c478bd9Sstevel@tonic-gate 
472d04ccbb3Scarlsonj 		/* assemble the DHCPREQUEST message. */
473d04ccbb3Scarlsonj 		dpkt = init_pkt(dsmp, REQUEST);
474d04ccbb3Scarlsonj 		dpkt->pkt->ciaddr.s_addr = lif->lif_addr;
4757c478bd9Sstevel@tonic-gate 
476d04ccbb3Scarlsonj 		/*
477d04ccbb3Scarlsonj 		 * The max dhcp message size option is set to the interface
478d04ccbb3Scarlsonj 		 * max, minus the size of the udp and ip headers.
479d04ccbb3Scarlsonj 		 */
480d04ccbb3Scarlsonj 		(void) add_pkt_opt16(dpkt, CD_MAX_DHCP_SIZE,
481d04ccbb3Scarlsonj 		    htons(lif->lif_max - sizeof (struct udpiphdr)));
482d04ccbb3Scarlsonj 		(void) add_pkt_opt32(dpkt, CD_LEASE_TIME, htonl(DHCP_PERM));
483d04ccbb3Scarlsonj 
484f4b3ec61Sdh 		if (class_id_len != 0) {
485f4b3ec61Sdh 			(void) add_pkt_opt(dpkt, CD_CLASS_ID, class_id,
486f4b3ec61Sdh 			    class_id_len);
487f4b3ec61Sdh 		}
488d04ccbb3Scarlsonj 		(void) add_pkt_prl(dpkt, dsmp);
489d04ccbb3Scarlsonj 		/*
490d04ccbb3Scarlsonj 		 * dsm_reqhost was set for this state machine in
491d04ccbb3Scarlsonj 		 * dhcp_selecting() if the REQUEST_HOSTNAME option was set and
492d04ccbb3Scarlsonj 		 * a host name was found.
493d04ccbb3Scarlsonj 		 */
494*b31320a7SChris Fraire 		if (!dhcp_add_fqdn_opt(dpkt, dsmp) &&
495*b31320a7SChris Fraire 		    dsmp->dsm_reqhost != NULL) {
496d04ccbb3Scarlsonj 			(void) add_pkt_opt(dpkt, CD_HOSTNAME, dsmp->dsm_reqhost,
497d04ccbb3Scarlsonj 			    strlen(dsmp->dsm_reqhost));
498d04ccbb3Scarlsonj 		}
499d04ccbb3Scarlsonj 		(void) add_pkt_opt(dpkt, CD_END, NULL, 0);
500d04ccbb3Scarlsonj 
501d04ccbb3Scarlsonj 		IN6_V4MAPPED_TO_IPADDR(&dsmp->dsm_server, server);
502d04ccbb3Scarlsonj 		return (send_pkt(dsmp, dpkt, server, stop_extending));
503d04ccbb3Scarlsonj 	}
504d04ccbb3Scarlsonj }
505d04ccbb3Scarlsonj 
506d04ccbb3Scarlsonj /*
507d04ccbb3Scarlsonj  * stop_extending(): decides when to stop retransmitting v4 REQUEST or v6
508d04ccbb3Scarlsonj  *		     Renew/Rebind messages.  If we're renewing, then stop if
509d04ccbb3Scarlsonj  *		     T2 is soon approaching.
510d04ccbb3Scarlsonj  *
511d04ccbb3Scarlsonj  *   input: dhcp_smach_t *: the state machine REQUESTs are being sent from
512d04ccbb3Scarlsonj  *	    unsigned int: the number of REQUESTs sent so far
513d04ccbb3Scarlsonj  *  output: boolean_t: B_TRUE if retransmissions should stop
514d04ccbb3Scarlsonj  */
515d04ccbb3Scarlsonj 
516d04ccbb3Scarlsonj /* ARGSUSED */
517d04ccbb3Scarlsonj static boolean_t
stop_extending(dhcp_smach_t * dsmp,unsigned int n_requests)518d04ccbb3Scarlsonj stop_extending(dhcp_smach_t *dsmp, unsigned int n_requests)
519d04ccbb3Scarlsonj {
520d04ccbb3Scarlsonj 	dhcp_lease_t *dlp;
5217c478bd9Sstevel@tonic-gate 
5227c478bd9Sstevel@tonic-gate 	/*
523d04ccbb3Scarlsonj 	 * If we're renewing and rebind time is soon approaching, then don't
524d04ccbb3Scarlsonj 	 * schedule
5257c478bd9Sstevel@tonic-gate 	 */
526d04ccbb3Scarlsonj 	if (dsmp->dsm_state == RENEWING) {
527d04ccbb3Scarlsonj 		monosec_t t2;
528d04ccbb3Scarlsonj 
529d04ccbb3Scarlsonj 		t2 = 0;
530d04ccbb3Scarlsonj 		for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
531d04ccbb3Scarlsonj 			if (dlp->dl_t2.dt_start > t2)
532d04ccbb3Scarlsonj 				t2 = dlp->dl_t2.dt_start;
533d04ccbb3Scarlsonj 		}
534d04ccbb3Scarlsonj 		t2 += dsmp->dsm_curstart_monosec;
535d04ccbb3Scarlsonj 		if (monosec() + TOO_CLOSE >= t2) {
536d04ccbb3Scarlsonj 			dhcpmsg(MSG_DEBUG, "stop_extending: %spast T2 on %s",
537d04ccbb3Scarlsonj 			    monosec() > t2 ? "" : "almost ", dsmp->dsm_name);
538d04ccbb3Scarlsonj 			return (B_TRUE);
539d04ccbb3Scarlsonj 		}
5407c478bd9Sstevel@tonic-gate 	}
5417c478bd9Sstevel@tonic-gate 
5427c478bd9Sstevel@tonic-gate 	/*
543d04ccbb3Scarlsonj 	 * Note that returning B_TRUE cancels both this transmission and the
544d04ccbb3Scarlsonj 	 * one that would occur at dsm_send_timeout, and that for v4 we cut the
545d04ccbb3Scarlsonj 	 * time in half for each retransmission.  Thus we check here against
546d04ccbb3Scarlsonj 	 * half of the minimum.
5477c478bd9Sstevel@tonic-gate 	 */
548d04ccbb3Scarlsonj 	if (!dsmp->dsm_isv6 &&
549d04ccbb3Scarlsonj 	    dsmp->dsm_send_timeout < DHCP_REBIND_MIN * MILLISEC / 2) {
550d04ccbb3Scarlsonj 		dhcpmsg(MSG_DEBUG, "stop_extending: next retry would be in "
551d04ccbb3Scarlsonj 		    "%d.%03d; stopping", dsmp->dsm_send_timeout / MILLISEC,
552d04ccbb3Scarlsonj 		    dsmp->dsm_send_timeout % MILLISEC);
553d04ccbb3Scarlsonj 		return (B_TRUE);
554d04ccbb3Scarlsonj 	}
5557c478bd9Sstevel@tonic-gate 
556d04ccbb3Scarlsonj 	/* Otherwise, w stop only when the next timer (rebind, expire) fires */
557d04ccbb3Scarlsonj 	return (B_FALSE);
5587c478bd9Sstevel@tonic-gate }
559