1 /* $NetBSD: ntpsim.c,v 1.6 2020/05/25 20:47:25 christos Exp $ */
2
3 /* ntpdsim.c
4 *
5 * The source code for the ntp discrete event simulator.
6 *
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)
12 */
13
14 #include <config.h>
15 #ifdef SIM
16 #include "ntpd.h"
17 #include "ntp_config.h"
18
19 /* forward prototypes */
20 int determine_event_ordering(const Event *e1, const Event *e2);
21 int determine_recv_buf_ordering(const struct recvbuf *b1,
22 const struct recvbuf *b2);
23 void create_server_associations(void);
24 void init_sim_io(void);
25
26 /* Global Variable Definitions */
27 sim_info simulation; /* Simulation Control Variables */
28 local_clock_info simclock; /* Local Clock Variables */
29 queue *event_queue; /* Event Queue */
30 queue *recv_queue; /* Receive Queue */
31 static double sys_residual = 0; /* adjustment residue (s) */
32
33 void (*event_ptr[]) (Event *) = {
34 sim_event_beep, sim_update_clocks, sim_event_timer, sim_event_recv_packet
35 }; /* Function pointer to the events */
36
37
38 /*
39 * Define a function to compare two events to determine which one occurs
40 * first.
41 */
42 int
determine_event_ordering(const Event * e1,const Event * e2)43 determine_event_ordering(
44 const Event *e1,
45 const Event *e2
46 )
47 {
48 return (e1->time - e2->time);
49 }
50
51
52 /*
53 * Define a function to compare two received packets to determine which
54 * one is received first.
55 */
56 int
determine_recv_buf_ordering(const struct recvbuf * b1,const struct recvbuf * b2)57 determine_recv_buf_ordering(
58 const struct recvbuf *b1,
59 const struct recvbuf *b2
60 )
61 {
62 double recv_time1;
63 double recv_time2;
64
65 /* Simply convert the time received to double and subtract */
66 LFPTOD(&b1->recv_time, recv_time1);
67 LFPTOD(&b2->recv_time, recv_time2);
68
69 return (int)(recv_time1 - recv_time2);
70 }
71
72
73 /* Define a function to create the server associations */
create_server_associations(void)74 void create_server_associations(void)
75 {
76 int i;
77
78 for (i = 0; i < simulation.num_of_servers; ++i) {
79 printf("%s\n", stoa(simulation.servers[i].addr));
80 if (peer_config(simulation.servers[i].addr,
81 NULL,
82 loopback_interface,
83 MODE_CLIENT,
84 -1,
85 NTP_VERSION,
86 NTP_MINDPOLL,
87 NTP_MAXDPOLL,
88 0, /* peerflags */
89 0, /* ttl */
90 0, /* peerkey */
91 NULL /* group ident */) == 0) {
92 fprintf(stderr,
93 "ERROR!! Could not create association for: %s\n",
94 stoa(simulation.servers[i].addr));
95 }
96 }
97 }
98
99
100 /* Main Simulator Code */
101
102 int
ntpsim(int argc,char * argv[])103 ntpsim(
104 int argc,
105 char * argv[]
106 )
107 {
108 Event * curr_event;
109 struct timeval seed;
110
111 /* Initialize the local Clock */
112 simclock.local_time = 0;
113 simclock.adj = 0;
114 simclock.slew = 500e-6;
115
116 /* Initialize the simulation */
117 simulation.num_of_servers = 0;
118 simulation.beep_delay = BEEP_DLY;
119 simulation.sim_time = 0;
120 simulation.end_time = SIM_TIME;
121
122 /* Initialize ntp modules */
123 initializing = TRUE;
124 msyslog_term = TRUE;
125 init_sim_io();
126 init_auth();
127 init_util();
128 init_restrict();
129 init_mon();
130 init_timer();
131 init_lib();
132 init_request();
133 init_control();
134 init_peer();
135 init_proto();
136 init_loopfilter();
137 mon_start(MON_OFF);
138
139 /* Call getconfig to parse the configuration file */
140 getconfig(argc, argv);
141 loop_config(LOOP_DRIFTINIT, 0);
142 initializing = FALSE;
143
144 /*
145 * Watch out here, we want the real time, not the silly stuff.
146 */
147 gettimeofday(&seed, NULL);
148 ntp_srandom(seed.tv_usec);
149
150 /* Initialize the event queue */
151 event_queue = create_priority_queue((q_order_func)
152 determine_event_ordering);
153
154 /* Initialize the receive queue */
155 recv_queue = create_priority_queue((q_order_func)
156 determine_recv_buf_ordering);
157
158 /* Push a beep and a timer on the event queue */
159 enqueue(event_queue, event(0, BEEP));
160 enqueue(event_queue, event(simulation.sim_time + 1.0, TIMER));
161
162 /*
163 * Pop the queue until nothing is left or time is exceeded
164 */
165 /* maxtime = simulation.sim_time + simulation.end_time;*/
166 while (simulation.sim_time <= simulation.end_time &&
167 (!empty(event_queue))) {
168 curr_event = dequeue(event_queue);
169 /* Update all the clocks to the time on the event */
170 sim_update_clocks(curr_event);
171
172 /* Execute the function associated with the event */
173 (*event_ptr[curr_event->function])(curr_event);
174 free_node(curr_event);
175 }
176 printf("sys_received: %lu\n", sys_received);
177 printf("sys_badlength: %lu\n", sys_badlength);
178 printf("sys_declined: %lu\n", sys_declined);
179 printf("sys_restricted: %lu\n", sys_restricted);
180 printf("sys_newversion: %lu\n", sys_newversion);
181 printf("sys_oldversion: %lu\n", sys_oldversion);
182 printf("sys_limitrejected: %lu\n", sys_limitrejected);
183 printf("sys_badauth: %lu\n", sys_badauth);
184
185 return (0);
186 }
187
188
189 void
init_sim_io(void)190 init_sim_io(void)
191 {
192 loopback_interface = emalloc_zero(sizeof(*loopback_interface));
193 ep_list = loopback_interface;
194 strlcpy(loopback_interface->name, "IPv4loop",
195 sizeof(loopback_interface->name));
196 loopback_interface->flags = INT_UP | INT_LOOPBACK;
197 loopback_interface->fd = -1;
198 loopback_interface->bfd = -1;
199 loopback_interface->ifnum = 1;
200 loopback_interface->family = AF_INET;
201 AF(&loopback_interface->sin) = AF_INET;
202 SET_ADDR4(&loopback_interface->sin, LOOPBACKADR);
203 SET_PORT(&loopback_interface->sin, NTP_PORT);
204 AF(&loopback_interface->mask) = AF_INET;
205 SET_ADDR4(&loopback_interface->mask, LOOPNETMASK);
206 }
207
208
209 /* Define a function to create an return an Event */
210
event(double t,funcTkn f)211 Event *event(double t, funcTkn f)
212 {
213 Event *e;
214
215 if ((e = get_node(sizeof(*e))) == NULL)
216 abortsim("get_node failed in event");
217 e->time = t;
218 e->function = f;
219 return (e);
220 }
221
222 /* NTP SIMULATION FUNCTIONS */
223
224 /* Define a function for processing a timer interrupt.
225 * On every timer interrupt, call the NTP timer to send packets and process
226 * the clock and then call the receive function to receive packets.
227 */
sim_event_timer(Event * e)228 void sim_event_timer(Event *e)
229 {
230 struct recvbuf *rbuf;
231
232 /* Call the NTP timer.
233 * This will be responsible for actually "sending the packets."
234 * Since this is a simulation, the packets sent over the network
235 * will be processed by the simulate_server routine below.
236 */
237 timer();
238
239 /* Process received buffers */
240 while (!empty(recv_queue)) {
241 rbuf = (struct recvbuf *)dequeue(recv_queue);
242 (*rbuf->receiver)(rbuf);
243 free_node(rbuf);
244 }
245
246 /* Arm the next timer interrupt. */
247 enqueue(event_queue,
248 event(simulation.sim_time + (1 << EVENT_TIMEOUT), TIMER));
249 }
250
251
252
253 /* Define a function to simulate a server.
254 * This function processes the sent packet according to the server script,
255 * creates a reply packet and pushes the reply packet onto the event queue
256 */
simulate_server(sockaddr_u * serv_addr,endpt * inter,struct pkt * rpkt)257 int simulate_server(
258 sockaddr_u *serv_addr, /* Address of the server */
259 endpt * inter, /* Interface on which the reply should
260 be inserted */
261 struct pkt *rpkt /* Packet sent to the server that
262 needs to be processed. */
263 )
264 {
265 struct pkt xpkt; /* Packet to be transmitted back
266 to the client */
267 struct recvbuf rbuf; /* Buffer for the received packet */
268 Event *e; /* Packet receive event */
269 server_info *server; /* Pointer to the server being simulated */
270 script_info *curr_script; /* Current script being processed */
271 int i;
272 double d1, d2, d3; /* Delays while the packet is enroute */
273 double t1, t2, t3, t4; /* The four timestamps in the packet */
274 l_fp lfp_host; /* host-order l_fp */
275
276 ZERO(xpkt);
277 ZERO(rbuf);
278
279 /* Search for the server with the desired address */
280 server = NULL;
281 for (i = 0; i < simulation.num_of_servers; ++i) {
282 if (memcmp(simulation.servers[i].addr, serv_addr,
283 sizeof(*serv_addr)) == 0) {
284 server = &simulation.servers[i];
285 break;
286 }
287 }
288
289 fprintf(stderr, "Received packet from %s on %s\n",
290 stoa(serv_addr), latoa(inter));
291 if (server == NULL)
292 abortsim("Server with specified address not found!!!");
293
294 /* Get the current script for the server */
295 curr_script = server->curr_script;
296
297 /* Create a server reply packet.
298 * Masquerade the reply as a stratum-1 server with a GPS clock
299 */
300 xpkt.li_vn_mode = PKT_LI_VN_MODE(LEAP_NOWARNING, NTP_VERSION,
301 MODE_SERVER);
302 xpkt.stratum = STRATUM_TO_PKT(((u_char)1));
303 memcpy(&xpkt.refid, "GPS", 4);
304 xpkt.ppoll = rpkt->ppoll;
305 xpkt.precision = rpkt->precision;
306 xpkt.rootdelay = 0;
307 xpkt.rootdisp = 0;
308
309 /* TIMESTAMP CALCULATIONS
310 t1 t4
311 \ /
312 d1 \ / d3
313 \ /
314 t2 ----------------- t3
315 d2
316 */
317 /* Compute the delays */
318 d1 = poisson(curr_script->prop_delay, curr_script->jitter);
319 d2 = poisson(curr_script->proc_delay, 0);
320 d3 = poisson(curr_script->prop_delay, curr_script->jitter);
321
322 /* Note: In the transmitted packet:
323 * 1. t1 and t4 are times in the client according to the local clock.
324 * 2. t2 and t3 are server times according to the simulated server.
325 * Compute t1, t2, t3 and t4
326 * Note: This function is called at time t1.
327 */
328
329 NTOHL_FP(&rpkt->xmt, &lfp_host);
330 LFPTOD(&lfp_host, t1);
331 t2 = server->server_time + d1;
332 t3 = server->server_time + d1 + d2;
333 t4 = t1 + d1 + d2 + d3;
334
335 /* Save the timestamps */
336 xpkt.org = rpkt->xmt;
337 DTOLFP(t2, &lfp_host);
338 HTONL_FP(&lfp_host, &xpkt.rec);
339 DTOLFP(t3, &lfp_host);
340 HTONL_FP(&lfp_host, &xpkt.xmt);
341 xpkt.reftime = xpkt.xmt;
342
343 /*
344 * Ok, we are done with the packet. Now initialize the receive
345 * buffer for the packet.
346 */
347 rbuf.used = 1;
348 rbuf.receiver = &receive; /* callback to process the packet */
349 rbuf.recv_length = LEN_PKT_NOMAC;
350 rbuf.recv_pkt = xpkt;
351 rbuf.dstadr = inter;
352 rbuf.fd = inter->fd;
353 memcpy(&rbuf.srcadr, serv_addr, sizeof(rbuf.srcadr));
354 memcpy(&rbuf.recv_srcadr, serv_addr, sizeof(rbuf.recv_srcadr));
355
356 /*
357 * Create a packet event and insert it onto the event_queue at the
358 * arrival time (t4) of the packet at the client
359 */
360 e = event(t4, PACKET);
361 e->rcv_buf = rbuf;
362 enqueue(event_queue, e);
363
364 /*
365 * Check if the time of the script has expired. If yes, delete it.
366 */
367 if (curr_script->duration > simulation.sim_time &&
368 NULL == HEAD_PFIFO(server->script)) {
369 printf("Hello\n");
370 /*
371 * For some reason freeing up the curr_script memory kills the
372 * simulation. Further debugging is needed to determine why.
373 * free(curr_script);
374 */
375 UNLINK_FIFO(curr_script, *server->script, link);
376 }
377
378 return (0);
379 }
380
381
382 /* Define a function to update all the clocks
383 * Most of the code is modified from the systime.c file by Prof. Mills
384 */
385
sim_update_clocks(Event * e)386 void sim_update_clocks(Event *e)
387 {
388 double time_gap;
389 double adj;
390 int i;
391
392 /* Compute the time between the last update event and this update */
393 time_gap = e->time - simulation.sim_time;
394
395 if (time_gap < 0)
396 printf("WARNING: e->time %.6g comes before sim_time %.6g (gap %+.6g)\n",
397 e->time, simulation.sim_time, time_gap);
398
399 /* Advance the client clock */
400 if (e->time + time_gap < simclock.local_time)
401 printf("WARNING: e->time + gap %.6g comes before local_time %.6g\n",
402 e->time + time_gap, simclock.local_time);
403 simclock.local_time = e->time + time_gap;
404
405 /* Advance the simulation time */
406 simulation.sim_time = e->time;
407
408 /* Advance the server clocks adjusted for systematic and random frequency
409 * errors. The random error is a random walk computed as the
410 * integral of samples from a Gaussian distribution.
411 */
412 for (i = 0; i < simulation.num_of_servers; ++i) {
413 simulation.servers[i].curr_script->freq_offset +=
414 gauss(0, time_gap * simulation.servers[i].curr_script->wander);
415
416 simulation.servers[i].server_time += time_gap *
417 (1 + simulation.servers[i].curr_script->freq_offset);
418 }
419
420 /* Perform the adjtime() function. If the adjustment completed
421 * in the previous interval, amortize the entire amount; if not,
422 * carry the leftover to the next interval.
423 */
424
425 adj = time_gap * simclock.slew;
426 if (adj < fabs(simclock.adj)) {
427 if (simclock.adj < 0) {
428 simclock.adj += adj;
429 simclock.local_time -= adj;
430 } else {
431 simclock.adj -= adj;
432 simclock.local_time += adj;
433 }
434 } else {
435 simclock.local_time += simclock.adj;
436 simclock.adj = 0;
437 }
438 }
439
440
441 /* Define a function that processes a receive packet event.
442 * This function simply inserts the packet received onto the receive queue
443 */
444
sim_event_recv_packet(Event * e)445 void sim_event_recv_packet(Event *e)
446 {
447 struct recvbuf *rbuf;
448
449 /* Allocate a receive buffer and copy the packet to it */
450 if ((rbuf = get_node(sizeof(*rbuf))) == NULL)
451 abortsim("get_node failed in sim_event_recv_packet");
452 memcpy(rbuf, &e->rcv_buf, sizeof(*rbuf));
453
454 /* Store the local time in the received packet */
455 DTOLFP(simclock.local_time, &rbuf->recv_time);
456
457 /* Insert the packet received onto the receive queue */
458 enqueue(recv_queue, rbuf);
459 }
460
461
462
463 /* Define a function to output simulation statistics on a beep event
464 */
465
466 /*** TODO: Need to decide on how to output for multiple servers ***/
sim_event_beep(Event * e)467 void sim_event_beep(Event *e)
468 {
469 #if 0
470 static int first_time = 1;
471 char *dash = "-----------------";
472 #endif
473
474 fprintf(stderr, "BEEP!!!\n");
475 enqueue(event_queue, event(e->time + simulation.beep_delay, BEEP));
476 #if 0
477 if(simulation.beep_delay > 0) {
478 if (first_time) {
479 printf("\t%4c T %4c\t%4c T+ERR %3c\t%5cT+ERR+NTP\n",
480 ' ', ' ', ' ', ' ',' ');
481 printf("\t%s\t%s\t%s\n", dash, dash, dash);
482 first_time = 0;
483
484 printf("\t%16.6f\t%16.6f\t%16.6f\n",
485 n->time, n->clk_time, n->ntp_time);
486 return;
487 }
488 printf("\t%16.6f\t%16.6f\t%16.6f\n",
489 simclock.local_time,
490 n->time, n->clk_time, n->ntp_time);
491 #endif
492
493 }
494
495
496 /* Define a function to abort the simulation on an error and spit out an
497 * error message
498 */
499
500 void abortsim(char *errmsg)
501 {
502 perror(errmsg);
503 exit(1);
504 }
505
506
507
508 /* CODE ORIGINALLY IN libntp/systime.c
509 * -----------------------------------
510 * This code was a part of the original NTP simulator and originally
511 * had its home in the libntp/systime.c file.
512 *
513 * It has been shamelessly moved to here and has been modified for the
514 * purposes of the current simulator.
515 */
516
517
518 /*
519 * get_systime - return the system time in NTP timestamp format
520 */
521 void
522 get_systime(
523 l_fp *now /* current system time in l_fp */ )
524 {
525 /*
526 * To fool the code that determines the local clock precision,
527 * we advance the clock a minimum of 200 nanoseconds on every
528 * clock read. This is appropriate for a typical modern machine
529 * with nanosecond clocks. Note we make no attempt here to
530 * simulate reading error, since the error is so small. This may
531 * change when the need comes to implement picosecond clocks.
532 */
533 if (simclock.local_time == simclock.last_read_time)
534 simclock.local_time += 200e-9;
535
536 simclock.last_read_time = simclock.local_time;
537 DTOLFP(simclock.local_time, now);
538 /* OLD Code
539 if (ntp_node.ntp_time == ntp_node.last_time)
540 ntp_node.ntp_time += 200e-9;
541 ntp_node.last_time = ntp_node.ntp_time;
542 DTOLFP(ntp_node.ntp_time, now);
543 */
544 }
545
546
547 /*
548 * adj_systime - advance or retard the system clock exactly like the
549 * real thng.
550 */
551 int /* always succeeds */
552 adj_systime(
553 double now /* time adjustment (s) */
554 )
555 {
556 struct timeval adjtv; /* new adjustment */
557 double dtemp;
558 long ticks;
559 int isneg = 0;
560
561 /*
562 * Most Unix adjtime() implementations adjust the system clock
563 * in microsecond quanta, but some adjust in 10-ms quanta. We
564 * carefully round the adjustment to the nearest quantum, then
565 * adjust in quanta and keep the residue for later.
566 */
567 dtemp = now + sys_residual;
568 if (dtemp < 0) {
569 isneg = 1;
570 dtemp = -dtemp;
571 }
572 adjtv.tv_sec = (long)dtemp;
573 dtemp -= adjtv.tv_sec;
574 ticks = (long)(dtemp / sys_tick + .5);
575 adjtv.tv_usec = (long)(ticks * sys_tick * 1e6);
576 dtemp -= adjtv.tv_usec / 1e6;
577 sys_residual = dtemp;
578
579 /*
580 * Convert to signed seconds and microseconds for the Unix
581 * adjtime() system call. Note we purposely lose the adjtime()
582 * leftover.
583 */
584 if (isneg) {
585 adjtv.tv_sec = -adjtv.tv_sec;
586 adjtv.tv_usec = -adjtv.tv_usec;
587 sys_residual = -sys_residual;
588 }
589 simclock.adj = now;
590 /* ntp_node.adj = now; */
591 return (1);
592 }
593
594
595 /*
596 * step_systime - step the system clock. We are religious here.
597 */
598 int /* always succeeds */
599 step_systime(
600 double now /* step adjustment (s) */
601 )
602 {
603 #ifdef DEBUG
604 if (debug)
605 printf("step_systime: time %.6f adj %.6f\n",
606 simclock.local_time, now);
607 #endif
608 simclock.local_time += now;
609 return (1);
610 }
611
612 /*
613 * gauss() - returns samples from a gaussion distribution
614 */
615 double /* Gaussian sample */
616 gauss(
617 double m, /* sample mean */
618 double s /* sample standard deviation (sigma) */
619 )
620 {
621 double q1, q2;
622
623 /*
624 * Roll a sample from a Gaussian distribution with mean m and
625 * standard deviation s. For m = 0, s = 1, mean(y) = 0,
626 * std(y) = 1.
627 */
628 if (s == 0)
629 return (m);
630 while ((q1 = drand48()) == 0)
631 /* empty statement */;
632 q2 = drand48();
633 return (m + s * sqrt(-2. * log(q1)) * cos(2. * PI * q2));
634 }
635
636
637 /*
638 * poisson() - returns samples from a network delay distribution
639 */
640 double /* delay sample (s) */
641 poisson(
642 double m, /* fixed propagation delay (s) */
643 double s /* exponential parameter (mu) */
644 )
645 {
646 double q1;
647
648 /*
649 * Roll a sample from a composite distribution with propagation
650 * delay m and exponential distribution time with parameter s.
651 * For m = 0, s = 1, mean(y) = std(y) = 1.
652 */
653 if (s == 0)
654 return (m);
655 while ((q1 = drand48()) == 0)
656 /* empty statement */;
657 return (m - s * log(q1 * s));
658 }
659
660 #endif
661