1 /* $NetBSD: dispatch.c,v 1.2 2018/04/07 22:37:29 christos Exp $ */ 2 3 /* dispatch.c 4 5 Network input dispatcher... */ 6 7 /* 8 * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") 9 * Copyright (c) 1995-2003 by Internet Software Consortium 10 * 11 * This Source Code Form is subject to the terms of the Mozilla Public 12 * License, v. 2.0. If a copy of the MPL was not distributed with this 13 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 16 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 17 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 18 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 20 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 21 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 22 * 23 * Internet Systems Consortium, Inc. 24 * 950 Charter Street 25 * Redwood City, CA 94063 26 * <info@isc.org> 27 * https://www.isc.org/ 28 * 29 */ 30 31 #include <sys/cdefs.h> 32 __RCSID("$NetBSD: dispatch.c,v 1.2 2018/04/07 22:37:29 christos Exp $"); 33 34 #include "dhcpd.h" 35 36 #include <sys/time.h> 37 38 struct timeout *timeouts; 39 static struct timeout *free_timeouts; 40 41 libdhcp_callbacks_t libdhcp_callbacks; 42 43 void set_time(TIME t) 44 { 45 /* Do any outstanding timeouts. */ 46 if (cur_tv . tv_sec != t) { 47 cur_tv . tv_sec = t; 48 cur_tv . tv_usec = 0; 49 process_outstanding_timeouts ((struct timeval *)0); 50 } 51 } 52 53 struct timeval *process_outstanding_timeouts (struct timeval *tvp) 54 { 55 /* Call any expired timeouts, and then if there's 56 still a timeout registered, time out the select 57 call then. */ 58 another: 59 if (timeouts) { 60 struct timeout *t; 61 if ((timeouts -> when . tv_sec < cur_tv . tv_sec) || 62 ((timeouts -> when . tv_sec == cur_tv . tv_sec) && 63 (timeouts -> when . tv_usec <= cur_tv . tv_usec))) { 64 t = timeouts; 65 timeouts = timeouts -> next; 66 (*(t -> func)) (t -> what); 67 if (t -> unref) 68 (*t -> unref) (&t -> what, MDL); 69 t -> next = free_timeouts; 70 free_timeouts = t; 71 goto another; 72 } 73 if (tvp) { 74 tvp -> tv_sec = timeouts -> when . tv_sec; 75 tvp -> tv_usec = timeouts -> when . tv_usec; 76 } 77 return tvp; 78 } else 79 return (struct timeval *)0; 80 } 81 82 /* Wait for packets to come in using select(). When one does, call 83 receive_packet to receive the packet and possibly strip hardware 84 addressing information from it, and then call through the 85 bootp_packet_handler hook to try to do something with it. */ 86 87 /* 88 * Use the DHCP timeout list as a place to store DHCP specific 89 * information, but use the ISC timer system to actually dispatch 90 * the events. 91 * 92 * There are several things that the DHCP timer code does that the 93 * ISC code doesn't: 94 * 1) It allows for negative times 95 * 2) The cancel arguments are different. The DHCP code uses the 96 * function and data to find the proper timer to cancel while the 97 * ISC code uses a pointer to the timer. 98 * 3) The DHCP code includes provision for incrementing and decrementing 99 * a reference counter associated with the data. 100 * The first one is fairly easy to fix but will take some time to go throuh 101 * the callers and update them. The second is also not all that difficult 102 * in concept - add a pointer to the appropriate structures to hold a pointer 103 * to the timer and use that. The complications arise in trying to ensure 104 * that all of the corner cases are covered. The last one is potentially 105 * more painful and requires more investigation. 106 * 107 * The plan is continue with the older DHCP calls and timer list. The 108 * calls will continue to manipulate the list but will also pass a 109 * timer to the ISC timer code for the actual dispatch. Later, if desired, 110 * we can go back and modify the underlying calls to use the ISC 111 * timer functions directly without requiring all of the code to change 112 * at the same time. 113 */ 114 115 void 116 dispatch(void) 117 { 118 isc_result_t status; 119 120 do { 121 status = isc_app_ctxrun(dhcp_gbl_ctx.actx); 122 123 /* 124 * isc_app_ctxrun can be stopped by receiving a 125 * signal. It will return ISC_R_RELOAD in that 126 * case. That is a normal behavior. 127 */ 128 129 if (status == ISC_R_RELOAD) { 130 /* 131 * dhcp_set_control_state() will do the job. 132 * Note its first argument is ignored. 133 */ 134 status = libdhcp_callbacks.dhcp_set_control_state 135 (server_shutdown, server_shutdown); 136 if (status == ISC_R_SUCCESS) 137 status = ISC_R_RELOAD; 138 } 139 } while (status == ISC_R_RELOAD); 140 141 log_fatal ("Dispatch routine failed: %s -- exiting", 142 isc_result_totext (status)); 143 } 144 145 static void 146 isclib_timer_callback(isc_task_t *taskp, 147 isc_event_t *eventp) 148 { 149 struct timeout *t = (struct timeout *)eventp->ev_arg; 150 struct timeout *q, *r; 151 152 /* Get the current time... */ 153 gettimeofday (&cur_tv, (struct timezone *)0); 154 155 /* 156 * Find the timeout on the dhcp list and remove it. 157 * As the list isn't ordered we search the entire list 158 */ 159 160 r = NULL; 161 for (q = timeouts; q; q = q->next) { 162 if (q == t) { 163 if (r) 164 r->next = q->next; 165 else 166 timeouts = q->next; 167 break; 168 } 169 r = q; 170 } 171 172 /* 173 * The timer should always be on the list. If it is we do 174 * the work and detach the timer block, if not we log an error. 175 * In both cases we attempt free the ISC event and continue 176 * processing. 177 */ 178 179 if (q != NULL) { 180 /* call the callback function */ 181 (*(q->func)) (q->what); 182 if (q->unref) { 183 (*q->unref) (&q->what, MDL); 184 } 185 q->next = free_timeouts; 186 isc_timer_detach(&q->isc_timeout); 187 free_timeouts = q; 188 } else { 189 /* 190 * Hmm, we should clean up the timer structure but aren't 191 * sure about the pointer to the timer block we got so 192 * don't try to - may change this to a log_fatal 193 */ 194 log_error("Error finding timer structure"); 195 } 196 197 isc_event_free(&eventp); 198 return; 199 } 200 201 /* maximum value for usec */ 202 #define USEC_MAX 1000000 203 204 void add_timeout (when, where, what, ref, unref) 205 struct timeval *when; 206 void (*where) (void *); 207 void *what; 208 tvref_t ref; 209 tvunref_t unref; 210 { 211 struct timeout *t, *q; 212 int usereset = 0; 213 isc_result_t status; 214 int64_t sec; 215 int usec; 216 isc_interval_t interval; 217 isc_time_t expires; 218 219 /* See if this timeout supersedes an existing timeout. */ 220 t = (struct timeout *)0; 221 for (q = timeouts; q; q = q->next) { 222 if ((where == NULL || q->func == where) && 223 q->what == what) { 224 if (t) 225 t->next = q->next; 226 else 227 timeouts = q->next; 228 usereset = 1; 229 break; 230 } 231 t = q; 232 } 233 234 /* If we didn't supersede a timeout, allocate a timeout 235 structure now. */ 236 if (!q) { 237 if (free_timeouts) { 238 q = free_timeouts; 239 free_timeouts = q->next; 240 } else { 241 q = ((struct timeout *) 242 dmalloc(sizeof(struct timeout), MDL)); 243 if (!q) { 244 log_fatal("add_timeout: no memory!"); 245 } 246 } 247 memset(q, 0, sizeof *q); 248 q->func = where; 249 q->ref = ref; 250 q->unref = unref; 251 if (q->ref) 252 (*q->ref)(&q->what, what, MDL); 253 else 254 q->what = what; 255 } 256 257 /* 258 * The value passed in is a time from an epoch but we need a relative 259 * time so we need to do some math to try and recover the period. 260 * This is complicated by the fact that not all of the calls cared 261 * about the usec value, if it's zero we assume the caller didn't care. 262 * 263 * The ISC timer library doesn't seem to like negative values 264 * and on 64-bit systems, isc_time_nowplusinterval() can generate range 265 * errors on values sufficiently larger than 0x7FFFFFFF (TIME_MAX), so 266 * we'll limit the interval to: 267 * 268 * 0 <= interval <= TIME_MAX - 1 269 * 270 * We do it before checking the trace option so that both the trace 271 * code and * the working code use the same values. 272 */ 273 274 sec = when->tv_sec - cur_tv.tv_sec; 275 usec = when->tv_usec - cur_tv.tv_usec; 276 277 if ((when->tv_usec != 0) && (usec < 0)) { 278 sec--; 279 usec += USEC_MAX; 280 } 281 282 if (sec < 0) { 283 sec = 0; 284 usec = 0; 285 } else if (sec >= TIME_MAX) { 286 log_error("Timeout too large " 287 "reducing to: %lu (TIME_MAX - 1)", 288 (unsigned long)(TIME_MAX - 1)); 289 sec = TIME_MAX - 1; 290 usec = 0; 291 } else if (usec < 0) { 292 usec = 0; 293 } else if (usec >= USEC_MAX) { 294 usec = USEC_MAX - 1; 295 } 296 297 /* 298 * This is necessary for the tracing code but we put it 299 * here in case we want to compare timing information 300 * for some reason, like debugging. 301 */ 302 q->when.tv_sec = cur_tv.tv_sec + sec; 303 q->when.tv_usec = usec; 304 305 #if defined (TRACING) 306 if (trace_playback()) { 307 /* 308 * If we are doing playback we need to handle the timers 309 * within this code rather than having the isclib handle 310 * them for us. We need to keep the timer list in order 311 * to allow us to find the ones to timeout. 312 * 313 * By using a different timer setup in the playback we may 314 * have variations between the orginal and the playback but 315 * it's the best we can do for now. 316 */ 317 318 /* Beginning of list? */ 319 if (!timeouts || (timeouts->when.tv_sec > q-> when.tv_sec) || 320 ((timeouts->when.tv_sec == q->when.tv_sec) && 321 (timeouts->when.tv_usec > q->when.tv_usec))) { 322 q->next = timeouts; 323 timeouts = q; 324 return; 325 } 326 327 /* Middle of list? */ 328 for (t = timeouts; t->next; t = t->next) { 329 if ((t->next->when.tv_sec > q->when.tv_sec) || 330 ((t->next->when.tv_sec == q->when.tv_sec) && 331 (t->next->when.tv_usec > q->when.tv_usec))) { 332 q->next = t->next; 333 t->next = q; 334 return; 335 } 336 } 337 338 /* End of list. */ 339 t->next = q; 340 q->next = (struct timeout *)0; 341 return; 342 } 343 #endif 344 /* 345 * Don't bother sorting the DHCP list, just add it to the front. 346 * Eventually the list should be removed as we migrate the callers 347 * to the native ISC timer functions, if it becomes a performance 348 * problem before then we may need to order the list. 349 */ 350 q->next = timeouts; 351 timeouts = q; 352 353 isc_interval_set(&interval, sec, usec * 1000); 354 status = isc_time_nowplusinterval(&expires, &interval); 355 if (status != ISC_R_SUCCESS) { 356 /* 357 * The system time function isn't happy. Range errors 358 * should not be possible with the check logic above. 359 */ 360 log_fatal("Unable to set up timer: %s", 361 isc_result_totext(status)); 362 } 363 364 if (usereset == 0) { 365 status = isc_timer_create(dhcp_gbl_ctx.timermgr, 366 isc_timertype_once, &expires, 367 NULL, dhcp_gbl_ctx.task, 368 isclib_timer_callback, 369 (void *)q, &q->isc_timeout); 370 } else { 371 status = isc_timer_reset(q->isc_timeout, 372 isc_timertype_once, &expires, 373 NULL, 0); 374 } 375 376 /* If it fails log an error and die */ 377 if (status != ISC_R_SUCCESS) { 378 log_fatal("Unable to add timeout to isclib\n"); 379 } 380 381 return; 382 } 383 384 void cancel_timeout (where, what) 385 void (*where) (void *); 386 void *what; 387 { 388 struct timeout *t, *q; 389 390 /* Look for this timeout on the list, and unlink it if we find it. */ 391 t = (struct timeout *)0; 392 for (q = timeouts; q; q = q -> next) { 393 if (q->func == where && q->what == what) { 394 if (t) 395 t->next = q->next; 396 else 397 timeouts = q->next; 398 break; 399 } 400 t = q; 401 } 402 403 /* 404 * If we found the timeout, cancel it and put it on the free list. 405 * The TRACING stuff is ugly but we don't add a timer when doing 406 * playback so we don't want to remove them then either. 407 */ 408 if (q) { 409 #if defined (TRACING) 410 if (!trace_playback()) { 411 #endif 412 isc_timer_detach(&q->isc_timeout); 413 #if defined (TRACING) 414 } 415 #endif 416 417 if (q->unref) 418 (*q->unref) (&q->what, MDL); 419 q->next = free_timeouts; 420 free_timeouts = q; 421 } 422 } 423 424 #if defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) 425 void cancel_all_timeouts () 426 { 427 struct timeout *t, *n; 428 for (t = timeouts; t; t = n) { 429 n = t->next; 430 isc_timer_detach(&t->isc_timeout); 431 if (t->unref && t->what) 432 (*t->unref) (&t->what, MDL); 433 t->next = free_timeouts; 434 free_timeouts = t; 435 } 436 } 437 438 void relinquish_timeouts () 439 { 440 struct timeout *t, *n; 441 for (t = free_timeouts; t; t = n) { 442 n = t->next; 443 dfree(t, MDL); 444 } 445 } 446 #endif 447 448 void libdhcp_callbacks_register(cb) 449 libdhcp_callbacks_t *cb; 450 { 451 memcpy(&libdhcp_callbacks, cb, sizeof(libdhcp_callbacks)); 452 return; 453 } 454