12b15cb3dSCy Schubert /* ntpdsim.c
22b15cb3dSCy Schubert *
32b15cb3dSCy Schubert * The source code for the ntp discrete event simulator.
42b15cb3dSCy Schubert *
52b15cb3dSCy Schubert * Written By: Sachin Kamboj
62b15cb3dSCy Schubert * University of Delaware
72b15cb3dSCy Schubert * Newark, DE 19711
82b15cb3dSCy Schubert * Copyright (c) 2006
92b15cb3dSCy Schubert * (Some code shamelessly based on the original NTP discrete event simulator)
109c2daa00SOllivier Robert */
112b15cb3dSCy Schubert
122b15cb3dSCy Schubert #include <config.h>
132b15cb3dSCy Schubert #ifdef SIM
149c2daa00SOllivier Robert #include "ntpd.h"
152b15cb3dSCy Schubert #include "ntp_config.h"
169c2daa00SOllivier Robert
172b15cb3dSCy Schubert /* forward prototypes */
182b15cb3dSCy Schubert int determine_event_ordering(const Event *e1, const Event *e2);
192b15cb3dSCy Schubert int determine_recv_buf_ordering(const struct recvbuf *b1,
202b15cb3dSCy Schubert const struct recvbuf *b2);
212b15cb3dSCy Schubert void create_server_associations(void);
222b15cb3dSCy Schubert void init_sim_io(void);
239c2daa00SOllivier Robert
242b15cb3dSCy Schubert /* Global Variable Definitions */
252b15cb3dSCy Schubert sim_info simulation; /* Simulation Control Variables */
262b15cb3dSCy Schubert local_clock_info simclock; /* Local Clock Variables */
272b15cb3dSCy Schubert queue *event_queue; /* Event Queue */
282b15cb3dSCy Schubert queue *recv_queue; /* Receive Queue */
292b15cb3dSCy Schubert static double sys_residual = 0; /* adjustment residue (s) */
302b15cb3dSCy Schubert
312b15cb3dSCy Schubert void (*event_ptr[]) (Event *) = {
322b15cb3dSCy Schubert sim_event_beep, sim_update_clocks, sim_event_timer, sim_event_recv_packet
332b15cb3dSCy Schubert }; /* Function pointer to the events */
349c2daa00SOllivier Robert
359c2daa00SOllivier Robert
369c2daa00SOllivier Robert /*
372b15cb3dSCy Schubert * Define a function to compare two events to determine which one occurs
382b15cb3dSCy Schubert * first.
399c2daa00SOllivier Robert */
409c2daa00SOllivier Robert int
determine_event_ordering(const Event * e1,const Event * e2)412b15cb3dSCy Schubert determine_event_ordering(
422b15cb3dSCy Schubert const Event *e1,
432b15cb3dSCy Schubert const Event *e2
442b15cb3dSCy Schubert )
452b15cb3dSCy Schubert {
462b15cb3dSCy Schubert return (e1->time - e2->time);
472b15cb3dSCy Schubert }
482b15cb3dSCy Schubert
492b15cb3dSCy Schubert
502b15cb3dSCy Schubert /*
512b15cb3dSCy Schubert * Define a function to compare two received packets to determine which
522b15cb3dSCy Schubert * one is received first.
532b15cb3dSCy Schubert */
542b15cb3dSCy Schubert int
determine_recv_buf_ordering(const struct recvbuf * b1,const struct recvbuf * b2)552b15cb3dSCy Schubert determine_recv_buf_ordering(
562b15cb3dSCy Schubert const struct recvbuf *b1,
572b15cb3dSCy Schubert const struct recvbuf *b2
582b15cb3dSCy Schubert )
592b15cb3dSCy Schubert {
602b15cb3dSCy Schubert double recv_time1;
612b15cb3dSCy Schubert double recv_time2;
622b15cb3dSCy Schubert
632b15cb3dSCy Schubert /* Simply convert the time received to double and subtract */
642b15cb3dSCy Schubert LFPTOD(&b1->recv_time, recv_time1);
652b15cb3dSCy Schubert LFPTOD(&b2->recv_time, recv_time2);
662b15cb3dSCy Schubert
672b15cb3dSCy Schubert return (int)(recv_time1 - recv_time2);
682b15cb3dSCy Schubert }
692b15cb3dSCy Schubert
702b15cb3dSCy Schubert
712b15cb3dSCy Schubert /* Define a function to create the server associations */
create_server_associations(void)722b15cb3dSCy Schubert void create_server_associations(void)
732b15cb3dSCy Schubert {
742b15cb3dSCy Schubert int i;
752b15cb3dSCy Schubert
762b15cb3dSCy Schubert for (i = 0; i < simulation.num_of_servers; ++i) {
772b15cb3dSCy Schubert printf("%s\n", stoa(simulation.servers[i].addr));
782b15cb3dSCy Schubert if (peer_config(simulation.servers[i].addr,
792b15cb3dSCy Schubert NULL,
802b15cb3dSCy Schubert loopback_interface,
812b15cb3dSCy Schubert MODE_CLIENT,
82*09100258SXin LI -1,
832b15cb3dSCy Schubert NTP_VERSION,
842b15cb3dSCy Schubert NTP_MINDPOLL,
852b15cb3dSCy Schubert NTP_MAXDPOLL,
862b15cb3dSCy Schubert 0, /* peerflags */
872b15cb3dSCy Schubert 0, /* ttl */
882b15cb3dSCy Schubert 0, /* peerkey */
892b15cb3dSCy Schubert NULL /* group ident */) == 0) {
902b15cb3dSCy Schubert fprintf(stderr,
912b15cb3dSCy Schubert "ERROR!! Could not create association for: %s\n",
922b15cb3dSCy Schubert stoa(simulation.servers[i].addr));
932b15cb3dSCy Schubert }
942b15cb3dSCy Schubert }
952b15cb3dSCy Schubert }
962b15cb3dSCy Schubert
972b15cb3dSCy Schubert
982b15cb3dSCy Schubert /* Main Simulator Code */
992b15cb3dSCy Schubert
1002b15cb3dSCy Schubert int
ntpsim(int argc,char * argv[])1019c2daa00SOllivier Robert ntpsim(
1029c2daa00SOllivier Robert int argc,
1039c2daa00SOllivier Robert char * argv[]
1049c2daa00SOllivier Robert )
1059c2daa00SOllivier Robert {
1062b15cb3dSCy Schubert Event * curr_event;
1079c2daa00SOllivier Robert struct timeval seed;
1089c2daa00SOllivier Robert
1092b15cb3dSCy Schubert /* Initialize the local Clock */
1102b15cb3dSCy Schubert simclock.local_time = 0;
1112b15cb3dSCy Schubert simclock.adj = 0;
1122b15cb3dSCy Schubert simclock.slew = 500e-6;
1139c2daa00SOllivier Robert
1142b15cb3dSCy Schubert /* Initialize the simulation */
1152b15cb3dSCy Schubert simulation.num_of_servers = 0;
1162b15cb3dSCy Schubert simulation.beep_delay = BEEP_DLY;
1172b15cb3dSCy Schubert simulation.sim_time = 0;
1182b15cb3dSCy Schubert simulation.end_time = SIM_TIME;
1199c2daa00SOllivier Robert
1202b15cb3dSCy Schubert /* Initialize ntp modules */
1212b15cb3dSCy Schubert initializing = TRUE;
1222b15cb3dSCy Schubert msyslog_term = TRUE;
1232b15cb3dSCy Schubert init_sim_io();
1249c2daa00SOllivier Robert init_auth();
1259c2daa00SOllivier Robert init_util();
1269c2daa00SOllivier Robert init_restrict();
1279c2daa00SOllivier Robert init_mon();
1289c2daa00SOllivier Robert init_timer();
1299c2daa00SOllivier Robert init_lib();
1309c2daa00SOllivier Robert init_request();
1319c2daa00SOllivier Robert init_control();
1329c2daa00SOllivier Robert init_peer();
1339c2daa00SOllivier Robert init_proto();
1349c2daa00SOllivier Robert init_loopfilter();
1359c2daa00SOllivier Robert mon_start(MON_OFF);
136ea906c41SOllivier Robert
1372b15cb3dSCy Schubert /* Call getconfig to parse the configuration file */
1389c2daa00SOllivier Robert getconfig(argc, argv);
1392b15cb3dSCy Schubert loop_config(LOOP_DRIFTINIT, 0);
1402b15cb3dSCy Schubert initializing = FALSE;
1419c2daa00SOllivier Robert
1429c2daa00SOllivier Robert /*
1439c2daa00SOllivier Robert * Watch out here, we want the real time, not the silly stuff.
1449c2daa00SOllivier Robert */
1459c2daa00SOllivier Robert gettimeofday(&seed, NULL);
146ea906c41SOllivier Robert ntp_srandom(seed.tv_usec);
1479c2daa00SOllivier Robert
1482b15cb3dSCy Schubert /* Initialize the event queue */
1492b15cb3dSCy Schubert event_queue = create_priority_queue((q_order_func)
1502b15cb3dSCy Schubert determine_event_ordering);
1512b15cb3dSCy Schubert
1522b15cb3dSCy Schubert /* Initialize the receive queue */
1532b15cb3dSCy Schubert recv_queue = create_priority_queue((q_order_func)
1542b15cb3dSCy Schubert determine_recv_buf_ordering);
1552b15cb3dSCy Schubert
1562b15cb3dSCy Schubert /* Push a beep and a timer on the event queue */
1572b15cb3dSCy Schubert enqueue(event_queue, event(0, BEEP));
1582b15cb3dSCy Schubert enqueue(event_queue, event(simulation.sim_time + 1.0, TIMER));
1599c2daa00SOllivier Robert
1609c2daa00SOllivier Robert /*
1619c2daa00SOllivier Robert * Pop the queue until nothing is left or time is exceeded
1629c2daa00SOllivier Robert */
1632b15cb3dSCy Schubert /* maxtime = simulation.sim_time + simulation.end_time;*/
1642b15cb3dSCy Schubert while (simulation.sim_time <= simulation.end_time &&
1652b15cb3dSCy Schubert (!empty(event_queue))) {
1662b15cb3dSCy Schubert curr_event = dequeue(event_queue);
1672b15cb3dSCy Schubert /* Update all the clocks to the time on the event */
1682b15cb3dSCy Schubert sim_update_clocks(curr_event);
1692b15cb3dSCy Schubert
1702b15cb3dSCy Schubert /* Execute the function associated with the event */
1712b15cb3dSCy Schubert (*event_ptr[curr_event->function])(curr_event);
1722b15cb3dSCy Schubert free_node(curr_event);
1739c2daa00SOllivier Robert }
1742b15cb3dSCy Schubert printf("sys_received: %lu\n", sys_received);
1752b15cb3dSCy Schubert printf("sys_badlength: %lu\n", sys_badlength);
1762b15cb3dSCy Schubert printf("sys_declined: %lu\n", sys_declined);
1772b15cb3dSCy Schubert printf("sys_restricted: %lu\n", sys_restricted);
1782b15cb3dSCy Schubert printf("sys_newversion: %lu\n", sys_newversion);
1792b15cb3dSCy Schubert printf("sys_oldversion: %lu\n", sys_oldversion);
1802b15cb3dSCy Schubert printf("sys_limitrejected: %lu\n", sys_limitrejected);
1812b15cb3dSCy Schubert printf("sys_badauth: %lu\n", sys_badauth);
1822b15cb3dSCy Schubert
1839c2daa00SOllivier Robert return (0);
1849c2daa00SOllivier Robert }
1859c2daa00SOllivier Robert
1869c2daa00SOllivier Robert
1872b15cb3dSCy Schubert void
init_sim_io(void)1882b15cb3dSCy Schubert init_sim_io(void)
1899c2daa00SOllivier Robert {
1902b15cb3dSCy Schubert loopback_interface = emalloc_zero(sizeof(*loopback_interface));
1912b15cb3dSCy Schubert ep_list = loopback_interface;
1922b15cb3dSCy Schubert strlcpy(loopback_interface->name, "IPv4loop",
1932b15cb3dSCy Schubert sizeof(loopback_interface->name));
1942b15cb3dSCy Schubert loopback_interface->flags = INT_UP | INT_LOOPBACK;
1952b15cb3dSCy Schubert loopback_interface->fd = -1;
1962b15cb3dSCy Schubert loopback_interface->bfd = -1;
1972b15cb3dSCy Schubert loopback_interface->ifnum = 1;
1982b15cb3dSCy Schubert loopback_interface->family = AF_INET;
1992b15cb3dSCy Schubert AF(&loopback_interface->sin) = AF_INET;
2002b15cb3dSCy Schubert SET_ADDR4(&loopback_interface->sin, LOOPBACKADR);
2012b15cb3dSCy Schubert SET_PORT(&loopback_interface->sin, NTP_PORT);
2022b15cb3dSCy Schubert AF(&loopback_interface->mask) = AF_INET;
2032b15cb3dSCy Schubert SET_ADDR4(&loopback_interface->mask, LOOPNETMASK);
2042b15cb3dSCy Schubert }
2059c2daa00SOllivier Robert
2062b15cb3dSCy Schubert
2072b15cb3dSCy Schubert /* Define a function to create an return an Event */
2082b15cb3dSCy Schubert
event(double t,funcTkn f)2092b15cb3dSCy Schubert Event *event(double t, funcTkn f)
2102b15cb3dSCy Schubert {
2112b15cb3dSCy Schubert Event *e;
2122b15cb3dSCy Schubert
2132b15cb3dSCy Schubert if ((e = get_node(sizeof(*e))) == NULL)
2142b15cb3dSCy Schubert abortsim("get_node failed in event");
2152b15cb3dSCy Schubert e->time = t;
2162b15cb3dSCy Schubert e->function = f;
2179c2daa00SOllivier Robert return (e);
2189c2daa00SOllivier Robert }
2199c2daa00SOllivier Robert
2202b15cb3dSCy Schubert /* NTP SIMULATION FUNCTIONS */
2212b15cb3dSCy Schubert
2222b15cb3dSCy Schubert /* Define a function for processing a timer interrupt.
2232b15cb3dSCy Schubert * On every timer interrupt, call the NTP timer to send packets and process
2242b15cb3dSCy Schubert * the clock and then call the receive function to receive packets.
2259c2daa00SOllivier Robert */
sim_event_timer(Event * e)2262b15cb3dSCy Schubert void sim_event_timer(Event *e)
2279c2daa00SOllivier Robert {
2289c2daa00SOllivier Robert struct recvbuf *rbuf;
2299c2daa00SOllivier Robert
2302b15cb3dSCy Schubert /* Call the NTP timer.
2312b15cb3dSCy Schubert * This will be responsible for actually "sending the packets."
2322b15cb3dSCy Schubert * Since this is a simulation, the packets sent over the network
2332b15cb3dSCy Schubert * will be processed by the simulate_server routine below.
2342b15cb3dSCy Schubert */
2359c2daa00SOllivier Robert timer();
2369c2daa00SOllivier Robert
2372b15cb3dSCy Schubert /* Process received buffers */
2382b15cb3dSCy Schubert while (!empty(recv_queue)) {
2392b15cb3dSCy Schubert rbuf = (struct recvbuf *)dequeue(recv_queue);
2402b15cb3dSCy Schubert (*rbuf->receiver)(rbuf);
2412b15cb3dSCy Schubert free_node(rbuf);
2429c2daa00SOllivier Robert }
2439c2daa00SOllivier Robert
2442b15cb3dSCy Schubert /* Arm the next timer interrupt. */
2452b15cb3dSCy Schubert enqueue(event_queue,
2462b15cb3dSCy Schubert event(simulation.sim_time + (1 << EVENT_TIMEOUT), TIMER));
2479c2daa00SOllivier Robert }
2489c2daa00SOllivier Robert
2499c2daa00SOllivier Robert
2502b15cb3dSCy Schubert
2512b15cb3dSCy Schubert /* Define a function to simulate a server.
2522b15cb3dSCy Schubert * This function processes the sent packet according to the server script,
2532b15cb3dSCy Schubert * creates a reply packet and pushes the reply packet onto the event queue
2549c2daa00SOllivier Robert */
simulate_server(sockaddr_u * serv_addr,endpt * inter,struct pkt * rpkt)2552b15cb3dSCy Schubert int simulate_server(
2562b15cb3dSCy Schubert sockaddr_u *serv_addr, /* Address of the server */
2572b15cb3dSCy Schubert endpt * inter, /* Interface on which the reply should
2582b15cb3dSCy Schubert be inserted */
2592b15cb3dSCy Schubert struct pkt *rpkt /* Packet sent to the server that
2602b15cb3dSCy Schubert needs to be processed. */
2619c2daa00SOllivier Robert )
2629c2daa00SOllivier Robert {
2632b15cb3dSCy Schubert struct pkt xpkt; /* Packet to be transmitted back
2642b15cb3dSCy Schubert to the client */
2652b15cb3dSCy Schubert struct recvbuf rbuf; /* Buffer for the received packet */
2662b15cb3dSCy Schubert Event *e; /* Packet receive event */
2672b15cb3dSCy Schubert server_info *server; /* Pointer to the server being simulated */
2682b15cb3dSCy Schubert script_info *curr_script; /* Current script being processed */
2692b15cb3dSCy Schubert int i;
2702b15cb3dSCy Schubert double d1, d2, d3; /* Delays while the packet is enroute */
2712b15cb3dSCy Schubert double t1, t2, t3, t4; /* The four timestamps in the packet */
2722b15cb3dSCy Schubert l_fp lfp_host; /* host-order l_fp */
2739c2daa00SOllivier Robert
2742b15cb3dSCy Schubert ZERO(xpkt);
2752b15cb3dSCy Schubert ZERO(rbuf);
2762b15cb3dSCy Schubert
2772b15cb3dSCy Schubert /* Search for the server with the desired address */
2782b15cb3dSCy Schubert server = NULL;
2792b15cb3dSCy Schubert for (i = 0; i < simulation.num_of_servers; ++i) {
2802b15cb3dSCy Schubert if (memcmp(simulation.servers[i].addr, serv_addr,
2812b15cb3dSCy Schubert sizeof(*serv_addr)) == 0) {
2822b15cb3dSCy Schubert server = &simulation.servers[i];
2832b15cb3dSCy Schubert break;
2842b15cb3dSCy Schubert }
2852b15cb3dSCy Schubert }
2862b15cb3dSCy Schubert
2872b15cb3dSCy Schubert fprintf(stderr, "Received packet from %s on %s\n",
2882b15cb3dSCy Schubert stoa(serv_addr), latoa(inter));
2892b15cb3dSCy Schubert if (server == NULL)
2902b15cb3dSCy Schubert abortsim("Server with specified address not found!!!");
2912b15cb3dSCy Schubert
2922b15cb3dSCy Schubert /* Get the current script for the server */
2932b15cb3dSCy Schubert curr_script = server->curr_script;
2942b15cb3dSCy Schubert
2952b15cb3dSCy Schubert /* Create a server reply packet.
2962b15cb3dSCy Schubert * Masquerade the reply as a stratum-1 server with a GPS clock
2979c2daa00SOllivier Robert */
2989c2daa00SOllivier Robert xpkt.li_vn_mode = PKT_LI_VN_MODE(LEAP_NOWARNING, NTP_VERSION,
2999c2daa00SOllivier Robert MODE_SERVER);
3009c2daa00SOllivier Robert xpkt.stratum = STRATUM_TO_PKT(((u_char)1));
3019c2daa00SOllivier Robert memcpy(&xpkt.refid, "GPS", 4);
3029c2daa00SOllivier Robert xpkt.ppoll = rpkt->ppoll;
3039c2daa00SOllivier Robert xpkt.precision = rpkt->precision;
3049c2daa00SOllivier Robert xpkt.rootdelay = 0;
3052b15cb3dSCy Schubert xpkt.rootdisp = 0;
3069c2daa00SOllivier Robert
3072b15cb3dSCy Schubert /* TIMESTAMP CALCULATIONS
3082b15cb3dSCy Schubert t1 t4
3092b15cb3dSCy Schubert \ /
3102b15cb3dSCy Schubert d1 \ / d3
3112b15cb3dSCy Schubert \ /
3122b15cb3dSCy Schubert t2 ----------------- t3
3132b15cb3dSCy Schubert d2
3149c2daa00SOllivier Robert */
3152b15cb3dSCy Schubert /* Compute the delays */
3162b15cb3dSCy Schubert d1 = poisson(curr_script->prop_delay, curr_script->jitter);
3172b15cb3dSCy Schubert d2 = poisson(curr_script->proc_delay, 0);
3182b15cb3dSCy Schubert d3 = poisson(curr_script->prop_delay, curr_script->jitter);
3192b15cb3dSCy Schubert
3202b15cb3dSCy Schubert /* Note: In the transmitted packet:
3212b15cb3dSCy Schubert * 1. t1 and t4 are times in the client according to the local clock.
3222b15cb3dSCy Schubert * 2. t2 and t3 are server times according to the simulated server.
3232b15cb3dSCy Schubert * Compute t1, t2, t3 and t4
3242b15cb3dSCy Schubert * Note: This function is called at time t1.
3252b15cb3dSCy Schubert */
3262b15cb3dSCy Schubert
3272b15cb3dSCy Schubert NTOHL_FP(&rpkt->xmt, &lfp_host);
3282b15cb3dSCy Schubert LFPTOD(&lfp_host, t1);
3292b15cb3dSCy Schubert t2 = server->server_time + d1;
3302b15cb3dSCy Schubert t3 = server->server_time + d1 + d2;
3312b15cb3dSCy Schubert t4 = t1 + d1 + d2 + d3;
3322b15cb3dSCy Schubert
3332b15cb3dSCy Schubert /* Save the timestamps */
3349c2daa00SOllivier Robert xpkt.org = rpkt->xmt;
3352b15cb3dSCy Schubert DTOLFP(t2, &lfp_host);
3362b15cb3dSCy Schubert HTONL_FP(&lfp_host, &xpkt.rec);
3372b15cb3dSCy Schubert DTOLFP(t3, &lfp_host);
3382b15cb3dSCy Schubert HTONL_FP(&lfp_host, &xpkt.xmt);
3399c2daa00SOllivier Robert xpkt.reftime = xpkt.xmt;
3409c2daa00SOllivier Robert
3419c2daa00SOllivier Robert /*
3422b15cb3dSCy Schubert * Ok, we are done with the packet. Now initialize the receive
3432b15cb3dSCy Schubert * buffer for the packet.
3449c2daa00SOllivier Robert */
3452b15cb3dSCy Schubert rbuf.used = 1;
3462b15cb3dSCy Schubert rbuf.receiver = &receive; /* callback to process the packet */
3479c2daa00SOllivier Robert rbuf.recv_length = LEN_PKT_NOMAC;
3489c2daa00SOllivier Robert rbuf.recv_pkt = xpkt;
3492b15cb3dSCy Schubert rbuf.dstadr = inter;
3502b15cb3dSCy Schubert rbuf.fd = inter->fd;
3512b15cb3dSCy Schubert memcpy(&rbuf.srcadr, serv_addr, sizeof(rbuf.srcadr));
3522b15cb3dSCy Schubert memcpy(&rbuf.recv_srcadr, serv_addr, sizeof(rbuf.recv_srcadr));
3539c2daa00SOllivier Robert
3549c2daa00SOllivier Robert /*
3552b15cb3dSCy Schubert * Create a packet event and insert it onto the event_queue at the
3562b15cb3dSCy Schubert * arrival time (t4) of the packet at the client
3579c2daa00SOllivier Robert */
3582b15cb3dSCy Schubert e = event(t4, PACKET);
3592b15cb3dSCy Schubert e->rcv_buf = rbuf;
3602b15cb3dSCy Schubert enqueue(event_queue, e);
3612b15cb3dSCy Schubert
3622b15cb3dSCy Schubert /*
3632b15cb3dSCy Schubert * Check if the time of the script has expired. If yes, delete it.
3642b15cb3dSCy Schubert */
3652b15cb3dSCy Schubert if (curr_script->duration > simulation.sim_time &&
3662b15cb3dSCy Schubert NULL == HEAD_PFIFO(server->script)) {
3672b15cb3dSCy Schubert printf("Hello\n");
3682b15cb3dSCy Schubert /*
3692b15cb3dSCy Schubert * For some reason freeing up the curr_script memory kills the
3702b15cb3dSCy Schubert * simulation. Further debugging is needed to determine why.
3712b15cb3dSCy Schubert * free(curr_script);
3722b15cb3dSCy Schubert */
3732b15cb3dSCy Schubert UNLINK_FIFO(curr_script, *server->script, link);
3742b15cb3dSCy Schubert }
3752b15cb3dSCy Schubert
3769c2daa00SOllivier Robert return (0);
3779c2daa00SOllivier Robert }
3789c2daa00SOllivier Robert
3799c2daa00SOllivier Robert
3802b15cb3dSCy Schubert /* Define a function to update all the clocks
3812b15cb3dSCy Schubert * Most of the code is modified from the systime.c file by Prof. Mills
3829c2daa00SOllivier Robert */
3832b15cb3dSCy Schubert
sim_update_clocks(Event * e)3842b15cb3dSCy Schubert void sim_update_clocks(Event *e)
3852b15cb3dSCy Schubert {
3862b15cb3dSCy Schubert double time_gap;
3872b15cb3dSCy Schubert double adj;
3882b15cb3dSCy Schubert int i;
3892b15cb3dSCy Schubert
3902b15cb3dSCy Schubert /* Compute the time between the last update event and this update */
3912b15cb3dSCy Schubert time_gap = e->time - simulation.sim_time;
3922b15cb3dSCy Schubert
3932b15cb3dSCy Schubert if (time_gap < 0)
3942b15cb3dSCy Schubert printf("WARNING: e->time %.6g comes before sim_time %.6g (gap %+.6g)\n",
3952b15cb3dSCy Schubert e->time, simulation.sim_time, time_gap);
3962b15cb3dSCy Schubert
3972b15cb3dSCy Schubert /* Advance the client clock */
3982b15cb3dSCy Schubert if (e->time + time_gap < simclock.local_time)
3992b15cb3dSCy Schubert printf("WARNING: e->time + gap %.6g comes before local_time %.6g\n",
4002b15cb3dSCy Schubert e->time + time_gap, simclock.local_time);
4012b15cb3dSCy Schubert simclock.local_time = e->time + time_gap;
4022b15cb3dSCy Schubert
4032b15cb3dSCy Schubert /* Advance the simulation time */
4042b15cb3dSCy Schubert simulation.sim_time = e->time;
4052b15cb3dSCy Schubert
4062b15cb3dSCy Schubert /* Advance the server clocks adjusted for systematic and random frequency
4072b15cb3dSCy Schubert * errors. The random error is a random walk computed as the
4082b15cb3dSCy Schubert * integral of samples from a Gaussian distribution.
4092b15cb3dSCy Schubert */
4102b15cb3dSCy Schubert for (i = 0; i < simulation.num_of_servers; ++i) {
4112b15cb3dSCy Schubert simulation.servers[i].curr_script->freq_offset +=
4122b15cb3dSCy Schubert gauss(0, time_gap * simulation.servers[i].curr_script->wander);
4132b15cb3dSCy Schubert
4142b15cb3dSCy Schubert simulation.servers[i].server_time += time_gap *
4152b15cb3dSCy Schubert (1 + simulation.servers[i].curr_script->freq_offset);
4162b15cb3dSCy Schubert }
4172b15cb3dSCy Schubert
4182b15cb3dSCy Schubert /* Perform the adjtime() function. If the adjustment completed
4192b15cb3dSCy Schubert * in the previous interval, amortize the entire amount; if not,
4202b15cb3dSCy Schubert * carry the leftover to the next interval.
4212b15cb3dSCy Schubert */
4222b15cb3dSCy Schubert
4232b15cb3dSCy Schubert adj = time_gap * simclock.slew;
4242b15cb3dSCy Schubert if (adj < fabs(simclock.adj)) {
4252b15cb3dSCy Schubert if (simclock.adj < 0) {
4262b15cb3dSCy Schubert simclock.adj += adj;
4272b15cb3dSCy Schubert simclock.local_time -= adj;
4282b15cb3dSCy Schubert } else {
4292b15cb3dSCy Schubert simclock.adj -= adj;
4302b15cb3dSCy Schubert simclock.local_time += adj;
4312b15cb3dSCy Schubert }
4322b15cb3dSCy Schubert } else {
4332b15cb3dSCy Schubert simclock.local_time += simclock.adj;
4342b15cb3dSCy Schubert simclock.adj = 0;
4352b15cb3dSCy Schubert }
4362b15cb3dSCy Schubert }
4372b15cb3dSCy Schubert
4382b15cb3dSCy Schubert
4392b15cb3dSCy Schubert /* Define a function that processes a receive packet event.
4402b15cb3dSCy Schubert * This function simply inserts the packet received onto the receive queue
4412b15cb3dSCy Schubert */
4422b15cb3dSCy Schubert
sim_event_recv_packet(Event * e)4432b15cb3dSCy Schubert void sim_event_recv_packet(Event *e)
4449c2daa00SOllivier Robert {
4459c2daa00SOllivier Robert struct recvbuf *rbuf;
4469c2daa00SOllivier Robert
4472b15cb3dSCy Schubert /* Allocate a receive buffer and copy the packet to it */
4482b15cb3dSCy Schubert if ((rbuf = get_node(sizeof(*rbuf))) == NULL)
4492b15cb3dSCy Schubert abortsim("get_node failed in sim_event_recv_packet");
4502b15cb3dSCy Schubert memcpy(rbuf, &e->rcv_buf, sizeof(*rbuf));
4519c2daa00SOllivier Robert
4522b15cb3dSCy Schubert /* Store the local time in the received packet */
4532b15cb3dSCy Schubert DTOLFP(simclock.local_time, &rbuf->recv_time);
4542b15cb3dSCy Schubert
4552b15cb3dSCy Schubert /* Insert the packet received onto the receive queue */
4562b15cb3dSCy Schubert enqueue(recv_queue, rbuf);
4579c2daa00SOllivier Robert }
4589c2daa00SOllivier Robert
4599c2daa00SOllivier Robert
4602b15cb3dSCy Schubert
4612b15cb3dSCy Schubert /* Define a function to output simulation statistics on a beep event
4629c2daa00SOllivier Robert */
4632b15cb3dSCy Schubert
4642b15cb3dSCy Schubert /*** TODO: Need to decide on how to output for multiple servers ***/
sim_event_beep(Event * e)4652b15cb3dSCy Schubert void sim_event_beep(Event *e)
4669c2daa00SOllivier Robert {
4672b15cb3dSCy Schubert #if 0
4689c2daa00SOllivier Robert static int first_time = 1;
4699c2daa00SOllivier Robert char *dash = "-----------------";
4702b15cb3dSCy Schubert #endif
4719c2daa00SOllivier Robert
4722b15cb3dSCy Schubert fprintf(stderr, "BEEP!!!\n");
4732b15cb3dSCy Schubert enqueue(event_queue, event(e->time + simulation.beep_delay, BEEP));
4742b15cb3dSCy Schubert #if 0
4752b15cb3dSCy Schubert if(simulation.beep_delay > 0) {
4769c2daa00SOllivier Robert if (first_time) {
4772b15cb3dSCy Schubert printf("\t%4c T %4c\t%4c T+ERR %3c\t%5cT+ERR+NTP\n",
4782b15cb3dSCy Schubert ' ', ' ', ' ', ' ',' ');
4799c2daa00SOllivier Robert printf("\t%s\t%s\t%s\n", dash, dash, dash);
4809c2daa00SOllivier Robert first_time = 0;
4812b15cb3dSCy Schubert
4829c2daa00SOllivier Robert printf("\t%16.6f\t%16.6f\t%16.6f\n",
4839c2daa00SOllivier Robert n->time, n->clk_time, n->ntp_time);
4849c2daa00SOllivier Robert return;
4859c2daa00SOllivier Robert }
4869c2daa00SOllivier Robert printf("\t%16.6f\t%16.6f\t%16.6f\n",
4872b15cb3dSCy Schubert simclock.local_time,
4889c2daa00SOllivier Robert n->time, n->clk_time, n->ntp_time);
4892b15cb3dSCy Schubert #endif
4902b15cb3dSCy Schubert
4919c2daa00SOllivier Robert }
4929c2daa00SOllivier Robert
4939c2daa00SOllivier Robert
4942b15cb3dSCy Schubert /* Define a function to abort the simulation on an error and spit out an
4952b15cb3dSCy Schubert * error message
4969c2daa00SOllivier Robert */
4972b15cb3dSCy Schubert
4982b15cb3dSCy Schubert void abortsim(char *errmsg)
4999c2daa00SOllivier Robert {
5009c2daa00SOllivier Robert perror(errmsg);
5019c2daa00SOllivier Robert exit(1);
5029c2daa00SOllivier Robert }
5032b15cb3dSCy Schubert
5042b15cb3dSCy Schubert
5052b15cb3dSCy Schubert
5062b15cb3dSCy Schubert /* CODE ORIGINALLY IN libntp/systime.c
5072b15cb3dSCy Schubert * -----------------------------------
5082b15cb3dSCy Schubert * This code was a part of the original NTP simulator and originally
5092b15cb3dSCy Schubert * had its home in the libntp/systime.c file.
5102b15cb3dSCy Schubert *
5112b15cb3dSCy Schubert * It has been shamelessly moved to here and has been modified for the
5122b15cb3dSCy Schubert * purposes of the current simulator.
5132b15cb3dSCy Schubert */
5142b15cb3dSCy Schubert
5152b15cb3dSCy Schubert
5162b15cb3dSCy Schubert /*
5172b15cb3dSCy Schubert * get_systime - return the system time in NTP timestamp format
5182b15cb3dSCy Schubert */
5192b15cb3dSCy Schubert void
5202b15cb3dSCy Schubert get_systime(
5212b15cb3dSCy Schubert l_fp *now /* current system time in l_fp */ )
5222b15cb3dSCy Schubert {
5232b15cb3dSCy Schubert /*
5242b15cb3dSCy Schubert * To fool the code that determines the local clock precision,
5252b15cb3dSCy Schubert * we advance the clock a minimum of 200 nanoseconds on every
5262b15cb3dSCy Schubert * clock read. This is appropriate for a typical modern machine
5272b15cb3dSCy Schubert * with nanosecond clocks. Note we make no attempt here to
5282b15cb3dSCy Schubert * simulate reading error, since the error is so small. This may
5292b15cb3dSCy Schubert * change when the need comes to implement picosecond clocks.
5302b15cb3dSCy Schubert */
5312b15cb3dSCy Schubert if (simclock.local_time == simclock.last_read_time)
5322b15cb3dSCy Schubert simclock.local_time += 200e-9;
5332b15cb3dSCy Schubert
5342b15cb3dSCy Schubert simclock.last_read_time = simclock.local_time;
5352b15cb3dSCy Schubert DTOLFP(simclock.local_time, now);
5362b15cb3dSCy Schubert /* OLD Code
5372b15cb3dSCy Schubert if (ntp_node.ntp_time == ntp_node.last_time)
5382b15cb3dSCy Schubert ntp_node.ntp_time += 200e-9;
5392b15cb3dSCy Schubert ntp_node.last_time = ntp_node.ntp_time;
5402b15cb3dSCy Schubert DTOLFP(ntp_node.ntp_time, now);
5412b15cb3dSCy Schubert */
5422b15cb3dSCy Schubert }
5432b15cb3dSCy Schubert
5442b15cb3dSCy Schubert
5452b15cb3dSCy Schubert /*
5462b15cb3dSCy Schubert * adj_systime - advance or retard the system clock exactly like the
5472b15cb3dSCy Schubert * real thng.
5482b15cb3dSCy Schubert */
5492b15cb3dSCy Schubert int /* always succeeds */
5502b15cb3dSCy Schubert adj_systime(
5512b15cb3dSCy Schubert double now /* time adjustment (s) */
5522b15cb3dSCy Schubert )
5532b15cb3dSCy Schubert {
5542b15cb3dSCy Schubert struct timeval adjtv; /* new adjustment */
5552b15cb3dSCy Schubert double dtemp;
5562b15cb3dSCy Schubert long ticks;
5572b15cb3dSCy Schubert int isneg = 0;
5582b15cb3dSCy Schubert
5592b15cb3dSCy Schubert /*
5602b15cb3dSCy Schubert * Most Unix adjtime() implementations adjust the system clock
5612b15cb3dSCy Schubert * in microsecond quanta, but some adjust in 10-ms quanta. We
5622b15cb3dSCy Schubert * carefully round the adjustment to the nearest quantum, then
5632b15cb3dSCy Schubert * adjust in quanta and keep the residue for later.
5642b15cb3dSCy Schubert */
5652b15cb3dSCy Schubert dtemp = now + sys_residual;
5662b15cb3dSCy Schubert if (dtemp < 0) {
5672b15cb3dSCy Schubert isneg = 1;
5682b15cb3dSCy Schubert dtemp = -dtemp;
5692b15cb3dSCy Schubert }
5702b15cb3dSCy Schubert adjtv.tv_sec = (long)dtemp;
5712b15cb3dSCy Schubert dtemp -= adjtv.tv_sec;
5722b15cb3dSCy Schubert ticks = (long)(dtemp / sys_tick + .5);
5732b15cb3dSCy Schubert adjtv.tv_usec = (long)(ticks * sys_tick * 1e6);
5742b15cb3dSCy Schubert dtemp -= adjtv.tv_usec / 1e6;
5752b15cb3dSCy Schubert sys_residual = dtemp;
5762b15cb3dSCy Schubert
5772b15cb3dSCy Schubert /*
5782b15cb3dSCy Schubert * Convert to signed seconds and microseconds for the Unix
5792b15cb3dSCy Schubert * adjtime() system call. Note we purposely lose the adjtime()
5802b15cb3dSCy Schubert * leftover.
5812b15cb3dSCy Schubert */
5822b15cb3dSCy Schubert if (isneg) {
5832b15cb3dSCy Schubert adjtv.tv_sec = -adjtv.tv_sec;
5842b15cb3dSCy Schubert adjtv.tv_usec = -adjtv.tv_usec;
5852b15cb3dSCy Schubert sys_residual = -sys_residual;
5862b15cb3dSCy Schubert }
5872b15cb3dSCy Schubert simclock.adj = now;
5882b15cb3dSCy Schubert /* ntp_node.adj = now; */
5892b15cb3dSCy Schubert return (1);
5902b15cb3dSCy Schubert }
5912b15cb3dSCy Schubert
5922b15cb3dSCy Schubert
5932b15cb3dSCy Schubert /*
5942b15cb3dSCy Schubert * step_systime - step the system clock. We are religious here.
5952b15cb3dSCy Schubert */
5962b15cb3dSCy Schubert int /* always succeeds */
5972b15cb3dSCy Schubert step_systime(
5982b15cb3dSCy Schubert double now /* step adjustment (s) */
5992b15cb3dSCy Schubert )
6002b15cb3dSCy Schubert {
6012b15cb3dSCy Schubert #ifdef DEBUG
6022b15cb3dSCy Schubert if (debug)
6032b15cb3dSCy Schubert printf("step_systime: time %.6f adj %.6f\n",
6042b15cb3dSCy Schubert simclock.local_time, now);
6052b15cb3dSCy Schubert #endif
6062b15cb3dSCy Schubert simclock.local_time += now;
6072b15cb3dSCy Schubert return (1);
6082b15cb3dSCy Schubert }
6092b15cb3dSCy Schubert
6102b15cb3dSCy Schubert /*
6112b15cb3dSCy Schubert * gauss() - returns samples from a gaussion distribution
6122b15cb3dSCy Schubert */
6132b15cb3dSCy Schubert double /* Gaussian sample */
6142b15cb3dSCy Schubert gauss(
6152b15cb3dSCy Schubert double m, /* sample mean */
6162b15cb3dSCy Schubert double s /* sample standard deviation (sigma) */
6172b15cb3dSCy Schubert )
6182b15cb3dSCy Schubert {
6192b15cb3dSCy Schubert double q1, q2;
6202b15cb3dSCy Schubert
6212b15cb3dSCy Schubert /*
6222b15cb3dSCy Schubert * Roll a sample from a Gaussian distribution with mean m and
6232b15cb3dSCy Schubert * standard deviation s. For m = 0, s = 1, mean(y) = 0,
6242b15cb3dSCy Schubert * std(y) = 1.
6252b15cb3dSCy Schubert */
6262b15cb3dSCy Schubert if (s == 0)
6272b15cb3dSCy Schubert return (m);
6282b15cb3dSCy Schubert while ((q1 = drand48()) == 0)
6292b15cb3dSCy Schubert /* empty statement */;
6302b15cb3dSCy Schubert q2 = drand48();
6312b15cb3dSCy Schubert return (m + s * sqrt(-2. * log(q1)) * cos(2. * PI * q2));
6322b15cb3dSCy Schubert }
6332b15cb3dSCy Schubert
6342b15cb3dSCy Schubert
6352b15cb3dSCy Schubert /*
6362b15cb3dSCy Schubert * poisson() - returns samples from a network delay distribution
6372b15cb3dSCy Schubert */
6382b15cb3dSCy Schubert double /* delay sample (s) */
6392b15cb3dSCy Schubert poisson(
6402b15cb3dSCy Schubert double m, /* fixed propagation delay (s) */
6412b15cb3dSCy Schubert double s /* exponential parameter (mu) */
6422b15cb3dSCy Schubert )
6432b15cb3dSCy Schubert {
6442b15cb3dSCy Schubert double q1;
6452b15cb3dSCy Schubert
6462b15cb3dSCy Schubert /*
6472b15cb3dSCy Schubert * Roll a sample from a composite distribution with propagation
6482b15cb3dSCy Schubert * delay m and exponential distribution time with parameter s.
6492b15cb3dSCy Schubert * For m = 0, s = 1, mean(y) = std(y) = 1.
6502b15cb3dSCy Schubert */
6512b15cb3dSCy Schubert if (s == 0)
6522b15cb3dSCy Schubert return (m);
6532b15cb3dSCy Schubert while ((q1 = drand48()) == 0)
6542b15cb3dSCy Schubert /* empty statement */;
6552b15cb3dSCy Schubert return (m - s * log(q1 * s));
6562b15cb3dSCy Schubert }
6572b15cb3dSCy Schubert
6582b15cb3dSCy Schubert #endif
659