]>
Commit | Line | Data |
---|---|---|
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 |
33 | struct timeout *timeouts; |
34 | static struct timeout *free_timeouts; | |
decf33c2 | 35 | |
88cd8aca | 36 | void 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 | ||
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; | |
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 | ||
108 | void | |
109 | dispatch(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 |
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 | ||
20916cae | 197 | void 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 | ||
377 | void 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) | |
418 | void 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 | ||
431 | void 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 |