ntp_util.c revision 182007
1/*
2 * ntp_util.c - stuff I didn't have any other place for
3 */
4
5#ifdef HAVE_CONFIG_H
6# include <config.h>
7#endif
8
9#include "ntpd.h"
10#include "ntp_io.h"
11#include "ntp_unixtime.h"
12#include "ntp_filegen.h"
13#include "ntp_if.h"
14#include "ntp_stdlib.h"
15
16#include <stdio.h>
17#include <ctype.h>
18#include <sys/types.h>
19#ifdef HAVE_SYS_IOCTL_H
20# include <sys/ioctl.h>
21#endif
22
23#ifdef HAVE_IEEEFP_H
24# include <ieeefp.h>
25#endif
26#ifdef HAVE_MATH_H
27# include <math.h>
28#endif
29
30#ifdef  DOSYNCTODR
31# if !defined(VMS)
32#  include <sys/resource.h>
33# endif /* VMS */
34#endif
35
36#if defined(VMS)
37# include <descrip.h>
38#endif /* VMS */
39
40/*
41 * This contains odds and ends.  Right now the only thing you'll find
42 * in here is the hourly stats printer and some code to support
43 * rereading the keys file, but I may eventually put other things in
44 * here such as code to do something with the leap bits.
45 */
46/*
47 * Name of the keys file
48 */
49static	char *key_file_name;
50
51/*
52 * The name of the drift_comp file and the temporary.
53 */
54static	char *stats_drift_file;
55static	char *stats_temp_file;
56int stats_write_period = 3600;	/* # of seconds between writes. */
57double stats_write_tolerance = 0;
58static double prev_drift_comp = 99999.;
59
60/*
61 * Statistics file stuff
62 */
63#ifndef NTP_VAR
64# ifndef SYS_WINNT
65#  define NTP_VAR "/var/NTP/"		/* NOTE the trailing '/' */
66# else
67#  define NTP_VAR "c:\\var\\ntp\\"		/* NOTE the trailing '\\' */
68# endif /* SYS_WINNT */
69#endif
70
71#ifndef MAXPATHLEN
72# define MAXPATHLEN 256
73#endif
74
75static	char statsdir[MAXPATHLEN] = NTP_VAR;
76
77static FILEGEN peerstats;
78static FILEGEN loopstats;
79static FILEGEN clockstats;
80static FILEGEN rawstats;
81static FILEGEN sysstats;
82#ifdef DEBUG_TIMING
83static FILEGEN timingstats;
84#endif
85#ifdef OPENSSL
86static FILEGEN cryptostats;
87#endif /* OPENSSL */
88
89/*
90 * This controls whether stats are written to the fileset. Provided
91 * so that ntpdc can turn off stats when the file system fills up.
92 */
93int stats_control;
94
95/*
96 * Initial frequency offset later passed to the loopfilter.
97 */
98double	old_drift;
99
100/*
101 * init_util - initialize the utilities
102 */
103void
104init_util(void)
105{
106	stats_drift_file = 0;
107	stats_temp_file = 0;
108	key_file_name = 0;
109
110	filegen_register(&statsdir[0], "peerstats", &peerstats);
111
112	filegen_register(&statsdir[0], "loopstats", &loopstats);
113
114	filegen_register(&statsdir[0], "clockstats", &clockstats);
115
116	filegen_register(&statsdir[0], "rawstats", &rawstats);
117
118	filegen_register(&statsdir[0], "sysstats", &sysstats);
119
120#ifdef OPENSSL
121	filegen_register(&statsdir[0], "cryptostats", &cryptostats);
122#endif /* OPENSSL */
123
124#ifdef DEBUG_TIMING
125	filegen_register(&statsdir[0], "timingstats", &timingstats);
126#endif
127}
128
129
130/*
131 * hourly_stats - print some interesting stats
132 */
133void
134write_stats(void)
135{
136	FILE *fp;
137
138#ifdef DOSYNCTODR
139	struct timeval tv;
140#if !defined(VMS)
141	int prio_set;
142#endif
143#ifdef HAVE_GETCLOCK
144        struct timespec ts;
145#endif
146	int o_prio;
147
148	/*
149	 * Sometimes having a Sun can be a drag.
150	 *
151	 * The kernel variable dosynctodr controls whether the system's
152	 * soft clock is kept in sync with the battery clock. If it
153	 * is zero, then the soft clock is not synced, and the battery
154	 * clock is simply left to rot. That means that when the system
155	 * reboots, the battery clock (which has probably gone wacky)
156	 * sets the soft clock. That means ntpd starts off with a very
157	 * confused idea of what time it is. It then takes a large
158	 * amount of time to figure out just how wacky the battery clock
159	 * has made things drift, etc, etc. The solution is to make the
160	 * battery clock sync up to system time. The way to do THAT is
161	 * to simply set the time of day to the current time of day, but
162	 * as quickly as possible. This may, or may not be a sensible
163	 * thing to do.
164	 *
165	 * CAVEAT: settimeofday() steps the sun clock by about 800 us,
166	 *         so setting DOSYNCTODR seems a bad idea in the
167	 *         case of us resolution
168	 */
169
170#if !defined(VMS)
171	/* (prr) getpriority returns -1 on error, but -1 is also a valid
172	 * return value (!), so instead we have to zero errno before the
173	 * call and check it for non-zero afterwards.
174	 */
175	errno = 0;
176	prio_set = 0;
177	o_prio = getpriority(PRIO_PROCESS,0); /* Save setting */
178
179	/*
180	 * (prr) if getpriority succeeded, call setpriority to raise
181	 * scheduling priority as high as possible.  If that succeeds
182	 * as well, set the prio_set flag so we remember to reset
183	 * priority to its previous value below.  Note that on Solaris
184	 * 2.6 (and beyond?), both getpriority and setpriority will fail
185	 * with ESRCH, because sched_setscheduler (called from main) put
186	 * us in the real-time scheduling class which setpriority
187	 * doesn't know about. Being in the real-time class is better
188	 * than anything setpriority can do, anyhow, so this error is
189	 * silently ignored.
190	 */
191	if ((errno == 0) && (setpriority(PRIO_PROCESS,0,-20) == 0))
192		prio_set = 1;	/* overdrive */
193#endif /* VMS */
194#ifdef HAVE_GETCLOCK
195        (void) getclock(TIMEOFDAY, &ts);
196        tv.tv_sec = ts.tv_sec;
197        tv.tv_usec = ts.tv_nsec / 1000;
198#else /*  not HAVE_GETCLOCK */
199	GETTIMEOFDAY(&tv,(struct timezone *)NULL);
200#endif /* not HAVE_GETCLOCK */
201	if (ntp_set_tod(&tv,(struct timezone *)NULL) != 0) {
202		msyslog(LOG_ERR, "can't sync battery time: %m");
203	}
204#if !defined(VMS)
205	if (prio_set)
206		setpriority(PRIO_PROCESS, 0, o_prio); /* downshift */
207#endif /* VMS */
208#endif /* DOSYNCTODR */
209
210	NLOG(NLOG_SYSSTATIST)
211		msyslog(LOG_INFO,
212		    "offset %.6f sec freq %.3f ppm error %.6f poll %d",
213		    last_offset, drift_comp * 1e6, sys_jitter,
214		    sys_poll);
215
216
217	record_sys_stats();
218	if ((u_long)(fabs(prev_drift_comp - drift_comp) * 1e9) <=
219	    (u_long)(fabs(stats_write_tolerance * drift_comp) * 1e9)) {
220	     return;
221	}
222	prev_drift_comp = drift_comp;
223	if (stats_drift_file != 0) {
224		if ((fp = fopen(stats_temp_file, "w")) == NULL) {
225			msyslog(LOG_ERR, "can't open %s: %m",
226			    stats_temp_file);
227			return;
228		}
229		fprintf(fp, "%.3f\n", drift_comp * 1e6);
230		(void)fclose(fp);
231		/* atomic */
232#ifdef SYS_WINNT
233		(void) _unlink(stats_drift_file); /* rename semantics differ under NT */
234#endif /* SYS_WINNT */
235
236#ifndef NO_RENAME
237		(void) rename(stats_temp_file, stats_drift_file);
238#else
239		/* we have no rename NFS of ftp in use */
240		if ((fp = fopen(stats_drift_file, "w")) == NULL) {
241			msyslog(LOG_ERR, "can't open %s: %m",
242			    stats_drift_file);
243			return;
244		}
245
246#endif
247
248#if defined(VMS)
249		/* PURGE */
250		{
251			$DESCRIPTOR(oldvers,";-1");
252			struct dsc$descriptor driftdsc = {
253				strlen(stats_drift_file),0,0,stats_drift_file };
254
255			while(lib$delete_file(&oldvers,&driftdsc) & 1) ;
256		}
257#endif
258	}
259}
260
261
262/*
263 * stats_config - configure the stats operation
264 */
265void
266stats_config(
267	int item,
268	const char *invalue	/* only one type so far */
269	)
270{
271	FILE *fp;
272	const char *value;
273	int len;
274
275	/*
276	 * Expand environment strings under Windows NT, since the
277	 * command interpreter doesn't do this, the program must.
278	 */
279#ifdef SYS_WINNT
280	char newvalue[MAX_PATH], parameter[MAX_PATH];
281
282	if (!ExpandEnvironmentStrings(invalue, newvalue, MAX_PATH)) {
283 		switch(item) {
284		    case STATS_FREQ_FILE:
285			strcpy(parameter,"STATS_FREQ_FILE");
286			break;
287		    case STATS_STATSDIR:
288			strcpy(parameter,"STATS_STATSDIR");
289			break;
290		    case STATS_PID_FILE:
291			strcpy(parameter,"STATS_PID_FILE");
292			break;
293		    default:
294			strcpy(parameter,"UNKNOWN");
295			break;
296		}
297		value = invalue;
298
299		msyslog(LOG_ERR,
300		    "ExpandEnvironmentStrings(%s) failed: %m\n", parameter);
301	} else {
302		value = newvalue;
303	}
304#else
305	value = invalue;
306#endif /* SYS_WINNT */
307
308	switch(item) {
309	    case STATS_FREQ_FILE:
310		if (stats_drift_file != 0) {
311			(void) free(stats_drift_file);
312			(void) free(stats_temp_file);
313			stats_drift_file = 0;
314			stats_temp_file = 0;
315		}
316
317		if (value == 0 || (len = strlen(value)) == 0)
318		    break;
319
320		stats_drift_file = (char*)emalloc((u_int)(len + 1));
321#if !defined(VMS)
322		stats_temp_file = (char*)emalloc((u_int)(len +
323		    sizeof(".TEMP")));
324#else
325		stats_temp_file = (char*)emalloc((u_int)(len +
326		    sizeof("-TEMP")));
327#endif /* VMS */
328		memmove(stats_drift_file, value, (unsigned)(len+1));
329		memmove(stats_temp_file, value, (unsigned)len);
330#if !defined(VMS)
331		memmove(stats_temp_file + len, ".TEMP",
332		    sizeof(".TEMP"));
333#else
334		memmove(stats_temp_file + len, "-TEMP",
335		    sizeof("-TEMP"));
336#endif /* VMS */
337
338		/*
339		 * Open drift file and read frequency. If the file is
340		 * missing or contains errors, tell the loop to reset.
341		 */
342		if ((fp = fopen(stats_drift_file, "r")) == NULL) {
343			old_drift = 1e9;
344			break;
345		}
346		if (fscanf(fp, "%lf", &old_drift) != 1) {
347			msyslog(LOG_ERR, "Frequency format error in %s",
348			    stats_drift_file);
349			old_drift = 1e9;
350			fclose(fp);
351			break;
352		}
353		fclose(fp);
354		prev_drift_comp = old_drift / 1e6;
355		msyslog(LOG_INFO,
356		    "frequency initialized %.3f PPM from %s",
357			old_drift, stats_drift_file);
358		break;
359
360	    case STATS_STATSDIR:
361		if (strlen(value) >= sizeof(statsdir)) {
362			msyslog(LOG_ERR,
363			    "value for statsdir too long (>%d, sigh)",
364			    (int)sizeof(statsdir)-1);
365		} else {
366			l_fp now;
367
368			get_systime(&now);
369			strcpy(statsdir,value);
370			if(peerstats.prefix == &statsdir[0] &&
371			    peerstats.fp != NULL) {
372				fclose(peerstats.fp);
373				peerstats.fp = NULL;
374				filegen_setup(&peerstats, now.l_ui);
375			}
376			if(loopstats.prefix == &statsdir[0] &&
377			    loopstats.fp != NULL) {
378				fclose(loopstats.fp);
379				loopstats.fp = NULL;
380				filegen_setup(&loopstats, now.l_ui);
381			}
382			if(clockstats.prefix == &statsdir[0] &&
383			    clockstats.fp != NULL) {
384				fclose(clockstats.fp);
385				clockstats.fp = NULL;
386				filegen_setup(&clockstats, now.l_ui);
387			}
388			if(rawstats.prefix == &statsdir[0] &&
389			    rawstats.fp != NULL) {
390				fclose(rawstats.fp);
391				rawstats.fp = NULL;
392				filegen_setup(&rawstats, now.l_ui);
393			}
394			if(sysstats.prefix == &statsdir[0] &&
395			    sysstats.fp != NULL) {
396				fclose(sysstats.fp);
397				sysstats.fp = NULL;
398				filegen_setup(&sysstats, now.l_ui);
399			}
400#ifdef OPENSSL
401			if(cryptostats.prefix == &statsdir[0] &&
402			    cryptostats.fp != NULL) {
403				fclose(cryptostats.fp);
404				cryptostats.fp = NULL;
405				filegen_setup(&cryptostats, now.l_ui);
406			}
407#endif /* OPENSSL */
408		}
409		break;
410
411	    case STATS_PID_FILE:
412		if ((fp = fopen(value, "w")) == NULL) {
413			msyslog(LOG_ERR, "Can't open %s: %m", value);
414			break;
415		}
416		fprintf(fp, "%d", (int) getpid());
417		fclose(fp);;
418		break;
419
420	    default:
421		/* oh well */
422		break;
423	}
424}
425
426/*
427 * record_peer_stats - write peer statistics to file
428 *
429 * file format:
430 * day (mjd)
431 * time (s past UTC midnight)
432 * peer (ip address)
433 * peer status word (hex)
434 * peer offset (s)
435 * peer delay (s)
436 * peer error bound (s)
437 * peer error (s)
438*/
439void
440record_peer_stats(
441	struct sockaddr_storage *addr,
442	int	status,
443	double	offset,
444	double	delay,
445	double	dispersion,
446	double	skew
447	)
448{
449	l_fp	now;
450	u_long	day;
451
452	if (!stats_control)
453		return;
454
455	get_systime(&now);
456	filegen_setup(&peerstats, now.l_ui);
457	day = now.l_ui / 86400 + MJD_1900;
458	now.l_ui %= 86400;
459	if (peerstats.fp != NULL) {
460		fprintf(peerstats.fp,
461		    "%lu %s %s %x %.9f %.9f %.9f %.9f\n",
462		    day, ulfptoa(&now, 3), stoa(addr), status, offset,
463		    delay, dispersion, skew);
464		fflush(peerstats.fp);
465	}
466}
467
468/*
469 * record_loop_stats - write loop filter statistics to file
470 *
471 * file format:
472 * day (mjd)
473 * time (s past midnight)
474 * offset (s)
475 * frequency (approx ppm)
476 * time constant (log base 2)
477 */
478void
479record_loop_stats(
480	double	offset,
481	double	freq,
482	double	jitter,
483	double	stability,
484	int spoll
485	)
486{
487	l_fp	now;
488	u_long	day;
489
490	if (!stats_control)
491		return;
492
493	get_systime(&now);
494	filegen_setup(&loopstats, now.l_ui);
495	day = now.l_ui / 86400 + MJD_1900;
496	now.l_ui %= 86400;
497	if (loopstats.fp != NULL) {
498		fprintf(loopstats.fp, "%lu %s %.9f %.3f %.9f %.6f %d\n",
499		    day, ulfptoa(&now, 3), offset, freq * 1e6, jitter,
500		    stability * 1e6, spoll);
501		fflush(loopstats.fp);
502	}
503}
504
505/*
506 * record_clock_stats - write clock statistics to file
507 *
508 * file format:
509 * day (mjd)
510 * time (s past midnight)
511 * peer (ip address)
512 * text message
513 */
514void
515record_clock_stats(
516	struct sockaddr_storage *addr,
517	const char *text
518	)
519{
520	l_fp	now;
521	u_long	day;
522
523	if (!stats_control)
524		return;
525
526	get_systime(&now);
527	filegen_setup(&clockstats, now.l_ui);
528	day = now.l_ui / 86400 + MJD_1900;
529	now.l_ui %= 86400;
530	if (clockstats.fp != NULL) {
531		fprintf(clockstats.fp, "%lu %s %s %s\n",
532		    day, ulfptoa(&now, 3), stoa(addr), text);
533		fflush(clockstats.fp);
534	}
535}
536
537/*
538 * record_raw_stats - write raw timestamps to file
539 *
540 *
541 * file format
542 * time (s past midnight)
543 * peer ip address
544 * local ip address
545 * t1 t2 t3 t4 timestamps
546 */
547void
548record_raw_stats(
549        struct sockaddr_storage *srcadr,
550        struct sockaddr_storage *dstadr,
551	l_fp	*t1,
552	l_fp	*t2,
553	l_fp	*t3,
554	l_fp	*t4
555	)
556{
557	l_fp	now;
558	u_long	day;
559
560	if (!stats_control)
561		return;
562
563	get_systime(&now);
564	filegen_setup(&rawstats, now.l_ui);
565	day = now.l_ui / 86400 + MJD_1900;
566	now.l_ui %= 86400;
567	if (rawstats.fp != NULL) {
568                fprintf(rawstats.fp, "%lu %s %s %s %s %s %s %s\n",
569			day, ulfptoa(&now, 3), stoa(srcadr), dstadr ? stoa(dstadr) : "-",
570			ulfptoa(t1, 9), ulfptoa(t2, 9), ulfptoa(t3, 9),
571			ulfptoa(t4, 9));
572		fflush(rawstats.fp);
573	}
574}
575
576
577/*
578 * record_sys_stats - write system statistics to file
579 *
580 * file format
581 * time (s past midnight)
582 * time since startup (hr)
583 * packets recieved
584 * packets processed
585 * current version
586 * previous version
587 * bad version
588 * access denied
589 * bad length or format
590 * bad authentication
591 * rate exceeded
592 */
593void
594record_sys_stats(void)
595{
596	l_fp	now;
597	u_long	day;
598
599	if (!stats_control)
600		return;
601
602	get_systime(&now);
603	filegen_setup(&sysstats, now.l_ui);
604	day = now.l_ui / 86400 + MJD_1900;
605	now.l_ui %= 86400;
606	if (sysstats.fp != NULL) {
607                fprintf(sysstats.fp,
608		    "%lu %s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu\n",
609		    day, ulfptoa(&now, 3), sys_stattime / 3600,
610		    sys_received, sys_processed, sys_newversionpkt,
611		    sys_oldversionpkt, sys_unknownversion,
612		    sys_restricted, sys_badlength, sys_badauth,
613		    sys_limitrejected);
614		fflush(sysstats.fp);
615		proto_clr_stats();
616	}
617}
618
619
620#ifdef OPENSSL
621/*
622 * record_crypto_stats - write crypto statistics to file
623 *
624 * file format:
625 * day (mjd)
626 * time (s past midnight)
627 * peer (ip address)
628 * text message
629 */
630void
631record_crypto_stats(
632	struct sockaddr_storage *addr,
633	const char *text
634	)
635{
636	l_fp	now;
637	u_long	day;
638
639	if (!stats_control)
640		return;
641
642	get_systime(&now);
643	filegen_setup(&cryptostats, now.l_ui);
644	day = now.l_ui / 86400 + MJD_1900;
645	now.l_ui %= 86400;
646	if (cryptostats.fp != NULL) {
647		if (addr == NULL)
648			fprintf(cryptostats.fp, "%lu %s %s\n",
649			    day, ulfptoa(&now, 3), text);
650		else
651			fprintf(cryptostats.fp, "%lu %s %s %s\n",
652			    day, ulfptoa(&now, 3), stoa(addr), text);
653		fflush(cryptostats.fp);
654	}
655}
656#endif /* OPENSSL */
657
658#ifdef DEBUG_TIMING
659/*
660 * record_crypto_stats - write crypto statistics to file
661 *
662 * file format:
663 * day (mjd)
664 * time (s past midnight)
665 * text message
666 */
667void
668record_timing_stats(
669	const char *text
670	)
671{
672	static unsigned int flshcnt;
673	l_fp	now;
674	u_long	day;
675
676	if (!stats_control)
677		return;
678
679	get_systime(&now);
680	filegen_setup(&timingstats, now.l_ui);
681	day = now.l_ui / 86400 + MJD_1900;
682	now.l_ui %= 86400;
683	if (timingstats.fp != NULL) {
684		fprintf(timingstats.fp, "%lu %s %s\n",
685			    day, lfptoa(&now, 3), text);
686		if (++flshcnt % 100 == 0)
687			fflush(timingstats.fp);
688	}
689}
690#endif
691/*
692 * getauthkeys - read the authentication keys from the specified file
693 */
694void
695getauthkeys(
696	const char *keyfile
697	)
698{
699	int len;
700
701	len = strlen(keyfile);
702	if (len == 0)
703		return;
704
705	if (key_file_name != 0) {
706		if (len > (int)strlen(key_file_name)) {
707			(void) free(key_file_name);
708			key_file_name = 0;
709		}
710	}
711
712	if (key_file_name == 0) {
713#ifndef SYS_WINNT
714		key_file_name = (char*)emalloc((u_int) (len + 1));
715#else
716		key_file_name = (char*)emalloc((u_int)  (MAXPATHLEN));
717#endif
718	}
719#ifndef SYS_WINNT
720 	memmove(key_file_name, keyfile, (unsigned)(len+1));
721#else
722	if (!ExpandEnvironmentStrings(keyfile, key_file_name, MAXPATHLEN))
723	{
724		msyslog(LOG_ERR,
725		    "ExpandEnvironmentStrings(KEY_FILE) failed: %m\n");
726	}
727#endif /* SYS_WINNT */
728
729	authreadkeys(key_file_name);
730}
731
732
733/*
734 * rereadkeys - read the authentication key file over again.
735 */
736void
737rereadkeys(void)
738{
739	if (key_file_name != 0)
740	    authreadkeys(key_file_name);
741}
742
743/*
744 * sock_hash - hash an sockaddr_storage structure
745 */
746int
747sock_hash(
748	struct sockaddr_storage *addr
749	)
750{
751	int hashVal;
752	int i;
753	int len;
754	char *ch;
755
756	hashVal = 0;
757	len = 0;
758	/*
759	 * We can't just hash the whole thing because there are hidden
760	 * fields in sockaddr_in6 that might be filled in by recvfrom(),
761	 * so just use the family, port and address.
762	 */
763	ch = (char *)&addr->ss_family;
764	hashVal = 37 * hashVal + (int)*ch;
765	if (sizeof(addr->ss_family) > 1) {
766		ch++;
767		hashVal = 37 * hashVal + (int)*ch;
768	}
769	switch(addr->ss_family) {
770	case AF_INET:
771		ch = (char *)&((struct sockaddr_in *)addr)->sin_addr;
772		len = sizeof(struct in_addr);
773		break;
774	case AF_INET6:
775		ch = (char *)&((struct sockaddr_in6 *)addr)->sin6_addr;
776		len = sizeof(struct in6_addr);
777		break;
778	}
779
780	for (i = 0; i < len ; i++)
781		hashVal = 37 * hashVal + (int)*(ch + i);
782
783	hashVal = hashVal % 128;  /* % MON_HASH_SIZE hardcoded */
784
785	if (hashVal < 0)
786		hashVal += 128;
787
788	return hashVal;
789}
790
791#if notyet
792/*
793 * ntp_exit - document explicitly that ntpd has exited
794 */
795void
796ntp_exit(int retval)
797{
798  msyslog(LOG_ERR, "EXITING with return code %d", retval);
799  exit(retval);
800}
801#endif
802