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