5 * The source code for the ntp discrete event simulator.
7 * Written By: Sachin Kamboj
8 * University of Delaware
11 * (Some code shamelessly based on the original NTP discrete event simulator)
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
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()
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
),
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
[])
88 /* Initialize the local Clock
90 simclock
.local_time
= 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
119 /* Call getconfig to parse the configuration file */
120 getconfig(argc
, argv
);
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
);
161 /* Define a function to create an return an Event */
163 Event
*event(double t
, funcTkn f
)
167 if ((e
= get_node(sizeof(*e
))) == NULL
)
168 abortsim("get_node failed in event");
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.
191 /* Process received buffers */
192 while (!empty(recv_queue
)) {
193 rbuf
= (struct recvbuf
*)dequeue(recv_queue
);
194 (rbuf
->receiver
)(rbuf
);
198 /* Arm the next timer interrupt. */
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
210 sockaddr_u
*serv_addr
, /* Address of the server */
211 struct interface
*inter
, /* Interface on which the reply should
213 struct pkt
*rpkt
/* Packet sent to the server that
214 needs to be processed. */
217 struct pkt xpkt
; /* Packet to be transmitted back
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 */
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 */
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
];
241 fprintf(stderr
, "Received packet for: %s\n", stoa(serv_addr
));
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
,
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
;
260 /* TIMESTAMP CALCULATIONS
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
296 rbuf
.receiver
= receive
; /* Function to call to process the packet */
297 rbuf
.recv_length
= LEN_PKT_NOMAC
;
298 rbuf
.recv_pkt
= xpkt
;
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
);
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
)) {
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
);
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
)
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) {
375 simclock
.local_time
-= adj
;
379 simclock
.local_time
+= adj
;
383 simclock
.local_time
+= simclock
.adj
;
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
)
418 static int first_time
= 1;
419 char *dash
= "-----------------";
422 fprintf(stderr
, "BEEP!!!\n");
423 enqueue(event_queue
, event(e
->time
+ simulation
.beep_delay
, BEEP
));
425 if(simulation
.beep_delay
> 0) {
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
);
432 printf("\t%16.6f\t%16.6f\t%16.6f\n",
433 n
->time
, n
->clk_time
, n
->ntp_time
);
436 printf("\t%16.6f\t%16.6f\t%16.6f\n",
438 n
->time
, n
->clk_time
, n
->ntp_time
);
444 /* Define a function to abort the simulation on an error and spit out an
448 void abortsim(char *errmsg
)
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
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
);
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
499 int /* always succeeds */
501 double now
/* time adjustment (s) */
504 struct timeval adjtv
; /* new adjustment */
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
;
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()
533 adjtv
.tv_sec
= -adjtv
.tv_sec
;
534 adjtv
.tv_usec
= -adjtv
.tv_usec
;
535 sys_residual
= -sys_residual
;
538 /* ntp_node.adj = now; */
544 * step_systime - step the system clock. We are religious here.
546 int /* always succeeds */
548 double now
/* step adjustment (s) */
553 printf("step_systime: time %.6f adj %.6f\n",
554 simclock
.local_time
, now
);
556 simclock
.local_time
+= now
;
561 * gauss() - returns samples from a gaussion distribution
563 double /* Gaussian sample */
565 double m
, /* sample mean */
566 double s
/* sample standard deviation (sigma) */
572 * Roll a sample from a Gaussian distribution with mean m and
573 * standard deviation s. For m = 0, s = 1, mean(y) = 0,
578 while ((q1
= drand48()) == 0);
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) */
589 double m
, /* fixed propagation delay (s) */
590 double s
/* exponential parameter (mu) */
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.
602 while ((q1
= drand48()) == 0);
603 return (m
- s
* log(q1
* s
));