ISC DHCP  4.4.1
A reference DHCPv4 and DHCPv6 implementation
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
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  * 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  * 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  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 
147 void
148 isclib_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 
206 void add_timeout (when, where, what, ref, unref)
207  struct timeval *when;
208  void (*where) (void *);
209  void *what;
210  tvref_t ref;
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 
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
tvunref_t unref
Definition: dhcpd.h:1445
#define MDL
Definition: omapip.h:567
void cancel_timeout(void(*)(void *) where, void *what)
Definition: dispatch.c:390
dhcp_context_t dhcp_gbl_ctx
Definition: isclib.c:33
isc_timermgr_t * timermgr
Definition: isclib.h:98
return ISC_R_SUCCESS
Definition: parse.c:1341
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:206
log_fatal("no memory for uname information.")
void dispatch(void)
Definition: dispatch.c:109
tvref_t ref
Definition: dhcpd.h:1444
struct timeval when
Definition: dhcpd.h:1441
void relinquish_timeouts(void)
isc_timer_t * isc_timeout
Definition: dhcpd.h:1446
void set_time(TIME t)
Definition: dispatch.c:36
void(* tvref_t)(void *, void *, const char *, int)
Definition: dhcpd.h:1437
int int log_info(const char *,...) __attribute__((__format__(__printf__
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:1440
void(* tvunref_t)(void *, const char *, int)
Definition: dhcpd.h:1438
dfree(uname, MDL)
struct timeval cur_tv
Definition: dispatch.c:35
#define USEC_MAX
Definition: dispatch.c:204
#define TIME_MAX
Definition: osdep.h:82
int sec
Definition: parse.c:980
isc_task_t * task
Definition: isclib.h:96
time_t TIME
Definition: dhcpd.h:85
void(* func)(void *)
Definition: dhcpd.h:1442
isc_result_t dhcp_set_control_state(control_object_state_t oldstate, control_object_state_t newstate)
Definition: dhclient.c:5350
void * what
Definition: dhcpd.h:1443
void isclib_timer_callback(isc_task_t *taskp, isc_event_t *eventp)
Definition: dispatch.c:148
struct timeout * timeouts
Definition: dispatch.c:33