1 /* $NetBSD: dispatch.c,v 1.4 2014/07/12 12:09:37 spz Exp $ */
4 Network input dispatcher... */
7 * Copyright (c) 2004-2011,2013 by Internet Systems Consortium, Inc. ("ISC")
8 * Copyright (c) 1995-2003 by Internet Software Consortium
10 * Permission to use, copy, modify, and distribute this software for any
11 * purpose with or without fee is hereby granted, provided that the above
12 * copyright notice and this permission notice appear in all copies.
14 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
15 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
17 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
20 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 * Internet Systems Consortium, Inc.
24 * Redwood City, CA 94063
26 * https://www.isc.org/
30 #include <sys/cdefs.h>
31 __RCSID("$NetBSD: dispatch.c,v 1.4 2014/07/12 12:09:37 spz Exp $");
37 struct timeout
*timeouts
;
38 static struct timeout
*free_timeouts
;
42 /* Do any outstanding timeouts. */
43 if (cur_tv
. tv_sec
!= t
) {
46 process_outstanding_timeouts ((struct timeval
*)0);
50 struct timeval
*process_outstanding_timeouts (struct timeval
*tvp
)
52 /* Call any expired timeouts, and then if there's
53 still a timeout registered, time out the select
58 if ((timeouts
-> when
. tv_sec
< cur_tv
. tv_sec
) ||
59 ((timeouts
-> when
. tv_sec
== cur_tv
. tv_sec
) &&
60 (timeouts
-> when
. tv_usec
<= cur_tv
. tv_usec
))) {
62 timeouts
= timeouts
-> next
;
63 (*(t
-> func
)) (t
-> what
);
65 (*t
-> unref
) (&t
-> what
, MDL
);
66 t
-> next
= free_timeouts
;
71 tvp
-> tv_sec
= timeouts
-> when
. tv_sec
;
72 tvp
-> tv_usec
= timeouts
-> when
. tv_usec
;
76 return (struct timeval
*)0;
79 /* Wait for packets to come in using select(). When one does, call
80 receive_packet to receive the packet and possibly strip hardware
81 addressing information from it, and then call through the
82 bootp_packet_handler hook to try to do something with it. */
85 * Use the DHCP timeout list as a place to store DHCP specific
86 * information, but use the ISC timer system to actually dispatch
89 * There are several things that the DHCP timer code does that the
91 * 1) It allows for negative times
92 * 2) The cancel arguments are different. The DHCP code uses the
93 * function and data to find the proper timer to cancel while the
94 * ISC code uses a pointer to the timer.
95 * 3) The DHCP code includes provision for incrementing and decrementing
96 * a reference counter associated with the data.
97 * The first one is fairly easy to fix but will take some time to go throuh
98 * the callers and update them. The second is also not all that difficult
99 * in concept - add a pointer to the appropriate structures to hold a pointer
100 * to the timer and use that. The complications arise in trying to ensure
101 * that all of the corner cases are covered. The last one is potentially
102 * more painful and requires more investigation.
104 * The plan is continue with the older DHCP calls and timer list. The
105 * calls will continue to manipulate the list but will also pass a
106 * timer to the ISC timer code for the actual dispatch. Later, if desired,
107 * we can go back and modify the underlying calls to use the ISC
108 * timer functions directly without requiring all of the code to change
118 status
= isc_app_ctxrun(dhcp_gbl_ctx
.actx
);
121 * isc_app_ctxrun can be stopped by receiving a
122 * signal. It will return ISC_R_RELOAD in that
123 * case. That is a normal behavior.
126 if (status
== ISC_R_RELOAD
) {
128 * dhcp_set_control_state() will do the job.
129 * Note its first argument is ignored.
131 status
= dhcp_set_control_state(server_shutdown
,
133 if (status
== ISC_R_SUCCESS
)
134 status
= ISC_R_RELOAD
;
136 } while (status
== ISC_R_RELOAD
);
138 log_fatal ("Dispatch routine failed: %s -- exiting",
139 isc_result_totext (status
));
143 isclib_timer_callback(isc_task_t
*taskp
,
146 struct timeout
*t
= (struct timeout
*)eventp
->ev_arg
;
147 struct timeout
*q
, *r
;
149 /* Get the current time... */
150 gettimeofday (&cur_tv
, (struct timezone
*)0);
153 * Find the timeout on the dhcp list and remove it.
154 * As the list isn't ordered we search the entire list
158 for (q
= timeouts
; q
; q
= q
->next
) {
170 * The timer should always be on the list. If it is we do
171 * the work and detach the timer block, if not we log an error.
172 * In both cases we attempt free the ISC event and continue
177 /* call the callback function */
178 (*(q
->func
)) (q
->what
);
180 (*q
->unref
) (&q
->what
, MDL
);
182 q
->next
= free_timeouts
;
183 isc_timer_detach(&q
->isc_timeout
);
187 * Hmm, we should clean up the timer structure but aren't
188 * sure about the pointer to the timer block we got so
189 * don't try to - may change this to a log_fatal
191 log_error("Error finding timer structure");
194 isc_event_free(&eventp
);
198 /* maximum value for usec */
199 #define USEC_MAX 1000000
200 #define DHCP_SEC_MAX 0xFFFFFFFF
202 void add_timeout (when
, where
, what
, ref
, unref
)
203 struct timeval
*when
;
204 void (*where
) (void *);
209 struct timeout
*t
, *q
;
214 isc_interval_t interval
;
217 /* See if this timeout supersedes an existing timeout. */
218 t
= (struct timeout
*)0;
219 for (q
= timeouts
; q
; q
= q
->next
) {
220 if ((where
== NULL
|| q
->func
== where
) &&
232 /* If we didn't supersede a timeout, allocate a timeout
237 free_timeouts
= q
->next
;
239 q
= ((struct timeout
*)
240 dmalloc(sizeof(struct timeout
), MDL
));
242 log_fatal("add_timeout: no memory!");
245 memset(q
, 0, sizeof *q
);
250 (*q
->ref
)(&q
->what
, what
, MDL
);
256 * The value passed in is a time from an epoch but we need a relative
257 * time so we need to do some math to try and recover the period.
258 * This is complicated by the fact that not all of the calls cared
259 * about the usec value, if it's zero we assume the caller didn't care.
261 * The ISC timer library doesn't seem to like negative values
262 * and can't accept any values above 4G-1 seconds so we limit
263 * the values to 0 <= value < 4G-1. We do it before
264 * checking the trace option so that both the trace code and
265 * the working code use the same values.
268 sec
= when
->tv_sec
- cur_tv
.tv_sec
;
269 usec
= when
->tv_usec
- cur_tv
.tv_usec
;
271 if ((when
->tv_usec
!= 0) && (usec
< 0)) {
279 } else if (sec
> DHCP_SEC_MAX
) {
280 log_error("Timeout requested too large "
281 "reducing to 2^^32-1");
284 } else if (usec
< 0) {
286 } else if (usec
>= USEC_MAX
) {
291 * This is necessary for the tracing code but we put it
292 * here in case we want to compare timing information
293 * for some reason, like debugging.
295 q
->when
.tv_sec
= cur_tv
.tv_sec
+ (sec
& DHCP_SEC_MAX
);
296 q
->when
.tv_usec
= usec
;
298 #if defined (TRACING)
299 if (trace_playback()) {
301 * If we are doing playback we need to handle the timers
302 * within this code rather than having the isclib handle
303 * them for us. We need to keep the timer list in order
304 * to allow us to find the ones to timeout.
306 * By using a different timer setup in the playback we may
307 * have variations between the orginal and the playback but
308 * it's the best we can do for now.
311 /* Beginning of list? */
312 if (!timeouts
|| (timeouts
->when
.tv_sec
> q
-> when
.tv_sec
) ||
313 ((timeouts
->when
.tv_sec
== q
->when
.tv_sec
) &&
314 (timeouts
->when
.tv_usec
> q
->when
.tv_usec
))) {
320 /* Middle of list? */
321 for (t
= timeouts
; t
->next
; t
= t
->next
) {
322 if ((t
->next
->when
.tv_sec
> q
->when
.tv_sec
) ||
323 ((t
->next
->when
.tv_sec
== q
->when
.tv_sec
) &&
324 (t
->next
->when
.tv_usec
> q
->when
.tv_usec
))) {
333 q
->next
= (struct timeout
*)0;
338 * Don't bother sorting the DHCP list, just add it to the front.
339 * Eventually the list should be removed as we migrate the callers
340 * to the native ISC timer functions, if it becomes a performance
341 * problem before then we may need to order the list.
346 isc_interval_set(&interval
, sec
& DHCP_SEC_MAX
, usec
* 1000);
347 status
= isc_time_nowplusinterval(&expires
, &interval
);
348 if (status
!= ISC_R_SUCCESS
) {
350 * The system time function isn't happy or returned
351 * a value larger than isc_time_t can hold.
353 log_fatal("Unable to set up timer: %s",
354 isc_result_totext(status
));
358 status
= isc_timer_create(dhcp_gbl_ctx
.timermgr
,
359 isc_timertype_once
, &expires
,
360 NULL
, dhcp_gbl_ctx
.task
,
361 isclib_timer_callback
,
362 (void *)q
, &q
->isc_timeout
);
364 status
= isc_timer_reset(q
->isc_timeout
,
365 isc_timertype_once
, &expires
,
369 /* If it fails log an error and die */
370 if (status
!= ISC_R_SUCCESS
) {
371 log_fatal("Unable to add timeout to isclib\n");
377 void cancel_timeout (where
, what
)
378 void (*where
) (void *);
381 struct timeout
*t
, *q
;
383 /* Look for this timeout on the list, and unlink it if we find it. */
384 t
= (struct timeout
*)0;
385 for (q
= timeouts
; q
; q
= q
-> next
) {
386 if (q
->func
== where
&& q
->what
== what
) {
397 * If we found the timeout, cancel it and put it on the free list.
398 * The TRACING stuff is ugly but we don't add a timer when doing
399 * playback so we don't want to remove them then either.
402 #if defined (TRACING)
403 if (!trace_playback()) {
405 isc_timer_detach(&q
->isc_timeout
);
406 #if defined (TRACING)
411 (*q
->unref
) (&q
->what
, MDL
);
412 q
->next
= free_timeouts
;
417 #if defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
418 void cancel_all_timeouts ()
420 struct timeout
*t
, *n
;
421 for (t
= timeouts
; t
; t
= n
) {
423 isc_timer_detach(&t
->isc_timeout
);
424 if (t
->unref
&& t
->what
)
425 (*t
->unref
) (&t
->what
, MDL
);
426 t
->next
= free_timeouts
;
431 void relinquish_timeouts ()
433 struct timeout
*t
, *n
;
434 for (t
= free_timeouts
; t
; t
= n
) {