1 /* $NetBSD: dispatch.c,v 1.4 2014/07/12 12:09:37 spz Exp $ */ 2 /* dispatch.c 3 4 Network input dispatcher... */ 5 6 /* 7 * Copyright (c) 2004-2011,2013 by Internet Systems Consortium, Inc. ("ISC") 8 * Copyright (c) 1995-2003 by Internet Software Consortium 9 * 10 * Permission to use, copy, modify, and distribute this software for any 11 * purpose with or without fee is hereby granted, provided that the above 12 * copyright notice and this permission notice appear in all copies. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 15 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 16 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 17 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 19 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 20 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 21 * 22 * Internet Systems Consortium, Inc. 23 * 950 Charter Street 24 * Redwood City, CA 94063 25 * <info@isc.org> 26 * https://www.isc.org/ 27 * 28 */ 29 30 #include <sys/cdefs.h> 31 __RCSID("$NetBSD: dispatch.c,v 1.4 2014/07/12 12:09:37 spz Exp $"); 32 33 #include "dhcpd.h" 34 35 #include <sys/time.h> 36 37 struct timeout *timeouts; 38 static struct timeout *free_timeouts; 39 40 void set_time(TIME t) 41 { 42 /* Do any outstanding timeouts. */ 43 if (cur_tv . tv_sec != t) { 44 cur_tv . tv_sec = t; 45 cur_tv . tv_usec = 0; 46 process_outstanding_timeouts ((struct timeval *)0); 47 } 48 } 49 50 struct timeval *process_outstanding_timeouts (struct timeval *tvp) 51 { 52 /* Call any expired timeouts, and then if there's 53 still a timeout registered, time out the select 54 call then. */ 55 another: 56 if (timeouts) { 57 struct timeout *t; 58 if ((timeouts -> when . tv_sec < cur_tv . tv_sec) || 59 ((timeouts -> when . tv_sec == cur_tv . tv_sec) && 60 (timeouts -> when . tv_usec <= cur_tv . tv_usec))) { 61 t = timeouts; 62 timeouts = timeouts -> next; 63 (*(t -> func)) (t -> what); 64 if (t -> unref) 65 (*t -> unref) (&t -> what, MDL); 66 t -> next = free_timeouts; 67 free_timeouts = t; 68 goto another; 69 } 70 if (tvp) { 71 tvp -> tv_sec = timeouts -> when . tv_sec; 72 tvp -> tv_usec = timeouts -> when . tv_usec; 73 } 74 return tvp; 75 } else 76 return (struct timeval *)0; 77 } 78 79 /* Wait for packets to come in using select(). When one does, call 80 receive_packet to receive the packet and possibly strip hardware 81 addressing information from it, and then call through the 82 bootp_packet_handler hook to try to do something with it. */ 83 84 /* 85 * Use the DHCP timeout list as a place to store DHCP specific 86 * information, but use the ISC timer system to actually dispatch 87 * the events. 88 * 89 * There are several things that the DHCP timer code does that the 90 * ISC code doesn't: 91 * 1) It allows for negative times 92 * 2) The cancel arguments are different. The DHCP code uses the 93 * function and data to find the proper timer to cancel while the 94 * ISC code uses a pointer to the timer. 95 * 3) The DHCP code includes provision for incrementing and decrementing 96 * a reference counter associated with the data. 97 * The first one is fairly easy to fix but will take some time to go throuh 98 * the callers and update them. The second is also not all that difficult 99 * in concept - add a pointer to the appropriate structures to hold a pointer 100 * to the timer and use that. The complications arise in trying to ensure 101 * that all of the corner cases are covered. The last one is potentially 102 * more painful and requires more investigation. 103 * 104 * The plan is continue with the older DHCP calls and timer list. The 105 * calls will continue to manipulate the list but will also pass a 106 * timer to the ISC timer code for the actual dispatch. Later, if desired, 107 * we can go back and modify the underlying calls to use the ISC 108 * timer functions directly without requiring all of the code to change 109 * at the same time. 110 */ 111 112 void 113 dispatch(void) 114 { 115 isc_result_t status; 116 117 do { 118 status = isc_app_ctxrun(dhcp_gbl_ctx.actx); 119 120 /* 121 * isc_app_ctxrun can be stopped by receiving a 122 * signal. It will return ISC_R_RELOAD in that 123 * case. That is a normal behavior. 124 */ 125 126 if (status == ISC_R_RELOAD) { 127 /* 128 * dhcp_set_control_state() will do the job. 129 * Note its first argument is ignored. 130 */ 131 status = dhcp_set_control_state(server_shutdown, 132 server_shutdown); 133 if (status == ISC_R_SUCCESS) 134 status = ISC_R_RELOAD; 135 } 136 } while (status == ISC_R_RELOAD); 137 138 log_fatal ("Dispatch routine failed: %s -- exiting", 139 isc_result_totext (status)); 140 } 141 142 static void 143 isclib_timer_callback(isc_task_t *taskp, 144 isc_event_t *eventp) 145 { 146 struct timeout *t = (struct timeout *)eventp->ev_arg; 147 struct timeout *q, *r; 148 149 /* Get the current time... */ 150 gettimeofday (&cur_tv, (struct timezone *)0); 151 152 /* 153 * Find the timeout on the dhcp list and remove it. 154 * As the list isn't ordered we search the entire list 155 */ 156 157 r = NULL; 158 for (q = timeouts; q; q = q->next) { 159 if (q == t) { 160 if (r) 161 r->next = q->next; 162 else 163 timeouts = q->next; 164 break; 165 } 166 r = q; 167 } 168 169 /* 170 * The timer should always be on the list. If it is we do 171 * the work and detach the timer block, if not we log an error. 172 * In both cases we attempt free the ISC event and continue 173 * processing. 174 */ 175 176 if (q != NULL) { 177 /* call the callback function */ 178 (*(q->func)) (q->what); 179 if (q->unref) { 180 (*q->unref) (&q->what, MDL); 181 } 182 q->next = free_timeouts; 183 isc_timer_detach(&q->isc_timeout); 184 free_timeouts = q; 185 } else { 186 /* 187 * Hmm, we should clean up the timer structure but aren't 188 * sure about the pointer to the timer block we got so 189 * don't try to - may change this to a log_fatal 190 */ 191 log_error("Error finding timer structure"); 192 } 193 194 isc_event_free(&eventp); 195 return; 196 } 197 198 /* maximum value for usec */ 199 #define USEC_MAX 1000000 200 #define DHCP_SEC_MAX 0xFFFFFFFF 201 202 void add_timeout (when, where, what, ref, unref) 203 struct timeval *when; 204 void (*where) (void *); 205 void *what; 206 tvref_t ref; 207 tvunref_t unref; 208 { 209 struct timeout *t, *q; 210 int usereset = 0; 211 isc_result_t status; 212 int64_t sec; 213 int usec; 214 isc_interval_t interval; 215 isc_time_t expires; 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_interval_set(&interval, sec & DHCP_SEC_MAX, usec * 1000); 347 status = isc_time_nowplusinterval(&expires, &interval); 348 if (status != ISC_R_SUCCESS) { 349 /* 350 * The system time function isn't happy or returned 351 * a value larger than isc_time_t can hold. 352 */ 353 log_fatal("Unable to set up timer: %s", 354 isc_result_totext(status)); 355 } 356 357 if (usereset == 0) { 358 status = isc_timer_create(dhcp_gbl_ctx.timermgr, 359 isc_timertype_once, &expires, 360 NULL, dhcp_gbl_ctx.task, 361 isclib_timer_callback, 362 (void *)q, &q->isc_timeout); 363 } else { 364 status = isc_timer_reset(q->isc_timeout, 365 isc_timertype_once, &expires, 366 NULL, 0); 367 } 368 369 /* If it fails log an error and die */ 370 if (status != ISC_R_SUCCESS) { 371 log_fatal("Unable to add timeout to isclib\n"); 372 } 373 374 return; 375 } 376 377 void cancel_timeout (where, what) 378 void (*where) (void *); 379 void *what; 380 { 381 struct timeout *t, *q; 382 383 /* Look for this timeout on the list, and unlink it if we find it. */ 384 t = (struct timeout *)0; 385 for (q = timeouts; q; q = q -> next) { 386 if (q->func == where && q->what == what) { 387 if (t) 388 t->next = q->next; 389 else 390 timeouts = q->next; 391 break; 392 } 393 t = q; 394 } 395 396 /* 397 * If we found the timeout, cancel it and put it on the free list. 398 * The TRACING stuff is ugly but we don't add a timer when doing 399 * playback so we don't want to remove them then either. 400 */ 401 if (q) { 402 #if defined (TRACING) 403 if (!trace_playback()) { 404 #endif 405 isc_timer_detach(&q->isc_timeout); 406 #if defined (TRACING) 407 } 408 #endif 409 410 if (q->unref) 411 (*q->unref) (&q->what, MDL); 412 q->next = free_timeouts; 413 free_timeouts = q; 414 } 415 } 416 417 #if defined (DEBUG_MEMORY_LEAKAGE_ON_EXIT) 418 void cancel_all_timeouts () 419 { 420 struct timeout *t, *n; 421 for (t = timeouts; t; t = n) { 422 n = t->next; 423 isc_timer_detach(&t->isc_timeout); 424 if (t->unref && t->what) 425 (*t->unref) (&t->what, MDL); 426 t->next = free_timeouts; 427 free_timeouts = t; 428 } 429 } 430 431 void relinquish_timeouts () 432 { 433 struct timeout *t, *n; 434 for (t = free_timeouts; t; t = n) { 435 n = t->next; 436 dfree(t, MDL); 437 } 438 } 439 #endif 440