1285612Sdelphij/* ntpdsim.c
2285612Sdelphij *
3285612Sdelphij * The source code for the ntp discrete event simulator.
4285612Sdelphij *
5285612Sdelphij * Written By:	Sachin Kamboj
6285612Sdelphij *		University of Delaware
7285612Sdelphij *		Newark, DE 19711
8285612Sdelphij * Copyright (c) 2006
9285612Sdelphij * (Some code shamelessly based on the original NTP discrete event simulator)
10132451Sroberto */
11285612Sdelphij
12285612Sdelphij#include <config.h>
13285612Sdelphij#ifdef SIM
14132451Sroberto#include "ntpd.h"
15285612Sdelphij#include "ntp_config.h"
16132451Sroberto
17285612Sdelphij/* forward prototypes */
18285612Sdelphijint determine_event_ordering(const Event *e1, const Event *e2);
19285612Sdelphijint determine_recv_buf_ordering(const struct recvbuf *b1,
20285612Sdelphij				const struct recvbuf *b2);
21285612Sdelphijvoid create_server_associations(void);
22285612Sdelphijvoid init_sim_io(void);
23132451Sroberto
24285612Sdelphij/* Global Variable Definitions */
25285612Sdelphijsim_info simulation;		/* Simulation Control Variables */
26285612Sdelphijlocal_clock_info simclock;	/* Local Clock Variables */
27285612Sdelphijqueue *event_queue;		/* Event Queue */
28285612Sdelphijqueue *recv_queue;		/* Receive Queue */
29285612Sdelphijstatic double sys_residual = 0;	/* adjustment residue (s) */
30285612Sdelphij
31285612Sdelphijvoid (*event_ptr[]) (Event *) = {
32285612Sdelphij    sim_event_beep, sim_update_clocks, sim_event_timer, sim_event_recv_packet
33285612Sdelphij};			/* Function pointer to the events */
34285612Sdelphij
35285612Sdelphij
36132451Sroberto/*
37285612Sdelphij * Define a function to compare two events to determine which one occurs
38285612Sdelphij * first.
39132451Sroberto */
40285612Sdelphijint
41285612Sdelphijdetermine_event_ordering(
42285612Sdelphij	const Event *e1,
43285612Sdelphij	const Event *e2
44285612Sdelphij	)
45285612Sdelphij{
46285612Sdelphij	return (e1->time - e2->time);
47285612Sdelphij}
48132451Sroberto
49132451Sroberto
50132451Sroberto/*
51285612Sdelphij * Define a function to compare two received packets to determine which
52285612Sdelphij * one is received first.
53132451Sroberto */
54132451Srobertoint
55285612Sdelphijdetermine_recv_buf_ordering(
56285612Sdelphij	const struct recvbuf *b1,
57285612Sdelphij	const struct recvbuf *b2
58132451Sroberto	)
59132451Sroberto{
60285612Sdelphij	double recv_time1;
61285612Sdelphij	double recv_time2;
62132451Sroberto
63285612Sdelphij	/* Simply convert the time received to double and subtract */
64285612Sdelphij	LFPTOD(&b1->recv_time, recv_time1);
65285612Sdelphij	LFPTOD(&b2->recv_time, recv_time2);
66132451Sroberto
67285612Sdelphij	return (int)(recv_time1 - recv_time2);
68285612Sdelphij}
69132451Sroberto
70132451Sroberto
71285612Sdelphij/* Define a function to create the server associations */
72285612Sdelphijvoid create_server_associations(void)
73285612Sdelphij{
74285612Sdelphij	int i;
75182007Sroberto
76285612Sdelphij	for (i = 0; i < simulation.num_of_servers; ++i) {
77285612Sdelphij		printf("%s\n", stoa(simulation.servers[i].addr));
78285612Sdelphij		if (peer_config(simulation.servers[i].addr,
79285612Sdelphij				NULL,
80285612Sdelphij				loopback_interface,
81285612Sdelphij				MODE_CLIENT,
82330141Sdelphij				-1,
83285612Sdelphij				NTP_VERSION,
84285612Sdelphij				NTP_MINDPOLL,
85285612Sdelphij				NTP_MAXDPOLL,
86285612Sdelphij				0, /* peerflags */
87285612Sdelphij				0, /* ttl */
88285612Sdelphij				0, /* peerkey */
89285612Sdelphij				NULL /* group ident */) == 0) {
90285612Sdelphij			fprintf(stderr,
91285612Sdelphij				"ERROR!! Could not create association for: %s\n",
92285612Sdelphij				stoa(simulation.servers[i].addr));
93285612Sdelphij		}
94182007Sroberto	}
95285612Sdelphij}
96182007Sroberto
97285612Sdelphij
98285612Sdelphij/* Main Simulator Code */
99285612Sdelphij
100285612Sdelphijint
101285612Sdelphijntpsim(
102285612Sdelphij	int	argc,
103285612Sdelphij	char *	argv[]
104285612Sdelphij	)
105285612Sdelphij{
106285612Sdelphij	Event *		curr_event;
107285612Sdelphij	struct timeval	seed;
108285612Sdelphij
109285612Sdelphij	/* Initialize the local Clock */
110285612Sdelphij	simclock.local_time = 0;
111285612Sdelphij	simclock.adj = 0;
112285612Sdelphij	simclock.slew = 500e-6;
113285612Sdelphij
114285612Sdelphij	/* Initialize the simulation */
115285612Sdelphij	simulation.num_of_servers = 0;
116285612Sdelphij	simulation.beep_delay = BEEP_DLY;
117285612Sdelphij	simulation.sim_time = 0;
118285612Sdelphij	simulation.end_time = SIM_TIME;
119285612Sdelphij
120285612Sdelphij	/* Initialize ntp modules */
121285612Sdelphij	initializing = TRUE;
122285612Sdelphij	msyslog_term = TRUE;
123285612Sdelphij	init_sim_io();
124285612Sdelphij	init_auth();
125285612Sdelphij	init_util();
126285612Sdelphij	init_restrict();
127285612Sdelphij	init_mon();
128285612Sdelphij	init_timer();
129285612Sdelphij	init_lib();
130285612Sdelphij	init_request();
131285612Sdelphij	init_control();
132285612Sdelphij	init_peer();
133285612Sdelphij	init_proto();
134285612Sdelphij	init_loopfilter();
135285612Sdelphij	mon_start(MON_OFF);
136285612Sdelphij
137285612Sdelphij	/* Call getconfig to parse the configuration file */
138132451Sroberto	getconfig(argc, argv);
139285612Sdelphij	loop_config(LOOP_DRIFTINIT, 0);
140285612Sdelphij	initializing = FALSE;
141182007Sroberto
142132451Sroberto	/*
143132451Sroberto	 * Watch out here, we want the real time, not the silly stuff.
144132451Sroberto	 */
145132451Sroberto	gettimeofday(&seed, NULL);
146182007Sroberto	ntp_srandom(seed.tv_usec);
147132451Sroberto
148285612Sdelphij	/* Initialize the event queue */
149285612Sdelphij	event_queue = create_priority_queue((q_order_func)
150285612Sdelphij	    determine_event_ordering);
151132451Sroberto
152285612Sdelphij	/* Initialize the receive queue */
153285612Sdelphij	recv_queue = create_priority_queue((q_order_func)
154285612Sdelphij	    determine_recv_buf_ordering);
155285612Sdelphij
156285612Sdelphij	/* Push a beep and a timer on the event queue */
157285612Sdelphij	enqueue(event_queue, event(0, BEEP));
158285612Sdelphij	enqueue(event_queue, event(simulation.sim_time + 1.0, TIMER));
159285612Sdelphij
160285612Sdelphij	/*
161132451Sroberto	 * Pop the queue until nothing is left or time is exceeded
162132451Sroberto	 */
163285612Sdelphij	/* maxtime = simulation.sim_time + simulation.end_time;*/
164285612Sdelphij	while (simulation.sim_time <= simulation.end_time &&
165285612Sdelphij	   (!empty(event_queue))) {
166285612Sdelphij		curr_event = dequeue(event_queue);
167285612Sdelphij		/* Update all the clocks to the time on the event */
168285612Sdelphij		sim_update_clocks(curr_event);
169285612Sdelphij
170285612Sdelphij		/* Execute the function associated with the event */
171285612Sdelphij		(*event_ptr[curr_event->function])(curr_event);
172285612Sdelphij		free_node(curr_event);
173132451Sroberto	}
174285612Sdelphij	printf("sys_received: %lu\n", sys_received);
175285612Sdelphij	printf("sys_badlength: %lu\n", sys_badlength);
176285612Sdelphij	printf("sys_declined: %lu\n", sys_declined);
177285612Sdelphij	printf("sys_restricted: %lu\n", sys_restricted);
178285612Sdelphij	printf("sys_newversion: %lu\n", sys_newversion);
179285612Sdelphij	printf("sys_oldversion: %lu\n", sys_oldversion);
180285612Sdelphij	printf("sys_limitrejected: %lu\n", sys_limitrejected);
181285612Sdelphij	printf("sys_badauth: %lu\n", sys_badauth);
182285612Sdelphij
183132451Sroberto	return (0);
184132451Sroberto}
185132451Sroberto
186132451Sroberto
187285612Sdelphijvoid
188285612Sdelphijinit_sim_io(void)
189132451Sroberto{
190285612Sdelphij	loopback_interface = emalloc_zero(sizeof(*loopback_interface));
191285612Sdelphij	ep_list = loopback_interface;
192285612Sdelphij	strlcpy(loopback_interface->name, "IPv4loop",
193285612Sdelphij		sizeof(loopback_interface->name));
194285612Sdelphij	loopback_interface->flags = INT_UP | INT_LOOPBACK;
195285612Sdelphij	loopback_interface->fd = -1;
196285612Sdelphij	loopback_interface->bfd = -1;
197285612Sdelphij	loopback_interface->ifnum = 1;
198285612Sdelphij	loopback_interface->family = AF_INET;
199285612Sdelphij	AF(&loopback_interface->sin) = AF_INET;
200285612Sdelphij	SET_ADDR4(&loopback_interface->sin, LOOPBACKADR);
201285612Sdelphij	SET_PORT(&loopback_interface->sin, NTP_PORT);
202285612Sdelphij	AF(&loopback_interface->mask) = AF_INET;
203285612Sdelphij	SET_ADDR4(&loopback_interface->mask, LOOPNETMASK);
204285612Sdelphij}
205132451Sroberto
206285612Sdelphij
207285612Sdelphij/* Define a function to create an return an Event  */
208285612Sdelphij
209285612SdelphijEvent *event(double t, funcTkn f)
210285612Sdelphij{
211285612Sdelphij    Event *e;
212285612Sdelphij
213285612Sdelphij    if ((e = get_node(sizeof(*e))) == NULL)
214285612Sdelphij	abortsim("get_node failed in event");
215285612Sdelphij    e->time = t;
216285612Sdelphij    e->function = f;
217285612Sdelphij    return (e);
218132451Sroberto}
219132451Sroberto
220285612Sdelphij/* NTP SIMULATION FUNCTIONS */
221285612Sdelphij
222285612Sdelphij/* Define a function for processing a timer interrupt.
223285612Sdelphij * On every timer interrupt, call the NTP timer to send packets and process
224285612Sdelphij * the clock and then call the receive function to receive packets.
225132451Sroberto */
226285612Sdelphijvoid sim_event_timer(Event *e)
227132451Sroberto{
228285612Sdelphij    struct recvbuf *rbuf;
229132451Sroberto
230285612Sdelphij    /* Call the NTP timer.
231285612Sdelphij     * This will be responsible for actually "sending the packets."
232285612Sdelphij     * Since this is a simulation, the packets sent over the network
233285612Sdelphij     * will be processed by the simulate_server routine below.
234285612Sdelphij     */
235285612Sdelphij    timer();
236285612Sdelphij
237285612Sdelphij    /* Process received buffers */
238285612Sdelphij    while (!empty(recv_queue)) {
239285612Sdelphij	rbuf = (struct recvbuf *)dequeue(recv_queue);
240285612Sdelphij	(*rbuf->receiver)(rbuf);
241285612Sdelphij	free_node(rbuf);
242285612Sdelphij    }
243285612Sdelphij
244285612Sdelphij    /* Arm the next timer interrupt. */
245285612Sdelphij    enqueue(event_queue,
246285612Sdelphij	    event(simulation.sim_time + (1 << EVENT_TIMEOUT), TIMER));
247132451Sroberto}
248132451Sroberto
249132451Sroberto
250285612Sdelphij
251285612Sdelphij/* Define a function to simulate a server.
252285612Sdelphij * This function processes the sent packet according to the server script,
253285612Sdelphij * creates a reply packet and pushes the reply packet onto the event queue
254132451Sroberto */
255285612Sdelphijint simulate_server(
256285612Sdelphij    sockaddr_u *serv_addr,	/* Address of the server */
257285612Sdelphij    endpt *	inter,		/* Interface on which the reply should
258285612Sdelphij				   be inserted */
259285612Sdelphij    struct pkt *rpkt		/* Packet sent to the server that
260285612Sdelphij				   needs to be processed. */
261285612Sdelphij    )
262132451Sroberto{
263285612Sdelphij    struct pkt xpkt;		/* Packet to be transmitted back
264285612Sdelphij				   to the client */
265285612Sdelphij    struct recvbuf rbuf;	/* Buffer for the received packet */
266285612Sdelphij    Event *e;			/* Packet receive event */
267285612Sdelphij    server_info *server;	/* Pointer to the server being simulated */
268285612Sdelphij    script_info *curr_script;	/* Current script being processed */
269285612Sdelphij    int i;
270285612Sdelphij    double d1, d2, d3;		/* Delays while the packet is enroute */
271285612Sdelphij    double t1, t2, t3, t4;	/* The four timestamps in the packet */
272285612Sdelphij    l_fp lfp_host;		/* host-order l_fp */
273132451Sroberto
274285612Sdelphij    ZERO(xpkt);
275285612Sdelphij    ZERO(rbuf);
276285612Sdelphij
277285612Sdelphij    /* Search for the server with the desired address */
278285612Sdelphij    server = NULL;
279285612Sdelphij    for (i = 0; i < simulation.num_of_servers; ++i) {
280285612Sdelphij	if (memcmp(simulation.servers[i].addr, serv_addr,
281285612Sdelphij		   sizeof(*serv_addr)) == 0) {
282285612Sdelphij	    server = &simulation.servers[i];
283285612Sdelphij	    break;
284285612Sdelphij	}
285285612Sdelphij    }
286285612Sdelphij
287285612Sdelphij    fprintf(stderr, "Received packet from %s on %s\n",
288285612Sdelphij	    stoa(serv_addr), latoa(inter));
289285612Sdelphij    if (server == NULL)
290285612Sdelphij	abortsim("Server with specified address not found!!!");
291285612Sdelphij
292285612Sdelphij    /* Get the current script for the server */
293285612Sdelphij    curr_script = server->curr_script;
294285612Sdelphij
295285612Sdelphij    /* Create a server reply packet.
296285612Sdelphij     * Masquerade the reply as a stratum-1 server with a GPS clock
297285612Sdelphij     */
298285612Sdelphij    xpkt.li_vn_mode = PKT_LI_VN_MODE(LEAP_NOWARNING, NTP_VERSION,
299285612Sdelphij				     MODE_SERVER);
300285612Sdelphij    xpkt.stratum = STRATUM_TO_PKT(((u_char)1));
301285612Sdelphij    memcpy(&xpkt.refid, "GPS", 4);
302285612Sdelphij    xpkt.ppoll = rpkt->ppoll;
303285612Sdelphij    xpkt.precision = rpkt->precision;
304285612Sdelphij    xpkt.rootdelay = 0;
305285612Sdelphij    xpkt.rootdisp = 0;
306285612Sdelphij
307285612Sdelphij    /* TIMESTAMP CALCULATIONS
308285612Sdelphij	    t1				 t4
309285612Sdelphij	     \				/
310285612Sdelphij	  d1  \			       / d3
311285612Sdelphij	       \		      /
312285612Sdelphij	       t2 ----------------- t3
313285612Sdelphij			 d2
314285612Sdelphij    */
315285612Sdelphij    /* Compute the delays */
316285612Sdelphij    d1 = poisson(curr_script->prop_delay, curr_script->jitter);
317285612Sdelphij    d2 = poisson(curr_script->proc_delay, 0);
318285612Sdelphij    d3 = poisson(curr_script->prop_delay, curr_script->jitter);
319285612Sdelphij
320285612Sdelphij    /* Note: In the transmitted packet:
321285612Sdelphij     * 1. t1 and t4 are times in the client according to the local clock.
322285612Sdelphij     * 2. t2 and t3 are server times according to the simulated server.
323285612Sdelphij     * Compute t1, t2, t3 and t4
324285612Sdelphij     * Note: This function is called at time t1.
325285612Sdelphij     */
326285612Sdelphij
327285612Sdelphij    NTOHL_FP(&rpkt->xmt, &lfp_host);
328285612Sdelphij    LFPTOD(&lfp_host, t1);
329285612Sdelphij    t2 = server->server_time + d1;
330285612Sdelphij    t3 = server->server_time + d1 + d2;
331285612Sdelphij    t4 = t1 + d1 + d2 + d3;
332285612Sdelphij
333285612Sdelphij    /* Save the timestamps */
334285612Sdelphij    xpkt.org = rpkt->xmt;
335285612Sdelphij    DTOLFP(t2, &lfp_host);
336285612Sdelphij    HTONL_FP(&lfp_host, &xpkt.rec);
337285612Sdelphij    DTOLFP(t3, &lfp_host);
338285612Sdelphij    HTONL_FP(&lfp_host, &xpkt.xmt);
339285612Sdelphij    xpkt.reftime = xpkt.xmt;
340285612Sdelphij
341285612Sdelphij    /*
342285612Sdelphij     * Ok, we are done with the packet. Now initialize the receive
343285612Sdelphij     * buffer for the packet.
344285612Sdelphij     */
345285612Sdelphij    rbuf.used = 1;
346285612Sdelphij    rbuf.receiver = &receive;   /* callback to process the packet */
347285612Sdelphij    rbuf.recv_length = LEN_PKT_NOMAC;
348285612Sdelphij    rbuf.recv_pkt = xpkt;
349285612Sdelphij    rbuf.dstadr = inter;
350285612Sdelphij    rbuf.fd = inter->fd;
351285612Sdelphij    memcpy(&rbuf.srcadr, serv_addr, sizeof(rbuf.srcadr));
352285612Sdelphij    memcpy(&rbuf.recv_srcadr, serv_addr, sizeof(rbuf.recv_srcadr));
353285612Sdelphij
354285612Sdelphij    /*
355285612Sdelphij     * Create a packet event and insert it onto the event_queue at the
356285612Sdelphij     * arrival time (t4) of the packet at the client
357285612Sdelphij     */
358285612Sdelphij    e = event(t4, PACKET);
359285612Sdelphij    e->rcv_buf = rbuf;
360285612Sdelphij    enqueue(event_queue, e);
361285612Sdelphij
362285612Sdelphij    /*
363285612Sdelphij     * Check if the time of the script has expired. If yes, delete it.
364285612Sdelphij     */
365285612Sdelphij    if (curr_script->duration > simulation.sim_time &&
366285612Sdelphij	NULL == HEAD_PFIFO(server->script)) {
367285612Sdelphij	printf("Hello\n");
368285612Sdelphij	/*
369285612Sdelphij	 * For some reason freeing up the curr_script memory kills the
370285612Sdelphij	 * simulation. Further debugging is needed to determine why.
371285612Sdelphij	 * free(curr_script);
372285612Sdelphij	 */
373285612Sdelphij	UNLINK_FIFO(curr_script, *server->script, link);
374285612Sdelphij    }
375285612Sdelphij
376285612Sdelphij    return (0);
377132451Sroberto}
378132451Sroberto
379132451Sroberto
380285612Sdelphij/* Define a function to update all the clocks
381285612Sdelphij * Most of the code is modified from the systime.c file by Prof. Mills
382132451Sroberto */
383285612Sdelphij
384285612Sdelphijvoid sim_update_clocks(Event *e)
385132451Sroberto{
386285612Sdelphij    double time_gap;
387285612Sdelphij    double adj;
388285612Sdelphij    int i;
389132451Sroberto
390285612Sdelphij    /* Compute the time between the last update event and this update */
391285612Sdelphij    time_gap = e->time - simulation.sim_time;
392285612Sdelphij
393285612Sdelphij    if (time_gap < 0)
394285612Sdelphij	    printf("WARNING: e->time %.6g comes before sim_time %.6g (gap %+.6g)\n",
395285612Sdelphij		   e->time, simulation.sim_time, time_gap);
396285612Sdelphij
397285612Sdelphij    /* Advance the client clock */
398285612Sdelphij    if (e->time + time_gap < simclock.local_time)
399285612Sdelphij	    printf("WARNING: e->time + gap %.6g comes before local_time %.6g\n",
400285612Sdelphij		   e->time + time_gap, simclock.local_time);
401285612Sdelphij    simclock.local_time = e->time + time_gap;
402285612Sdelphij
403285612Sdelphij    /* Advance the simulation time */
404285612Sdelphij    simulation.sim_time = e->time;
405285612Sdelphij
406285612Sdelphij    /* Advance the server clocks adjusted for systematic and random frequency
407285612Sdelphij     * errors. The random error is a random walk computed as the
408285612Sdelphij     * integral of samples from a Gaussian distribution.
409285612Sdelphij     */
410285612Sdelphij    for (i = 0; i < simulation.num_of_servers; ++i) {
411285612Sdelphij	simulation.servers[i].curr_script->freq_offset +=
412285612Sdelphij	    gauss(0, time_gap * simulation.servers[i].curr_script->wander);
413285612Sdelphij
414285612Sdelphij	simulation.servers[i].server_time += time_gap *
415285612Sdelphij	    (1 + simulation.servers[i].curr_script->freq_offset);
416285612Sdelphij    }
417285612Sdelphij
418285612Sdelphij    /* Perform the adjtime() function. If the adjustment completed
419285612Sdelphij     * in the previous interval, amortize the entire amount; if not,
420285612Sdelphij     * carry the leftover to the next interval.
421285612Sdelphij     */
422285612Sdelphij
423285612Sdelphij    adj = time_gap * simclock.slew;
424285612Sdelphij    if (adj < fabs(simclock.adj)) {
425285612Sdelphij	if (simclock.adj < 0) {
426285612Sdelphij	    simclock.adj += adj;
427285612Sdelphij	    simclock.local_time -= adj;
428285612Sdelphij	} else {
429285612Sdelphij	    simclock.adj -= adj;
430285612Sdelphij	    simclock.local_time += adj;
431285612Sdelphij	}
432285612Sdelphij    } else {
433285612Sdelphij	simclock.local_time += simclock.adj;
434285612Sdelphij	simclock.adj = 0;
435285612Sdelphij    }
436132451Sroberto}
437132451Sroberto
438132451Sroberto
439285612Sdelphij/* Define a function that processes a receive packet event.
440285612Sdelphij * This function simply inserts the packet received onto the receive queue
441285612Sdelphij */
442285612Sdelphij
443285612Sdelphijvoid sim_event_recv_packet(Event *e)
444132451Sroberto{
445285612Sdelphij    struct recvbuf *rbuf;
446285612Sdelphij
447285612Sdelphij    /* Allocate a receive buffer and copy the packet to it */
448285612Sdelphij    if ((rbuf = get_node(sizeof(*rbuf))) == NULL)
449285612Sdelphij	abortsim("get_node failed in sim_event_recv_packet");
450285612Sdelphij    memcpy(rbuf, &e->rcv_buf, sizeof(*rbuf));
451285612Sdelphij
452285612Sdelphij    /* Store the local time in the received packet */
453285612Sdelphij    DTOLFP(simclock.local_time, &rbuf->recv_time);
454285612Sdelphij
455285612Sdelphij    /* Insert the packet received onto the receive queue */
456285612Sdelphij    enqueue(recv_queue, rbuf);
457132451Sroberto}
458132451Sroberto
459132451Sroberto
460285612Sdelphij
461285612Sdelphij/* Define a function to output simulation statistics on a beep event
462132451Sroberto */
463285612Sdelphij
464285612Sdelphij/*** TODO: Need to decide on how to output for multiple servers ***/
465285612Sdelphijvoid sim_event_beep(Event *e)
466132451Sroberto{
467285612Sdelphij#if 0
468285612Sdelphij    static int first_time = 1;
469285612Sdelphij    char *dash = "-----------------";
470285612Sdelphij#endif
471132451Sroberto
472285612Sdelphij    fprintf(stderr, "BEEP!!!\n");
473285612Sdelphij    enqueue(event_queue, event(e->time + simulation.beep_delay, BEEP));
474285612Sdelphij#if 0
475285612Sdelphij    if(simulation.beep_delay > 0) {
476285612Sdelphij	if (first_time) {
477285612Sdelphij	    printf("\t%4c    T    %4c\t%4c  T+ERR  %3c\t%5cT+ERR+NTP\n",
478285612Sdelphij	           ' ', ' ', ' ', ' ',' ');
479285612Sdelphij	    printf("\t%s\t%s\t%s\n", dash, dash, dash);
480285612Sdelphij	    first_time = 0;
481132451Sroberto
482285612Sdelphij	    printf("\t%16.6f\t%16.6f\t%16.6f\n",
483285612Sdelphij	           n->time, n->clk_time, n->ntp_time);
484285612Sdelphij	    return;
485132451Sroberto	}
486285612Sdelphij	printf("\t%16.6f\t%16.6f\t%16.6f\n",
487285612Sdelphij	       simclock.local_time,
488285612Sdelphij	       n->time, n->clk_time, n->ntp_time);
489285612Sdelphij#endif
490132451Sroberto
491132451Sroberto}
492132451Sroberto
493132451Sroberto
494285612Sdelphij/* Define a function to abort the simulation on an error and spit out an
495285612Sdelphij * error message
496132451Sroberto */
497285612Sdelphij
498285612Sdelphijvoid abortsim(char *errmsg)
499132451Sroberto{
500285612Sdelphij    perror(errmsg);
501285612Sdelphij    exit(1);
502285612Sdelphij}
503132451Sroberto
504132451Sroberto
505132451Sroberto
506285612Sdelphij/* CODE ORIGINALLY IN libntp/systime.c
507285612Sdelphij * -----------------------------------
508285612Sdelphij * This code was a part of the original NTP simulator and originally
509285612Sdelphij * had its home in the libntp/systime.c file.
510285612Sdelphij *
511285612Sdelphij * It has been shamelessly moved to here and has been modified for the
512285612Sdelphij * purposes of the current simulator.
513285612Sdelphij */
514132451Sroberto
515132451Sroberto
516132451Sroberto/*
517285612Sdelphij * get_systime - return the system time in NTP timestamp format
518132451Sroberto */
519132451Srobertovoid
520285612Sdelphijget_systime(
521285612Sdelphij    l_fp *now		/* current system time in l_fp */        )
522132451Sroberto{
523285612Sdelphij    /*
524285612Sdelphij     * To fool the code that determines the local clock precision,
525285612Sdelphij     * we advance the clock a minimum of 200 nanoseconds on every
526285612Sdelphij     * clock read. This is appropriate for a typical modern machine
527285612Sdelphij     * with nanosecond clocks. Note we make no attempt here to
528285612Sdelphij     * simulate reading error, since the error is so small. This may
529285612Sdelphij     * change when the need comes to implement picosecond clocks.
530285612Sdelphij     */
531285612Sdelphij    if (simclock.local_time == simclock.last_read_time)
532285612Sdelphij        simclock.local_time += 200e-9;
533132451Sroberto
534285612Sdelphij    simclock.last_read_time = simclock.local_time;
535285612Sdelphij    DTOLFP(simclock.local_time, now);
536285612Sdelphij/* OLD Code
537285612Sdelphij   if (ntp_node.ntp_time == ntp_node.last_time)
538285612Sdelphij   ntp_node.ntp_time += 200e-9;
539285612Sdelphij   ntp_node.last_time = ntp_node.ntp_time;
540285612Sdelphij   DTOLFP(ntp_node.ntp_time, now);
541285612Sdelphij*/
542132451Sroberto}
543285612Sdelphij
544285612Sdelphij
545285612Sdelphij/*
546285612Sdelphij * adj_systime - advance or retard the system clock exactly like the
547285612Sdelphij * real thng.
548285612Sdelphij */
549285612Sdelphijint				/* always succeeds */
550285612Sdelphijadj_systime(
551285612Sdelphij    double now		/* time adjustment (s) */
552285612Sdelphij    )
553285612Sdelphij{
554285612Sdelphij    struct timeval adjtv;	/* new adjustment */
555285612Sdelphij    double	dtemp;
556285612Sdelphij    long	ticks;
557285612Sdelphij    int	isneg = 0;
558132451Sroberto
559285612Sdelphij    /*
560285612Sdelphij     * Most Unix adjtime() implementations adjust the system clock
561285612Sdelphij     * in microsecond quanta, but some adjust in 10-ms quanta. We
562285612Sdelphij     * carefully round the adjustment to the nearest quantum, then
563285612Sdelphij     * adjust in quanta and keep the residue for later.
564285612Sdelphij     */
565285612Sdelphij    dtemp = now + sys_residual;
566285612Sdelphij    if (dtemp < 0) {
567285612Sdelphij	isneg = 1;
568285612Sdelphij	dtemp = -dtemp;
569285612Sdelphij    }
570285612Sdelphij    adjtv.tv_sec = (long)dtemp;
571285612Sdelphij    dtemp -= adjtv.tv_sec;
572285612Sdelphij    ticks = (long)(dtemp / sys_tick + .5);
573285612Sdelphij    adjtv.tv_usec = (long)(ticks * sys_tick * 1e6);
574285612Sdelphij    dtemp -= adjtv.tv_usec / 1e6;
575285612Sdelphij    sys_residual = dtemp;
576132451Sroberto
577285612Sdelphij    /*
578285612Sdelphij     * Convert to signed seconds and microseconds for the Unix
579285612Sdelphij     * adjtime() system call. Note we purposely lose the adjtime()
580285612Sdelphij     * leftover.
581285612Sdelphij     */
582285612Sdelphij    if (isneg) {
583285612Sdelphij	adjtv.tv_sec = -adjtv.tv_sec;
584285612Sdelphij	adjtv.tv_usec = -adjtv.tv_usec;
585285612Sdelphij	sys_residual = -sys_residual;
586285612Sdelphij    }
587285612Sdelphij    simclock.adj = now;
588285612Sdelphij/*	ntp_node.adj = now; */
589285612Sdelphij    return (1);
590285612Sdelphij}
591285612Sdelphij
592285612Sdelphij
593132451Sroberto/*
594285612Sdelphij * step_systime - step the system clock. We are religious here.
595132451Sroberto */
596285612Sdelphijint				/* always succeeds */
597285612Sdelphijstep_systime(
598285612Sdelphij    double now		/* step adjustment (s) */
599285612Sdelphij    )
600132451Sroberto{
601285612Sdelphij#ifdef DEBUG
602285612Sdelphij    if (debug)
603285612Sdelphij	printf("step_systime: time %.6f adj %.6f\n",
604285612Sdelphij	       simclock.local_time, now);
605285612Sdelphij#endif
606285612Sdelphij    simclock.local_time += now;
607285612Sdelphij    return (1);
608285612Sdelphij}
609285612Sdelphij
610285612Sdelphij/*
611285612Sdelphij * gauss() - returns samples from a gaussion distribution
612285612Sdelphij */
613285612Sdelphijdouble				/* Gaussian sample */
614285612Sdelphijgauss(
615285612Sdelphij    double m,		/* sample mean */
616285612Sdelphij    double s		/* sample standard deviation (sigma) */
617285612Sdelphij    )
618285612Sdelphij{
619285612Sdelphij    double q1, q2;
620132451Sroberto
621285612Sdelphij    /*
622285612Sdelphij     * Roll a sample from a Gaussian distribution with mean m and
623285612Sdelphij     * standard deviation s. For m = 0, s = 1, mean(y) = 0,
624285612Sdelphij     * std(y) = 1.
625285612Sdelphij     */
626285612Sdelphij    if (s == 0)
627285612Sdelphij        return (m);
628285612Sdelphij    while ((q1 = drand48()) == 0)
629285612Sdelphij	/* empty statement */;
630285612Sdelphij    q2 = drand48();
631285612Sdelphij    return (m + s * sqrt(-2. * log(q1)) * cos(2. * PI * q2));
632132451Sroberto}
633132451Sroberto
634285612Sdelphij
635132451Sroberto/*
636285612Sdelphij * poisson() - returns samples from a network delay distribution
637132451Sroberto */
638285612Sdelphijdouble				/* delay sample (s) */
639285612Sdelphijpoisson(
640285612Sdelphij    double m,		/* fixed propagation delay (s) */
641285612Sdelphij    double s		/* exponential parameter (mu) */
642285612Sdelphij    )
643132451Sroberto{
644285612Sdelphij    double q1;
645285612Sdelphij
646285612Sdelphij    /*
647285612Sdelphij     * Roll a sample from a composite distribution with propagation
648285612Sdelphij     * delay m and exponential distribution time with parameter s.
649285612Sdelphij     * For m = 0, s = 1, mean(y) = std(y) = 1.
650285612Sdelphij     */
651285612Sdelphij    if (s == 0)
652285612Sdelphij        return (m);
653285612Sdelphij    while ((q1 = drand48()) == 0)
654285612Sdelphij	/* empty statement */;
655285612Sdelphij    return (m - s * log(q1 * s));
656132451Sroberto}
657285612Sdelphij
658285612Sdelphij#endif
659