summaryrefslogtreecommitdiff
path: root/usr/src/cmd/cmd-inet/sbin/dhcpagent/renew.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/cmd-inet/sbin/dhcpagent/renew.c')
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/renew.c367
1 files changed, 367 insertions, 0 deletions
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/renew.c b/usr/src/cmd/cmd-inet/sbin/dhcpagent/renew.c
new file mode 100644
index 0000000000..8613a41245
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/renew.c
@@ -0,0 +1,367 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <netinet/dhcp.h>
+#include <netinet/udp.h>
+#include <netinet/ip_var.h>
+#include <netinet/udp_var.h>
+#include <libinetutil.h>
+#include <dhcpmsg.h>
+#include <string.h>
+
+#include "packet.h"
+#include "agent.h"
+#include "script_handler.h"
+#include "interface.h"
+#include "states.h"
+#include "util.h"
+
+/*
+ * next_extend_time(): returns the next time an EXTEND request should be sent
+ *
+ * input: monosec_t: the absolute time when the next state is entered
+ * output: uint32_t: the number of seconds in the future to send the next
+ * EXTEND request
+ */
+
+static uint32_t
+next_extend_time(monosec_t limit_monosec)
+{
+ monosec_t current_monosec = monosec();
+
+ if (limit_monosec - current_monosec < DHCP_REBIND_MIN)
+ return (0);
+
+ return ((limit_monosec - current_monosec) / 2);
+}
+
+/*
+ * dhcp_renew(): attempts to renew a DHCP lease
+ *
+ * input: iu_tq_t *: unused
+ * void *: the ifslist to renew the lease on
+ * output: void
+ */
+
+/* ARGSUSED */
+void
+dhcp_renew(iu_tq_t *tqp, void *arg)
+{
+ struct ifslist *ifsp = (struct ifslist *)arg;
+ uint32_t next;
+
+
+ ifsp->if_timer[DHCP_T1_TIMER] = -1;
+
+ if (check_ifs(ifsp) == 0) {
+ (void) release_ifs(ifsp);
+ return;
+ }
+
+ /*
+ * sanity check: don't send packets if we're past t2.
+ */
+
+ if (monosec() > (ifsp->if_curstart_monosec + ifsp->if_t2))
+ return;
+
+ next = next_extend_time(ifsp->if_curstart_monosec + ifsp->if_t2);
+
+ /*
+ * if there isn't an async event pending, then try to renew.
+ */
+
+ if (!async_pending(ifsp))
+ if (async_start(ifsp, DHCP_EXTEND, B_FALSE) != 0)
+
+ /*
+ * try to send extend. if we don't succeed,
+ * async_timeout() will clean us up.
+ */
+
+ (void) dhcp_extending(ifsp);
+
+ /*
+ * if we're within DHCP_REBIND_MIN seconds of REBINDING, don't
+ * reschedule ourselves.
+ */
+
+ if (next == 0)
+ return;
+
+ /*
+ * no big deal if we can't reschedule; we still have the REBIND
+ * state to save us.
+ */
+
+ (void) schedule_ifs_timer(ifsp, DHCP_T1_TIMER, next, dhcp_renew);
+}
+
+/*
+ * dhcp_rebind(): attempts to renew a DHCP lease from the REBINDING state
+ *
+ * input: iu_tq_t *: unused
+ * void *: the ifslist to renew the lease on
+ * output: void
+ */
+
+/* ARGSUSED */
+void
+dhcp_rebind(iu_tq_t *tqp, void *arg)
+{
+ struct ifslist *ifsp = (struct ifslist *)arg;
+ uint32_t next;
+
+ ifsp->if_timer[DHCP_T2_TIMER] = -1;
+
+ if (check_ifs(ifsp) == 0) {
+ (void) release_ifs(ifsp);
+ return;
+ }
+
+ /*
+ * sanity check: don't send packets if we've already expired.
+ */
+
+ if (monosec() > (ifsp->if_curstart_monosec + ifsp->if_lease))
+ return;
+
+ next = next_extend_time(ifsp->if_curstart_monosec + ifsp->if_lease);
+
+ /*
+ * if this is our first venture into the REBINDING state, then
+ * reset the server address. we know the renew timer has
+ * already been cancelled (or we wouldn't be here).
+ */
+
+ if (ifsp->if_state == RENEWING) {
+ ifsp->if_state = REBINDING;
+ ifsp->if_server.s_addr = htonl(INADDR_BROADCAST);
+ }
+
+ /*
+ * if there isn't an async event pending, then try to rebind.
+ */
+
+ if (!async_pending(ifsp))
+ if (async_start(ifsp, DHCP_EXTEND, B_FALSE) != 0)
+
+ /*
+ * try to send extend. if we don't succeed,
+ * async_timeout() will clean us up.
+ */
+
+ (void) dhcp_extending(ifsp);
+
+ /*
+ * if we're within DHCP_REBIND_MIN seconds of EXPIRE, don't
+ * reschedule ourselves.
+ */
+
+ if (next == 0) {
+ dhcpmsg(MSG_WARNING, "dhcp_rebind: lease on %s expires in less "
+ "than %i seconds!", ifsp->if_name, DHCP_REBIND_MIN);
+ return;
+ }
+
+ if (schedule_ifs_timer(ifsp, DHCP_T2_TIMER, next, dhcp_rebind) == 0)
+
+ /*
+ * we'll just end up in dhcp_expire(), but it sure sucks.
+ */
+
+ dhcpmsg(MSG_CRIT, "dhcp_rebind: cannot reschedule another "
+ "rebind attempt; lease may expire for %s", ifsp->if_name);
+}
+
+/*
+ * dhcp_restart(): callback function to script_start
+ *
+ * input: struct ifslist *: the interface to be restarted
+ * const char *: unused
+ * output: int: always 1
+ */
+
+/* ARGSUSED */
+static int
+dhcp_restart(struct ifslist *ifsp, const char *msg)
+{
+ dhcpmsg(MSG_INFO, "lease expired on %s -- restarting DHCP",
+ ifsp->if_name);
+
+ /*
+ * in the case where the lease is less than DHCP_REBIND_MIN
+ * seconds, we will never enter dhcp_renew() and thus the packet
+ * counters will not be reset. in that case, reset them here.
+ */
+
+ if (ifsp->if_state == BOUND) {
+ ifsp->if_bad_offers = 0;
+ ifsp->if_sent = 0;
+ ifsp->if_received = 0;
+ }
+
+ (void) canonize_ifs(ifsp);
+
+ /* reset_ifs() in dhcp_selecting() will clean up any leftover state */
+ dhcp_selecting(ifsp);
+ return (1);
+}
+
+/*
+ * dhcp_expire(): expires a lease on a given interface and restarts DHCP
+ *
+ * input: iu_tq_t *: unused
+ * void *: the ifslist to expire the lease on
+ * output: void
+ */
+
+/* ARGSUSED */
+void
+dhcp_expire(iu_tq_t *tqp, void *arg)
+{
+ struct ifslist *ifsp = (struct ifslist *)arg;
+
+ ifsp->if_timer[DHCP_LEASE_TIMER] = -1;
+
+ if (check_ifs(ifsp) == 0) {
+ (void) release_ifs(ifsp);
+ return;
+ }
+
+ if (async_pending(ifsp))
+
+ if (async_cancel(ifsp) == 0) {
+
+ dhcpmsg(MSG_WARNING, "dhcp_expire: cannot cancel "
+ "current asynchronous command against %s",
+ ifsp->if_name);
+
+ /*
+ * try to schedule ourselves for callback.
+ * we're really situation critical here
+ * there's not much hope for us if this fails.
+ */
+
+ if (iu_schedule_timer(tq, DHCP_EXPIRE_WAIT, dhcp_expire,
+ ifsp) != -1) {
+ hold_ifs(ifsp);
+ return;
+ }
+
+ dhcpmsg(MSG_CRIT, "dhcp_expire: cannot reschedule "
+ "dhcp_expire to get called back, proceeding...");
+ }
+
+ /*
+ * just march on if this fails; at worst someone will be able
+ * to async_start() while we're actually busy with our own
+ * asynchronous transaction. better than not having a lease.
+ */
+
+ if (async_start(ifsp, DHCP_START, B_FALSE) == 0)
+ dhcpmsg(MSG_WARNING, "dhcp_expire: cannot start asynchronous "
+ "transaction on %s, continuing...", ifsp->if_name);
+
+ (void) script_start(ifsp, EVENT_EXPIRE, dhcp_restart, NULL, NULL);
+}
+
+/*
+ * dhcp_extending(): sends a REQUEST to extend a lease on a given interface
+ * and registers to receive the ACK/NAK server reply
+ *
+ * input: struct ifslist *: the interface to send the REQUEST on
+ * output: int: 1 if the extension request was sent, 0 otherwise
+ */
+
+int
+dhcp_extending(struct ifslist *ifsp)
+{
+ dhcp_pkt_t *dpkt;
+
+ if (ifsp->if_state == BOUND) {
+ ifsp->if_neg_monosec = monosec();
+ ifsp->if_state = RENEWING;
+ ifsp->if_bad_offers = 0;
+ ifsp->if_sent = 0;
+ ifsp->if_received = 0;
+ }
+
+ dhcpmsg(MSG_DEBUG, "dhcp_extending: registering dhcp_acknak on %s",
+ ifsp->if_name);
+
+ if (register_acknak(ifsp) == 0) {
+
+ ipc_action_finish(ifsp, DHCP_IPC_E_MEMORY);
+ async_finish(ifsp);
+
+ dhcpmsg(MSG_WARNING, "dhcp_extending: cannot register "
+ "dhcp_acknak for %s, not sending renew request",
+ ifsp->if_name);
+
+ return (0);
+ }
+
+ /*
+ * assemble DHCPREQUEST message. The max dhcp message size
+ * option is set to the interface max, minus the size of the udp and
+ * ip headers.
+ */
+
+ dpkt = init_pkt(ifsp, REQUEST);
+ dpkt->pkt->ciaddr.s_addr = ifsp->if_addr.s_addr;
+
+ add_pkt_opt16(dpkt, CD_MAX_DHCP_SIZE, htons(ifsp->if_max -
+ sizeof (struct udpiphdr)));
+ add_pkt_opt32(dpkt, CD_LEASE_TIME, htonl(DHCP_PERM));
+
+ add_pkt_opt(dpkt, CD_CLASS_ID, class_id, class_id_len);
+ add_pkt_opt(dpkt, CD_REQUEST_LIST, ifsp->if_prl, ifsp->if_prllen);
+ /*
+ * if_reqhost was set for this interface in dhcp_selecting()
+ * if the REQUEST_HOSTNAME option was set and a host name was
+ * found.
+ */
+ if (ifsp->if_reqhost != NULL) {
+ add_pkt_opt(dpkt, CD_HOSTNAME, ifsp->if_reqhost,
+ strlen(ifsp->if_reqhost));
+ }
+ add_pkt_opt(dpkt, CD_END, NULL, 0);
+
+ /*
+ * if we can't send the packet, leave the event handler registered
+ * anyway, since we're not expecting to get any other types of
+ * packets in other than ACKs/NAKs anyway.
+ */
+
+ return (send_pkt(ifsp, dpkt, ifsp->if_server.s_addr, NULL));
+}