ISC DHCP  4.3.6
A reference DHCPv4 and DHCPv6 implementation
dispatch.c
Go to the documentation of this file.
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  * Permission to use, copy, modify, and distribute this software for any
10  * purpose with or without fee is hereby granted, provided that the above
11  * copyright notice and this permission notice appear in all copies.
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  * 950 Charter Street
23  * Redwood City, CA 94063
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  */
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  if (when == NULL) {
213  return;
214  }
215 
216  /* See if this timeout supersedes an existing timeout. */
217  t = (struct timeout *)0;
218  for (q = timeouts; q; q = q->next) {
219  if ((where == NULL || q->func == where) &&
220  q->what == what) {
221  if (t)
222  t->next = q->next;
223  else
224  timeouts = q->next;
225  usereset = 1;
226  break;
227  }
228  t = q;
229  }
230 
231  /* If we didn't supersede a timeout, allocate a timeout
232  structure now. */
233  if (!q) {
234  if (free_timeouts) {
235  q = free_timeouts;
236  free_timeouts = q->next;
237  } else {
238  q = ((struct timeout *)
239  dmalloc(sizeof(struct timeout), MDL));
240  if (!q) {
241  log_fatal("add_timeout: no memory!");
242  }
243  }
244  memset(q, 0, sizeof *q);
245  q->func = where;
246  q->ref = ref;
247  q->unref = unref;
248  if (q->ref)
249  (*q->ref)(&q->what, what, MDL);
250  else
251  q->what = what;
252  }
253 
254  /*
255  * The value passed in is a time from an epoch but we need a relative
256  * time so we need to do some math to try and recover the period.
257  * This is complicated by the fact that not all of the calls cared
258  * about the usec value, if it's zero we assume the caller didn't care.
259  *
260  * The ISC timer library doesn't seem to like negative values
261  * and on 64-bit systems, isc_time_nowplusinterval() can generate range
262  * errors on values sufficiently larger than 0x7FFFFFFF (TIME_MAX), so
263  * we'll limit the interval to:
264  *
265  * 0 <= interval <= TIME_MAX - 1
266  *
267  * We do it before checking the trace option so that both the trace
268  * code and * the working code use the same values.
269  */
270 
271  sec = when->tv_sec - cur_tv.tv_sec;
272  usec = when->tv_usec - cur_tv.tv_usec;
273 
274  if ((when->tv_usec != 0) && (usec < 0)) {
275  sec--;
276  usec += USEC_MAX;
277  }
278 
279  if (sec < 0) {
280  sec = 0;
281  usec = 0;
282  } else if (sec >= TIME_MAX) {
283  log_error("Timeout too large "
284  "reducing to: %lu (TIME_MAX - 1)",
285  (unsigned long)(TIME_MAX - 1));
286  sec = TIME_MAX - 1;
287  usec = 0;
288  } else if (usec < 0) {
289  usec = 0;
290  } else if (usec >= USEC_MAX) {
291  usec = USEC_MAX - 1;
292  }
293 
294  /*
295  * This is necessary for the tracing code but we put it
296  * here in case we want to compare timing information
297  * for some reason, like debugging.
298  */
299  q->when.tv_sec = cur_tv.tv_sec + sec;
300  q->when.tv_usec = usec;
301 
302 #if defined (TRACING)
303  if (trace_playback()) {
304  /*
305  * If we are doing playback we need to handle the timers
306  * within this code rather than having the isclib handle
307  * them for us. We need to keep the timer list in order
308  * to allow us to find the ones to timeout.
309  *
310  * By using a different timer setup in the playback we may
311  * have variations between the orginal and the playback but
312  * it's the best we can do for now.
313  */
314 
315  /* Beginning of list? */
316  if (!timeouts || (timeouts->when.tv_sec > q-> when.tv_sec) ||
317  ((timeouts->when.tv_sec == q->when.tv_sec) &&
318  (timeouts->when.tv_usec > q->when.tv_usec))) {
319  q->next = timeouts;
320  timeouts = q;
321  return;
322  }
323 
324  /* Middle of list? */
325  for (t = timeouts; t->next; t = t->next) {
326  if ((t->next->when.tv_sec > q->when.tv_sec) ||
327  ((t->next->when.tv_sec == q->when.tv_sec) &&
328  (t->next->when.tv_usec > q->when.tv_usec))) {
329  q->next = t->next;
330  t->next = q;
331  return;
332  }
333  }
334 
335  /* End of list. */
336  t->next = q;
337  q->next = (struct timeout *)0;
338  return;
339  }
340 #endif
341  /*
342  * Don't bother sorting the DHCP list, just add it to the front.
343  * Eventually the list should be removed as we migrate the callers
344  * to the native ISC timer functions, if it becomes a performance
345  * problem before then we may need to order the list.
346  */
347  q->next = timeouts;
348  timeouts = q;
349 
350  isc_interval_set(&interval, sec, usec * 1000);
351  status = isc_time_nowplusinterval(&expires, &interval);
352  if (status != ISC_R_SUCCESS) {
353  /*
354  * The system time function isn't happy. Range errors
355  * should not be possible with the check logic above.
356  */
357  log_fatal("Unable to set up timer: %s",
358  isc_result_totext(status));
359  }
360 
361  if (usereset == 0) {
362  status = isc_timer_create(dhcp_gbl_ctx.timermgr,
363  isc_timertype_once, &expires,
364  NULL, dhcp_gbl_ctx.task,
366  (void *)q, &q->isc_timeout);
367  } else {
368  status = isc_timer_reset(q->isc_timeout,
369  isc_timertype_once, &expires,
370  NULL, 0);
371  }
372 
373  /* If it fails log an error and die */
374  if (status != ISC_R_SUCCESS) {
375  log_fatal("Unable to add timeout to isclib\n");
376  }
377 
378  return;
379 }
380 
381 void cancel_timeout (where, what)
382  void (*where) (void *);
383  void *what;
384 {
385  struct timeout *t, *q;
386 
387  /* Look for this timeout on the list, and unlink it if we find it. */
388  t = (struct timeout *)0;
389  for (q = timeouts; q; q = q -> next) {
390  if (q->func == where && q->what == what) {
391  if (t)
392  t->next = q->next;
393  else
394  timeouts = q->next;
395  break;
396  }
397  t = q;
398  }
399 
400  /*
401  * If we found the timeout, cancel it and put it on the free list.
402  * The TRACING stuff is ugly but we don't add a timer when doing
403  * playback so we don't want to remove them then either.
404  */
405  if (q) {
406 #if defined (TRACING)
407  if (!trace_playback()) {
408 #endif
409  isc_timer_detach(&q->isc_timeout);
410 #if defined (TRACING)
411  }
412 #endif
413 
414  if (q->unref)
415  (*q->unref) (&q->what, MDL);
416  q->next = free_timeouts;
417  free_timeouts = q;
418  }
419 }
420 
421 #if defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
422 void cancel_all_timeouts ()
423 {
424  struct timeout *t, *n;
425  for (t = timeouts; t; t = n) {
426  n = t->next;
427  isc_timer_detach(&t->isc_timeout);
428  if (t->unref && t->what)
429  (*t->unref) (&t->what, MDL);
430  t->next = free_timeouts;
431  free_timeouts = t;
432  }
433 }
434 
435 void relinquish_timeouts ()
436 {
437  struct timeout *t, *n;
438  for (t = free_timeouts; t; t = n) {
439  n = t->next;
440  dfree(t, MDL);
441  }
442 }
443 #endif
struct timeval cur_tv
Definition: dispatch.c:35
tvunref_t unref
Definition: dhcpd.h:1427
#define MDL
Definition: omapip.h:568
void cancel_timeout(void(*)(void *) where, void *what)
Definition: dispatch.c:381
void(* func)(void *)
Definition: dhcpd.h:1424
dhcp_context_t dhcp_gbl_ctx
Definition: isclib.c:33
isc_timermgr_t * timermgr
Definition: isclib.h:98
struct timeval * process_outstanding_timeouts(struct timeval *tvp)
Definition: dispatch.c:46
int trace_playback(void)
int log_error(const char *,...) __attribute__((__format__(__printf__
void cancel_all_timeouts(void)
void add_timeout(struct timeval *when, void(*)(void *) where, void *what, tvref_t ref, tvunref_t unref)
Definition: dispatch.c:197
void(* tvunref_t)(void *, const char *, int)
Definition: dhcpd.h:1420
void(* tvref_t)(void *, void *, const char *, int)
Definition: dhcpd.h:1419
void dispatch(void)
Definition: dispatch.c:109
tvref_t ref
Definition: dhcpd.h:1426
void log_fatal(const char *,...) __attribute__((__format__(__printf__
struct timeval when
Definition: dhcpd.h:1423
void relinquish_timeouts(void)
isc_timer_t * isc_timeout
Definition: dhcpd.h:1428
void set_time(TIME t)
Definition: dispatch.c:36
void dfree(void *, const char *, int)
Definition: alloc.c:145
void * dmalloc(size_t, const char *, int)
Definition: alloc.c:57
isc_appctx_t * actx
Definition: isclib.h:93
struct timeout * next
Definition: dhcpd.h:1422
#define USEC_MAX
Definition: dispatch.c:195
#define TIME_MAX
Definition: osdep.h:83
isc_task_t * task
Definition: isclib.h:96
time_t TIME
Definition: dhcpd.h:85
isc_result_t dhcp_set_control_state(control_object_state_t oldstate, control_object_state_t newstate)
Definition: dhclient.c:5241
void * what
Definition: dhcpd.h:1425
void isclib_timer_callback(isc_task_t *taskp, isc_event_t *eventp)
Definition: dispatch.c:139
struct timeout * timeouts
Definition: dispatch.c:33