]> git.ipfire.org Git - thirdparty/dhcp.git/blob - common/dispatch.c
6687ab176fca62890f38352a54295e2ad7ec278d
[thirdparty/dhcp.git] / common / dispatch.c
1 /* dispatch.c
2
3 Network input dispatcher... */
4
5 /*
6 * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC")
7 * Copyright (c) 1995-2003 by Internet Software Consortium
8 *
9 * This Source Code Form is subject to the terms of the Mozilla Public
10 * License, v. 2.0. If a copy of the MPL was not distributed with this
11 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
19 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 *
21 * Internet Systems Consortium, Inc.
22 * PO Box 360
23 * Newmarket, NH 03857 USA
24 * <info@isc.org>
25 * https://www.isc.org/
26 *
27 */
28
29 #include "dhcpd.h"
30
31 #include <sys/time.h>
32
33 struct timeout *timeouts;
34 static struct timeout *free_timeouts;
35
36 void set_time(TIME t)
37 {
38 /* Do any outstanding timeouts. */
39 if (cur_tv . tv_sec != t) {
40 cur_tv . tv_sec = t;
41 cur_tv . tv_usec = 0;
42 process_outstanding_timeouts ((struct timeval *)0);
43 }
44 }
45
46 struct timeval *process_outstanding_timeouts (struct timeval *tvp)
47 {
48 /* Call any expired timeouts, and then if there's
49 still a timeout registered, time out the select
50 call then. */
51 another:
52 if (timeouts) {
53 struct timeout *t;
54 if ((timeouts -> when . tv_sec < cur_tv . tv_sec) ||
55 ((timeouts -> when . tv_sec == cur_tv . tv_sec) &&
56 (timeouts -> when . tv_usec <= cur_tv . tv_usec))) {
57 t = timeouts;
58 timeouts = timeouts -> next;
59 (*(t -> func)) (t -> what);
60 if (t -> unref)
61 (*t -> unref) (&t -> what, MDL);
62 t -> next = free_timeouts;
63 free_timeouts = t;
64 goto another;
65 }
66 if (tvp) {
67 tvp -> tv_sec = timeouts -> when . tv_sec;
68 tvp -> tv_usec = timeouts -> when . tv_usec;
69 }
70 return tvp;
71 } else
72 return (struct timeval *)0;
73 }
74
75 /* Wait for packets to come in using select(). When one does, call
76 receive_packet to receive the packet and possibly strip hardware
77 addressing information from it, and then call through the
78 bootp_packet_handler hook to try to do something with it. */
79
80 /*
81 * Use the DHCP timeout list as a place to store DHCP specific
82 * information, but use the ISC timer system to actually dispatch
83 * the events.
84 *
85 * There are several things that the DHCP timer code does that the
86 * ISC code doesn't:
87 * 1) It allows for negative times
88 * 2) The cancel arguments are different. The DHCP code uses the
89 * function and data to find the proper timer to cancel while the
90 * ISC code uses a pointer to the timer.
91 * 3) The DHCP code includes provision for incrementing and decrementing
92 * a reference counter associated with the data.
93 * The first one is fairly easy to fix but will take some time to go throuh
94 * the callers and update them. The second is also not all that difficult
95 * in concept - add a pointer to the appropriate structures to hold a pointer
96 * to the timer and use that. The complications arise in trying to ensure
97 * that all of the corner cases are covered. The last one is potentially
98 * more painful and requires more investigation.
99 *
100 * The plan is continue with the older DHCP calls and timer list. The
101 * calls will continue to manipulate the list but will also pass a
102 * timer to the ISC timer code for the actual dispatch. Later, if desired,
103 * we can go back and modify the underlying calls to use the ISC
104 * timer functions directly without requiring all of the code to change
105 * at the same time.
106 */
107
108 void
109 dispatch(void)
110 {
111 isc_result_t status;
112
113 do {
114 status = isc_app_ctxrun(dhcp_gbl_ctx.actx);
115
116 /*
117 * isc_app_ctxrun can be stopped by receiving a
118 * signal. It will return ISC_R_RELOAD in that
119 * case. That is a normal behavior.
120 */
121
122 if (status == ISC_R_RELOAD) {
123 /*
124 * dhcp_set_control_state() will do the job.
125 * Note its first argument is ignored.
126 */
127 status = dhcp_set_control_state(server_shutdown,
128 server_shutdown);
129 if (status == ISC_R_SUCCESS)
130 status = ISC_R_RELOAD;
131 }
132 } while (status == ISC_R_RELOAD);
133
134 log_fatal ("Dispatch routine failed: %s -- exiting",
135 isc_result_totext (status));
136 }
137
138 void
139 isclib_timer_callback(isc_task_t *taskp,
140 isc_event_t *eventp)
141 {
142 struct timeout *t = (struct timeout *)eventp->ev_arg;
143 struct timeout *q, *r;
144
145 /* Get the current time... */
146 gettimeofday (&cur_tv, (struct timezone *)0);
147
148 /*
149 * Find the timeout on the dhcp list and remove it.
150 * As the list isn't ordered we search the entire list
151 */
152
153 r = NULL;
154 for (q = timeouts; q; q = q->next) {
155 if (q == t) {
156 if (r)
157 r->next = q->next;
158 else
159 timeouts = q->next;
160 break;
161 }
162 r = q;
163 }
164
165 /*
166 * The timer should always be on the list. If it is we do
167 * the work and detach the timer block, if not we log an error.
168 * In both cases we attempt free the ISC event and continue
169 * processing.
170 */
171
172 if (q != NULL) {
173 /* call the callback function */
174 (*(q->func)) (q->what);
175 if (q->unref) {
176 (*q->unref) (&q->what, MDL);
177 }
178 q->next = free_timeouts;
179 isc_timer_detach(&q->isc_timeout);
180 free_timeouts = q;
181 } else {
182 /*
183 * Hmm, we should clean up the timer structure but aren't
184 * sure about the pointer to the timer block we got so
185 * don't try to - may change this to a log_fatal
186 */
187 log_error("Error finding timer structure");
188 }
189
190 isc_event_free(&eventp);
191 return;
192 }
193
194 /* maximum value for usec */
195 #define USEC_MAX 1000000
196
197 void add_timeout (when, where, what, ref, unref)
198 struct timeval *when;
199 void (*where) (void *);
200 void *what;
201 tvref_t ref;
202 tvunref_t unref;
203 {
204 struct timeout *t, *q;
205 int usereset = 0;
206 isc_result_t status;
207 int64_t sec;
208 int usec;
209 isc_interval_t interval;
210 isc_time_t expires;
211
212 /* See if this timeout supersedes an existing timeout. */
213 t = (struct timeout *)0;
214 for (q = timeouts; q; q = q->next) {
215 if ((where == NULL || q->func == where) &&
216 q->what == what) {
217 if (t)
218 t->next = q->next;
219 else
220 timeouts = q->next;
221 usereset = 1;
222 break;
223 }
224 t = q;
225 }
226
227 /* If we didn't supersede a timeout, allocate a timeout
228 structure now. */
229 if (!q) {
230 if (free_timeouts) {
231 q = free_timeouts;
232 free_timeouts = q->next;
233 } else {
234 q = ((struct timeout *)
235 dmalloc(sizeof(struct timeout), MDL));
236 if (!q) {
237 log_fatal("add_timeout: no memory!");
238 }
239 }
240 memset(q, 0, sizeof *q);
241 q->func = where;
242 q->ref = ref;
243 q->unref = unref;
244 if (q->ref)
245 (*q->ref)(&q->what, what, MDL);
246 else
247 q->what = what;
248 }
249
250 /*
251 * The value passed in is a time from an epoch but we need a relative
252 * time so we need to do some math to try and recover the period.
253 * This is complicated by the fact that not all of the calls cared
254 * about the usec value, if it's zero we assume the caller didn't care.
255 *
256 * The ISC timer library doesn't seem to like negative values
257 * and on 64-bit systems, isc_time_nowplusinterval() can generate range
258 * errors on values sufficiently larger than 0x7FFFFFFF (TIME_MAX), so
259 * we'll limit the interval to:
260 *
261 * 0 <= interval <= TIME_MAX - 1
262 *
263 * We do it before checking the trace option so that both the trace
264 * code and * the working code use the same values.
265 */
266
267 sec = when->tv_sec - cur_tv.tv_sec;
268 usec = when->tv_usec - cur_tv.tv_usec;
269
270 if ((when->tv_usec != 0) && (usec < 0)) {
271 sec--;
272 usec += USEC_MAX;
273 }
274
275 if (sec < 0) {
276 sec = 0;
277 usec = 0;
278 } else if (sec >= TIME_MAX) {
279 log_error("Timeout too large "
280 "reducing to: %lu (TIME_MAX - 1)",
281 (unsigned long)(TIME_MAX - 1));
282 sec = TIME_MAX - 1;
283 usec = 0;
284 } else if (usec < 0) {
285 usec = 0;
286 } else if (usec >= USEC_MAX) {
287 usec = USEC_MAX - 1;
288 }
289
290 /*
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.
294 */
295 q->when.tv_sec = cur_tv.tv_sec + sec;
296 q->when.tv_usec = usec;
297
298 #if defined (TRACING)
299 if (trace_playback()) {
300 /*
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.
305 *
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.
309 */
310
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))) {
315 q->next = timeouts;
316 timeouts = q;
317 return;
318 }
319
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))) {
325 q->next = t->next;
326 t->next = q;
327 return;
328 }
329 }
330
331 /* End of list. */
332 t->next = q;
333 q->next = (struct timeout *)0;
334 return;
335 }
336 #endif
337 /*
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.
342 */
343 q->next = timeouts;
344 timeouts = q;
345
346 isc_interval_set(&interval, sec, usec * 1000);
347 status = isc_time_nowplusinterval(&expires, &interval);
348 if (status != ISC_R_SUCCESS) {
349 /*
350 * The system time function isn't happy. Range errors
351 * should not be possible with the check logic above.
352 */
353 log_fatal("Unable to set up timer: %s",
354 isc_result_totext(status));
355 }
356
357 if (usereset == 0) {
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);
363 } else {
364 status = isc_timer_reset(q->isc_timeout,
365 isc_timertype_once, &expires,
366 NULL, 0);
367 }
368
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");
372 }
373
374 return;
375 }
376
377 void cancel_timeout (where, what)
378 void (*where) (void *);
379 void *what;
380 {
381 struct timeout *t, *q;
382
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) {
387 if (t)
388 t->next = q->next;
389 else
390 timeouts = q->next;
391 break;
392 }
393 t = q;
394 }
395
396 /*
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.
400 */
401 if (q) {
402 #if defined (TRACING)
403 if (!trace_playback()) {
404 #endif
405 isc_timer_detach(&q->isc_timeout);
406 #if defined (TRACING)
407 }
408 #endif
409
410 if (q->unref)
411 (*q->unref) (&q->what, MDL);
412 q->next = free_timeouts;
413 free_timeouts = q;
414 }
415 }
416
417 #if defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
418 void cancel_all_timeouts ()
419 {
420 struct timeout *t, *n;
421 for (t = timeouts; t; t = n) {
422 n = t->next;
423 isc_timer_detach(&t->isc_timeout);
424 if (t->unref && t->what)
425 (*t->unref) (&t->what, MDL);
426 t->next = free_timeouts;
427 free_timeouts = t;
428 }
429 }
430
431 void relinquish_timeouts ()
432 {
433 struct timeout *t, *n;
434 for (t = free_timeouts; t; t = n) {
435 n = t->next;
436 dfree(t, MDL);
437 }
438 }
439 #endif