1290001Sglebius/* ntpdsim.c
2290001Sglebius *
3290001Sglebius * The source code for the ntp discrete event simulator.
4290001Sglebius *
5290001Sglebius * Written By:	Sachin Kamboj
6290001Sglebius *		University of Delaware
7290001Sglebius *		Newark, DE 19711
8290001Sglebius * Copyright (c) 2006
9290001Sglebius * (Some code shamelessly based on the original NTP discrete event simulator)
10132451Sroberto */
11290001Sglebius
12290001Sglebius#include <config.h>
13290001Sglebius#ifdef SIM
14132451Sroberto#include "ntpd.h"
15290001Sglebius#include "ntp_config.h"
16132451Sroberto
17290001Sglebius/* forward prototypes */
18290001Sglebiusint determine_event_ordering(const Event *e1, const Event *e2);
19290001Sglebiusint determine_recv_buf_ordering(const struct recvbuf *b1,
20290001Sglebius				const struct recvbuf *b2);
21290001Sglebiusvoid create_server_associations(void);
22290001Sglebiusvoid init_sim_io(void);
23132451Sroberto
24290001Sglebius/* Global Variable Definitions */
25290001Sglebiussim_info simulation;		/* Simulation Control Variables */
26290001Sglebiuslocal_clock_info simclock;	/* Local Clock Variables */
27290001Sglebiusqueue *event_queue;		/* Event Queue */
28290001Sglebiusqueue *recv_queue;		/* Receive Queue */
29290001Sglebiusstatic double sys_residual = 0;	/* adjustment residue (s) */
30290001Sglebius
31290001Sglebiusvoid (*event_ptr[]) (Event *) = {
32290001Sglebius    sim_event_beep, sim_update_clocks, sim_event_timer, sim_event_recv_packet
33290001Sglebius};			/* Function pointer to the events */
34290001Sglebius
35290001Sglebius
36132451Sroberto/*
37290001Sglebius * Define a function to compare two events to determine which one occurs
38290001Sglebius * first.
39132451Sroberto */
40290001Sglebiusint
41290001Sglebiusdetermine_event_ordering(
42290001Sglebius	const Event *e1,
43290001Sglebius	const Event *e2
44290001Sglebius	)
45290001Sglebius{
46290001Sglebius	return (e1->time - e2->time);
47290001Sglebius}
48132451Sroberto
49132451Sroberto
50132451Sroberto/*
51290001Sglebius * Define a function to compare two received packets to determine which
52290001Sglebius * one is received first.
53132451Sroberto */
54132451Srobertoint
55290001Sglebiusdetermine_recv_buf_ordering(
56290001Sglebius	const struct recvbuf *b1,
57290001Sglebius	const struct recvbuf *b2
58132451Sroberto	)
59132451Sroberto{
60290001Sglebius	double recv_time1;
61290001Sglebius	double recv_time2;
62132451Sroberto
63290001Sglebius	/* Simply convert the time received to double and subtract */
64290001Sglebius	LFPTOD(&b1->recv_time, recv_time1);
65290001Sglebius	LFPTOD(&b2->recv_time, recv_time2);
66132451Sroberto
67290001Sglebius	return (int)(recv_time1 - recv_time2);
68290001Sglebius}
69132451Sroberto
70132451Sroberto
71290001Sglebius/* Define a function to create the server associations */
72290001Sglebiusvoid create_server_associations(void)
73290001Sglebius{
74290001Sglebius	int i;
75182007Sroberto
76290001Sglebius	for (i = 0; i < simulation.num_of_servers; ++i) {
77290001Sglebius		printf("%s\n", stoa(simulation.servers[i].addr));
78290001Sglebius		if (peer_config(simulation.servers[i].addr,
79290001Sglebius				NULL,
80290001Sglebius				loopback_interface,
81290001Sglebius				MODE_CLIENT,
82290001Sglebius				NTP_VERSION,
83290001Sglebius				NTP_MINDPOLL,
84290001Sglebius				NTP_MAXDPOLL,
85290001Sglebius				0, /* peerflags */
86290001Sglebius				0, /* ttl */
87290001Sglebius				0, /* peerkey */
88290001Sglebius				NULL /* group ident */) == 0) {
89290001Sglebius			fprintf(stderr,
90290001Sglebius				"ERROR!! Could not create association for: %s\n",
91290001Sglebius				stoa(simulation.servers[i].addr));
92290001Sglebius		}
93182007Sroberto	}
94290001Sglebius}
95182007Sroberto
96290001Sglebius
97290001Sglebius/* Main Simulator Code */
98290001Sglebius
99290001Sglebiusint
100290001Sglebiusntpsim(
101290001Sglebius	int	argc,
102290001Sglebius	char *	argv[]
103290001Sglebius	)
104290001Sglebius{
105290001Sglebius	Event *		curr_event;
106290001Sglebius	struct timeval	seed;
107290001Sglebius
108290001Sglebius	/* Initialize the local Clock */
109290001Sglebius	simclock.local_time = 0;
110290001Sglebius	simclock.adj = 0;
111290001Sglebius	simclock.slew = 500e-6;
112290001Sglebius
113290001Sglebius	/* Initialize the simulation */
114290001Sglebius	simulation.num_of_servers = 0;
115290001Sglebius	simulation.beep_delay = BEEP_DLY;
116290001Sglebius	simulation.sim_time = 0;
117290001Sglebius	simulation.end_time = SIM_TIME;
118290001Sglebius
119290001Sglebius	/* Initialize ntp modules */
120290001Sglebius	initializing = TRUE;
121290001Sglebius	msyslog_term = TRUE;
122290001Sglebius	init_sim_io();
123290001Sglebius	init_auth();
124290001Sglebius	init_util();
125290001Sglebius	init_restrict();
126290001Sglebius	init_mon();
127290001Sglebius	init_timer();
128290001Sglebius	init_lib();
129290001Sglebius	init_request();
130290001Sglebius	init_control();
131290001Sglebius	init_peer();
132290001Sglebius	init_proto();
133290001Sglebius	init_loopfilter();
134290001Sglebius	mon_start(MON_OFF);
135290001Sglebius
136290001Sglebius	/* Call getconfig to parse the configuration file */
137132451Sroberto	getconfig(argc, argv);
138290001Sglebius	loop_config(LOOP_DRIFTINIT, 0);
139290001Sglebius	initializing = FALSE;
140182007Sroberto
141132451Sroberto	/*
142132451Sroberto	 * Watch out here, we want the real time, not the silly stuff.
143132451Sroberto	 */
144132451Sroberto	gettimeofday(&seed, NULL);
145182007Sroberto	ntp_srandom(seed.tv_usec);
146132451Sroberto
147290001Sglebius	/* Initialize the event queue */
148290001Sglebius	event_queue = create_priority_queue((q_order_func)
149290001Sglebius	    determine_event_ordering);
150132451Sroberto
151290001Sglebius	/* Initialize the receive queue */
152290001Sglebius	recv_queue = create_priority_queue((q_order_func)
153290001Sglebius	    determine_recv_buf_ordering);
154290001Sglebius
155290001Sglebius	/* Push a beep and a timer on the event queue */
156290001Sglebius	enqueue(event_queue, event(0, BEEP));
157290001Sglebius	enqueue(event_queue, event(simulation.sim_time + 1.0, TIMER));
158290001Sglebius
159290001Sglebius	/*
160132451Sroberto	 * Pop the queue until nothing is left or time is exceeded
161132451Sroberto	 */
162290001Sglebius	/* maxtime = simulation.sim_time + simulation.end_time;*/
163290001Sglebius	while (simulation.sim_time <= simulation.end_time &&
164290001Sglebius	   (!empty(event_queue))) {
165290001Sglebius		curr_event = dequeue(event_queue);
166290001Sglebius		/* Update all the clocks to the time on the event */
167290001Sglebius		sim_update_clocks(curr_event);
168290001Sglebius
169290001Sglebius		/* Execute the function associated with the event */
170290001Sglebius		(*event_ptr[curr_event->function])(curr_event);
171290001Sglebius		free_node(curr_event);
172132451Sroberto	}
173290001Sglebius	printf("sys_received: %lu\n", sys_received);
174290001Sglebius	printf("sys_badlength: %lu\n", sys_badlength);
175290001Sglebius	printf("sys_declined: %lu\n", sys_declined);
176290001Sglebius	printf("sys_restricted: %lu\n", sys_restricted);
177290001Sglebius	printf("sys_newversion: %lu\n", sys_newversion);
178290001Sglebius	printf("sys_oldversion: %lu\n", sys_oldversion);
179290001Sglebius	printf("sys_limitrejected: %lu\n", sys_limitrejected);
180290001Sglebius	printf("sys_badauth: %lu\n", sys_badauth);
181290001Sglebius
182132451Sroberto	return (0);
183132451Sroberto}
184132451Sroberto
185132451Sroberto
186290001Sglebiusvoid
187290001Sglebiusinit_sim_io(void)
188132451Sroberto{
189290001Sglebius	loopback_interface = emalloc_zero(sizeof(*loopback_interface));
190290001Sglebius	ep_list = loopback_interface;
191290001Sglebius	strlcpy(loopback_interface->name, "IPv4loop",
192290001Sglebius		sizeof(loopback_interface->name));
193290001Sglebius	loopback_interface->flags = INT_UP | INT_LOOPBACK;
194290001Sglebius	loopback_interface->fd = -1;
195290001Sglebius	loopback_interface->bfd = -1;
196290001Sglebius	loopback_interface->ifnum = 1;
197290001Sglebius	loopback_interface->family = AF_INET;
198290001Sglebius	AF(&loopback_interface->sin) = AF_INET;
199290001Sglebius	SET_ADDR4(&loopback_interface->sin, LOOPBACKADR);
200290001Sglebius	SET_PORT(&loopback_interface->sin, NTP_PORT);
201290001Sglebius	AF(&loopback_interface->mask) = AF_INET;
202290001Sglebius	SET_ADDR4(&loopback_interface->mask, LOOPNETMASK);
203290001Sglebius}
204132451Sroberto
205290001Sglebius
206290001Sglebius/* Define a function to create an return an Event  */
207290001Sglebius
208290001SglebiusEvent *event(double t, funcTkn f)
209290001Sglebius{
210290001Sglebius    Event *e;
211290001Sglebius
212290001Sglebius    if ((e = get_node(sizeof(*e))) == NULL)
213290001Sglebius	abortsim("get_node failed in event");
214290001Sglebius    e->time = t;
215290001Sglebius    e->function = f;
216290001Sglebius    return (e);
217132451Sroberto}
218132451Sroberto
219290001Sglebius/* NTP SIMULATION FUNCTIONS */
220290001Sglebius
221290001Sglebius/* Define a function for processing a timer interrupt.
222290001Sglebius * On every timer interrupt, call the NTP timer to send packets and process
223290001Sglebius * the clock and then call the receive function to receive packets.
224132451Sroberto */
225290001Sglebiusvoid sim_event_timer(Event *e)
226132451Sroberto{
227290001Sglebius    struct recvbuf *rbuf;
228132451Sroberto
229290001Sglebius    /* Call the NTP timer.
230290001Sglebius     * This will be responsible for actually "sending the packets."
231290001Sglebius     * Since this is a simulation, the packets sent over the network
232290001Sglebius     * will be processed by the simulate_server routine below.
233290001Sglebius     */
234290001Sglebius    timer();
235290001Sglebius
236290001Sglebius    /* Process received buffers */
237290001Sglebius    while (!empty(recv_queue)) {
238290001Sglebius	rbuf = (struct recvbuf *)dequeue(recv_queue);
239290001Sglebius	(*rbuf->receiver)(rbuf);
240290001Sglebius	free_node(rbuf);
241290001Sglebius    }
242290001Sglebius
243290001Sglebius    /* Arm the next timer interrupt. */
244290001Sglebius    enqueue(event_queue,
245290001Sglebius	    event(simulation.sim_time + (1 << EVENT_TIMEOUT), TIMER));
246132451Sroberto}
247132451Sroberto
248132451Sroberto
249290001Sglebius
250290001Sglebius/* Define a function to simulate a server.
251290001Sglebius * This function processes the sent packet according to the server script,
252290001Sglebius * creates a reply packet and pushes the reply packet onto the event queue
253132451Sroberto */
254290001Sglebiusint simulate_server(
255290001Sglebius    sockaddr_u *serv_addr,	/* Address of the server */
256290001Sglebius    endpt *	inter,		/* Interface on which the reply should
257290001Sglebius				   be inserted */
258290001Sglebius    struct pkt *rpkt		/* Packet sent to the server that
259290001Sglebius				   needs to be processed. */
260290001Sglebius    )
261132451Sroberto{
262290001Sglebius    struct pkt xpkt;		/* Packet to be transmitted back
263290001Sglebius				   to the client */
264290001Sglebius    struct recvbuf rbuf;	/* Buffer for the received packet */
265290001Sglebius    Event *e;			/* Packet receive event */
266290001Sglebius    server_info *server;	/* Pointer to the server being simulated */
267290001Sglebius    script_info *curr_script;	/* Current script being processed */
268290001Sglebius    int i;
269290001Sglebius    double d1, d2, d3;		/* Delays while the packet is enroute */
270290001Sglebius    double t1, t2, t3, t4;	/* The four timestamps in the packet */
271290001Sglebius    l_fp lfp_host;		/* host-order l_fp */
272132451Sroberto
273290001Sglebius    ZERO(xpkt);
274290001Sglebius    ZERO(rbuf);
275290001Sglebius
276290001Sglebius    /* Search for the server with the desired address */
277290001Sglebius    server = NULL;
278290001Sglebius    for (i = 0; i < simulation.num_of_servers; ++i) {
279290001Sglebius	if (memcmp(simulation.servers[i].addr, serv_addr,
280290001Sglebius		   sizeof(*serv_addr)) == 0) {
281290001Sglebius	    server = &simulation.servers[i];
282290001Sglebius	    break;
283290001Sglebius	}
284290001Sglebius    }
285290001Sglebius
286290001Sglebius    fprintf(stderr, "Received packet from %s on %s\n",
287290001Sglebius	    stoa(serv_addr), latoa(inter));
288290001Sglebius    if (server == NULL)
289290001Sglebius	abortsim("Server with specified address not found!!!");
290290001Sglebius
291290001Sglebius    /* Get the current script for the server */
292290001Sglebius    curr_script = server->curr_script;
293290001Sglebius
294290001Sglebius    /* Create a server reply packet.
295290001Sglebius     * Masquerade the reply as a stratum-1 server with a GPS clock
296290001Sglebius     */
297290001Sglebius    xpkt.li_vn_mode = PKT_LI_VN_MODE(LEAP_NOWARNING, NTP_VERSION,
298290001Sglebius				     MODE_SERVER);
299290001Sglebius    xpkt.stratum = STRATUM_TO_PKT(((u_char)1));
300290001Sglebius    memcpy(&xpkt.refid, "GPS", 4);
301290001Sglebius    xpkt.ppoll = rpkt->ppoll;
302290001Sglebius    xpkt.precision = rpkt->precision;
303290001Sglebius    xpkt.rootdelay = 0;
304290001Sglebius    xpkt.rootdisp = 0;
305290001Sglebius
306290001Sglebius    /* TIMESTAMP CALCULATIONS
307290001Sglebius	    t1				 t4
308290001Sglebius	     \				/
309290001Sglebius	  d1  \			       / d3
310290001Sglebius	       \		      /
311290001Sglebius	       t2 ----------------- t3
312290001Sglebius			 d2
313290001Sglebius    */
314290001Sglebius    /* Compute the delays */
315290001Sglebius    d1 = poisson(curr_script->prop_delay, curr_script->jitter);
316290001Sglebius    d2 = poisson(curr_script->proc_delay, 0);
317290001Sglebius    d3 = poisson(curr_script->prop_delay, curr_script->jitter);
318290001Sglebius
319290001Sglebius    /* Note: In the transmitted packet:
320290001Sglebius     * 1. t1 and t4 are times in the client according to the local clock.
321290001Sglebius     * 2. t2 and t3 are server times according to the simulated server.
322290001Sglebius     * Compute t1, t2, t3 and t4
323290001Sglebius     * Note: This function is called at time t1.
324290001Sglebius     */
325290001Sglebius
326290001Sglebius    NTOHL_FP(&rpkt->xmt, &lfp_host);
327290001Sglebius    LFPTOD(&lfp_host, t1);
328290001Sglebius    t2 = server->server_time + d1;
329290001Sglebius    t3 = server->server_time + d1 + d2;
330290001Sglebius    t4 = t1 + d1 + d2 + d3;
331290001Sglebius
332290001Sglebius    /* Save the timestamps */
333290001Sglebius    xpkt.org = rpkt->xmt;
334290001Sglebius    DTOLFP(t2, &lfp_host);
335290001Sglebius    HTONL_FP(&lfp_host, &xpkt.rec);
336290001Sglebius    DTOLFP(t3, &lfp_host);
337290001Sglebius    HTONL_FP(&lfp_host, &xpkt.xmt);
338290001Sglebius    xpkt.reftime = xpkt.xmt;
339290001Sglebius
340290001Sglebius    /*
341290001Sglebius     * Ok, we are done with the packet. Now initialize the receive
342290001Sglebius     * buffer for the packet.
343290001Sglebius     */
344290001Sglebius    rbuf.used = 1;
345290001Sglebius    rbuf.receiver = &receive;   /* callback to process the packet */
346290001Sglebius    rbuf.recv_length = LEN_PKT_NOMAC;
347290001Sglebius    rbuf.recv_pkt = xpkt;
348290001Sglebius    rbuf.dstadr = inter;
349290001Sglebius    rbuf.fd = inter->fd;
350290001Sglebius    memcpy(&rbuf.srcadr, serv_addr, sizeof(rbuf.srcadr));
351290001Sglebius    memcpy(&rbuf.recv_srcadr, serv_addr, sizeof(rbuf.recv_srcadr));
352290001Sglebius
353290001Sglebius    /*
354290001Sglebius     * Create a packet event and insert it onto the event_queue at the
355290001Sglebius     * arrival time (t4) of the packet at the client
356290001Sglebius     */
357290001Sglebius    e = event(t4, PACKET);
358290001Sglebius    e->rcv_buf = rbuf;
359290001Sglebius    enqueue(event_queue, e);
360290001Sglebius
361290001Sglebius    /*
362290001Sglebius     * Check if the time of the script has expired. If yes, delete it.
363290001Sglebius     */
364290001Sglebius    if (curr_script->duration > simulation.sim_time &&
365290001Sglebius	NULL == HEAD_PFIFO(server->script)) {
366290001Sglebius	printf("Hello\n");
367290001Sglebius	/*
368290001Sglebius	 * For some reason freeing up the curr_script memory kills the
369290001Sglebius	 * simulation. Further debugging is needed to determine why.
370290001Sglebius	 * free(curr_script);
371290001Sglebius	 */
372290001Sglebius	UNLINK_FIFO(curr_script, *server->script, link);
373290001Sglebius    }
374290001Sglebius
375290001Sglebius    return (0);
376132451Sroberto}
377132451Sroberto
378132451Sroberto
379290001Sglebius/* Define a function to update all the clocks
380290001Sglebius * Most of the code is modified from the systime.c file by Prof. Mills
381132451Sroberto */
382290001Sglebius
383290001Sglebiusvoid sim_update_clocks(Event *e)
384132451Sroberto{
385290001Sglebius    double time_gap;
386290001Sglebius    double adj;
387290001Sglebius    int i;
388132451Sroberto
389290001Sglebius    /* Compute the time between the last update event and this update */
390290001Sglebius    time_gap = e->time - simulation.sim_time;
391290001Sglebius
392290001Sglebius    if (time_gap < 0)
393290001Sglebius	    printf("WARNING: e->time %.6g comes before sim_time %.6g (gap %+.6g)\n",
394290001Sglebius		   e->time, simulation.sim_time, time_gap);
395290001Sglebius
396290001Sglebius    /* Advance the client clock */
397290001Sglebius    if (e->time + time_gap < simclock.local_time)
398290001Sglebius	    printf("WARNING: e->time + gap %.6g comes before local_time %.6g\n",
399290001Sglebius		   e->time + time_gap, simclock.local_time);
400290001Sglebius    simclock.local_time = e->time + time_gap;
401290001Sglebius
402290001Sglebius    /* Advance the simulation time */
403290001Sglebius    simulation.sim_time = e->time;
404290001Sglebius
405290001Sglebius    /* Advance the server clocks adjusted for systematic and random frequency
406290001Sglebius     * errors. The random error is a random walk computed as the
407290001Sglebius     * integral of samples from a Gaussian distribution.
408290001Sglebius     */
409290001Sglebius    for (i = 0; i < simulation.num_of_servers; ++i) {
410290001Sglebius	simulation.servers[i].curr_script->freq_offset +=
411290001Sglebius	    gauss(0, time_gap * simulation.servers[i].curr_script->wander);
412290001Sglebius
413290001Sglebius	simulation.servers[i].server_time += time_gap *
414290001Sglebius	    (1 + simulation.servers[i].curr_script->freq_offset);
415290001Sglebius    }
416290001Sglebius
417290001Sglebius    /* Perform the adjtime() function. If the adjustment completed
418290001Sglebius     * in the previous interval, amortize the entire amount; if not,
419290001Sglebius     * carry the leftover to the next interval.
420290001Sglebius     */
421290001Sglebius
422290001Sglebius    adj = time_gap * simclock.slew;
423290001Sglebius    if (adj < fabs(simclock.adj)) {
424290001Sglebius	if (simclock.adj < 0) {
425290001Sglebius	    simclock.adj += adj;
426290001Sglebius	    simclock.local_time -= adj;
427290001Sglebius	} else {
428290001Sglebius	    simclock.adj -= adj;
429290001Sglebius	    simclock.local_time += adj;
430290001Sglebius	}
431290001Sglebius    } else {
432290001Sglebius	simclock.local_time += simclock.adj;
433290001Sglebius	simclock.adj = 0;
434290001Sglebius    }
435132451Sroberto}
436132451Sroberto
437132451Sroberto
438290001Sglebius/* Define a function that processes a receive packet event.
439290001Sglebius * This function simply inserts the packet received onto the receive queue
440290001Sglebius */
441290001Sglebius
442290001Sglebiusvoid sim_event_recv_packet(Event *e)
443132451Sroberto{
444290001Sglebius    struct recvbuf *rbuf;
445290001Sglebius
446290001Sglebius    /* Allocate a receive buffer and copy the packet to it */
447290001Sglebius    if ((rbuf = get_node(sizeof(*rbuf))) == NULL)
448290001Sglebius	abortsim("get_node failed in sim_event_recv_packet");
449290001Sglebius    memcpy(rbuf, &e->rcv_buf, sizeof(*rbuf));
450290001Sglebius
451290001Sglebius    /* Store the local time in the received packet */
452290001Sglebius    DTOLFP(simclock.local_time, &rbuf->recv_time);
453290001Sglebius
454290001Sglebius    /* Insert the packet received onto the receive queue */
455290001Sglebius    enqueue(recv_queue, rbuf);
456132451Sroberto}
457132451Sroberto
458132451Sroberto
459290001Sglebius
460290001Sglebius/* Define a function to output simulation statistics on a beep event
461132451Sroberto */
462290001Sglebius
463290001Sglebius/*** TODO: Need to decide on how to output for multiple servers ***/
464290001Sglebiusvoid sim_event_beep(Event *e)
465132451Sroberto{
466290001Sglebius#if 0
467290001Sglebius    static int first_time = 1;
468290001Sglebius    char *dash = "-----------------";
469290001Sglebius#endif
470132451Sroberto
471290001Sglebius    fprintf(stderr, "BEEP!!!\n");
472290001Sglebius    enqueue(event_queue, event(e->time + simulation.beep_delay, BEEP));
473290001Sglebius#if 0
474290001Sglebius    if(simulation.beep_delay > 0) {
475290001Sglebius	if (first_time) {
476290001Sglebius	    printf("\t%4c    T    %4c\t%4c  T+ERR  %3c\t%5cT+ERR+NTP\n",
477290001Sglebius	           ' ', ' ', ' ', ' ',' ');
478290001Sglebius	    printf("\t%s\t%s\t%s\n", dash, dash, dash);
479290001Sglebius	    first_time = 0;
480132451Sroberto
481290001Sglebius	    printf("\t%16.6f\t%16.6f\t%16.6f\n",
482290001Sglebius	           n->time, n->clk_time, n->ntp_time);
483290001Sglebius	    return;
484132451Sroberto	}
485290001Sglebius	printf("\t%16.6f\t%16.6f\t%16.6f\n",
486290001Sglebius	       simclock.local_time,
487290001Sglebius	       n->time, n->clk_time, n->ntp_time);
488290001Sglebius#endif
489132451Sroberto
490132451Sroberto}
491132451Sroberto
492132451Sroberto
493290001Sglebius/* Define a function to abort the simulation on an error and spit out an
494290001Sglebius * error message
495132451Sroberto */
496290001Sglebius
497290001Sglebiusvoid abortsim(char *errmsg)
498132451Sroberto{
499290001Sglebius    perror(errmsg);
500290001Sglebius    exit(1);
501290001Sglebius}
502132451Sroberto
503132451Sroberto
504132451Sroberto
505290001Sglebius/* CODE ORIGINALLY IN libntp/systime.c
506290001Sglebius * -----------------------------------
507290001Sglebius * This code was a part of the original NTP simulator and originally
508290001Sglebius * had its home in the libntp/systime.c file.
509290001Sglebius *
510290001Sglebius * It has been shamelessly moved to here and has been modified for the
511290001Sglebius * purposes of the current simulator.
512290001Sglebius */
513132451Sroberto
514132451Sroberto
515132451Sroberto/*
516290001Sglebius * get_systime - return the system time in NTP timestamp format
517132451Sroberto */
518132451Srobertovoid
519290001Sglebiusget_systime(
520290001Sglebius    l_fp *now		/* current system time in l_fp */        )
521132451Sroberto{
522290001Sglebius    /*
523290001Sglebius     * To fool the code that determines the local clock precision,
524290001Sglebius     * we advance the clock a minimum of 200 nanoseconds on every
525290001Sglebius     * clock read. This is appropriate for a typical modern machine
526290001Sglebius     * with nanosecond clocks. Note we make no attempt here to
527290001Sglebius     * simulate reading error, since the error is so small. This may
528290001Sglebius     * change when the need comes to implement picosecond clocks.
529290001Sglebius     */
530290001Sglebius    if (simclock.local_time == simclock.last_read_time)
531290001Sglebius        simclock.local_time += 200e-9;
532132451Sroberto
533290001Sglebius    simclock.last_read_time = simclock.local_time;
534290001Sglebius    DTOLFP(simclock.local_time, now);
535290001Sglebius/* OLD Code
536290001Sglebius   if (ntp_node.ntp_time == ntp_node.last_time)
537290001Sglebius   ntp_node.ntp_time += 200e-9;
538290001Sglebius   ntp_node.last_time = ntp_node.ntp_time;
539290001Sglebius   DTOLFP(ntp_node.ntp_time, now);
540290001Sglebius*/
541132451Sroberto}
542290001Sglebius
543290001Sglebius
544290001Sglebius/*
545290001Sglebius * adj_systime - advance or retard the system clock exactly like the
546290001Sglebius * real thng.
547290001Sglebius */
548290001Sglebiusint				/* always succeeds */
549290001Sglebiusadj_systime(
550290001Sglebius    double now		/* time adjustment (s) */
551290001Sglebius    )
552290001Sglebius{
553290001Sglebius    struct timeval adjtv;	/* new adjustment */
554290001Sglebius    double	dtemp;
555290001Sglebius    long	ticks;
556290001Sglebius    int	isneg = 0;
557132451Sroberto
558290001Sglebius    /*
559290001Sglebius     * Most Unix adjtime() implementations adjust the system clock
560290001Sglebius     * in microsecond quanta, but some adjust in 10-ms quanta. We
561290001Sglebius     * carefully round the adjustment to the nearest quantum, then
562290001Sglebius     * adjust in quanta and keep the residue for later.
563290001Sglebius     */
564290001Sglebius    dtemp = now + sys_residual;
565290001Sglebius    if (dtemp < 0) {
566290001Sglebius	isneg = 1;
567290001Sglebius	dtemp = -dtemp;
568290001Sglebius    }
569290001Sglebius    adjtv.tv_sec = (long)dtemp;
570290001Sglebius    dtemp -= adjtv.tv_sec;
571290001Sglebius    ticks = (long)(dtemp / sys_tick + .5);
572290001Sglebius    adjtv.tv_usec = (long)(ticks * sys_tick * 1e6);
573290001Sglebius    dtemp -= adjtv.tv_usec / 1e6;
574290001Sglebius    sys_residual = dtemp;
575132451Sroberto
576290001Sglebius    /*
577290001Sglebius     * Convert to signed seconds and microseconds for the Unix
578290001Sglebius     * adjtime() system call. Note we purposely lose the adjtime()
579290001Sglebius     * leftover.
580290001Sglebius     */
581290001Sglebius    if (isneg) {
582290001Sglebius	adjtv.tv_sec = -adjtv.tv_sec;
583290001Sglebius	adjtv.tv_usec = -adjtv.tv_usec;
584290001Sglebius	sys_residual = -sys_residual;
585290001Sglebius    }
586290001Sglebius    simclock.adj = now;
587290001Sglebius/*	ntp_node.adj = now; */
588290001Sglebius    return (1);
589290001Sglebius}
590290001Sglebius
591290001Sglebius
592132451Sroberto/*
593290001Sglebius * step_systime - step the system clock. We are religious here.
594132451Sroberto */
595290001Sglebiusint				/* always succeeds */
596290001Sglebiusstep_systime(
597290001Sglebius    double now		/* step adjustment (s) */
598290001Sglebius    )
599132451Sroberto{
600290001Sglebius#ifdef DEBUG
601290001Sglebius    if (debug)
602290001Sglebius	printf("step_systime: time %.6f adj %.6f\n",
603290001Sglebius	       simclock.local_time, now);
604290001Sglebius#endif
605290001Sglebius    simclock.local_time += now;
606290001Sglebius    return (1);
607290001Sglebius}
608290001Sglebius
609290001Sglebius/*
610290001Sglebius * gauss() - returns samples from a gaussion distribution
611290001Sglebius */
612290001Sglebiusdouble				/* Gaussian sample */
613290001Sglebiusgauss(
614290001Sglebius    double m,		/* sample mean */
615290001Sglebius    double s		/* sample standard deviation (sigma) */
616290001Sglebius    )
617290001Sglebius{
618290001Sglebius    double q1, q2;
619132451Sroberto
620290001Sglebius    /*
621290001Sglebius     * Roll a sample from a Gaussian distribution with mean m and
622290001Sglebius     * standard deviation s. For m = 0, s = 1, mean(y) = 0,
623290001Sglebius     * std(y) = 1.
624290001Sglebius     */
625290001Sglebius    if (s == 0)
626290001Sglebius        return (m);
627290001Sglebius    while ((q1 = drand48()) == 0)
628290001Sglebius	/* empty statement */;
629290001Sglebius    q2 = drand48();
630290001Sglebius    return (m + s * sqrt(-2. * log(q1)) * cos(2. * PI * q2));
631132451Sroberto}
632132451Sroberto
633290001Sglebius
634132451Sroberto/*
635290001Sglebius * poisson() - returns samples from a network delay distribution
636132451Sroberto */
637290001Sglebiusdouble				/* delay sample (s) */
638290001Sglebiuspoisson(
639290001Sglebius    double m,		/* fixed propagation delay (s) */
640290001Sglebius    double s		/* exponential parameter (mu) */
641290001Sglebius    )
642132451Sroberto{
643290001Sglebius    double q1;
644290001Sglebius
645290001Sglebius    /*
646290001Sglebius     * Roll a sample from a composite distribution with propagation
647290001Sglebius     * delay m and exponential distribution time with parameter s.
648290001Sglebius     * For m = 0, s = 1, mean(y) = std(y) = 1.
649290001Sglebius     */
650290001Sglebius    if (s == 0)
651290001Sglebius        return (m);
652290001Sglebius    while ((q1 = drand48()) == 0)
653290001Sglebius	/* empty statement */;
654290001Sglebius    return (m - s * log(q1 * s));
655132451Sroberto}
656290001Sglebius
657290001Sglebius#endif
658