summaryrefslogtreecommitdiff
path: root/ntpd/ntpsim.c
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@baserock.org>2014-12-02 09:01:21 +0000
committer <>2014-12-04 16:11:25 +0000
commitbdab5265fcbf3f472545073a23f8999749a9f2b9 (patch)
treec6018dd03dea906f8f1fb5f105f05b71a7dc250a /ntpd/ntpsim.c
downloadntp-bdab5265fcbf3f472545073a23f8999749a9f2b9.tar.gz
Imported from /home/lorry/working-area/delta_ntp/ntp-dev-4.2.7p482.tar.gz.ntp-dev-4.2.7p482
Diffstat (limited to 'ntpd/ntpsim.c')
-rw-r--r--ntpd/ntpsim.c657
1 files changed, 657 insertions, 0 deletions
diff --git a/ntpd/ntpsim.c b/ntpd/ntpsim.c
new file mode 100644
index 0000000..b7c3218
--- /dev/null
+++ b/ntpd/ntpsim.c
@@ -0,0 +1,657 @@
+/* ntpdsim.c
+ *
+ * The source code for the ntp discrete event simulator.
+ *
+ * Written By: Sachin Kamboj
+ * University of Delaware
+ * Newark, DE 19711
+ * Copyright (c) 2006
+ * (Some code shamelessly based on the original NTP discrete event simulator)
+ */
+
+#include <config.h>
+#ifdef SIM
+#include "ntpd.h"
+#include "ntp_config.h"
+
+/* forward prototypes */
+int determine_event_ordering(const Event *e1, const Event *e2);
+int determine_recv_buf_ordering(const struct recvbuf *b1,
+ const struct recvbuf *b2);
+void create_server_associations(void);
+void init_sim_io(void);
+
+/* Global Variable Definitions */
+sim_info simulation; /* Simulation Control Variables */
+local_clock_info simclock; /* Local Clock Variables */
+queue *event_queue; /* Event Queue */
+queue *recv_queue; /* Receive Queue */
+static double sys_residual = 0; /* adjustment residue (s) */
+
+void (*event_ptr[]) (Event *) = {
+ sim_event_beep, sim_update_clocks, sim_event_timer, sim_event_recv_packet
+}; /* Function pointer to the events */
+
+
+/*
+ * Define a function to compare two events to determine which one occurs
+ * first.
+ */
+int
+determine_event_ordering(
+ const Event *e1,
+ const Event *e2
+ )
+{
+ return (e1->time - e2->time);
+}
+
+
+/*
+ * Define a function to compare two received packets to determine which
+ * one is received first.
+ */
+int
+determine_recv_buf_ordering(
+ const struct recvbuf *b1,
+ const struct recvbuf *b2
+ )
+{
+ double recv_time1;
+ double recv_time2;
+
+ /* Simply convert the time received to double and subtract */
+ LFPTOD(&b1->recv_time, recv_time1);
+ LFPTOD(&b2->recv_time, recv_time2);
+
+ return (int)(recv_time1 - recv_time2);
+}
+
+
+/* Define a function to create the server associations */
+void create_server_associations(void)
+{
+ int i;
+
+ for (i = 0; i < simulation.num_of_servers; ++i) {
+ printf("%s\n", stoa(simulation.servers[i].addr));
+ if (peer_config(simulation.servers[i].addr,
+ NULL,
+ loopback_interface,
+ MODE_CLIENT,
+ NTP_VERSION,
+ NTP_MINDPOLL,
+ NTP_MAXDPOLL,
+ 0, /* peerflags */
+ 0, /* ttl */
+ 0, /* peerkey */
+ NULL /* group ident */) == 0) {
+ fprintf(stderr,
+ "ERROR!! Could not create association for: %s\n",
+ stoa(simulation.servers[i].addr));
+ }
+ }
+}
+
+
+/* Main Simulator Code */
+
+int
+ntpsim(
+ int argc,
+ char * argv[]
+ )
+{
+ Event * curr_event;
+ struct timeval seed;
+
+ /* Initialize the local Clock */
+ simclock.local_time = 0;
+ simclock.adj = 0;
+ simclock.slew = 500e-6;
+
+ /* Initialize the simulation */
+ simulation.num_of_servers = 0;
+ simulation.beep_delay = BEEP_DLY;
+ simulation.sim_time = 0;
+ simulation.end_time = SIM_TIME;
+
+ /* Initialize ntp modules */
+ initializing = TRUE;
+ msyslog_term = TRUE;
+ init_sim_io();
+ init_auth();
+ init_util();
+ init_restrict();
+ init_mon();
+ init_timer();
+ init_lib();
+ init_request();
+ init_control();
+ init_peer();
+ init_proto();
+ init_loopfilter();
+ mon_start(MON_OFF);
+
+ /* Call getconfig to parse the configuration file */
+ getconfig(argc, argv);
+ loop_config(LOOP_DRIFTINIT, 0);
+ initializing = FALSE;
+
+ /*
+ * Watch out here, we want the real time, not the silly stuff.
+ */
+ gettimeofday(&seed, NULL);
+ ntp_srandom(seed.tv_usec);
+
+ /* Initialize the event queue */
+ event_queue = create_priority_queue((q_order_func)
+ determine_event_ordering);
+
+ /* Initialize the receive queue */
+ recv_queue = create_priority_queue((q_order_func)
+ determine_recv_buf_ordering);
+
+ /* Push a beep and a timer on the event queue */
+ enqueue(event_queue, event(0, BEEP));
+ enqueue(event_queue, event(simulation.sim_time + 1.0, TIMER));
+
+ /*
+ * Pop the queue until nothing is left or time is exceeded
+ */
+ /* maxtime = simulation.sim_time + simulation.end_time;*/
+ while (simulation.sim_time <= simulation.end_time &&
+ (!empty(event_queue))) {
+ curr_event = dequeue(event_queue);
+ /* Update all the clocks to the time on the event */
+ sim_update_clocks(curr_event);
+
+ /* Execute the function associated with the event */
+ (*event_ptr[curr_event->function])(curr_event);
+ free_node(curr_event);
+ }
+ printf("sys_received: %lu\n", sys_received);
+ printf("sys_badlength: %lu\n", sys_badlength);
+ printf("sys_declined: %lu\n", sys_declined);
+ printf("sys_restricted: %lu\n", sys_restricted);
+ printf("sys_newversion: %lu\n", sys_newversion);
+ printf("sys_oldversion: %lu\n", sys_oldversion);
+ printf("sys_limitrejected: %lu\n", sys_limitrejected);
+ printf("sys_badauth: %lu\n", sys_badauth);
+
+ return (0);
+}
+
+
+void
+init_sim_io(void)
+{
+ loopback_interface = emalloc_zero(sizeof(*loopback_interface));
+ ep_list = loopback_interface;
+ strlcpy(loopback_interface->name, "IPv4loop",
+ sizeof(loopback_interface->name));
+ loopback_interface->flags = INT_UP | INT_LOOPBACK;
+ loopback_interface->fd = -1;
+ loopback_interface->bfd = -1;
+ loopback_interface->ifnum = 1;
+ loopback_interface->family = AF_INET;
+ AF(&loopback_interface->sin) = AF_INET;
+ SET_ADDR4(&loopback_interface->sin, LOOPBACKADR);
+ SET_PORT(&loopback_interface->sin, NTP_PORT);
+ AF(&loopback_interface->mask) = AF_INET;
+ SET_ADDR4(&loopback_interface->mask, LOOPNETMASK);
+}
+
+
+/* Define a function to create an return an Event */
+
+Event *event(double t, funcTkn f)
+{
+ Event *e;
+
+ if ((e = get_node(sizeof(*e))) == NULL)
+ abortsim("get_node failed in event");
+ e->time = t;
+ e->function = f;
+ return (e);
+}
+
+/* NTP SIMULATION FUNCTIONS */
+
+/* Define a function for processing a timer interrupt.
+ * On every timer interrupt, call the NTP timer to send packets and process
+ * the clock and then call the receive function to receive packets.
+ */
+void sim_event_timer(Event *e)
+{
+ struct recvbuf *rbuf;
+
+ /* Call the NTP timer.
+ * This will be responsible for actually "sending the packets."
+ * Since this is a simulation, the packets sent over the network
+ * will be processed by the simulate_server routine below.
+ */
+ timer();
+
+ /* Process received buffers */
+ while (!empty(recv_queue)) {
+ rbuf = (struct recvbuf *)dequeue(recv_queue);
+ (*rbuf->receiver)(rbuf);
+ free_node(rbuf);
+ }
+
+ /* Arm the next timer interrupt. */
+ enqueue(event_queue,
+ event(simulation.sim_time + (1 << EVENT_TIMEOUT), TIMER));
+}
+
+
+
+/* Define a function to simulate a server.
+ * This function processes the sent packet according to the server script,
+ * creates a reply packet and pushes the reply packet onto the event queue
+ */
+int simulate_server(
+ sockaddr_u *serv_addr, /* Address of the server */
+ endpt * inter, /* Interface on which the reply should
+ be inserted */
+ struct pkt *rpkt /* Packet sent to the server that
+ needs to be processed. */
+ )
+{
+ struct pkt xpkt; /* Packet to be transmitted back
+ to the client */
+ struct recvbuf rbuf; /* Buffer for the received packet */
+ Event *e; /* Packet receive event */
+ server_info *server; /* Pointer to the server being simulated */
+ script_info *curr_script; /* Current script being processed */
+ int i;
+ double d1, d2, d3; /* Delays while the packet is enroute */
+ double t1, t2, t3, t4; /* The four timestamps in the packet */
+ l_fp lfp_host; /* host-order l_fp */
+
+ ZERO(xpkt);
+ ZERO(rbuf);
+
+ /* Search for the server with the desired address */
+ server = NULL;
+ for (i = 0; i < simulation.num_of_servers; ++i) {
+ if (memcmp(simulation.servers[i].addr, serv_addr,
+ sizeof(*serv_addr)) == 0) {
+ server = &simulation.servers[i];
+ break;
+ }
+ }
+
+ fprintf(stderr, "Received packet from %s on %s\n",
+ stoa(serv_addr), latoa(inter));
+ if (server == NULL)
+ abortsim("Server with specified address not found!!!");
+
+ /* Get the current script for the server */
+ curr_script = server->curr_script;
+
+ /* Create a server reply packet.
+ * Masquerade the reply as a stratum-1 server with a GPS clock
+ */
+ xpkt.li_vn_mode = PKT_LI_VN_MODE(LEAP_NOWARNING, NTP_VERSION,
+ MODE_SERVER);
+ xpkt.stratum = STRATUM_TO_PKT(((u_char)1));
+ memcpy(&xpkt.refid, "GPS", 4);
+ xpkt.ppoll = rpkt->ppoll;
+ xpkt.precision = rpkt->precision;
+ xpkt.rootdelay = 0;
+ xpkt.rootdisp = 0;
+
+ /* TIMESTAMP CALCULATIONS
+ t1 t4
+ \ /
+ d1 \ / d3
+ \ /
+ t2 ----------------- t3
+ d2
+ */
+ /* Compute the delays */
+ d1 = poisson(curr_script->prop_delay, curr_script->jitter);
+ d2 = poisson(curr_script->proc_delay, 0);
+ d3 = poisson(curr_script->prop_delay, curr_script->jitter);
+
+ /* Note: In the transmitted packet:
+ * 1. t1 and t4 are times in the client according to the local clock.
+ * 2. t2 and t3 are server times according to the simulated server.
+ * Compute t1, t2, t3 and t4
+ * Note: This function is called at time t1.
+ */
+
+ NTOHL_FP(&rpkt->xmt, &lfp_host);
+ LFPTOD(&lfp_host, t1);
+ t2 = server->server_time + d1;
+ t3 = server->server_time + d1 + d2;
+ t4 = t1 + d1 + d2 + d3;
+
+ /* Save the timestamps */
+ xpkt.org = rpkt->xmt;
+ DTOLFP(t2, &lfp_host);
+ HTONL_FP(&lfp_host, &xpkt.rec);
+ DTOLFP(t3, &lfp_host);
+ HTONL_FP(&lfp_host, &xpkt.xmt);
+ xpkt.reftime = xpkt.xmt;
+
+ /*
+ * Ok, we are done with the packet. Now initialize the receive
+ * buffer for the packet.
+ */
+ rbuf.used = 1;
+ rbuf.receiver = &receive; /* callback to process the packet */
+ rbuf.recv_length = LEN_PKT_NOMAC;
+ rbuf.recv_pkt = xpkt;
+ rbuf.dstadr = inter;
+ rbuf.fd = inter->fd;
+ memcpy(&rbuf.srcadr, serv_addr, sizeof(rbuf.srcadr));
+ memcpy(&rbuf.recv_srcadr, serv_addr, sizeof(rbuf.recv_srcadr));
+
+ /*
+ * Create a packet event and insert it onto the event_queue at the
+ * arrival time (t4) of the packet at the client
+ */
+ e = event(t4, PACKET);
+ e->rcv_buf = rbuf;
+ enqueue(event_queue, e);
+
+ /*
+ * Check if the time of the script has expired. If yes, delete it.
+ */
+ if (curr_script->duration > simulation.sim_time &&
+ NULL == HEAD_PFIFO(server->script)) {
+ printf("Hello\n");
+ /*
+ * For some reason freeing up the curr_script memory kills the
+ * simulation. Further debugging is needed to determine why.
+ * free(curr_script);
+ */
+ UNLINK_FIFO(curr_script, *server->script, link);
+ }
+
+ return (0);
+}
+
+
+/* Define a function to update all the clocks
+ * Most of the code is modified from the systime.c file by Prof. Mills
+ */
+
+void sim_update_clocks(Event *e)
+{
+ double time_gap;
+ double adj;
+ int i;
+
+ /* Compute the time between the last update event and this update */
+ time_gap = e->time - simulation.sim_time;
+
+ if (time_gap < 0)
+ printf("WARNING: e->time %.6g comes before sim_time %.6g (gap %+.6g)\n",
+ e->time, simulation.sim_time, time_gap);
+
+ /* Advance the client clock */
+ if (e->time + time_gap < simclock.local_time)
+ printf("WARNING: e->time + gap %.6g comes before local_time %.6g\n",
+ e->time + time_gap, simclock.local_time);
+ simclock.local_time = e->time + time_gap;
+
+ /* Advance the simulation time */
+ simulation.sim_time = e->time;
+
+ /* Advance the server clocks adjusted for systematic and random frequency
+ * errors. The random error is a random walk computed as the
+ * integral of samples from a Gaussian distribution.
+ */
+ for (i = 0; i < simulation.num_of_servers; ++i) {
+ simulation.servers[i].curr_script->freq_offset +=
+ gauss(0, time_gap * simulation.servers[i].curr_script->wander);
+
+ simulation.servers[i].server_time += time_gap *
+ (1 + simulation.servers[i].curr_script->freq_offset);
+ }
+
+ /* Perform the adjtime() function. If the adjustment completed
+ * in the previous interval, amortize the entire amount; if not,
+ * carry the leftover to the next interval.
+ */
+
+ adj = time_gap * simclock.slew;
+ if (adj < fabs(simclock.adj)) {
+ if (simclock.adj < 0) {
+ simclock.adj += adj;
+ simclock.local_time -= adj;
+ } else {
+ simclock.adj -= adj;
+ simclock.local_time += adj;
+ }
+ } else {
+ simclock.local_time += simclock.adj;
+ simclock.adj = 0;
+ }
+}
+
+
+/* Define a function that processes a receive packet event.
+ * This function simply inserts the packet received onto the receive queue
+ */
+
+void sim_event_recv_packet(Event *e)
+{
+ struct recvbuf *rbuf;
+
+ /* Allocate a receive buffer and copy the packet to it */
+ if ((rbuf = get_node(sizeof(*rbuf))) == NULL)
+ abortsim("get_node failed in sim_event_recv_packet");
+ memcpy(rbuf, &e->rcv_buf, sizeof(*rbuf));
+
+ /* Store the local time in the received packet */
+ DTOLFP(simclock.local_time, &rbuf->recv_time);
+
+ /* Insert the packet received onto the receive queue */
+ enqueue(recv_queue, rbuf);
+}
+
+
+
+/* Define a function to output simulation statistics on a beep event
+ */
+
+/*** TODO: Need to decide on how to output for multiple servers ***/
+void sim_event_beep(Event *e)
+{
+#if 0
+ static int first_time = 1;
+ char *dash = "-----------------";
+#endif
+
+ fprintf(stderr, "BEEP!!!\n");
+ enqueue(event_queue, event(e->time + simulation.beep_delay, BEEP));
+#if 0
+ if(simulation.beep_delay > 0) {
+ if (first_time) {
+ printf("\t%4c T %4c\t%4c T+ERR %3c\t%5cT+ERR+NTP\n",
+ ' ', ' ', ' ', ' ',' ');
+ printf("\t%s\t%s\t%s\n", dash, dash, dash);
+ first_time = 0;
+
+ printf("\t%16.6f\t%16.6f\t%16.6f\n",
+ n->time, n->clk_time, n->ntp_time);
+ return;
+ }
+ printf("\t%16.6f\t%16.6f\t%16.6f\n",
+ simclock.local_time,
+ n->time, n->clk_time, n->ntp_time);
+#endif
+
+}
+
+
+/* Define a function to abort the simulation on an error and spit out an
+ * error message
+ */
+
+void abortsim(char *errmsg)
+{
+ perror(errmsg);
+ exit(1);
+}
+
+
+
+/* CODE ORIGINALLY IN libntp/systime.c
+ * -----------------------------------
+ * This code was a part of the original NTP simulator and originally
+ * had its home in the libntp/systime.c file.
+ *
+ * It has been shamelessly moved to here and has been modified for the
+ * purposes of the current simulator.
+ */
+
+
+/*
+ * get_systime - return the system time in NTP timestamp format
+ */
+void
+get_systime(
+ l_fp *now /* current system time in l_fp */ )
+{
+ /*
+ * To fool the code that determines the local clock precision,
+ * we advance the clock a minimum of 200 nanoseconds on every
+ * clock read. This is appropriate for a typical modern machine
+ * with nanosecond clocks. Note we make no attempt here to
+ * simulate reading error, since the error is so small. This may
+ * change when the need comes to implement picosecond clocks.
+ */
+ if (simclock.local_time == simclock.last_read_time)
+ simclock.local_time += 200e-9;
+
+ simclock.last_read_time = simclock.local_time;
+ DTOLFP(simclock.local_time, now);
+/* OLD Code
+ if (ntp_node.ntp_time == ntp_node.last_time)
+ ntp_node.ntp_time += 200e-9;
+ ntp_node.last_time = ntp_node.ntp_time;
+ DTOLFP(ntp_node.ntp_time, now);
+*/
+}
+
+
+/*
+ * adj_systime - advance or retard the system clock exactly like the
+ * real thng.
+ */
+int /* always succeeds */
+adj_systime(
+ double now /* time adjustment (s) */
+ )
+{
+ struct timeval adjtv; /* new adjustment */
+ double dtemp;
+ long ticks;
+ int isneg = 0;
+
+ /*
+ * Most Unix adjtime() implementations adjust the system clock
+ * in microsecond quanta, but some adjust in 10-ms quanta. We
+ * carefully round the adjustment to the nearest quantum, then
+ * adjust in quanta and keep the residue for later.
+ */
+ dtemp = now + sys_residual;
+ if (dtemp < 0) {
+ isneg = 1;
+ dtemp = -dtemp;
+ }
+ adjtv.tv_sec = (long)dtemp;
+ dtemp -= adjtv.tv_sec;
+ ticks = (long)(dtemp / sys_tick + .5);
+ adjtv.tv_usec = (long)(ticks * sys_tick * 1e6);
+ dtemp -= adjtv.tv_usec / 1e6;
+ sys_residual = dtemp;
+
+ /*
+ * Convert to signed seconds and microseconds for the Unix
+ * adjtime() system call. Note we purposely lose the adjtime()
+ * leftover.
+ */
+ if (isneg) {
+ adjtv.tv_sec = -adjtv.tv_sec;
+ adjtv.tv_usec = -adjtv.tv_usec;
+ sys_residual = -sys_residual;
+ }
+ simclock.adj = now;
+/* ntp_node.adj = now; */
+ return (1);
+}
+
+
+/*
+ * step_systime - step the system clock. We are religious here.
+ */
+int /* always succeeds */
+step_systime(
+ double now /* step adjustment (s) */
+ )
+{
+#ifdef DEBUG
+ if (debug)
+ printf("step_systime: time %.6f adj %.6f\n",
+ simclock.local_time, now);
+#endif
+ simclock.local_time += now;
+ return (1);
+}
+
+/*
+ * gauss() - returns samples from a gaussion distribution
+ */
+double /* Gaussian sample */
+gauss(
+ double m, /* sample mean */
+ double s /* sample standard deviation (sigma) */
+ )
+{
+ double q1, q2;
+
+ /*
+ * Roll a sample from a Gaussian distribution with mean m and
+ * standard deviation s. For m = 0, s = 1, mean(y) = 0,
+ * std(y) = 1.
+ */
+ if (s == 0)
+ return (m);
+ while ((q1 = drand48()) == 0)
+ /* empty statement */;
+ q2 = drand48();
+ return (m + s * sqrt(-2. * log(q1)) * cos(2. * PI * q2));
+}
+
+
+/*
+ * poisson() - returns samples from a network delay distribution
+ */
+double /* delay sample (s) */
+poisson(
+ double m, /* fixed propagation delay (s) */
+ double s /* exponential parameter (mu) */
+ )
+{
+ double q1;
+
+ /*
+ * Roll a sample from a composite distribution with propagation
+ * delay m and exponential distribution time with parameter s.
+ * For m = 0, s = 1, mean(y) = std(y) = 1.
+ */
+ if (s == 0)
+ return (m);
+ while ((q1 = drand48()) == 0)
+ /* empty statement */;
+ return (m - s * log(q1 * s));
+}
+
+#endif