8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / cmd-inet / sbin / dhcpagent / renew.c
blob8d0c46c8a5c8c86fd50627f654e6a4b18061c044
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
22 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
25 #include <sys/types.h>
26 #include <time.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>
33 #include <dhcpmsg.h>
34 #include <dhcp_hostconf.h>
35 #include <string.h>
37 #include "packet.h"
38 #include "agent.h"
39 #include "script_handler.h"
40 #include "interface.h"
41 #include "states.h"
42 #include "util.h"
45 * Number of seconds to wait for a retry if the user is interacting with the
46 * daemon.
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.
55 #define TOO_CLOSE 2
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)
64 * output: void
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
75 * in packet.c.
78 /* ARGSUSED */
79 void
80 dhcp_renew(iu_tq_t *tqp, void *arg)
82 dhcp_lease_t *dlp = arg;
83 dhcp_smach_t *dsmp = dlp->dl_smach;
84 uint32_t t2;
86 dhcpmsg(MSG_VERBOSE, "dhcp_renew: T1 timer expired on %s",
87 dsmp->dsm_name);
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");
93 release_lease(dlp);
94 return;
98 * Sanity check: don't send packets if we're past T2, or if we're
99 * extremely close.
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);
106 release_lease(dlp);
107 return;
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
120 * should be gone.
122 init_timer(&dlp->dl_t1, RETRY_DELAY);
123 (void) set_smach_state(dsmp, BOUND);
124 if (!schedule_lease_timer(dlp, &dlp->dl_t1,
125 dhcp_renew)) {
126 dhcpmsg(MSG_INFO, "dhcp_renew: unable to "
127 "reschedule renewal around user command "
128 "on %s; will wait for rebind",
129 dsmp->dsm_name);
131 } else {
132 dhcpmsg(MSG_DEBUG, "dhcp_renew: user busy on %s; will "
133 "wait for rebind", dsmp->dsm_name);
136 release_lease(dlp);
140 * dhcp_rebind(): attempts to renew a DHCP lease from the REBINDING state (T2
141 * timer expiry).
143 * input: iu_tq_t *: unused
144 * void *: the lease to renew
145 * output: void
146 * notes: For v4, we repeatedly reschedule the T2 timer to do the
147 * retransmissions. For v6, we rely on the common timer computation
148 * in packet.c.
151 /* ARGSUSED */
152 void
153 dhcp_rebind(iu_tq_t *tqp, void *arg)
155 dhcp_lease_t *dlp = arg;
156 dhcp_smach_t *dsmp = dlp->dl_smach;
157 int nlifs;
158 dhcp_lif_t *lif;
159 boolean_t some_valid;
160 uint32_t expiremax;
161 DHCPSTATE oldstate;
163 dhcpmsg(MSG_VERBOSE, "dhcp_rebind: T2 timer expired on %s",
164 dsmp->dsm_name);
166 dlp->dl_t2.dt_id = -1;
168 if ((oldstate = dsmp->dsm_state) == REBINDING) {
169 dhcpmsg(MSG_DEBUG, "dhcp_renew: already rebinding");
170 release_lease(dlp);
171 return;
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();
183 lif = dlp->dl_lifs;
184 for (nlifs = dlp->dl_nlifs; nlifs > 0; nlifs--, lif = lif->lif_next) {
185 uint32_t expire;
187 expire = dsmp->dsm_curstart_monosec + lif->lif_expire.dt_start;
188 if (expire > expiremax) {
189 expiremax = expire;
190 some_valid = B_TRUE;
193 if (!some_valid) {
194 dhcpmsg(MSG_DEBUG, "dhcp_rebind: all leases expired on %s",
195 dsmp->dsm_name);
196 release_lease(dlp);
197 return;
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;
207 } else {
208 IN6_IPADDR_TO_V4MAPPED(htonl(INADDR_BROADCAST),
209 &dsmp->dsm_server);
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
225 * should be gone.
227 init_timer(&dlp->dl_t2, RETRY_DELAY);
228 (void) set_smach_state(dsmp, oldstate);
229 if (!schedule_lease_timer(dlp, &dlp->dl_t2,
230 dhcp_rebind)) {
231 dhcpmsg(MSG_INFO, "dhcp_rebind: unable to "
232 "reschedule rebind around user command on "
233 "%s; lease may expire", dsmp->dsm_name);
235 } else {
236 dhcpmsg(MSG_WARNING, "dhcp_rebind: user busy on %s; "
237 "will expire", dsmp->dsm_name);
240 release_lease(dlp);
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
247 * dropped.
249 * input: dhcp_smach_t *: the state machine to be restarted
250 * void *: logical interface that has expired
251 * output: int: always 1
254 static int
255 dhcp_finish_expire(dhcp_smach_t *dsmp, void *arg)
257 dhcp_lif_t *lif = arg;
258 dhcp_lease_t *dlp;
260 dhcpmsg(MSG_DEBUG, "lease expired on %s; removing", lif->lif_name);
262 dlp = lif->lif_lease;
263 unplumb_lif(lif);
264 if (dlp->dl_nlifs == 0)
265 remove_lease(dlp);
266 release_lif(lif);
268 /* If some valid leases remain, then drive on */
269 if (dsmp->dsm_leases != NULL) {
270 dhcpmsg(MSG_DEBUG,
271 "dhcp_finish_expire: some leases remain on %s",
272 dsmp->dsm_name);
273 return (1);
276 (void) remove_hostconf(dsmp->dsm_name, dsmp->dsm_isv6);
278 dhcpmsg(MSG_INFO, "last lease expired on %s -- restarting DHCP",
279 dsmp->dsm_name);
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;
289 dsmp->dsm_sent = 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);
298 return (1);
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
307 * output: void
310 /* ARGSUSED */
311 void
312 dhcp_deprecate(iu_tq_t *tqp, void *arg)
314 dhcp_lif_t *lif = arg;
316 set_lif_deprecated(lif);
317 release_lif(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
326 * output: void
329 /* ARGSUSED */
330 void
331 dhcp_expire(iu_tq_t *tqp, void *arg)
333 dhcp_lif_t *lif = arg;
334 dhcp_smach_t *dsmp;
335 const char *event;
337 dhcpmsg(MSG_VERBOSE, "dhcp_expire: lease timer expired on %s",
338 lif->lif_name);
340 lif->lif_expire.dt_id = -1;
341 if (lif->lif_lease == NULL) {
342 release_lif(lif);
343 return;
346 set_lif_deprecated(lif);
348 dsmp = lif->lif_lease->dl_smach;
350 if (!async_cancel(dsmp)) {
352 dhcpmsg(MSG_WARNING,
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
359 * this fails.
361 init_timer(&lif->lif_expire, DHCP_EXPIRE_WAIT);
362 if (schedule_lif_timer(lif, &lif->lif_expire, dhcp_expire))
363 return;
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)
380 event = EVENT_LOSS6;
381 else if (dsmp->dsm_isv6)
382 event = EVENT_EXPIRE6;
383 else
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
403 boolean_t
404 dhcp_extending(dhcp_smach_t *dsmp)
406 dhcp_pkt_t *dpkt;
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;
417 dsmp->dsm_sent = 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",
424 dsmp->dsm_name);
426 if (dsmp->dsm_isv6) {
427 dhcp_lease_t *dlp;
428 dhcp_lif_t *lif;
429 uint_t nlifs;
430 uint_t irt, mrt;
433 * Start constructing the Renew/Rebind message. Only Renew has
434 * a server ID, as we still think our server might be
435 * reachable.
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;
443 } else {
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) {
454 lif = dlp->dl_lifs;
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));
467 } else {
468 dhcp_lif_t *lif = dsmp->dsm_lif;
469 ipaddr_t server;
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,
485 class_id_len);
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
514 /* ARGSUSED */
515 static boolean_t
516 stop_extending(dhcp_smach_t *dsmp, unsigned int n_requests)
518 dhcp_lease_t *dlp;
521 * If we're renewing and rebind time is soon approaching, then don't
522 * schedule
524 if (dsmp->dsm_state == RENEWING) {
525 monosec_t t2;
527 t2 = 0;
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);
536 return (B_TRUE);
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);
551 return (B_TRUE);
554 /* Otherwise, w stop only when the next timer (rebind, expire) fires */
555 return (B_FALSE);