1290001Sglebius/* ntpdsim.c 2290001Sglebius * 3290001Sglebius * The source code for the ntp discrete event simulator. 4290001Sglebius * 5290001Sglebius * Written By: Sachin Kamboj 6290001Sglebius * University of Delaware 7290001Sglebius * Newark, DE 19711 8290001Sglebius * Copyright (c) 2006 9290001Sglebius * (Some code shamelessly based on the original NTP discrete event simulator) 10132451Sroberto */ 11290001Sglebius 12290001Sglebius#include <config.h> 13290001Sglebius#ifdef SIM 14132451Sroberto#include "ntpd.h" 15290001Sglebius#include "ntp_config.h" 16132451Sroberto 17290001Sglebius/* forward prototypes */ 18290001Sglebiusint determine_event_ordering(const Event *e1, const Event *e2); 19290001Sglebiusint determine_recv_buf_ordering(const struct recvbuf *b1, 20290001Sglebius const struct recvbuf *b2); 21290001Sglebiusvoid create_server_associations(void); 22290001Sglebiusvoid init_sim_io(void); 23132451Sroberto 24290001Sglebius/* Global Variable Definitions */ 25290001Sglebiussim_info simulation; /* Simulation Control Variables */ 26290001Sglebiuslocal_clock_info simclock; /* Local Clock Variables */ 27290001Sglebiusqueue *event_queue; /* Event Queue */ 28290001Sglebiusqueue *recv_queue; /* Receive Queue */ 29290001Sglebiusstatic double sys_residual = 0; /* adjustment residue (s) */ 30290001Sglebius 31290001Sglebiusvoid (*event_ptr[]) (Event *) = { 32290001Sglebius sim_event_beep, sim_update_clocks, sim_event_timer, sim_event_recv_packet 33290001Sglebius}; /* Function pointer to the events */ 34290001Sglebius 35290001Sglebius 36132451Sroberto/* 37290001Sglebius * Define a function to compare two events to determine which one occurs 38290001Sglebius * first. 39132451Sroberto */ 40290001Sglebiusint 41290001Sglebiusdetermine_event_ordering( 42290001Sglebius const Event *e1, 43290001Sglebius const Event *e2 44290001Sglebius ) 45290001Sglebius{ 46290001Sglebius return (e1->time - e2->time); 47290001Sglebius} 48132451Sroberto 49132451Sroberto 50132451Sroberto/* 51290001Sglebius * Define a function to compare two received packets to determine which 52290001Sglebius * one is received first. 53132451Sroberto */ 54132451Srobertoint 55290001Sglebiusdetermine_recv_buf_ordering( 56290001Sglebius const struct recvbuf *b1, 57290001Sglebius const struct recvbuf *b2 58132451Sroberto ) 59132451Sroberto{ 60290001Sglebius double recv_time1; 61290001Sglebius double recv_time2; 62132451Sroberto 63290001Sglebius /* Simply convert the time received to double and subtract */ 64290001Sglebius LFPTOD(&b1->recv_time, recv_time1); 65290001Sglebius LFPTOD(&b2->recv_time, recv_time2); 66132451Sroberto 67290001Sglebius return (int)(recv_time1 - recv_time2); 68290001Sglebius} 69132451Sroberto 70132451Sroberto 71290001Sglebius/* Define a function to create the server associations */ 72290001Sglebiusvoid create_server_associations(void) 73290001Sglebius{ 74290001Sglebius int i; 75182007Sroberto 76290001Sglebius for (i = 0; i < simulation.num_of_servers; ++i) { 77290001Sglebius printf("%s\n", stoa(simulation.servers[i].addr)); 78290001Sglebius if (peer_config(simulation.servers[i].addr, 79290001Sglebius NULL, 80290001Sglebius loopback_interface, 81290001Sglebius MODE_CLIENT, 82290001Sglebius NTP_VERSION, 83290001Sglebius NTP_MINDPOLL, 84290001Sglebius NTP_MAXDPOLL, 85290001Sglebius 0, /* peerflags */ 86290001Sglebius 0, /* ttl */ 87290001Sglebius 0, /* peerkey */ 88290001Sglebius NULL /* group ident */) == 0) { 89290001Sglebius fprintf(stderr, 90290001Sglebius "ERROR!! Could not create association for: %s\n", 91290001Sglebius stoa(simulation.servers[i].addr)); 92290001Sglebius } 93182007Sroberto } 94290001Sglebius} 95182007Sroberto 96290001Sglebius 97290001Sglebius/* Main Simulator Code */ 98290001Sglebius 99290001Sglebiusint 100290001Sglebiusntpsim( 101290001Sglebius int argc, 102290001Sglebius char * argv[] 103290001Sglebius ) 104290001Sglebius{ 105290001Sglebius Event * curr_event; 106290001Sglebius struct timeval seed; 107290001Sglebius 108290001Sglebius /* Initialize the local Clock */ 109290001Sglebius simclock.local_time = 0; 110290001Sglebius simclock.adj = 0; 111290001Sglebius simclock.slew = 500e-6; 112290001Sglebius 113290001Sglebius /* Initialize the simulation */ 114290001Sglebius simulation.num_of_servers = 0; 115290001Sglebius simulation.beep_delay = BEEP_DLY; 116290001Sglebius simulation.sim_time = 0; 117290001Sglebius simulation.end_time = SIM_TIME; 118290001Sglebius 119290001Sglebius /* Initialize ntp modules */ 120290001Sglebius initializing = TRUE; 121290001Sglebius msyslog_term = TRUE; 122290001Sglebius init_sim_io(); 123290001Sglebius init_auth(); 124290001Sglebius init_util(); 125290001Sglebius init_restrict(); 126290001Sglebius init_mon(); 127290001Sglebius init_timer(); 128290001Sglebius init_lib(); 129290001Sglebius init_request(); 130290001Sglebius init_control(); 131290001Sglebius init_peer(); 132290001Sglebius init_proto(); 133290001Sglebius init_loopfilter(); 134290001Sglebius mon_start(MON_OFF); 135290001Sglebius 136290001Sglebius /* Call getconfig to parse the configuration file */ 137132451Sroberto getconfig(argc, argv); 138290001Sglebius loop_config(LOOP_DRIFTINIT, 0); 139290001Sglebius initializing = FALSE; 140182007Sroberto 141132451Sroberto /* 142132451Sroberto * Watch out here, we want the real time, not the silly stuff. 143132451Sroberto */ 144132451Sroberto gettimeofday(&seed, NULL); 145182007Sroberto ntp_srandom(seed.tv_usec); 146132451Sroberto 147290001Sglebius /* Initialize the event queue */ 148290001Sglebius event_queue = create_priority_queue((q_order_func) 149290001Sglebius determine_event_ordering); 150132451Sroberto 151290001Sglebius /* Initialize the receive queue */ 152290001Sglebius recv_queue = create_priority_queue((q_order_func) 153290001Sglebius determine_recv_buf_ordering); 154290001Sglebius 155290001Sglebius /* Push a beep and a timer on the event queue */ 156290001Sglebius enqueue(event_queue, event(0, BEEP)); 157290001Sglebius enqueue(event_queue, event(simulation.sim_time + 1.0, TIMER)); 158290001Sglebius 159290001Sglebius /* 160132451Sroberto * Pop the queue until nothing is left or time is exceeded 161132451Sroberto */ 162290001Sglebius /* maxtime = simulation.sim_time + simulation.end_time;*/ 163290001Sglebius while (simulation.sim_time <= simulation.end_time && 164290001Sglebius (!empty(event_queue))) { 165290001Sglebius curr_event = dequeue(event_queue); 166290001Sglebius /* Update all the clocks to the time on the event */ 167290001Sglebius sim_update_clocks(curr_event); 168290001Sglebius 169290001Sglebius /* Execute the function associated with the event */ 170290001Sglebius (*event_ptr[curr_event->function])(curr_event); 171290001Sglebius free_node(curr_event); 172132451Sroberto } 173290001Sglebius printf("sys_received: %lu\n", sys_received); 174290001Sglebius printf("sys_badlength: %lu\n", sys_badlength); 175290001Sglebius printf("sys_declined: %lu\n", sys_declined); 176290001Sglebius printf("sys_restricted: %lu\n", sys_restricted); 177290001Sglebius printf("sys_newversion: %lu\n", sys_newversion); 178290001Sglebius printf("sys_oldversion: %lu\n", sys_oldversion); 179290001Sglebius printf("sys_limitrejected: %lu\n", sys_limitrejected); 180290001Sglebius printf("sys_badauth: %lu\n", sys_badauth); 181290001Sglebius 182132451Sroberto return (0); 183132451Sroberto} 184132451Sroberto 185132451Sroberto 186290001Sglebiusvoid 187290001Sglebiusinit_sim_io(void) 188132451Sroberto{ 189290001Sglebius loopback_interface = emalloc_zero(sizeof(*loopback_interface)); 190290001Sglebius ep_list = loopback_interface; 191290001Sglebius strlcpy(loopback_interface->name, "IPv4loop", 192290001Sglebius sizeof(loopback_interface->name)); 193290001Sglebius loopback_interface->flags = INT_UP | INT_LOOPBACK; 194290001Sglebius loopback_interface->fd = -1; 195290001Sglebius loopback_interface->bfd = -1; 196290001Sglebius loopback_interface->ifnum = 1; 197290001Sglebius loopback_interface->family = AF_INET; 198290001Sglebius AF(&loopback_interface->sin) = AF_INET; 199290001Sglebius SET_ADDR4(&loopback_interface->sin, LOOPBACKADR); 200290001Sglebius SET_PORT(&loopback_interface->sin, NTP_PORT); 201290001Sglebius AF(&loopback_interface->mask) = AF_INET; 202290001Sglebius SET_ADDR4(&loopback_interface->mask, LOOPNETMASK); 203290001Sglebius} 204132451Sroberto 205290001Sglebius 206290001Sglebius/* Define a function to create an return an Event */ 207290001Sglebius 208290001SglebiusEvent *event(double t, funcTkn f) 209290001Sglebius{ 210290001Sglebius Event *e; 211290001Sglebius 212290001Sglebius if ((e = get_node(sizeof(*e))) == NULL) 213290001Sglebius abortsim("get_node failed in event"); 214290001Sglebius e->time = t; 215290001Sglebius e->function = f; 216290001Sglebius return (e); 217132451Sroberto} 218132451Sroberto 219290001Sglebius/* NTP SIMULATION FUNCTIONS */ 220290001Sglebius 221290001Sglebius/* Define a function for processing a timer interrupt. 222290001Sglebius * On every timer interrupt, call the NTP timer to send packets and process 223290001Sglebius * the clock and then call the receive function to receive packets. 224132451Sroberto */ 225290001Sglebiusvoid sim_event_timer(Event *e) 226132451Sroberto{ 227290001Sglebius struct recvbuf *rbuf; 228132451Sroberto 229290001Sglebius /* Call the NTP timer. 230290001Sglebius * This will be responsible for actually "sending the packets." 231290001Sglebius * Since this is a simulation, the packets sent over the network 232290001Sglebius * will be processed by the simulate_server routine below. 233290001Sglebius */ 234290001Sglebius timer(); 235290001Sglebius 236290001Sglebius /* Process received buffers */ 237290001Sglebius while (!empty(recv_queue)) { 238290001Sglebius rbuf = (struct recvbuf *)dequeue(recv_queue); 239290001Sglebius (*rbuf->receiver)(rbuf); 240290001Sglebius free_node(rbuf); 241290001Sglebius } 242290001Sglebius 243290001Sglebius /* Arm the next timer interrupt. */ 244290001Sglebius enqueue(event_queue, 245290001Sglebius event(simulation.sim_time + (1 << EVENT_TIMEOUT), TIMER)); 246132451Sroberto} 247132451Sroberto 248132451Sroberto 249290001Sglebius 250290001Sglebius/* Define a function to simulate a server. 251290001Sglebius * This function processes the sent packet according to the server script, 252290001Sglebius * creates a reply packet and pushes the reply packet onto the event queue 253132451Sroberto */ 254290001Sglebiusint simulate_server( 255290001Sglebius sockaddr_u *serv_addr, /* Address of the server */ 256290001Sglebius endpt * inter, /* Interface on which the reply should 257290001Sglebius be inserted */ 258290001Sglebius struct pkt *rpkt /* Packet sent to the server that 259290001Sglebius needs to be processed. */ 260290001Sglebius ) 261132451Sroberto{ 262290001Sglebius struct pkt xpkt; /* Packet to be transmitted back 263290001Sglebius to the client */ 264290001Sglebius struct recvbuf rbuf; /* Buffer for the received packet */ 265290001Sglebius Event *e; /* Packet receive event */ 266290001Sglebius server_info *server; /* Pointer to the server being simulated */ 267290001Sglebius script_info *curr_script; /* Current script being processed */ 268290001Sglebius int i; 269290001Sglebius double d1, d2, d3; /* Delays while the packet is enroute */ 270290001Sglebius double t1, t2, t3, t4; /* The four timestamps in the packet */ 271290001Sglebius l_fp lfp_host; /* host-order l_fp */ 272132451Sroberto 273290001Sglebius ZERO(xpkt); 274290001Sglebius ZERO(rbuf); 275290001Sglebius 276290001Sglebius /* Search for the server with the desired address */ 277290001Sglebius server = NULL; 278290001Sglebius for (i = 0; i < simulation.num_of_servers; ++i) { 279290001Sglebius if (memcmp(simulation.servers[i].addr, serv_addr, 280290001Sglebius sizeof(*serv_addr)) == 0) { 281290001Sglebius server = &simulation.servers[i]; 282290001Sglebius break; 283290001Sglebius } 284290001Sglebius } 285290001Sglebius 286290001Sglebius fprintf(stderr, "Received packet from %s on %s\n", 287290001Sglebius stoa(serv_addr), latoa(inter)); 288290001Sglebius if (server == NULL) 289290001Sglebius abortsim("Server with specified address not found!!!"); 290290001Sglebius 291290001Sglebius /* Get the current script for the server */ 292290001Sglebius curr_script = server->curr_script; 293290001Sglebius 294290001Sglebius /* Create a server reply packet. 295290001Sglebius * Masquerade the reply as a stratum-1 server with a GPS clock 296290001Sglebius */ 297290001Sglebius xpkt.li_vn_mode = PKT_LI_VN_MODE(LEAP_NOWARNING, NTP_VERSION, 298290001Sglebius MODE_SERVER); 299290001Sglebius xpkt.stratum = STRATUM_TO_PKT(((u_char)1)); 300290001Sglebius memcpy(&xpkt.refid, "GPS", 4); 301290001Sglebius xpkt.ppoll = rpkt->ppoll; 302290001Sglebius xpkt.precision = rpkt->precision; 303290001Sglebius xpkt.rootdelay = 0; 304290001Sglebius xpkt.rootdisp = 0; 305290001Sglebius 306290001Sglebius /* TIMESTAMP CALCULATIONS 307290001Sglebius t1 t4 308290001Sglebius \ / 309290001Sglebius d1 \ / d3 310290001Sglebius \ / 311290001Sglebius t2 ----------------- t3 312290001Sglebius d2 313290001Sglebius */ 314290001Sglebius /* Compute the delays */ 315290001Sglebius d1 = poisson(curr_script->prop_delay, curr_script->jitter); 316290001Sglebius d2 = poisson(curr_script->proc_delay, 0); 317290001Sglebius d3 = poisson(curr_script->prop_delay, curr_script->jitter); 318290001Sglebius 319290001Sglebius /* Note: In the transmitted packet: 320290001Sglebius * 1. t1 and t4 are times in the client according to the local clock. 321290001Sglebius * 2. t2 and t3 are server times according to the simulated server. 322290001Sglebius * Compute t1, t2, t3 and t4 323290001Sglebius * Note: This function is called at time t1. 324290001Sglebius */ 325290001Sglebius 326290001Sglebius NTOHL_FP(&rpkt->xmt, &lfp_host); 327290001Sglebius LFPTOD(&lfp_host, t1); 328290001Sglebius t2 = server->server_time + d1; 329290001Sglebius t3 = server->server_time + d1 + d2; 330290001Sglebius t4 = t1 + d1 + d2 + d3; 331290001Sglebius 332290001Sglebius /* Save the timestamps */ 333290001Sglebius xpkt.org = rpkt->xmt; 334290001Sglebius DTOLFP(t2, &lfp_host); 335290001Sglebius HTONL_FP(&lfp_host, &xpkt.rec); 336290001Sglebius DTOLFP(t3, &lfp_host); 337290001Sglebius HTONL_FP(&lfp_host, &xpkt.xmt); 338290001Sglebius xpkt.reftime = xpkt.xmt; 339290001Sglebius 340290001Sglebius /* 341290001Sglebius * Ok, we are done with the packet. Now initialize the receive 342290001Sglebius * buffer for the packet. 343290001Sglebius */ 344290001Sglebius rbuf.used = 1; 345290001Sglebius rbuf.receiver = &receive; /* callback to process the packet */ 346290001Sglebius rbuf.recv_length = LEN_PKT_NOMAC; 347290001Sglebius rbuf.recv_pkt = xpkt; 348290001Sglebius rbuf.dstadr = inter; 349290001Sglebius rbuf.fd = inter->fd; 350290001Sglebius memcpy(&rbuf.srcadr, serv_addr, sizeof(rbuf.srcadr)); 351290001Sglebius memcpy(&rbuf.recv_srcadr, serv_addr, sizeof(rbuf.recv_srcadr)); 352290001Sglebius 353290001Sglebius /* 354290001Sglebius * Create a packet event and insert it onto the event_queue at the 355290001Sglebius * arrival time (t4) of the packet at the client 356290001Sglebius */ 357290001Sglebius e = event(t4, PACKET); 358290001Sglebius e->rcv_buf = rbuf; 359290001Sglebius enqueue(event_queue, e); 360290001Sglebius 361290001Sglebius /* 362290001Sglebius * Check if the time of the script has expired. If yes, delete it. 363290001Sglebius */ 364290001Sglebius if (curr_script->duration > simulation.sim_time && 365290001Sglebius NULL == HEAD_PFIFO(server->script)) { 366290001Sglebius printf("Hello\n"); 367290001Sglebius /* 368290001Sglebius * For some reason freeing up the curr_script memory kills the 369290001Sglebius * simulation. Further debugging is needed to determine why. 370290001Sglebius * free(curr_script); 371290001Sglebius */ 372290001Sglebius UNLINK_FIFO(curr_script, *server->script, link); 373290001Sglebius } 374290001Sglebius 375290001Sglebius return (0); 376132451Sroberto} 377132451Sroberto 378132451Sroberto 379290001Sglebius/* Define a function to update all the clocks 380290001Sglebius * Most of the code is modified from the systime.c file by Prof. Mills 381132451Sroberto */ 382290001Sglebius 383290001Sglebiusvoid sim_update_clocks(Event *e) 384132451Sroberto{ 385290001Sglebius double time_gap; 386290001Sglebius double adj; 387290001Sglebius int i; 388132451Sroberto 389290001Sglebius /* Compute the time between the last update event and this update */ 390290001Sglebius time_gap = e->time - simulation.sim_time; 391290001Sglebius 392290001Sglebius if (time_gap < 0) 393290001Sglebius printf("WARNING: e->time %.6g comes before sim_time %.6g (gap %+.6g)\n", 394290001Sglebius e->time, simulation.sim_time, time_gap); 395290001Sglebius 396290001Sglebius /* Advance the client clock */ 397290001Sglebius if (e->time + time_gap < simclock.local_time) 398290001Sglebius printf("WARNING: e->time + gap %.6g comes before local_time %.6g\n", 399290001Sglebius e->time + time_gap, simclock.local_time); 400290001Sglebius simclock.local_time = e->time + time_gap; 401290001Sglebius 402290001Sglebius /* Advance the simulation time */ 403290001Sglebius simulation.sim_time = e->time; 404290001Sglebius 405290001Sglebius /* Advance the server clocks adjusted for systematic and random frequency 406290001Sglebius * errors. The random error is a random walk computed as the 407290001Sglebius * integral of samples from a Gaussian distribution. 408290001Sglebius */ 409290001Sglebius for (i = 0; i < simulation.num_of_servers; ++i) { 410290001Sglebius simulation.servers[i].curr_script->freq_offset += 411290001Sglebius gauss(0, time_gap * simulation.servers[i].curr_script->wander); 412290001Sglebius 413290001Sglebius simulation.servers[i].server_time += time_gap * 414290001Sglebius (1 + simulation.servers[i].curr_script->freq_offset); 415290001Sglebius } 416290001Sglebius 417290001Sglebius /* Perform the adjtime() function. If the adjustment completed 418290001Sglebius * in the previous interval, amortize the entire amount; if not, 419290001Sglebius * carry the leftover to the next interval. 420290001Sglebius */ 421290001Sglebius 422290001Sglebius adj = time_gap * simclock.slew; 423290001Sglebius if (adj < fabs(simclock.adj)) { 424290001Sglebius if (simclock.adj < 0) { 425290001Sglebius simclock.adj += adj; 426290001Sglebius simclock.local_time -= adj; 427290001Sglebius } else { 428290001Sglebius simclock.adj -= adj; 429290001Sglebius simclock.local_time += adj; 430290001Sglebius } 431290001Sglebius } else { 432290001Sglebius simclock.local_time += simclock.adj; 433290001Sglebius simclock.adj = 0; 434290001Sglebius } 435132451Sroberto} 436132451Sroberto 437132451Sroberto 438290001Sglebius/* Define a function that processes a receive packet event. 439290001Sglebius * This function simply inserts the packet received onto the receive queue 440290001Sglebius */ 441290001Sglebius 442290001Sglebiusvoid sim_event_recv_packet(Event *e) 443132451Sroberto{ 444290001Sglebius struct recvbuf *rbuf; 445290001Sglebius 446290001Sglebius /* Allocate a receive buffer and copy the packet to it */ 447290001Sglebius if ((rbuf = get_node(sizeof(*rbuf))) == NULL) 448290001Sglebius abortsim("get_node failed in sim_event_recv_packet"); 449290001Sglebius memcpy(rbuf, &e->rcv_buf, sizeof(*rbuf)); 450290001Sglebius 451290001Sglebius /* Store the local time in the received packet */ 452290001Sglebius DTOLFP(simclock.local_time, &rbuf->recv_time); 453290001Sglebius 454290001Sglebius /* Insert the packet received onto the receive queue */ 455290001Sglebius enqueue(recv_queue, rbuf); 456132451Sroberto} 457132451Sroberto 458132451Sroberto 459290001Sglebius 460290001Sglebius/* Define a function to output simulation statistics on a beep event 461132451Sroberto */ 462290001Sglebius 463290001Sglebius/*** TODO: Need to decide on how to output for multiple servers ***/ 464290001Sglebiusvoid sim_event_beep(Event *e) 465132451Sroberto{ 466290001Sglebius#if 0 467290001Sglebius static int first_time = 1; 468290001Sglebius char *dash = "-----------------"; 469290001Sglebius#endif 470132451Sroberto 471290001Sglebius fprintf(stderr, "BEEP!!!\n"); 472290001Sglebius enqueue(event_queue, event(e->time + simulation.beep_delay, BEEP)); 473290001Sglebius#if 0 474290001Sglebius if(simulation.beep_delay > 0) { 475290001Sglebius if (first_time) { 476290001Sglebius printf("\t%4c T %4c\t%4c T+ERR %3c\t%5cT+ERR+NTP\n", 477290001Sglebius ' ', ' ', ' ', ' ',' '); 478290001Sglebius printf("\t%s\t%s\t%s\n", dash, dash, dash); 479290001Sglebius first_time = 0; 480132451Sroberto 481290001Sglebius printf("\t%16.6f\t%16.6f\t%16.6f\n", 482290001Sglebius n->time, n->clk_time, n->ntp_time); 483290001Sglebius return; 484132451Sroberto } 485290001Sglebius printf("\t%16.6f\t%16.6f\t%16.6f\n", 486290001Sglebius simclock.local_time, 487290001Sglebius n->time, n->clk_time, n->ntp_time); 488290001Sglebius#endif 489132451Sroberto 490132451Sroberto} 491132451Sroberto 492132451Sroberto 493290001Sglebius/* Define a function to abort the simulation on an error and spit out an 494290001Sglebius * error message 495132451Sroberto */ 496290001Sglebius 497290001Sglebiusvoid abortsim(char *errmsg) 498132451Sroberto{ 499290001Sglebius perror(errmsg); 500290001Sglebius exit(1); 501290001Sglebius} 502132451Sroberto 503132451Sroberto 504132451Sroberto 505290001Sglebius/* CODE ORIGINALLY IN libntp/systime.c 506290001Sglebius * ----------------------------------- 507290001Sglebius * This code was a part of the original NTP simulator and originally 508290001Sglebius * had its home in the libntp/systime.c file. 509290001Sglebius * 510290001Sglebius * It has been shamelessly moved to here and has been modified for the 511290001Sglebius * purposes of the current simulator. 512290001Sglebius */ 513132451Sroberto 514132451Sroberto 515132451Sroberto/* 516290001Sglebius * get_systime - return the system time in NTP timestamp format 517132451Sroberto */ 518132451Srobertovoid 519290001Sglebiusget_systime( 520290001Sglebius l_fp *now /* current system time in l_fp */ ) 521132451Sroberto{ 522290001Sglebius /* 523290001Sglebius * To fool the code that determines the local clock precision, 524290001Sglebius * we advance the clock a minimum of 200 nanoseconds on every 525290001Sglebius * clock read. This is appropriate for a typical modern machine 526290001Sglebius * with nanosecond clocks. Note we make no attempt here to 527290001Sglebius * simulate reading error, since the error is so small. This may 528290001Sglebius * change when the need comes to implement picosecond clocks. 529290001Sglebius */ 530290001Sglebius if (simclock.local_time == simclock.last_read_time) 531290001Sglebius simclock.local_time += 200e-9; 532132451Sroberto 533290001Sglebius simclock.last_read_time = simclock.local_time; 534290001Sglebius DTOLFP(simclock.local_time, now); 535290001Sglebius/* OLD Code 536290001Sglebius if (ntp_node.ntp_time == ntp_node.last_time) 537290001Sglebius ntp_node.ntp_time += 200e-9; 538290001Sglebius ntp_node.last_time = ntp_node.ntp_time; 539290001Sglebius DTOLFP(ntp_node.ntp_time, now); 540290001Sglebius*/ 541132451Sroberto} 542290001Sglebius 543290001Sglebius 544290001Sglebius/* 545290001Sglebius * adj_systime - advance or retard the system clock exactly like the 546290001Sglebius * real thng. 547290001Sglebius */ 548290001Sglebiusint /* always succeeds */ 549290001Sglebiusadj_systime( 550290001Sglebius double now /* time adjustment (s) */ 551290001Sglebius ) 552290001Sglebius{ 553290001Sglebius struct timeval adjtv; /* new adjustment */ 554290001Sglebius double dtemp; 555290001Sglebius long ticks; 556290001Sglebius int isneg = 0; 557132451Sroberto 558290001Sglebius /* 559290001Sglebius * Most Unix adjtime() implementations adjust the system clock 560290001Sglebius * in microsecond quanta, but some adjust in 10-ms quanta. We 561290001Sglebius * carefully round the adjustment to the nearest quantum, then 562290001Sglebius * adjust in quanta and keep the residue for later. 563290001Sglebius */ 564290001Sglebius dtemp = now + sys_residual; 565290001Sglebius if (dtemp < 0) { 566290001Sglebius isneg = 1; 567290001Sglebius dtemp = -dtemp; 568290001Sglebius } 569290001Sglebius adjtv.tv_sec = (long)dtemp; 570290001Sglebius dtemp -= adjtv.tv_sec; 571290001Sglebius ticks = (long)(dtemp / sys_tick + .5); 572290001Sglebius adjtv.tv_usec = (long)(ticks * sys_tick * 1e6); 573290001Sglebius dtemp -= adjtv.tv_usec / 1e6; 574290001Sglebius sys_residual = dtemp; 575132451Sroberto 576290001Sglebius /* 577290001Sglebius * Convert to signed seconds and microseconds for the Unix 578290001Sglebius * adjtime() system call. Note we purposely lose the adjtime() 579290001Sglebius * leftover. 580290001Sglebius */ 581290001Sglebius if (isneg) { 582290001Sglebius adjtv.tv_sec = -adjtv.tv_sec; 583290001Sglebius adjtv.tv_usec = -adjtv.tv_usec; 584290001Sglebius sys_residual = -sys_residual; 585290001Sglebius } 586290001Sglebius simclock.adj = now; 587290001Sglebius/* ntp_node.adj = now; */ 588290001Sglebius return (1); 589290001Sglebius} 590290001Sglebius 591290001Sglebius 592132451Sroberto/* 593290001Sglebius * step_systime - step the system clock. We are religious here. 594132451Sroberto */ 595290001Sglebiusint /* always succeeds */ 596290001Sglebiusstep_systime( 597290001Sglebius double now /* step adjustment (s) */ 598290001Sglebius ) 599132451Sroberto{ 600290001Sglebius#ifdef DEBUG 601290001Sglebius if (debug) 602290001Sglebius printf("step_systime: time %.6f adj %.6f\n", 603290001Sglebius simclock.local_time, now); 604290001Sglebius#endif 605290001Sglebius simclock.local_time += now; 606290001Sglebius return (1); 607290001Sglebius} 608290001Sglebius 609290001Sglebius/* 610290001Sglebius * gauss() - returns samples from a gaussion distribution 611290001Sglebius */ 612290001Sglebiusdouble /* Gaussian sample */ 613290001Sglebiusgauss( 614290001Sglebius double m, /* sample mean */ 615290001Sglebius double s /* sample standard deviation (sigma) */ 616290001Sglebius ) 617290001Sglebius{ 618290001Sglebius double q1, q2; 619132451Sroberto 620290001Sglebius /* 621290001Sglebius * Roll a sample from a Gaussian distribution with mean m and 622290001Sglebius * standard deviation s. For m = 0, s = 1, mean(y) = 0, 623290001Sglebius * std(y) = 1. 624290001Sglebius */ 625290001Sglebius if (s == 0) 626290001Sglebius return (m); 627290001Sglebius while ((q1 = drand48()) == 0) 628290001Sglebius /* empty statement */; 629290001Sglebius q2 = drand48(); 630290001Sglebius return (m + s * sqrt(-2. * log(q1)) * cos(2. * PI * q2)); 631132451Sroberto} 632132451Sroberto 633290001Sglebius 634132451Sroberto/* 635290001Sglebius * poisson() - returns samples from a network delay distribution 636132451Sroberto */ 637290001Sglebiusdouble /* delay sample (s) */ 638290001Sglebiuspoisson( 639290001Sglebius double m, /* fixed propagation delay (s) */ 640290001Sglebius double s /* exponential parameter (mu) */ 641290001Sglebius ) 642132451Sroberto{ 643290001Sglebius double q1; 644290001Sglebius 645290001Sglebius /* 646290001Sglebius * Roll a sample from a composite distribution with propagation 647290001Sglebius * delay m and exponential distribution time with parameter s. 648290001Sglebius * For m = 0, s = 1, mean(y) = std(y) = 1. 649290001Sglebius */ 650290001Sglebius if (s == 0) 651290001Sglebius return (m); 652290001Sglebius while ((q1 = drand48()) == 0) 653290001Sglebius /* empty statement */; 654290001Sglebius return (m - s * log(q1 * s)); 655132451Sroberto} 656290001Sglebius 657290001Sglebius#endif 658