xref: /netbsd-src/external/bsd/ntp/dist/ntpd/ntpsim.c (revision cdfa2a7ef92791ba9db70a584a1d904730e6fb46)
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