ntpsim.c revision 132452
1/*
2 * NTP simulator engine - Harish Nair
3 * University of Delaware, 2001
4 */
5#include "ntpd.h"
6#include "ntpsim.h"
7
8/*
9 * Defines...
10 */
11#define SIM_TIME 86400		/* end simulation time */
12#define NET_DLY .001            /* network delay */
13#define PROC_DLY .001		/* processing delay */
14#define BEEP_DLY 3600           /* beep interval (s) */
15#define	SLEW	500e-6		/* correction rate (PPM) */
16
17/*
18 * Function pointers
19 */
20void (*funcPtr[]) (Node *, Event) = {
21	&ndbeep, &ndeclk, &ntptmr, &netpkt
22};
23
24
25/*
26 * ntpsim - initialize global variables and event queue and start
27 */
28int
29ntpsim(
30	int	argc,
31	char	*argv[]
32	)
33{
34	Event	e;
35	double	maxtime;
36	struct timeval seed;
37
38	/*
39	 * Initialize the global node
40	 */
41	ntp_node.time = 0;		/* simulation time */
42	ntp_node.sim_time = SIM_TIME;	/* end simulation time (-S) */
43	ntp_node.ntp_time = 0;		/* client disciplined time */
44	ntp_node.adj = 0;		/* remaining time correction */
45	ntp_node.slew = SLEW;		/* correction rate (-H) */
46
47	ntp_node.clk_time = 0;		/* server time (-O) */
48	ntp_node.ferr = 0;		/* frequency error (-T) */
49	ntp_node.fnse = 0;		/* random walk noise (-W) */
50	ntp_node.ndly = NET_DLY;	/* network delay (-Y) */
51	ntp_node.snse = 0;		/* phase noise (-C) */
52	ntp_node.pdly = PROC_DLY;	/* processing delay (-Z) */
53	ntp_node.bdly = BEEP_DLY;	/* beep interval (-B) */
54
55	ntp_node.events = NULL;
56	ntp_node.rbuflist = NULL;
57
58	/*
59	 * Initialize ntp variables
60	 */
61	initializing = 1;
62        init_auth();
63        init_util();
64        init_restrict();
65        init_mon();
66        init_timer();
67        init_lib();
68        init_random();
69        init_request();
70        init_control();
71        init_peer();
72        init_proto();
73        init_io();
74        init_loopfilter();
75        mon_start(MON_OFF);
76	getconfig(argc, argv);
77        initializing = 0;
78
79	/*
80	 * Watch out here, we want the real time, not the silly stuff.
81	 */
82	gettimeofday(&seed, NULL);
83	srand48(seed.tv_usec);
84
85	/*
86	 * Push a beep and timer interrupt on the queue
87	 */
88	push(event(0, BEEP), &ntp_node.events);
89	push(event(ntp_node.time + 1.0, TIMER), &ntp_node.events);
90
91	/*
92	 * Pop the queue until nothing is left or time is exceeded
93	 */
94	maxtime = ntp_node.time + ntp_node.sim_time;
95	while (ntp_node.time <= maxtime && ntp_node.events != NULL ) {
96		e = pop(&ntp_node.events);
97		ndeclk(&ntp_node, e);
98		funcPtr[e.function](&ntp_node, e);
99	}
100	return (0);
101}
102
103
104/*
105 * Return an event
106 */
107Event
108event(
109	double t,
110	funcTkn f
111	)
112{
113	Event e;
114
115	e.time = t;
116	e.function = f;
117	return (e);
118}
119
120/*
121 * Create an event queue
122 */
123Queue
124queue(
125	Event e,
126	Queue q
127	)
128{
129	Queue ret;
130
131	if ((ret = (Queue)malloc(sizeof(struct List))) == NULL)
132                abortsim("queue-malloc");
133	ret->event = e;
134	ret->next = q;
135	return (ret);
136}
137
138
139/*
140 * Push an event into the event queue
141 */
142void push(
143	Event e,
144	Queue *qp
145	)
146{
147	Queue *tmp = qp;
148
149	while (*tmp != NULL && ((*tmp)->event.time < e.time))
150		tmp = &((*tmp)->next);
151	*tmp = queue(e, (*tmp));
152}
153
154
155/*
156 * Pop the first event from the event queue
157 */
158Event
159pop(
160	Queue *qp
161	)
162{
163	Event ret;
164	Queue tmp;
165
166	tmp = *qp;
167	if (tmp == NULL)
168	    abortsim("pop - empty queue");
169	ret = tmp->event;
170	*qp = tmp->next;
171	free(tmp);
172	return (ret);
173}
174
175
176/*
177 * Update clocks
178 */
179void
180ndeclk(
181	Node *n,
182	Event e
183	)
184{
185	node_clock(n, e.time);
186}
187
188
189/*
190 * Timer interrupt. Eventually, this results in calling the
191 * srvr_rplyi() routine below.
192 */
193void
194ntptmr(
195	Node *n,
196	Event e
197	)
198{
199	struct recvbuf *rbuf;
200
201	timer();
202
203	/*
204	 * Process buffers received. They had better be in order by
205	 * receive timestamp.
206	 */
207	while (n->rbuflist != NULL) {
208		rbuf = n->rbuflist;
209		n->rbuflist = rbuf->next;
210		(rbuf->receiver)(rbuf);
211		free(rbuf);
212	}
213
214	/*
215	 * Arm the next timer interrupt.
216	 */
217	push(event(e.time + (1 << EVENT_TIMEOUT), TIMER), &n->events);
218}
219
220
221/*
222 * srvr_rply() - send packet
223 */
224int srvr_rply(
225	Node *n,
226	struct sockaddr_storage *dest,
227	struct interface *inter, struct pkt *rpkt
228	)
229{
230	struct pkt xpkt;
231	struct recvbuf rbuf;
232	Event   xvnt;
233	double	dtemp, etemp;
234
235	/*
236	 * Insert packet header values. We make this look like a
237	 * stratum-1 server with a GPS clock, but nobody will ever
238	 * notice that.
239	 */
240	xpkt.li_vn_mode = PKT_LI_VN_MODE(LEAP_NOWARNING, NTP_VERSION,
241	    MODE_SERVER);
242	xpkt.stratum = STRATUM_TO_PKT(((u_char)1));
243	memcpy(&xpkt.refid, "GPS", 4);
244	xpkt.ppoll = rpkt->ppoll;
245        xpkt.precision = rpkt->precision;
246        xpkt.rootdelay = 0;
247        xpkt.rootdispersion = 0;
248
249	/*
250	 * Insert the timestamps.
251	 */
252        xpkt.org = rpkt->xmt;
253	dtemp = poisson(n->ndly, n->snse); /* client->server delay */
254	DTOLFP(dtemp + n->clk_time, &xpkt.rec);
255	dtemp += poisson(n->pdly, 0);	/* server delay */
256	DTOLFP(dtemp + n->clk_time, &xpkt.xmt);
257	xpkt.reftime = xpkt.xmt;
258	dtemp += poisson(n->ndly, n->snse); /* server->client delay */
259
260	/*
261	 * Insert the I/O stuff.
262	 */
263	rbuf.receiver = receive;
264        get_systime(&rbuf.recv_time);
265        rbuf.recv_length = LEN_PKT_NOMAC;
266        rbuf.recv_pkt = xpkt;
267        memcpy(&rbuf.srcadr, dest, sizeof(struct sockaddr_storage));
268        memcpy(&rbuf.recv_srcadr, dest,
269	    sizeof(struct sockaddr_storage));
270        if ((rbuf.dstadr = malloc(sizeof(struct interface))) == NULL)
271		abortsim("server-malloc");
272        memcpy(rbuf.dstadr, inter, sizeof(struct interface));
273        rbuf.next = NULL;
274
275	/*
276	 * Very carefully predict the time of arrival for the received
277	 * packet.
278	 */
279	LFPTOD(&xpkt.org, etemp);
280	etemp += dtemp;
281	xvnt = event(etemp, PACKET);
282	xvnt.rcv_buf = rbuf;
283	push(xvnt, &n->events);
284	return (0);
285}
286
287
288/*
289 * netpkt() - receive packet
290 */
291void
292netpkt(
293	Node *n,
294	Event e
295	)
296{
297	struct recvbuf *rbuf;
298	struct recvbuf *obuf;
299
300	/*
301	 * Insert the packet on the receive queue and record the arrival
302	 * time.
303	 */
304	if ((rbuf = malloc(sizeof(struct recvbuf))) == NULL)
305		abortsim("ntprcv-malloc");
306	memcpy(rbuf, &e.rcv_buf, sizeof(struct recvbuf));
307	rbuf->receiver = receive;
308	DTOLFP(n->ntp_time, &rbuf->recv_time);
309	rbuf->next = NULL;
310	obuf = n->rbuflist;
311
312	/*
313	 * In the present incarnation, no more than one buffer can be on
314	 * the queue; however, we sniff the queue anyway as a hint for
315	 * further development.
316	 */
317	if (obuf == NULL) {
318		n->rbuflist = rbuf;
319	} else {
320		while (obuf->next != NULL)
321			obuf = obuf->next;
322		obuf->next = rbuf;
323	}
324}
325
326
327/*
328 * ndbeep() - progress indicator
329 */
330void
331ndbeep(
332	Node *n,
333	Event e
334	)
335{
336	static int first_time = 1;
337	char *dash = "-----------------";
338
339	if(n->bdly > 0) {
340		if (first_time) {
341			printf(
342			    "\t%4c    T    %4c\t%4c  T+ERR  %3c\t%5cT+ERR+NTP\n", ' ', ' ', ' ', ' ',' ');
343			printf("\t%s\t%s\t%s\n", dash, dash, dash);
344			first_time = 0;
345			push(event(n->bdly, BEEP), &n->events);
346        		push(event(n->sim_time, BEEP), &n->events);
347			printf("\t%16.6f\t%16.6f\t%16.6f\n",
348                            n->time, n->clk_time, n->ntp_time);
349			return;
350		}
351		printf("\t%16.6f\t%16.6f\t%16.6f\n",
352		    n->time, n->clk_time, n->ntp_time);
353		push(event(e.time + n->bdly, BEEP), &n->events);
354	}
355}
356
357
358/*
359 * Abort simulation
360 */
361void
362abortsim(
363	char *errmsg
364	)
365{
366        perror(errmsg);
367        exit(1);
368}
369