10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
52546Scarlsonj * Common Development and Distribution License (the "License").
62546Scarlsonj * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
210Sstevel@tonic-gate /*
22*12989SVasumathi.Sundaram@oracle.COM * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
230Sstevel@tonic-gate */
240Sstevel@tonic-gate
250Sstevel@tonic-gate #include <sys/types.h>
260Sstevel@tonic-gate #include <time.h>
270Sstevel@tonic-gate #include <netinet/in.h>
280Sstevel@tonic-gate #include <netinet/dhcp.h>
290Sstevel@tonic-gate #include <netinet/udp.h>
300Sstevel@tonic-gate #include <netinet/ip_var.h>
310Sstevel@tonic-gate #include <netinet/udp_var.h>
320Sstevel@tonic-gate #include <libinetutil.h>
330Sstevel@tonic-gate #include <dhcpmsg.h>
34*12989SVasumathi.Sundaram@oracle.COM #include <dhcp_hostconf.h>
350Sstevel@tonic-gate #include <string.h>
360Sstevel@tonic-gate
370Sstevel@tonic-gate #include "packet.h"
380Sstevel@tonic-gate #include "agent.h"
390Sstevel@tonic-gate #include "script_handler.h"
400Sstevel@tonic-gate #include "interface.h"
410Sstevel@tonic-gate #include "states.h"
420Sstevel@tonic-gate #include "util.h"
430Sstevel@tonic-gate
440Sstevel@tonic-gate /*
453431Scarlsonj * Number of seconds to wait for a retry if the user is interacting with the
463431Scarlsonj * daemon.
470Sstevel@tonic-gate */
483431Scarlsonj #define RETRY_DELAY 10
490Sstevel@tonic-gate
503431Scarlsonj /*
513431Scarlsonj * If the renew timer fires within this number of seconds of the rebind timer,
523431Scarlsonj * then skip renew. This prevents us from sending back-to-back renew and
533431Scarlsonj * rebind messages -- a pointless activity.
543431Scarlsonj */
553431Scarlsonj #define TOO_CLOSE 2
560Sstevel@tonic-gate
573431Scarlsonj static boolean_t stop_extending(dhcp_smach_t *, unsigned int);
580Sstevel@tonic-gate
590Sstevel@tonic-gate /*
603431Scarlsonj * dhcp_renew(): attempts to renew a DHCP lease on expiration of the T1 timer.
610Sstevel@tonic-gate *
620Sstevel@tonic-gate * input: iu_tq_t *: unused
633431Scarlsonj * void *: the lease to renew (dhcp_lease_t)
640Sstevel@tonic-gate * output: void
653431Scarlsonj *
663431Scarlsonj * notes: The primary expense involved with DHCP (like most UDP protocols) is
673431Scarlsonj * with the generation and handling of packets, not the contents of
683431Scarlsonj * those packets. Thus, we try to reduce the number of packets that
693431Scarlsonj * are sent. It would be nice to just renew all leases here (each one
703431Scarlsonj * added has trivial added overhead), but the DHCPv6 RFC doesn't
713431Scarlsonj * explicitly allow that behavior. Rather than having that argument,
723431Scarlsonj * we settle for ones that are close in expiry to the one that fired.
733431Scarlsonj * For v4, we repeatedly reschedule the T1 timer to do the
743431Scarlsonj * retransmissions. For v6, we rely on the common timer computation
753431Scarlsonj * in packet.c.
760Sstevel@tonic-gate */
770Sstevel@tonic-gate
780Sstevel@tonic-gate /* ARGSUSED */
790Sstevel@tonic-gate void
dhcp_renew(iu_tq_t * tqp,void * arg)800Sstevel@tonic-gate dhcp_renew(iu_tq_t *tqp, void *arg)
810Sstevel@tonic-gate {
823431Scarlsonj dhcp_lease_t *dlp = arg;
833431Scarlsonj dhcp_smach_t *dsmp = dlp->dl_smach;
843431Scarlsonj uint32_t t2;
850Sstevel@tonic-gate
863431Scarlsonj dhcpmsg(MSG_VERBOSE, "dhcp_renew: T1 timer expired on %s",
873431Scarlsonj dsmp->dsm_name);
883431Scarlsonj
893431Scarlsonj dlp->dl_t1.dt_id = -1;
900Sstevel@tonic-gate
913431Scarlsonj if (dsmp->dsm_state == RENEWING || dsmp->dsm_state == REBINDING) {
923431Scarlsonj dhcpmsg(MSG_DEBUG, "dhcp_renew: already renewing");
933431Scarlsonj release_lease(dlp);
943431Scarlsonj return;
953431Scarlsonj }
960Sstevel@tonic-gate
973431Scarlsonj /*
983431Scarlsonj * Sanity check: don't send packets if we're past T2, or if we're
993431Scarlsonj * extremely close.
1003431Scarlsonj */
1013431Scarlsonj
1023431Scarlsonj t2 = dsmp->dsm_curstart_monosec + dlp->dl_t2.dt_start;
1033431Scarlsonj if (monosec() + TOO_CLOSE >= t2) {
1043431Scarlsonj dhcpmsg(MSG_DEBUG, "dhcp_renew: %spast T2 on %s",
1053431Scarlsonj monosec() > t2 ? "" : "almost ", dsmp->dsm_name);
1063431Scarlsonj release_lease(dlp);
1070Sstevel@tonic-gate return;
1080Sstevel@tonic-gate }
1090Sstevel@tonic-gate
1100Sstevel@tonic-gate /*
1113431Scarlsonj * If there isn't an async event pending, or if we can cancel the one
1123431Scarlsonj * that's there, then try to renew by sending an extension request. If
1133431Scarlsonj * that fails, we'll try again when the next timer fires.
1140Sstevel@tonic-gate */
1153431Scarlsonj if (!async_cancel(dsmp) || !async_start(dsmp, DHCP_EXTEND, B_FALSE) ||
1163431Scarlsonj !dhcp_extending(dsmp)) {
1173431Scarlsonj if (monosec() + RETRY_DELAY < t2) {
1180Sstevel@tonic-gate /*
1193431Scarlsonj * Try again in RETRY_DELAY seconds; user command
1203431Scarlsonj * should be gone.
1210Sstevel@tonic-gate */
1223431Scarlsonj init_timer(&dlp->dl_t1, RETRY_DELAY);
1233431Scarlsonj (void) set_smach_state(dsmp, BOUND);
1243431Scarlsonj if (!schedule_lease_timer(dlp, &dlp->dl_t1,
1253431Scarlsonj dhcp_renew)) {
1263431Scarlsonj dhcpmsg(MSG_INFO, "dhcp_renew: unable to "
1273431Scarlsonj "reschedule renewal around user command "
1283431Scarlsonj "on %s; will wait for rebind",
1293431Scarlsonj dsmp->dsm_name);
1303431Scarlsonj }
1313431Scarlsonj } else {
1323431Scarlsonj dhcpmsg(MSG_DEBUG, "dhcp_renew: user busy on %s; will "
1333431Scarlsonj "wait for rebind", dsmp->dsm_name);
1343431Scarlsonj }
1353431Scarlsonj }
1363431Scarlsonj release_lease(dlp);
1370Sstevel@tonic-gate }
1380Sstevel@tonic-gate
1390Sstevel@tonic-gate /*
1403431Scarlsonj * dhcp_rebind(): attempts to renew a DHCP lease from the REBINDING state (T2
1413431Scarlsonj * timer expiry).
1420Sstevel@tonic-gate *
1430Sstevel@tonic-gate * input: iu_tq_t *: unused
1443431Scarlsonj * void *: the lease to renew
1450Sstevel@tonic-gate * output: void
1463431Scarlsonj * notes: For v4, we repeatedly reschedule the T2 timer to do the
1473431Scarlsonj * retransmissions. For v6, we rely on the common timer computation
1483431Scarlsonj * in packet.c.
1490Sstevel@tonic-gate */
1500Sstevel@tonic-gate
1510Sstevel@tonic-gate /* ARGSUSED */
1520Sstevel@tonic-gate void
dhcp_rebind(iu_tq_t * tqp,void * arg)1530Sstevel@tonic-gate dhcp_rebind(iu_tq_t *tqp, void *arg)
1540Sstevel@tonic-gate {
1553431Scarlsonj dhcp_lease_t *dlp = arg;
1563431Scarlsonj dhcp_smach_t *dsmp = dlp->dl_smach;
1573431Scarlsonj int nlifs;
1583431Scarlsonj dhcp_lif_t *lif;
1593431Scarlsonj boolean_t some_valid;
1603431Scarlsonj uint32_t expiremax;
1613431Scarlsonj DHCPSTATE oldstate;
1623431Scarlsonj
1633431Scarlsonj dhcpmsg(MSG_VERBOSE, "dhcp_rebind: T2 timer expired on %s",
1643431Scarlsonj dsmp->dsm_name);
1653431Scarlsonj
1663431Scarlsonj dlp->dl_t2.dt_id = -1;
1673431Scarlsonj
1683431Scarlsonj if ((oldstate = dsmp->dsm_state) == REBINDING) {
1693431Scarlsonj dhcpmsg(MSG_DEBUG, "dhcp_renew: already rebinding");
1703431Scarlsonj release_lease(dlp);
1713431Scarlsonj return;
1723431Scarlsonj }
1730Sstevel@tonic-gate
1743431Scarlsonj /*
1753431Scarlsonj * Sanity check: don't send packets if we've already expired on all of
1763431Scarlsonj * the addresses. We compute the maximum expiration time here, because
1773431Scarlsonj * it won't matter for v4 (there's only one lease) and for v6 we need
1783431Scarlsonj * to know when the last lease ages away.
1793431Scarlsonj */
1800Sstevel@tonic-gate
1813431Scarlsonj some_valid = B_FALSE;
1823431Scarlsonj expiremax = monosec();
1833431Scarlsonj lif = dlp->dl_lifs;
1843431Scarlsonj for (nlifs = dlp->dl_nlifs; nlifs > 0; nlifs--, lif = lif->lif_next) {
1853431Scarlsonj uint32_t expire;
1863431Scarlsonj
1873431Scarlsonj expire = dsmp->dsm_curstart_monosec + lif->lif_expire.dt_start;
1883431Scarlsonj if (expire > expiremax) {
1893431Scarlsonj expiremax = expire;
1903431Scarlsonj some_valid = B_TRUE;
1913431Scarlsonj }
1923431Scarlsonj }
1933431Scarlsonj if (!some_valid) {
1943431Scarlsonj dhcpmsg(MSG_DEBUG, "dhcp_rebind: all leases expired on %s",
1953431Scarlsonj dsmp->dsm_name);
1963431Scarlsonj release_lease(dlp);
1970Sstevel@tonic-gate return;
1980Sstevel@tonic-gate }
1990Sstevel@tonic-gate
2000Sstevel@tonic-gate /*
2013431Scarlsonj * This is our first venture into the REBINDING state, so reset the
2023431Scarlsonj * server address. We know the renew timer has already been cancelled
2033431Scarlsonj * (or we wouldn't be here).
2040Sstevel@tonic-gate */
2053431Scarlsonj if (dsmp->dsm_isv6) {
2063431Scarlsonj dsmp->dsm_server = ipv6_all_dhcp_relay_and_servers;
2073431Scarlsonj } else {
2083431Scarlsonj IN6_IPADDR_TO_V4MAPPED(htonl(INADDR_BROADCAST),
2093431Scarlsonj &dsmp->dsm_server);
2103431Scarlsonj }
2110Sstevel@tonic-gate
2123431Scarlsonj /* {Bound,Renew}->rebind transitions cannot fail */
2133431Scarlsonj (void) set_smach_state(dsmp, REBINDING);
2140Sstevel@tonic-gate
2150Sstevel@tonic-gate /*
2163431Scarlsonj * If there isn't an async event pending, or if we can cancel the one
2173431Scarlsonj * that's there, then try to rebind by sending an extension request.
2183431Scarlsonj * If that fails, we'll clean up when the lease expires.
2190Sstevel@tonic-gate */
2203431Scarlsonj if (!async_cancel(dsmp) || !async_start(dsmp, DHCP_EXTEND, B_FALSE) ||
2213431Scarlsonj !dhcp_extending(dsmp)) {
2223431Scarlsonj if (monosec() + RETRY_DELAY < expiremax) {
2230Sstevel@tonic-gate /*
2243431Scarlsonj * Try again in RETRY_DELAY seconds; user command
2253431Scarlsonj * should be gone.
2260Sstevel@tonic-gate */
2273431Scarlsonj init_timer(&dlp->dl_t2, RETRY_DELAY);
2283431Scarlsonj (void) set_smach_state(dsmp, oldstate);
2293431Scarlsonj if (!schedule_lease_timer(dlp, &dlp->dl_t2,
2303431Scarlsonj dhcp_rebind)) {
2313431Scarlsonj dhcpmsg(MSG_INFO, "dhcp_rebind: unable to "
2323431Scarlsonj "reschedule rebind around user command on "
2333431Scarlsonj "%s; lease may expire", dsmp->dsm_name);
2343431Scarlsonj }
2353431Scarlsonj } else {
2363431Scarlsonj dhcpmsg(MSG_WARNING, "dhcp_rebind: user busy on %s; "
2373431Scarlsonj "will expire", dsmp->dsm_name);
2383431Scarlsonj }
2390Sstevel@tonic-gate }
2403431Scarlsonj release_lease(dlp);
2410Sstevel@tonic-gate }
2420Sstevel@tonic-gate
2430Sstevel@tonic-gate /*
2443431Scarlsonj * dhcp_finish_expire(): finish expiration of a lease after the user script
2453431Scarlsonj * runs. If this is the last lease, then restart DHCP.
2463431Scarlsonj * The caller has a reference to the LIF, which will be
2473431Scarlsonj * dropped.
2480Sstevel@tonic-gate *
2493431Scarlsonj * input: dhcp_smach_t *: the state machine to be restarted
2503431Scarlsonj * void *: logical interface that has expired
2510Sstevel@tonic-gate * output: int: always 1
2520Sstevel@tonic-gate */
2530Sstevel@tonic-gate
2540Sstevel@tonic-gate static int
dhcp_finish_expire(dhcp_smach_t * dsmp,void * arg)2553431Scarlsonj dhcp_finish_expire(dhcp_smach_t *dsmp, void *arg)
2560Sstevel@tonic-gate {
2573431Scarlsonj dhcp_lif_t *lif = arg;
2583431Scarlsonj dhcp_lease_t *dlp;
2593431Scarlsonj
2603431Scarlsonj dhcpmsg(MSG_DEBUG, "lease expired on %s; removing", lif->lif_name);
2613431Scarlsonj
2623431Scarlsonj dlp = lif->lif_lease;
2633431Scarlsonj unplumb_lif(lif);
2643431Scarlsonj if (dlp->dl_nlifs == 0)
2653431Scarlsonj remove_lease(dlp);
2663431Scarlsonj release_lif(lif);
2673431Scarlsonj
2683431Scarlsonj /* If some valid leases remain, then drive on */
2693431Scarlsonj if (dsmp->dsm_leases != NULL) {
2703431Scarlsonj dhcpmsg(MSG_DEBUG,
2713431Scarlsonj "dhcp_finish_expire: some leases remain on %s",
2723431Scarlsonj dsmp->dsm_name);
2733431Scarlsonj return (1);
2743431Scarlsonj }
2753431Scarlsonj
276*12989SVasumathi.Sundaram@oracle.COM (void) remove_hostconf(dsmp->dsm_name, dsmp->dsm_isv6);
277*12989SVasumathi.Sundaram@oracle.COM
2783431Scarlsonj dhcpmsg(MSG_INFO, "last lease expired on %s -- restarting DHCP",
2793431Scarlsonj dsmp->dsm_name);
2800Sstevel@tonic-gate
2810Sstevel@tonic-gate /*
2820Sstevel@tonic-gate * in the case where the lease is less than DHCP_REBIND_MIN
2830Sstevel@tonic-gate * seconds, we will never enter dhcp_renew() and thus the packet
2840Sstevel@tonic-gate * counters will not be reset. in that case, reset them here.
2850Sstevel@tonic-gate */
2860Sstevel@tonic-gate
2873431Scarlsonj if (dsmp->dsm_state == BOUND) {
2883431Scarlsonj dsmp->dsm_bad_offers = 0;
2893431Scarlsonj dsmp->dsm_sent = 0;
2903431Scarlsonj dsmp->dsm_received = 0;
2910Sstevel@tonic-gate }
2920Sstevel@tonic-gate
2933431Scarlsonj deprecate_leases(dsmp);
2940Sstevel@tonic-gate
2953431Scarlsonj /* reset_smach() in dhcp_selecting() will clean up any leftover state */
2963431Scarlsonj dhcp_selecting(dsmp);
2973431Scarlsonj
2980Sstevel@tonic-gate return (1);
2990Sstevel@tonic-gate }
3000Sstevel@tonic-gate
3010Sstevel@tonic-gate /*
3023431Scarlsonj * dhcp_deprecate(): deprecates an address on a given logical interface when
3033431Scarlsonj * the preferred lifetime expires.
3040Sstevel@tonic-gate *
3050Sstevel@tonic-gate * input: iu_tq_t *: unused
3063431Scarlsonj * void *: the logical interface whose lease is expiring
3073431Scarlsonj * output: void
3083431Scarlsonj */
3093431Scarlsonj
3103431Scarlsonj /* ARGSUSED */
3113431Scarlsonj void
dhcp_deprecate(iu_tq_t * tqp,void * arg)3123431Scarlsonj dhcp_deprecate(iu_tq_t *tqp, void *arg)
3133431Scarlsonj {
3143431Scarlsonj dhcp_lif_t *lif = arg;
3153431Scarlsonj
3163431Scarlsonj set_lif_deprecated(lif);
3173431Scarlsonj release_lif(lif);
3183431Scarlsonj }
3193431Scarlsonj
3203431Scarlsonj /*
3213431Scarlsonj * dhcp_expire(): expires a lease on a given logical interface and, if there
3223431Scarlsonj * are no more leases, restarts DHCP.
3233431Scarlsonj *
3243431Scarlsonj * input: iu_tq_t *: unused
3253431Scarlsonj * void *: the logical interface whose lease has expired
3260Sstevel@tonic-gate * output: void
3270Sstevel@tonic-gate */
3280Sstevel@tonic-gate
3290Sstevel@tonic-gate /* ARGSUSED */
3300Sstevel@tonic-gate void
dhcp_expire(iu_tq_t * tqp,void * arg)3310Sstevel@tonic-gate dhcp_expire(iu_tq_t *tqp, void *arg)
3320Sstevel@tonic-gate {
3333431Scarlsonj dhcp_lif_t *lif = arg;
3343431Scarlsonj dhcp_smach_t *dsmp;
3353431Scarlsonj const char *event;
3360Sstevel@tonic-gate
3373431Scarlsonj dhcpmsg(MSG_VERBOSE, "dhcp_expire: lease timer expired on %s",
3383431Scarlsonj lif->lif_name);
3390Sstevel@tonic-gate
3403431Scarlsonj lif->lif_expire.dt_id = -1;
3413431Scarlsonj if (lif->lif_lease == NULL) {
3423431Scarlsonj release_lif(lif);
3430Sstevel@tonic-gate return;
3440Sstevel@tonic-gate }
3450Sstevel@tonic-gate
3463431Scarlsonj set_lif_deprecated(lif);
3470Sstevel@tonic-gate
3483431Scarlsonj dsmp = lif->lif_lease->dl_smach;
3493431Scarlsonj
3503431Scarlsonj if (!async_cancel(dsmp)) {
3510Sstevel@tonic-gate
3523431Scarlsonj dhcpmsg(MSG_WARNING,
3533431Scarlsonj "dhcp_expire: cannot cancel current asynchronous command "
3543431Scarlsonj "on %s", dsmp->dsm_name);
3553431Scarlsonj
3563431Scarlsonj /*
3573431Scarlsonj * Try to schedule ourselves for callback. We're really
3583431Scarlsonj * situation-critical here; there's not much hope for us if
3593431Scarlsonj * this fails.
3603431Scarlsonj */
3613431Scarlsonj init_timer(&lif->lif_expire, DHCP_EXPIRE_WAIT);
3623431Scarlsonj if (schedule_lif_timer(lif, &lif->lif_expire, dhcp_expire))
3633431Scarlsonj return;
3640Sstevel@tonic-gate
3653431Scarlsonj dhcpmsg(MSG_CRIT, "dhcp_expire: cannot reschedule dhcp_expire "
3663431Scarlsonj "to get called back, proceeding...");
3673431Scarlsonj }
3683431Scarlsonj
3693431Scarlsonj if (!async_start(dsmp, DHCP_START, B_FALSE))
3703431Scarlsonj dhcpmsg(MSG_WARNING, "dhcp_expire: cannot start asynchronous "
3713431Scarlsonj "transaction on %s, continuing...", dsmp->dsm_name);
3720Sstevel@tonic-gate
3733431Scarlsonj /*
3743431Scarlsonj * Determine if this state machine has any non-expired LIFs left in it.
3753431Scarlsonj * If it doesn't, then this is an "expire" event. Otherwise, if some
3763431Scarlsonj * valid leases remain, it's a "loss" event. The SOMEEXP case can
3773431Scarlsonj * occur only with DHCPv6.
3783431Scarlsonj */
3793431Scarlsonj if (expired_lif_state(dsmp) == DHCP_EXP_SOMEEXP)
3803431Scarlsonj event = EVENT_LOSS6;
3813431Scarlsonj else if (dsmp->dsm_isv6)
3823431Scarlsonj event = EVENT_EXPIRE6;
3833431Scarlsonj else
3843431Scarlsonj event = EVENT_EXPIRE;
3850Sstevel@tonic-gate
3860Sstevel@tonic-gate /*
3870Sstevel@tonic-gate * just march on if this fails; at worst someone will be able
3880Sstevel@tonic-gate * to async_start() while we're actually busy with our own
3890Sstevel@tonic-gate * asynchronous transaction. better than not having a lease.
3900Sstevel@tonic-gate */
3910Sstevel@tonic-gate
3923431Scarlsonj (void) script_start(dsmp, event, dhcp_finish_expire, lif, NULL);
3930Sstevel@tonic-gate }
3940Sstevel@tonic-gate
3950Sstevel@tonic-gate /*
3963431Scarlsonj * dhcp_extending(): sends a REQUEST (IPv4 DHCP) or Rebind/Renew (DHCPv6) to
3973431Scarlsonj * extend a lease on a given state machine
3980Sstevel@tonic-gate *
3993431Scarlsonj * input: dhcp_smach_t *: the state machine to send the message from
4003431Scarlsonj * output: boolean_t: B_TRUE if the extension request was sent
4010Sstevel@tonic-gate */
4020Sstevel@tonic-gate
4033431Scarlsonj boolean_t
dhcp_extending(dhcp_smach_t * dsmp)4043431Scarlsonj dhcp_extending(dhcp_smach_t *dsmp)
4050Sstevel@tonic-gate {
4060Sstevel@tonic-gate dhcp_pkt_t *dpkt;
4070Sstevel@tonic-gate
4083431Scarlsonj stop_pkt_retransmission(dsmp);
4093431Scarlsonj
4103431Scarlsonj /*
4113431Scarlsonj * We change state here because this function is also called when
4123431Scarlsonj * adopting a lease and on demand by the user.
4133431Scarlsonj */
4143431Scarlsonj if (dsmp->dsm_state == BOUND) {
4153431Scarlsonj dsmp->dsm_neg_hrtime = gethrtime();
4163431Scarlsonj dsmp->dsm_bad_offers = 0;
4173431Scarlsonj dsmp->dsm_sent = 0;
4183431Scarlsonj dsmp->dsm_received = 0;
4193431Scarlsonj /* Bound->renew can't fail */
4203431Scarlsonj (void) set_smach_state(dsmp, RENEWING);
4210Sstevel@tonic-gate }
4220Sstevel@tonic-gate
4233431Scarlsonj dhcpmsg(MSG_DEBUG, "dhcp_extending: sending request on %s",
4243431Scarlsonj dsmp->dsm_name);
4253431Scarlsonj
4263431Scarlsonj if (dsmp->dsm_isv6) {
4273431Scarlsonj dhcp_lease_t *dlp;
4283431Scarlsonj dhcp_lif_t *lif;
4293431Scarlsonj uint_t nlifs;
4303431Scarlsonj uint_t irt, mrt;
4313431Scarlsonj
4323431Scarlsonj /*
4333431Scarlsonj * Start constructing the Renew/Rebind message. Only Renew has
4343431Scarlsonj * a server ID, as we still think our server might be
4353431Scarlsonj * reachable.
4363431Scarlsonj */
4373431Scarlsonj if (dsmp->dsm_state == RENEWING) {
4383431Scarlsonj dpkt = init_pkt(dsmp, DHCPV6_MSG_RENEW);
4393431Scarlsonj (void) add_pkt_opt(dpkt, DHCPV6_OPT_SERVERID,
4403431Scarlsonj dsmp->dsm_serverid, dsmp->dsm_serveridlen);
4413431Scarlsonj irt = DHCPV6_REN_TIMEOUT;
4423431Scarlsonj mrt = DHCPV6_REN_MAX_RT;
4433431Scarlsonj } else {
4443431Scarlsonj dpkt = init_pkt(dsmp, DHCPV6_MSG_REBIND);
4453431Scarlsonj irt = DHCPV6_REB_TIMEOUT;
4463431Scarlsonj mrt = DHCPV6_REB_MAX_RT;
4473431Scarlsonj }
4480Sstevel@tonic-gate
4493431Scarlsonj /*
4503431Scarlsonj * Loop over the leases, and add an IA_NA for each and an
4513431Scarlsonj * IAADDR for each address.
4523431Scarlsonj */
4533431Scarlsonj for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
4543431Scarlsonj lif = dlp->dl_lifs;
4553431Scarlsonj for (nlifs = dlp->dl_nlifs; nlifs > 0;
4563431Scarlsonj nlifs--, lif = lif->lif_next) {
4573431Scarlsonj (void) add_pkt_lif(dpkt, lif,
4583431Scarlsonj DHCPV6_STAT_SUCCESS, NULL);
4593431Scarlsonj }
4603431Scarlsonj }
4613431Scarlsonj
4623431Scarlsonj /* Add required Option Request option */
4633431Scarlsonj (void) add_pkt_prl(dpkt, dsmp);
4643431Scarlsonj
4653431Scarlsonj return (send_pkt_v6(dsmp, dpkt, dsmp->dsm_server,
4663431Scarlsonj stop_extending, irt, mrt));
4673431Scarlsonj } else {
4683431Scarlsonj dhcp_lif_t *lif = dsmp->dsm_lif;
4693431Scarlsonj ipaddr_t server;
4703431Scarlsonj
4713431Scarlsonj /* assemble the DHCPREQUEST message. */
4723431Scarlsonj dpkt = init_pkt(dsmp, REQUEST);
4733431Scarlsonj dpkt->pkt->ciaddr.s_addr = lif->lif_addr;
4740Sstevel@tonic-gate
4753431Scarlsonj /*
4763431Scarlsonj * The max dhcp message size option is set to the interface
4773431Scarlsonj * max, minus the size of the udp and ip headers.
4783431Scarlsonj */
4793431Scarlsonj (void) add_pkt_opt16(dpkt, CD_MAX_DHCP_SIZE,
4803431Scarlsonj htons(lif->lif_max - sizeof (struct udpiphdr)));
4813431Scarlsonj (void) add_pkt_opt32(dpkt, CD_LEASE_TIME, htonl(DHCP_PERM));
4823431Scarlsonj
4833448Sdh155122 if (class_id_len != 0) {
4843448Sdh155122 (void) add_pkt_opt(dpkt, CD_CLASS_ID, class_id,
4853448Sdh155122 class_id_len);
4863448Sdh155122 }
4873431Scarlsonj (void) add_pkt_prl(dpkt, dsmp);
4883431Scarlsonj /*
4893431Scarlsonj * dsm_reqhost was set for this state machine in
4903431Scarlsonj * dhcp_selecting() if the REQUEST_HOSTNAME option was set and
4913431Scarlsonj * a host name was found.
4923431Scarlsonj */
4933431Scarlsonj if (dsmp->dsm_reqhost != NULL) {
4943431Scarlsonj (void) add_pkt_opt(dpkt, CD_HOSTNAME, dsmp->dsm_reqhost,
4953431Scarlsonj strlen(dsmp->dsm_reqhost));
4963431Scarlsonj }
4973431Scarlsonj (void) add_pkt_opt(dpkt, CD_END, NULL, 0);
4983431Scarlsonj
4993431Scarlsonj IN6_V4MAPPED_TO_IPADDR(&dsmp->dsm_server, server);
5003431Scarlsonj return (send_pkt(dsmp, dpkt, server, stop_extending));
5013431Scarlsonj }
5023431Scarlsonj }
5030Sstevel@tonic-gate
5043431Scarlsonj /*
5053431Scarlsonj * stop_extending(): decides when to stop retransmitting v4 REQUEST or v6
5063431Scarlsonj * Renew/Rebind messages. If we're renewing, then stop if
5073431Scarlsonj * T2 is soon approaching.
5083431Scarlsonj *
5093431Scarlsonj * input: dhcp_smach_t *: the state machine REQUESTs are being sent from
5103431Scarlsonj * unsigned int: the number of REQUESTs sent so far
5113431Scarlsonj * output: boolean_t: B_TRUE if retransmissions should stop
5123431Scarlsonj */
5133431Scarlsonj
5143431Scarlsonj /* ARGSUSED */
5153431Scarlsonj static boolean_t
stop_extending(dhcp_smach_t * dsmp,unsigned int n_requests)5163431Scarlsonj stop_extending(dhcp_smach_t *dsmp, unsigned int n_requests)
5173431Scarlsonj {
5183431Scarlsonj dhcp_lease_t *dlp;
5190Sstevel@tonic-gate
5203431Scarlsonj /*
5213431Scarlsonj * If we're renewing and rebind time is soon approaching, then don't
5223431Scarlsonj * schedule
5233431Scarlsonj */
5243431Scarlsonj if (dsmp->dsm_state == RENEWING) {
5253431Scarlsonj monosec_t t2;
5263431Scarlsonj
5273431Scarlsonj t2 = 0;
5283431Scarlsonj for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
5293431Scarlsonj if (dlp->dl_t2.dt_start > t2)
5303431Scarlsonj t2 = dlp->dl_t2.dt_start;
5313431Scarlsonj }
5323431Scarlsonj t2 += dsmp->dsm_curstart_monosec;
5333431Scarlsonj if (monosec() + TOO_CLOSE >= t2) {
5343431Scarlsonj dhcpmsg(MSG_DEBUG, "stop_extending: %spast T2 on %s",
5353431Scarlsonj monosec() > t2 ? "" : "almost ", dsmp->dsm_name);
5363431Scarlsonj return (B_TRUE);
5373431Scarlsonj }
5380Sstevel@tonic-gate }
5390Sstevel@tonic-gate
5400Sstevel@tonic-gate /*
5413431Scarlsonj * Note that returning B_TRUE cancels both this transmission and the
5423431Scarlsonj * one that would occur at dsm_send_timeout, and that for v4 we cut the
5433431Scarlsonj * time in half for each retransmission. Thus we check here against
5443431Scarlsonj * half of the minimum.
5450Sstevel@tonic-gate */
5463431Scarlsonj if (!dsmp->dsm_isv6 &&
5473431Scarlsonj dsmp->dsm_send_timeout < DHCP_REBIND_MIN * MILLISEC / 2) {
5483431Scarlsonj dhcpmsg(MSG_DEBUG, "stop_extending: next retry would be in "
5493431Scarlsonj "%d.%03d; stopping", dsmp->dsm_send_timeout / MILLISEC,
5503431Scarlsonj dsmp->dsm_send_timeout % MILLISEC);
5513431Scarlsonj return (B_TRUE);
5523431Scarlsonj }
5530Sstevel@tonic-gate
5543431Scarlsonj /* Otherwise, w stop only when the next timer (rebind, expire) fires */
5553431Scarlsonj return (B_FALSE);
5560Sstevel@tonic-gate }
557