aboutsummaryrefslogtreecommitdiff
path: root/common/dispatch.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/dispatch.c')
-rw-r--r--common/dispatch.c416
1 files changed, 416 insertions, 0 deletions
diff --git a/common/dispatch.c b/common/dispatch.c
new file mode 100644
index 0000000..7c4434e
--- /dev/null
+++ b/common/dispatch.c
@@ -0,0 +1,416 @@
+/* dispatch.c
+
+ Network input dispatcher... */
+
+/*
+ * Copyright (c) 2004-2011 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1995-2003 by Internet Software Consortium
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ */
+
+#include "dhcpd.h"
+
+#include <sys/time.h>
+
+struct timeout *timeouts;
+static struct timeout *free_timeouts;
+
+void set_time(TIME t)
+{
+ /* Do any outstanding timeouts. */
+ if (cur_tv . tv_sec != t) {
+ cur_tv . tv_sec = t;
+ cur_tv . tv_usec = 0;
+ process_outstanding_timeouts ((struct timeval *)0);
+ }
+}
+
+struct timeval *process_outstanding_timeouts (struct timeval *tvp)
+{
+ /* Call any expired timeouts, and then if there's
+ still a timeout registered, time out the select
+ call then. */
+ another:
+ if (timeouts) {
+ struct timeout *t;
+ if ((timeouts -> when . tv_sec < cur_tv . tv_sec) ||
+ ((timeouts -> when . tv_sec == cur_tv . tv_sec) &&
+ (timeouts -> when . tv_usec <= cur_tv . tv_usec))) {
+ t = timeouts;
+ timeouts = timeouts -> next;
+ (*(t -> func)) (t -> what);
+ if (t -> unref)
+ (*t -> unref) (&t -> what, MDL);
+ t -> next = free_timeouts;
+ free_timeouts = t;
+ goto another;
+ }
+ if (tvp) {
+ tvp -> tv_sec = timeouts -> when . tv_sec;
+ tvp -> tv_usec = timeouts -> when . tv_usec;
+ }
+ return tvp;
+ } else
+ return (struct timeval *)0;
+}
+
+/* Wait for packets to come in using select(). When one does, call
+ receive_packet to receive the packet and possibly strip hardware
+ addressing information from it, and then call through the
+ bootp_packet_handler hook to try to do something with it. */
+
+/*
+ * Use the DHCP timeout list as a place to store DHCP specific
+ * information, but use the ISC timer system to actually dispatch
+ * the events.
+ *
+ * There are several things that the DHCP timer code does that the
+ * ISC code doesn't:
+ * 1) It allows for negative times
+ * 2) The cancel arguments are different. The DHCP code uses the
+ * function and data to find the proper timer to cancel while the
+ * ISC code uses a pointer to the timer.
+ * 3) The DHCP code includes provision for incrementing and decrementing
+ * a reference counter associated with the data.
+ * The first one is fairly easy to fix but will take some time to go throuh
+ * the callers and update them. The second is also not all that difficult
+ * in concept - add a pointer to the appropriate structures to hold a pointer
+ * to the timer and use that. The complications arise in trying to ensure
+ * that all of the corner cases are covered. The last one is potentially
+ * more painful and requires more investigation.
+ *
+ * The plan is continue with the older DHCP calls and timer list. The
+ * calls will continue to manipulate the list but will also pass a
+ * timer to the ISC timer code for the actual dispatch. Later, if desired,
+ * we can go back and modify the underlying calls to use the ISC
+ * timer functions directly without requiring all of the code to change
+ * at the same time.
+ */
+
+void
+dispatch(void)
+{
+ isc_result_t status;
+
+ status = isc_app_ctxrun(dhcp_gbl_ctx.actx);
+
+ log_fatal ("Dispatch routine failed: %s -- exiting",
+ isc_result_totext (status));
+}
+
+void
+isclib_timer_callback(isc_task_t *taskp,
+ isc_event_t *eventp)
+{
+ struct timeout *t = (struct timeout *)eventp->ev_arg;
+ struct timeout *q, *r;
+
+ /* Get the current time... */
+ gettimeofday (&cur_tv, (struct timezone *)0);
+
+ /*
+ * Find the timeout on the dhcp list and remove it.
+ * As the list isn't ordered we search the entire list
+ */
+
+ r = NULL;
+ for (q = timeouts; q; q = q->next) {
+ if (q == t) {
+ if (r)
+ r->next = q->next;
+ else
+ timeouts = q->next;
+ break;
+ }
+ r = q;
+ }
+
+ /*
+ * The timer should always be on the list. If it is we do
+ * the work and detach the timer block, if not we log an error.
+ * In both cases we attempt free the ISC event and continue
+ * processing.
+ */
+
+ if (q != NULL) {
+ /* call the callback function */
+ (*(q->func)) (q->what);
+ if (q->unref) {
+ (*q->unref) (&q->what, MDL);
+ }
+ q->next = free_timeouts;
+ isc_timer_detach(&q->isc_timeout);
+ free_timeouts = q;
+ } else {
+ /*
+ * Hmm, we should clean up the timer structure but aren't
+ * sure about the pointer to the timer block we got so
+ * don't try to - may change this to a log_fatal
+ */
+ log_error("Error finding timer structure");
+ }
+
+ isc_event_free(&eventp);
+ return;
+}
+
+/* maximum value for usec */
+#define USEC_MAX 1000000
+#define DHCP_SEC_MAX 0xFFFFFFFF
+
+void add_timeout (when, where, what, ref, unref)
+ struct timeval *when;
+ void (*where) (void *);
+ void *what;
+ tvref_t ref;
+ tvunref_t unref;
+{
+ struct timeout *t, *q;
+ int usereset = 0;
+ isc_result_t status;
+ int64_t sec;
+ int usec;
+ isc_interval_t interval;
+ isc_time_t expires;
+
+ /* See if this timeout supersedes an existing timeout. */
+ t = (struct timeout *)0;
+ for (q = timeouts; q; q = q->next) {
+ if ((where == NULL || q->func == where) &&
+ q->what == what) {
+ if (t)
+ t->next = q->next;
+ else
+ timeouts = q->next;
+ usereset = 1;
+ break;
+ }
+ t = q;
+ }
+
+ /* If we didn't supersede a timeout, allocate a timeout
+ structure now. */
+ if (!q) {
+ if (free_timeouts) {
+ q = free_timeouts;
+ free_timeouts = q->next;
+ } else {
+ q = ((struct timeout *)
+ dmalloc(sizeof(struct timeout), MDL));
+ if (!q) {
+ log_fatal("add_timeout: no memory!");
+ }
+ }
+ memset(q, 0, sizeof *q);
+ q->func = where;
+ q->ref = ref;
+ q->unref = unref;
+ if (q->ref)
+ (*q->ref)(&q->what, what, MDL);
+ else
+ q->what = what;
+ }
+
+ /*
+ * The value passed in is a time from an epoch but we need a relative
+ * time so we need to do some math to try and recover the period.
+ * This is complicated by the fact that not all of the calls cared
+ * about the usec value, if it's zero we assume the caller didn't care.
+ *
+ * The ISC timer library doesn't seem to like negative values
+ * and can't accept any values above 4G-1 seconds so we limit
+ * the values to 0 <= value < 4G-1. We do it before
+ * checking the trace option so that both the trace code and
+ * the working code use the same values.
+ */
+
+ sec = when->tv_sec - cur_tv.tv_sec;
+ usec = when->tv_usec - cur_tv.tv_usec;
+
+ if ((when->tv_usec != 0) && (usec < 0)) {
+ sec--;
+ usec += USEC_MAX;
+ }
+
+ if (sec < 0) {
+ sec = 0;
+ usec = 0;
+ } else if (sec > DHCP_SEC_MAX) {
+ log_error("Timeout requested too large "
+ "reducing to 2^^32-1");
+ sec = DHCP_SEC_MAX;
+ usec = 0;
+ } else if (usec < 0) {
+ usec = 0;
+ } else if (usec >= USEC_MAX) {
+ usec = USEC_MAX - 1;
+ }
+
+ /*
+ * This is necessary for the tracing code but we put it
+ * here in case we want to compare timing information
+ * for some reason, like debugging.
+ */
+ q->when.tv_sec = cur_tv.tv_sec + (sec & DHCP_SEC_MAX);
+ q->when.tv_usec = usec;
+
+#if defined (TRACING)
+ if (trace_playback()) {
+ /*
+ * If we are doing playback we need to handle the timers
+ * within this code rather than having the isclib handle
+ * them for us. We need to keep the timer list in order
+ * to allow us to find the ones to timeout.
+ *
+ * By using a different timer setup in the playback we may
+ * have variations between the orginal and the playback but
+ * it's the best we can do for now.
+ */
+
+ /* Beginning of list? */
+ if (!timeouts || (timeouts->when.tv_sec > q-> when.tv_sec) ||
+ ((timeouts->when.tv_sec == q->when.tv_sec) &&
+ (timeouts->when.tv_usec > q->when.tv_usec))) {
+ q->next = timeouts;
+ timeouts = q;
+ return;
+ }
+
+ /* Middle of list? */
+ for (t = timeouts; t->next; t = t->next) {
+ if ((t->next->when.tv_sec > q->when.tv_sec) ||
+ ((t->next->when.tv_sec == q->when.tv_sec) &&
+ (t->next->when.tv_usec > q->when.tv_usec))) {
+ q->next = t->next;
+ t->next = q;
+ return;
+ }
+ }
+
+ /* End of list. */
+ t->next = q;
+ q->next = (struct timeout *)0;
+ return;
+ }
+#endif
+ /*
+ * Don't bother sorting the DHCP list, just add it to the front.
+ * Eventually the list should be removed as we migrate the callers
+ * to the native ISC timer functions, if it becomes a performance
+ * problem before then we may need to order the list.
+ */
+ q->next = timeouts;
+ timeouts = q;
+
+ isc_interval_set(&interval, sec & DHCP_SEC_MAX, usec * 1000);
+ status = isc_time_nowplusinterval(&expires, &interval);
+ if (status != ISC_R_SUCCESS) {
+ /*
+ * The system time function isn't happy or returned
+ * a value larger than isc_time_t can hold.
+ */
+ log_fatal("Unable to set up timer: %s",
+ isc_result_totext(status));
+ }
+
+ if (usereset == 0) {
+ status = isc_timer_create(dhcp_gbl_ctx.timermgr,
+ isc_timertype_once, &expires,
+ NULL, dhcp_gbl_ctx.task,
+ isclib_timer_callback,
+ (void *)q, &q->isc_timeout);
+ } else {
+ status = isc_timer_reset(q->isc_timeout,
+ isc_timertype_once, &expires,
+ NULL, 0);
+ }
+
+ /* If it fails log an error and die */
+ if (status != ISC_R_SUCCESS) {
+ log_fatal("Unable to add timeout to isclib\n");
+ }
+
+ return;
+}
+
+void cancel_timeout (where, what)
+ void (*where) (void *);
+ void *what;
+{
+ struct timeout *t, *q;
+
+ /* Look for this timeout on the list, and unlink it if we find it. */
+ t = (struct timeout *)0;
+ for (q = timeouts; q; q = q -> next) {
+ if (q->func == where && q->what == what) {
+ if (t)
+ t->next = q->next;
+ else
+ timeouts = q->next;
+ break;
+ }
+ t = q;
+ }
+
+ /*
+ * If we found the timeout, cancel it and put it on the free list.
+ * The TRACING stuff is ugly but we don't add a timer when doing
+ * playback so we don't want to remove them then either.
+ */
+ if (q) {
+#if defined (TRACING)
+ if (!trace_playback()) {
+#endif
+ isc_timer_detach(&q->isc_timeout);
+#if defined (TRACING)
+ }
+#endif
+
+ if (q->unref)
+ (*q->unref) (&q->what, MDL);
+ q->next = free_timeouts;
+ free_timeouts = q;
+ }
+}
+
+#if defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
+void cancel_all_timeouts ()
+{
+ struct timeout *t, *n;
+ for (t = timeouts; t; t = n) {
+ n = t->next;
+ isc_timer_detach(&t->isc_timeout);
+ if (t->unref && t->what)
+ (*t->unref) (&t->what, MDL);
+ t->next = free_timeouts;
+ free_timeouts = t;
+ }
+}
+
+void relinquish_timeouts ()
+{
+ struct timeout *t, *n;
+ for (t = free_timeouts; t; t = n) {
+ n = t->next;
+ dfree(t, MDL);
+ }
+}
+#endif