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 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 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 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 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 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 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 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