ISC DHCP 4.4.3-P1
A reference DHCPv4 and DHCPv6 implementation
 
Loading...
Searching...
No Matches
dispatch.c
Go to the documentation of this file.
1/* dispatch.c
2
3 Network input dispatcher... */
4
5/*
6 * Copyright (C) 2004-2022 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
34static struct timeout *free_timeouts;
35
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
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;
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;
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
108void
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 if (status == ISC_R_RELOAD) {
122 /*
123 * dhcp_set_control_state() will do the job.
124 * Note its first argument is ignored.
125 */
128 if (status == ISC_R_SUCCESS)
129 status = ISC_R_RELOAD;
130 }
131
132
133 if (status == ISC_R_TIMESHIFTED){
136 status = ISC_R_RELOAD;
137 log_info ("System time has been changed. Unable to use existing leases. Restarting");
138 // do nothing, restart context
139 };
140
141 } while (status == ISC_R_RELOAD);
142
143 log_fatal ("Dispatch routine failed: %s -- exiting",
144 isc_result_totext (status));
145}
146
147void
148isclib_timer_callback(isc_task_t *taskp,
149 isc_event_t *eventp)
150{
151 struct timeout *t = (struct timeout *)eventp->ev_arg;
152 struct timeout *q, *r;
153
154 /* Get the current time... */
155 gettimeofday (&cur_tv, (struct timezone *)0);
156
157 /*
158 * Find the timeout on the dhcp list and remove it.
159 * As the list isn't ordered we search the entire list
160 */
161
162 r = NULL;
163 for (q = timeouts; q; q = q->next) {
164 if (q == t) {
165 if (r)
166 r->next = q->next;
167 else
168 timeouts = q->next;
169 break;
170 }
171 r = q;
172 }
173
174 /*
175 * The timer should always be on the list. If it is we do
176 * the work and detach the timer block, if not we log an error.
177 * In both cases we attempt free the ISC event and continue
178 * processing.
179 */
180
181 if (q != NULL) {
182 /* call the callback function */
183 (*(q->func)) (q->what);
184 if (q->unref) {
185 (*q->unref) (&q->what, MDL);
186 }
187 q->next = free_timeouts;
188 isc_timer_detach(&q->isc_timeout);
189 free_timeouts = q;
190 } else {
191 /*
192 * Hmm, we should clean up the timer structure but aren't
193 * sure about the pointer to the timer block we got so
194 * don't try to - may change this to a log_fatal
195 */
196 log_error("Error finding timer structure");
197 }
198
199 isc_event_free(&eventp);
200 return;
201}
202
203/* maximum value for usec */
204#define USEC_MAX 1000000
205
206void add_timeout (when, where, what, ref, unref)
207 struct timeval *when;
208 void (*where) (void *);
209 void *what;
210 tvref_t ref;
211 tvunref_t unref;
212{
213 struct timeout *t, *q;
214 int usereset = 0;
215 isc_result_t status;
216 int64_t sec;
217 int usec;
218 isc_interval_t interval;
219 isc_time_t expires;
220
221 if (when == NULL) {
222 return;
223 }
224
225 /* See if this timeout supersedes an existing timeout. */
226 t = (struct timeout *)0;
227 for (q = timeouts; q; q = q->next) {
228 if ((where == NULL || q->func == where) &&
229 q->what == what) {
230 if (t)
231 t->next = q->next;
232 else
233 timeouts = q->next;
234 usereset = 1;
235 break;
236 }
237 t = q;
238 }
239
240 /* If we didn't supersede a timeout, allocate a timeout
241 structure now. */
242 if (!q) {
243 if (free_timeouts) {
244 q = free_timeouts;
245 free_timeouts = q->next;
246 } else {
247 q = ((struct timeout *)
248 dmalloc(sizeof(struct timeout), MDL));
249 if (!q) {
250 log_fatal("add_timeout: no memory!");
251 }
252 }
253 memset(q, 0, sizeof *q);
254 q->func = where;
255 q->ref = ref;
256 q->unref = unref;
257 if (q->ref)
258 (*q->ref)(&q->what, what, MDL);
259 else
260 q->what = what;
261 }
262
263 /*
264 * The value passed in is a time from an epoch but we need a relative
265 * time so we need to do some math to try and recover the period.
266 * This is complicated by the fact that not all of the calls cared
267 * about the usec value, if it's zero we assume the caller didn't care.
268 *
269 * The ISC timer library doesn't seem to like negative values
270 * and on 64-bit systems, isc_time_nowplusinterval() can generate range
271 * errors on values sufficiently larger than 0x7FFFFFFF (TIME_MAX), so
272 * we'll limit the interval to:
273 *
274 * 0 <= interval <= TIME_MAX - 1
275 *
276 * We do it before checking the trace option so that both the trace
277 * code and * the working code use the same values.
278 */
279
280 sec = when->tv_sec - cur_tv.tv_sec;
281 usec = when->tv_usec - cur_tv.tv_usec;
282
283 if ((when->tv_usec != 0) && (usec < 0)) {
284 sec--;
285 usec += USEC_MAX;
286 }
287
288 if (sec < 0) {
289 sec = 0;
290 usec = 0;
291 } else if (sec >= TIME_MAX) {
292 log_error("Timeout too large "
293 "reducing to: %lu (TIME_MAX - 1)",
294 (unsigned long)(TIME_MAX - 1));
295 sec = TIME_MAX - 1;
296 usec = 0;
297 } else if (usec < 0) {
298 usec = 0;
299 } else if (usec >= USEC_MAX) {
300 usec = USEC_MAX - 1;
301 }
302
303 /*
304 * This is necessary for the tracing code but we put it
305 * here in case we want to compare timing information
306 * for some reason, like debugging.
307 */
308 q->when.tv_sec = cur_tv.tv_sec + sec;
309 q->when.tv_usec = usec;
310
311#if defined (TRACING)
312 if (trace_playback()) {
313 /*
314 * If we are doing playback we need to handle the timers
315 * within this code rather than having the isclib handle
316 * them for us. We need to keep the timer list in order
317 * to allow us to find the ones to timeout.
318 *
319 * By using a different timer setup in the playback we may
320 * have variations between the orginal and the playback but
321 * it's the best we can do for now.
322 */
323
324 /* Beginning of list? */
325 if (!timeouts || (timeouts->when.tv_sec > q-> when.tv_sec) ||
326 ((timeouts->when.tv_sec == q->when.tv_sec) &&
327 (timeouts->when.tv_usec > q->when.tv_usec))) {
328 q->next = timeouts;
329 timeouts = q;
330 return;
331 }
332
333 /* Middle of list? */
334 for (t = timeouts; t->next; t = t->next) {
335 if ((t->next->when.tv_sec > q->when.tv_sec) ||
336 ((t->next->when.tv_sec == q->when.tv_sec) &&
337 (t->next->when.tv_usec > q->when.tv_usec))) {
338 q->next = t->next;
339 t->next = q;
340 return;
341 }
342 }
343
344 /* End of list. */
345 t->next = q;
346 q->next = (struct timeout *)0;
347 return;
348 }
349#endif
350 /*
351 * Don't bother sorting the DHCP list, just add it to the front.
352 * Eventually the list should be removed as we migrate the callers
353 * to the native ISC timer functions, if it becomes a performance
354 * problem before then we may need to order the list.
355 */
356 q->next = timeouts;
357 timeouts = q;
358
359 isc_interval_set(&interval, sec, usec * 1000);
360 status = isc_time_nowplusinterval(&expires, &interval);
361 if (status != ISC_R_SUCCESS) {
362 /*
363 * The system time function isn't happy. Range errors
364 * should not be possible with the check logic above.
365 */
366 log_fatal("Unable to set up timer: %s",
367 isc_result_totext(status));
368 }
369
370 if (usereset == 0) {
371 status = isc_timer_create(dhcp_gbl_ctx.timermgr,
372 isc_timertype_once, &expires,
373 NULL, dhcp_gbl_ctx.task,
375 (void *)q, &q->isc_timeout);
376 } else {
377 status = isc_timer_reset(q->isc_timeout,
378 isc_timertype_once, &expires,
379 NULL, 0);
380 }
381
382 /* If it fails log an error and die */
383 if (status != ISC_R_SUCCESS) {
384 log_fatal("Unable to add timeout to isclib\n");
385 }
386
387 return;
388}
389
390void cancel_timeout (where, what)
391 void (*where) (void *);
392 void *what;
393{
394 struct timeout *t, *q;
395
396 /* Look for this timeout on the list, and unlink it if we find it. */
397 t = (struct timeout *)0;
398 for (q = timeouts; q; q = q -> next) {
399 if (q->func == where && q->what == what) {
400 if (t)
401 t->next = q->next;
402 else
403 timeouts = q->next;
404 break;
405 }
406 t = q;
407 }
408
409 /*
410 * If we found the timeout, cancel it and put it on the free list.
411 * The TRACING stuff is ugly but we don't add a timer when doing
412 * playback so we don't want to remove them then either.
413 */
414 if (q) {
415#if defined (TRACING)
416 if (!trace_playback()) {
417#endif
418 isc_timer_detach(&q->isc_timeout);
419#if defined (TRACING)
420 }
421#endif
422
423 if (q->unref)
424 (*q->unref) (&q->what, MDL);
425 q->next = free_timeouts;
426 free_timeouts = q;
427 }
428}
429
430#if defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
432{
433 struct timeout *t, *n;
434 for (t = timeouts; t; t = n) {
435 n = t->next;
436 isc_timer_detach(&t->isc_timeout);
437 if (t->unref && t->what)
438 (*t->unref) (&t->what, MDL);
439 t->next = free_timeouts;
440 free_timeouts = t;
441 }
442}
443
445{
446 struct timeout *t, *n;
447 for (t = free_timeouts; t; t = n) {
448 n = t->next;
449 dfree(t, MDL);
450 }
451}
452#endif
void add_timeout(struct timeval *when, void *where, void *what, tvref_t ref, tvunref_t unref)
Definition dispatch.c:206
void isclib_timer_callback(isc_task_t *taskp, isc_event_t *eventp)
Definition dispatch.c:148
struct timeval * process_outstanding_timeouts(struct timeval *tvp)
Definition dispatch.c:46
void dispatch(void)
Definition dispatch.c:109
struct timeout * timeouts
Definition dispatch.c:33
void cancel_timeout(void *where, void *what)
Definition dispatch.c:390
void set_time(TIME t)
Definition dispatch.c:36
#define USEC_MAX
Definition dispatch.c:204
isc_result_t dhcp_set_control_state(control_object_state_t oldstate, control_object_state_t newstate)
Definition dhclient.c:5616
void cancel_all_timeouts(void)
void(* tvunref_t)(void *, const char *, int)
Definition dhcpd.h:1454
@ server_time_changed
Definition dhcpd.h:528
@ server_shutdown
Definition dhcpd.h:525
void relinquish_timeouts(void)
time_t TIME
Definition dhcpd.h:85
struct timeval cur_tv
Definition dispatch.c:35
void(* tvref_t)(void *, void *, const char *, int)
Definition dhcpd.h:1453
dhcp_context_t dhcp_gbl_ctx
Definition isclib.c:33
#define ISC_R_SUCCESS
#define MDL
Definition omapip.h:567
void * dmalloc(size_t, const char *, int)
Definition alloc.c:57
void dfree(void *, const char *, int)
Definition alloc.c:145
int log_error(const char *,...) __attribute__((__format__(__printf__
void log_fatal(const char *,...) __attribute__((__format__(__printf__
int int log_info(const char *,...) __attribute__((__format__(__printf__
#define TIME_MAX
Definition osdep.h:82
void(* func)(void *)
Definition dhcpd.h:1458
tvunref_t unref
Definition dhcpd.h:1461
tvref_t ref
Definition dhcpd.h:1460
void * what
Definition dhcpd.h:1459
struct timeout * next
Definition dhcpd.h:1456
isc_timer_t * isc_timeout
Definition dhcpd.h:1462
struct timeval when
Definition dhcpd.h:1457
int trace_playback(void)