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