xref: /dflybsd-src/usr.sbin/dntpd/client.c (revision 1d9b37b03175e07fb72491c79787272551cf3e84)
1e0163549SMatthew Dillon /*
2e0163549SMatthew Dillon  * Copyright (c) 2005 The DragonFly Project.  All rights reserved.
3e0163549SMatthew Dillon  *
4e0163549SMatthew Dillon  * This code is derived from software contributed to The DragonFly Project
5e0163549SMatthew Dillon  * by Matthew Dillon <dillon@backplane.com>
6e0163549SMatthew Dillon  *
7e0163549SMatthew Dillon  * Redistribution and use in source and binary forms, with or without
8e0163549SMatthew Dillon  * modification, are permitted provided that the following conditions
9e0163549SMatthew Dillon  * are met:
10e0163549SMatthew Dillon  *
11e0163549SMatthew Dillon  * 1. Redistributions of source code must retain the above copyright
12e0163549SMatthew Dillon  *    notice, this list of conditions and the following disclaimer.
13e0163549SMatthew Dillon  * 2. Redistributions in binary form must reproduce the above copyright
14e0163549SMatthew Dillon  *    notice, this list of conditions and the following disclaimer in
15e0163549SMatthew Dillon  *    the documentation and/or other materials provided with the
16e0163549SMatthew Dillon  *    distribution.
17e0163549SMatthew Dillon  * 3. Neither the name of The DragonFly Project nor the names of its
18e0163549SMatthew Dillon  *    contributors may be used to endorse or promote products derived
19e0163549SMatthew Dillon  *    from this software without specific, prior written permission.
20e0163549SMatthew Dillon  *
21e0163549SMatthew Dillon  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22e0163549SMatthew Dillon  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23e0163549SMatthew Dillon  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24e0163549SMatthew Dillon  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25e0163549SMatthew Dillon  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26e0163549SMatthew Dillon  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27e0163549SMatthew Dillon  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28e0163549SMatthew Dillon  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29e0163549SMatthew Dillon  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30e0163549SMatthew Dillon  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31e0163549SMatthew Dillon  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32e0163549SMatthew Dillon  * SUCH DAMAGE.
33e0163549SMatthew Dillon  */
34e0163549SMatthew Dillon 
35e0163549SMatthew Dillon #include "defs.h"
36e0163549SMatthew Dillon 
372408d859SMatthew Dillon static int client_insane(struct server_info **, int, server_info_t);
382408d859SMatthew Dillon 
39e0163549SMatthew Dillon void
client_init(void)40e0163549SMatthew Dillon client_init(void)
41e0163549SMatthew Dillon {
42e0163549SMatthew Dillon }
43e0163549SMatthew Dillon 
44*1d9b37b0SSascha Wildner void
client_main(struct server_info ** info_ary,int count)45e0163549SMatthew Dillon client_main(struct server_info **info_ary, int count)
46e0163549SMatthew Dillon {
478e4e9813SMatthew Dillon     struct server_info *best_off;
488e4e9813SMatthew Dillon     struct server_info *best_freq;
495d177fa2SMatthew Dillon     double last_freq;
50e0163549SMatthew Dillon     double freq;
51e0163549SMatthew Dillon     double offset;
52c32f1362SMatthew Dillon     int calc_offset_correction;
532408d859SMatthew Dillon     int didreconnect;
542408d859SMatthew Dillon     int i;
552408d859SMatthew Dillon     int insane;
56e0163549SMatthew Dillon 
575d177fa2SMatthew Dillon     last_freq = 0.0;
585d177fa2SMatthew Dillon 
59e0163549SMatthew Dillon     for (;;) {
60e0163549SMatthew Dillon 	/*
61fdd4337fSMatthew Dillon 	 * Subtract the interval from poll_sleep and poll the client
62fdd4337fSMatthew Dillon 	 * if it reaches 0.
63c32f1362SMatthew Dillon 	 *
64c32f1362SMatthew Dillon 	 * Because we do not compensate for offset corrections which are
65c32f1362SMatthew Dillon 	 * in progress, we cannot accumulate data for an offset correction
66c32f1362SMatthew Dillon 	 * while a prior correction is still being worked through by the
67c32f1362SMatthew Dillon 	 * system.
68e0163549SMatthew Dillon 	 */
69c32f1362SMatthew Dillon 	calc_offset_correction = !sysntp_offset_correction_is_running();
70e0163549SMatthew Dillon 	for (i = 0; i < count; ++i)
71c32f1362SMatthew Dillon 	    client_poll(info_ary[i], min_sleep_opt, calc_offset_correction);
72e0163549SMatthew Dillon 
73e0163549SMatthew Dillon 	/*
748e4e9813SMatthew Dillon 	 * Find the best client (or synthesize one).  A different client
758e4e9813SMatthew Dillon 	 * can be chosen for frequency and offset.  Note in particular
768e4e9813SMatthew Dillon 	 * that offset counters and averaging code gets reset when an
778e4e9813SMatthew Dillon 	 * offset correction is made (otherwise the averaging history will
788e4e9813SMatthew Dillon 	 * cause later corrections to overshoot).
798e4e9813SMatthew Dillon 	 *
808e4e9813SMatthew Dillon 	 * The regression used to calculate the frequency is a much
818e4e9813SMatthew Dillon 	 * longer-term entity and is NOT reset, so it is still possible
828e4e9813SMatthew Dillon 	 * for the offset correction code to make minor adjustments to
838e4e9813SMatthew Dillon 	 * the frequency if it so desires.
84e0163549SMatthew Dillon 	 *
85e0163549SMatthew Dillon 	 * client_check may replace the server_info pointer with a new
86e0163549SMatthew Dillon 	 * one.
87e0163549SMatthew Dillon 	 */
888e4e9813SMatthew Dillon 	best_off = NULL;
898e4e9813SMatthew Dillon 	best_freq = NULL;
90e0163549SMatthew Dillon 	for (i = 0; i < count; ++i)
918e4e9813SMatthew Dillon 	    client_check(&info_ary[i], &best_off, &best_freq);
928e4e9813SMatthew Dillon 
938e4e9813SMatthew Dillon 	/*
942408d859SMatthew Dillon 	 * Check for server insanity.  In large NNTP pools some servers
952408d859SMatthew Dillon 	 * may just be dead wrong, but report that they are right.
962408d859SMatthew Dillon 	 */
972408d859SMatthew Dillon 	if (best_off) {
982408d859SMatthew Dillon 	    insane = client_insane(info_ary, count, best_off);
992408d859SMatthew Dillon 	    if (insane > 0) {
1002408d859SMatthew Dillon 		/*
1012408d859SMatthew Dillon 		 * best_off meets the quorum requirements and is good
1022408d859SMatthew Dillon 		 * (keep best_off)
1032408d859SMatthew Dillon 		 */
1045e13edd7SMatthew Dillon 		best_off->server_insane = 0;
1052408d859SMatthew Dillon 	    } else if (insane == 0) {
1062408d859SMatthew Dillon 		/*
1072408d859SMatthew Dillon 		 * best_off is probably good, but we do not have enough
1082408d859SMatthew Dillon 		 * servers reporting yet to meet the quorum requirements.
1092408d859SMatthew Dillon 		 */
1102408d859SMatthew Dillon 		best_off = NULL;
1112408d859SMatthew Dillon 	    } else {
1122408d859SMatthew Dillon 		/*
1135e13edd7SMatthew Dillon 		 * best_off is ugly, mark the server as being insane for
1145e13edd7SMatthew Dillon 		 * 60 minutes.
1152408d859SMatthew Dillon 		 */
1165e13edd7SMatthew Dillon 		best_off->server_insane = 60 * 60;
1175e13edd7SMatthew Dillon 		logdebuginfo(best_off, 1,
1185e13edd7SMatthew Dillon 			     "excessive offset deviation, mapping out\n");
1195e13edd7SMatthew Dillon 		best_off = NULL;
1202408d859SMatthew Dillon 	    }
1212408d859SMatthew Dillon 	}
1222408d859SMatthew Dillon 
1232408d859SMatthew Dillon 	/*
1248e4e9813SMatthew Dillon 	 * Offset correction.
1258e4e9813SMatthew Dillon 	 */
1268e4e9813SMatthew Dillon 	if (best_off) {
1278e4e9813SMatthew Dillon 	    offset = best_off->lin_sumoffset / best_off->lin_countoffset;
1288e4e9813SMatthew Dillon 	    lin_resetalloffsets(info_ary, count);
1293d15852bSMatthew Dillon 	    if (offset < -COURSE_OFFSET_CORRECTION_LIMIT ||
13075349a56SMatthew Dillon 		offset > COURSE_OFFSET_CORRECTION_LIMIT ||
13175349a56SMatthew Dillon 		quickset_opt
1323d15852bSMatthew Dillon 	    ) {
1333d15852bSMatthew Dillon 		freq = sysntp_correct_course_offset(offset);
13475349a56SMatthew Dillon 		quickset_opt = 0;
1353d15852bSMatthew Dillon 	    } else {
136e0163549SMatthew Dillon 		freq = sysntp_correct_offset(offset);
1373d15852bSMatthew Dillon 	    }
1388e4e9813SMatthew Dillon 	} else {
1398e4e9813SMatthew Dillon 	    freq = 0.0;
140e0163549SMatthew Dillon 	}
1418e4e9813SMatthew Dillon 
1428e4e9813SMatthew Dillon 	/*
1438e4e9813SMatthew Dillon 	 * Frequency correction (throw away minor freq adjusts from the
1445d177fa2SMatthew Dillon 	 * offset code if we can't do a frequency correction here).  Do
1455d177fa2SMatthew Dillon 	 * not reissue if it hasn't changed from the last issued correction.
1468e4e9813SMatthew Dillon 	 */
1478e4e9813SMatthew Dillon 	if (best_freq) {
1485d177fa2SMatthew Dillon 	    freq += best_freq->lin_cache_freq;
1495d177fa2SMatthew Dillon 	    if (last_freq != freq) {
1505d177fa2SMatthew Dillon 		sysntp_correct_freq(freq);
1515d177fa2SMatthew Dillon 		last_freq = freq;
1525d177fa2SMatthew Dillon 	    }
1538e4e9813SMatthew Dillon 	}
1548e4e9813SMatthew Dillon 
155fdd4337fSMatthew Dillon 	/*
156fdd4337fSMatthew Dillon 	 * This function is responsible for managing the polling mode and
157fdd4337fSMatthew Dillon 	 * figures out how long we should sleep.
158fdd4337fSMatthew Dillon 	 */
1592408d859SMatthew Dillon 	didreconnect = 0;
160fdd4337fSMatthew Dillon 	for (i = 0; i < count; ++i)
1612408d859SMatthew Dillon 	    client_manage_polling_mode(info_ary[i], &didreconnect);
1622408d859SMatthew Dillon 	if (didreconnect)
1632408d859SMatthew Dillon 	    client_check_duplicate_ips(info_ary, count);
164fdd4337fSMatthew Dillon 
165fdd4337fSMatthew Dillon 	/*
166fdd4337fSMatthew Dillon 	 * Polling loop sleep.
167fdd4337fSMatthew Dillon 	 */
168fdd4337fSMatthew Dillon 	usleep(min_sleep_opt * 1000000 + random() % 500000);
169e0163549SMatthew Dillon     }
170e0163549SMatthew Dillon }
171e0163549SMatthew Dillon 
172e0163549SMatthew Dillon void
client_poll(server_info_t info,int poll_interval,int calc_offset_correction)173c32f1362SMatthew Dillon client_poll(server_info_t info, int poll_interval, int calc_offset_correction)
174e0163549SMatthew Dillon {
175e0163549SMatthew Dillon     struct timeval rtv;
176e0163549SMatthew Dillon     struct timeval ltv;
177e0163549SMatthew Dillon     struct timeval lbtv;
178e0163549SMatthew Dillon     double offset;
179e0163549SMatthew Dillon 
180fdd4337fSMatthew Dillon     /*
1815e13edd7SMatthew Dillon      * Adjust the insane-server countdown
1825e13edd7SMatthew Dillon      */
1835e13edd7SMatthew Dillon     if (info->server_insane > poll_interval)
1845e13edd7SMatthew Dillon 	info->server_insane -= poll_interval;
1855e13edd7SMatthew Dillon     else
1865e13edd7SMatthew Dillon 	info->server_insane = 0;
1875e13edd7SMatthew Dillon 
1885e13edd7SMatthew Dillon     /*
189fdd4337fSMatthew Dillon      * By default we always poll.  If the polling interval comes under
190fdd4337fSMatthew Dillon      * active management the poll_sleep will be non-zero.
191fdd4337fSMatthew Dillon      */
192fdd4337fSMatthew Dillon     if (info->poll_sleep > poll_interval) {
193fdd4337fSMatthew Dillon 	info->poll_sleep -= poll_interval;
194fdd4337fSMatthew Dillon 	return;
195fdd4337fSMatthew Dillon     }
196fdd4337fSMatthew Dillon     info->poll_sleep = 0;
197fdd4337fSMatthew Dillon 
1982408d859SMatthew Dillon     /*
1992408d859SMatthew Dillon      * If the client isn't open don't mess with the poll_failed count
2002408d859SMatthew Dillon      * or anything else.  We are left in the init or startup phase.
2012408d859SMatthew Dillon      */
2022408d859SMatthew Dillon     if (info->fd < 0) {
2032408d859SMatthew Dillon 	if (info->poll_failed < 0x7FFFFFFF)
2042408d859SMatthew Dillon 	    ++info->poll_failed;
2052408d859SMatthew Dillon 	return;
2062408d859SMatthew Dillon     }
2072408d859SMatthew Dillon 
2082408d859SMatthew Dillon     logdebuginfo(info, 4, "poll, ");
209e0163549SMatthew Dillon     if (udp_ntptimereq(info->fd, &rtv, &ltv, &lbtv) < 0) {
210fdd4337fSMatthew Dillon 	++info->poll_failed;
2112408d859SMatthew Dillon 	logdebug(4, "no response (%d failures in a row)\n", info->poll_failed);
212fdd4337fSMatthew Dillon 	if (info->poll_failed == POLL_FAIL_RESET) {
2133d15852bSMatthew Dillon 	    if (info->lin_count != 0) {
2142408d859SMatthew Dillon 		logdebuginfo(info, 4, "resetting regression due to failures\n");
215fdd4337fSMatthew Dillon 	    }
216fdd4337fSMatthew Dillon 	    lin_reset(info);
217fdd4337fSMatthew Dillon 	}
218e0163549SMatthew Dillon 	return;
219e0163549SMatthew Dillon     }
220e0163549SMatthew Dillon 
221e0163549SMatthew Dillon     /*
222fdd4337fSMatthew Dillon      * Successful query.  Update polling info for the polling mode manager.
223fdd4337fSMatthew Dillon      */
224fdd4337fSMatthew Dillon     ++info->poll_count;
225fdd4337fSMatthew Dillon     info->poll_failed = 0;
226fdd4337fSMatthew Dillon 
227fdd4337fSMatthew Dillon     /*
228e0163549SMatthew Dillon      * Figure out the offset (the difference between the reported
229e0163549SMatthew Dillon      * time and our current time) for linear regression purposes.
230e0163549SMatthew Dillon      */
2318e4e9813SMatthew Dillon     offset = tv_delta_double(&rtv, &ltv);
232e0163549SMatthew Dillon 
233e0163549SMatthew Dillon     while (info) {
234e0163549SMatthew Dillon 	/*
235e0163549SMatthew Dillon 	 * Linear regression
236e0163549SMatthew Dillon 	 */
2373d15852bSMatthew Dillon 	if (debug_level >= 4) {
238e0163549SMatthew Dillon 	    struct tm *tp;
239e0163549SMatthew Dillon 	    char buf[64];
240e0163549SMatthew Dillon 	    time_t t;
241e0163549SMatthew Dillon 
242e0163549SMatthew Dillon 	    t = rtv.tv_sec;
243e0163549SMatthew Dillon 	    tp = localtime(&t);
244e0163549SMatthew Dillon 	    strftime(buf, sizeof(buf), "%d-%b-%Y %H:%M:%S", tp);
2453d15852bSMatthew Dillon 	    logdebug(4, "%s.%03ld ", buf, rtv.tv_usec / 1000);
246e0163549SMatthew Dillon 	}
247c32f1362SMatthew Dillon 	lin_regress(info, &ltv, &lbtv, offset, calc_offset_correction);
248e0163549SMatthew Dillon 	info = info->altinfo;
2493d15852bSMatthew Dillon 	if (info && debug_level >= 4) {
2503d15852bSMatthew Dillon 	    logdebug(4, "%*.*s: poll, ",
2519b724d8cSMatthew Dillon 		(int)strlen(info->target),
2523d15852bSMatthew Dillon 		(int)strlen(info->target), "(alt)");
2539b724d8cSMatthew Dillon 	}
254e0163549SMatthew Dillon     }
255e0163549SMatthew Dillon }
256e0163549SMatthew Dillon 
257e0163549SMatthew Dillon /*
2588e4e9813SMatthew Dillon  * Find the best client (or synthesize a fake info structure to return).
2598e4e9813SMatthew Dillon  * We can find separate best clients for offset and frequency.
260e0163549SMatthew Dillon  */
2618e4e9813SMatthew Dillon void
client_check(struct server_info ** checkp,struct server_info ** best_off,struct server_info ** best_freq)2628e4e9813SMatthew Dillon client_check(struct server_info **checkp,
2638e4e9813SMatthew Dillon 	     struct server_info **best_off,
2648e4e9813SMatthew Dillon 	     struct server_info **best_freq)
265e0163549SMatthew Dillon {
266e0163549SMatthew Dillon     struct server_info *check = *checkp;
267e0163549SMatthew Dillon     struct server_info *info;
2684c77cdebSMatthew Dillon     int min_samples;
269e0163549SMatthew Dillon 
270e0163549SMatthew Dillon     /*
271e0163549SMatthew Dillon      * Start an alternate linear regression once our current one
272e0163549SMatthew Dillon      * has passed a certain point.
273e0163549SMatthew Dillon      */
274e0163549SMatthew Dillon     if (check->lin_count >= LIN_RESTART / 2 && check->altinfo == NULL) {
275e0163549SMatthew Dillon 	info = malloc(sizeof(*info));
276e0163549SMatthew Dillon 	assert(info != NULL);
277fdd4337fSMatthew Dillon 	/* note: check->altinfo is NULL as of the bcopy */
278e0163549SMatthew Dillon 	bcopy(check, info, sizeof(*info));
279e0163549SMatthew Dillon 	check->altinfo = info;
280e0163549SMatthew Dillon 	lin_reset(info);
281e0163549SMatthew Dillon     }
282e0163549SMatthew Dillon 
283e0163549SMatthew Dillon     /*
284e0163549SMatthew Dillon      * Replace our current linear regression with the alternate once
285e0163549SMatthew Dillon      * the current one has hit its limit (beyond a certain point the
286e0163549SMatthew Dillon      * linear regression starts to work against us, preventing us from
287e0163549SMatthew Dillon      * reacting to changing conditions).
288e0163549SMatthew Dillon      *
289e0163549SMatthew Dillon      * Report any significant change in the offset or ppm.
290e0163549SMatthew Dillon      */
291e0163549SMatthew Dillon     if (check->lin_count >= LIN_RESTART) {
292e0163549SMatthew Dillon 	if ((info = check->altinfo) && info->lin_count >= LIN_RESTART / 2) {
293e0163549SMatthew Dillon 	    double freq_diff;
294e0163549SMatthew Dillon 
295e0163549SMatthew Dillon 	    freq_diff = info->lin_cache_freq - check->lin_cache_freq;
2968d67cbb3SSascha Wildner 	    logdebuginfo(info, 4, "Switching to alternate, Frequency "
2973d15852bSMatthew Dillon 			 "difference is %6.3f ppm\n",
2982408d859SMatthew Dillon 			 freq_diff * 1.0E+6);
299e0163549SMatthew Dillon 	    *checkp = info;
300e0163549SMatthew Dillon 	    free(check);
301e0163549SMatthew Dillon 	    check = info;
302e0163549SMatthew Dillon 	}
303e0163549SMatthew Dillon     }
304e0163549SMatthew Dillon 
305e0163549SMatthew Dillon     /*
3068e4e9813SMatthew Dillon      * BEST CLIENT FOR FREQUENCY CORRECTION:
3072e8f7384SMatthew Dillon      *
3084c77cdebSMatthew Dillon      * Frequency corrections get better the longer the time separation
3094c77cdebSMatthew Dillon      * between samples.
3104c77cdebSMatthew Dillon      *
3113f5e28f4SSascha Wildner      *	8 samples and a correlation > 0.99, or
3123f5e28f4SSascha Wildner      * 16 samples and a correlation > 0.96
313e0163549SMatthew Dillon      */
3148e4e9813SMatthew Dillon     info = *best_freq;
3152e8f7384SMatthew Dillon     if ((check->lin_count >= 8 && fabs(check->lin_cache_corr) >= 0.99) ||
3162e8f7384SMatthew Dillon 	(check->lin_count >= 16 && fabs(check->lin_cache_corr) >= 0.96)
3172e8f7384SMatthew Dillon     ) {
3188e4e9813SMatthew Dillon 	if (info == NULL ||
3198e4e9813SMatthew Dillon 	    fabs(check->lin_cache_corr) > fabs(info->lin_cache_corr)
3208e4e9813SMatthew Dillon 	) {
3218e4e9813SMatthew Dillon 	    info = check;
3228e4e9813SMatthew Dillon 	    *best_freq = info;
323e0163549SMatthew Dillon 	}
324e0163549SMatthew Dillon 
325e0163549SMatthew Dillon     }
3268e4e9813SMatthew Dillon 
3278e4e9813SMatthew Dillon     /*
3288e4e9813SMatthew Dillon      * BEST CLIENT FOR OFFSET CORRECTION:
3298e4e9813SMatthew Dillon      *
3308e4e9813SMatthew Dillon      * Use the standard-deviation and require at least 4 samples.  An
3318e4e9813SMatthew Dillon      * offset correction is valid if the standard deviation is less then
3328e4e9813SMatthew Dillon      * the average offset divided by 4.
3335e13edd7SMatthew Dillon      *
3344c77cdebSMatthew Dillon      * If we are in maintainance mode, require 8 samples instead of 4.
3354c77cdebSMatthew Dillon      * Offset corrections get better with more samples.  This reduces
3364c77cdebSMatthew Dillon      * ping-pong effects that can occur with a small number of samples.
3374c77cdebSMatthew Dillon      *
3385e13edd7SMatthew Dillon      * Servers marked as being insane are not allowed
3398e4e9813SMatthew Dillon      */
3408e4e9813SMatthew Dillon     info = *best_off;
3414c77cdebSMatthew Dillon     if (info && info->poll_mode == POLL_MAINTAIN)
3424c77cdebSMatthew Dillon 	min_samples = 8;
3434c77cdebSMatthew Dillon     else
3444c77cdebSMatthew Dillon 	min_samples = 4;
3454c77cdebSMatthew Dillon     if (check->lin_countoffset >= min_samples &&
3465e13edd7SMatthew Dillon 	(check->lin_cache_stddev <
3475e13edd7SMatthew Dillon 	 fabs(check->lin_sumoffset / check->lin_countoffset / 4)) &&
3485e13edd7SMatthew Dillon 	check->server_insane == 0
3495e13edd7SMatthew Dillon      ) {
3508e4e9813SMatthew Dillon 	if (info == NULL ||
3518e4e9813SMatthew Dillon 	    fabs(check->lin_cache_stddev) < fabs(info->lin_cache_stddev)
3528e4e9813SMatthew Dillon 	) {
3538e4e9813SMatthew Dillon 	    info = check;
3548e4e9813SMatthew Dillon 	    *best_off = info;
3558e4e9813SMatthew Dillon 	}
3568e4e9813SMatthew Dillon     }
357e0163549SMatthew Dillon }
358e0163549SMatthew Dillon 
359e0163549SMatthew Dillon /*
360fdd4337fSMatthew Dillon  * Actively manage the polling interval.  Note that the poll_* fields are
361fdd4337fSMatthew Dillon  * always transfered to the alternate regression when the check code replaces
362fdd4337fSMatthew Dillon  * the current regression with a new one.
363fdd4337fSMatthew Dillon  *
364fdd4337fSMatthew Dillon  * This routine is called from the main loop for each base info structure.
365fdd4337fSMatthew Dillon  * The polling mode applies to all alternates so we do not have to iterate
366fdd4337fSMatthew Dillon  * through the alt's.
367fdd4337fSMatthew Dillon  */
368fdd4337fSMatthew Dillon void
client_manage_polling_mode(struct server_info * info,int * didreconnect)3692408d859SMatthew Dillon client_manage_polling_mode(struct server_info *info, int *didreconnect)
370fdd4337fSMatthew Dillon {
371fdd4337fSMatthew Dillon     /*
3722408d859SMatthew Dillon      * Permanently failed servers are ignored.
373fdd4337fSMatthew Dillon      */
3742408d859SMatthew Dillon     if (info->server_state == -2)
3752408d859SMatthew Dillon 	return;
3762408d859SMatthew Dillon 
3772408d859SMatthew Dillon     /*
3782408d859SMatthew Dillon      * Our polling interval has not yet passed.
3792408d859SMatthew Dillon      */
3802408d859SMatthew Dillon     if (info->poll_sleep)
3812408d859SMatthew Dillon 	return;
382fdd4337fSMatthew Dillon 
383fdd4337fSMatthew Dillon     /*
384fdd4337fSMatthew Dillon      * Standard polling mode progression
385fdd4337fSMatthew Dillon      */
386fdd4337fSMatthew Dillon     switch(info->poll_mode) {
387fdd4337fSMatthew Dillon     case POLL_FIXED:
3882408d859SMatthew Dillon 	/*
3892408d859SMatthew Dillon 	 * Initial state after connect or when a reconnect is required.
3902408d859SMatthew Dillon 	 */
3912408d859SMatthew Dillon 	if (info->fd < 0) {
3922408d859SMatthew Dillon 	    logdebuginfo(info, 2, "polling mode INIT, relookup & reconnect\n");
3932408d859SMatthew Dillon 	    reconnect_server(info);
3942408d859SMatthew Dillon 	    *didreconnect = 1;
3952408d859SMatthew Dillon 	    if (info->fd < 0) {
3962408d859SMatthew Dillon 		if (info->poll_failed >= POLL_RECOVERY_RESTART * 5)
3972408d859SMatthew Dillon 		    info->poll_sleep = max_sleep_opt;
3982408d859SMatthew Dillon 		else if (info->poll_failed >= POLL_RECOVERY_RESTART)
3992408d859SMatthew Dillon 		    info->poll_sleep = nom_sleep_opt;
4002408d859SMatthew Dillon 		else
401fdd4337fSMatthew Dillon 		    info->poll_sleep = min_sleep_opt;
402fdd4337fSMatthew Dillon 		break;
403fdd4337fSMatthew Dillon 	    }
4042408d859SMatthew Dillon 
4052408d859SMatthew Dillon 	    /*
4062408d859SMatthew Dillon 	     * Transition the server to the DNS lookup successful state.
4072408d859SMatthew Dillon 	     * Note that the server state does not transition out of
4082408d859SMatthew Dillon 	     * lookup successful if we relookup after a packet failure
4092408d859SMatthew Dillon 	     * so the message is printed only once, usually.
4102408d859SMatthew Dillon 	     */
4112408d859SMatthew Dillon 	    client_setserverstate(info, 0, "DNS lookup success");
4122408d859SMatthew Dillon 
4132408d859SMatthew Dillon 	    /*
4142408d859SMatthew Dillon 	     * If we've failed many times switch to the startup state but
4152408d859SMatthew Dillon 	     * do not fall through into it.  break the switch and a single
4162408d859SMatthew Dillon 	     * poll will be made after the nominal polling interval.
4172408d859SMatthew Dillon 	     */
4182408d859SMatthew Dillon 	    if (info->poll_failed >= POLL_RECOVERY_RESTART * 5) {
4192408d859SMatthew Dillon 		logdebuginfo(info, 2, "polling mode INIT->STARTUP (very slow)\n");
4202408d859SMatthew Dillon 		info->poll_mode = POLL_STARTUP;
4212408d859SMatthew Dillon 		info->poll_sleep = max_sleep_opt;
4222408d859SMatthew Dillon 		info->poll_count = 0;
4232408d859SMatthew Dillon 		break;
4242408d859SMatthew Dillon 	    } else if (info->poll_failed >= POLL_RECOVERY_RESTART) {
4252408d859SMatthew Dillon 		logdebuginfo(info, 2, "polling mode INIT->STARTUP (slow)\n");
4262408d859SMatthew Dillon 		info->poll_mode = POLL_STARTUP;
4272408d859SMatthew Dillon 		info->poll_count = 0;
4282408d859SMatthew Dillon 		break;
4292408d859SMatthew Dillon 	    }
4302408d859SMatthew Dillon 	}
4312408d859SMatthew Dillon 
4322408d859SMatthew Dillon 	/*
4332408d859SMatthew Dillon 	 * Fall through to the startup state.
4342408d859SMatthew Dillon 	 */
4352408d859SMatthew Dillon 	info->poll_mode = POLL_STARTUP;
4362408d859SMatthew Dillon 	logdebuginfo(info, 2, "polling mode INIT->STARTUP (normal)\n");
4372408d859SMatthew Dillon 	/* fall through */
4382408d859SMatthew Dillon     case POLL_STARTUP:
4392408d859SMatthew Dillon 	/*
4402408d859SMatthew Dillon 	 * Transition to a FAILED state if too many poll failures occured.
4412408d859SMatthew Dillon 	 */
4422408d859SMatthew Dillon 	if (info->poll_failed >= POLL_FAIL_RESET) {
4432408d859SMatthew Dillon 	    logdebuginfo(info, 2, "polling mode STARTUP->FAILED\n");
4442408d859SMatthew Dillon 	    info->poll_mode = POLL_FAILED;
4452408d859SMatthew Dillon 	    info->poll_count = 0;
4462408d859SMatthew Dillon 	    break;
4472408d859SMatthew Dillon 	}
4482408d859SMatthew Dillon 
4492408d859SMatthew Dillon 	/*
4502408d859SMatthew Dillon 	 * Transition the server to operational.  Do a number of minimum
4512408d859SMatthew Dillon 	 * interval polls to try to get a good offset calculation quickly.
4522408d859SMatthew Dillon 	 */
4532408d859SMatthew Dillon 	if (info->poll_count)
4542408d859SMatthew Dillon 	    client_setserverstate(info, 1, "connected ok");
4552408d859SMatthew Dillon 	if (info->poll_count < POLL_STARTUP_MAX) {
4562408d859SMatthew Dillon 	    info->poll_sleep = min_sleep_opt;
4572408d859SMatthew Dillon 	    break;
4582408d859SMatthew Dillon 	}
4592408d859SMatthew Dillon 
4602408d859SMatthew Dillon 	/*
4612408d859SMatthew Dillon 	 * Once we've got our polls fall through to aquisition mode to
4622408d859SMatthew Dillon 	 * do aquisition processing.
4632408d859SMatthew Dillon 	 */
464fdd4337fSMatthew Dillon 	info->poll_mode = POLL_ACQUIRE;
465fdd4337fSMatthew Dillon 	info->poll_count = 0;
4662408d859SMatthew Dillon 	logdebuginfo(info, 2, "polling mode STARTUP->ACQUIRE\n");
467fdd4337fSMatthew Dillon 	/* fall through */
468fdd4337fSMatthew Dillon     case POLL_ACQUIRE:
469fdd4337fSMatthew Dillon 	/*
4702408d859SMatthew Dillon 	 * Transition to a FAILED state if too many poll failures occured.
4712408d859SMatthew Dillon 	 */
4722408d859SMatthew Dillon 	if (info->poll_failed >= POLL_FAIL_RESET) {
4732408d859SMatthew Dillon 	    logdebuginfo(info, 2, "polling mode STARTUP->FAILED\n");
4742408d859SMatthew Dillon 	    info->poll_mode = POLL_FAILED;
4752408d859SMatthew Dillon 	    info->poll_count = 0;
4762408d859SMatthew Dillon 	    break;
4772408d859SMatthew Dillon 	}
4782408d859SMatthew Dillon 
4792408d859SMatthew Dillon 	/*
480fdd4337fSMatthew Dillon 	 * Acquisition mode using the nominal timeout.  We do not shift
4813f5e28f4SSascha Wildner 	 * to maintainance mode unless the correlation is at least 0.90
482fdd4337fSMatthew Dillon 	 */
483fdd4337fSMatthew Dillon 	if (info->poll_count < POLL_ACQUIRE_MAX ||
484fdd4337fSMatthew Dillon 	    info->lin_count < 8 ||
485fdd4337fSMatthew Dillon 	    fabs(info->lin_cache_corr) < 0.85
486fdd4337fSMatthew Dillon 	) {
4873d15852bSMatthew Dillon 	    if (info->poll_count >= POLL_ACQUIRE_MAX &&
488fdd4337fSMatthew Dillon 		info->lin_count == LIN_RESTART - 2
489fdd4337fSMatthew Dillon 	    ) {
4902408d859SMatthew Dillon 		logdebuginfo(info, 2,
4912408d859SMatthew Dillon 		    "WARNING: Unable to shift this source to "
4924e8ef5c1SSascha Wildner 		    "maintenance mode.  Target correlation is awful\n");
493fdd4337fSMatthew Dillon 	    }
494fdd4337fSMatthew Dillon 	    break;
495fdd4337fSMatthew Dillon 	}
496fdd4337fSMatthew Dillon 	info->poll_mode = POLL_MAINTAIN;
497fdd4337fSMatthew Dillon 	info->poll_count = 0;
4982408d859SMatthew Dillon 	logdebuginfo(info, 2, "polling mode ACQUIRE->MAINTAIN\n");
499fdd4337fSMatthew Dillon 	/* fall through */
500fdd4337fSMatthew Dillon     case POLL_MAINTAIN:
5012408d859SMatthew Dillon 	/*
5022408d859SMatthew Dillon 	 * Transition to a FAILED state if too many poll failures occured.
5032408d859SMatthew Dillon 	 */
5042408d859SMatthew Dillon 	if (info->poll_failed >= POLL_FAIL_RESET) {
5052408d859SMatthew Dillon 	    logdebuginfo(info, 2, "polling mode STARTUP->FAILED\n");
5062408d859SMatthew Dillon 	    info->poll_mode = POLL_FAILED;
5072408d859SMatthew Dillon 	    info->poll_count = 0;
5082408d859SMatthew Dillon 	    break;
5092408d859SMatthew Dillon 	}
5102408d859SMatthew Dillon 
5112408d859SMatthew Dillon 	/*
5122408d859SMatthew Dillon 	 * Maintaince mode, max polling interval.
5132408d859SMatthew Dillon 	 *
5142408d859SMatthew Dillon 	 * Transition back to acquisition mode if we are unable to maintain
5152408d859SMatthew Dillon 	 * this mode due to the correlation going bad.
5162408d859SMatthew Dillon 	 */
517fdd4337fSMatthew Dillon 	if (info->lin_count >= LIN_RESTART / 2 &&
518fdd4337fSMatthew Dillon 	    fabs(info->lin_cache_corr) < 0.70
519fdd4337fSMatthew Dillon 	) {
5202408d859SMatthew Dillon 	    logdebuginfo(info, 2,
5212408d859SMatthew Dillon 		"polling mode MAINTAIN->ACQUIRE.  Unable to maintain\n"
5223f5e28f4SSascha Wildner 		"the maintenance mode because the correlation went"
5232408d859SMatthew Dillon 		" bad!\n");
524fdd4337fSMatthew Dillon 	    info->poll_mode = POLL_ACQUIRE;
525fdd4337fSMatthew Dillon 	    info->poll_count = 0;
526fdd4337fSMatthew Dillon 	    break;
527fdd4337fSMatthew Dillon 	}
528fdd4337fSMatthew Dillon 	info->poll_sleep = max_sleep_opt;
529fdd4337fSMatthew Dillon 	break;
5302408d859SMatthew Dillon     case POLL_FAILED:
531fdd4337fSMatthew Dillon 	/*
5322408d859SMatthew Dillon 	 * We have a communications failure.  A late recovery is possible
5332408d859SMatthew Dillon 	 * if we enter this state with a good poll.
534fdd4337fSMatthew Dillon 	 */
535fdd4337fSMatthew Dillon 	if (info->poll_count != 0) {
5362408d859SMatthew Dillon 	    logdebuginfo(info, 2, "polling mode FAILED->ACQUIRE\n");
5372408d859SMatthew Dillon 	    if (info->poll_failed >= POLL_FAIL_RESET)
5382408d859SMatthew Dillon 		info->poll_mode = POLL_STARTUP;
5392408d859SMatthew Dillon 	    else
540fdd4337fSMatthew Dillon 		info->poll_mode = POLL_ACQUIRE;
541fdd4337fSMatthew Dillon 	    /* do not reset poll_count */
542fdd4337fSMatthew Dillon 	    break;
543fdd4337fSMatthew Dillon 	}
5442408d859SMatthew Dillon 
545fdd4337fSMatthew Dillon 	/*
5462408d859SMatthew Dillon 	 * If we have been failed too long, disconnect from the server
5472408d859SMatthew Dillon 	 * and start us all over again.  Note that the failed count is not
5482408d859SMatthew Dillon 	 * reset to 0.
549fdd4337fSMatthew Dillon 	 */
5502408d859SMatthew Dillon 	if (info->poll_failed >= POLL_RECOVERY_RESTART) {
5512408d859SMatthew Dillon 	    logdebuginfo(info, 2, "polling mode FAILED->INIT\n");
5522408d859SMatthew Dillon 	    client_setserverstate(info, 0, "FAILED");
5532408d859SMatthew Dillon 	    disconnect_server(info);
5542408d859SMatthew Dillon 	    info->poll_mode = POLL_FIXED;
555fdd4337fSMatthew Dillon 	    break;
556fdd4337fSMatthew Dillon 	}
5572408d859SMatthew Dillon 	break;
5582408d859SMatthew Dillon     }
5592408d859SMatthew Dillon 
5602408d859SMatthew Dillon     /*
5612408d859SMatthew Dillon      * If the above state machine has not set a polling interval, set a
5622408d859SMatthew Dillon      * nominal polling interval.
5632408d859SMatthew Dillon      */
564fdd4337fSMatthew Dillon     if (info->poll_sleep == 0)
565fdd4337fSMatthew Dillon 	info->poll_sleep = nom_sleep_opt;
5662408d859SMatthew Dillon }
5672408d859SMatthew Dillon 
5682408d859SMatthew Dillon /*
5692408d859SMatthew Dillon  * Look for duplicate IP addresses.  This is done very inoften, so we do
5702408d859SMatthew Dillon  * not use a particularly efficient algorithm.
5712408d859SMatthew Dillon  *
5722408d859SMatthew Dillon  * Only reconnect a client which has not done its initial poll.
5732408d859SMatthew Dillon  */
5742408d859SMatthew Dillon void
client_check_duplicate_ips(struct server_info ** info_ary,int count)5752408d859SMatthew Dillon client_check_duplicate_ips(struct server_info **info_ary, int count)
5762408d859SMatthew Dillon {
5772408d859SMatthew Dillon     server_info_t info1;
5782408d859SMatthew Dillon     server_info_t info2;
5792408d859SMatthew Dillon     int tries;
5802408d859SMatthew Dillon     int i;
5812408d859SMatthew Dillon     int j;
5822408d859SMatthew Dillon 
5832408d859SMatthew Dillon     for (i = 0; i < count; ++i) {
5842408d859SMatthew Dillon 	info1 = info_ary[i];
5852408d859SMatthew Dillon 	if (info1->fd < 0 || info1->server_state != 0)
5862408d859SMatthew Dillon 	    continue;
5872408d859SMatthew Dillon 	for (tries = 0; tries < 10; ++tries) {
5882408d859SMatthew Dillon 	    for (j = 0; j < count; ++j) {
5892408d859SMatthew Dillon 		info2 = info_ary[j];
5902408d859SMatthew Dillon 		if (i == j || info2->fd < 0)
5912408d859SMatthew Dillon 		    continue;
5927af666f2SSepherosa Ziehau 		if (info1->fd < 0 || /* info1 was lost in previous reconnect */
5937af666f2SSepherosa Ziehau 		    strcmp(info1->ipstr, info2->ipstr) == 0) {
5942408d859SMatthew Dillon 		    reconnect_server(info1);
595fdd4337fSMatthew Dillon 		    break;
596fdd4337fSMatthew Dillon 		}
597fdd4337fSMatthew Dillon 	    }
5982408d859SMatthew Dillon 	    if (j == count)
5992408d859SMatthew Dillon 		break;
6002408d859SMatthew Dillon 	}
6012408d859SMatthew Dillon 	if (tries == 10) {
6022408d859SMatthew Dillon 	    disconnect_server(info1);
6032408d859SMatthew Dillon 	    client_setserverstate(info1, -2,
6042408d859SMatthew Dillon 				  "permanently disabling duplicate server");
6052408d859SMatthew Dillon 	}
6062408d859SMatthew Dillon     }
6072408d859SMatthew Dillon }
6082408d859SMatthew Dillon 
6092408d859SMatthew Dillon /*
6102408d859SMatthew Dillon  * Calculate whether the server pointed to by *bestp is insane or not.
6112408d859SMatthew Dillon  * For some reason some servers in e.g. the ntp pool are sometimes an hour
6122408d859SMatthew Dillon  * off.  If we have at least three servers in the pool require that a
6132408d859SMatthew Dillon  * quorum agree that the current best server's offset is reasonable.
6142408d859SMatthew Dillon  *
6155e13edd7SMatthew Dillon  * Allow +/- 0.5 seconds of error for now (settable with option).
6162408d859SMatthew Dillon  *
6172408d859SMatthew Dillon  * Returns -1 if insane, 0 if not enough samples, and 1 if ok
6182408d859SMatthew Dillon  */
6192408d859SMatthew Dillon static
6202408d859SMatthew Dillon int
client_insane(struct server_info ** info_ary,int count,server_info_t best)6212408d859SMatthew Dillon client_insane(struct server_info **info_ary, int count, server_info_t best)
6222408d859SMatthew Dillon {
6232408d859SMatthew Dillon     server_info_t info;
6242408d859SMatthew Dillon     double best_offset;
6252408d859SMatthew Dillon     double info_offset;
6262408d859SMatthew Dillon     int good;
6272408d859SMatthew Dillon     int bad;
6282408d859SMatthew Dillon     int skip;
6292408d859SMatthew Dillon     int quorum;
6302408d859SMatthew Dillon     int i;
6312408d859SMatthew Dillon 
6322408d859SMatthew Dillon     /*
6332408d859SMatthew Dillon      * If only one ntp server we cannot check to see if it is insane
6342408d859SMatthew Dillon      */
6352408d859SMatthew Dillon     if (count < 2)
6362408d859SMatthew Dillon 	    return(1);
6372408d859SMatthew Dillon     best_offset = best->lin_sumoffset / best->lin_countoffset;
6382408d859SMatthew Dillon 
6392408d859SMatthew Dillon     /*
6402408d859SMatthew Dillon      * Calculated the quorum.  Do not count permanently failed servers
6412408d859SMatthew Dillon      * in the calculation.
6422408d859SMatthew Dillon      *
6432408d859SMatthew Dillon      * adjusted count	quorum
6442408d859SMatthew Dillon      *   2		  2
6452408d859SMatthew Dillon      *   3		  2
6462408d859SMatthew Dillon      *   4		  3
6472408d859SMatthew Dillon      *   5		  3
6482408d859SMatthew Dillon      */
6492408d859SMatthew Dillon     quorum = count;
6502408d859SMatthew Dillon     for (i = 0; i < count; ++i) {
6512408d859SMatthew Dillon 	info = info_ary[i];
6522408d859SMatthew Dillon 	if (info->server_state == -2)
6532408d859SMatthew Dillon 	    --quorum;
6542408d859SMatthew Dillon     }
6552408d859SMatthew Dillon 
6562408d859SMatthew Dillon     quorum = quorum / 2 + 1;
6572408d859SMatthew Dillon     good = 0;
6582408d859SMatthew Dillon     bad = 0;
6592408d859SMatthew Dillon     skip = 0;
6602408d859SMatthew Dillon 
6612408d859SMatthew Dillon     /*
6625e13edd7SMatthew Dillon      * Find the good, the bad, and the ugly.  We need at least four samples
6635e13edd7SMatthew Dillon      * and a stddev within the deviation being checked to count a server
6645e13edd7SMatthew Dillon      * in the calculation.
6652408d859SMatthew Dillon      */
6662408d859SMatthew Dillon     for (i = 0; i < count; ++i) {
6672408d859SMatthew Dillon 	info = info_ary[i];
6685e13edd7SMatthew Dillon 	if (info->lin_countoffset < 4 ||
6695e13edd7SMatthew Dillon 	    info->lin_cache_stddev > insane_deviation
6705e13edd7SMatthew Dillon 	) {
6712408d859SMatthew Dillon 	    ++skip;
6722408d859SMatthew Dillon 	    continue;
6732408d859SMatthew Dillon 	}
6745e13edd7SMatthew Dillon 
6752408d859SMatthew Dillon 	info_offset = info->lin_sumoffset / info->lin_countoffset;
6762408d859SMatthew Dillon 	info_offset -= best_offset;
6775e13edd7SMatthew Dillon 	if (info_offset < -insane_deviation || info_offset > insane_deviation)
6782408d859SMatthew Dillon 		++bad;
6792408d859SMatthew Dillon 	else
6802408d859SMatthew Dillon 		++good;
6812408d859SMatthew Dillon     }
6822408d859SMatthew Dillon 
6832408d859SMatthew Dillon     /*
6842408d859SMatthew Dillon      * Did we meet our quorum?
6852408d859SMatthew Dillon      */
6865e13edd7SMatthew Dillon     logdebuginfo(best, 5, "insanecheck good=%d bad=%d skip=%d "
6875e13edd7SMatthew Dillon 			  "quorum=%d (allowed=%-+8.6f)\n",
6885e13edd7SMatthew Dillon 		 good, bad, skip, quorum, insane_deviation);
6892408d859SMatthew Dillon     if (good >= quorum)
6902408d859SMatthew Dillon 	return(1);
6912408d859SMatthew Dillon     if (good + skip >= quorum)
6922408d859SMatthew Dillon 	return(0);
6932408d859SMatthew Dillon     return(-1);
6942408d859SMatthew Dillon }
695fdd4337fSMatthew Dillon 
696fdd4337fSMatthew Dillon /*
697e0163549SMatthew Dillon  * Linear regression.
698e0163549SMatthew Dillon  *
699e0163549SMatthew Dillon  *	ltv	local time as of when the offset error was calculated between
700e0163549SMatthew Dillon  *		local time and remote time.
701e0163549SMatthew Dillon  *
702e0163549SMatthew Dillon  *	lbtv	base time as of when local time was obtained.  Used to
703e0163549SMatthew Dillon  *		calculate the cumulative corrections made to the system's
704e0163549SMatthew Dillon  *		real time clock so we can de-correct the offset for the
705e0163549SMatthew Dillon  *		linear regression.
706e0163549SMatthew Dillon  *
707e0163549SMatthew Dillon  * X is the time axis, in seconds.
708e0163549SMatthew Dillon  * Y is the uncorrected offset, in seconds.
709e0163549SMatthew Dillon  */
710e0163549SMatthew Dillon void
lin_regress(server_info_t info,struct timeval * ltv,struct timeval * lbtv,double offset,int calc_offset_correction)711e0163549SMatthew Dillon lin_regress(server_info_t info, struct timeval *ltv, struct timeval *lbtv,
712c32f1362SMatthew Dillon 	    double offset, int calc_offset_correction)
713e0163549SMatthew Dillon {
714e0163549SMatthew Dillon     double time_axis;
715e0163549SMatthew Dillon     double uncorrected_offset;
716e0163549SMatthew Dillon 
7178e4e9813SMatthew Dillon     /*
7188e4e9813SMatthew Dillon      * De-correcting the offset:
7198e4e9813SMatthew Dillon      *
7208e4e9813SMatthew Dillon      *	The passed offset is (our_real_time - remote_real_time).  To remove
7218e4e9813SMatthew Dillon      *  corrections from our_real_time we take the difference in the basetime
7228e4e9813SMatthew Dillon      *  (new_base_time - old_base_time) and subtract that from the offset.
7238e4e9813SMatthew Dillon      *  That is, if the basetime goesup, the uncorrected offset goes down.
7248e4e9813SMatthew Dillon      */
725e0163549SMatthew Dillon     if (info->lin_count == 0) {
726e0163549SMatthew Dillon 	info->lin_tv = *ltv;
727e0163549SMatthew Dillon 	info->lin_btv = *lbtv;
728e0163549SMatthew Dillon 	time_axis = 0;
729e0163549SMatthew Dillon 	uncorrected_offset = offset;
730e0163549SMatthew Dillon     } else {
7318e4e9813SMatthew Dillon 	time_axis = tv_delta_double(&info->lin_tv, ltv);
7328e4e9813SMatthew Dillon 	uncorrected_offset = offset - tv_delta_double(&info->lin_btv, lbtv);
733e0163549SMatthew Dillon     }
7348e4e9813SMatthew Dillon 
7358e4e9813SMatthew Dillon     /*
7368e4e9813SMatthew Dillon      * We have to use the uncorrected offset for frequency calculations.
7378e4e9813SMatthew Dillon      */
738e0163549SMatthew Dillon     ++info->lin_count;
739e0163549SMatthew Dillon     info->lin_sumx += time_axis;
740e0163549SMatthew Dillon     info->lin_sumx2 += time_axis * time_axis;
741e0163549SMatthew Dillon     info->lin_sumy += uncorrected_offset;
742e0163549SMatthew Dillon     info->lin_sumy2 += uncorrected_offset * uncorrected_offset;
743e0163549SMatthew Dillon     info->lin_sumxy += time_axis * uncorrected_offset;
744e0163549SMatthew Dillon 
745e0163549SMatthew Dillon     /*
7468e4e9813SMatthew Dillon      * We have to use the corrected offset for offset calculations.
747e0163549SMatthew Dillon      */
748c32f1362SMatthew Dillon     if (calc_offset_correction) {
7498e4e9813SMatthew Dillon 	++info->lin_countoffset;
7508e4e9813SMatthew Dillon 	info->lin_sumoffset += offset;
7518e4e9813SMatthew Dillon 	info->lin_sumoffset2 += offset * offset;
752c32f1362SMatthew Dillon     }
753e0163549SMatthew Dillon 
7548e4e9813SMatthew Dillon     /*
7558e4e9813SMatthew Dillon      * Calculate various derived values.   This gets us slope, y-intercept,
7563f5e28f4SSascha Wildner      * and correlation from the linear regression.
7578e4e9813SMatthew Dillon      */
7589b724d8cSMatthew Dillon     if (info->lin_count > 1) {
759e0163549SMatthew Dillon 	info->lin_cache_slope =
760e0163549SMatthew Dillon 	 (info->lin_count * info->lin_sumxy - info->lin_sumx * info->lin_sumy) /
761e0163549SMatthew Dillon 	 (info->lin_count * info->lin_sumx2 - info->lin_sumx * info->lin_sumx);
762e0163549SMatthew Dillon 
763e0163549SMatthew Dillon 	info->lin_cache_yint =
764e0163549SMatthew Dillon 	 (info->lin_sumy - info->lin_cache_slope * info->lin_sumx) /
765e0163549SMatthew Dillon 	 (info->lin_count);
766e0163549SMatthew Dillon 
767e0163549SMatthew Dillon 	info->lin_cache_corr =
768e0163549SMatthew Dillon 	 (info->lin_count * info->lin_sumxy - info->lin_sumx * info->lin_sumy) /
769e0163549SMatthew Dillon 	 sqrt((info->lin_count * info->lin_sumx2 -
770e0163549SMatthew Dillon 		      info->lin_sumx * info->lin_sumx) *
771e0163549SMatthew Dillon 	     (info->lin_count * info->lin_sumy2 -
772e0163549SMatthew Dillon 		      info->lin_sumy * info->lin_sumy)
773e0163549SMatthew Dillon 	 );
7749b724d8cSMatthew Dillon     }
775e0163549SMatthew Dillon 
776e0163549SMatthew Dillon     /*
7778e4e9813SMatthew Dillon      * Calculate more derived values.  This gets us the standard-deviation
7788e4e9813SMatthew Dillon      * of offsets.  The standard deviation approximately means that 68%
7798e4e9813SMatthew Dillon      * of the samples fall within the calculated stddev of the mean.
7808e4e9813SMatthew Dillon      */
7818e4e9813SMatthew Dillon     if (info->lin_countoffset > 1) {
7828e4e9813SMatthew Dillon 	 info->lin_cache_stddev =
7838e4e9813SMatthew Dillon 	     sqrt((info->lin_sumoffset2 -
7848e4e9813SMatthew Dillon 		 ((info->lin_sumoffset * info->lin_sumoffset /
7858e4e9813SMatthew Dillon 		   info->lin_countoffset))) /
7868e4e9813SMatthew Dillon 	         (info->lin_countoffset - 1.0));
7878e4e9813SMatthew Dillon     }
7888e4e9813SMatthew Dillon 
7898e4e9813SMatthew Dillon     /*
7908e4e9813SMatthew Dillon      * Save the most recent offset, we might use it in the future.
7918e4e9813SMatthew Dillon      * Save the frequency correction (we might scale the slope later so
7928e4e9813SMatthew Dillon      * we have a separate field for the actual frequency correction in
7938e4e9813SMatthew Dillon      * seconds per second).
794e0163549SMatthew Dillon      */
795e0163549SMatthew Dillon     info->lin_cache_offset = offset;
796e0163549SMatthew Dillon     info->lin_cache_freq = info->lin_cache_slope;
797e0163549SMatthew Dillon 
7983d15852bSMatthew Dillon     if (debug_level >= 4) {
7994709be8cSMatthew Dillon 	logdebuginfo(info, 4, "iter=%2d time=%7.3f off=%+.6f uoff=%+.6f",
8009b724d8cSMatthew Dillon 	    (int)info->lin_count,
8019b724d8cSMatthew Dillon 	    time_axis, offset, uncorrected_offset);
8029b724d8cSMatthew Dillon 	if (info->lin_count > 1) {
8034709be8cSMatthew Dillon 	    logdebug(4, " slope %+7.6f"
8044709be8cSMatthew Dillon 			    " yint %+3.2f corr %+7.6f freq_ppm %+4.2f",
805e0163549SMatthew Dillon 		info->lin_cache_slope,
806e0163549SMatthew Dillon 		info->lin_cache_yint,
807e0163549SMatthew Dillon 		info->lin_cache_corr,
808e0163549SMatthew Dillon 		info->lin_cache_freq * 1000000.0);
809e0163549SMatthew Dillon 	}
8108e4e9813SMatthew Dillon 	if (info->lin_countoffset > 1) {
8113d15852bSMatthew Dillon 	    logdebug(4, " stddev %7.6f", info->lin_cache_stddev);
812c32f1362SMatthew Dillon 	} else if (calc_offset_correction == 0) {
813c32f1362SMatthew Dillon 	    /* cannot calculate offset correction due to prior correction */
814c32f1362SMatthew Dillon 	    logdebug(4, " offset_ignored");
8158e4e9813SMatthew Dillon 	}
8163d15852bSMatthew Dillon 	logdebug(4, "\n");
8179b724d8cSMatthew Dillon     }
818e0163549SMatthew Dillon }
819e0163549SMatthew Dillon 
8208e4e9813SMatthew Dillon /*
8218e4e9813SMatthew Dillon  * Reset the linear regression data.  The info structure will not again be
8228e4e9813SMatthew Dillon  * a candidate for frequency or offset correction until sufficient data
8238e4e9813SMatthew Dillon  * has been accumulated to make a decision.
8248e4e9813SMatthew Dillon  */
825e0163549SMatthew Dillon void
lin_reset(server_info_t info)826e0163549SMatthew Dillon lin_reset(server_info_t info)
827e0163549SMatthew Dillon {
828fdd4337fSMatthew Dillon     server_info_t scan;
829fdd4337fSMatthew Dillon 
830e0163549SMatthew Dillon     info->lin_count = 0;
831e0163549SMatthew Dillon     info->lin_sumx = 0;
832e0163549SMatthew Dillon     info->lin_sumy = 0;
833e0163549SMatthew Dillon     info->lin_sumxy = 0;
834e0163549SMatthew Dillon     info->lin_sumx2 = 0;
835e0163549SMatthew Dillon     info->lin_sumy2 = 0;
8368e4e9813SMatthew Dillon 
8378e4e9813SMatthew Dillon     info->lin_countoffset = 0;
838e0163549SMatthew Dillon     info->lin_sumoffset = 0;
8398e4e9813SMatthew Dillon     info->lin_sumoffset2 = 0;
840e0163549SMatthew Dillon 
841e0163549SMatthew Dillon     info->lin_cache_slope = 0;
842e0163549SMatthew Dillon     info->lin_cache_yint = 0;
843e0163549SMatthew Dillon     info->lin_cache_corr = 0;
844e0163549SMatthew Dillon     info->lin_cache_offset = 0;
845e0163549SMatthew Dillon     info->lin_cache_freq = 0;
846fdd4337fSMatthew Dillon 
847fdd4337fSMatthew Dillon     /*
848fdd4337fSMatthew Dillon      * Destroy any additional alternative regressions.
849fdd4337fSMatthew Dillon      */
850fdd4337fSMatthew Dillon     while ((scan = info->altinfo) != NULL) {
851fdd4337fSMatthew Dillon 	info->altinfo = scan->altinfo;
852fdd4337fSMatthew Dillon 	free(scan);
853fdd4337fSMatthew Dillon     }
854e0163549SMatthew Dillon }
855e0163549SMatthew Dillon 
8568e4e9813SMatthew Dillon /*
8578e4e9813SMatthew Dillon  * Sometimes we want to clean out the offset calculations without
8588e4e9813SMatthew Dillon  * destroying the linear regression used to figure out the frequency
8598e4e9813SMatthew Dillon  * correction.  This usually occurs whenever we issue an offset
8608e4e9813SMatthew Dillon  * adjustment to the system, which invalidates any offset data accumulated
8618e4e9813SMatthew Dillon  * up to that point.
8628e4e9813SMatthew Dillon  */
8638e4e9813SMatthew Dillon void
lin_resetalloffsets(struct server_info ** info_ary,int count)8648e4e9813SMatthew Dillon lin_resetalloffsets(struct server_info **info_ary, int count)
8658e4e9813SMatthew Dillon {
8668e4e9813SMatthew Dillon     server_info_t info;
8678e4e9813SMatthew Dillon     int i;
8688e4e9813SMatthew Dillon 
8698e4e9813SMatthew Dillon     for (i = 0; i < count; ++i) {
8708e4e9813SMatthew Dillon 	for (info = info_ary[i]; info; info = info->altinfo)
8718e4e9813SMatthew Dillon 	    lin_resetoffsets(info);
8728e4e9813SMatthew Dillon     }
8738e4e9813SMatthew Dillon }
8748e4e9813SMatthew Dillon 
8758e4e9813SMatthew Dillon void
lin_resetoffsets(server_info_t info)8768e4e9813SMatthew Dillon lin_resetoffsets(server_info_t info)
8778e4e9813SMatthew Dillon {
8788e4e9813SMatthew Dillon     info->lin_countoffset = 0;
8798e4e9813SMatthew Dillon     info->lin_sumoffset = 0;
8808e4e9813SMatthew Dillon     info->lin_sumoffset2 = 0;
8818e4e9813SMatthew Dillon }
8828e4e9813SMatthew Dillon 
8832408d859SMatthew Dillon void
client_setserverstate(server_info_t info,int state,const char * str)8842408d859SMatthew Dillon client_setserverstate(server_info_t info, int state, const char *str)
8852408d859SMatthew Dillon {
8862408d859SMatthew Dillon     if (info->server_state != state) {
8872408d859SMatthew Dillon         info->server_state = state;
8882408d859SMatthew Dillon 	logdebuginfo(info, 1, "%s\n", str);
8892408d859SMatthew Dillon     }
8902408d859SMatthew Dillon }
8912408d859SMatthew Dillon 
892