diff --git a/chrony.texi b/chrony.texi index bee73f5..d25421f 100644 --- a/chrony.texi +++ b/chrony.texi @@ -1828,7 +1828,7 @@ An example line (which actually appears as a single line in the file) from the measurements log file is shown below. @example -1998-07-22 05:40:50 158.152.1.76 N 8 1111 11 1111 10 10 1 \ +2010-12-22 05:40:50 158.152.1.76 N 8 1111 111 1111 10 10 1.0 \ -4.966e-03 2.296e-01 1.577e-05 1.615e-01 7.446e-03 @end example @@ -1837,7 +1837,7 @@ values from the example line above) : @enumerate 1 @item -Date [1998-07-22] +Date [2010-12-22] @item Hour:Minute:Second [05:40:50]. Note that the date/time pair is expressed in UTC, not the local time zone. @@ -1853,8 +1853,8 @@ Stratum of remote computer. [2] @item RFC1305 tests 1 through 4 (1=pass, 0=fail) [1111] @item -Tests for maximum delay and maximum delay ratio, against user defined -parameters (1=pass, 0=fail) [11] +Tests for maximum delay, maximum delay ratio and maximum delay dev ratio, +against defined parameters (1=pass, 0=fail) [111] @item RFC1305 tests 5 through 8 (1=pass, 0=fail) [1111] @item @@ -2595,6 +2595,12 @@ measurements that it has buffered. If a measurement has a round trip delay that is greater than the maxdelayratio times the minimum delay, it will be rejected. +@item maxdelaydevratio +If a measurement has ratio of the increase in round-trip delay from +the minimum delay amongst the previous measurements to the standard +deviation of the previous measurements that is greater than +maxdelaydevratio, it will be rejected. The default is 10.0. + @item presend If the timing measurements being made by @code{chronyd} are the only network data passing between two computers, you may find that some diff --git a/client.c b/client.c index eaf92ab..67456e4 100644 --- a/client.c +++ b/client.c @@ -963,6 +963,11 @@ process_cmd_add_server_or_peer(CMD_Request *msg, char *line) break; } + if (data.params.max_delay_dev_ratio != SRC_DEFAULT_MAXDELAYDEVRATIO) { + fprintf(stderr, "Option maxdelaydevratio not supported\n"); + break; + } + msg->data.ntp_source.port = htonl((unsigned long) data.port); UTI_IPHostToNetwork(&data.ip_addr, &msg->data.ntp_source.ip_addr); msg->data.ntp_source.minpoll = htonl(data.params.minpoll); @@ -998,6 +1003,9 @@ process_cmd_add_server_or_peer(CMD_Request *msg, char *line) case CPS_BadPresend: fprintf(stderr, "Unreadable presend value\n"); break; + case CPS_BadMaxdelaydevratio: + fprintf(stderr, "Unreadable max delay dev ratio value\n"); + break; case CPS_BadMaxdelayratio: fprintf(stderr, "Unreadable max delay ratio value\n"); break; diff --git a/cmdmon.c b/cmdmon.c index 884c623..3d5df74 100644 --- a/cmdmon.c +++ b/cmdmon.c @@ -1271,6 +1271,7 @@ handle_add_source(NTP_Source_Type type, CMD_Request *rx_message, CMD_Reply *tx_m /* not transmitted in cmdmon protocol yet */ params.min_stratum = SRC_DEFAULT_MINSTRATUM; params.poll_target = SRC_DEFAULT_POLLTARGET; + params.max_delay_dev_ratio = SRC_DEFAULT_MAXDELAYDEVRATIO; status = NSR_AddSource(&rem_addr, type, ¶ms); switch (status) { diff --git a/cmdparse.c b/cmdparse.c index be45989..983c334 100644 --- a/cmdparse.c +++ b/cmdparse.c @@ -56,6 +56,7 @@ CPS_ParseNTPSourceAdd(const char *line, CPS_NTP_Source *src) src->params.authkey = INACTIVE_AUTHKEY; src->params.max_delay = SRC_DEFAULT_MAXDELAY; src->params.max_delay_ratio = SRC_DEFAULT_MAXDELAYRATIO; + src->params.max_delay_dev_ratio = SRC_DEFAULT_MAXDELAYDEVRATIO; src->params.online = 1; src->params.auto_offline = 0; src->params.iburst = 0; @@ -123,6 +124,14 @@ CPS_ParseNTPSourceAdd(const char *line, CPS_NTP_Source *src) } else { line += n; } + } else if (!strncasecmp(cmd, "maxdelaydevratio", 16)) { + if (sscanf(line, "%lf%n", &src->params.max_delay_dev_ratio, &n) != 1) { + result = CPS_BadMaxdelaydevratio; + ok = 0; + done = 1; + } else { + line += n; + } /* This MUST come before the following one ! */ } else if (!strncasecmp(cmd, "maxdelayratio", 13)) { if (sscanf(line, "%lf%n", &src->params.max_delay_ratio, &n) != 1) { diff --git a/cmdparse.h b/cmdparse.h index a6353bd..0f41cfd 100644 --- a/cmdparse.h +++ b/cmdparse.h @@ -42,6 +42,7 @@ typedef enum { CPS_BadMinpoll, CPS_BadMaxpoll, CPS_BadPresend, + CPS_BadMaxdelaydevratio, CPS_BadMaxdelayratio, CPS_BadMaxdelay, CPS_BadKey, diff --git a/conf.c b/conf.c index f6fd9b2..b64c4bc 100644 --- a/conf.c +++ b/conf.c @@ -391,6 +391,9 @@ parse_source(const char *line, NTP_Source_Type type) case CPS_BadPresend: LOG(LOGS_WARN, LOGF_Configure, "Unreadable presend value at line %d", line_number); break; + case CPS_BadMaxdelaydevratio: + LOG(LOGS_WARN, LOGF_Configure, "Unreadable max delay dev ratio value at line %d", line_number); + break; case CPS_BadMaxdelayratio: LOG(LOGS_WARN, LOGF_Configure, "Unreadable max delay ratio value at line %d", line_number); break; diff --git a/ntp_core.c b/ntp_core.c index 8306ad2..4211594 100644 --- a/ntp_core.c +++ b/ntp_core.c @@ -119,6 +119,8 @@ struct NCR_Instance_Record { min_delay_in_register that we can tolerate. */ + double max_delay_dev_ratio; /* Maximum ratio of increase in delay / stddev */ + int do_auth; /* Flag indicating whether we authenticate packets we send to this machine (if it's serving us or @@ -213,7 +215,7 @@ void NCR_Initialise(void) { logfileid = CNF_GetLogMeasurements() ? LOG_FileOpen("measurements", - " Date (UTC) Time IP Address L St 1234 ab 5678 LP RP Score Offset Peer del. Peer disp. Root del. Root disp.") + " Date (UTC) Time IP Address L St 1234 abc 5678 LP RP Score Offset Peer del. Peer disp. Root del. Root disp.") : -1; access_auth_table = ADF_CreateTable(); @@ -284,6 +286,7 @@ NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourcePar result->max_delay = params->max_delay; result->max_delay_ratio = params->max_delay_ratio; + result->max_delay_dev_ratio = params->max_delay_dev_ratio; result->tx_count = 0; @@ -777,7 +780,7 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins int test1, test2, test3, test4, test5, test6, test7, test8; - int test4a, test4b; + int test4a, test4b, test4c; /* In the words of section 3.4.4 of RFC1305, valid_data means that the NTP protocol association with the peer/server is @@ -941,6 +944,18 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins test4b = 1; /* Success */ } + /* Test 4c (additional to RFC1305) requires that the ratio of the + increase in delay from the minimum one in the stats data register to + the standard deviation of the offsets in the register is less than an + administrator-defined value or the difference between measured offset + and predicted offset is larger than the increase in delay */ + if (!SRC_IsGoodSample(inst->source, -theta, delta, inst->max_delay_dev_ratio, + LCL_GetMaxClockError(), &sample_time)) { + test4c = 0; /* Failed */ + } else { + test4c = 1; /* Success */ + } + /* Test 5 relates to authentication. */ if (inst->do_auth) { if (do_auth) { @@ -1011,7 +1026,7 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins valid_kod = test1 && test2 && test5; - valid_data = test1 && test2 && test3 && test4 && test4a && test4b; + valid_data = test1 && test2 && test3 && test4 && test4a && test4b && test4c; valid_header = test5 && test6 && test7 && test8; root_delay = pkt_root_delay + delta; @@ -1240,13 +1255,13 @@ receive_packet(NTP_Packet *message, struct timeval *now, double now_err, NCR_Ins /* Do measurement logging */ if (logfileid != -1) { - LOG_FileWrite(logfileid, "%s %-15s %1c %2d %1d%1d%1d%1d %1d%1d %1d%1d%1d%1d %2d %2d %4.2f %10.3e %10.3e %10.3e %10.3e %10.3e", + LOG_FileWrite(logfileid, "%s %-15s %1c %2d %1d%1d%1d%1d %1d%1d%1d %1d%1d%1d%1d %2d %2d %4.2f %10.3e %10.3e %10.3e %10.3e %10.3e", UTI_TimeToLogForm(sample_time.tv_sec), UTI_IPToString(&inst->remote_addr.ip_addr), sync_stats[pkt_leap], message->stratum, test1, test2, test3, test4, - test4a, test4b, + test4a, test4b, test4c, test5, test6, test7, test8, inst->local_poll, inst->remote_poll, inst->poll_score, diff --git a/sources.c b/sources.c index 38706ca..20c0855 100644 --- a/sources.c +++ b/sources.c @@ -794,6 +794,16 @@ SRC_MinRoundTripDelay(SRC_Instance inst) return SST_MinRoundTripDelay(inst->stats); } +/* ================================================== */ + +int +SRC_IsGoodSample(SRC_Instance inst, double offset, double delay, + double max_delay_dev_ratio, double clock_error, struct timeval *when) +{ + return SST_IsGoodSample(inst->stats, offset, delay, max_delay_dev_ratio, + clock_error, when); +} + /* ================================================== */ /* This routine is registered as a callback with the local clock module, to be called whenever the local clock changes frequency or diff --git a/sources.h b/sources.h index d89ce59..b731191 100644 --- a/sources.h +++ b/sources.h @@ -145,6 +145,10 @@ extern double SRC_PredictOffset(SRC_Instance inst, struct timeval *when); currently held in the register */ extern double SRC_MinRoundTripDelay(SRC_Instance inst); +/* This routine determines if a new sample is good enough that it should be + accumulated */ +extern int SRC_IsGoodSample(SRC_Instance inst, double offset, double delay, double max_delay_dev_ratio, double clock_error, struct timeval *when); + extern void SRC_DumpSources(void); extern void SRC_ReloadSources(void); diff --git a/sourcestats.c b/sourcestats.c index c149db9..522abd0 100644 --- a/sourcestats.c +++ b/sourcestats.c @@ -693,6 +693,45 @@ SST_MinRoundTripDelay(SST_Stats inst) return inst->peer_delays[inst->min_delay_sample]; } +/* ================================================== */ + +int +SST_IsGoodSample(SST_Stats inst, double offset, double delay, + double max_delay_dev_ratio, double clock_error, struct timeval *when) +{ + double elapsed, allowed_increase, delay_increase; + + if (inst->n_samples < 3) + return 1; + + UTI_DiffTimevalsToDouble(&elapsed, when, &inst->offset_time); + + /* Require that the ratio of the increase in delay from the minimum to the + standard deviation is less than max_delay_dev_ratio. In the allowed + increase in delay include also skew and clock_error. */ + + allowed_increase = sqrt(inst->variance) * max_delay_dev_ratio + + elapsed * (inst->skew + clock_error); + delay_increase = (delay - SST_MinRoundTripDelay(inst)) / 2.0; + + if (delay_increase < allowed_increase) + return 1; + + offset -= inst->estimated_offset + elapsed * inst->estimated_frequency; + + /* Before we decide to drop the sample, make sure the difference between + measured offset and predicted offset is not significantly larger than + the increase in delay */ + if (fabs(offset) - delay_increase > allowed_increase) + return 1; + +#if 0 + LOG(LOGS_INFO, LOGF_SourceStats, "bad sample: offset=%f delay=%f incr_delay=%f allowed=%f", offset, delay, allowed_increase, delay_increase); +#endif + + return 0; +} + /* ================================================== */ /* This is used to save the register to a file, so that we can reload it after restarting the daemon */ diff --git a/sourcestats.h b/sourcestats.h index e47287f..27908c8 100644 --- a/sourcestats.h +++ b/sourcestats.h @@ -137,6 +137,11 @@ extern double SST_PredictOffset(SST_Stats inst, struct timeval *when); /* Find the minimum round trip delay in the register */ extern double SST_MinRoundTripDelay(SST_Stats inst); +/* This routine determines if a new sample is good enough that it should be + accumulated */ +extern int SST_IsGoodSample(SST_Stats inst, double offset, double delay, + double max_delay_dev_ratio, double clock_error, struct timeval *when); + extern void SST_SaveToFile(SST_Stats inst, FILE *out); extern int SST_LoadFromFile(SST_Stats inst, FILE *in); diff --git a/srcparams.h b/srcparams.h index 58395cb..0cab90a 100644 --- a/srcparams.h +++ b/srcparams.h @@ -45,6 +45,7 @@ typedef struct { unsigned long authkey; double max_delay; double max_delay_ratio; + double max_delay_dev_ratio; SRC_SelectOption sel_option; } SourceParameters; @@ -54,6 +55,7 @@ typedef struct { #define SRC_DEFAULT_PRESEND_MINPOLL 0 #define SRC_DEFAULT_MAXDELAY 16.0 #define SRC_DEFAULT_MAXDELAYRATIO 16384.0 +#define SRC_DEFAULT_MAXDELAYDEVRATIO 10.0 #define SRC_DEFAULT_MINSTRATUM 0 #define SRC_DEFAULT_POLLTARGET 6 #define INACTIVE_AUTHKEY 0UL