common/dispatch.c

Go to the documentation of this file.
00001 /* dispatch.c
00002 
00003    Network input dispatcher... */
00004 
00005 /*
00006  * Copyright (c) 2004-2011,2013 by Internet Systems Consortium, Inc. ("ISC")
00007  * Copyright (c) 1995-2003 by Internet Software Consortium
00008  *
00009  * Permission to use, copy, modify, and distribute this software for any
00010  * purpose with or without fee is hereby granted, provided that the above
00011  * copyright notice and this permission notice appear in all copies.
00012  *
00013  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
00014  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
00015  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
00016  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
00017  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
00018  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
00019  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
00020  *
00021  *   Internet Systems Consortium, Inc.
00022  *   950 Charter Street
00023  *   Redwood City, CA 94063
00024  *   <info@isc.org>
00025  *   https://www.isc.org/
00026  *
00027  */
00028 
00029 #include "dhcpd.h"
00030 
00031 #include <sys/time.h>
00032 
00033 struct timeout *timeouts;
00034 static struct timeout *free_timeouts;
00035 
00036 void set_time(TIME t)
00037 {
00038         /* Do any outstanding timeouts. */
00039         if (cur_tv . tv_sec != t) {
00040                 cur_tv . tv_sec = t;
00041                 cur_tv . tv_usec = 0;
00042                 process_outstanding_timeouts ((struct timeval *)0);
00043         }
00044 }
00045 
00046 struct timeval *process_outstanding_timeouts (struct timeval *tvp)
00047 {
00048         /* Call any expired timeouts, and then if there's
00049            still a timeout registered, time out the select
00050            call then. */
00051       another:
00052         if (timeouts) {
00053                 struct timeout *t;
00054                 if ((timeouts -> when . tv_sec < cur_tv . tv_sec) ||
00055                     ((timeouts -> when . tv_sec == cur_tv . tv_sec) &&
00056                      (timeouts -> when . tv_usec <= cur_tv . tv_usec))) {
00057                         t = timeouts;
00058                         timeouts = timeouts -> next;
00059                         (*(t -> func)) (t -> what);
00060                         if (t -> unref)
00061                                 (*t -> unref) (&t -> what, MDL);
00062                         t -> next = free_timeouts;
00063                         free_timeouts = t;
00064                         goto another;
00065                 }
00066                 if (tvp) {
00067                         tvp -> tv_sec = timeouts -> when . tv_sec;
00068                         tvp -> tv_usec = timeouts -> when . tv_usec;
00069                 }
00070                 return tvp;
00071         } else
00072                 return (struct timeval *)0;
00073 }
00074 
00075 /* Wait for packets to come in using select().   When one does, call
00076    receive_packet to receive the packet and possibly strip hardware
00077    addressing information from it, and then call through the
00078    bootp_packet_handler hook to try to do something with it. */
00079 
00080 /*
00081  * Use the DHCP timeout list as a place to store DHCP specific
00082  * information, but use the ISC timer system to actually dispatch
00083  * the events.
00084  *
00085  * There are several things that the DHCP timer code does that the
00086  * ISC code doesn't:
00087  * 1) It allows for negative times
00088  * 2) The cancel arguments are different.  The DHCP code uses the
00089  * function and data to find the proper timer to cancel while the
00090  * ISC code uses a pointer to the timer.
00091  * 3) The DHCP code includes provision for incrementing and decrementing
00092  * a reference counter associated with the data.
00093  * The first one is fairly easy to fix but will take some time to go throuh
00094  * the callers and update them.  The second is also not all that difficult
00095  * in concept - add a pointer to the appropriate structures to hold a pointer
00096  * to the timer and use that.  The complications arise in trying to ensure
00097  * that all of the corner cases are covered.  The last one is potentially
00098  * more painful and requires more investigation.
00099  * 
00100  * The plan is continue with the older DHCP calls and timer list.  The
00101  * calls will continue to manipulate the list but will also pass a
00102  * timer to the ISC timer code for the actual dispatch.  Later, if desired,
00103  * we can go back and modify the underlying calls to use the ISC
00104  * timer functions directly without requiring all of the code to change
00105  * at the same time.
00106  */
00107 
00108 void
00109 dispatch(void)
00110 {
00111         isc_result_t status;
00112 
00113         do {
00114                 status = isc_app_ctxrun(dhcp_gbl_ctx.actx);
00115 
00116                 /*
00117                  * isc_app_ctxrun can be stopped by receiving a
00118                  * signal. It will return ISC_R_RELOAD in that
00119                  * case. That is a normal behavior.
00120                  */
00121 
00122                 if (status == ISC_R_RELOAD) {
00123                         /*
00124                          * dhcp_set_control_state() will do the job.
00125                          * Note its first argument is ignored.
00126                          */
00127                         status = dhcp_set_control_state(server_shutdown,
00128                                                         server_shutdown);
00129                         if (status == ISC_R_SUCCESS)
00130                                 status = ISC_R_RELOAD;
00131                 }
00132         } while (status == ISC_R_RELOAD);
00133 
00134         log_fatal ("Dispatch routine failed: %s -- exiting",
00135                    isc_result_totext (status));
00136 }
00137 
00138 void
00139 isclib_timer_callback(isc_task_t  *taskp,
00140                       isc_event_t *eventp)
00141 {
00142         struct timeout *t = (struct timeout *)eventp->ev_arg;
00143         struct timeout *q, *r;
00144 
00145         /* Get the current time... */
00146         gettimeofday (&cur_tv, (struct timezone *)0);
00147 
00148         /*
00149          * Find the timeout on the dhcp list and remove it.
00150          * As the list isn't ordered we search the entire list
00151          */
00152 
00153         r = NULL;
00154         for (q = timeouts; q; q = q->next) {
00155                 if (q == t) {
00156                         if (r)
00157                                 r->next = q->next;
00158                         else
00159                                 timeouts = q->next;
00160                         break;
00161                 }
00162                 r = q;
00163         }
00164 
00165         /*
00166          * The timer should always be on the list.  If it is we do
00167          * the work and detach the timer block, if not we log an error.
00168          * In both cases we attempt free the ISC event and continue
00169          * processing.
00170          */
00171 
00172         if (q != NULL) {
00173                 /* call the callback function */
00174                 (*(q->func)) (q->what);
00175                 if (q->unref) {
00176                         (*q->unref) (&q->what, MDL);
00177                 }
00178                 q->next = free_timeouts;
00179                 isc_timer_detach(&q->isc_timeout);
00180                 free_timeouts = q;
00181         } else {
00182                 /*
00183                  * Hmm, we should clean up the timer structure but aren't
00184                  * sure about the pointer to the timer block we got so
00185                  * don't try to - may change this to a log_fatal
00186                  */
00187                 log_error("Error finding timer structure");
00188         }
00189 
00190         isc_event_free(&eventp);
00191         return;
00192 }
00193 
00194 /* maximum value for usec */
00195 #define USEC_MAX 1000000
00196 #define DHCP_SEC_MAX  0xFFFFFFFF
00197 
00198 void add_timeout (when, where, what, ref, unref)
00199         struct timeval *when;
00200         void (*where) (void *);
00201         void *what;
00202         tvref_t ref;
00203         tvunref_t unref;
00204 {
00205         struct timeout *t, *q;
00206         int usereset = 0;
00207         isc_result_t status;
00208         int64_t sec;
00209         int usec;
00210         isc_interval_t interval;
00211         isc_time_t expires;
00212 
00213         if (when == NULL) {
00214                 return;
00215         }
00216 
00217         /* See if this timeout supersedes an existing timeout. */
00218         t = (struct timeout *)0;
00219         for (q = timeouts; q; q = q->next) {
00220                 if ((where == NULL || q->func == where) &&
00221                     q->what == what) {
00222                         if (t)
00223                                 t->next = q->next;
00224                         else
00225                                 timeouts = q->next;
00226                         usereset = 1;
00227                         break;
00228                 }
00229                 t = q;
00230         }
00231 
00232         /* If we didn't supersede a timeout, allocate a timeout
00233            structure now. */
00234         if (!q) {
00235                 if (free_timeouts) {
00236                         q = free_timeouts;
00237                         free_timeouts = q->next;
00238                 } else {
00239                         q = ((struct timeout *)
00240                              dmalloc(sizeof(struct timeout), MDL));
00241                         if (!q) {
00242                                 log_fatal("add_timeout: no memory!");
00243                         }
00244                 }
00245                 memset(q, 0, sizeof *q);
00246                 q->func = where;
00247                 q->ref = ref;
00248                 q->unref = unref;
00249                 if (q->ref)
00250                         (*q->ref)(&q->what, what, MDL);
00251                 else
00252                         q->what = what;
00253         }
00254 
00255         /*
00256          * The value passed in is a time from an epoch but we need a relative
00257          * time so we need to do some math to try and recover the period.
00258          * This is complicated by the fact that not all of the calls cared
00259          * about the usec value, if it's zero we assume the caller didn't care.
00260          *
00261          * The ISC timer library doesn't seem to like negative values
00262          * and can't accept any values above 4G-1 seconds so we limit
00263          * the values to 0 <= value < 4G-1.  We do it before
00264          * checking the trace option so that both the trace code and
00265          * the working code use the same values.
00266          */
00267 
00268         sec  = when->tv_sec - cur_tv.tv_sec;
00269         usec = when->tv_usec - cur_tv.tv_usec;
00270         
00271         if ((when->tv_usec != 0) && (usec < 0)) {
00272                 sec--;
00273                 usec += USEC_MAX;
00274         }
00275 
00276         if (sec < 0) {
00277                 sec  = 0;
00278                 usec = 0;
00279         } else if (sec > DHCP_SEC_MAX) {
00280                 log_error("Timeout requested too large "
00281                           "reducing to 2^^32-1");
00282                 sec = DHCP_SEC_MAX;
00283                 usec = 0;
00284         } else if (usec < 0) {
00285                 usec = 0;
00286         } else if (usec >= USEC_MAX) {
00287                 usec = USEC_MAX - 1;
00288         }
00289 
00290         /* 
00291          * This is necessary for the tracing code but we put it
00292          * here in case we want to compare timing information
00293          * for some reason, like debugging.
00294          */
00295         q->when.tv_sec  = cur_tv.tv_sec + (sec & DHCP_SEC_MAX);
00296         q->when.tv_usec = usec;
00297 
00298 #if defined (TRACING)
00299         if (trace_playback()) {
00300                 /*
00301                  * If we are doing playback we need to handle the timers
00302                  * within this code rather than having the isclib handle
00303                  * them for us.  We need to keep the timer list in order
00304                  * to allow us to find the ones to timeout.
00305                  *
00306                  * By using a different timer setup in the playback we may
00307                  * have variations between the orginal and the playback but
00308                  * it's the best we can do for now.
00309                  */
00310 
00311                 /* Beginning of list? */
00312                 if (!timeouts || (timeouts->when.tv_sec > q-> when.tv_sec) ||
00313                     ((timeouts->when.tv_sec == q->when.tv_sec) &&
00314                      (timeouts->when.tv_usec > q->when.tv_usec))) {
00315                         q->next = timeouts;
00316                         timeouts = q;
00317                         return;
00318                 }
00319 
00320                 /* Middle of list? */
00321                 for (t = timeouts; t->next; t = t->next) {
00322                         if ((t->next->when.tv_sec > q->when.tv_sec) ||
00323                             ((t->next->when.tv_sec == q->when.tv_sec) &&
00324                              (t->next->when.tv_usec > q->when.tv_usec))) {
00325                                 q->next = t->next;
00326                                 t->next = q;
00327                                 return;
00328                         }
00329                 }
00330 
00331                 /* End of list. */
00332                 t->next = q;
00333                 q->next = (struct timeout *)0;
00334                 return;
00335         }
00336 #endif
00337         /*
00338          * Don't bother sorting the DHCP list, just add it to the front.
00339          * Eventually the list should be removed as we migrate the callers
00340          * to the native ISC timer functions, if it becomes a performance
00341          * problem before then we may need to order the list.
00342          */
00343         q->next  = timeouts;
00344         timeouts = q;
00345 
00346         /* isc_time_nowplusinterval() is not safe with 64-bit time_t and will
00347          * return an error for sufficiently large intervals.  We have to limit
00348          * the interval to INT_MAX or less to ensure the interval doesn't
00349          * overflow 32 bits, since the returned isc_time_t fields are
00350          * 32-bit unsigned ints.
00351          *
00352          * HACK: The 9 is a magic number of seconds, since some time may have
00353          * gone by since the last call to gettimeofday() and the one in
00354          * isc_time_nowplusinterval().
00355          */
00356         if (sec > TIME_MAX)
00357                 sec = TIME_MAX - 9;
00358 
00359         isc_interval_set(&interval, sec, usec * 1000);
00360         status = isc_time_nowplusinterval(&expires, &interval);
00361         if (status != ISC_R_SUCCESS) {
00362                 /*
00363                  * The system time function isn't happy or returned
00364                  * a value larger than isc_time_t can hold.
00365                  */
00366                 log_fatal("Unable to set up timer: %s",
00367                           isc_result_totext(status));
00368         }
00369 
00370         if (usereset == 0) {
00371                 status = isc_timer_create(dhcp_gbl_ctx.timermgr,
00372                                           isc_timertype_once, &expires,
00373                                           NULL, dhcp_gbl_ctx.task,
00374                                           isclib_timer_callback,
00375                                           (void *)q, &q->isc_timeout);
00376         } else {
00377                 status = isc_timer_reset(q->isc_timeout,
00378                                          isc_timertype_once, &expires,
00379                                          NULL, 0);
00380         }
00381 
00382         /* If it fails log an error and die */
00383         if (status != ISC_R_SUCCESS) {
00384                 log_fatal("Unable to add timeout to isclib\n");
00385         }
00386 
00387         return;
00388 }
00389 
00390 void cancel_timeout (where, what)
00391         void (*where) (void *);
00392         void *what;
00393 {
00394         struct timeout *t, *q;
00395 
00396         /* Look for this timeout on the list, and unlink it if we find it. */
00397         t = (struct timeout *)0;
00398         for (q = timeouts; q; q = q -> next) {
00399                 if (q->func == where && q->what == what) {
00400                         if (t)
00401                                 t->next = q->next;
00402                         else
00403                                 timeouts = q->next;
00404                         break;
00405                 }
00406                 t = q;
00407         }
00408 
00409         /*
00410          * If we found the timeout, cancel it and put it on the free list.
00411          * The TRACING stuff is ugly but we don't add a timer when doing
00412          * playback so we don't want to remove them then either.
00413          */
00414         if (q) {
00415 #if defined (TRACING)
00416                 if (!trace_playback()) {
00417 #endif
00418                         isc_timer_detach(&q->isc_timeout);
00419 #if defined (TRACING)
00420                 }
00421 #endif
00422 
00423                 if (q->unref)
00424                         (*q->unref) (&q->what, MDL);
00425                 q->next = free_timeouts;
00426                 free_timeouts = q;
00427         }
00428 }
00429 
00430 #if defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT)
00431 void cancel_all_timeouts ()
00432 {
00433         struct timeout *t, *n;
00434         for (t = timeouts; t; t = n) {
00435                 n = t->next;
00436                 isc_timer_detach(&t->isc_timeout);
00437                 if (t->unref && t->what)
00438                         (*t->unref) (&t->what, MDL);
00439                 t->next = free_timeouts;
00440                 free_timeouts = t;
00441         }
00442 }
00443 
00444 void relinquish_timeouts ()
00445 {
00446         struct timeout *t, *n;
00447         for (t = free_timeouts; t; t = n) {
00448                 n = t->next;
00449                 dfree(t, MDL);
00450         }
00451 }
00452 #endif

Generated on 5 Apr 2014 for ISC DHCP by  doxygen 1.6.1