xref: /freebsd/contrib/ntp/ntpd/ntpsim.c (revision 091002585974d17c9533f943ec351c13a69788ab)
12b15cb3dSCy Schubert /* ntpdsim.c
22b15cb3dSCy Schubert  *
32b15cb3dSCy Schubert  * The source code for the ntp discrete event simulator.
42b15cb3dSCy Schubert  *
52b15cb3dSCy Schubert  * Written By:	Sachin Kamboj
62b15cb3dSCy Schubert  *		University of Delaware
72b15cb3dSCy Schubert  *		Newark, DE 19711
82b15cb3dSCy Schubert  * Copyright (c) 2006
92b15cb3dSCy Schubert  * (Some code shamelessly based on the original NTP discrete event simulator)
109c2daa00SOllivier Robert  */
112b15cb3dSCy Schubert 
122b15cb3dSCy Schubert #include <config.h>
132b15cb3dSCy Schubert #ifdef SIM
149c2daa00SOllivier Robert #include "ntpd.h"
152b15cb3dSCy Schubert #include "ntp_config.h"
169c2daa00SOllivier Robert 
172b15cb3dSCy Schubert /* forward prototypes */
182b15cb3dSCy Schubert int determine_event_ordering(const Event *e1, const Event *e2);
192b15cb3dSCy Schubert int determine_recv_buf_ordering(const struct recvbuf *b1,
202b15cb3dSCy Schubert 				const struct recvbuf *b2);
212b15cb3dSCy Schubert void create_server_associations(void);
222b15cb3dSCy Schubert void init_sim_io(void);
239c2daa00SOllivier Robert 
242b15cb3dSCy Schubert /* Global Variable Definitions */
252b15cb3dSCy Schubert sim_info simulation;		/* Simulation Control Variables */
262b15cb3dSCy Schubert local_clock_info simclock;	/* Local Clock Variables */
272b15cb3dSCy Schubert queue *event_queue;		/* Event Queue */
282b15cb3dSCy Schubert queue *recv_queue;		/* Receive Queue */
292b15cb3dSCy Schubert static double sys_residual = 0;	/* adjustment residue (s) */
302b15cb3dSCy Schubert 
312b15cb3dSCy Schubert void (*event_ptr[]) (Event *) = {
322b15cb3dSCy Schubert     sim_event_beep, sim_update_clocks, sim_event_timer, sim_event_recv_packet
332b15cb3dSCy Schubert };			/* Function pointer to the events */
349c2daa00SOllivier Robert 
359c2daa00SOllivier Robert 
369c2daa00SOllivier Robert /*
372b15cb3dSCy Schubert  * Define a function to compare two events to determine which one occurs
382b15cb3dSCy Schubert  * first.
399c2daa00SOllivier Robert  */
409c2daa00SOllivier Robert int
determine_event_ordering(const Event * e1,const Event * e2)412b15cb3dSCy Schubert determine_event_ordering(
422b15cb3dSCy Schubert 	const Event *e1,
432b15cb3dSCy Schubert 	const Event *e2
442b15cb3dSCy Schubert 	)
452b15cb3dSCy Schubert {
462b15cb3dSCy Schubert 	return (e1->time - e2->time);
472b15cb3dSCy Schubert }
482b15cb3dSCy Schubert 
492b15cb3dSCy Schubert 
502b15cb3dSCy Schubert /*
512b15cb3dSCy Schubert  * Define a function to compare two received packets to determine which
522b15cb3dSCy Schubert  * one is received first.
532b15cb3dSCy Schubert  */
542b15cb3dSCy Schubert int
determine_recv_buf_ordering(const struct recvbuf * b1,const struct recvbuf * b2)552b15cb3dSCy Schubert determine_recv_buf_ordering(
562b15cb3dSCy Schubert 	const struct recvbuf *b1,
572b15cb3dSCy Schubert 	const struct recvbuf *b2
582b15cb3dSCy Schubert 	)
592b15cb3dSCy Schubert {
602b15cb3dSCy Schubert 	double recv_time1;
612b15cb3dSCy Schubert 	double recv_time2;
622b15cb3dSCy Schubert 
632b15cb3dSCy Schubert 	/* Simply convert the time received to double and subtract */
642b15cb3dSCy Schubert 	LFPTOD(&b1->recv_time, recv_time1);
652b15cb3dSCy Schubert 	LFPTOD(&b2->recv_time, recv_time2);
662b15cb3dSCy Schubert 
672b15cb3dSCy Schubert 	return (int)(recv_time1 - recv_time2);
682b15cb3dSCy Schubert }
692b15cb3dSCy Schubert 
702b15cb3dSCy Schubert 
712b15cb3dSCy Schubert /* Define a function to create the server associations */
create_server_associations(void)722b15cb3dSCy Schubert void create_server_associations(void)
732b15cb3dSCy Schubert {
742b15cb3dSCy Schubert 	int i;
752b15cb3dSCy Schubert 
762b15cb3dSCy Schubert 	for (i = 0; i < simulation.num_of_servers; ++i) {
772b15cb3dSCy Schubert 		printf("%s\n", stoa(simulation.servers[i].addr));
782b15cb3dSCy Schubert 		if (peer_config(simulation.servers[i].addr,
792b15cb3dSCy Schubert 				NULL,
802b15cb3dSCy Schubert 				loopback_interface,
812b15cb3dSCy Schubert 				MODE_CLIENT,
82*09100258SXin LI 				-1,
832b15cb3dSCy Schubert 				NTP_VERSION,
842b15cb3dSCy Schubert 				NTP_MINDPOLL,
852b15cb3dSCy Schubert 				NTP_MAXDPOLL,
862b15cb3dSCy Schubert 				0, /* peerflags */
872b15cb3dSCy Schubert 				0, /* ttl */
882b15cb3dSCy Schubert 				0, /* peerkey */
892b15cb3dSCy Schubert 				NULL /* group ident */) == 0) {
902b15cb3dSCy Schubert 			fprintf(stderr,
912b15cb3dSCy Schubert 				"ERROR!! Could not create association for: %s\n",
922b15cb3dSCy Schubert 				stoa(simulation.servers[i].addr));
932b15cb3dSCy Schubert 		}
942b15cb3dSCy Schubert 	}
952b15cb3dSCy Schubert }
962b15cb3dSCy Schubert 
972b15cb3dSCy Schubert 
982b15cb3dSCy Schubert /* Main Simulator Code */
992b15cb3dSCy Schubert 
1002b15cb3dSCy Schubert int
ntpsim(int argc,char * argv[])1019c2daa00SOllivier Robert ntpsim(
1029c2daa00SOllivier Robert 	int	argc,
1039c2daa00SOllivier Robert 	char *	argv[]
1049c2daa00SOllivier Robert 	)
1059c2daa00SOllivier Robert {
1062b15cb3dSCy Schubert 	Event *		curr_event;
1079c2daa00SOllivier Robert 	struct timeval	seed;
1089c2daa00SOllivier Robert 
1092b15cb3dSCy Schubert 	/* Initialize the local Clock */
1102b15cb3dSCy Schubert 	simclock.local_time = 0;
1112b15cb3dSCy Schubert 	simclock.adj = 0;
1122b15cb3dSCy Schubert 	simclock.slew = 500e-6;
1139c2daa00SOllivier Robert 
1142b15cb3dSCy Schubert 	/* Initialize the simulation */
1152b15cb3dSCy Schubert 	simulation.num_of_servers = 0;
1162b15cb3dSCy Schubert 	simulation.beep_delay = BEEP_DLY;
1172b15cb3dSCy Schubert 	simulation.sim_time = 0;
1182b15cb3dSCy Schubert 	simulation.end_time = SIM_TIME;
1199c2daa00SOllivier Robert 
1202b15cb3dSCy Schubert 	/* Initialize ntp modules */
1212b15cb3dSCy Schubert 	initializing = TRUE;
1222b15cb3dSCy Schubert 	msyslog_term = TRUE;
1232b15cb3dSCy Schubert 	init_sim_io();
1249c2daa00SOllivier Robert 	init_auth();
1259c2daa00SOllivier Robert 	init_util();
1269c2daa00SOllivier Robert 	init_restrict();
1279c2daa00SOllivier Robert 	init_mon();
1289c2daa00SOllivier Robert 	init_timer();
1299c2daa00SOllivier Robert 	init_lib();
1309c2daa00SOllivier Robert 	init_request();
1319c2daa00SOllivier Robert 	init_control();
1329c2daa00SOllivier Robert 	init_peer();
1339c2daa00SOllivier Robert 	init_proto();
1349c2daa00SOllivier Robert 	init_loopfilter();
1359c2daa00SOllivier Robert 	mon_start(MON_OFF);
136ea906c41SOllivier Robert 
1372b15cb3dSCy Schubert 	/* Call getconfig to parse the configuration file */
1389c2daa00SOllivier Robert 	getconfig(argc, argv);
1392b15cb3dSCy Schubert 	loop_config(LOOP_DRIFTINIT, 0);
1402b15cb3dSCy Schubert 	initializing = FALSE;
1419c2daa00SOllivier Robert 
1429c2daa00SOllivier Robert 	/*
1439c2daa00SOllivier Robert 	 * Watch out here, we want the real time, not the silly stuff.
1449c2daa00SOllivier Robert 	 */
1459c2daa00SOllivier Robert 	gettimeofday(&seed, NULL);
146ea906c41SOllivier Robert 	ntp_srandom(seed.tv_usec);
1479c2daa00SOllivier Robert 
1482b15cb3dSCy Schubert 	/* Initialize the event queue */
1492b15cb3dSCy Schubert 	event_queue = create_priority_queue((q_order_func)
1502b15cb3dSCy Schubert 	    determine_event_ordering);
1512b15cb3dSCy Schubert 
1522b15cb3dSCy Schubert 	/* Initialize the receive queue */
1532b15cb3dSCy Schubert 	recv_queue = create_priority_queue((q_order_func)
1542b15cb3dSCy Schubert 	    determine_recv_buf_ordering);
1552b15cb3dSCy Schubert 
1562b15cb3dSCy Schubert 	/* Push a beep and a timer on the event queue */
1572b15cb3dSCy Schubert 	enqueue(event_queue, event(0, BEEP));
1582b15cb3dSCy Schubert 	enqueue(event_queue, event(simulation.sim_time + 1.0, TIMER));
1599c2daa00SOllivier Robert 
1609c2daa00SOllivier Robert 	/*
1619c2daa00SOllivier Robert 	 * Pop the queue until nothing is left or time is exceeded
1629c2daa00SOllivier Robert 	 */
1632b15cb3dSCy Schubert 	/* maxtime = simulation.sim_time + simulation.end_time;*/
1642b15cb3dSCy Schubert 	while (simulation.sim_time <= simulation.end_time &&
1652b15cb3dSCy Schubert 	   (!empty(event_queue))) {
1662b15cb3dSCy Schubert 		curr_event = dequeue(event_queue);
1672b15cb3dSCy Schubert 		/* Update all the clocks to the time on the event */
1682b15cb3dSCy Schubert 		sim_update_clocks(curr_event);
1692b15cb3dSCy Schubert 
1702b15cb3dSCy Schubert 		/* Execute the function associated with the event */
1712b15cb3dSCy Schubert 		(*event_ptr[curr_event->function])(curr_event);
1722b15cb3dSCy Schubert 		free_node(curr_event);
1739c2daa00SOllivier Robert 	}
1742b15cb3dSCy Schubert 	printf("sys_received: %lu\n", sys_received);
1752b15cb3dSCy Schubert 	printf("sys_badlength: %lu\n", sys_badlength);
1762b15cb3dSCy Schubert 	printf("sys_declined: %lu\n", sys_declined);
1772b15cb3dSCy Schubert 	printf("sys_restricted: %lu\n", sys_restricted);
1782b15cb3dSCy Schubert 	printf("sys_newversion: %lu\n", sys_newversion);
1792b15cb3dSCy Schubert 	printf("sys_oldversion: %lu\n", sys_oldversion);
1802b15cb3dSCy Schubert 	printf("sys_limitrejected: %lu\n", sys_limitrejected);
1812b15cb3dSCy Schubert 	printf("sys_badauth: %lu\n", sys_badauth);
1822b15cb3dSCy Schubert 
1839c2daa00SOllivier Robert 	return (0);
1849c2daa00SOllivier Robert }
1859c2daa00SOllivier Robert 
1869c2daa00SOllivier Robert 
1872b15cb3dSCy Schubert void
init_sim_io(void)1882b15cb3dSCy Schubert init_sim_io(void)
1899c2daa00SOllivier Robert {
1902b15cb3dSCy Schubert 	loopback_interface = emalloc_zero(sizeof(*loopback_interface));
1912b15cb3dSCy Schubert 	ep_list = loopback_interface;
1922b15cb3dSCy Schubert 	strlcpy(loopback_interface->name, "IPv4loop",
1932b15cb3dSCy Schubert 		sizeof(loopback_interface->name));
1942b15cb3dSCy Schubert 	loopback_interface->flags = INT_UP | INT_LOOPBACK;
1952b15cb3dSCy Schubert 	loopback_interface->fd = -1;
1962b15cb3dSCy Schubert 	loopback_interface->bfd = -1;
1972b15cb3dSCy Schubert 	loopback_interface->ifnum = 1;
1982b15cb3dSCy Schubert 	loopback_interface->family = AF_INET;
1992b15cb3dSCy Schubert 	AF(&loopback_interface->sin) = AF_INET;
2002b15cb3dSCy Schubert 	SET_ADDR4(&loopback_interface->sin, LOOPBACKADR);
2012b15cb3dSCy Schubert 	SET_PORT(&loopback_interface->sin, NTP_PORT);
2022b15cb3dSCy Schubert 	AF(&loopback_interface->mask) = AF_INET;
2032b15cb3dSCy Schubert 	SET_ADDR4(&loopback_interface->mask, LOOPNETMASK);
2042b15cb3dSCy Schubert }
2059c2daa00SOllivier Robert 
2062b15cb3dSCy Schubert 
2072b15cb3dSCy Schubert /* Define a function to create an return an Event  */
2082b15cb3dSCy Schubert 
event(double t,funcTkn f)2092b15cb3dSCy Schubert Event *event(double t, funcTkn f)
2102b15cb3dSCy Schubert {
2112b15cb3dSCy Schubert     Event *e;
2122b15cb3dSCy Schubert 
2132b15cb3dSCy Schubert     if ((e = get_node(sizeof(*e))) == NULL)
2142b15cb3dSCy Schubert 	abortsim("get_node failed in event");
2152b15cb3dSCy Schubert     e->time = t;
2162b15cb3dSCy Schubert     e->function = f;
2179c2daa00SOllivier Robert     return (e);
2189c2daa00SOllivier Robert }
2199c2daa00SOllivier Robert 
2202b15cb3dSCy Schubert /* NTP SIMULATION FUNCTIONS */
2212b15cb3dSCy Schubert 
2222b15cb3dSCy Schubert /* Define a function for processing a timer interrupt.
2232b15cb3dSCy Schubert  * On every timer interrupt, call the NTP timer to send packets and process
2242b15cb3dSCy Schubert  * the clock and then call the receive function to receive packets.
2259c2daa00SOllivier Robert  */
sim_event_timer(Event * e)2262b15cb3dSCy Schubert void sim_event_timer(Event *e)
2279c2daa00SOllivier Robert {
2289c2daa00SOllivier Robert     struct recvbuf *rbuf;
2299c2daa00SOllivier Robert 
2302b15cb3dSCy Schubert     /* Call the NTP timer.
2312b15cb3dSCy Schubert      * This will be responsible for actually "sending the packets."
2322b15cb3dSCy Schubert      * Since this is a simulation, the packets sent over the network
2332b15cb3dSCy Schubert      * will be processed by the simulate_server routine below.
2342b15cb3dSCy Schubert      */
2359c2daa00SOllivier Robert     timer();
2369c2daa00SOllivier Robert 
2372b15cb3dSCy Schubert     /* Process received buffers */
2382b15cb3dSCy Schubert     while (!empty(recv_queue)) {
2392b15cb3dSCy Schubert 	rbuf = (struct recvbuf *)dequeue(recv_queue);
2402b15cb3dSCy Schubert 	(*rbuf->receiver)(rbuf);
2412b15cb3dSCy Schubert 	free_node(rbuf);
2429c2daa00SOllivier Robert     }
2439c2daa00SOllivier Robert 
2442b15cb3dSCy Schubert     /* Arm the next timer interrupt. */
2452b15cb3dSCy Schubert     enqueue(event_queue,
2462b15cb3dSCy Schubert 	    event(simulation.sim_time + (1 << EVENT_TIMEOUT), TIMER));
2479c2daa00SOllivier Robert }
2489c2daa00SOllivier Robert 
2499c2daa00SOllivier Robert 
2502b15cb3dSCy Schubert 
2512b15cb3dSCy Schubert /* Define a function to simulate a server.
2522b15cb3dSCy Schubert  * This function processes the sent packet according to the server script,
2532b15cb3dSCy Schubert  * creates a reply packet and pushes the reply packet onto the event queue
2549c2daa00SOllivier Robert  */
simulate_server(sockaddr_u * serv_addr,endpt * inter,struct pkt * rpkt)2552b15cb3dSCy Schubert int simulate_server(
2562b15cb3dSCy Schubert     sockaddr_u *serv_addr,	/* Address of the server */
2572b15cb3dSCy Schubert     endpt *	inter,		/* Interface on which the reply should
2582b15cb3dSCy Schubert 				   be inserted */
2592b15cb3dSCy Schubert     struct pkt *rpkt		/* Packet sent to the server that
2602b15cb3dSCy Schubert 				   needs to be processed. */
2619c2daa00SOllivier Robert     )
2629c2daa00SOllivier Robert {
2632b15cb3dSCy Schubert     struct pkt xpkt;		/* Packet to be transmitted back
2642b15cb3dSCy Schubert 				   to the client */
2652b15cb3dSCy Schubert     struct recvbuf rbuf;	/* Buffer for the received packet */
2662b15cb3dSCy Schubert     Event *e;			/* Packet receive event */
2672b15cb3dSCy Schubert     server_info *server;	/* Pointer to the server being simulated */
2682b15cb3dSCy Schubert     script_info *curr_script;	/* Current script being processed */
2692b15cb3dSCy Schubert     int i;
2702b15cb3dSCy Schubert     double d1, d2, d3;		/* Delays while the packet is enroute */
2712b15cb3dSCy Schubert     double t1, t2, t3, t4;	/* The four timestamps in the packet */
2722b15cb3dSCy Schubert     l_fp lfp_host;		/* host-order l_fp */
2739c2daa00SOllivier Robert 
2742b15cb3dSCy Schubert     ZERO(xpkt);
2752b15cb3dSCy Schubert     ZERO(rbuf);
2762b15cb3dSCy Schubert 
2772b15cb3dSCy Schubert     /* Search for the server with the desired address */
2782b15cb3dSCy Schubert     server = NULL;
2792b15cb3dSCy Schubert     for (i = 0; i < simulation.num_of_servers; ++i) {
2802b15cb3dSCy Schubert 	if (memcmp(simulation.servers[i].addr, serv_addr,
2812b15cb3dSCy Schubert 		   sizeof(*serv_addr)) == 0) {
2822b15cb3dSCy Schubert 	    server = &simulation.servers[i];
2832b15cb3dSCy Schubert 	    break;
2842b15cb3dSCy Schubert 	}
2852b15cb3dSCy Schubert     }
2862b15cb3dSCy Schubert 
2872b15cb3dSCy Schubert     fprintf(stderr, "Received packet from %s on %s\n",
2882b15cb3dSCy Schubert 	    stoa(serv_addr), latoa(inter));
2892b15cb3dSCy Schubert     if (server == NULL)
2902b15cb3dSCy Schubert 	abortsim("Server with specified address not found!!!");
2912b15cb3dSCy Schubert 
2922b15cb3dSCy Schubert     /* Get the current script for the server */
2932b15cb3dSCy Schubert     curr_script = server->curr_script;
2942b15cb3dSCy Schubert 
2952b15cb3dSCy Schubert     /* Create a server reply packet.
2962b15cb3dSCy Schubert      * Masquerade the reply as a stratum-1 server with a GPS clock
2979c2daa00SOllivier Robert      */
2989c2daa00SOllivier Robert     xpkt.li_vn_mode = PKT_LI_VN_MODE(LEAP_NOWARNING, NTP_VERSION,
2999c2daa00SOllivier Robert 				     MODE_SERVER);
3009c2daa00SOllivier Robert     xpkt.stratum = STRATUM_TO_PKT(((u_char)1));
3019c2daa00SOllivier Robert     memcpy(&xpkt.refid, "GPS", 4);
3029c2daa00SOllivier Robert     xpkt.ppoll = rpkt->ppoll;
3039c2daa00SOllivier Robert     xpkt.precision = rpkt->precision;
3049c2daa00SOllivier Robert     xpkt.rootdelay = 0;
3052b15cb3dSCy Schubert     xpkt.rootdisp = 0;
3069c2daa00SOllivier Robert 
3072b15cb3dSCy Schubert     /* TIMESTAMP CALCULATIONS
3082b15cb3dSCy Schubert 	    t1				 t4
3092b15cb3dSCy Schubert 	     \				/
3102b15cb3dSCy Schubert 	  d1  \			       / d3
3112b15cb3dSCy Schubert 	       \		      /
3122b15cb3dSCy Schubert 	       t2 ----------------- t3
3132b15cb3dSCy Schubert 			 d2
3149c2daa00SOllivier Robert     */
3152b15cb3dSCy Schubert     /* Compute the delays */
3162b15cb3dSCy Schubert     d1 = poisson(curr_script->prop_delay, curr_script->jitter);
3172b15cb3dSCy Schubert     d2 = poisson(curr_script->proc_delay, 0);
3182b15cb3dSCy Schubert     d3 = poisson(curr_script->prop_delay, curr_script->jitter);
3192b15cb3dSCy Schubert 
3202b15cb3dSCy Schubert     /* Note: In the transmitted packet:
3212b15cb3dSCy Schubert      * 1. t1 and t4 are times in the client according to the local clock.
3222b15cb3dSCy Schubert      * 2. t2 and t3 are server times according to the simulated server.
3232b15cb3dSCy Schubert      * Compute t1, t2, t3 and t4
3242b15cb3dSCy Schubert      * Note: This function is called at time t1.
3252b15cb3dSCy Schubert      */
3262b15cb3dSCy Schubert 
3272b15cb3dSCy Schubert     NTOHL_FP(&rpkt->xmt, &lfp_host);
3282b15cb3dSCy Schubert     LFPTOD(&lfp_host, t1);
3292b15cb3dSCy Schubert     t2 = server->server_time + d1;
3302b15cb3dSCy Schubert     t3 = server->server_time + d1 + d2;
3312b15cb3dSCy Schubert     t4 = t1 + d1 + d2 + d3;
3322b15cb3dSCy Schubert 
3332b15cb3dSCy Schubert     /* Save the timestamps */
3349c2daa00SOllivier Robert     xpkt.org = rpkt->xmt;
3352b15cb3dSCy Schubert     DTOLFP(t2, &lfp_host);
3362b15cb3dSCy Schubert     HTONL_FP(&lfp_host, &xpkt.rec);
3372b15cb3dSCy Schubert     DTOLFP(t3, &lfp_host);
3382b15cb3dSCy Schubert     HTONL_FP(&lfp_host, &xpkt.xmt);
3399c2daa00SOllivier Robert     xpkt.reftime = xpkt.xmt;
3409c2daa00SOllivier Robert 
3419c2daa00SOllivier Robert     /*
3422b15cb3dSCy Schubert      * Ok, we are done with the packet. Now initialize the receive
3432b15cb3dSCy Schubert      * buffer for the packet.
3449c2daa00SOllivier Robert      */
3452b15cb3dSCy Schubert     rbuf.used = 1;
3462b15cb3dSCy Schubert     rbuf.receiver = &receive;   /* callback to process the packet */
3479c2daa00SOllivier Robert     rbuf.recv_length = LEN_PKT_NOMAC;
3489c2daa00SOllivier Robert     rbuf.recv_pkt = xpkt;
3492b15cb3dSCy Schubert     rbuf.dstadr = inter;
3502b15cb3dSCy Schubert     rbuf.fd = inter->fd;
3512b15cb3dSCy Schubert     memcpy(&rbuf.srcadr, serv_addr, sizeof(rbuf.srcadr));
3522b15cb3dSCy Schubert     memcpy(&rbuf.recv_srcadr, serv_addr, sizeof(rbuf.recv_srcadr));
3539c2daa00SOllivier Robert 
3549c2daa00SOllivier Robert     /*
3552b15cb3dSCy Schubert      * Create a packet event and insert it onto the event_queue at the
3562b15cb3dSCy Schubert      * arrival time (t4) of the packet at the client
3579c2daa00SOllivier Robert      */
3582b15cb3dSCy Schubert     e = event(t4, PACKET);
3592b15cb3dSCy Schubert     e->rcv_buf = rbuf;
3602b15cb3dSCy Schubert     enqueue(event_queue, e);
3612b15cb3dSCy Schubert 
3622b15cb3dSCy Schubert     /*
3632b15cb3dSCy Schubert      * Check if the time of the script has expired. If yes, delete it.
3642b15cb3dSCy Schubert      */
3652b15cb3dSCy Schubert     if (curr_script->duration > simulation.sim_time &&
3662b15cb3dSCy Schubert 	NULL == HEAD_PFIFO(server->script)) {
3672b15cb3dSCy Schubert 	printf("Hello\n");
3682b15cb3dSCy Schubert 	/*
3692b15cb3dSCy Schubert 	 * For some reason freeing up the curr_script memory kills the
3702b15cb3dSCy Schubert 	 * simulation. Further debugging is needed to determine why.
3712b15cb3dSCy Schubert 	 * free(curr_script);
3722b15cb3dSCy Schubert 	 */
3732b15cb3dSCy Schubert 	UNLINK_FIFO(curr_script, *server->script, link);
3742b15cb3dSCy Schubert     }
3752b15cb3dSCy Schubert 
3769c2daa00SOllivier Robert     return (0);
3779c2daa00SOllivier Robert }
3789c2daa00SOllivier Robert 
3799c2daa00SOllivier Robert 
3802b15cb3dSCy Schubert /* Define a function to update all the clocks
3812b15cb3dSCy Schubert  * Most of the code is modified from the systime.c file by Prof. Mills
3829c2daa00SOllivier Robert  */
3832b15cb3dSCy Schubert 
sim_update_clocks(Event * e)3842b15cb3dSCy Schubert void sim_update_clocks(Event *e)
3852b15cb3dSCy Schubert {
3862b15cb3dSCy Schubert     double time_gap;
3872b15cb3dSCy Schubert     double adj;
3882b15cb3dSCy Schubert     int i;
3892b15cb3dSCy Schubert 
3902b15cb3dSCy Schubert     /* Compute the time between the last update event and this update */
3912b15cb3dSCy Schubert     time_gap = e->time - simulation.sim_time;
3922b15cb3dSCy Schubert 
3932b15cb3dSCy Schubert     if (time_gap < 0)
3942b15cb3dSCy Schubert 	    printf("WARNING: e->time %.6g comes before sim_time %.6g (gap %+.6g)\n",
3952b15cb3dSCy Schubert 		   e->time, simulation.sim_time, time_gap);
3962b15cb3dSCy Schubert 
3972b15cb3dSCy Schubert     /* Advance the client clock */
3982b15cb3dSCy Schubert     if (e->time + time_gap < simclock.local_time)
3992b15cb3dSCy Schubert 	    printf("WARNING: e->time + gap %.6g comes before local_time %.6g\n",
4002b15cb3dSCy Schubert 		   e->time + time_gap, simclock.local_time);
4012b15cb3dSCy Schubert     simclock.local_time = e->time + time_gap;
4022b15cb3dSCy Schubert 
4032b15cb3dSCy Schubert     /* Advance the simulation time */
4042b15cb3dSCy Schubert     simulation.sim_time = e->time;
4052b15cb3dSCy Schubert 
4062b15cb3dSCy Schubert     /* Advance the server clocks adjusted for systematic and random frequency
4072b15cb3dSCy Schubert      * errors. The random error is a random walk computed as the
4082b15cb3dSCy Schubert      * integral of samples from a Gaussian distribution.
4092b15cb3dSCy Schubert      */
4102b15cb3dSCy Schubert     for (i = 0; i < simulation.num_of_servers; ++i) {
4112b15cb3dSCy Schubert 	simulation.servers[i].curr_script->freq_offset +=
4122b15cb3dSCy Schubert 	    gauss(0, time_gap * simulation.servers[i].curr_script->wander);
4132b15cb3dSCy Schubert 
4142b15cb3dSCy Schubert 	simulation.servers[i].server_time += time_gap *
4152b15cb3dSCy Schubert 	    (1 + simulation.servers[i].curr_script->freq_offset);
4162b15cb3dSCy Schubert     }
4172b15cb3dSCy Schubert 
4182b15cb3dSCy Schubert     /* Perform the adjtime() function. If the adjustment completed
4192b15cb3dSCy Schubert      * in the previous interval, amortize the entire amount; if not,
4202b15cb3dSCy Schubert      * carry the leftover to the next interval.
4212b15cb3dSCy Schubert      */
4222b15cb3dSCy Schubert 
4232b15cb3dSCy Schubert     adj = time_gap * simclock.slew;
4242b15cb3dSCy Schubert     if (adj < fabs(simclock.adj)) {
4252b15cb3dSCy Schubert 	if (simclock.adj < 0) {
4262b15cb3dSCy Schubert 	    simclock.adj += adj;
4272b15cb3dSCy Schubert 	    simclock.local_time -= adj;
4282b15cb3dSCy Schubert 	} else {
4292b15cb3dSCy Schubert 	    simclock.adj -= adj;
4302b15cb3dSCy Schubert 	    simclock.local_time += adj;
4312b15cb3dSCy Schubert 	}
4322b15cb3dSCy Schubert     } else {
4332b15cb3dSCy Schubert 	simclock.local_time += simclock.adj;
4342b15cb3dSCy Schubert 	simclock.adj = 0;
4352b15cb3dSCy Schubert     }
4362b15cb3dSCy Schubert }
4372b15cb3dSCy Schubert 
4382b15cb3dSCy Schubert 
4392b15cb3dSCy Schubert /* Define a function that processes a receive packet event.
4402b15cb3dSCy Schubert  * This function simply inserts the packet received onto the receive queue
4412b15cb3dSCy Schubert  */
4422b15cb3dSCy Schubert 
sim_event_recv_packet(Event * e)4432b15cb3dSCy Schubert void sim_event_recv_packet(Event *e)
4449c2daa00SOllivier Robert {
4459c2daa00SOllivier Robert     struct recvbuf *rbuf;
4469c2daa00SOllivier Robert 
4472b15cb3dSCy Schubert     /* Allocate a receive buffer and copy the packet to it */
4482b15cb3dSCy Schubert     if ((rbuf = get_node(sizeof(*rbuf))) == NULL)
4492b15cb3dSCy Schubert 	abortsim("get_node failed in sim_event_recv_packet");
4502b15cb3dSCy Schubert     memcpy(rbuf, &e->rcv_buf, sizeof(*rbuf));
4519c2daa00SOllivier Robert 
4522b15cb3dSCy Schubert     /* Store the local time in the received packet */
4532b15cb3dSCy Schubert     DTOLFP(simclock.local_time, &rbuf->recv_time);
4542b15cb3dSCy Schubert 
4552b15cb3dSCy Schubert     /* Insert the packet received onto the receive queue */
4562b15cb3dSCy Schubert     enqueue(recv_queue, rbuf);
4579c2daa00SOllivier Robert }
4589c2daa00SOllivier Robert 
4599c2daa00SOllivier Robert 
4602b15cb3dSCy Schubert 
4612b15cb3dSCy Schubert /* Define a function to output simulation statistics on a beep event
4629c2daa00SOllivier Robert  */
4632b15cb3dSCy Schubert 
4642b15cb3dSCy Schubert /*** TODO: Need to decide on how to output for multiple servers ***/
sim_event_beep(Event * e)4652b15cb3dSCy Schubert void sim_event_beep(Event *e)
4669c2daa00SOllivier Robert {
4672b15cb3dSCy Schubert #if 0
4689c2daa00SOllivier Robert     static int first_time = 1;
4699c2daa00SOllivier Robert     char *dash = "-----------------";
4702b15cb3dSCy Schubert #endif
4719c2daa00SOllivier Robert 
4722b15cb3dSCy Schubert     fprintf(stderr, "BEEP!!!\n");
4732b15cb3dSCy Schubert     enqueue(event_queue, event(e->time + simulation.beep_delay, BEEP));
4742b15cb3dSCy Schubert #if 0
4752b15cb3dSCy Schubert     if(simulation.beep_delay > 0) {
4769c2daa00SOllivier Robert 	if (first_time) {
4772b15cb3dSCy Schubert 	    printf("\t%4c    T    %4c\t%4c  T+ERR  %3c\t%5cT+ERR+NTP\n",
4782b15cb3dSCy Schubert 	           ' ', ' ', ' ', ' ',' ');
4799c2daa00SOllivier Robert 	    printf("\t%s\t%s\t%s\n", dash, dash, dash);
4809c2daa00SOllivier Robert 	    first_time = 0;
4812b15cb3dSCy Schubert 
4829c2daa00SOllivier Robert 	    printf("\t%16.6f\t%16.6f\t%16.6f\n",
4839c2daa00SOllivier Robert 	           n->time, n->clk_time, n->ntp_time);
4849c2daa00SOllivier Robert 	    return;
4859c2daa00SOllivier Robert 	}
4869c2daa00SOllivier Robert 	printf("\t%16.6f\t%16.6f\t%16.6f\n",
4872b15cb3dSCy Schubert 	       simclock.local_time,
4889c2daa00SOllivier Robert 	       n->time, n->clk_time, n->ntp_time);
4892b15cb3dSCy Schubert #endif
4902b15cb3dSCy Schubert 
4919c2daa00SOllivier Robert }
4929c2daa00SOllivier Robert 
4939c2daa00SOllivier Robert 
4942b15cb3dSCy Schubert /* Define a function to abort the simulation on an error and spit out an
4952b15cb3dSCy Schubert  * error message
4969c2daa00SOllivier Robert  */
4972b15cb3dSCy Schubert 
4982b15cb3dSCy Schubert void abortsim(char *errmsg)
4999c2daa00SOllivier Robert {
5009c2daa00SOllivier Robert     perror(errmsg);
5019c2daa00SOllivier Robert     exit(1);
5029c2daa00SOllivier Robert }
5032b15cb3dSCy Schubert 
5042b15cb3dSCy Schubert 
5052b15cb3dSCy Schubert 
5062b15cb3dSCy Schubert /* CODE ORIGINALLY IN libntp/systime.c
5072b15cb3dSCy Schubert  * -----------------------------------
5082b15cb3dSCy Schubert  * This code was a part of the original NTP simulator and originally
5092b15cb3dSCy Schubert  * had its home in the libntp/systime.c file.
5102b15cb3dSCy Schubert  *
5112b15cb3dSCy Schubert  * It has been shamelessly moved to here and has been modified for the
5122b15cb3dSCy Schubert  * purposes of the current simulator.
5132b15cb3dSCy Schubert  */
5142b15cb3dSCy Schubert 
5152b15cb3dSCy Schubert 
5162b15cb3dSCy Schubert /*
5172b15cb3dSCy Schubert  * get_systime - return the system time in NTP timestamp format
5182b15cb3dSCy Schubert  */
5192b15cb3dSCy Schubert void
5202b15cb3dSCy Schubert get_systime(
5212b15cb3dSCy Schubert     l_fp *now		/* current system time in l_fp */        )
5222b15cb3dSCy Schubert {
5232b15cb3dSCy Schubert     /*
5242b15cb3dSCy Schubert      * To fool the code that determines the local clock precision,
5252b15cb3dSCy Schubert      * we advance the clock a minimum of 200 nanoseconds on every
5262b15cb3dSCy Schubert      * clock read. This is appropriate for a typical modern machine
5272b15cb3dSCy Schubert      * with nanosecond clocks. Note we make no attempt here to
5282b15cb3dSCy Schubert      * simulate reading error, since the error is so small. This may
5292b15cb3dSCy Schubert      * change when the need comes to implement picosecond clocks.
5302b15cb3dSCy Schubert      */
5312b15cb3dSCy Schubert     if (simclock.local_time == simclock.last_read_time)
5322b15cb3dSCy Schubert         simclock.local_time += 200e-9;
5332b15cb3dSCy Schubert 
5342b15cb3dSCy Schubert     simclock.last_read_time = simclock.local_time;
5352b15cb3dSCy Schubert     DTOLFP(simclock.local_time, now);
5362b15cb3dSCy Schubert /* OLD Code
5372b15cb3dSCy Schubert    if (ntp_node.ntp_time == ntp_node.last_time)
5382b15cb3dSCy Schubert    ntp_node.ntp_time += 200e-9;
5392b15cb3dSCy Schubert    ntp_node.last_time = ntp_node.ntp_time;
5402b15cb3dSCy Schubert    DTOLFP(ntp_node.ntp_time, now);
5412b15cb3dSCy Schubert */
5422b15cb3dSCy Schubert }
5432b15cb3dSCy Schubert 
5442b15cb3dSCy Schubert 
5452b15cb3dSCy Schubert /*
5462b15cb3dSCy Schubert  * adj_systime - advance or retard the system clock exactly like the
5472b15cb3dSCy Schubert  * real thng.
5482b15cb3dSCy Schubert  */
5492b15cb3dSCy Schubert int				/* always succeeds */
5502b15cb3dSCy Schubert adj_systime(
5512b15cb3dSCy Schubert     double now		/* time adjustment (s) */
5522b15cb3dSCy Schubert     )
5532b15cb3dSCy Schubert {
5542b15cb3dSCy Schubert     struct timeval adjtv;	/* new adjustment */
5552b15cb3dSCy Schubert     double	dtemp;
5562b15cb3dSCy Schubert     long	ticks;
5572b15cb3dSCy Schubert     int	isneg = 0;
5582b15cb3dSCy Schubert 
5592b15cb3dSCy Schubert     /*
5602b15cb3dSCy Schubert      * Most Unix adjtime() implementations adjust the system clock
5612b15cb3dSCy Schubert      * in microsecond quanta, but some adjust in 10-ms quanta. We
5622b15cb3dSCy Schubert      * carefully round the adjustment to the nearest quantum, then
5632b15cb3dSCy Schubert      * adjust in quanta and keep the residue for later.
5642b15cb3dSCy Schubert      */
5652b15cb3dSCy Schubert     dtemp = now + sys_residual;
5662b15cb3dSCy Schubert     if (dtemp < 0) {
5672b15cb3dSCy Schubert 	isneg = 1;
5682b15cb3dSCy Schubert 	dtemp = -dtemp;
5692b15cb3dSCy Schubert     }
5702b15cb3dSCy Schubert     adjtv.tv_sec = (long)dtemp;
5712b15cb3dSCy Schubert     dtemp -= adjtv.tv_sec;
5722b15cb3dSCy Schubert     ticks = (long)(dtemp / sys_tick + .5);
5732b15cb3dSCy Schubert     adjtv.tv_usec = (long)(ticks * sys_tick * 1e6);
5742b15cb3dSCy Schubert     dtemp -= adjtv.tv_usec / 1e6;
5752b15cb3dSCy Schubert     sys_residual = dtemp;
5762b15cb3dSCy Schubert 
5772b15cb3dSCy Schubert     /*
5782b15cb3dSCy Schubert      * Convert to signed seconds and microseconds for the Unix
5792b15cb3dSCy Schubert      * adjtime() system call. Note we purposely lose the adjtime()
5802b15cb3dSCy Schubert      * leftover.
5812b15cb3dSCy Schubert      */
5822b15cb3dSCy Schubert     if (isneg) {
5832b15cb3dSCy Schubert 	adjtv.tv_sec = -adjtv.tv_sec;
5842b15cb3dSCy Schubert 	adjtv.tv_usec = -adjtv.tv_usec;
5852b15cb3dSCy Schubert 	sys_residual = -sys_residual;
5862b15cb3dSCy Schubert     }
5872b15cb3dSCy Schubert     simclock.adj = now;
5882b15cb3dSCy Schubert /*	ntp_node.adj = now; */
5892b15cb3dSCy Schubert     return (1);
5902b15cb3dSCy Schubert }
5912b15cb3dSCy Schubert 
5922b15cb3dSCy Schubert 
5932b15cb3dSCy Schubert /*
5942b15cb3dSCy Schubert  * step_systime - step the system clock. We are religious here.
5952b15cb3dSCy Schubert  */
5962b15cb3dSCy Schubert int				/* always succeeds */
5972b15cb3dSCy Schubert step_systime(
5982b15cb3dSCy Schubert     double now		/* step adjustment (s) */
5992b15cb3dSCy Schubert     )
6002b15cb3dSCy Schubert {
6012b15cb3dSCy Schubert #ifdef DEBUG
6022b15cb3dSCy Schubert     if (debug)
6032b15cb3dSCy Schubert 	printf("step_systime: time %.6f adj %.6f\n",
6042b15cb3dSCy Schubert 	       simclock.local_time, now);
6052b15cb3dSCy Schubert #endif
6062b15cb3dSCy Schubert     simclock.local_time += now;
6072b15cb3dSCy Schubert     return (1);
6082b15cb3dSCy Schubert }
6092b15cb3dSCy Schubert 
6102b15cb3dSCy Schubert /*
6112b15cb3dSCy Schubert  * gauss() - returns samples from a gaussion distribution
6122b15cb3dSCy Schubert  */
6132b15cb3dSCy Schubert double				/* Gaussian sample */
6142b15cb3dSCy Schubert gauss(
6152b15cb3dSCy Schubert     double m,		/* sample mean */
6162b15cb3dSCy Schubert     double s		/* sample standard deviation (sigma) */
6172b15cb3dSCy Schubert     )
6182b15cb3dSCy Schubert {
6192b15cb3dSCy Schubert     double q1, q2;
6202b15cb3dSCy Schubert 
6212b15cb3dSCy Schubert     /*
6222b15cb3dSCy Schubert      * Roll a sample from a Gaussian distribution with mean m and
6232b15cb3dSCy Schubert      * standard deviation s. For m = 0, s = 1, mean(y) = 0,
6242b15cb3dSCy Schubert      * std(y) = 1.
6252b15cb3dSCy Schubert      */
6262b15cb3dSCy Schubert     if (s == 0)
6272b15cb3dSCy Schubert         return (m);
6282b15cb3dSCy Schubert     while ((q1 = drand48()) == 0)
6292b15cb3dSCy Schubert 	/* empty statement */;
6302b15cb3dSCy Schubert     q2 = drand48();
6312b15cb3dSCy Schubert     return (m + s * sqrt(-2. * log(q1)) * cos(2. * PI * q2));
6322b15cb3dSCy Schubert }
6332b15cb3dSCy Schubert 
6342b15cb3dSCy Schubert 
6352b15cb3dSCy Schubert /*
6362b15cb3dSCy Schubert  * poisson() - returns samples from a network delay distribution
6372b15cb3dSCy Schubert  */
6382b15cb3dSCy Schubert double				/* delay sample (s) */
6392b15cb3dSCy Schubert poisson(
6402b15cb3dSCy Schubert     double m,		/* fixed propagation delay (s) */
6412b15cb3dSCy Schubert     double s		/* exponential parameter (mu) */
6422b15cb3dSCy Schubert     )
6432b15cb3dSCy Schubert {
6442b15cb3dSCy Schubert     double q1;
6452b15cb3dSCy Schubert 
6462b15cb3dSCy Schubert     /*
6472b15cb3dSCy Schubert      * Roll a sample from a composite distribution with propagation
6482b15cb3dSCy Schubert      * delay m and exponential distribution time with parameter s.
6492b15cb3dSCy Schubert      * For m = 0, s = 1, mean(y) = std(y) = 1.
6502b15cb3dSCy Schubert      */
6512b15cb3dSCy Schubert     if (s == 0)
6522b15cb3dSCy Schubert         return (m);
6532b15cb3dSCy Schubert     while ((q1 = drand48()) == 0)
6542b15cb3dSCy Schubert 	/* empty statement */;
6552b15cb3dSCy Schubert     return (m - s * log(q1 * s));
6562b15cb3dSCy Schubert }
6572b15cb3dSCy Schubert 
6582b15cb3dSCy Schubert #endif
659