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