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