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 /*
22*0a3e1f6cSVasumathi Sundaram * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
237c478bd9Sstevel@tonic-gate */
247c478bd9Sstevel@tonic-gate
257c478bd9Sstevel@tonic-gate #include <sys/types.h>
267c478bd9Sstevel@tonic-gate #include <time.h>
277c478bd9Sstevel@tonic-gate #include <netinet/in.h>
287c478bd9Sstevel@tonic-gate #include <netinet/dhcp.h>
297c478bd9Sstevel@tonic-gate #include <netinet/udp.h>
307c478bd9Sstevel@tonic-gate #include <netinet/ip_var.h>
317c478bd9Sstevel@tonic-gate #include <netinet/udp_var.h>
327c478bd9Sstevel@tonic-gate #include <libinetutil.h>
337c478bd9Sstevel@tonic-gate #include <dhcpmsg.h>
34*0a3e1f6cSVasumathi Sundaram #include <dhcp_hostconf.h>
357c478bd9Sstevel@tonic-gate #include <string.h>
367c478bd9Sstevel@tonic-gate
377c478bd9Sstevel@tonic-gate #include "packet.h"
387c478bd9Sstevel@tonic-gate #include "agent.h"
397c478bd9Sstevel@tonic-gate #include "script_handler.h"
407c478bd9Sstevel@tonic-gate #include "interface.h"
417c478bd9Sstevel@tonic-gate #include "states.h"
427c478bd9Sstevel@tonic-gate #include "util.h"
437c478bd9Sstevel@tonic-gate
447c478bd9Sstevel@tonic-gate /*
45d04ccbb3Scarlsonj * Number of seconds to wait for a retry if the user is interacting with the
46d04ccbb3Scarlsonj * daemon.
477c478bd9Sstevel@tonic-gate */
48d04ccbb3Scarlsonj #define RETRY_DELAY 10
497c478bd9Sstevel@tonic-gate
507c478bd9Sstevel@tonic-gate /*
51d04ccbb3Scarlsonj * If the renew timer fires within this number of seconds of the rebind timer,
52d04ccbb3Scarlsonj * then skip renew. This prevents us from sending back-to-back renew and
53d04ccbb3Scarlsonj * rebind messages -- a pointless activity.
54d04ccbb3Scarlsonj */
55d04ccbb3Scarlsonj #define TOO_CLOSE 2
56d04ccbb3Scarlsonj
57d04ccbb3Scarlsonj static boolean_t stop_extending(dhcp_smach_t *, unsigned int);
58d04ccbb3Scarlsonj
59d04ccbb3Scarlsonj /*
60d04ccbb3Scarlsonj * dhcp_renew(): attempts to renew a DHCP lease on expiration of the T1 timer.
617c478bd9Sstevel@tonic-gate *
627c478bd9Sstevel@tonic-gate * input: iu_tq_t *: unused
63d04ccbb3Scarlsonj * void *: the lease to renew (dhcp_lease_t)
647c478bd9Sstevel@tonic-gate * output: void
65d04ccbb3Scarlsonj *
66d04ccbb3Scarlsonj * notes: The primary expense involved with DHCP (like most UDP protocols) is
67d04ccbb3Scarlsonj * with the generation and handling of packets, not the contents of
68d04ccbb3Scarlsonj * those packets. Thus, we try to reduce the number of packets that
69d04ccbb3Scarlsonj * are sent. It would be nice to just renew all leases here (each one
70d04ccbb3Scarlsonj * added has trivial added overhead), but the DHCPv6 RFC doesn't
71d04ccbb3Scarlsonj * explicitly allow that behavior. Rather than having that argument,
72d04ccbb3Scarlsonj * we settle for ones that are close in expiry to the one that fired.
73d04ccbb3Scarlsonj * For v4, we repeatedly reschedule the T1 timer to do the
74d04ccbb3Scarlsonj * retransmissions. For v6, we rely on the common timer computation
75d04ccbb3Scarlsonj * in packet.c.
767c478bd9Sstevel@tonic-gate */
777c478bd9Sstevel@tonic-gate
787c478bd9Sstevel@tonic-gate /* ARGSUSED */
797c478bd9Sstevel@tonic-gate void
dhcp_renew(iu_tq_t * tqp,void * arg)807c478bd9Sstevel@tonic-gate dhcp_renew(iu_tq_t *tqp, void *arg)
817c478bd9Sstevel@tonic-gate {
82d04ccbb3Scarlsonj dhcp_lease_t *dlp = arg;
83d04ccbb3Scarlsonj dhcp_smach_t *dsmp = dlp->dl_smach;
84d04ccbb3Scarlsonj uint32_t t2;
857c478bd9Sstevel@tonic-gate
86d04ccbb3Scarlsonj dhcpmsg(MSG_VERBOSE, "dhcp_renew: T1 timer expired on %s",
87d04ccbb3Scarlsonj dsmp->dsm_name);
887c478bd9Sstevel@tonic-gate
89d04ccbb3Scarlsonj dlp->dl_t1.dt_id = -1;
907c478bd9Sstevel@tonic-gate
91d04ccbb3Scarlsonj if (dsmp->dsm_state == RENEWING || dsmp->dsm_state == REBINDING) {
92d04ccbb3Scarlsonj dhcpmsg(MSG_DEBUG, "dhcp_renew: already renewing");
93d04ccbb3Scarlsonj release_lease(dlp);
947c478bd9Sstevel@tonic-gate return;
957c478bd9Sstevel@tonic-gate }
967c478bd9Sstevel@tonic-gate
977c478bd9Sstevel@tonic-gate /*
98d04ccbb3Scarlsonj * Sanity check: don't send packets if we're past T2, or if we're
99d04ccbb3Scarlsonj * extremely close.
1007c478bd9Sstevel@tonic-gate */
1017c478bd9Sstevel@tonic-gate
102d04ccbb3Scarlsonj t2 = dsmp->dsm_curstart_monosec + dlp->dl_t2.dt_start;
103d04ccbb3Scarlsonj if (monosec() + TOO_CLOSE >= t2) {
104d04ccbb3Scarlsonj dhcpmsg(MSG_DEBUG, "dhcp_renew: %spast T2 on %s",
105d04ccbb3Scarlsonj monosec() > t2 ? "" : "almost ", dsmp->dsm_name);
106d04ccbb3Scarlsonj release_lease(dlp);
1077c478bd9Sstevel@tonic-gate return;
1087c478bd9Sstevel@tonic-gate }
1097c478bd9Sstevel@tonic-gate
1107c478bd9Sstevel@tonic-gate /*
111d04ccbb3Scarlsonj * If there isn't an async event pending, or if we can cancel the one
112d04ccbb3Scarlsonj * that's there, then try to renew by sending an extension request. If
113d04ccbb3Scarlsonj * that fails, we'll try again when the next timer fires.
114d04ccbb3Scarlsonj */
115d04ccbb3Scarlsonj if (!async_cancel(dsmp) || !async_start(dsmp, DHCP_EXTEND, B_FALSE) ||
116d04ccbb3Scarlsonj !dhcp_extending(dsmp)) {
117d04ccbb3Scarlsonj if (monosec() + RETRY_DELAY < t2) {
118d04ccbb3Scarlsonj /*
119d04ccbb3Scarlsonj * Try again in RETRY_DELAY seconds; user command
120d04ccbb3Scarlsonj * should be gone.
121d04ccbb3Scarlsonj */
122d04ccbb3Scarlsonj init_timer(&dlp->dl_t1, RETRY_DELAY);
123d04ccbb3Scarlsonj (void) set_smach_state(dsmp, BOUND);
124d04ccbb3Scarlsonj if (!schedule_lease_timer(dlp, &dlp->dl_t1,
125d04ccbb3Scarlsonj dhcp_renew)) {
126d04ccbb3Scarlsonj dhcpmsg(MSG_INFO, "dhcp_renew: unable to "
127d04ccbb3Scarlsonj "reschedule renewal around user command "
128d04ccbb3Scarlsonj "on %s; will wait for rebind",
129d04ccbb3Scarlsonj dsmp->dsm_name);
130d04ccbb3Scarlsonj }
131d04ccbb3Scarlsonj } else {
132d04ccbb3Scarlsonj dhcpmsg(MSG_DEBUG, "dhcp_renew: user busy on %s; will "
133d04ccbb3Scarlsonj "wait for rebind", dsmp->dsm_name);
134d04ccbb3Scarlsonj }
135d04ccbb3Scarlsonj }
136d04ccbb3Scarlsonj release_lease(dlp);
137d04ccbb3Scarlsonj }
138d04ccbb3Scarlsonj
139d04ccbb3Scarlsonj /*
140d04ccbb3Scarlsonj * dhcp_rebind(): attempts to renew a DHCP lease from the REBINDING state (T2
141d04ccbb3Scarlsonj * timer expiry).
1427c478bd9Sstevel@tonic-gate *
1437c478bd9Sstevel@tonic-gate * input: iu_tq_t *: unused
144d04ccbb3Scarlsonj * void *: the lease to renew
1457c478bd9Sstevel@tonic-gate * output: void
146d04ccbb3Scarlsonj * notes: For v4, we repeatedly reschedule the T2 timer to do the
147d04ccbb3Scarlsonj * retransmissions. For v6, we rely on the common timer computation
148d04ccbb3Scarlsonj * in packet.c.
1497c478bd9Sstevel@tonic-gate */
1507c478bd9Sstevel@tonic-gate
1517c478bd9Sstevel@tonic-gate /* ARGSUSED */
1527c478bd9Sstevel@tonic-gate void
dhcp_rebind(iu_tq_t * tqp,void * arg)1537c478bd9Sstevel@tonic-gate dhcp_rebind(iu_tq_t *tqp, void *arg)
1547c478bd9Sstevel@tonic-gate {
155d04ccbb3Scarlsonj dhcp_lease_t *dlp = arg;
156d04ccbb3Scarlsonj dhcp_smach_t *dsmp = dlp->dl_smach;
157d04ccbb3Scarlsonj int nlifs;
158d04ccbb3Scarlsonj dhcp_lif_t *lif;
159d04ccbb3Scarlsonj boolean_t some_valid;
160d04ccbb3Scarlsonj uint32_t expiremax;
161d04ccbb3Scarlsonj DHCPSTATE oldstate;
1627c478bd9Sstevel@tonic-gate
163d04ccbb3Scarlsonj dhcpmsg(MSG_VERBOSE, "dhcp_rebind: T2 timer expired on %s",
164d04ccbb3Scarlsonj dsmp->dsm_name);
1657c478bd9Sstevel@tonic-gate
166d04ccbb3Scarlsonj dlp->dl_t2.dt_id = -1;
167d04ccbb3Scarlsonj
168d04ccbb3Scarlsonj if ((oldstate = dsmp->dsm_state) == REBINDING) {
169d04ccbb3Scarlsonj dhcpmsg(MSG_DEBUG, "dhcp_renew: already rebinding");
170d04ccbb3Scarlsonj release_lease(dlp);
1717c478bd9Sstevel@tonic-gate return;
1727c478bd9Sstevel@tonic-gate }
1737c478bd9Sstevel@tonic-gate
1747c478bd9Sstevel@tonic-gate /*
175d04ccbb3Scarlsonj * Sanity check: don't send packets if we've already expired on all of
176d04ccbb3Scarlsonj * the addresses. We compute the maximum expiration time here, because
177d04ccbb3Scarlsonj * it won't matter for v4 (there's only one lease) and for v6 we need
178d04ccbb3Scarlsonj * to know when the last lease ages away.
1797c478bd9Sstevel@tonic-gate */
1807c478bd9Sstevel@tonic-gate
181d04ccbb3Scarlsonj some_valid = B_FALSE;
182d04ccbb3Scarlsonj expiremax = monosec();
183d04ccbb3Scarlsonj lif = dlp->dl_lifs;
184d04ccbb3Scarlsonj for (nlifs = dlp->dl_nlifs; nlifs > 0; nlifs--, lif = lif->lif_next) {
185d04ccbb3Scarlsonj uint32_t expire;
1867c478bd9Sstevel@tonic-gate
187d04ccbb3Scarlsonj expire = dsmp->dsm_curstart_monosec + lif->lif_expire.dt_start;
188d04ccbb3Scarlsonj if (expire > expiremax) {
189d04ccbb3Scarlsonj expiremax = expire;
190d04ccbb3Scarlsonj some_valid = B_TRUE;
1917c478bd9Sstevel@tonic-gate }
192d04ccbb3Scarlsonj }
193d04ccbb3Scarlsonj if (!some_valid) {
194d04ccbb3Scarlsonj dhcpmsg(MSG_DEBUG, "dhcp_rebind: all leases expired on %s",
195d04ccbb3Scarlsonj dsmp->dsm_name);
196d04ccbb3Scarlsonj release_lease(dlp);
1977c478bd9Sstevel@tonic-gate return;
1987c478bd9Sstevel@tonic-gate }
1997c478bd9Sstevel@tonic-gate
200d04ccbb3Scarlsonj /*
201d04ccbb3Scarlsonj * This is our first venture into the REBINDING state, so reset the
202d04ccbb3Scarlsonj * server address. We know the renew timer has already been cancelled
203d04ccbb3Scarlsonj * (or we wouldn't be here).
204d04ccbb3Scarlsonj */
205d04ccbb3Scarlsonj if (dsmp->dsm_isv6) {
206d04ccbb3Scarlsonj dsmp->dsm_server = ipv6_all_dhcp_relay_and_servers;
207d04ccbb3Scarlsonj } else {
208d04ccbb3Scarlsonj IN6_IPADDR_TO_V4MAPPED(htonl(INADDR_BROADCAST),
209d04ccbb3Scarlsonj &dsmp->dsm_server);
210d04ccbb3Scarlsonj }
211d04ccbb3Scarlsonj
212d04ccbb3Scarlsonj /* {Bound,Renew}->rebind transitions cannot fail */
213d04ccbb3Scarlsonj (void) set_smach_state(dsmp, REBINDING);
2147c478bd9Sstevel@tonic-gate
2157c478bd9Sstevel@tonic-gate /*
216d04ccbb3Scarlsonj * If there isn't an async event pending, or if we can cancel the one
217d04ccbb3Scarlsonj * that's there, then try to rebind by sending an extension request.
218d04ccbb3Scarlsonj * If that fails, we'll clean up when the lease expires.
2197c478bd9Sstevel@tonic-gate */
220d04ccbb3Scarlsonj if (!async_cancel(dsmp) || !async_start(dsmp, DHCP_EXTEND, B_FALSE) ||
221d04ccbb3Scarlsonj !dhcp_extending(dsmp)) {
222d04ccbb3Scarlsonj if (monosec() + RETRY_DELAY < expiremax) {
223d04ccbb3Scarlsonj /*
224d04ccbb3Scarlsonj * Try again in RETRY_DELAY seconds; user command
225d04ccbb3Scarlsonj * should be gone.
226d04ccbb3Scarlsonj */
227d04ccbb3Scarlsonj init_timer(&dlp->dl_t2, RETRY_DELAY);
228d04ccbb3Scarlsonj (void) set_smach_state(dsmp, oldstate);
229d04ccbb3Scarlsonj if (!schedule_lease_timer(dlp, &dlp->dl_t2,
230d04ccbb3Scarlsonj dhcp_rebind)) {
231d04ccbb3Scarlsonj dhcpmsg(MSG_INFO, "dhcp_rebind: unable to "
232d04ccbb3Scarlsonj "reschedule rebind around user command on "
233d04ccbb3Scarlsonj "%s; lease may expire", dsmp->dsm_name);
234d04ccbb3Scarlsonj }
235d04ccbb3Scarlsonj } else {
236d04ccbb3Scarlsonj dhcpmsg(MSG_WARNING, "dhcp_rebind: user busy on %s; "
237d04ccbb3Scarlsonj "will expire", dsmp->dsm_name);
238d04ccbb3Scarlsonj }
239d04ccbb3Scarlsonj }
240d04ccbb3Scarlsonj release_lease(dlp);
2417c478bd9Sstevel@tonic-gate }
2427c478bd9Sstevel@tonic-gate
2437c478bd9Sstevel@tonic-gate /*
244d04ccbb3Scarlsonj * dhcp_finish_expire(): finish expiration of a lease after the user script
245d04ccbb3Scarlsonj * runs. If this is the last lease, then restart DHCP.
246d04ccbb3Scarlsonj * The caller has a reference to the LIF, which will be
247d04ccbb3Scarlsonj * dropped.
2487c478bd9Sstevel@tonic-gate *
249d04ccbb3Scarlsonj * input: dhcp_smach_t *: the state machine to be restarted
250d04ccbb3Scarlsonj * void *: logical interface that has expired
2517c478bd9Sstevel@tonic-gate * output: int: always 1
2527c478bd9Sstevel@tonic-gate */
2537c478bd9Sstevel@tonic-gate
2547c478bd9Sstevel@tonic-gate static int
dhcp_finish_expire(dhcp_smach_t * dsmp,void * arg)255d04ccbb3Scarlsonj dhcp_finish_expire(dhcp_smach_t *dsmp, void *arg)
2567c478bd9Sstevel@tonic-gate {
257d04ccbb3Scarlsonj dhcp_lif_t *lif = arg;
258d04ccbb3Scarlsonj dhcp_lease_t *dlp;
259d04ccbb3Scarlsonj
260d04ccbb3Scarlsonj dhcpmsg(MSG_DEBUG, "lease expired on %s; removing", lif->lif_name);
261d04ccbb3Scarlsonj
262d04ccbb3Scarlsonj dlp = lif->lif_lease;
263d04ccbb3Scarlsonj unplumb_lif(lif);
264d04ccbb3Scarlsonj if (dlp->dl_nlifs == 0)
265d04ccbb3Scarlsonj remove_lease(dlp);
266d04ccbb3Scarlsonj release_lif(lif);
267d04ccbb3Scarlsonj
268d04ccbb3Scarlsonj /* If some valid leases remain, then drive on */
269d04ccbb3Scarlsonj if (dsmp->dsm_leases != NULL) {
270d04ccbb3Scarlsonj dhcpmsg(MSG_DEBUG,
271d04ccbb3Scarlsonj "dhcp_finish_expire: some leases remain on %s",
272d04ccbb3Scarlsonj dsmp->dsm_name);
273d04ccbb3Scarlsonj return (1);
274d04ccbb3Scarlsonj }
275d04ccbb3Scarlsonj
276*0a3e1f6cSVasumathi Sundaram (void) remove_hostconf(dsmp->dsm_name, dsmp->dsm_isv6);
277*0a3e1f6cSVasumathi Sundaram
278d04ccbb3Scarlsonj dhcpmsg(MSG_INFO, "last lease expired on %s -- restarting DHCP",
279d04ccbb3Scarlsonj dsmp->dsm_name);
2807c478bd9Sstevel@tonic-gate
2817c478bd9Sstevel@tonic-gate /*
2827c478bd9Sstevel@tonic-gate * in the case where the lease is less than DHCP_REBIND_MIN
2837c478bd9Sstevel@tonic-gate * seconds, we will never enter dhcp_renew() and thus the packet
2847c478bd9Sstevel@tonic-gate * counters will not be reset. in that case, reset them here.
2857c478bd9Sstevel@tonic-gate */
2867c478bd9Sstevel@tonic-gate
287d04ccbb3Scarlsonj if (dsmp->dsm_state == BOUND) {
288d04ccbb3Scarlsonj dsmp->dsm_bad_offers = 0;
289d04ccbb3Scarlsonj dsmp->dsm_sent = 0;
290d04ccbb3Scarlsonj dsmp->dsm_received = 0;
2917c478bd9Sstevel@tonic-gate }
2927c478bd9Sstevel@tonic-gate
293d04ccbb3Scarlsonj deprecate_leases(dsmp);
2947c478bd9Sstevel@tonic-gate
295d04ccbb3Scarlsonj /* reset_smach() in dhcp_selecting() will clean up any leftover state */
296d04ccbb3Scarlsonj dhcp_selecting(dsmp);
297d04ccbb3Scarlsonj
2987c478bd9Sstevel@tonic-gate return (1);
2997c478bd9Sstevel@tonic-gate }
3007c478bd9Sstevel@tonic-gate
3017c478bd9Sstevel@tonic-gate /*
302d04ccbb3Scarlsonj * dhcp_deprecate(): deprecates an address on a given logical interface when
303d04ccbb3Scarlsonj * the preferred lifetime expires.
3047c478bd9Sstevel@tonic-gate *
3057c478bd9Sstevel@tonic-gate * input: iu_tq_t *: unused
306d04ccbb3Scarlsonj * void *: the logical interface whose lease is expiring
307d04ccbb3Scarlsonj * output: void
308d04ccbb3Scarlsonj */
309d04ccbb3Scarlsonj
310d04ccbb3Scarlsonj /* ARGSUSED */
311d04ccbb3Scarlsonj void
dhcp_deprecate(iu_tq_t * tqp,void * arg)312d04ccbb3Scarlsonj dhcp_deprecate(iu_tq_t *tqp, void *arg)
313d04ccbb3Scarlsonj {
314d04ccbb3Scarlsonj dhcp_lif_t *lif = arg;
315d04ccbb3Scarlsonj
316d04ccbb3Scarlsonj set_lif_deprecated(lif);
317d04ccbb3Scarlsonj release_lif(lif);
318d04ccbb3Scarlsonj }
319d04ccbb3Scarlsonj
320d04ccbb3Scarlsonj /*
321d04ccbb3Scarlsonj * dhcp_expire(): expires a lease on a given logical interface and, if there
322d04ccbb3Scarlsonj * are no more leases, restarts DHCP.
323d04ccbb3Scarlsonj *
324d04ccbb3Scarlsonj * input: iu_tq_t *: unused
325d04ccbb3Scarlsonj * void *: the logical interface whose lease has expired
3267c478bd9Sstevel@tonic-gate * output: void
3277c478bd9Sstevel@tonic-gate */
3287c478bd9Sstevel@tonic-gate
3297c478bd9Sstevel@tonic-gate /* ARGSUSED */
3307c478bd9Sstevel@tonic-gate void
dhcp_expire(iu_tq_t * tqp,void * arg)3317c478bd9Sstevel@tonic-gate dhcp_expire(iu_tq_t *tqp, void *arg)
3327c478bd9Sstevel@tonic-gate {
333d04ccbb3Scarlsonj dhcp_lif_t *lif = arg;
334d04ccbb3Scarlsonj dhcp_smach_t *dsmp;
335d04ccbb3Scarlsonj const char *event;
3367c478bd9Sstevel@tonic-gate
337d04ccbb3Scarlsonj dhcpmsg(MSG_VERBOSE, "dhcp_expire: lease timer expired on %s",
338d04ccbb3Scarlsonj lif->lif_name);
3397c478bd9Sstevel@tonic-gate
340d04ccbb3Scarlsonj lif->lif_expire.dt_id = -1;
341d04ccbb3Scarlsonj if (lif->lif_lease == NULL) {
342d04ccbb3Scarlsonj release_lif(lif);
3437c478bd9Sstevel@tonic-gate return;
3447c478bd9Sstevel@tonic-gate }
3457c478bd9Sstevel@tonic-gate
346d04ccbb3Scarlsonj set_lif_deprecated(lif);
3477c478bd9Sstevel@tonic-gate
348d04ccbb3Scarlsonj dsmp = lif->lif_lease->dl_smach;
3497c478bd9Sstevel@tonic-gate
350d04ccbb3Scarlsonj if (!async_cancel(dsmp)) {
351d04ccbb3Scarlsonj
352d04ccbb3Scarlsonj dhcpmsg(MSG_WARNING,
353d04ccbb3Scarlsonj "dhcp_expire: cannot cancel current asynchronous command "
354d04ccbb3Scarlsonj "on %s", dsmp->dsm_name);
3557c478bd9Sstevel@tonic-gate
3567c478bd9Sstevel@tonic-gate /*
357d04ccbb3Scarlsonj * Try to schedule ourselves for callback. We're really
358d04ccbb3Scarlsonj * situation-critical here; there's not much hope for us if
359d04ccbb3Scarlsonj * this fails.
3607c478bd9Sstevel@tonic-gate */
361d04ccbb3Scarlsonj init_timer(&lif->lif_expire, DHCP_EXPIRE_WAIT);
362d04ccbb3Scarlsonj if (schedule_lif_timer(lif, &lif->lif_expire, dhcp_expire))
3637c478bd9Sstevel@tonic-gate return;
364d04ccbb3Scarlsonj
365d04ccbb3Scarlsonj dhcpmsg(MSG_CRIT, "dhcp_expire: cannot reschedule dhcp_expire "
366d04ccbb3Scarlsonj "to get called back, proceeding...");
3677c478bd9Sstevel@tonic-gate }
3687c478bd9Sstevel@tonic-gate
369d04ccbb3Scarlsonj if (!async_start(dsmp, DHCP_START, B_FALSE))
370d04ccbb3Scarlsonj dhcpmsg(MSG_WARNING, "dhcp_expire: cannot start asynchronous "
371d04ccbb3Scarlsonj "transaction on %s, continuing...", dsmp->dsm_name);
372d04ccbb3Scarlsonj
373d04ccbb3Scarlsonj /*
374d04ccbb3Scarlsonj * Determine if this state machine has any non-expired LIFs left in it.
375d04ccbb3Scarlsonj * If it doesn't, then this is an "expire" event. Otherwise, if some
376d04ccbb3Scarlsonj * valid leases remain, it's a "loss" event. The SOMEEXP case can
377d04ccbb3Scarlsonj * occur only with DHCPv6.
378d04ccbb3Scarlsonj */
379d04ccbb3Scarlsonj if (expired_lif_state(dsmp) == DHCP_EXP_SOMEEXP)
380d04ccbb3Scarlsonj event = EVENT_LOSS6;
381d04ccbb3Scarlsonj else if (dsmp->dsm_isv6)
382d04ccbb3Scarlsonj event = EVENT_EXPIRE6;
383d04ccbb3Scarlsonj else
384d04ccbb3Scarlsonj event = EVENT_EXPIRE;
3857c478bd9Sstevel@tonic-gate
3867c478bd9Sstevel@tonic-gate /*
3877c478bd9Sstevel@tonic-gate * just march on if this fails; at worst someone will be able
3887c478bd9Sstevel@tonic-gate * to async_start() while we're actually busy with our own
3897c478bd9Sstevel@tonic-gate * asynchronous transaction. better than not having a lease.
3907c478bd9Sstevel@tonic-gate */
3917c478bd9Sstevel@tonic-gate
392d04ccbb3Scarlsonj (void) script_start(dsmp, event, dhcp_finish_expire, lif, NULL);
3937c478bd9Sstevel@tonic-gate }
3947c478bd9Sstevel@tonic-gate
3957c478bd9Sstevel@tonic-gate /*
396d04ccbb3Scarlsonj * dhcp_extending(): sends a REQUEST (IPv4 DHCP) or Rebind/Renew (DHCPv6) to
397d04ccbb3Scarlsonj * extend a lease on a given state machine
3987c478bd9Sstevel@tonic-gate *
399d04ccbb3Scarlsonj * input: dhcp_smach_t *: the state machine to send the message from
400d04ccbb3Scarlsonj * output: boolean_t: B_TRUE if the extension request was sent
4017c478bd9Sstevel@tonic-gate */
4027c478bd9Sstevel@tonic-gate
403d04ccbb3Scarlsonj boolean_t
dhcp_extending(dhcp_smach_t * dsmp)404d04ccbb3Scarlsonj dhcp_extending(dhcp_smach_t *dsmp)
4057c478bd9Sstevel@tonic-gate {
4067c478bd9Sstevel@tonic-gate dhcp_pkt_t *dpkt;
4077c478bd9Sstevel@tonic-gate
408d04ccbb3Scarlsonj stop_pkt_retransmission(dsmp);
409d04ccbb3Scarlsonj
410d04ccbb3Scarlsonj /*
411d04ccbb3Scarlsonj * We change state here because this function is also called when
412d04ccbb3Scarlsonj * adopting a lease and on demand by the user.
413d04ccbb3Scarlsonj */
414d04ccbb3Scarlsonj if (dsmp->dsm_state == BOUND) {
415d04ccbb3Scarlsonj dsmp->dsm_neg_hrtime = gethrtime();
416d04ccbb3Scarlsonj dsmp->dsm_bad_offers = 0;
417d04ccbb3Scarlsonj dsmp->dsm_sent = 0;
418d04ccbb3Scarlsonj dsmp->dsm_received = 0;
419d04ccbb3Scarlsonj /* Bound->renew can't fail */
420d04ccbb3Scarlsonj (void) set_smach_state(dsmp, RENEWING);
4217c478bd9Sstevel@tonic-gate }
4227c478bd9Sstevel@tonic-gate
423d04ccbb3Scarlsonj dhcpmsg(MSG_DEBUG, "dhcp_extending: sending request on %s",
424d04ccbb3Scarlsonj dsmp->dsm_name);
4257c478bd9Sstevel@tonic-gate
426d04ccbb3Scarlsonj if (dsmp->dsm_isv6) {
427d04ccbb3Scarlsonj dhcp_lease_t *dlp;
428d04ccbb3Scarlsonj dhcp_lif_t *lif;
429d04ccbb3Scarlsonj uint_t nlifs;
430d04ccbb3Scarlsonj uint_t irt, mrt;
4317c478bd9Sstevel@tonic-gate
432d04ccbb3Scarlsonj /*
433d04ccbb3Scarlsonj * Start constructing the Renew/Rebind message. Only Renew has
434d04ccbb3Scarlsonj * a server ID, as we still think our server might be
435d04ccbb3Scarlsonj * reachable.
436d04ccbb3Scarlsonj */
437d04ccbb3Scarlsonj if (dsmp->dsm_state == RENEWING) {
438d04ccbb3Scarlsonj dpkt = init_pkt(dsmp, DHCPV6_MSG_RENEW);
439d04ccbb3Scarlsonj (void) add_pkt_opt(dpkt, DHCPV6_OPT_SERVERID,
440d04ccbb3Scarlsonj dsmp->dsm_serverid, dsmp->dsm_serveridlen);
441d04ccbb3Scarlsonj irt = DHCPV6_REN_TIMEOUT;
442d04ccbb3Scarlsonj mrt = DHCPV6_REN_MAX_RT;
443d04ccbb3Scarlsonj } else {
444d04ccbb3Scarlsonj dpkt = init_pkt(dsmp, DHCPV6_MSG_REBIND);
445d04ccbb3Scarlsonj irt = DHCPV6_REB_TIMEOUT;
446d04ccbb3Scarlsonj mrt = DHCPV6_REB_MAX_RT;
4477c478bd9Sstevel@tonic-gate }
4487c478bd9Sstevel@tonic-gate
4497c478bd9Sstevel@tonic-gate /*
450d04ccbb3Scarlsonj * Loop over the leases, and add an IA_NA for each and an
451d04ccbb3Scarlsonj * IAADDR for each address.
4527c478bd9Sstevel@tonic-gate */
453d04ccbb3Scarlsonj for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
454d04ccbb3Scarlsonj lif = dlp->dl_lifs;
455d04ccbb3Scarlsonj for (nlifs = dlp->dl_nlifs; nlifs > 0;
456d04ccbb3Scarlsonj nlifs--, lif = lif->lif_next) {
457d04ccbb3Scarlsonj (void) add_pkt_lif(dpkt, lif,
458d04ccbb3Scarlsonj DHCPV6_STAT_SUCCESS, NULL);
4597c478bd9Sstevel@tonic-gate }
460d04ccbb3Scarlsonj }
461d04ccbb3Scarlsonj
462d04ccbb3Scarlsonj /* Add required Option Request option */
463d04ccbb3Scarlsonj (void) add_pkt_prl(dpkt, dsmp);
464d04ccbb3Scarlsonj
465d04ccbb3Scarlsonj return (send_pkt_v6(dsmp, dpkt, dsmp->dsm_server,
466d04ccbb3Scarlsonj stop_extending, irt, mrt));
467d04ccbb3Scarlsonj } else {
468d04ccbb3Scarlsonj dhcp_lif_t *lif = dsmp->dsm_lif;
469d04ccbb3Scarlsonj ipaddr_t server;
470d04ccbb3Scarlsonj
471d04ccbb3Scarlsonj /* assemble the DHCPREQUEST message. */
472d04ccbb3Scarlsonj dpkt = init_pkt(dsmp, REQUEST);
473d04ccbb3Scarlsonj dpkt->pkt->ciaddr.s_addr = lif->lif_addr;
4747c478bd9Sstevel@tonic-gate
4757c478bd9Sstevel@tonic-gate /*
476d04ccbb3Scarlsonj * The max dhcp message size option is set to the interface
477d04ccbb3Scarlsonj * max, minus the size of the udp and ip headers.
478d04ccbb3Scarlsonj */
479d04ccbb3Scarlsonj (void) add_pkt_opt16(dpkt, CD_MAX_DHCP_SIZE,
480d04ccbb3Scarlsonj htons(lif->lif_max - sizeof (struct udpiphdr)));
481d04ccbb3Scarlsonj (void) add_pkt_opt32(dpkt, CD_LEASE_TIME, htonl(DHCP_PERM));
482d04ccbb3Scarlsonj
483f4b3ec61Sdh155122 if (class_id_len != 0) {
484f4b3ec61Sdh155122 (void) add_pkt_opt(dpkt, CD_CLASS_ID, class_id,
485f4b3ec61Sdh155122 class_id_len);
486f4b3ec61Sdh155122 }
487d04ccbb3Scarlsonj (void) add_pkt_prl(dpkt, dsmp);
488d04ccbb3Scarlsonj /*
489d04ccbb3Scarlsonj * dsm_reqhost was set for this state machine in
490d04ccbb3Scarlsonj * dhcp_selecting() if the REQUEST_HOSTNAME option was set and
491d04ccbb3Scarlsonj * a host name was found.
492d04ccbb3Scarlsonj */
493d04ccbb3Scarlsonj if (dsmp->dsm_reqhost != NULL) {
494d04ccbb3Scarlsonj (void) add_pkt_opt(dpkt, CD_HOSTNAME, dsmp->dsm_reqhost,
495d04ccbb3Scarlsonj strlen(dsmp->dsm_reqhost));
496d04ccbb3Scarlsonj }
497d04ccbb3Scarlsonj (void) add_pkt_opt(dpkt, CD_END, NULL, 0);
498d04ccbb3Scarlsonj
499d04ccbb3Scarlsonj IN6_V4MAPPED_TO_IPADDR(&dsmp->dsm_server, server);
500d04ccbb3Scarlsonj return (send_pkt(dsmp, dpkt, server, stop_extending));
501d04ccbb3Scarlsonj }
502d04ccbb3Scarlsonj }
503d04ccbb3Scarlsonj
504d04ccbb3Scarlsonj /*
505d04ccbb3Scarlsonj * stop_extending(): decides when to stop retransmitting v4 REQUEST or v6
506d04ccbb3Scarlsonj * Renew/Rebind messages. If we're renewing, then stop if
507d04ccbb3Scarlsonj * T2 is soon approaching.
508d04ccbb3Scarlsonj *
509d04ccbb3Scarlsonj * input: dhcp_smach_t *: the state machine REQUESTs are being sent from
510d04ccbb3Scarlsonj * unsigned int: the number of REQUESTs sent so far
511d04ccbb3Scarlsonj * output: boolean_t: B_TRUE if retransmissions should stop
5127c478bd9Sstevel@tonic-gate */
5137c478bd9Sstevel@tonic-gate
514d04ccbb3Scarlsonj /* ARGSUSED */
515d04ccbb3Scarlsonj static boolean_t
stop_extending(dhcp_smach_t * dsmp,unsigned int n_requests)516d04ccbb3Scarlsonj stop_extending(dhcp_smach_t *dsmp, unsigned int n_requests)
517d04ccbb3Scarlsonj {
518d04ccbb3Scarlsonj dhcp_lease_t *dlp;
519d04ccbb3Scarlsonj
520d04ccbb3Scarlsonj /*
521d04ccbb3Scarlsonj * If we're renewing and rebind time is soon approaching, then don't
522d04ccbb3Scarlsonj * schedule
523d04ccbb3Scarlsonj */
524d04ccbb3Scarlsonj if (dsmp->dsm_state == RENEWING) {
525d04ccbb3Scarlsonj monosec_t t2;
526d04ccbb3Scarlsonj
527d04ccbb3Scarlsonj t2 = 0;
528d04ccbb3Scarlsonj for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
529d04ccbb3Scarlsonj if (dlp->dl_t2.dt_start > t2)
530d04ccbb3Scarlsonj t2 = dlp->dl_t2.dt_start;
531d04ccbb3Scarlsonj }
532d04ccbb3Scarlsonj t2 += dsmp->dsm_curstart_monosec;
533d04ccbb3Scarlsonj if (monosec() + TOO_CLOSE >= t2) {
534d04ccbb3Scarlsonj dhcpmsg(MSG_DEBUG, "stop_extending: %spast T2 on %s",
535d04ccbb3Scarlsonj monosec() > t2 ? "" : "almost ", dsmp->dsm_name);
536d04ccbb3Scarlsonj return (B_TRUE);
537d04ccbb3Scarlsonj }
538d04ccbb3Scarlsonj }
539d04ccbb3Scarlsonj
540d04ccbb3Scarlsonj /*
541d04ccbb3Scarlsonj * Note that returning B_TRUE cancels both this transmission and the
542d04ccbb3Scarlsonj * one that would occur at dsm_send_timeout, and that for v4 we cut the
543d04ccbb3Scarlsonj * time in half for each retransmission. Thus we check here against
544d04ccbb3Scarlsonj * half of the minimum.
545d04ccbb3Scarlsonj */
546d04ccbb3Scarlsonj if (!dsmp->dsm_isv6 &&
547d04ccbb3Scarlsonj dsmp->dsm_send_timeout < DHCP_REBIND_MIN * MILLISEC / 2) {
548d04ccbb3Scarlsonj dhcpmsg(MSG_DEBUG, "stop_extending: next retry would be in "
549d04ccbb3Scarlsonj "%d.%03d; stopping", dsmp->dsm_send_timeout / MILLISEC,
550d04ccbb3Scarlsonj dsmp->dsm_send_timeout % MILLISEC);
551d04ccbb3Scarlsonj return (B_TRUE);
552d04ccbb3Scarlsonj }
553d04ccbb3Scarlsonj
554d04ccbb3Scarlsonj /* Otherwise, w stop only when the next timer (rebind, expire) fires */
555d04ccbb3Scarlsonj return (B_FALSE);
5567c478bd9Sstevel@tonic-gate }
557