4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
25 #include <sys/types.h>
27 #include <netinet/in.h>
28 #include <netinet/dhcp.h>
29 #include <netinet/udp.h>
30 #include <netinet/ip_var.h>
31 #include <netinet/udp_var.h>
32 #include <libinetutil.h>
34 #include <dhcp_hostconf.h>
39 #include "script_handler.h"
40 #include "interface.h"
45 * Number of seconds to wait for a retry if the user is interacting with the
48 #define RETRY_DELAY 10
51 * If the renew timer fires within this number of seconds of the rebind timer,
52 * then skip renew. This prevents us from sending back-to-back renew and
53 * rebind messages -- a pointless activity.
57 static boolean_t
stop_extending(dhcp_smach_t
*, unsigned int);
60 * dhcp_renew(): attempts to renew a DHCP lease on expiration of the T1 timer.
62 * input: iu_tq_t *: unused
63 * void *: the lease to renew (dhcp_lease_t)
66 * notes: The primary expense involved with DHCP (like most UDP protocols) is
67 * with the generation and handling of packets, not the contents of
68 * those packets. Thus, we try to reduce the number of packets that
69 * are sent. It would be nice to just renew all leases here (each one
70 * added has trivial added overhead), but the DHCPv6 RFC doesn't
71 * explicitly allow that behavior. Rather than having that argument,
72 * we settle for ones that are close in expiry to the one that fired.
73 * For v4, we repeatedly reschedule the T1 timer to do the
74 * retransmissions. For v6, we rely on the common timer computation
80 dhcp_renew(iu_tq_t
*tqp
, void *arg
)
82 dhcp_lease_t
*dlp
= arg
;
83 dhcp_smach_t
*dsmp
= dlp
->dl_smach
;
86 dhcpmsg(MSG_VERBOSE
, "dhcp_renew: T1 timer expired on %s",
89 dlp
->dl_t1
.dt_id
= -1;
91 if (dsmp
->dsm_state
== RENEWING
|| dsmp
->dsm_state
== REBINDING
) {
92 dhcpmsg(MSG_DEBUG
, "dhcp_renew: already renewing");
98 * Sanity check: don't send packets if we're past T2, or if we're
102 t2
= dsmp
->dsm_curstart_monosec
+ dlp
->dl_t2
.dt_start
;
103 if (monosec() + TOO_CLOSE
>= t2
) {
104 dhcpmsg(MSG_DEBUG
, "dhcp_renew: %spast T2 on %s",
105 monosec() > t2
? "" : "almost ", dsmp
->dsm_name
);
111 * If there isn't an async event pending, or if we can cancel the one
112 * that's there, then try to renew by sending an extension request. If
113 * that fails, we'll try again when the next timer fires.
115 if (!async_cancel(dsmp
) || !async_start(dsmp
, DHCP_EXTEND
, B_FALSE
) ||
116 !dhcp_extending(dsmp
)) {
117 if (monosec() + RETRY_DELAY
< t2
) {
119 * Try again in RETRY_DELAY seconds; user command
122 init_timer(&dlp
->dl_t1
, RETRY_DELAY
);
123 (void) set_smach_state(dsmp
, BOUND
);
124 if (!schedule_lease_timer(dlp
, &dlp
->dl_t1
,
126 dhcpmsg(MSG_INFO
, "dhcp_renew: unable to "
127 "reschedule renewal around user command "
128 "on %s; will wait for rebind",
132 dhcpmsg(MSG_DEBUG
, "dhcp_renew: user busy on %s; will "
133 "wait for rebind", dsmp
->dsm_name
);
140 * dhcp_rebind(): attempts to renew a DHCP lease from the REBINDING state (T2
143 * input: iu_tq_t *: unused
144 * void *: the lease to renew
146 * notes: For v4, we repeatedly reschedule the T2 timer to do the
147 * retransmissions. For v6, we rely on the common timer computation
153 dhcp_rebind(iu_tq_t
*tqp
, void *arg
)
155 dhcp_lease_t
*dlp
= arg
;
156 dhcp_smach_t
*dsmp
= dlp
->dl_smach
;
159 boolean_t some_valid
;
163 dhcpmsg(MSG_VERBOSE
, "dhcp_rebind: T2 timer expired on %s",
166 dlp
->dl_t2
.dt_id
= -1;
168 if ((oldstate
= dsmp
->dsm_state
) == REBINDING
) {
169 dhcpmsg(MSG_DEBUG
, "dhcp_renew: already rebinding");
175 * Sanity check: don't send packets if we've already expired on all of
176 * the addresses. We compute the maximum expiration time here, because
177 * it won't matter for v4 (there's only one lease) and for v6 we need
178 * to know when the last lease ages away.
181 some_valid
= B_FALSE
;
182 expiremax
= monosec();
184 for (nlifs
= dlp
->dl_nlifs
; nlifs
> 0; nlifs
--, lif
= lif
->lif_next
) {
187 expire
= dsmp
->dsm_curstart_monosec
+ lif
->lif_expire
.dt_start
;
188 if (expire
> expiremax
) {
194 dhcpmsg(MSG_DEBUG
, "dhcp_rebind: all leases expired on %s",
201 * This is our first venture into the REBINDING state, so reset the
202 * server address. We know the renew timer has already been cancelled
203 * (or we wouldn't be here).
205 if (dsmp
->dsm_isv6
) {
206 dsmp
->dsm_server
= ipv6_all_dhcp_relay_and_servers
;
208 IN6_IPADDR_TO_V4MAPPED(htonl(INADDR_BROADCAST
),
212 /* {Bound,Renew}->rebind transitions cannot fail */
213 (void) set_smach_state(dsmp
, REBINDING
);
216 * If there isn't an async event pending, or if we can cancel the one
217 * that's there, then try to rebind by sending an extension request.
218 * If that fails, we'll clean up when the lease expires.
220 if (!async_cancel(dsmp
) || !async_start(dsmp
, DHCP_EXTEND
, B_FALSE
) ||
221 !dhcp_extending(dsmp
)) {
222 if (monosec() + RETRY_DELAY
< expiremax
) {
224 * Try again in RETRY_DELAY seconds; user command
227 init_timer(&dlp
->dl_t2
, RETRY_DELAY
);
228 (void) set_smach_state(dsmp
, oldstate
);
229 if (!schedule_lease_timer(dlp
, &dlp
->dl_t2
,
231 dhcpmsg(MSG_INFO
, "dhcp_rebind: unable to "
232 "reschedule rebind around user command on "
233 "%s; lease may expire", dsmp
->dsm_name
);
236 dhcpmsg(MSG_WARNING
, "dhcp_rebind: user busy on %s; "
237 "will expire", dsmp
->dsm_name
);
244 * dhcp_finish_expire(): finish expiration of a lease after the user script
245 * runs. If this is the last lease, then restart DHCP.
246 * The caller has a reference to the LIF, which will be
249 * input: dhcp_smach_t *: the state machine to be restarted
250 * void *: logical interface that has expired
251 * output: int: always 1
255 dhcp_finish_expire(dhcp_smach_t
*dsmp
, void *arg
)
257 dhcp_lif_t
*lif
= arg
;
260 dhcpmsg(MSG_DEBUG
, "lease expired on %s; removing", lif
->lif_name
);
262 dlp
= lif
->lif_lease
;
264 if (dlp
->dl_nlifs
== 0)
268 /* If some valid leases remain, then drive on */
269 if (dsmp
->dsm_leases
!= NULL
) {
271 "dhcp_finish_expire: some leases remain on %s",
276 (void) remove_hostconf(dsmp
->dsm_name
, dsmp
->dsm_isv6
);
278 dhcpmsg(MSG_INFO
, "last lease expired on %s -- restarting DHCP",
282 * in the case where the lease is less than DHCP_REBIND_MIN
283 * seconds, we will never enter dhcp_renew() and thus the packet
284 * counters will not be reset. in that case, reset them here.
287 if (dsmp
->dsm_state
== BOUND
) {
288 dsmp
->dsm_bad_offers
= 0;
290 dsmp
->dsm_received
= 0;
293 deprecate_leases(dsmp
);
295 /* reset_smach() in dhcp_selecting() will clean up any leftover state */
296 dhcp_selecting(dsmp
);
302 * dhcp_deprecate(): deprecates an address on a given logical interface when
303 * the preferred lifetime expires.
305 * input: iu_tq_t *: unused
306 * void *: the logical interface whose lease is expiring
312 dhcp_deprecate(iu_tq_t
*tqp
, void *arg
)
314 dhcp_lif_t
*lif
= arg
;
316 set_lif_deprecated(lif
);
321 * dhcp_expire(): expires a lease on a given logical interface and, if there
322 * are no more leases, restarts DHCP.
324 * input: iu_tq_t *: unused
325 * void *: the logical interface whose lease has expired
331 dhcp_expire(iu_tq_t
*tqp
, void *arg
)
333 dhcp_lif_t
*lif
= arg
;
337 dhcpmsg(MSG_VERBOSE
, "dhcp_expire: lease timer expired on %s",
340 lif
->lif_expire
.dt_id
= -1;
341 if (lif
->lif_lease
== NULL
) {
346 set_lif_deprecated(lif
);
348 dsmp
= lif
->lif_lease
->dl_smach
;
350 if (!async_cancel(dsmp
)) {
353 "dhcp_expire: cannot cancel current asynchronous command "
354 "on %s", dsmp
->dsm_name
);
357 * Try to schedule ourselves for callback. We're really
358 * situation-critical here; there's not much hope for us if
361 init_timer(&lif
->lif_expire
, DHCP_EXPIRE_WAIT
);
362 if (schedule_lif_timer(lif
, &lif
->lif_expire
, dhcp_expire
))
365 dhcpmsg(MSG_CRIT
, "dhcp_expire: cannot reschedule dhcp_expire "
366 "to get called back, proceeding...");
369 if (!async_start(dsmp
, DHCP_START
, B_FALSE
))
370 dhcpmsg(MSG_WARNING
, "dhcp_expire: cannot start asynchronous "
371 "transaction on %s, continuing...", dsmp
->dsm_name
);
374 * Determine if this state machine has any non-expired LIFs left in it.
375 * If it doesn't, then this is an "expire" event. Otherwise, if some
376 * valid leases remain, it's a "loss" event. The SOMEEXP case can
377 * occur only with DHCPv6.
379 if (expired_lif_state(dsmp
) == DHCP_EXP_SOMEEXP
)
381 else if (dsmp
->dsm_isv6
)
382 event
= EVENT_EXPIRE6
;
384 event
= EVENT_EXPIRE
;
387 * just march on if this fails; at worst someone will be able
388 * to async_start() while we're actually busy with our own
389 * asynchronous transaction. better than not having a lease.
392 (void) script_start(dsmp
, event
, dhcp_finish_expire
, lif
, NULL
);
396 * dhcp_extending(): sends a REQUEST (IPv4 DHCP) or Rebind/Renew (DHCPv6) to
397 * extend a lease on a given state machine
399 * input: dhcp_smach_t *: the state machine to send the message from
400 * output: boolean_t: B_TRUE if the extension request was sent
404 dhcp_extending(dhcp_smach_t
*dsmp
)
408 stop_pkt_retransmission(dsmp
);
411 * We change state here because this function is also called when
412 * adopting a lease and on demand by the user.
414 if (dsmp
->dsm_state
== BOUND
) {
415 dsmp
->dsm_neg_hrtime
= gethrtime();
416 dsmp
->dsm_bad_offers
= 0;
418 dsmp
->dsm_received
= 0;
419 /* Bound->renew can't fail */
420 (void) set_smach_state(dsmp
, RENEWING
);
423 dhcpmsg(MSG_DEBUG
, "dhcp_extending: sending request on %s",
426 if (dsmp
->dsm_isv6
) {
433 * Start constructing the Renew/Rebind message. Only Renew has
434 * a server ID, as we still think our server might be
437 if (dsmp
->dsm_state
== RENEWING
) {
438 dpkt
= init_pkt(dsmp
, DHCPV6_MSG_RENEW
);
439 (void) add_pkt_opt(dpkt
, DHCPV6_OPT_SERVERID
,
440 dsmp
->dsm_serverid
, dsmp
->dsm_serveridlen
);
441 irt
= DHCPV6_REN_TIMEOUT
;
442 mrt
= DHCPV6_REN_MAX_RT
;
444 dpkt
= init_pkt(dsmp
, DHCPV6_MSG_REBIND
);
445 irt
= DHCPV6_REB_TIMEOUT
;
446 mrt
= DHCPV6_REB_MAX_RT
;
450 * Loop over the leases, and add an IA_NA for each and an
451 * IAADDR for each address.
453 for (dlp
= dsmp
->dsm_leases
; dlp
!= NULL
; dlp
= dlp
->dl_next
) {
455 for (nlifs
= dlp
->dl_nlifs
; nlifs
> 0;
456 nlifs
--, lif
= lif
->lif_next
) {
457 (void) add_pkt_lif(dpkt
, lif
,
458 DHCPV6_STAT_SUCCESS
, NULL
);
462 /* Add required Option Request option */
463 (void) add_pkt_prl(dpkt
, dsmp
);
465 return (send_pkt_v6(dsmp
, dpkt
, dsmp
->dsm_server
,
466 stop_extending
, irt
, mrt
));
468 dhcp_lif_t
*lif
= dsmp
->dsm_lif
;
471 /* assemble the DHCPREQUEST message. */
472 dpkt
= init_pkt(dsmp
, REQUEST
);
473 dpkt
->pkt
->ciaddr
.s_addr
= lif
->lif_addr
;
476 * The max dhcp message size option is set to the interface
477 * max, minus the size of the udp and ip headers.
479 (void) add_pkt_opt16(dpkt
, CD_MAX_DHCP_SIZE
,
480 htons(lif
->lif_max
- sizeof (struct udpiphdr
)));
481 (void) add_pkt_opt32(dpkt
, CD_LEASE_TIME
, htonl(DHCP_PERM
));
483 if (class_id_len
!= 0) {
484 (void) add_pkt_opt(dpkt
, CD_CLASS_ID
, class_id
,
487 (void) add_pkt_prl(dpkt
, dsmp
);
489 * dsm_reqhost was set for this state machine in
490 * dhcp_selecting() if the REQUEST_HOSTNAME option was set and
491 * a host name was found.
493 if (dsmp
->dsm_reqhost
!= NULL
) {
494 (void) add_pkt_opt(dpkt
, CD_HOSTNAME
, dsmp
->dsm_reqhost
,
495 strlen(dsmp
->dsm_reqhost
));
497 (void) add_pkt_opt(dpkt
, CD_END
, NULL
, 0);
499 IN6_V4MAPPED_TO_IPADDR(&dsmp
->dsm_server
, server
);
500 return (send_pkt(dsmp
, dpkt
, server
, stop_extending
));
505 * stop_extending(): decides when to stop retransmitting v4 REQUEST or v6
506 * Renew/Rebind messages. If we're renewing, then stop if
507 * T2 is soon approaching.
509 * input: dhcp_smach_t *: the state machine REQUESTs are being sent from
510 * unsigned int: the number of REQUESTs sent so far
511 * output: boolean_t: B_TRUE if retransmissions should stop
516 stop_extending(dhcp_smach_t
*dsmp
, unsigned int n_requests
)
521 * If we're renewing and rebind time is soon approaching, then don't
524 if (dsmp
->dsm_state
== RENEWING
) {
528 for (dlp
= dsmp
->dsm_leases
; dlp
!= NULL
; dlp
= dlp
->dl_next
) {
529 if (dlp
->dl_t2
.dt_start
> t2
)
530 t2
= dlp
->dl_t2
.dt_start
;
532 t2
+= dsmp
->dsm_curstart_monosec
;
533 if (monosec() + TOO_CLOSE
>= t2
) {
534 dhcpmsg(MSG_DEBUG
, "stop_extending: %spast T2 on %s",
535 monosec() > t2
? "" : "almost ", dsmp
->dsm_name
);
541 * Note that returning B_TRUE cancels both this transmission and the
542 * one that would occur at dsm_send_timeout, and that for v4 we cut the
543 * time in half for each retransmission. Thus we check here against
544 * half of the minimum.
546 if (!dsmp
->dsm_isv6
&&
547 dsmp
->dsm_send_timeout
< DHCP_REBIND_MIN
* MILLISEC
/ 2) {
548 dhcpmsg(MSG_DEBUG
, "stop_extending: next retry would be in "
549 "%d.%03d; stopping", dsmp
->dsm_send_timeout
/ MILLISEC
,
550 dsmp
->dsm_send_timeout
% MILLISEC
);
554 /* Otherwise, w stop only when the next timer (rebind, expire) fires */