1/*	$NetBSD: ntpsim.c,v 1.1.1.1 2009/12/13 16:56:16 kardel 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#ifdef SIM
15#include "ntpd.h"
16#include "ntpsim.h"
17#include "ntp_data_structures.h"
18
19
20/* Global Variable Definitions */
21
22sim_info simulation;		/* Simulation Control Variables */
23local_clock_info simclock;	/* Local Clock Variables */
24queue *event_queue;		/* Event Queue */
25queue *recv_queue;		/* Receive Queue */
26static double sys_residual = 0;	/* adjustment residue (s) */
27
28void (*event_ptr[]) (Event *) = {
29    sim_event_beep, sim_update_clocks, sim_event_timer, sim_event_recv_packet
30};			/* Function pointer to the events */
31
32
33/* Define a function to compare two events to determine which one occurs first
34 */
35
36int determine_event_ordering(Event *e1, Event *e2);
37
38int determine_event_ordering(Event *e1, Event *e2)
39{
40    return (e1->time - e2->time);
41}
42
43/* Define a function to compare two received packets to determine which one
44 * is received first
45 */
46int determine_recv_buf_ordering(struct recvbuf *b1, struct recvbuf *b2);
47
48int determine_recv_buf_ordering(struct recvbuf *b1, struct recvbuf *b2)
49{
50    double recv_time1, recv_time2;
51
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));
56}
57
58/* Define a function to create the server associations */
59void create_server_associations()
60{
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));
76	}
77    }
78}
79
80
81/* Main Simulator Code */
82
83int ntpsim(int argc, char *argv[])
84{
85    Event *curr_event;
86    struct timeval seed;
87
88    /* Initialize the local Clock
89     */
90    simclock.local_time = 0;
91    simclock.adj = 0;
92    simclock.slew = 0;
93
94    /* Initialize the simulation
95     */
96    simulation.num_of_servers = 0;
97    simulation.beep_delay = BEEP_DLY;
98    simulation.sim_time = 0;
99    simulation.end_time = SIM_TIME;
100
101    /*
102     * Initialize ntp variables
103     */
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);
118
119    /* Call getconfig to parse the configuration file */
120    getconfig(argc, argv);
121    initializing = 0;
122    loop_config(LOOP_DRIFTCOMP, old_drift / 1e6);
123
124    /*
125     * Watch out here, we want the real time, not the silly stuff.
126     */
127    gettimeofday(&seed, NULL);
128    ntp_srandom(seed.tv_usec);
129
130
131    /* Initialize the event queue */
132    event_queue = create_priority_queue((int(*)(void *, void*))
133					determine_event_ordering);
134
135    /* Initialize the receive queue */
136    recv_queue = create_priority_queue((int(*)(void *, void*))
137				       determine_recv_buf_ordering);
138
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));
142    /*
143     * Pop the queue until nothing is left or time is exceeded
144     */
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);
151
152	/* Execute the function associated with the event */
153	event_ptr[curr_event->function](curr_event);
154	free_node(curr_event);
155    }
156    return (0);
157}
158
159
160
161/* Define a function to create an return an Event  */
162
163Event *event(double t, funcTkn f)
164{
165    Event *e;
166
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);
172}
173
174/* NTP SIMULATION FUNCTIONS */
175
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.
179 */
180void sim_event_timer(Event *e)
181{
182    struct recvbuf *rbuf;
183
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.
188     */
189    timer();
190
191    /* Process received buffers */
192    while (!empty(recv_queue)) {
193	rbuf = (struct recvbuf *)dequeue(recv_queue);
194	(rbuf->receiver)(rbuf);
195	free_node(rbuf);
196    }
197
198    /* Arm the next timer interrupt. */
199    enqueue(event_queue,
200	    event(simulation.sim_time + (1 << EVENT_TIMEOUT), TIMER));
201}
202
203
204
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
208 */
209int 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. */
215)
216{
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 */
226
227    memset(&xpkt, 0, sizeof(xpkt));
228    memset(&rbuf, 0, sizeof(rbuf));
229
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;
238	}
239    }
240
241    fprintf(stderr, "Received packet for: %s\n", stoa(serv_addr));
242    if (server == NULL)
243	abortsim("Server with specified address not found!!!");
244
245    /* Get the current script for the server */
246    curr_script = server->curr_script;
247
248    /* Create a server reply packet.
249     * Masquerade the reply as a stratum-1 server with a GPS clock
250     */
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;
259
260    /* TIMESTAMP CALCULATIONS
261	    t1				 t4
262	     \				/
263	  d1  \			       / d3
264	       \		      /
265	       t2 ----------------- t3
266			 d2
267    */
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);
272
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.
278     */
279
280    LFPTOD(&rpkt->xmt, t1);
281    t2 = server->server_time + d1;
282    t3 = server->server_time + d1 + d2;
283    t4 = t1 + d1 + d2 + d3;
284
285    /* Save the timestamps */
286    xpkt.org = rpkt->xmt;
287    DTOLFP(t2, &xpkt.rec);
288    DTOLFP(t3, &xpkt.xmt);
289    xpkt.reftime = xpkt.xmt;
290
291
292
293    /* Ok, we are done with the packet. Now initialize the receive buffer for
294     * the packet.
295     */
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;
300
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; */
307
308    /* Create a packet event and insert it onto the event_queue at the
309     * arrival time (t4) of the packet at the client
310     */
311    e = event(t4, PACKET);
312    e->rcv_buf = rbuf;
313    enqueue(event_queue, e);
314
315
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
318     */
319    if (curr_script->duration > simulation.sim_time &&
320	!empty(server->script)) {
321	printf("Hello\n");
322	/*
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);
326	 */
327	curr_script = dequeue(server->script);
328    }
329
330    return (0);
331}
332
333
334/* Define a function to update all the clocks
335 * Most of the code is modified from the systime.c file by Prof. Mills
336 */
337
338void sim_update_clocks (Event *e)
339{
340    double time_gap;
341    double adj;
342    int i;
343
344    /* Compute the time between the last update event and this update */
345    time_gap = e->time - simulation.sim_time;
346
347    /* Advance the client clock */
348    simclock.local_time = e->time + time_gap;
349
350    /* Advance the simulation time */
351    simulation.sim_time = e->time;
352
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.
356     */
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);
360
361	simulation.servers[i].server_time += time_gap *
362	    (1 + simulation.servers[i].curr_script->freq_offset);
363    }
364
365
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.
369     */
370
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;
376	}
377	else {
378	    simclock.adj -= adj;
379	    simclock.local_time += adj;
380	}
381    }
382    else {
383	simclock.local_time += simclock.adj;
384	simclock.adj = 0;
385    }
386}
387
388
389/* Define a function that processes a receive packet event.
390 * This function simply inserts the packet received onto the receive queue
391 */
392
393void sim_event_recv_packet(Event *e)
394{
395    struct recvbuf *rbuf;
396
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));
401
402    /* Store the local time in the received packet */
403    DTOLFP(simclock.local_time, &rbuf->recv_time);
404
405    /* Insert the packet received onto the receive queue */
406    enqueue(recv_queue, rbuf);
407}
408
409
410
411/* Define a function to output simulation statistics on a beep event
412 */
413
414/*** TODO: Need to decide on how to output for multiple servers ***/
415void sim_event_beep(Event *e)
416{
417#if 0
418    static int first_time = 1;
419    char *dash = "-----------------";
420#endif
421
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;
431
432	    printf("\t%16.6f\t%16.6f\t%16.6f\n",
433	           n->time, n->clk_time, n->ntp_time);
434	    return;
435	}
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
440
441}
442
443
444/* Define a function to abort the simulation on an error and spit out an
445 * error message
446 */
447
448void abortsim(char *errmsg)
449{
450    perror(errmsg);
451    exit(1);
452}
453
454
455
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.
460 *
461 * It has been shamelessly moved to here and has been modified for the
462 * purposes of the current simulator.
463 */
464
465
466/*
467 * get_systime - return the system time in NTP timestamp format
468 */
469void
470get_systime(
471    l_fp *now		/* current system time in l_fp */        )
472{
473    /*
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.
480     */
481    if (simclock.local_time == simclock.last_read_time)
482        simclock.local_time += 200e-9;
483
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);
491*/
492}
493
494
495/*
496 * adj_systime - advance or retard the system clock exactly like the
497 * real thng.
498 */
499int				/* always succeeds */
500adj_systime(
501    double now		/* time adjustment (s) */
502    )
503{
504    struct timeval adjtv;	/* new adjustment */
505    double	dtemp;
506    long	ticks;
507    int	isneg = 0;
508
509    /*
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.
514     */
515    dtemp = now + sys_residual;
516    if (dtemp < 0) {
517	isneg = 1;
518	dtemp = -dtemp;
519    }
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;
526
527    /*
528     * Convert to signed seconds and microseconds for the Unix
529     * adjtime() system call. Note we purposely lose the adjtime()
530     * leftover.
531     */
532    if (isneg) {
533	adjtv.tv_sec = -adjtv.tv_sec;
534	adjtv.tv_usec = -adjtv.tv_usec;
535	sys_residual = -sys_residual;
536    }
537    simclock.adj = now;
538/*	ntp_node.adj = now; */
539    return (1);
540}
541
542
543/*
544 * step_systime - step the system clock. We are religious here.
545 */
546int				/* always succeeds */
547step_systime(
548    double now		/* step adjustment (s) */
549    )
550{
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);
558}
559
560/*
561 * gauss() - returns samples from a gaussion distribution
562 */
563double				/* Gaussian sample */
564gauss(
565    double m,		/* sample mean */
566    double s		/* sample standard deviation (sigma) */
567    )
568{
569    double q1, q2;
570
571    /*
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.
575     */
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));
581}
582
583
584/*
585 * poisson() - returns samples from a network delay distribution
586 */
587double				/* delay sample (s) */
588poisson(
589    double m,		/* fixed propagation delay (s) */
590    double s		/* exponential parameter (mu) */
591    )
592{
593    double q1;
594
595    /*
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.
599     */
600    if (s == 0)
601        return (m);
602    while ((q1 = drand48()) == 0);
603    return (m - s * log(q1 * s));
604}
605
606#endif
607