xref: /netbsd/external/bsd/ntp/dist/ntpd/ntp_timer.c (revision 6550d01e)
1 /*	$NetBSD: ntp_timer.c,v 1.1.1.1 2009/12/13 16:55:41 kardel Exp $	*/
2 
3 /*
4  * ntp_timer.c - event timer support routines
5  */
6 #ifdef HAVE_CONFIG_H
7 # include <config.h>
8 #endif
9 
10 #include "ntp_machine.h"
11 #include "ntpd.h"
12 #include "ntp_stdlib.h"
13 
14 #include <stdio.h>
15 #include <signal.h>
16 #ifdef HAVE_SYS_SIGNAL_H
17 # include <sys/signal.h>
18 #endif
19 #ifdef HAVE_UNISTD_H
20 # include <unistd.h>
21 #endif
22 
23 #if defined(HAVE_IO_COMPLETION_PORT)
24 # include "ntp_iocompletionport.h"
25 # include "ntp_timer.h"
26 #endif
27 
28 #ifdef KERNEL_PLL
29 #include "ntp_syscall.h"
30 #endif /* KERNEL_PLL */
31 
32 #ifdef OPENSSL
33 #include <openssl/rand.h>
34 #endif /* OPENSSL */
35 
36 /*
37  * These routines provide support for the event timer.	The timer is
38  * implemented by an interrupt routine which sets a flag once every
39  * 2**EVENT_TIMEOUT seconds (currently 4), and a timer routine which
40  * is called when the mainline code gets around to seeing the flag.
41  * The timer routine dispatches the clock adjustment code if its time
42  * has come, then searches the timer queue for expiries which are
43  * dispatched to the transmit procedure.  Finally, we call the hourly
44  * procedure to do cleanup and print a message.
45  */
46 volatile int interface_interval = 300;     /* update interface every 5 minutes as default */
47 
48 /*
49  * Alarm flag. The mainline code imports this.
50  */
51 volatile int alarm_flag;
52 
53 /*
54  * The counters and timeouts
55  */
56 static  u_long interface_timer;	/* interface update timer */
57 static	u_long adjust_timer;	/* second timer */
58 static	u_long stats_timer;	/* stats timer */
59 static	u_long huffpuff_timer;	/* huff-n'-puff timer */
60 u_long	leapsec;		/* leapseconds countdown */
61 l_fp	sys_time;		/* current system time */
62 #ifdef OPENSSL
63 static	u_long revoke_timer;	/* keys revoke timer */
64 static	u_long keys_timer;	/* session key timer */
65 u_long	sys_revoke = KEY_REVOKE; /* keys revoke timeout (log2 s) */
66 u_long	sys_automax = NTP_AUTOMAX; /* key list timeout (log2 s) */
67 #endif /* OPENSSL */
68 
69 /*
70  * Statistics counter for the interested.
71  */
72 volatile u_long alarm_overflow;
73 
74 #define MINUTE	60
75 #define HOUR	(60 * MINUTE)
76 #define	DAY	(24 * HOUR)
77 
78 u_long current_time;		/* seconds since startup */
79 
80 /*
81  * Stats.  Number of overflows and number of calls to transmit().
82  */
83 u_long timer_timereset;
84 u_long timer_overflows;
85 u_long timer_xmtcalls;
86 
87 #if defined(VMS)
88 static int vmstimer[2]; 	/* time for next timer AST */
89 static int vmsinc[2];		/* timer increment */
90 #endif /* VMS */
91 
92 #if defined SYS_WINNT
93 static HANDLE WaitableTimerHandle = NULL;
94 #else
95 static	RETSIGTYPE alarming (int);
96 #endif /* SYS_WINNT */
97 
98 #if !defined(VMS)
99 # if !defined SYS_WINNT || defined(SYS_CYGWIN32)
100 #  ifndef HAVE_TIMER_SETTIME
101 	struct itimerval itimer;
102 #  else
103 	static timer_t ntpd_timerid;
104 	struct itimerspec itimer;
105 #  endif /* HAVE_TIMER_SETTIME */
106 # endif /* SYS_WINNT */
107 #endif /* VMS */
108 
109 /*
110  * reinit_timer - reinitialize interval timer.
111  */
112 void
113 reinit_timer(void)
114 {
115 #if !defined(SYS_WINNT) && !defined(VMS)
116 #  if defined(HAVE_TIMER_CREATE) && defined(HAVE_TIMER_SETTIME)
117 	timer_gettime(ntpd_timerid, &itimer);
118 	if (itimer.it_value.tv_sec < 0 || itimer.it_value.tv_sec > (1<<EVENT_TIMEOUT)) {
119 		itimer.it_value.tv_sec = (1<<EVENT_TIMEOUT);
120 	}
121 	if (itimer.it_value.tv_nsec < 0 ) {
122 		itimer.it_value.tv_nsec = 0;
123 	}
124 	if (itimer.it_value.tv_sec == 0 && itimer.it_value.tv_nsec == 0) {
125 		itimer.it_value.tv_sec = (1<<EVENT_TIMEOUT);
126 		itimer.it_value.tv_nsec = 0;
127 	}
128 	itimer.it_interval.tv_sec = (1<<EVENT_TIMEOUT);
129 	itimer.it_interval.tv_nsec = 0;
130 	timer_settime(ntpd_timerid, 0 /*!TIMER_ABSTIME*/, &itimer, NULL);
131 #  else
132 	getitimer(ITIMER_REAL, &itimer);
133 	if (itimer.it_value.tv_sec < 0 || itimer.it_value.tv_sec > (1<<EVENT_TIMEOUT)) {
134 		itimer.it_value.tv_sec = (1<<EVENT_TIMEOUT);
135 	}
136 	if (itimer.it_value.tv_usec < 0 ) {
137 		itimer.it_value.tv_usec = 0;
138 	}
139 	if (itimer.it_value.tv_sec == 0 && itimer.it_value.tv_usec == 0) {
140 		itimer.it_value.tv_sec = (1<<EVENT_TIMEOUT);
141 		itimer.it_value.tv_usec = 0;
142 	}
143 	itimer.it_interval.tv_sec = (1<<EVENT_TIMEOUT);
144 	itimer.it_interval.tv_usec = 0;
145 	setitimer(ITIMER_REAL, &itimer, (struct itimerval *)0);
146 #  endif
147 # endif /* VMS */
148 }
149 
150 /*
151  * init_timer - initialize the timer data structures
152  */
153 void
154 init_timer(void)
155 {
156 	/*
157 	 * Initialize...
158 	 */
159 	alarm_flag = 0;
160 	alarm_overflow = 0;
161 	adjust_timer = 1;
162 	stats_timer = 0;
163 	huffpuff_timer = 0;
164 	interface_timer = 0;
165 	current_time = 0;
166 	timer_overflows = 0;
167 	timer_xmtcalls = 0;
168 	timer_timereset = 0;
169 
170 #if !defined(SYS_WINNT)
171 	/*
172 	 * Set up the alarm interrupt.	The first comes 2**EVENT_TIMEOUT
173 	 * seconds from now and they continue on every 2**EVENT_TIMEOUT
174 	 * seconds.
175 	 */
176 # if !defined(VMS)
177 #  if defined(HAVE_TIMER_CREATE) && defined(HAVE_TIMER_SETTIME)
178 	if (timer_create (CLOCK_REALTIME, NULL, &ntpd_timerid) ==
179 #	ifdef SYS_VXWORKS
180 		ERROR
181 #	else
182 		-1
183 #	endif
184 	   )
185 	{
186 		fprintf (stderr, "timer create FAILED\n");
187 		exit (0);
188 	}
189 	(void) signal_no_reset(SIGALRM, alarming);
190 	itimer.it_interval.tv_sec = itimer.it_value.tv_sec = (1<<EVENT_TIMEOUT);
191 	itimer.it_interval.tv_nsec = itimer.it_value.tv_nsec = 0;
192 	timer_settime(ntpd_timerid, 0 /*!TIMER_ABSTIME*/, &itimer, NULL);
193 #  else
194 	(void) signal_no_reset(SIGALRM, alarming);
195 	itimer.it_interval.tv_sec = itimer.it_value.tv_sec = (1<<EVENT_TIMEOUT);
196 	itimer.it_interval.tv_usec = itimer.it_value.tv_usec = 0;
197 	setitimer(ITIMER_REAL, &itimer, (struct itimerval *)0);
198 #  endif
199 # else /* VMS */
200 	vmsinc[0] = 10000000;		/* 1 sec */
201 	vmsinc[1] = 0;
202 	lib$emul(&(1<<EVENT_TIMEOUT), &vmsinc, &0, &vmsinc);
203 
204 	sys$gettim(&vmstimer);	/* that's "now" as abstime */
205 
206 	lib$addx(&vmsinc, &vmstimer, &vmstimer);
207 	sys$setimr(0, &vmstimer, alarming, alarming, 0);
208 # endif /* VMS */
209 #else /* SYS_WINNT */
210 	/*
211 	 * Set up timer interrupts for every 2**EVENT_TIMEOUT seconds
212 	 * Under Windows/NT,
213 	 */
214 
215 	WaitableTimerHandle = CreateWaitableTimer(NULL, FALSE, NULL);
216 	if (WaitableTimerHandle == NULL) {
217 		msyslog(LOG_ERR, "CreateWaitableTimer failed: %m");
218 		exit(1);
219 	}
220 	else {
221 		DWORD Period = (1<<EVENT_TIMEOUT) * 1000;
222 		LARGE_INTEGER DueTime;
223 		DueTime.QuadPart = Period * 10000i64;
224 		if (!SetWaitableTimer(WaitableTimerHandle, &DueTime, Period, NULL, NULL, FALSE) != NO_ERROR) {
225 			msyslog(LOG_ERR, "SetWaitableTimer failed: %m");
226 			exit(1);
227 		}
228 	}
229 
230 #endif /* SYS_WINNT */
231 }
232 
233 #if defined(SYS_WINNT)
234 extern HANDLE
235 get_timer_handle(void)
236 {
237 	return WaitableTimerHandle;
238 }
239 #endif
240 
241 /*
242  * timer - event timer
243  */
244 void
245 timer(void)
246 {
247 	register struct peer *peer, *next_peer;
248 	u_int	n;
249 
250 	/*
251 	 * The basic timerevent is one second. This is used to adjust
252 	 * the system clock in time and frequency, implement the
253 	 * kiss-o'-deatch function and implement the association
254 	 * polling function..
255 	 */
256 	current_time++;
257 	get_systime(&sys_time);
258 	if (adjust_timer <= current_time) {
259 		adjust_timer += 1;
260 		adj_host_clock();
261 #ifdef REFCLOCK
262 		for (n = 0; n < NTP_HASH_SIZE; n++) {
263 			for (peer = peer_hash[n]; peer != 0; peer = next_peer) {
264 				next_peer = peer->next;
265 				if (peer->flags & FLAG_REFCLOCK)
266 					refclock_timer(peer);
267 			}
268 		}
269 #endif /* REFCLOCK */
270 	}
271 
272 	/*
273 	 * Now dispatch any peers whose event timer has expired. Be
274 	 * careful here, since the peer structure might go away as the
275 	 * result of the call.
276 	 */
277 	for (n = 0; n < NTP_HASH_SIZE; n++) {
278 		for (peer = peer_hash[n]; peer != 0; peer = next_peer) {
279 			next_peer = peer->next;
280 			if (peer->action && peer->nextaction <=
281 			    current_time)
282 				peer->action(peer);
283 
284 			/*
285 			 * Restrain the non-burst packet rate not more
286 			 * than one packet every 16 seconds. This is
287 			 * usually tripped using iburst and minpoll of
288 			 * 128 s or less.
289 			 */
290 			if (peer->throttle > 0)
291 				peer->throttle--;
292 			if (peer->nextdate <= current_time) {
293 #ifdef REFCLOCK
294 				if (peer->flags & FLAG_REFCLOCK)
295 					refclock_transmit(peer);
296 				else
297 					transmit(peer);
298 #else /* REFCLOCK */
299 				transmit(peer);
300 #endif /* REFCLOCK */
301 			}
302 		}
303 	}
304 
305 	/*
306 	 * Orphan mode is active when enabled and when no servers less
307 	 * than the orphan statum are available. A server with no other
308 	 * synchronization source is an orphan It shows offset zero and
309 	 * reference ID the loopback address.
310 	 */
311 	if (sys_orphan < STRATUM_UNSPEC && sys_peer == NULL) {
312 		if (sys_leap == LEAP_NOTINSYNC) {
313 			sys_leap = LEAP_NOWARNING;
314 #ifdef OPENSSL
315 			if (crypto_flags)
316 				crypto_update();
317 #endif /* OPENSSL */
318 		}
319 		sys_stratum = (u_char)sys_orphan;
320 		if (sys_stratum > 1)
321 			sys_refid = htonl(LOOPBACKADR);
322 		else
323 			memcpy(&sys_refid, "LOOP", 4);
324 		sys_offset = 0;
325 		sys_rootdelay = 0;
326 		sys_rootdisp = 0;
327 	}
328 
329 	/*
330 	 * Leapseconds. If a leap is pending, decrement the time
331 	 * remaining. If less than one day remains, set the leap bits.
332 	 * When no time remains, clear the leap bits and increment the
333 	 * TAI. If kernel suppport is not available, do the leap
334 	 * crudely. Note a leap cannot be pending unless the clock is
335 	 * set.
336 	 */
337 	if (leapsec > 0) {
338 		leapsec--;
339 		if (leapsec == 0) {
340 			sys_leap = LEAP_NOWARNING;
341 			sys_tai = leap_tai;
342 #ifdef KERNEL_PLL
343 			if (!(pll_control && kern_enable))
344 				step_systime(-1.0);
345 #else /* KERNEL_PLL */
346 #ifndef SYS_WINNT /* WinNT port has its own leap second handling */
347 			step_systime(-1.0);
348 #endif /* SYS_WINNT */
349 #endif /* KERNEL_PLL */
350 			report_event(EVNT_LEAP, NULL, NULL);
351 		} else {
352 			if (leapsec < DAY)
353 				sys_leap = LEAP_ADDSECOND;
354 			if (leap_tai > 0)
355 				sys_tai = leap_tai - 1;
356 		}
357 	}
358 
359 	/*
360 	 * Update huff-n'-puff filter.
361 	 */
362 	if (huffpuff_timer <= current_time) {
363 		huffpuff_timer += HUFFPUFF;
364 		huffpuff();
365 	}
366 
367 #ifdef OPENSSL
368 	/*
369 	 * Garbage collect expired keys.
370 	 */
371 	if (keys_timer <= current_time) {
372 		keys_timer += 1 << sys_automax;
373 		auth_agekeys();
374 	}
375 
376 	/*
377 	 * Garbage collect key list and generate new private value. The
378 	 * timer runs only after initial synchronization and fires about
379 	 * once per day.
380 	 */
381 	if (revoke_timer <= current_time && sys_leap !=
382 	    LEAP_NOTINSYNC) {
383 		revoke_timer += 1 << sys_revoke;
384 		RAND_bytes((u_char *)&sys_private, 4);
385 	}
386 #endif /* OPENSSL */
387 
388 	/*
389 	 * Interface update timer
390 	 */
391 	if (interface_interval && interface_timer <= current_time) {
392 
393 		timer_interfacetimeout(current_time +
394 		    interface_interval);
395 		DPRINTF(2, ("timer: interface update\n"));
396 		interface_update(NULL, NULL);
397 	}
398 
399 	/*
400 	 * Finally, write hourly stats.
401 	 */
402 	if (stats_timer <= current_time) {
403 		stats_timer += HOUR;
404 		write_stats();
405 		if (sys_tai != 0 && sys_time.l_ui > leap_expire)
406 			report_event(EVNT_LEAPVAL, NULL, NULL);
407 	}
408 }
409 
410 
411 #ifndef SYS_WINNT
412 /*
413  * alarming - tell the world we've been alarmed
414  */
415 static RETSIGTYPE
416 alarming(
417 	int sig
418 	)
419 {
420 #if !defined(VMS)
421 	if (initializing)
422 		return;
423 	if (alarm_flag)
424 		alarm_overflow++;
425 	else
426 		alarm_flag++;
427 #else /* VMS AST routine */
428 	if (!initializing) {
429 		if (alarm_flag) alarm_overflow++;
430 		else alarm_flag = 1;	/* increment is no good */
431 	}
432 	lib$addx(&vmsinc,&vmstimer,&vmstimer);
433 	sys$setimr(0,&vmstimer,alarming,alarming,0);
434 #endif /* VMS */
435 }
436 #endif /* SYS_WINNT */
437 
438 void
439 timer_interfacetimeout(u_long timeout)
440 {
441 	interface_timer = timeout;
442 }
443 
444 
445 /*
446  * timer_clr_stats - clear timer module stat counters
447  */
448 void
449 timer_clr_stats(void)
450 {
451 	timer_overflows = 0;
452 	timer_xmtcalls = 0;
453 	timer_timereset = current_time;
454 }
455 
456