diff options
author | Lorry Tar Creator <lorry-tar-importer@baserock.org> | 2014-12-02 09:01:21 +0000 |
---|---|---|
committer | <> | 2014-12-04 16:11:25 +0000 |
commit | bdab5265fcbf3f472545073a23f8999749a9f2b9 (patch) | |
tree | c6018dd03dea906f8f1fb5f105f05b71a7dc250a /ntpd/ntpsim.c | |
download | ntp-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.c | 657 |
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 |