]> git.ipfire.org Git - thirdparty/dhcp.git/blame - common/dispatch.c
copy rights update
[thirdparty/dhcp.git] / common / dispatch.c
CommitLineData
48d507a9 1/* dispatch.c
decf33c2 2
48d507a9 3 Network input dispatcher... */
decf33c2
TL
4
5/*
49a7fb58 6 * Copyright (C) 2004-2022 Internet Systems Consortium, Inc. ("ISC")
98311e4b 7 * Copyright (c) 1995-2003 by Internet Software Consortium
decf33c2 8 *
7512d88b
TM
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/.
decf33c2 12 *
98311e4b
DH
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.
decf33c2 20 *
98311e4b 21 * Internet Systems Consortium, Inc.
429a56d7
TM
22 * PO Box 360
23 * Newmarket, NH 03857 USA
98311e4b 24 * <info@isc.org>
2c85ac9b 25 * https://www.isc.org/
49733f31 26 *
decf33c2
TL
27 */
28
decf33c2 29#include "dhcpd.h"
decf33c2 30
3ac2a573
SR
31#include <sys/time.h>
32
6806c5ae
TL
33struct timeout *timeouts;
34static struct timeout *free_timeouts;
decf33c2 35
88cd8aca 36void set_time(TIME t)
eb6b5a20
TL
37{
38 /* Do any outstanding timeouts. */
be62cf06
FD
39 if (cur_tv . tv_sec != t) {
40 cur_tv . tv_sec = t;
41 cur_tv . tv_usec = 0;
eb6b5a20
TL
42 process_outstanding_timeouts ((struct timeval *)0);
43 }
44}
45
46struct 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;
be62cf06
FD
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))) {
eb6b5a20
TL
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) {
be62cf06
FD
67 tvp -> tv_sec = timeouts -> when . tv_sec;
68 tvp -> tv_usec = timeouts -> when . tv_usec;
eb6b5a20
TL
69 }
70 return tvp;
71 } else
72 return (struct timeval *)0;
73}
74
decf33c2
TL
75/* Wait for packets to come in using select(). When one does, call
76 receive_packet to receive the packet and possibly strip hardware
b25cbbad
TL
77 addressing information from it, and then call through the
78 bootp_packet_handler hook to try to do something with it. */
decf33c2 79
98bf1607
SR
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.
f6b8f48d 99 *
98bf1607
SR
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
108void
109dispatch(void)
decf33c2 110{
16be3937 111 isc_result_t status;
decf33c2 112
0895c955
SR
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);
47e8308d 133
0895c955
SR
134 log_fatal ("Dispatch routine failed: %s -- exiting",
135 isc_result_totext (status));
decf33c2
TL
136}
137
98bf1607
SR
138void
139isclib_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
20916cae 197void add_timeout (when, where, what, ref, unref)
be62cf06 198 struct timeval *when;
a34feb7d 199 void (*where) (void *);
e4749e75 200 void *what;
20916cae
TL
201 tvref_t ref;
202 tvunref_t unref;
6806c5ae
TL
203{
204 struct timeout *t, *q;
98bf1607
SR
205 int usereset = 0;
206 isc_result_t status;
0c9d3a81
SR
207 int64_t sec;
208 int usec;
98bf1607
SR
209 isc_interval_t interval;
210 isc_time_t expires;
6806c5ae
TL
211
212 /* See if this timeout supersedes an existing timeout. */
213 t = (struct timeout *)0;
98bf1607
SR
214 for (q = timeouts; q; q = q->next) {
215 if ((where == NULL || q->func == where) &&
216 q->what == what) {
6806c5ae 217 if (t)
98bf1607 218 t->next = q->next;
6806c5ae 219 else
98bf1607
SR
220 timeouts = q->next;
221 usereset = 1;
6806c5ae
TL
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;
98bf1607 232 free_timeouts = q->next;
6806c5ae 233 } else {
4bd8800e 234 q = ((struct timeout *)
98bf1607
SR
235 dmalloc(sizeof(struct timeout), MDL));
236 if (!q) {
237 log_fatal("add_timeout: no memory!");
238 }
6806c5ae 239 }
98bf1607
SR
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);
20916cae 246 else
98bf1607 247 q->what = what;
6806c5ae
TL
248 }
249
0c9d3a81
SR
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
2004eea2
TM
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.
0c9d3a81
SR
265 */
266
267 sec = when->tv_sec - cur_tv.tv_sec;
268 usec = when->tv_usec - cur_tv.tv_usec;
f6b8f48d 269
0c9d3a81
SR
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;
2004eea2 278 } else if (sec >= TIME_MAX) {
121a5681
TM
279 log_error("Timeout too large "
280 "reducing to: %lu (TIME_MAX - 1)",
2004eea2
TM
281 (unsigned long)(TIME_MAX - 1));
282 sec = TIME_MAX - 1;
0c9d3a81
SR
283 usec = 0;
284 } else if (usec < 0) {
285 usec = 0;
286 } else if (usec >= USEC_MAX) {
287 usec = USEC_MAX - 1;
288 }
289
f6b8f48d 290 /*
0c9d3a81
SR
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 */
2004eea2 295 q->when.tv_sec = cur_tv.tv_sec + sec;
0c9d3a81 296 q->when.tv_usec = usec;
98bf1607 297
7aa153b8
SR
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
98bf1607
SR
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
2004eea2 346 isc_interval_set(&interval, sec, usec * 1000);
98bf1607
SR
347 status = isc_time_nowplusinterval(&expires, &interval);
348 if (status != ISC_R_SUCCESS) {
349 /*
2004eea2
TM
350 * The system time function isn't happy. Range errors
351 * should not be possible with the check logic above.
98bf1607
SR
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");
6806c5ae
TL
372 }
373
98bf1607 374 return;
6806c5ae
TL
375}
376
377void cancel_timeout (where, what)
a34feb7d 378 void (*where) (void *);
e4749e75 379 void *what;
6806c5ae
TL
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) {
98bf1607 386 if (q->func == where && q->what == what) {
6806c5ae 387 if (t)
98bf1607 388 t->next = q->next;
6806c5ae 389 else
98bf1607 390 timeouts = q->next;
6806c5ae
TL
391 break;
392 }
393 t = q;
394 }
395
7aa153b8
SR
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 */
6806c5ae 401 if (q) {
7aa153b8
SR
402#if defined (TRACING)
403 if (!trace_playback()) {
404#endif
405 isc_timer_detach(&q->isc_timeout);
406#if defined (TRACING)
407 }
408#endif
98bf1607
SR
409
410 if (q->unref)
411 (*q->unref) (&q->what, MDL);
412 q->next = free_timeouts;
6806c5ae
TL
413 free_timeouts = q;
414 }
415}
d758ad8c
TL
416
417#if defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
418void cancel_all_timeouts ()
419{
420 struct timeout *t, *n;
421 for (t = timeouts; t; t = n) {
98bf1607
SR
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;
d758ad8c
TL
427 free_timeouts = t;
428 }
429}
430
431void relinquish_timeouts ()
432{
433 struct timeout *t, *n;
434 for (t = free_timeouts; t; t = n) {
98bf1607
SR
435 n = t->next;
436 dfree(t, MDL);
d758ad8c
TL
437 }
438}
439#endif