xref: /freebsd/contrib/ntp/ntpd/ntpsim.c (revision 2be1a816)
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  */
20 void (*funcPtr[]) (Node *, Event) = {
21 	&ndbeep, &ndeclk, &ntptmr, &netpkt
22 };
23 
24 
25 /*
26  * ntpsim - initialize global variables and event queue and start
27  */
28 int
29 ntpsim(
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  */
107 Event
108 event(
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  */
123 Queue
124 queue(
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  */
142 void 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  */
158 Event
159 pop(
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  */
179 void
180 ndeclk(
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  */
193 void
194 ntptmr(
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  */
224 int 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  */
291 void
292 netpkt(
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  */
330 void
331 ndbeep(
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  */
361 void
362 abortsim(
363 	char *errmsg
364 	)
365 {
366         perror(errmsg);
367         exit(1);
368 }
369