1/*	$NetBSD$	*/
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 */
46volatile int interface_interval = 300;     /* update interface every 5 minutes as default */
47
48/*
49 * Alarm flag. The mainline code imports this.
50 */
51volatile int alarm_flag;
52
53/*
54 * The counters and timeouts
55 */
56static  u_long interface_timer;	/* interface update timer */
57static	u_long adjust_timer;	/* second timer */
58static	u_long stats_timer;	/* stats timer */
59static	u_long huffpuff_timer;	/* huff-n'-puff timer */
60u_long	leapsec;		/* leapseconds countdown */
61l_fp	sys_time;		/* current system time */
62#ifdef OPENSSL
63static	u_long revoke_timer;	/* keys revoke timer */
64static	u_long keys_timer;	/* session key timer */
65u_long	sys_revoke = KEY_REVOKE; /* keys revoke timeout (log2 s) */
66u_long	sys_automax = NTP_AUTOMAX; /* key list timeout (log2 s) */
67#endif /* OPENSSL */
68
69/*
70 * Statistics counter for the interested.
71 */
72volatile u_long alarm_overflow;
73
74#define MINUTE	60
75#define HOUR	(60 * MINUTE)
76#define	DAY	(24 * HOUR)
77
78u_long current_time;		/* seconds since startup */
79
80/*
81 * Stats.  Number of overflows and number of calls to transmit().
82 */
83u_long timer_timereset;
84u_long timer_overflows;
85u_long timer_xmtcalls;
86
87#if defined(VMS)
88static int vmstimer[2]; 	/* time for next timer AST */
89static int vmsinc[2];		/* timer increment */
90#endif /* VMS */
91
92#if defined SYS_WINNT
93static HANDLE WaitableTimerHandle = NULL;
94#else
95static	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 */
112void
113reinit_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 */
153void
154init_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)
234extern HANDLE
235get_timer_handle(void)
236{
237	return WaitableTimerHandle;
238}
239#endif
240
241/*
242 * timer - event timer
243 */
244void
245timer(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 stratum 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 */
415static RETSIGTYPE
416alarming(
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
438void
439timer_interfacetimeout(u_long timeout)
440{
441	interface_timer = timeout;
442}
443
444
445/*
446 * timer_clr_stats - clear timer module stat counters
447 */
448void
449timer_clr_stats(void)
450{
451	timer_overflows = 0;
452	timer_xmtcalls = 0;
453	timer_timereset = current_time;
454}
455
456