1 /* $NetBSD: ntpsim.c,v 1.6 2020/05/25 20:47:25 christos Exp $ */ 2 3 /* ntpdsim.c 4 * 5 * The source code for the ntp discrete event simulator. 6 * 7 * Written By: Sachin Kamboj 8 * University of Delaware 9 * Newark, DE 19711 10 * Copyright (c) 2006 11 * (Some code shamelessly based on the original NTP discrete event simulator) 12 */ 13 14 #include <config.h> 15 #ifdef SIM 16 #include "ntpd.h" 17 #include "ntp_config.h" 18 19 /* forward prototypes */ 20 int determine_event_ordering(const Event *e1, const Event *e2); 21 int determine_recv_buf_ordering(const struct recvbuf *b1, 22 const struct recvbuf *b2); 23 void create_server_associations(void); 24 void init_sim_io(void); 25 26 /* Global Variable Definitions */ 27 sim_info simulation; /* Simulation Control Variables */ 28 local_clock_info simclock; /* Local Clock Variables */ 29 queue *event_queue; /* Event Queue */ 30 queue *recv_queue; /* Receive Queue */ 31 static double sys_residual = 0; /* adjustment residue (s) */ 32 33 void (*event_ptr[]) (Event *) = { 34 sim_event_beep, sim_update_clocks, sim_event_timer, sim_event_recv_packet 35 }; /* Function pointer to the events */ 36 37 38 /* 39 * Define a function to compare two events to determine which one occurs 40 * first. 41 */ 42 int 43 determine_event_ordering( 44 const Event *e1, 45 const Event *e2 46 ) 47 { 48 return (e1->time - e2->time); 49 } 50 51 52 /* 53 * Define a function to compare two received packets to determine which 54 * one is received first. 55 */ 56 int 57 determine_recv_buf_ordering( 58 const struct recvbuf *b1, 59 const struct recvbuf *b2 60 ) 61 { 62 double recv_time1; 63 double recv_time2; 64 65 /* Simply convert the time received to double and subtract */ 66 LFPTOD(&b1->recv_time, recv_time1); 67 LFPTOD(&b2->recv_time, recv_time2); 68 69 return (int)(recv_time1 - recv_time2); 70 } 71 72 73 /* Define a function to create the server associations */ 74 void create_server_associations(void) 75 { 76 int i; 77 78 for (i = 0; i < simulation.num_of_servers; ++i) { 79 printf("%s\n", stoa(simulation.servers[i].addr)); 80 if (peer_config(simulation.servers[i].addr, 81 NULL, 82 loopback_interface, 83 MODE_CLIENT, 84 -1, 85 NTP_VERSION, 86 NTP_MINDPOLL, 87 NTP_MAXDPOLL, 88 0, /* peerflags */ 89 0, /* ttl */ 90 0, /* peerkey */ 91 NULL /* group ident */) == 0) { 92 fprintf(stderr, 93 "ERROR!! Could not create association for: %s\n", 94 stoa(simulation.servers[i].addr)); 95 } 96 } 97 } 98 99 100 /* Main Simulator Code */ 101 102 int 103 ntpsim( 104 int argc, 105 char * argv[] 106 ) 107 { 108 Event * curr_event; 109 struct timeval seed; 110 111 /* Initialize the local Clock */ 112 simclock.local_time = 0; 113 simclock.adj = 0; 114 simclock.slew = 500e-6; 115 116 /* Initialize the simulation */ 117 simulation.num_of_servers = 0; 118 simulation.beep_delay = BEEP_DLY; 119 simulation.sim_time = 0; 120 simulation.end_time = SIM_TIME; 121 122 /* Initialize ntp modules */ 123 initializing = TRUE; 124 msyslog_term = TRUE; 125 init_sim_io(); 126 init_auth(); 127 init_util(); 128 init_restrict(); 129 init_mon(); 130 init_timer(); 131 init_lib(); 132 init_request(); 133 init_control(); 134 init_peer(); 135 init_proto(); 136 init_loopfilter(); 137 mon_start(MON_OFF); 138 139 /* Call getconfig to parse the configuration file */ 140 getconfig(argc, argv); 141 loop_config(LOOP_DRIFTINIT, 0); 142 initializing = FALSE; 143 144 /* 145 * Watch out here, we want the real time, not the silly stuff. 146 */ 147 gettimeofday(&seed, NULL); 148 ntp_srandom(seed.tv_usec); 149 150 /* Initialize the event queue */ 151 event_queue = create_priority_queue((q_order_func) 152 determine_event_ordering); 153 154 /* Initialize the receive queue */ 155 recv_queue = create_priority_queue((q_order_func) 156 determine_recv_buf_ordering); 157 158 /* Push a beep and a timer on the event queue */ 159 enqueue(event_queue, event(0, BEEP)); 160 enqueue(event_queue, event(simulation.sim_time + 1.0, TIMER)); 161 162 /* 163 * Pop the queue until nothing is left or time is exceeded 164 */ 165 /* maxtime = simulation.sim_time + simulation.end_time;*/ 166 while (simulation.sim_time <= simulation.end_time && 167 (!empty(event_queue))) { 168 curr_event = dequeue(event_queue); 169 /* Update all the clocks to the time on the event */ 170 sim_update_clocks(curr_event); 171 172 /* Execute the function associated with the event */ 173 (*event_ptr[curr_event->function])(curr_event); 174 free_node(curr_event); 175 } 176 printf("sys_received: %lu\n", sys_received); 177 printf("sys_badlength: %lu\n", sys_badlength); 178 printf("sys_declined: %lu\n", sys_declined); 179 printf("sys_restricted: %lu\n", sys_restricted); 180 printf("sys_newversion: %lu\n", sys_newversion); 181 printf("sys_oldversion: %lu\n", sys_oldversion); 182 printf("sys_limitrejected: %lu\n", sys_limitrejected); 183 printf("sys_badauth: %lu\n", sys_badauth); 184 185 return (0); 186 } 187 188 189 void 190 init_sim_io(void) 191 { 192 loopback_interface = emalloc_zero(sizeof(*loopback_interface)); 193 ep_list = loopback_interface; 194 strlcpy(loopback_interface->name, "IPv4loop", 195 sizeof(loopback_interface->name)); 196 loopback_interface->flags = INT_UP | INT_LOOPBACK; 197 loopback_interface->fd = -1; 198 loopback_interface->bfd = -1; 199 loopback_interface->ifnum = 1; 200 loopback_interface->family = AF_INET; 201 AF(&loopback_interface->sin) = AF_INET; 202 SET_ADDR4(&loopback_interface->sin, LOOPBACKADR); 203 SET_PORT(&loopback_interface->sin, NTP_PORT); 204 AF(&loopback_interface->mask) = AF_INET; 205 SET_ADDR4(&loopback_interface->mask, LOOPNETMASK); 206 } 207 208 209 /* Define a function to create an return an Event */ 210 211 Event *event(double t, funcTkn f) 212 { 213 Event *e; 214 215 if ((e = get_node(sizeof(*e))) == NULL) 216 abortsim("get_node failed in event"); 217 e->time = t; 218 e->function = f; 219 return (e); 220 } 221 222 /* NTP SIMULATION FUNCTIONS */ 223 224 /* Define a function for processing a timer interrupt. 225 * On every timer interrupt, call the NTP timer to send packets and process 226 * the clock and then call the receive function to receive packets. 227 */ 228 void sim_event_timer(Event *e) 229 { 230 struct recvbuf *rbuf; 231 232 /* Call the NTP timer. 233 * This will be responsible for actually "sending the packets." 234 * Since this is a simulation, the packets sent over the network 235 * will be processed by the simulate_server routine below. 236 */ 237 timer(); 238 239 /* Process received buffers */ 240 while (!empty(recv_queue)) { 241 rbuf = (struct recvbuf *)dequeue(recv_queue); 242 (*rbuf->receiver)(rbuf); 243 free_node(rbuf); 244 } 245 246 /* Arm the next timer interrupt. */ 247 enqueue(event_queue, 248 event(simulation.sim_time + (1 << EVENT_TIMEOUT), TIMER)); 249 } 250 251 252 253 /* Define a function to simulate a server. 254 * This function processes the sent packet according to the server script, 255 * creates a reply packet and pushes the reply packet onto the event queue 256 */ 257 int simulate_server( 258 sockaddr_u *serv_addr, /* Address of the server */ 259 endpt * inter, /* Interface on which the reply should 260 be inserted */ 261 struct pkt *rpkt /* Packet sent to the server that 262 needs to be processed. */ 263 ) 264 { 265 struct pkt xpkt; /* Packet to be transmitted back 266 to the client */ 267 struct recvbuf rbuf; /* Buffer for the received packet */ 268 Event *e; /* Packet receive event */ 269 server_info *server; /* Pointer to the server being simulated */ 270 script_info *curr_script; /* Current script being processed */ 271 int i; 272 double d1, d2, d3; /* Delays while the packet is enroute */ 273 double t1, t2, t3, t4; /* The four timestamps in the packet */ 274 l_fp lfp_host; /* host-order l_fp */ 275 276 ZERO(xpkt); 277 ZERO(rbuf); 278 279 /* Search for the server with the desired address */ 280 server = NULL; 281 for (i = 0; i < simulation.num_of_servers; ++i) { 282 if (memcmp(simulation.servers[i].addr, serv_addr, 283 sizeof(*serv_addr)) == 0) { 284 server = &simulation.servers[i]; 285 break; 286 } 287 } 288 289 fprintf(stderr, "Received packet from %s on %s\n", 290 stoa(serv_addr), latoa(inter)); 291 if (server == NULL) 292 abortsim("Server with specified address not found!!!"); 293 294 /* Get the current script for the server */ 295 curr_script = server->curr_script; 296 297 /* Create a server reply packet. 298 * Masquerade the reply as a stratum-1 server with a GPS clock 299 */ 300 xpkt.li_vn_mode = PKT_LI_VN_MODE(LEAP_NOWARNING, NTP_VERSION, 301 MODE_SERVER); 302 xpkt.stratum = STRATUM_TO_PKT(((u_char)1)); 303 memcpy(&xpkt.refid, "GPS", 4); 304 xpkt.ppoll = rpkt->ppoll; 305 xpkt.precision = rpkt->precision; 306 xpkt.rootdelay = 0; 307 xpkt.rootdisp = 0; 308 309 /* TIMESTAMP CALCULATIONS 310 t1 t4 311 \ / 312 d1 \ / d3 313 \ / 314 t2 ----------------- t3 315 d2 316 */ 317 /* Compute the delays */ 318 d1 = poisson(curr_script->prop_delay, curr_script->jitter); 319 d2 = poisson(curr_script->proc_delay, 0); 320 d3 = poisson(curr_script->prop_delay, curr_script->jitter); 321 322 /* Note: In the transmitted packet: 323 * 1. t1 and t4 are times in the client according to the local clock. 324 * 2. t2 and t3 are server times according to the simulated server. 325 * Compute t1, t2, t3 and t4 326 * Note: This function is called at time t1. 327 */ 328 329 NTOHL_FP(&rpkt->xmt, &lfp_host); 330 LFPTOD(&lfp_host, t1); 331 t2 = server->server_time + d1; 332 t3 = server->server_time + d1 + d2; 333 t4 = t1 + d1 + d2 + d3; 334 335 /* Save the timestamps */ 336 xpkt.org = rpkt->xmt; 337 DTOLFP(t2, &lfp_host); 338 HTONL_FP(&lfp_host, &xpkt.rec); 339 DTOLFP(t3, &lfp_host); 340 HTONL_FP(&lfp_host, &xpkt.xmt); 341 xpkt.reftime = xpkt.xmt; 342 343 /* 344 * Ok, we are done with the packet. Now initialize the receive 345 * buffer for the packet. 346 */ 347 rbuf.used = 1; 348 rbuf.receiver = &receive; /* callback to process the packet */ 349 rbuf.recv_length = LEN_PKT_NOMAC; 350 rbuf.recv_pkt = xpkt; 351 rbuf.dstadr = inter; 352 rbuf.fd = inter->fd; 353 memcpy(&rbuf.srcadr, serv_addr, sizeof(rbuf.srcadr)); 354 memcpy(&rbuf.recv_srcadr, serv_addr, sizeof(rbuf.recv_srcadr)); 355 356 /* 357 * Create a packet event and insert it onto the event_queue at the 358 * arrival time (t4) of the packet at the client 359 */ 360 e = event(t4, PACKET); 361 e->rcv_buf = rbuf; 362 enqueue(event_queue, e); 363 364 /* 365 * Check if the time of the script has expired. If yes, delete it. 366 */ 367 if (curr_script->duration > simulation.sim_time && 368 NULL == HEAD_PFIFO(server->script)) { 369 printf("Hello\n"); 370 /* 371 * For some reason freeing up the curr_script memory kills the 372 * simulation. Further debugging is needed to determine why. 373 * free(curr_script); 374 */ 375 UNLINK_FIFO(curr_script, *server->script, link); 376 } 377 378 return (0); 379 } 380 381 382 /* Define a function to update all the clocks 383 * Most of the code is modified from the systime.c file by Prof. Mills 384 */ 385 386 void sim_update_clocks(Event *e) 387 { 388 double time_gap; 389 double adj; 390 int i; 391 392 /* Compute the time between the last update event and this update */ 393 time_gap = e->time - simulation.sim_time; 394 395 if (time_gap < 0) 396 printf("WARNING: e->time %.6g comes before sim_time %.6g (gap %+.6g)\n", 397 e->time, simulation.sim_time, time_gap); 398 399 /* Advance the client clock */ 400 if (e->time + time_gap < simclock.local_time) 401 printf("WARNING: e->time + gap %.6g comes before local_time %.6g\n", 402 e->time + time_gap, simclock.local_time); 403 simclock.local_time = e->time + time_gap; 404 405 /* Advance the simulation time */ 406 simulation.sim_time = e->time; 407 408 /* Advance the server clocks adjusted for systematic and random frequency 409 * errors. The random error is a random walk computed as the 410 * integral of samples from a Gaussian distribution. 411 */ 412 for (i = 0; i < simulation.num_of_servers; ++i) { 413 simulation.servers[i].curr_script->freq_offset += 414 gauss(0, time_gap * simulation.servers[i].curr_script->wander); 415 416 simulation.servers[i].server_time += time_gap * 417 (1 + simulation.servers[i].curr_script->freq_offset); 418 } 419 420 /* Perform the adjtime() function. If the adjustment completed 421 * in the previous interval, amortize the entire amount; if not, 422 * carry the leftover to the next interval. 423 */ 424 425 adj = time_gap * simclock.slew; 426 if (adj < fabs(simclock.adj)) { 427 if (simclock.adj < 0) { 428 simclock.adj += adj; 429 simclock.local_time -= adj; 430 } else { 431 simclock.adj -= adj; 432 simclock.local_time += adj; 433 } 434 } else { 435 simclock.local_time += simclock.adj; 436 simclock.adj = 0; 437 } 438 } 439 440 441 /* Define a function that processes a receive packet event. 442 * This function simply inserts the packet received onto the receive queue 443 */ 444 445 void sim_event_recv_packet(Event *e) 446 { 447 struct recvbuf *rbuf; 448 449 /* Allocate a receive buffer and copy the packet to it */ 450 if ((rbuf = get_node(sizeof(*rbuf))) == NULL) 451 abortsim("get_node failed in sim_event_recv_packet"); 452 memcpy(rbuf, &e->rcv_buf, sizeof(*rbuf)); 453 454 /* Store the local time in the received packet */ 455 DTOLFP(simclock.local_time, &rbuf->recv_time); 456 457 /* Insert the packet received onto the receive queue */ 458 enqueue(recv_queue, rbuf); 459 } 460 461 462 463 /* Define a function to output simulation statistics on a beep event 464 */ 465 466 /*** TODO: Need to decide on how to output for multiple servers ***/ 467 void sim_event_beep(Event *e) 468 { 469 #if 0 470 static int first_time = 1; 471 char *dash = "-----------------"; 472 #endif 473 474 fprintf(stderr, "BEEP!!!\n"); 475 enqueue(event_queue, event(e->time + simulation.beep_delay, BEEP)); 476 #if 0 477 if(simulation.beep_delay > 0) { 478 if (first_time) { 479 printf("\t%4c T %4c\t%4c T+ERR %3c\t%5cT+ERR+NTP\n", 480 ' ', ' ', ' ', ' ',' '); 481 printf("\t%s\t%s\t%s\n", dash, dash, dash); 482 first_time = 0; 483 484 printf("\t%16.6f\t%16.6f\t%16.6f\n", 485 n->time, n->clk_time, n->ntp_time); 486 return; 487 } 488 printf("\t%16.6f\t%16.6f\t%16.6f\n", 489 simclock.local_time, 490 n->time, n->clk_time, n->ntp_time); 491 #endif 492 493 } 494 495 496 /* Define a function to abort the simulation on an error and spit out an 497 * error message 498 */ 499 500 void abortsim(char *errmsg) 501 { 502 perror(errmsg); 503 exit(1); 504 } 505 506 507 508 /* CODE ORIGINALLY IN libntp/systime.c 509 * ----------------------------------- 510 * This code was a part of the original NTP simulator and originally 511 * had its home in the libntp/systime.c file. 512 * 513 * It has been shamelessly moved to here and has been modified for the 514 * purposes of the current simulator. 515 */ 516 517 518 /* 519 * get_systime - return the system time in NTP timestamp format 520 */ 521 void 522 get_systime( 523 l_fp *now /* current system time in l_fp */ ) 524 { 525 /* 526 * To fool the code that determines the local clock precision, 527 * we advance the clock a minimum of 200 nanoseconds on every 528 * clock read. This is appropriate for a typical modern machine 529 * with nanosecond clocks. Note we make no attempt here to 530 * simulate reading error, since the error is so small. This may 531 * change when the need comes to implement picosecond clocks. 532 */ 533 if (simclock.local_time == simclock.last_read_time) 534 simclock.local_time += 200e-9; 535 536 simclock.last_read_time = simclock.local_time; 537 DTOLFP(simclock.local_time, now); 538 /* OLD Code 539 if (ntp_node.ntp_time == ntp_node.last_time) 540 ntp_node.ntp_time += 200e-9; 541 ntp_node.last_time = ntp_node.ntp_time; 542 DTOLFP(ntp_node.ntp_time, now); 543 */ 544 } 545 546 547 /* 548 * adj_systime - advance or retard the system clock exactly like the 549 * real thng. 550 */ 551 int /* always succeeds */ 552 adj_systime( 553 double now /* time adjustment (s) */ 554 ) 555 { 556 struct timeval adjtv; /* new adjustment */ 557 double dtemp; 558 long ticks; 559 int isneg = 0; 560 561 /* 562 * Most Unix adjtime() implementations adjust the system clock 563 * in microsecond quanta, but some adjust in 10-ms quanta. We 564 * carefully round the adjustment to the nearest quantum, then 565 * adjust in quanta and keep the residue for later. 566 */ 567 dtemp = now + sys_residual; 568 if (dtemp < 0) { 569 isneg = 1; 570 dtemp = -dtemp; 571 } 572 adjtv.tv_sec = (long)dtemp; 573 dtemp -= adjtv.tv_sec; 574 ticks = (long)(dtemp / sys_tick + .5); 575 adjtv.tv_usec = (long)(ticks * sys_tick * 1e6); 576 dtemp -= adjtv.tv_usec / 1e6; 577 sys_residual = dtemp; 578 579 /* 580 * Convert to signed seconds and microseconds for the Unix 581 * adjtime() system call. Note we purposely lose the adjtime() 582 * leftover. 583 */ 584 if (isneg) { 585 adjtv.tv_sec = -adjtv.tv_sec; 586 adjtv.tv_usec = -adjtv.tv_usec; 587 sys_residual = -sys_residual; 588 } 589 simclock.adj = now; 590 /* ntp_node.adj = now; */ 591 return (1); 592 } 593 594 595 /* 596 * step_systime - step the system clock. We are religious here. 597 */ 598 int /* always succeeds */ 599 step_systime( 600 double now /* step adjustment (s) */ 601 ) 602 { 603 #ifdef DEBUG 604 if (debug) 605 printf("step_systime: time %.6f adj %.6f\n", 606 simclock.local_time, now); 607 #endif 608 simclock.local_time += now; 609 return (1); 610 } 611 612 /* 613 * gauss() - returns samples from a gaussion distribution 614 */ 615 double /* Gaussian sample */ 616 gauss( 617 double m, /* sample mean */ 618 double s /* sample standard deviation (sigma) */ 619 ) 620 { 621 double q1, q2; 622 623 /* 624 * Roll a sample from a Gaussian distribution with mean m and 625 * standard deviation s. For m = 0, s = 1, mean(y) = 0, 626 * std(y) = 1. 627 */ 628 if (s == 0) 629 return (m); 630 while ((q1 = drand48()) == 0) 631 /* empty statement */; 632 q2 = drand48(); 633 return (m + s * sqrt(-2. * log(q1)) * cos(2. * PI * q2)); 634 } 635 636 637 /* 638 * poisson() - returns samples from a network delay distribution 639 */ 640 double /* delay sample (s) */ 641 poisson( 642 double m, /* fixed propagation delay (s) */ 643 double s /* exponential parameter (mu) */ 644 ) 645 { 646 double q1; 647 648 /* 649 * Roll a sample from a composite distribution with propagation 650 * delay m and exponential distribution time with parameter s. 651 * For m = 0, s = 1, mean(y) = std(y) = 1. 652 */ 653 if (s == 0) 654 return (m); 655 while ((q1 = drand48()) == 0) 656 /* empty statement */; 657 return (m - s * log(q1 * s)); 658 } 659 660 #endif 661