ISC DHCP  4.3.3
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-2011,2013 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 #define DHCP_SEC_MAX 0xFFFFFFFF
197 
198 void add_timeout (when, where, what, ref, unref)
199  struct timeval *when;
200  void (*where) (void *);
201  void *what;
202  tvref_t ref;
204 {
205  struct timeout *t, *q;
206  int usereset = 0;
207  isc_result_t status;
208  int64_t sec;
209  int usec;
210  isc_interval_t interval;
211  isc_time_t expires;
212 
213  if (when == NULL) {
214  return;
215  }
216 
217  /* See if this timeout supersedes an existing timeout. */
218  t = (struct timeout *)0;
219  for (q = timeouts; q; q = q->next) {
220  if ((where == NULL || q->func == where) &&
221  q->what == what) {
222  if (t)
223  t->next = q->next;
224  else
225  timeouts = q->next;
226  usereset = 1;
227  break;
228  }
229  t = q;
230  }
231 
232  /* If we didn't supersede a timeout, allocate a timeout
233  structure now. */
234  if (!q) {
235  if (free_timeouts) {
236  q = free_timeouts;
237  free_timeouts = q->next;
238  } else {
239  q = ((struct timeout *)
240  dmalloc(sizeof(struct timeout), MDL));
241  if (!q) {
242  log_fatal("add_timeout: no memory!");
243  }
244  }
245  memset(q, 0, sizeof *q);
246  q->func = where;
247  q->ref = ref;
248  q->unref = unref;
249  if (q->ref)
250  (*q->ref)(&q->what, what, MDL);
251  else
252  q->what = what;
253  }
254 
255  /*
256  * The value passed in is a time from an epoch but we need a relative
257  * time so we need to do some math to try and recover the period.
258  * This is complicated by the fact that not all of the calls cared
259  * about the usec value, if it's zero we assume the caller didn't care.
260  *
261  * The ISC timer library doesn't seem to like negative values
262  * and can't accept any values above 4G-1 seconds so we limit
263  * the values to 0 <= value < 4G-1. We do it before
264  * checking the trace option so that both the trace code and
265  * the working code use the same values.
266  */
267 
268  sec = when->tv_sec - cur_tv.tv_sec;
269  usec = when->tv_usec - cur_tv.tv_usec;
270 
271  if ((when->tv_usec != 0) && (usec < 0)) {
272  sec--;
273  usec += USEC_MAX;
274  }
275 
276  if (sec < 0) {
277  sec = 0;
278  usec = 0;
279  } else if (sec > DHCP_SEC_MAX) {
280  log_error("Timeout requested too large "
281  "reducing to 2^^32-1");
282  sec = DHCP_SEC_MAX;
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 & DHCP_SEC_MAX);
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_time_nowplusinterval() is not safe with 64-bit time_t and will
347  * return an error for sufficiently large intervals. We have to limit
348  * the interval to INT_MAX or less to ensure the interval doesn't
349  * overflow 32 bits, since the returned isc_time_t fields are
350  * 32-bit unsigned ints.
351  *
352  * HACK: The 9 is a magic number of seconds, since some time may have
353  * gone by since the last call to gettimeofday() and the one in
354  * isc_time_nowplusinterval().
355  */
356  if (sec > TIME_MAX)
357  sec = TIME_MAX - 9;
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 or returned
364  * a value larger than isc_time_t can hold.
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 
390 void 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)
431 void cancel_all_timeouts ()
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 
444 void relinquish_timeouts ()
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
int trace_playback(void)
#define DHCP_SEC_MAX
Definition: dispatch.c:196
struct timeval cur_tv
Definition: dispatch.c:35
tvunref_t unref
Definition: dhcpd.h:1403
void * dmalloc(unsigned, const char *, int)
Definition: alloc.c:56
#define MDL
Definition: omapip.h:568
void cancel_timeout(void(*)(void *) where, void *what)
Definition: dispatch.c:390
void(* func)(void *)
Definition: dhcpd.h:1400
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 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:198
void(* tvunref_t)(void *, const char *, int)
Definition: dhcpd.h:1396
void(* tvref_t)(void *, void *, const char *, int)
Definition: dhcpd.h:1395
void dispatch(void)
Definition: dispatch.c:109
tvref_t ref
Definition: dhcpd.h:1402
void log_fatal(const char *,...) __attribute__((__format__(__printf__
struct timeval when
Definition: dhcpd.h:1399
void relinquish_timeouts(void)
isc_timer_t * isc_timeout
Definition: dhcpd.h:1404
void set_time(TIME t)
Definition: dispatch.c:36
void dfree(void *, const char *, int)
Definition: alloc.c:131
isc_appctx_t * actx
Definition: isclib.h:93
struct timeout * next
Definition: dhcpd.h:1398
#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:4488
void * what
Definition: dhcpd.h:1401
void isclib_timer_callback(isc_task_t *taskp, isc_event_t *eventp)
Definition: dispatch.c:139
struct timeout * timeouts
Definition: dispatch.c:33