mirror of
https://gitlab.com/chrony/chrony.git
synced 2025-12-03 18:25:07 -05:00
Compare commits
85 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b197953e8 | ||
|
|
9dcace0fc4 | ||
|
|
a07ac38331 | ||
|
|
166e43b13e | ||
|
|
b84d6759f9 | ||
|
|
f323c814af | ||
|
|
19b47dcbc9 | ||
|
|
5edeadcbd9 | ||
|
|
d91ae2094f | ||
|
|
30a5845098 | ||
|
|
0f367efac5 | ||
|
|
24c011d4a6 | ||
|
|
0c2cdd2fb1 | ||
|
|
cd1a666e1b | ||
|
|
070b4f69d0 | ||
|
|
851c823b42 | ||
|
|
df80274644 | ||
|
|
bb2d68ddf9 | ||
|
|
685d8f725b | ||
|
|
4234732b08 | ||
|
|
a16094adfb | ||
|
|
a4349b13df | ||
|
|
3556dadea1 | ||
|
|
220e6d1907 | ||
|
|
a738037705 | ||
|
|
7daf34675a | ||
|
|
de598c2310 | ||
|
|
91cc4dbb12 | ||
|
|
0ae6f2485b | ||
|
|
52ec694d2b | ||
|
|
e2e07af8a4 | ||
|
|
2ed88c31c7 | ||
|
|
af8e4a5115 | ||
|
|
f503a9a490 | ||
|
|
9c64fbb9c4 | ||
|
|
b428f901c7 | ||
|
|
09b7f77f9a | ||
|
|
c23c0b8484 | ||
|
|
d530055917 | ||
|
|
f41d09e19f | ||
|
|
46030d9d3e | ||
|
|
02ccd3a3c7 | ||
|
|
9cc609c4b0 | ||
|
|
a0a496dcb4 | ||
|
|
8d08486edf | ||
|
|
a3b376cf0a | ||
|
|
e66f1df89d | ||
|
|
35220aac9d | ||
|
|
5b04f3ca90 | ||
|
|
beb1c36136 | ||
|
|
da3495c472 | ||
|
|
356771c0c3 | ||
|
|
fca8966ada | ||
|
|
25f80a1a9d | ||
|
|
1219f99935 | ||
|
|
33a1fe7a9c | ||
|
|
eed0a0de56 | ||
|
|
07600cbd71 | ||
|
|
f2e341b5ed | ||
|
|
55717c1ccd | ||
|
|
d5e645eb38 | ||
|
|
3196630fb9 | ||
|
|
663dde1ad7 | ||
|
|
62757cda49 | ||
|
|
af6ae9186b | ||
|
|
4c29f8888c | ||
|
|
d06ae4a60e | ||
|
|
f9af2f9733 | ||
|
|
43ae0131cd | ||
|
|
8bb8f15a7d | ||
|
|
e55f174bd3 | ||
|
|
5bd13c8d59 | ||
|
|
759580aa6f | ||
|
|
b61cbed689 | ||
|
|
2ac2247756 | ||
|
|
55f48b14b7 | ||
|
|
3dfac33858 | ||
|
|
d5f2401421 | ||
|
|
fb0570cc73 | ||
|
|
43936ba0d1 | ||
|
|
f2ba20f293 | ||
|
|
fcd384523b | ||
|
|
48bce351bf | ||
|
|
25f93875d9 | ||
|
|
ebc610fcb3 |
@@ -35,7 +35,7 @@ LDFLAGS = @LDFLAGS@
|
||||
|
||||
EXTRA_OBJS = @EXTRA_OBJS@
|
||||
|
||||
OBJS = array.o cmdparse.o conf.o local.o logging.o main.o memory.o \
|
||||
OBJS = array.o cmdparse.o conf.o local.o logging.o main.o memory.o quantiles.o \
|
||||
reference.o regress.o rtc.o samplefilt.o sched.o socket.o sources.o sourcestats.o \
|
||||
stubs.o smooth.o sys.o sys_null.o tempcomp.o util.o $(EXTRA_OBJS)
|
||||
|
||||
|
||||
26
NEWS
26
NEWS
@@ -1,3 +1,24 @@
|
||||
New in version 4.3
|
||||
==================
|
||||
|
||||
Enhancements
|
||||
------------
|
||||
* Add local option to refclock directive to stabilise system clock
|
||||
with more stable free-running clock (e.g. TCXO, OCXO)
|
||||
* Add maxdelayquant option to server/pool/peer directive to replace
|
||||
maxdelaydevratio filter with long-term quantile-based filtering
|
||||
* Add selection option to log directive
|
||||
* Allow external PPS in PHC refclock without configurable pin
|
||||
* Don't accept first interleaved response to minimise error in delay
|
||||
* Don't use arc4random on Linux to avoid server performance loss
|
||||
* Improve filter option to better handle missing NTP samples
|
||||
* Improve stability with hardware timestamping and PHC refclock
|
||||
* Update seccomp filter
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
* Fix waitsync command to reconnect when not getting response
|
||||
|
||||
New in version 4.2
|
||||
==================
|
||||
|
||||
@@ -16,6 +37,11 @@ Bug fixes
|
||||
---------
|
||||
* Fix RTC support with 64-bit time_t on 32-bit Linux
|
||||
* Fix seccomp filter to work correctly with bind*device directives
|
||||
* Suppress kernel adjustments of system clock (dosynctodr) on illumos
|
||||
|
||||
Other changes
|
||||
-------------
|
||||
* Switch Solaris support to illumos
|
||||
|
||||
New in version 4.1
|
||||
==================
|
||||
|
||||
2
README
2
README
@@ -28,7 +28,7 @@ What will chrony run on?
|
||||
========================
|
||||
|
||||
The software is known to work on Linux, FreeBSD, NetBSD, macOS and
|
||||
Solaris. Closely related systems may work too. Any other system will
|
||||
illumos. Closely related systems may work too. Any other system will
|
||||
likely require a porting exercise.
|
||||
|
||||
How do I set it up?
|
||||
|
||||
6
candm.h
6
candm.h
@@ -296,7 +296,8 @@ typedef struct {
|
||||
uint32_t flags;
|
||||
int32_t filter_length;
|
||||
uint32_t cert_set;
|
||||
uint32_t reserved[2];
|
||||
Float max_delay_quant;
|
||||
uint32_t reserved[1];
|
||||
int32_t EOR;
|
||||
} REQ_NTP_Source;
|
||||
|
||||
@@ -733,7 +734,8 @@ typedef struct {
|
||||
uint32_t total_tx_count;
|
||||
uint32_t total_rx_count;
|
||||
uint32_t total_valid_count;
|
||||
uint32_t reserved[4];
|
||||
uint32_t total_good_count;
|
||||
uint32_t reserved[3];
|
||||
int32_t EOR;
|
||||
} RPY_NTPData;
|
||||
|
||||
|
||||
69
client.c
69
client.c
@@ -283,6 +283,9 @@ open_io(void)
|
||||
close_io();
|
||||
}
|
||||
|
||||
/* Start from the first address if called again */
|
||||
address_index = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -949,6 +952,8 @@ process_cmd_add_source(CMD_Request *msg, char *line)
|
||||
(data.params.sel_options & SRC_SELECT_REQUIRE ? REQ_ADDSRC_REQUIRE : 0));
|
||||
msg->data.ntp_source.filter_length = htonl(data.params.filter_length);
|
||||
msg->data.ntp_source.cert_set = htonl(data.params.cert_set);
|
||||
msg->data.ntp_source.max_delay_quant =
|
||||
UTI_FloatHostToNetwork(data.params.max_delay_quant);
|
||||
memset(msg->data.ntp_source.reserved, 0, sizeof (msg->data.ntp_source.reserved));
|
||||
|
||||
result = 1;
|
||||
@@ -2341,7 +2346,8 @@ process_cmd_ntpdata(char *line)
|
||||
"RX timestamping : %N\n"
|
||||
"Total TX : %U\n"
|
||||
"Total RX : %U\n"
|
||||
"Total valid RX : %U\n",
|
||||
"Total valid RX : %U\n"
|
||||
"Total good RX : %U\n",
|
||||
UTI_IPToString(&remote_addr), (unsigned long)UTI_IPToRefid(&remote_addr),
|
||||
ntohs(reply.data.ntp_data.remote_port),
|
||||
UTI_IPToString(&local_addr), (unsigned long)UTI_IPToRefid(&local_addr),
|
||||
@@ -2369,6 +2375,7 @@ process_cmd_ntpdata(char *line)
|
||||
(unsigned long)ntohl(reply.data.ntp_data.total_tx_count),
|
||||
(unsigned long)ntohl(reply.data.ntp_data.total_rx_count),
|
||||
(unsigned long)ntohl(reply.data.ntp_data.total_valid_count),
|
||||
(unsigned long)ntohl(reply.data.ntp_data.total_good_count),
|
||||
REPORT_END);
|
||||
}
|
||||
|
||||
@@ -2396,12 +2403,12 @@ process_cmd_selectdata(char *line)
|
||||
n_sources = ntohl(reply.data.n_sources.n_sources);
|
||||
|
||||
if (verbose) {
|
||||
printf( " .-- State: N - noselect, M - missing samples, d/D - large distance,\n");
|
||||
printf( " / ~ - jittery, w/W - waits for others, T - not trusted,\n");
|
||||
printf( "| x - falseticker, P - not preferred, U - waits for update,\n");
|
||||
printf( "| S - stale, O - orphan, + - combined, * - best.\n");
|
||||
printf( "| Effective options ------. (N - noselect, P - prefer\n");
|
||||
printf( "| Configured options -. \\ T - trust, R - require)\n");
|
||||
printf( " . State: N - noselect, s - unsynchronised, M - missing samples,\n");
|
||||
printf( " / d/D - large distance, ~ - jittery, w/W - waits for others,\n");
|
||||
printf( "| S - stale, O - orphan, T - not trusted, P - not preferred,\n");
|
||||
printf( "| U - waits for update,, x - falseticker, + - combined, * - best.\n");
|
||||
printf( "| Effective options ---------. (N - noselect, P - prefer\n");
|
||||
printf( "| Configured options ----. \\ T - trust, R - require)\n");
|
||||
printf( "| Auth. enabled (Y/N) -. \\ \\ Offset interval --.\n");
|
||||
printf( "| | | | |\n");
|
||||
}
|
||||
@@ -3235,44 +3242,46 @@ process_line(char *line)
|
||||
if (do_normal_submit) {
|
||||
ret = request_reply(&tx_message, &rx_message, RPY_NULL, 1);
|
||||
}
|
||||
|
||||
fflush(stderr);
|
||||
fflush(stdout);
|
||||
|
||||
if (fflush(stdout) != 0 || ferror(stdout) != 0) {
|
||||
LOG(LOGS_ERR, "Could not write to stdout");
|
||||
|
||||
/* Return error for commands that print data */
|
||||
if (!do_normal_submit)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
#define MAX_LINE_LENGTH 2048
|
||||
|
||||
static int
|
||||
process_args(int argc, char **argv, int multi)
|
||||
{
|
||||
int total_length, i, ret = 0;
|
||||
char *line;
|
||||
char line[MAX_LINE_LENGTH];
|
||||
int i, l, ret = 0;
|
||||
|
||||
total_length = 0;
|
||||
for(i=0; i<argc; i++) {
|
||||
total_length += strlen(argv[i]) + 1;
|
||||
}
|
||||
|
||||
line = (char *) Malloc((2 + total_length) * sizeof(char));
|
||||
|
||||
for (i = 0; i < argc; i++) {
|
||||
line[0] = '\0';
|
||||
if (multi) {
|
||||
strcat(line, argv[i]);
|
||||
} else {
|
||||
for (; i < argc; i++) {
|
||||
strcat(line, argv[i]);
|
||||
if (i + 1 < argc)
|
||||
strcat(line, " ");
|
||||
}
|
||||
for (i = l = 0; i < argc; i++) {
|
||||
l += snprintf(line + l, sizeof (line) - l, "%s ", argv[i]);
|
||||
if (l >= sizeof (line)) {
|
||||
LOG(LOGS_ERR, "Command too long");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!multi && i + 1 < argc)
|
||||
continue;
|
||||
|
||||
ret = process_line(line);
|
||||
if (!ret || quit)
|
||||
break;
|
||||
}
|
||||
|
||||
Free(line);
|
||||
l = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -3291,7 +3300,7 @@ static void
|
||||
display_gpl(void)
|
||||
{
|
||||
printf("chrony version %s\n"
|
||||
"Copyright (C) 1997-2003, 2007, 2009-2021 Richard P. Curnow and others\n"
|
||||
"Copyright (C) 1997-2003, 2007, 2009-2022 Richard P. Curnow and others\n"
|
||||
"chrony comes with ABSOLUTELY NO WARRANTY. This is free software, and\n"
|
||||
"you are welcome to redistribute it under certain conditions. See the\n"
|
||||
"GNU General Public License version 2 for details.\n\n",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2009, 2015-2017
|
||||
* Copyright (C) Miroslav Lichvar 2009, 2015-2017, 2021
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -751,7 +751,7 @@ static void
|
||||
int64_to_ntp64(uint64_t ts, NTP_int64 *ntp_ts)
|
||||
{
|
||||
ntp_ts->hi = htonl(ts >> 32);
|
||||
ntp_ts->lo = htonl(ts & ((1ULL << 32) - 1));
|
||||
ntp_ts->lo = htonl(ts);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
6
cmdmon.c
6
cmdmon.c
@@ -321,7 +321,8 @@ transmit_reply(int sock_fd, int request_length, SCK_Message *message)
|
||||
|
||||
#if !defined(HAVE_IN_PKTINFO) && defined(IP_SENDSRCADDR)
|
||||
/* On FreeBSD a local IPv4 address cannot be specified on bound socket */
|
||||
if (message->local_addr.ip.family == IPADDR_INET4 && (sock_fd != sock_fd4 || bound_sock_fd4))
|
||||
if (message->addr_type == SCK_ADDR_IP && message->local_addr.ip.family == IPADDR_INET4 &&
|
||||
(sock_fd != sock_fd4 || bound_sock_fd4))
|
||||
message->local_addr.ip.family = IPADDR_UNSPEC;
|
||||
#endif
|
||||
|
||||
@@ -756,6 +757,8 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
|
||||
UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_ratio);
|
||||
params.max_delay_dev_ratio =
|
||||
UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_dev_ratio);
|
||||
params.max_delay_quant =
|
||||
UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_quant);
|
||||
params.min_delay = UTI_FloatNetworkToHost(rx_message->data.ntp_source.min_delay);
|
||||
params.asymmetry = UTI_FloatNetworkToHost(rx_message->data.ntp_source.asymmetry);
|
||||
params.offset = UTI_FloatNetworkToHost(rx_message->data.ntp_source.offset);
|
||||
@@ -1221,6 +1224,7 @@ handle_ntp_data(CMD_Request *rx_message, CMD_Reply *tx_message)
|
||||
tx_message->data.ntp_data.total_tx_count = htonl(report.total_tx_count);
|
||||
tx_message->data.ntp_data.total_rx_count = htonl(report.total_rx_count);
|
||||
tx_message->data.ntp_data.total_valid_count = htonl(report.total_valid_count);
|
||||
tx_message->data.ntp_data.total_good_count = htonl(report.total_good_count);
|
||||
memset(tx_message->data.ntp_data.reserved, 0xff, sizeof (tx_message->data.ntp_data.reserved));
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2013-2014, 2016
|
||||
* Copyright (C) Miroslav Lichvar 2013-2014, 2016, 2021
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -72,6 +72,7 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
|
||||
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.max_delay_quant = 0.0;
|
||||
src->params.min_delay = 0.0;
|
||||
src->params.asymmetry = SRC_DEFAULT_ASYMMETRY;
|
||||
src->params.offset = 0.0;
|
||||
@@ -140,6 +141,9 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
|
||||
} else if (!strcasecmp(cmd, "maxdelaydevratio")) {
|
||||
if (sscanf(line, "%lf%n", &src->params.max_delay_dev_ratio, &n) != 1)
|
||||
return 0;
|
||||
} else if (!strcasecmp(cmd, "maxdelayquant")) {
|
||||
if (sscanf(line, "%lf%n", &src->params.max_delay_quant, &n) != 1)
|
||||
return 0;
|
||||
} else if (!strcasecmp(cmd, "maxpoll")) {
|
||||
if (sscanf(line, "%d%n", &src->params.maxpoll, &n) != 1)
|
||||
return 0;
|
||||
|
||||
18
conf.c
18
conf.c
@@ -115,6 +115,7 @@ static int cmd_port = DEFAULT_CANDM_PORT;
|
||||
|
||||
static int raw_measurements = 0;
|
||||
static int do_log_measurements = 0;
|
||||
static int do_log_selection = 0;
|
||||
static int do_log_statistics = 0;
|
||||
static int do_log_tracking = 0;
|
||||
static int do_log_rtc = 0;
|
||||
@@ -861,7 +862,7 @@ static void
|
||||
parse_refclock(char *line)
|
||||
{
|
||||
int n, poll, dpoll, filter_length, pps_rate, min_samples, max_samples, sel_options;
|
||||
int max_lock_age, pps_forced, stratum, tai;
|
||||
int local, max_lock_age, pps_forced, stratum, tai;
|
||||
uint32_t ref_id, lock_ref_id;
|
||||
double offset, delay, precision, max_dispersion, pulse_width;
|
||||
char *p, *cmd, *name, *param;
|
||||
@@ -871,6 +872,7 @@ parse_refclock(char *line)
|
||||
poll = 4;
|
||||
dpoll = 0;
|
||||
filter_length = 64;
|
||||
local = 0;
|
||||
pps_forced = 0;
|
||||
pps_rate = 0;
|
||||
min_samples = SRC_DEFAULT_MINSAMPLES;
|
||||
@@ -929,6 +931,9 @@ parse_refclock(char *line)
|
||||
if (sscanf(line, "%d%n", &filter_length, &n) != 1) {
|
||||
break;
|
||||
}
|
||||
} else if (!strcasecmp(cmd, "local")) {
|
||||
n = 0;
|
||||
local = 1;
|
||||
} else if (!strcasecmp(cmd, "rate")) {
|
||||
if (sscanf(line, "%d%n", &pps_rate, &n) != 1)
|
||||
break;
|
||||
@@ -995,6 +1000,7 @@ parse_refclock(char *line)
|
||||
refclock->driver_poll = dpoll;
|
||||
refclock->poll = poll;
|
||||
refclock->filter_length = filter_length;
|
||||
refclock->local = local;
|
||||
refclock->pps_forced = pps_forced;
|
||||
refclock->pps_rate = pps_rate;
|
||||
refclock->min_samples = min_samples;
|
||||
@@ -1027,6 +1033,8 @@ parse_log(char *line)
|
||||
raw_measurements = 1;
|
||||
} else if (!strcmp(log_name, "measurements")) {
|
||||
do_log_measurements = 1;
|
||||
} else if (!strcmp(log_name, "selection")) {
|
||||
do_log_selection = 1;
|
||||
} else if (!strcmp(log_name, "statistics")) {
|
||||
do_log_statistics = 1;
|
||||
} else if (!strcmp(log_name, "tracking")) {
|
||||
@@ -1924,6 +1932,14 @@ CNF_GetLogMeasurements(int *raw)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
CNF_GetLogSelection(void)
|
||||
{
|
||||
return do_log_selection;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
CNF_GetLogStatistics(void)
|
||||
{
|
||||
|
||||
1
conf.h
1
conf.h
@@ -58,6 +58,7 @@ extern char *CNF_GetLogDir(void);
|
||||
extern char *CNF_GetDumpDir(void);
|
||||
extern int CNF_GetLogBanner(void);
|
||||
extern int CNF_GetLogMeasurements(int *raw);
|
||||
extern int CNF_GetLogSelection(void);
|
||||
extern int CNF_GetLogStatistics(void);
|
||||
extern int CNF_GetLogTracking(void);
|
||||
extern int CNF_GetLogRtc(void);
|
||||
|
||||
43
configure
vendored
43
configure
vendored
@@ -245,6 +245,7 @@ try_lockmem=0
|
||||
feat_asyncdns=1
|
||||
feat_forcednsretry=1
|
||||
try_clock_gettime=1
|
||||
try_arc4random=1
|
||||
try_recvmmsg=1
|
||||
feat_timestamping=1
|
||||
try_timestamping=0
|
||||
@@ -421,6 +422,7 @@ case $OPERATINGSYSTEM in
|
||||
try_setsched=1
|
||||
try_lockmem=1
|
||||
try_phc=1
|
||||
try_arc4random=0
|
||||
add_def LINUX
|
||||
echo "Configuring for " $SYSTEM
|
||||
;;
|
||||
@@ -467,7 +469,7 @@ case $OPERATINGSYSTEM in
|
||||
;;
|
||||
SunOS)
|
||||
EXTRA_OBJECTS="sys_generic.o sys_solaris.o sys_timex.o sys_posix.o"
|
||||
LIBS="$LIBS -lsocket -lnsl -lresolv"
|
||||
LIBS="$LIBS -lsocket -lnsl -lkvm -lelf -lresolv"
|
||||
try_setsched=1
|
||||
try_lockmem=1
|
||||
add_def SOLARIS
|
||||
@@ -479,7 +481,7 @@ case $OPERATINGSYSTEM in
|
||||
add_def FEAT_PRIVDROP
|
||||
priv_ops="ADJUSTTIMEX SETTIME BINDSOCKET"
|
||||
fi
|
||||
echo "Configuring for Solaris (" $SYSTEM "SunOS version" $VERSION ")"
|
||||
echo "Configuring for illumos (" $SYSTEM "SunOS version" $VERSION ")"
|
||||
;;
|
||||
* )
|
||||
echo "error: $SYSTEM is not supported (yet?)"
|
||||
@@ -671,12 +673,12 @@ fi
|
||||
|
||||
if [ $try_clock_gettime = "1" ]; then
|
||||
if test_code 'clock_gettime()' 'time.h' '' '' \
|
||||
'clock_gettime(CLOCK_REALTIME, NULL);'
|
||||
'clock_gettime(CLOCK_REALTIME, (void *)1);'
|
||||
then
|
||||
add_def HAVE_CLOCK_GETTIME
|
||||
else
|
||||
if test_code 'clock_gettime() in -lrt' 'time.h' '' '-lrt' \
|
||||
'clock_gettime(CLOCK_REALTIME, NULL);'
|
||||
'clock_gettime(CLOCK_REALTIME, (void *)1);'
|
||||
then
|
||||
add_def HAVE_CLOCK_GETTIME
|
||||
EXTRA_LIBS="$EXTRA_LIBS -lrt"
|
||||
@@ -702,11 +704,14 @@ then
|
||||
use_pthread=1
|
||||
fi
|
||||
|
||||
if test_code 'arc4random_buf()' 'stdlib.h' '' '' 'arc4random_buf(NULL, 0);'; then
|
||||
if [ $try_arc4random = "1" ] && \
|
||||
test_code 'arc4random_buf()' 'stdlib.h' '' '' \
|
||||
'arc4random_buf((void *)1, 1);'
|
||||
then
|
||||
add_def HAVE_ARC4RANDOM
|
||||
else
|
||||
if test_code 'getrandom()' 'stdlib.h sys/random.h' '' '' \
|
||||
'return getrandom(NULL, 256, 0);'; then
|
||||
'return getrandom((void *)1, 1, 0);'; then
|
||||
add_def HAVE_GETRANDOM
|
||||
fi
|
||||
fi
|
||||
@@ -900,7 +905,7 @@ if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_nettle = "1" ];
|
||||
add_def FEAT_SECHASH
|
||||
|
||||
if test_code 'CMAC in nettle' 'nettle/cmac.h' "$test_cflags" "$test_link" \
|
||||
'cmac128_update(NULL, NULL, NULL, 0, NULL);'
|
||||
'cmac128_update((void *)1, (void *)2, (void *)3, 1, (void *)4);'
|
||||
then
|
||||
add_def HAVE_CMAC
|
||||
EXTRA_OBJECTS="$EXTRA_OBJECTS cmac_nettle.o"
|
||||
@@ -925,7 +930,7 @@ fi
|
||||
|
||||
if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_tomcrypt = "1" ]; then
|
||||
if test_code 'tomcrypt' 'tomcrypt.h' '-I/usr/include/tomcrypt' '-ltomcrypt' \
|
||||
'hash_memory_multi(find_hash("md5"), NULL, NULL, NULL, 0, NULL, 0);'
|
||||
'hash_memory_multi(find_hash("md5"), (void *)1, (void *)2, (void *)3, 1, (void *)4, 1);'
|
||||
then
|
||||
HASH_OBJ="hash_tomcrypt.o"
|
||||
HASH_LINK="-ltomcrypt"
|
||||
@@ -939,7 +944,7 @@ if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_gnutls = "1" ];
|
||||
test_link="`pkg_config --libs gnutls`"
|
||||
if test_code 'gnutls' 'gnutls/crypto.h' \
|
||||
"$test_cflags" "$test_link" '
|
||||
return gnutls_hash(NULL, NULL, 0);'
|
||||
return gnutls_hash((void *)1, (void *)2, 1);'
|
||||
then
|
||||
HASH_OBJ="hash_gnutls.o"
|
||||
HASH_LINK="$test_link"
|
||||
@@ -947,7 +952,7 @@ if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_gnutls = "1" ];
|
||||
add_def FEAT_SECHASH
|
||||
|
||||
if test_code 'CMAC in gnutls' 'gnutls/crypto.h' "$test_cflags" "$test_link" \
|
||||
'return gnutls_hmac_init(NULL, GNUTLS_MAC_AES_CMAC_128, NULL, 0);'
|
||||
'return gnutls_hmac_init((void *)1, GNUTLS_MAC_AES_CMAC_128, (void *)2, 0);'
|
||||
then
|
||||
add_def HAVE_CMAC
|
||||
EXTRA_OBJECTS="$EXTRA_OBJECTS cmac_gnutls.o"
|
||||
@@ -970,13 +975,13 @@ if [ $feat_ntp = "1" ] && [ $feat_nts = "1" ] && [ $try_gnutls = "1" ]; then
|
||||
fi
|
||||
if test_code 'TLS1.3 in gnutls' 'gnutls/gnutls.h' \
|
||||
"$test_cflags" "$test_link $LIBS" '
|
||||
return gnutls_init(NULL, 0) + GNUTLS_TLS1_3 +
|
||||
gnutls_priority_init2(NULL, "", NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND) +
|
||||
gnutls_prf_rfc5705(NULL, 0, "", 0, "", 16, NULL);'
|
||||
return gnutls_init((void *)1, 0) + GNUTLS_TLS1_3 +
|
||||
gnutls_priority_init2((void *)1, "", NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND) +
|
||||
gnutls_prf_rfc5705((void *)1, 0, "", 0, "", 16, (void *)2);'
|
||||
then
|
||||
if test_code 'SIV in nettle' \
|
||||
'nettle/siv-cmac.h' "" "$LIBS" \
|
||||
'siv_cmac_aes128_set_key(NULL, NULL);'
|
||||
'siv_cmac_aes128_set_key((void *)1, (void *)2);'
|
||||
then
|
||||
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o"
|
||||
add_def HAVE_SIV
|
||||
@@ -984,13 +989,19 @@ if [ $feat_ntp = "1" ] && [ $feat_nts = "1" ] && [ $try_gnutls = "1" ]; then
|
||||
else
|
||||
if test_code 'SIV in gnutls' 'gnutls/crypto.h' \
|
||||
"$test_cflags" "$test_link $LIBS" '
|
||||
return gnutls_aead_cipher_init(NULL, GNUTLS_CIPHER_AES_128_SIV, NULL);'
|
||||
return gnutls_aead_cipher_init((void *)1, GNUTLS_CIPHER_AES_128_SIV, (void *)2);'
|
||||
then
|
||||
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_gnutls.o"
|
||||
add_def HAVE_SIV
|
||||
if test_code 'gnutls_aead_cipher_set_key()' 'gnutls/crypto.h' \
|
||||
"$test_cflags" "$test_link $LIBS" '
|
||||
return gnutls_aead_cipher_set_key((void *)1, (void *)2);'
|
||||
then
|
||||
add_def HAVE_GNUTLS_AEAD_CIPHER_SET_KEY
|
||||
fi
|
||||
else
|
||||
if test_code 'AES128 in nettle' 'nettle/aes.h' '' "$LIBS" \
|
||||
'aes128_set_encrypt_key(NULL, NULL);'
|
||||
'aes128_set_encrypt_key((void *)1, (void *)2);'
|
||||
then
|
||||
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o"
|
||||
add_def HAVE_SIV
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Copyright (C) Richard P. Curnow 1997-2003
|
||||
// Copyright (C) Stephen Wadeley 2016
|
||||
// Copyright (C) Bryan Christianson 2017
|
||||
// Copyright (C) Miroslav Lichvar 2009-2021
|
||||
// Copyright (C) Miroslav Lichvar 2009-2022
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -63,16 +63,28 @@ source. The client-server relationship is strictly hierarchical: a client might
|
||||
synchronise its system time to that of the server, but the server's system time
|
||||
will never be influenced by that of a client.
|
||||
+
|
||||
The server can be specified by its hostname or IP address. If the hostname cannot
|
||||
be resolved on start, *chronyd* will try it again in increasing intervals, and
|
||||
also when the <<chronyc.adoc#online,*online*>> command is issued in *chronyc*.
|
||||
+
|
||||
The DNS record can change over time. The used address will be replaced with a
|
||||
newly resolved address when the server becomes unreachable (i.e. no valid
|
||||
response to last 8 requests), unsynchronised, a falseticker (i.e. does not
|
||||
agree with a majority of other sources), or the root distance is too large (the
|
||||
limit can be configured by the <<maxdistance,*maxdistance*>> directive). The
|
||||
automatic replacement happens at most once per 30 minutes. It can also be
|
||||
triggered manually for all sources by the <<chronyc.adoc#refresh,*refresh*>>
|
||||
command in *chronyc*.
|
||||
+
|
||||
This directive can be used multiple times to specify multiple servers.
|
||||
+
|
||||
The directive is immediately followed by either the name of the
|
||||
server, or its IP address. It supports the following options:
|
||||
The directive supports the following options:
|
||||
+
|
||||
*minpoll* _poll_:::
|
||||
This option specifies the minimum interval between requests sent to the server
|
||||
as a power of 2 in seconds. For example, *minpoll 5* would mean that the
|
||||
polling interval should not drop below 32 seconds. The default is 6 (64
|
||||
seconds), the minimum is -6 (1/64th of a second), and the maximum is 24 (6
|
||||
seconds), the minimum is -7 (1/128th of a second), and the maximum is 24 (6
|
||||
months). Note that intervals shorter than 6 (64 seconds) should generally not
|
||||
be used with public servers on the Internet, because it might be considered
|
||||
abuse. A sub-second interval will be enabled only when the server is reachable
|
||||
@@ -82,7 +94,7 @@ should be in a local network.
|
||||
This option specifies the maximum interval between requests sent to the server
|
||||
as a power of 2 in seconds. For example, *maxpoll 9* indicates that the polling
|
||||
interval should stay at or below 9 (512 seconds). The default is 10 (1024
|
||||
seconds), the minimum is -6 (1/64th of a second), and the maximum is 24 (6
|
||||
seconds), the minimum is -7 (1/128th of a second), and the maximum is 24 (6
|
||||
months).
|
||||
*iburst*:::
|
||||
With this option, *chronyd* will start with a burst of 4-8 requests in order to
|
||||
@@ -138,18 +150,33 @@ behind a lot of packets related to a download of some sort).
|
||||
If the user knows that round trip delays above a certain level should cause the
|
||||
measurement to be ignored, this level can be defined with the *maxdelay*
|
||||
option. For example, *maxdelay 0.3* would indicate that measurements with a
|
||||
round-trip delay of 0.3 seconds or more should be ignored. The default value is
|
||||
3 seconds and the maximum value is 1000 seconds.
|
||||
round-trip delay greater than 0.3 seconds should be ignored. The default value
|
||||
is 3 seconds and the maximum value is 1000 seconds.
|
||||
*maxdelayratio* _ratio_:::
|
||||
This option is similar to the *maxdelay* option above. *chronyd* keeps a record
|
||||
of the minimum round-trip delay amongst the previous measurements that it has
|
||||
buffered. If a measurement has a round trip delay that is greater than the
|
||||
specified ratio times the minimum delay, it will be rejected.
|
||||
buffered. If a measurement has a round-trip delay that is greater than the
|
||||
specified ratio times the minimum delay, it will be rejected. By default, this
|
||||
test is disabled.
|
||||
*maxdelaydevratio* _ratio_:::
|
||||
If a measurement has a ratio of the increase in the round-trip delay from the
|
||||
minimum delay amongst the previous measurements to the standard deviation of
|
||||
the previous measurements that is greater than the specified ratio, it will be
|
||||
rejected. The default is 10.0.
|
||||
*maxdelayquant* _p_:::
|
||||
This option disables the *maxdelaydevratio* test and specifies the maximum
|
||||
acceptable delay as a quantile of the round-trip delay instead of a function of
|
||||
the minimum delay amongst the buffered measurements. If a measurement has a
|
||||
round-trip delay that is greater than a long-term estimate of the _p_-quantile,
|
||||
it will be rejected.
|
||||
+
|
||||
The specified _p_ value should be between 0.05 and 0.95. For example,
|
||||
*maxdelayquant 0.2* would indicate that only measurements with the lowest 20
|
||||
percent of round-trip delays should be accepted. Note that it can take many
|
||||
measurements for the estimated quantile to reach the expected value. This
|
||||
option is intended for synchronisation in mostly static local networks with
|
||||
very short polling intervals and possibly combined with the *filter* option.
|
||||
By default, this test is disabled in favour of the *maxdelaydevratio* test.
|
||||
*mindelay* _delay_:::
|
||||
This option specifies a fixed minimum round-trip delay to be used instead of
|
||||
the minimum amongst the previous measurements. This can be useful in networks
|
||||
@@ -176,11 +203,12 @@ Set the minimum number of samples kept for this source. This overrides the
|
||||
*maxsamples* _samples_:::
|
||||
Set the maximum number of samples kept for this source. This overrides the
|
||||
<<maxsamples,*maxsamples*>> directive.
|
||||
*filter* _samples_:::
|
||||
*filter* _polls_:::
|
||||
This option enables a median filter to reduce noise in NTP measurements. The
|
||||
filter will reduce the specified number of samples to a single sample. It is
|
||||
intended to be used with very short polling intervals in local networks where
|
||||
it is acceptable to generate a lot of NTP traffic.
|
||||
filter will process samples collected in the specified number of polls
|
||||
into a single sample. It is intended to be used with very short polling
|
||||
intervals in local networks where it is acceptable to generate a lot of NTP
|
||||
traffic.
|
||||
*offline*:::
|
||||
If the server will not be reachable when *chronyd* is started, the *offline*
|
||||
option can be specified. *chronyd* will not try to poll the server until it is
|
||||
@@ -325,12 +353,6 @@ This option sets the desired number of sources to be used from the pool.
|
||||
sources responding to requests. The default value is 4 and the maximum value is
|
||||
16.
|
||||
+
|
||||
{blank}::
|
||||
When an NTP source is unreachable,
|
||||
marked as a falseticker, or has a distance larger than the limit set by the
|
||||
<<maxdistance,*maxdistance*>> directive, *chronyd* will try to replace the
|
||||
source with a newly resolved address of the name.
|
||||
+
|
||||
An example of the *pool* directive is
|
||||
+
|
||||
----
|
||||
@@ -511,8 +533,10 @@ Note that some PTP clocks cannot be configured to timestamp only assert or
|
||||
clear events, and it is necessary to use the *width* option to filter wrong
|
||||
PPS samples.
|
||||
*pin*=_index_::::
|
||||
This option specifies the index of the pin to which is connected the PPS
|
||||
signal. The default value is 0.
|
||||
This option specifies the index of the pin which should be enabled for the
|
||||
PPS timestamping. If the PHC does not have configurable pins (i.e. the channel
|
||||
function is fixed), the index needs to be set to -1 to disable the pin
|
||||
configuration. The default value is 0.
|
||||
*channel*=_index_::::
|
||||
This option specifies the index of the channel for the PPS mode. The default
|
||||
value is 0.
|
||||
@@ -624,6 +648,13 @@ and that *chronyd* should correct its offset by the current TAI-UTC offset. The
|
||||
<<leapsectz,*leapsectz*>> directive must be used with this option and the
|
||||
database must be kept up to date in order for this correction to work as
|
||||
expected. This option does not make sense with PPS refclocks.
|
||||
*local*:::
|
||||
This option specifies that the reference clock is an unsynchronised clock which
|
||||
is more stable than the system clock (e.g. TCXO, OCXO, or atomic clock) and
|
||||
it should be used as a local standard to stabilise the system clock. The
|
||||
refclock will bypass the source selection. There should be at most one refclock
|
||||
specified with this option and it should have the shortest polling interval
|
||||
among all configured sources.
|
||||
*minsamples* _samples_:::
|
||||
Set the minimum number of samples kept for this source. This overrides the
|
||||
<<minsamples,*minsamples*>> directive.
|
||||
@@ -1099,7 +1130,11 @@ clock will be off for a longer time. On Linux with the default
|
||||
*ignore*:::
|
||||
No correction is applied to the clock for the leap second. The clock will be
|
||||
corrected later in normal operation when new measurements are made and the
|
||||
estimated offset includes the one second error.
|
||||
estimated offset includes the one second error. This option is particularly
|
||||
useful when multiple *chronyd* instances are running on the system, one
|
||||
controlling the system clock and others started with the *-x* option, which
|
||||
should rely on the first instance to correct the system clock and ignore it for
|
||||
the correction of their own NTP clock running on top of the system clock.
|
||||
{blank}::
|
||||
+
|
||||
When serving time to NTP clients that cannot be configured to correct their
|
||||
@@ -1206,12 +1241,16 @@ This would step the system clock if the adjustment is larger than 0.1 seconds, b
|
||||
only in the first three clock updates.
|
||||
|
||||
[[maxchange]]*maxchange* _offset_ _start_ _ignore_::
|
||||
This directive sets the maximum allowed offset corrected on a clock update. The
|
||||
check is performed only after the specified number of updates to allow a large
|
||||
initial adjustment of the system clock. When an offset larger than the
|
||||
specified maximum occurs, it will be ignored for the specified number of times
|
||||
and then *chronyd* will give up and exit (a negative value can be used to never
|
||||
exit). In both cases a message is sent to syslog.
|
||||
This directive sets the maximum offset to be accepted on a clock update. The
|
||||
offset is measured relative to the current estimate of the true time, which is
|
||||
different from the system time if a previous slew did not finish.
|
||||
+
|
||||
The check is enabled after the specified number of clock updates to allow a
|
||||
large initial offset to be corrected on start. Offsets larger than the
|
||||
specified maximum will be ignored for the specified number of times. Another
|
||||
large offset will cause *chronyd* to give up and exit. A negative value
|
||||
can be used to disable the limit to ignore all large offsets. A syslog message
|
||||
will be generated when an offset is ignored or it causes the exit.
|
||||
+
|
||||
An example of the use of this directive is:
|
||||
+
|
||||
@@ -1238,7 +1277,7 @@ This directive specifies the maximum assumed drift (frequency error) of the
|
||||
system clock. It limits the frequency adjustment that *chronyd* is allowed to
|
||||
use to correct the measured drift. It is an additional limit to the maximum
|
||||
adjustment that can be set by the system driver (100000 ppm on Linux, 500 ppm
|
||||
on FreeBSD, NetBSD, and macOS 10.13+, 32500 ppm on Solaris).
|
||||
on FreeBSD, NetBSD, and macOS 10.13+, 32500 ppm on illumos).
|
||||
+
|
||||
By default, the maximum assumed drift is 500000 ppm, i.e. the adjustment is
|
||||
limited by the system driver rather than this directive.
|
||||
@@ -1277,7 +1316,7 @@ all supported systems with the exception of macOS 12 or earlier).
|
||||
+
|
||||
For each system there is a maximum frequency offset of the clock that can be set
|
||||
by the driver. On Linux it is 100000 ppm, on FreeBSD, NetBSD and macOS 10.13+ it
|
||||
is 5000 ppm, and on Solaris it is 32500 ppm. Also, due to a kernel limitation,
|
||||
is 5000 ppm, and on illumos it is 32500 ppm. Also, due to a kernel limitation,
|
||||
setting *maxslewrate* on FreeBSD, NetBSD, macOS 10.13+ to a value between 500
|
||||
ppm and 5000 ppm will effectively set it to 500 ppm.
|
||||
+
|
||||
@@ -2051,9 +2090,11 @@ from the example line above):
|
||||
. Stratum of remote computer. [2]
|
||||
. RFC 5905 tests 1 through 3 (1=pass, 0=fail) [111]
|
||||
. RFC 5905 tests 5 through 7 (1=pass, 0=fail) [111]
|
||||
. Tests for maximum delay, maximum delay ratio and maximum delay dev ratio,
|
||||
against defined parameters, and a test for synchronisation loop (1=pass,
|
||||
0=fail) [1111]
|
||||
. Results of the *maxdelay*, *maxdelayratio*, and *maxdelaydevratio* (or
|
||||
*maxdelayquant*) tests, and a test for synchronisation loop (1=pass,
|
||||
0=fail). The first test from these four also checks the server precision,
|
||||
response time, and whether an interleaved response is acceptable for
|
||||
synchronisation. [1111]
|
||||
. Local poll [10]
|
||||
. Remote poll [10]
|
||||
. '`Score`' (an internal score within each polling level used to decide when to
|
||||
@@ -2127,6 +2168,71 @@ from the example line above):
|
||||
the source is more variable than the delay of packets sent from the source
|
||||
back. [0.00, i.e. no correction for asymmetry]
|
||||
+
|
||||
*selection*:::
|
||||
This option logs information about selection of sources for synchronisation to
|
||||
a file called _selection.log_. Note that the rate of entries written to this
|
||||
file grows quadratically with the number of specified sources (each measurement
|
||||
triggers the selection for all sources). An example line (which actually
|
||||
appears as a single line in the file) from the log file is shown below.
|
||||
+
|
||||
----
|
||||
2022-05-01 02:01:20 203.0.113.15 * ----- 377 1.00 \
|
||||
4.228e+01 -1.575e-04 1.239e-04
|
||||
----
|
||||
+
|
||||
The columns are as follows (the quantities in square brackets are the values
|
||||
from the example line above):
|
||||
+
|
||||
. Date [2022-05-01]
|
||||
. Hour:Minute:Second. Note that the date-time pair is expressed in
|
||||
UTC, not the local time zone. [02:01:20]
|
||||
. IP address or reference ID of the source. [203.0.113.15]
|
||||
. State of the source indicated with one of the following symbols. [*]
|
||||
{blank}::::
|
||||
Not considered selectable for synchronisation:
|
||||
* _N_ - has the *noselect* option.
|
||||
* _s_ - is not synchronised.
|
||||
* _M_ - does not have enough measurements.
|
||||
* _d_ - has a root distance larger than the maximum distance (configured by the
|
||||
<<maxdistance,*maxdistance*>> directive).
|
||||
* _~_ - has a jitter larger than the maximum jitter (configured by the
|
||||
<<maxjitter,*maxjitter*>> directive).
|
||||
* _w_ - waits for other sources to get out of the _M_ state.
|
||||
* _S_ - has older measurements than other sources.
|
||||
* _O_ - has a stratum equal or larger than the orphan stratum (configured by
|
||||
the <<local,*local*>> directive).
|
||||
* _T_ - does not fully agree with sources that have the *trust* option.
|
||||
* _x_ - does not agree with other sources (falseticker).
|
||||
{blank}::::
|
||||
Considered selectable for synchronisation, but not currently used:
|
||||
* _W_ - waits for other sources to be selectable (required by the
|
||||
<<minsources,*minsources*>> directive, or the *require* option of
|
||||
another source).
|
||||
* _P_ - another selectable source is preferred due to the *prefer* option.
|
||||
* _U_ - waits for a new measurement (after selecting a different best source).
|
||||
* _D_ - has, or recently had, a root distance which is too large to be combined
|
||||
with other sources (configured by the <<combinelimit,*combinelimit*>>
|
||||
directive).
|
||||
{blank}::::
|
||||
Used for synchronisation of the local clock:
|
||||
* _+_ - combined with the best source.
|
||||
* _*_ - selected as the best source to update the reference data (e.g. root
|
||||
delay, root dispersion).
|
||||
. Reachability register printed as an octal number. The register has 8 bits and
|
||||
is updated on every received or missed packet from the source. A value of 377
|
||||
indicates that a valid reply was received for all from the last eight
|
||||
transmissions. [377]
|
||||
. Current score against the source in the _*_ state. The scoring system avoids
|
||||
frequent reselection when multiple sources have a similar root distance. A
|
||||
value larger than 1 indicates this source was better than the _*_ source in
|
||||
recent selections. If the score reaches 10, the best source will be reselected
|
||||
and the scores will be reset to 1. [1.00]
|
||||
. Interval since the last measurement of the source in seconds. [4.228e+01]
|
||||
. Lower endpoint of the interval which was expected to contain the true offset
|
||||
of the local clock determined by the root distance of the source. [-1.575e-04]
|
||||
. Upper endpoint of the interval which was expected to contain the true offset
|
||||
of the local clock determined by the root distance of the source. [1.239e-04]
|
||||
+
|
||||
*tracking*:::
|
||||
This option logs changes to the estimate of the system's gain or loss rate, and
|
||||
any slews made, to a file called _tracking.log_. An example line (which
|
||||
@@ -2381,13 +2487,20 @@ be enabled by the *xleave* option in the <<server,*server*>> or the
|
||||
+
|
||||
This directive is supported on Linux 3.19 and newer. The NIC must support HW
|
||||
timestamping, which can be verified with the *ethtool -T* command. The list of
|
||||
capabilities should include _SOF_TIMESTAMPING_RAW_HARDWARE_,
|
||||
_SOF_TIMESTAMPING_TX_HARDWARE_, and _SOF_TIMESTAMPING_RX_HARDWARE_. Receive
|
||||
filter _HWTSTAMP_FILTER_ALL_, or _HWTSTAMP_FILTER_NTP_ALL_, is necessary for
|
||||
timestamping of received NTP packets. Timestamping of packets received on bridged
|
||||
and bonded interfaces is supported on Linux 4.13 and newer. When *chronyd* is
|
||||
running, no other process (e.g. a PTP daemon) should be working with the NIC
|
||||
clock.
|
||||
capabilities should include _hardware-raw-clock_, _hardware-transmit_, and
|
||||
_hardware-receive_. The receive filter _all_, or _ntp_, is necessary for
|
||||
timestamping of received NTP packets. Timestamping of packets received on
|
||||
bridged and bonded interfaces is supported on Linux 4.13 and newer. If HW
|
||||
timestamping does not work for received packets, *chronyd* will use kernel
|
||||
receive timestamps instead. Transmit-only HW timestamping can still be useful
|
||||
to improve stability of the synchronisation.
|
||||
+
|
||||
*chronyd* does not synchronise the NIC clock. It assumes the clock is running
|
||||
free. Multiple instances of *chronyd* can use the same interface with enabled
|
||||
HW timestamping. Applications which need HW timestamping with a synchronised
|
||||
clock (e.g. a PTP daemon) should use a virtual clock running on top of the
|
||||
physical clock created by writing to _/sys/class/ptp/ptpX/n_vclocks_. This
|
||||
feature is available on Linux 5.14 and newer.
|
||||
+
|
||||
If the kernel supports software timestamping, it will be enabled for all
|
||||
interfaces. The source of timestamps (i.e. hardware, kernel, or daemon) is
|
||||
@@ -2445,7 +2558,8 @@ is selected by default. Some NICs can timestamp PTP packets only. By default,
|
||||
they will be configured with the _none_ filter and expected to provide hardware
|
||||
timestamps for transmitted packets only. Timestamping of PTP packets is useful
|
||||
with NTP-over-PTP enabled by the <<chrony.conf.adoc#ptpport,*ptpport*>>
|
||||
directive. Forcing timestamping of all packets with the _all_ filter could be
|
||||
directive, or when another application is receiving PTP packets on the
|
||||
interface. Forcing timestamping of all packets with the _all_ filter could be
|
||||
useful if the NIC supported both the _all_ and _ntp_ filters, and it should
|
||||
timestamp both NTP and PTP packets, or NTP packets on a different UDP port.
|
||||
{blank}::
|
||||
@@ -2491,7 +2605,7 @@ The type is a name of a cryptographic hash function or cipher which is used to
|
||||
generate and verify the MAC. The default type is *MD5*, which is always
|
||||
supported.
|
||||
If *chronyd* was built with enabled support for hashing using a crypto library
|
||||
(nettle, nss, or libtomcrypt), the following functions are available: *MD5*,
|
||||
(Nettle, GnuTLS, NSS, or LibTomCrypt), the following functions are available: *MD5*,
|
||||
*SHA1*, *SHA256*, *SHA384*, *SHA512*. Depending on which library and version is
|
||||
*chronyd* using, some of the following hash functions and ciphers may
|
||||
also be available:
|
||||
@@ -2524,7 +2638,7 @@ file when the <<chronyc.adoc#rekey,*rekey*>> command is issued by *chronyc*).
|
||||
[[lock_all]]*lock_all*::
|
||||
The *lock_all* directive will lock the *chronyd* process into RAM so that it
|
||||
will never be paged out. This can result in lower and more consistent latency.
|
||||
The directive is supported on Linux, FreeBSD, NetBSD, and Solaris.
|
||||
The directive is supported on Linux, FreeBSD, NetBSD, and illumos.
|
||||
|
||||
[[pidfile]]*pidfile* _file_::
|
||||
Unless *chronyd* is started with the *-Q* option, it writes its process ID
|
||||
@@ -2564,7 +2678,7 @@ ptpport 319
|
||||
----
|
||||
|
||||
[[sched_priority]]*sched_priority* _priority_::
|
||||
On Linux, FreeBSD, NetBSD, and Solaris, the *sched_priority* directive will
|
||||
On Linux, FreeBSD, NetBSD, and illumos, the *sched_priority* directive will
|
||||
select the SCHED_FIFO real-time scheduler at the specified priority (which must
|
||||
be between 0 and 100). On macOS, this option must have either a value of 0 (the
|
||||
default) to disable the thread time constraint policy or 1 for the policy to be
|
||||
@@ -2590,7 +2704,7 @@ The *user* directive sets the name of the system user to which *chronyd* will
|
||||
switch after start in order to drop root privileges.
|
||||
+
|
||||
On Linux, *chronyd* needs to be compiled with support for the *libcap* library.
|
||||
On macOS, FreeBSD, NetBSD and Solaris *chronyd* forks into two processes.
|
||||
On macOS, FreeBSD, NetBSD and illumos *chronyd* forks into two processes.
|
||||
The child process retains root privileges, but can only perform a very limited
|
||||
range of privileged system calls on behalf of the parent.
|
||||
+
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//
|
||||
// Copyright (C) Richard P. Curnow 1997-2003
|
||||
// Copyright (C) Stephen Wadeley 2016
|
||||
// Copyright (C) Miroslav Lichvar 2009-2017, 2019-2020
|
||||
// Copyright (C) Miroslav Lichvar 2009-2017, 2019-2022
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -99,12 +99,15 @@ With this option multiple commands can be specified. Each argument will be
|
||||
interpreted as a whole command.
|
||||
|
||||
*-h* _host_::
|
||||
This option allows the user to specify which host (or comma-separated list of
|
||||
addresses) running the *chronyd* program is to be contacted. This allows for
|
||||
remote monitoring, without having to connect over SSH to the other host first.
|
||||
This option specifies the host to be contacted by *chronyc*. It can be
|
||||
specified with a hostname, IP address, or path to the local Unix domain socket.
|
||||
Multiple values can be specified as a comma-separated list to provide a
|
||||
fallback.
|
||||
+
|
||||
The default is to contact *chronyd* running on the same host where
|
||||
*chronyc* is being run.
|
||||
The default value is _@CHRONYRUNDIR@/chronyd.sock,127.0.0.1,::1_, i.e. the host
|
||||
where *chronyc* is being run. First, it tries to connect to the Unix domain
|
||||
socket and if that fails (e.g. due to running under a non-root user), it
|
||||
will try to connect to 127.0.0.1 and then ::1.
|
||||
|
||||
*-p* _port_::
|
||||
This option allows the user to specify the UDP port number which the target
|
||||
@@ -177,15 +180,19 @@ stratum-2 and is synchronised from a stratum-1).
|
||||
This is the time (UTC) at which the last measurement from the reference
|
||||
source was processed.
|
||||
*System time*:::
|
||||
In normal operation, *chronyd* by default never steps the system clock, because
|
||||
any jump in the time can have adverse consequences for certain application
|
||||
programs. Instead, any error in the system clock is corrected by slightly
|
||||
speeding up or slowing down the system clock until the error has been removed,
|
||||
and then returning to the system clock's normal speed. A consequence of this is
|
||||
that there will be a period when the system clock (as read by other programs)
|
||||
will be different from *chronyd*'s estimate of the current true time (which it
|
||||
reports to NTP clients when it is operating as a server). The value reported
|
||||
on this line is the difference due to this effect.
|
||||
This is the current offset between the NTP clock and system clock. The NTP
|
||||
clock is a software (virtual) clock maintained by *chronyd*, which is
|
||||
synchronised to the configured time sources and provides time to NTP clients.
|
||||
The system clock is synchronised to the NTP clock. To avoid steps in the
|
||||
system time, which might have adverse consequences for certain applications,
|
||||
the system clock is normally corrected only by speeding up or slowing down (up
|
||||
to the rate configured by the <<chrony.conf.adoc#maxslewrate,*maxslewrate*>>
|
||||
directive). If the offset is too large, this correction will take a very long
|
||||
time. A step can be forced by the <<makestep,*makestep*>> command, or the
|
||||
<<chrony.conf.adoc#makestep,*makestep*>> directive in the configuration file.
|
||||
+
|
||||
Note that all other offsets reported by *chronyc* and most offsets in the log
|
||||
files are relative to the NTP clock, not the system clock.
|
||||
*Last offset*:::
|
||||
This is the estimated local offset on the last clock update. A positive value
|
||||
indicates the local time (as previously estimated true time) was ahead of the
|
||||
@@ -448,6 +455,7 @@ states are reported.
|
||||
The following states indicate the source is not considered selectable for
|
||||
synchronisation:
|
||||
* _N_ - has the *noselect* option.
|
||||
* _s_ - is not synchronised.
|
||||
* _M_ - does not have enough measurements.
|
||||
* _d_ - has a root distance larger than the maximum distance (configured by the
|
||||
<<chrony.conf.adoc#maxdistance,*maxdistance*>> directive).
|
||||
@@ -658,6 +666,7 @@ RX timestamping : Kernel
|
||||
Total TX : 24
|
||||
Total RX : 24
|
||||
Total valid RX : 24
|
||||
Total good RX : 22
|
||||
----
|
||||
+
|
||||
The fields are explained as follows:
|
||||
@@ -695,7 +704,8 @@ packets sent to the source is more variable than the delay of packets sent
|
||||
from the source back.
|
||||
*NTP tests*:::
|
||||
Results of RFC 5905 tests 1 through 3, 5 through 7, and tests for maximum
|
||||
delay, delay ratio, delay dev ratio, and synchronisation loop.
|
||||
delay, delay ratio, delay dev ratio (or delay quantile), and synchronisation
|
||||
loop.
|
||||
*Interleaved*:::
|
||||
This shows if the response was in the interleaved mode.
|
||||
*Authenticated*:::
|
||||
@@ -710,7 +720,10 @@ The number of packets sent to the source.
|
||||
*Total RX*:::
|
||||
The number of all packets received from the source.
|
||||
*Total valid RX*:::
|
||||
The number of valid packets received from the source.
|
||||
The number of packets which passed the first two groups of NTP tests.
|
||||
*Total good RX*:::
|
||||
The number of packets which passed all three groups of NTP tests, i.e. the NTP
|
||||
measurement was accepted.
|
||||
|
||||
[[add_peer]]*add peer* _name_ [_option_]...::
|
||||
The *add peer* command allows a new NTP peer to be added whilst
|
||||
|
||||
@@ -100,7 +100,7 @@ directive in the configuration file. This option is useful if you want to stop
|
||||
and restart *chronyd* briefly for any reason, e.g. to install a new version.
|
||||
However, it should be used only on systems where the kernel can maintain clock
|
||||
compensation whilst not under *chronyd*'s control (i.e. Linux, FreeBSD, NetBSD,
|
||||
Solaris, and macOS 10.13 or later).
|
||||
illumos, and macOS 10.13 or later).
|
||||
|
||||
*-R*::
|
||||
When this option is used, the <<chrony.conf.adoc#initstepslew,*initstepslew*>>
|
||||
@@ -141,7 +141,7 @@ after start in order to drop root privileges. It overrides the
|
||||
_@DEFAULT_USER@_.
|
||||
+
|
||||
On Linux, *chronyd* needs to be compiled with support for the *libcap* library.
|
||||
On macOS, FreeBSD, NetBSD and Solaris *chronyd* forks into two processes.
|
||||
On macOS, FreeBSD, NetBSD, and illumos *chronyd* forks into two processes.
|
||||
The child process retains root privileges, but can only perform a very limited
|
||||
range of privileged system calls on behalf of the parent.
|
||||
|
||||
@@ -181,7 +181,7 @@ limited.
|
||||
The filters cannot be enabled with the *mailonchange* directive.
|
||||
|
||||
*-P* _priority_::
|
||||
On Linux, FreeBSD, NetBSD, and Solaris, this option will select the SCHED_FIFO
|
||||
On Linux, FreeBSD, NetBSD, and illumos this option will select the SCHED_FIFO
|
||||
real-time scheduler at the specified priority (which must be between 0 and
|
||||
100). On macOS, this option must have either a value of 0 to disable the thread
|
||||
time constraint policy or 1 for the policy to be enabled. Other systems do not
|
||||
@@ -189,7 +189,7 @@ support this option. The default value is 0.
|
||||
|
||||
*-m*::
|
||||
This option will lock *chronyd* into RAM so that it will never be paged out.
|
||||
This mode is only supported on Linux, FreeBSD, NetBSD, and Solaris.
|
||||
This mode is only supported on Linux, FreeBSD, NetBSD, and illumos.
|
||||
|
||||
*-x*::
|
||||
This option disables the control of the system clock. *chronyd* will not try to
|
||||
|
||||
140
doc/faq.adoc
140
doc/faq.adoc
@@ -1,7 +1,7 @@
|
||||
// This file is part of chrony
|
||||
//
|
||||
// Copyright (C) Richard P. Curnow 1997-2003
|
||||
// Copyright (C) Miroslav Lichvar 2014-2016, 2020-2021
|
||||
// Copyright (C) Miroslav Lichvar 2014-2016, 2020-2022
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -345,6 +345,15 @@ server ntp.local minpoll -6 maxpoll -6 filter 15 xleave
|
||||
hwtimestamp eth0 minpoll -6
|
||||
----
|
||||
|
||||
Since `chrony` version 4.3, the minimum `minpoll` is -7 and a filter using a
|
||||
long-term estimate of a delay quantile can be enabled by the `maxdelayquant`
|
||||
option to replace the default `maxdelaydevratio` filter, which is sensitive to
|
||||
outliers corrupting the minimum delay. For example:
|
||||
|
||||
----
|
||||
server ntp.local minpoll -7 maxpoll -7 filter 31 maxdelayquant 0.3 xleave
|
||||
----
|
||||
|
||||
As an experimental feature added in version 4.2, `chronyd` supports an NTPv4
|
||||
extension field containing an additional timestamp to enable frequency transfer
|
||||
and significantly improve stability of synchronisation. It can be enabled by
|
||||
@@ -475,6 +484,64 @@ identically configured leap-smearing servers. Note that some clients can get
|
||||
leap seconds from other sources (e.g. with the `leapsectz` directive in
|
||||
`chrony`) and they will not work correctly with a leap smearing server.
|
||||
|
||||
=== How should `chronyd` be configuration with `gpsd`?
|
||||
|
||||
A GPS or other GNSS receiver can be used as a reference clock with `gpsd`. It
|
||||
can work as one or two separate time sources for each connected receiver. The
|
||||
first time source is based on timestamping of messages sent by the receiver.
|
||||
Typically, it is accurate to milliseconds. The other source is much more
|
||||
accurate. It is timestamping a pulse-per-second (PPS) signal, usually connected
|
||||
to a serial port (e.g. DCD pin) or GPIO pin.
|
||||
|
||||
If the PPS signal is connected to the serial port which is receiving messages
|
||||
from the GPS/GNSS receiver, `gpsd` should detect and use it automatically. If
|
||||
it is connected to a GPIO pin, or another serial port, the PPS device needs to
|
||||
be specified on the command line as an additional data source. On Linux, the
|
||||
`ldattach` utility can be used to create a PPS device for a serial device.
|
||||
|
||||
The message-based time source provided by `gpsd` is specified as a `SHM 0`
|
||||
refclock, or other even number if `gpsd` is configured with multiple receivers.
|
||||
|
||||
The PPS-based time source is specified as a `SHM 1` refclock (or other odd
|
||||
number), or `SOCK /var/run/chrony.DEV.sock` where `DEV` is the name of the
|
||||
serial device (e.g. ttyS0).
|
||||
|
||||
With `chronyd` and `gpsd` both supporting PPS, and `gpsd` providing two
|
||||
different refclocks for PPS, there are three different recommended
|
||||
configurations:
|
||||
|
||||
----
|
||||
# First option
|
||||
refclock SOCK /var/run/chrony.ttyS0.sock refid GPS
|
||||
|
||||
# Second option
|
||||
refclock SHM 1 refid GPS
|
||||
|
||||
# Third option
|
||||
refclock PPS /dev/pps0 lock NMEA refid GPS
|
||||
refclock SHM 0 offset 0.5 delay 0.1 refid NMEA noselect
|
||||
----
|
||||
|
||||
Each option has some advantages:
|
||||
|
||||
* `SOCK` does not use polling (i.e. it can get samples earlier than `SHM`),
|
||||
but it requires `gpsd` to be started after `chronyd` in order to connect to
|
||||
its socket
|
||||
* `SOCK` and `SHM 1` can be more accurate than `PPS` if `gpsd` corrects for the
|
||||
sawtooth error provided by the receiver in serial data
|
||||
* `PPS` can be used with higher PPS rates (specified by the `rate` option),
|
||||
but it requires a second refclock or another time source to pair pulses
|
||||
with seconds, and the `SHM 0` offset needs to be specified
|
||||
<<using-pps-refclock,correctly>> to compensate for the message delay, while
|
||||
`gpsd` can apply HW-specific information
|
||||
|
||||
If the PPS signal is not available, or cannot be used for some reason, the only
|
||||
option is the message-based timing
|
||||
|
||||
----
|
||||
refclock SHM 0 offset 0.5 delay 0.1 refid GPS
|
||||
----
|
||||
|
||||
=== Does `chrony` support PTP?
|
||||
|
||||
No, the Precision Time Protocol (PTP) is not supported as a protocol for
|
||||
@@ -498,6 +565,27 @@ transport for NTP messages (NTP over PTP) to enable hardware timestamping on
|
||||
hardware which can timestamp PTP packets only. It can be enabled by the
|
||||
`ptpport` directive.
|
||||
|
||||
=== Why are client log records dropped before reaching `clientloglimit`?
|
||||
|
||||
The number of dropped client log records reported by the `serverstats` command
|
||||
can be increasing before the number of clients reported by the `clients` command
|
||||
reaches the maximum value corresponding to the memory limit set by the
|
||||
`clientloglimit` directive.
|
||||
|
||||
This is due to the design of the data structure keeping the client records. It
|
||||
is a hash table which can store only up to 16 colliding addresses per slot. If
|
||||
a slot has more collisions and the table already has the maximum size, the
|
||||
oldest record will be dropped and replaced by the new client.
|
||||
|
||||
Note that the size of the table is always a power of two and it can only grow.
|
||||
The limit set by the `clientloglimit` directive takes into account that two
|
||||
copies of the table exist when it is being resized. This means the actual
|
||||
memory usage reported by `top` and other utilities can be significantly smaller
|
||||
than the limit even when the maximum number of records is used.
|
||||
|
||||
The absolute maximum number of client records kept at the same time is
|
||||
16777216.
|
||||
|
||||
=== What happened to the `commandkey` and `generatecommandkey` directives?
|
||||
|
||||
They were removed in version 2.2. Authentication is no longer supported in the
|
||||
@@ -624,6 +712,18 @@ was not shut down for too long and the server's certificate was not renewed too
|
||||
close to its expiration, it should be sufficient for the time checks to
|
||||
succeed.
|
||||
|
||||
If you run your own server, you can use a self-signed certificate covering
|
||||
all dates where the client can start (e.g. years 1970-2100). The certificate
|
||||
needs to be installed on the client and specified with the `ntstrustedcerts`
|
||||
directive. The server can have multiple names and certificates. To avoid
|
||||
trusting a certificate for too long, a new certificate can be added to the
|
||||
server periodically (e.g. once per year) and the client can have the server
|
||||
name and trusted certificate updated automatically (e.g. using a package
|
||||
repository, or a cron script downloading the files directly from the server
|
||||
over HTTPS). A client that was shut down for years will still be able to
|
||||
synchronise its clock and perform the update as long as the server keeps
|
||||
the old certificate.
|
||||
|
||||
As a last resort, you can disable the time checks by the `nocerttimecheck`
|
||||
directive. This has some important security implications. To reduce the
|
||||
security risk, you can use the `nosystemcert` and `ntstrustedcerts` directives
|
||||
@@ -692,12 +792,14 @@ frequently, you can effectively disable the test by setting the
|
||||
`maxdelaydevratio` option to a very large value (e.g. 1000000), or speed up the
|
||||
recovery by increasing the clock error rate with the `maxclockerror` directive.
|
||||
|
||||
[[using-pps-refclock]]
|
||||
=== Using a PPS reference clock?
|
||||
|
||||
A pulse-per-second (PPS) reference clock requires a non-PPS time source to
|
||||
determine which second of UTC corresponds to each pulse. If it is another
|
||||
reference clock specified with the `lock` option in the `refclock` directive,
|
||||
the offset between the two reference clocks must be smaller than 0.2 seconds in
|
||||
the offset between the two reference clocks must be smaller than 0.4 seconds
|
||||
(0.2 seconds with `chrony` versions before 4.1) in
|
||||
order for the PPS reference clock to work. With NMEA reference clocks it is
|
||||
common to have a larger offset. It needs to be corrected with the `offset`
|
||||
option.
|
||||
@@ -716,7 +818,7 @@ foo.example.net 7 3 200 -2.991 16.141 -107us 492us
|
||||
|
||||
the offset of the NMEA source would need to be increased by about 0.504
|
||||
seconds. It does not have to be very accurate. As long as the offset of the
|
||||
NMEA reference clock stays below 0.2 seconds, the PPS reference clock should be
|
||||
NMEA reference clock stays below the limit, the PPS reference clock should be
|
||||
able to determine the seconds corresponding to the pulses and allow the samples
|
||||
to be used for synchronisation.
|
||||
|
||||
@@ -772,6 +874,10 @@ in parentheses) on the `Reference ID` line.
|
||||
Only by the source code. See _cmdmon.c_ (`chronyd` side) and _client.c_
|
||||
(`chronyc` side).
|
||||
|
||||
Note that this protocol is not compatible with the mode 6 or mode 7 protocol
|
||||
supported by `ntpd`, i.e. the `ntpq` or `ntpdc` utility cannot be used to
|
||||
monitor `chronyd`, and `chronyc` cannot be used to monitor `ntpd`.
|
||||
|
||||
== Real-time clock issues
|
||||
|
||||
=== What is the real-time clock (RTC)?
|
||||
@@ -898,6 +1004,34 @@ timestamps (e.g. daemon timestamp vs kernel timestamp) for serving time and
|
||||
synchronisation of its own clock, which will cause the other computer to
|
||||
measure a significant offset.
|
||||
|
||||
== Operation
|
||||
|
||||
=== What clocks does `chronyd` use?
|
||||
|
||||
There are several different clocks used by `chronyd`:
|
||||
|
||||
* *System clock:* software clock maintained by the kernel. It is the main clock
|
||||
used by applications running on the computer. It is synchronised by `chronyd`
|
||||
to its NTP clock, unless started with the *-x* option.
|
||||
* *NTP clock:* software clock (virtual) based on the system clock and internal
|
||||
to `chronyd`. It keeps the best estimate of the true time according to the
|
||||
configured time sources, which is served to NTP clients unless time smoothing
|
||||
is enabled by the *smoothtime* directive. The *System time* value in the
|
||||
`tracking` report is the current offset between the system and NTP clock.
|
||||
* *Real-time clock (RTC):* hardware clock keeping time even when the
|
||||
computer is turned off. It is used by the kernel to initialise the system
|
||||
clock on boot and also by `chronyd` to compensate for its measured drift if
|
||||
configured with the `rtcfile` directive and started with the `-s` option.
|
||||
The clock can be kept accurate only by stepping enabled by the `rtcsync` or
|
||||
`rtcautotrim` directive.
|
||||
* *Reference clock:* hardware clock used as a time source. It is specified by
|
||||
the `refclock` directive.
|
||||
* *NIC clock (also known as PTP hardware clock):* hardware clock timestamping
|
||||
packets received and transmitted by a network device specified by the
|
||||
*hwtimestamp* directive. The clock is expected to be running free. It is not
|
||||
synchronised by `chronyd`. Its offset is tracked relative to the NTP clock in
|
||||
order to convert the hardware timestamps.
|
||||
|
||||
== Operating systems
|
||||
|
||||
=== Does `chrony` support Windows?
|
||||
|
||||
@@ -27,7 +27,7 @@ The following libraries with their development files, and programs, are needed
|
||||
to enable optional features:
|
||||
|
||||
* pkg-config: detection of development libraries
|
||||
* Nettle, NSS, or LibTomCrypt: secure hash functions (`SECHASH`)
|
||||
* Nettle, GnuTLS, NSS, or LibTomCrypt: secure hash functions (`SECHASH`)
|
||||
* libcap: dropping root privileges on Linux (`DROPROOT`)
|
||||
* libseccomp: system call filter on Linux (`SCFILTER`)
|
||||
* GnuTLS and Nettle: Network Time Security (`NTS`)
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#!/bin/sh
|
||||
# This is a NetworkManager dispatcher script for chronyd to update
|
||||
# its NTP sources passed from DHCP options. Note that this script is
|
||||
# specific to NetworkManager-dispatcher due to use of the
|
||||
# DHCP4_NTP_SERVERS environment variable.
|
||||
# its NTP sources with servers from DHCP options passed by NetworkManager
|
||||
# in the DHCP4_NTP_SERVERS and DHCP6_DHCP6_NTP_SERVERS environment variables.
|
||||
|
||||
export LC_ALL=C
|
||||
|
||||
@@ -10,17 +9,23 @@ interface=$1
|
||||
action=$2
|
||||
|
||||
chronyc=/usr/bin/chronyc
|
||||
default_server_options=iburst
|
||||
server_options=iburst
|
||||
server_dir=/var/run/chrony-dhcp
|
||||
|
||||
dhcp_server_file=$server_dir/$interface.sources
|
||||
# DHCP4_NTP_SERVERS is passed from DHCP options by NetworkManager.
|
||||
nm_dhcp_servers=$DHCP4_NTP_SERVERS
|
||||
dhcp_ntp_servers="$DHCP4_NTP_SERVERS $DHCP6_DHCP6_NTP_SERVERS"
|
||||
|
||||
add_servers_from_dhcp() {
|
||||
rm -f "$dhcp_server_file"
|
||||
for server in $nm_dhcp_servers; do
|
||||
echo "server $server $default_server_options" >> "$dhcp_server_file"
|
||||
for server in $dhcp_ntp_servers; do
|
||||
# Check for invalid characters (from the DHCPv6 NTP FQDN suboption)
|
||||
len1=$(printf '%s' "$server" | wc -c)
|
||||
len2=$(printf '%s' "$server" | tr -d -c 'A-Za-z0-9:.-' | wc -c)
|
||||
if [ "$len1" -ne "$len2" ] || [ "$len2" -lt 1 ] || [ "$len2" -gt 255 ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
printf 'server %s %s\n' "$server" "$server_options" >> "$dhcp_server_file"
|
||||
done
|
||||
$chronyc reload sources > /dev/null 2>&1 || :
|
||||
}
|
||||
@@ -34,10 +39,11 @@ clear_servers_from_dhcp() {
|
||||
|
||||
mkdir -p $server_dir
|
||||
|
||||
if [ "$action" = "up" ] || [ "$action" = "dhcp4-change" ]; then
|
||||
add_servers_from_dhcp
|
||||
elif [ "$action" = "down" ]; then
|
||||
clear_servers_from_dhcp
|
||||
fi
|
||||
case "$action" in
|
||||
up|dhcp4-change|dhcp6-change)
|
||||
add_servers_from_dhcp;;
|
||||
down)
|
||||
clear_servers_from_dhcp;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
|
||||
@@ -7,8 +7,18 @@ export LC_ALL=C
|
||||
|
||||
chronyc=/usr/bin/chronyc
|
||||
|
||||
# For NetworkManager consider only up/down events
|
||||
[ $# -ge 2 ] && [ "$2" != "up" ] && [ "$2" != "down" ] && exit 0
|
||||
# For NetworkManager consider only selected events
|
||||
if [ $# -ge 2 ]; then
|
||||
case "$2" in
|
||||
up|down|connectivity-change)
|
||||
;;
|
||||
dhcp6-change)
|
||||
# No other action is reported for routable IPv6
|
||||
;;
|
||||
*)
|
||||
exit 0;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Note: for networkd-dispatcher routable.d ~= on and off.d ~= off
|
||||
|
||||
|
||||
111
hwclock.c
111
hwclock.c
@@ -2,7 +2,7 @@
|
||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Miroslav Lichvar 2016-2018
|
||||
* Copyright (C) Miroslav Lichvar 2016-2018, 2022
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "local.h"
|
||||
#include "logging.h"
|
||||
#include "memory.h"
|
||||
#include "quantiles.h"
|
||||
#include "regress.h"
|
||||
#include "util.h"
|
||||
|
||||
@@ -43,6 +44,13 @@
|
||||
/* Maximum acceptable frequency offset of the clock */
|
||||
#define MAX_FREQ_OFFSET (2.0 / 3.0)
|
||||
|
||||
/* Quantiles for filtering readings by delay */
|
||||
#define DELAY_QUANT_MIN_K 1
|
||||
#define DELAY_QUANT_MAX_K 2
|
||||
#define DELAY_QUANT_Q 10
|
||||
#define DELAY_QUANT_REPEAT 7
|
||||
#define DELAY_QUANT_MIN_STEP 1.0e-9
|
||||
|
||||
struct HCL_Instance_Record {
|
||||
/* HW and local reference timestamp */
|
||||
struct timespec hw_ref;
|
||||
@@ -64,12 +72,18 @@ struct HCL_Instance_Record {
|
||||
/* Minimum interval between samples */
|
||||
double min_separation;
|
||||
|
||||
/* Expected precision of readings */
|
||||
double precision;
|
||||
|
||||
/* Flag indicating the offset and frequency values are valid */
|
||||
int valid_coefs;
|
||||
|
||||
/* Estimated offset and frequency of HW clock relative to local clock */
|
||||
double offset;
|
||||
double frequency;
|
||||
|
||||
/* Estimated quantiles of reading delay */
|
||||
QNT_Instance delay_quants;
|
||||
};
|
||||
|
||||
/* ================================================== */
|
||||
@@ -92,7 +106,7 @@ handle_slew(struct timespec *raw, struct timespec *cooked, double dfreq,
|
||||
/* ================================================== */
|
||||
|
||||
HCL_Instance
|
||||
HCL_CreateInstance(int min_samples, int max_samples, double min_separation)
|
||||
HCL_CreateInstance(int min_samples, int max_samples, double min_separation, double precision)
|
||||
{
|
||||
HCL_Instance clock;
|
||||
|
||||
@@ -110,6 +124,10 @@ HCL_CreateInstance(int min_samples, int max_samples, double min_separation)
|
||||
clock->n_samples = 0;
|
||||
clock->valid_coefs = 0;
|
||||
clock->min_separation = min_separation;
|
||||
clock->precision = precision;
|
||||
clock->delay_quants = QNT_CreateInstance(DELAY_QUANT_MIN_K, DELAY_QUANT_MAX_K,
|
||||
DELAY_QUANT_Q, DELAY_QUANT_REPEAT,
|
||||
DELAY_QUANT_MIN_STEP);
|
||||
|
||||
LCL_AddParameterChangeHandler(handle_slew, clock);
|
||||
|
||||
@@ -121,6 +139,7 @@ HCL_CreateInstance(int min_samples, int max_samples, double min_separation)
|
||||
void HCL_DestroyInstance(HCL_Instance clock)
|
||||
{
|
||||
LCL_RemoveParameterChangeHandler(handle_slew, clock);
|
||||
QNT_DestroyInstance(clock->delay_quants);
|
||||
Free(clock->y_data);
|
||||
Free(clock->x_data);
|
||||
Free(clock);
|
||||
@@ -140,6 +159,94 @@ HCL_NeedsNewSample(HCL_Instance clock, struct timespec *now)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
HCL_ProcessReadings(HCL_Instance clock, int n_readings, struct timespec tss[][3],
|
||||
struct timespec *hw_ts, struct timespec *local_ts, double *err)
|
||||
{
|
||||
double delay, raw_delay, min_delay, low_delay, high_delay, e, pred_err;
|
||||
double delay_sum, hw_sum, local_sum, local_prec, freq;
|
||||
int i, min_reading, combined;
|
||||
struct timespec ts1, ts2;
|
||||
|
||||
if (n_readings < 1)
|
||||
return 0;
|
||||
|
||||
/* Work out the current correction multiplier needed to get cooked delays */
|
||||
LCL_CookTime(&tss[0][0], &ts1, NULL);
|
||||
LCL_CookTime(&tss[n_readings - 1][2], &ts2, NULL);
|
||||
if (UTI_CompareTimespecs(&tss[0][0], &tss[n_readings - 1][2]) < 0)
|
||||
freq = UTI_DiffTimespecsToDouble(&ts1, &ts2) /
|
||||
UTI_DiffTimespecsToDouble(&tss[0][0], &tss[n_readings - 1][2]);
|
||||
else
|
||||
freq = 1.0;
|
||||
|
||||
for (i = 0; i < n_readings; i++) {
|
||||
delay = freq * UTI_DiffTimespecsToDouble(&tss[i][2], &tss[i][0]);
|
||||
|
||||
if (delay < 0.0) {
|
||||
/* Step in the middle of a reading? */
|
||||
DEBUG_LOG("Bad reading delay=%e", delay);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (i == 0 || min_delay > delay) {
|
||||
min_delay = delay;
|
||||
min_reading = i;
|
||||
}
|
||||
|
||||
QNT_Accumulate(clock->delay_quants, delay);
|
||||
}
|
||||
|
||||
local_prec = LCL_GetSysPrecisionAsQuantum();
|
||||
|
||||
low_delay = QNT_GetQuantile(clock->delay_quants, DELAY_QUANT_MIN_K);
|
||||
high_delay = QNT_GetQuantile(clock->delay_quants, DELAY_QUANT_MAX_K);
|
||||
low_delay = MIN(low_delay, high_delay);
|
||||
high_delay = MAX(high_delay, low_delay + local_prec);
|
||||
|
||||
/* Combine readings with delay in the expected interval */
|
||||
for (i = combined = 0, delay_sum = hw_sum = local_sum = 0.0; i < n_readings; i++) {
|
||||
raw_delay = UTI_DiffTimespecsToDouble(&tss[i][2], &tss[i][0]);
|
||||
delay = freq * raw_delay;
|
||||
|
||||
if (delay < low_delay || delay > high_delay)
|
||||
continue;
|
||||
|
||||
delay_sum += delay;
|
||||
hw_sum += UTI_DiffTimespecsToDouble(&tss[i][1], &tss[0][1]);
|
||||
local_sum += UTI_DiffTimespecsToDouble(&tss[i][0], &tss[0][0]) + raw_delay / 2.0;
|
||||
combined++;
|
||||
}
|
||||
|
||||
DEBUG_LOG("Combined %d readings lo=%e hi=%e", combined, low_delay, high_delay);
|
||||
|
||||
if (combined > 0) {
|
||||
UTI_AddDoubleToTimespec(&tss[0][1], hw_sum / combined, hw_ts);
|
||||
UTI_AddDoubleToTimespec(&tss[0][0], local_sum / combined, local_ts);
|
||||
*err = MAX(delay_sum / combined / 2.0, clock->precision);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Accept the reading with minimum delay if its interval does not contain
|
||||
the current offset predicted from previous samples */
|
||||
|
||||
*hw_ts = tss[min_reading][1];
|
||||
UTI_AddDoubleToTimespec(&tss[min_reading][0], min_delay / freq / 2.0, local_ts);
|
||||
*err = MAX(min_delay / 2.0, clock->precision);
|
||||
|
||||
pred_err = 0.0;
|
||||
LCL_CookTime(local_ts, &ts1, NULL);
|
||||
if (!HCL_CookTime(clock, hw_ts, &ts2, &e) ||
|
||||
((pred_err = UTI_DiffTimespecsToDouble(&ts1, &ts2)) > *err)) {
|
||||
DEBUG_LOG("Accepted reading err=%e prerr=%e", *err, pred_err);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
|
||||
struct timespec *local_ts, double err)
|
||||
|
||||
@@ -30,7 +30,7 @@ typedef struct HCL_Instance_Record *HCL_Instance;
|
||||
|
||||
/* Create a new HW clock instance */
|
||||
extern HCL_Instance HCL_CreateInstance(int min_samples, int max_samples,
|
||||
double min_separation);
|
||||
double min_separation, double precision);
|
||||
|
||||
/* Destroy a HW clock instance */
|
||||
extern void HCL_DestroyInstance(HCL_Instance clock);
|
||||
@@ -38,6 +38,11 @@ extern void HCL_DestroyInstance(HCL_Instance clock);
|
||||
/* Check if a new sample should be accumulated at this time */
|
||||
extern int HCL_NeedsNewSample(HCL_Instance clock, struct timespec *now);
|
||||
|
||||
/* Process new readings of the HW clock in form of (sys, hw, sys) triplets and
|
||||
produce a sample which can be accumulated */
|
||||
extern int HCL_ProcessReadings(HCL_Instance clock, int n_readings, struct timespec tss[][3],
|
||||
struct timespec *hw_ts, struct timespec *local_ts, double *err);
|
||||
|
||||
/* Accumulate a new sample */
|
||||
extern void HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
|
||||
struct timespec *local_ts, double err);
|
||||
|
||||
20
local.c
20
local.c
@@ -563,6 +563,8 @@ void
|
||||
LCL_NotifyExternalTimeStep(struct timespec *raw, struct timespec *cooked,
|
||||
double offset, double dispersion)
|
||||
{
|
||||
LCL_CancelOffsetCorrection();
|
||||
|
||||
/* Dispatch to all handlers */
|
||||
invoke_parameter_change_handlers(raw, cooked, 0.0, offset, LCL_ChangeUnknownStep);
|
||||
|
||||
@@ -628,6 +630,24 @@ LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
LCL_AccumulateFrequencyAndOffsetNoHandlers(double dfreq, double doffset, double corr_rate)
|
||||
{
|
||||
ChangeListEntry *first_handler;
|
||||
int r;
|
||||
|
||||
first_handler = change_list.next;
|
||||
change_list.next = &change_list;
|
||||
|
||||
r = LCL_AccumulateFrequencyAndOffset(dfreq, doffset, corr_rate);
|
||||
|
||||
change_list.next = first_handler;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
lcl_InvokeDispersionNotifyHandlers(double dispersion)
|
||||
{
|
||||
|
||||
5
local.h
5
local.h
@@ -173,6 +173,11 @@ extern void LCL_NotifyLeap(int leap);
|
||||
a slew, in one easy step */
|
||||
extern int LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate);
|
||||
|
||||
/* Same as the routine above, except it does not call the registered
|
||||
parameter change handlers */
|
||||
extern int LCL_AccumulateFrequencyAndOffsetNoHandlers(double dfreq, double doffset,
|
||||
double corr_rate);
|
||||
|
||||
/* Routine to read the system precision as a log to base 2 value. */
|
||||
extern int LCL_GetSysPrecisionAsLog(void);
|
||||
|
||||
|
||||
2
main.c
2
main.c
@@ -166,6 +166,8 @@ signal_cleanup(int x)
|
||||
static void
|
||||
quit_timeout(void *arg)
|
||||
{
|
||||
LOG(LOGS_INFO, "Timeout reached");
|
||||
|
||||
/* Return with non-zero status if the clock is not synchronised */
|
||||
exit_status = REF_GetOurStratum() >= NTP_MAX_STRATUM;
|
||||
SCH_QuitProgram();
|
||||
|
||||
130
ntp_core.c
130
ntp_core.c
@@ -3,7 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2009-2020
|
||||
* Copyright (C) Miroslav Lichvar 2009-2022
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "ntp_ext.h"
|
||||
#include "ntp_io.h"
|
||||
#include "memory.h"
|
||||
#include "quantiles.h"
|
||||
#include "sched.h"
|
||||
#include "reference.h"
|
||||
#include "local.h"
|
||||
@@ -196,8 +197,12 @@ struct NCR_Instance_Record {
|
||||
|
||||
SRC_Instance source;
|
||||
|
||||
/* Optional long-term quantile estimate of peer delay */
|
||||
QNT_Instance delay_quant;
|
||||
|
||||
/* Optional median filter for NTP measurements */
|
||||
SPF_Instance filter;
|
||||
int filter_count;
|
||||
|
||||
int burst_good_samples_to_go;
|
||||
int burst_total_samples_to_go;
|
||||
@@ -265,8 +270,12 @@ static ARR_Instance broadcasts;
|
||||
#define MAX_MAXDELAYRATIO 1.0e6
|
||||
#define MAX_MAXDELAYDEVRATIO 1.0e6
|
||||
|
||||
/* Parameters for the peer delay quantile */
|
||||
#define DELAY_QUANT_Q 100
|
||||
#define DELAY_QUANT_REPEAT 7
|
||||
|
||||
/* Minimum and maximum allowed poll interval */
|
||||
#define MIN_POLL -6
|
||||
#define MIN_POLL -7
|
||||
#define MAX_POLL 24
|
||||
|
||||
/* Enable sub-second polling intervals only when the peer delay is not
|
||||
@@ -318,6 +327,7 @@ static void transmit_timeout(void *arg);
|
||||
static double get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx);
|
||||
static double get_separation(int poll);
|
||||
static int parse_packet(NTP_Packet *packet, int length, NTP_PacketInfo *info);
|
||||
static void process_sample(NCR_Instance inst, NTP_Sample *sample);
|
||||
static void set_connectivity(NCR_Instance inst, SRC_Connectivity connectivity);
|
||||
|
||||
/* ================================================== */
|
||||
@@ -546,6 +556,16 @@ take_offline(NCR_Instance inst)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
reset_report(NCR_Instance inst)
|
||||
{
|
||||
memset(&inst->report, 0, sizeof (inst->report));
|
||||
inst->report.remote_addr = inst->remote_addr.ip_addr;
|
||||
inst->report.remote_port = inst->remote_addr.port;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
NCR_Instance
|
||||
NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
|
||||
SourceParameters *params, const char *name)
|
||||
@@ -640,9 +660,16 @@ NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
|
||||
params->min_samples, params->max_samples,
|
||||
params->min_delay, params->asymmetry);
|
||||
|
||||
if (params->max_delay_quant > 0.0) {
|
||||
int k = round(CLAMP(0.05, params->max_delay_quant, 0.95) * DELAY_QUANT_Q);
|
||||
result->delay_quant = QNT_CreateInstance(k, k, DELAY_QUANT_Q, DELAY_QUANT_REPEAT,
|
||||
LCL_GetSysPrecisionAsQuantum() / 2.0);
|
||||
} else {
|
||||
result->delay_quant = NULL;
|
||||
}
|
||||
|
||||
if (params->filter_length >= 1)
|
||||
result->filter = SPF_CreateInstance(params->filter_length, params->filter_length,
|
||||
NTP_MAX_DISPERSION, 0.0);
|
||||
result->filter = SPF_CreateInstance(1, params->filter_length, NTP_MAX_DISPERSION, 0.0);
|
||||
else
|
||||
result->filter = NULL;
|
||||
|
||||
@@ -650,17 +677,18 @@ NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
|
||||
result->tx_timeout_id = 0;
|
||||
result->tx_suspended = 1;
|
||||
result->opmode = MD_OFFLINE;
|
||||
result->local_poll = result->minpoll;
|
||||
result->local_poll = MAX(result->minpoll, MIN_NONLAN_POLL);
|
||||
result->poll_score = 0.0;
|
||||
zero_local_timestamp(&result->local_tx);
|
||||
result->burst_good_samples_to_go = 0;
|
||||
result->burst_total_samples_to_go = 0;
|
||||
memset(&result->report, 0, sizeof (result->report));
|
||||
|
||||
NCR_ResetInstance(result);
|
||||
|
||||
set_connectivity(result, params->connectivity);
|
||||
|
||||
reset_report(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -676,6 +704,8 @@ NCR_DestroyInstance(NCR_Instance instance)
|
||||
if (instance->mode == MODE_ACTIVE)
|
||||
NIO_CloseServerSocket(instance->local_addr.sock_fd);
|
||||
|
||||
if (instance->delay_quant)
|
||||
QNT_DestroyInstance(instance->delay_quant);
|
||||
if (instance->filter)
|
||||
SPF_DestroyInstance(instance->filter);
|
||||
|
||||
@@ -732,8 +762,11 @@ NCR_ResetInstance(NCR_Instance instance)
|
||||
UTI_ZeroNtp64(&instance->init_remote_ntp_tx);
|
||||
zero_local_timestamp(&instance->init_local_rx);
|
||||
|
||||
if (instance->delay_quant)
|
||||
QNT_Reset(instance->delay_quant);
|
||||
if (instance->filter)
|
||||
SPF_DropSamples(instance->filter);
|
||||
instance->filter_count = 0;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -755,7 +788,6 @@ NCR_ResetPoll(NCR_Instance instance)
|
||||
void
|
||||
NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr, int ntp_only)
|
||||
{
|
||||
memset(&inst->report, 0, sizeof (inst->report));
|
||||
NCR_ResetInstance(inst);
|
||||
|
||||
if (!ntp_only)
|
||||
@@ -776,6 +808,8 @@ NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr, int
|
||||
SRC_SetRefid(inst->source, UTI_IPToRefid(&remote_addr->ip_addr),
|
||||
&inst->remote_addr.ip_addr);
|
||||
SRC_ResetInstance(inst->source);
|
||||
|
||||
reset_report(inst);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -783,6 +817,8 @@ NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr, int
|
||||
static void
|
||||
adjust_poll(NCR_Instance inst, double adj)
|
||||
{
|
||||
NTP_Sample last_sample;
|
||||
|
||||
inst->poll_score += adj;
|
||||
|
||||
if (inst->poll_score >= 1.0) {
|
||||
@@ -808,7 +844,9 @@ adjust_poll(NCR_Instance inst, double adj)
|
||||
or it is not in a local network according to the measured delay */
|
||||
if (inst->local_poll < MIN_NONLAN_POLL &&
|
||||
(!SRC_IsReachable(inst->source) ||
|
||||
SST_MinRoundTripDelay(SRC_GetSourcestats(inst->source)) > MAX_LAN_PEER_DELAY))
|
||||
(SST_MinRoundTripDelay(SRC_GetSourcestats(inst->source)) > MAX_LAN_PEER_DELAY &&
|
||||
(!inst->filter || !SPF_GetLastSample(inst->filter, &last_sample) ||
|
||||
last_sample.peer_delay > MAX_LAN_PEER_DELAY))))
|
||||
inst->local_poll = MIN_NONLAN_POLL;
|
||||
}
|
||||
|
||||
@@ -1353,6 +1391,9 @@ transmit_timeout(void *arg)
|
||||
}
|
||||
|
||||
SRC_UpdateReachability(inst->source, 0);
|
||||
|
||||
/* Count missing samples for the sample filter */
|
||||
process_sample(inst, NULL);
|
||||
}
|
||||
|
||||
/* With auto_offline take the source offline if sending failed */
|
||||
@@ -1541,6 +1582,22 @@ check_delay_ratio(NCR_Instance inst, SST_Stats stats,
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
check_delay_quant(NCR_Instance inst, double delay)
|
||||
{
|
||||
double quant;
|
||||
|
||||
quant = QNT_GetQuantile(inst->delay_quant, QNT_GetMinK(inst->delay_quant));
|
||||
|
||||
if (delay <= quant)
|
||||
return 1;
|
||||
|
||||
DEBUG_LOG("maxdelayquant: delay=%e quant=%e", delay, quant);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
check_delay_dev_ratio(NCR_Instance inst, SST_Stats stats,
|
||||
struct timespec *sample_time, double offset, double delay)
|
||||
@@ -1626,33 +1683,29 @@ check_sync_loop(NCR_Instance inst, NTP_Packet *message, NTP_Local_Address *local
|
||||
static void
|
||||
process_sample(NCR_Instance inst, NTP_Sample *sample)
|
||||
{
|
||||
double estimated_offset, error_in_estimate, filtered_sample_ago;
|
||||
double estimated_offset, error_in_estimate;
|
||||
NTP_Sample filtered_sample;
|
||||
int filtered_samples;
|
||||
|
||||
/* Accumulate the sample to the median filter if it is enabled. When the
|
||||
filter produces a result, check if it is not too old, i.e. the filter did
|
||||
not miss too many samples due to missing responses or failing tests. */
|
||||
/* Accumulate the sample to the median filter if enabled and wait for
|
||||
the configured number of samples before processing (NULL indicates
|
||||
a missing sample) */
|
||||
if (inst->filter) {
|
||||
SPF_AccumulateSample(inst->filter, sample);
|
||||
if (sample)
|
||||
SPF_AccumulateSample(inst->filter, sample);
|
||||
|
||||
filtered_samples = SPF_GetNumberOfSamples(inst->filter);
|
||||
if (++inst->filter_count < SPF_GetMaxSamples(inst->filter))
|
||||
return;
|
||||
|
||||
if (!SPF_GetFilteredSample(inst->filter, &filtered_sample))
|
||||
return;
|
||||
|
||||
filtered_sample_ago = UTI_DiffTimespecsToDouble(&sample->time, &filtered_sample.time);
|
||||
|
||||
if (filtered_sample_ago > SOURCE_REACH_BITS / 2 * filtered_samples *
|
||||
UTI_Log2ToDouble(inst->local_poll)) {
|
||||
DEBUG_LOG("filtered sample dropped ago=%f poll=%d", filtered_sample_ago,
|
||||
inst->local_poll);
|
||||
return;
|
||||
}
|
||||
|
||||
sample = &filtered_sample;
|
||||
inst->filter_count = 0;
|
||||
}
|
||||
|
||||
if (!sample)
|
||||
return;
|
||||
|
||||
/* Get the estimated offset predicted from previous samples. The
|
||||
convention here is that positive means local clock FAST of
|
||||
reference, i.e. backwards to the way that 'offset' is defined. */
|
||||
@@ -1907,12 +1960,17 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
|
||||
|
||||
/* Test A requires that the minimum estimate of the peer delay is not
|
||||
larger than the configured maximum, in both client modes that the server
|
||||
processing time is sane, and in interleaved symmetric mode that the
|
||||
processing time is sane, in interleaved client/server mode that the
|
||||
previous response was not in basic mode (which prevents using timestamps
|
||||
that minimise delay error), and in interleaved symmetric mode that the
|
||||
measured delay and intervals between remote timestamps don't indicate
|
||||
a missed response */
|
||||
testA = sample.peer_delay - sample.peer_dispersion <= inst->max_delay &&
|
||||
precision <= inst->max_delay &&
|
||||
!(inst->mode == MODE_CLIENT && response_time > MAX_SERVER_INTERVAL) &&
|
||||
!(inst->mode == MODE_CLIENT && interleaved_packet &&
|
||||
UTI_IsZeroTimespec(&inst->prev_local_tx.ts) &&
|
||||
UTI_CompareTimespecs(&local_transmit.ts, &inst->local_tx.ts) == 0) &&
|
||||
!(inst->mode == MODE_ACTIVE && interleaved_packet &&
|
||||
(sample.peer_delay > 0.5 * prev_remote_poll_interval ||
|
||||
UTI_CompareNtp64(&message->receive_ts, &message->transmit_ts) <= 0 ||
|
||||
@@ -1925,12 +1983,18 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
|
||||
administrator-defined value */
|
||||
testB = check_delay_ratio(inst, stats, &sample.time, sample.peer_delay);
|
||||
|
||||
/* Test C requires that the ratio of the increase in delay from the minimum
|
||||
/* Test C either requires that the delay is less than an estimate of an
|
||||
administrator-defined quantile, or (if the quantile is not specified)
|
||||
it 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 */
|
||||
testC = check_delay_dev_ratio(inst, stats, &sample.time, sample.offset, sample.peer_delay);
|
||||
if (inst->delay_quant)
|
||||
testC = check_delay_quant(inst, sample.peer_delay);
|
||||
else
|
||||
testC = check_delay_dev_ratio(inst, stats, &sample.time, sample.offset,
|
||||
sample.peer_delay);
|
||||
|
||||
/* Test D requires that the source is not synchronised to us and is not us
|
||||
to prevent a synchronisation loop */
|
||||
@@ -1972,7 +2036,7 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
|
||||
updated_timestamps = 2;
|
||||
|
||||
/* If available, update the monotonic timestamp and accumulate the offset.
|
||||
This needs to be done here to no lose changes in remote_ntp_rx in
|
||||
This needs to be done here to not lose changes in remote_ntp_rx in
|
||||
symmetric mode when there are multiple responses per request. */
|
||||
if (ef_exp1 && !UTI_IsZeroNtp64(&ef_exp1->mono_receive_ts)) {
|
||||
inst->remote_mono_epoch = ntohl(ef_exp1->mono_epoch);
|
||||
@@ -2063,6 +2127,9 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
|
||||
}
|
||||
|
||||
SRC_UpdateStatus(inst->source, MAX(inst->remote_stratum, inst->min_stratum), pkt_leap);
|
||||
|
||||
if (inst->delay_quant)
|
||||
QNT_Accumulate(inst->delay_quant, sample.peer_delay);
|
||||
}
|
||||
|
||||
if (good_packet) {
|
||||
@@ -2088,6 +2155,9 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
|
||||
} else {
|
||||
/* Slowly increase the polling interval if we can't get a good response */
|
||||
adjust_poll(inst, testD ? 0.02 : 0.1);
|
||||
|
||||
/* Count missing samples for the sample filter */
|
||||
process_sample(inst, NULL);
|
||||
}
|
||||
|
||||
/* If in client mode, no more packets are expected to be coming from the
|
||||
@@ -2121,9 +2191,7 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
|
||||
}
|
||||
|
||||
/* Update the NTP report */
|
||||
inst->report.remote_addr = inst->remote_addr.ip_addr;
|
||||
inst->report.local_addr = inst->local_addr.ip_addr;
|
||||
inst->report.remote_port = inst->remote_addr.port;
|
||||
inst->report.leap = pkt_leap;
|
||||
inst->report.version = pkt_version;
|
||||
inst->report.mode = NTP_LVM_TO_MODE(message->lvm);
|
||||
@@ -2148,6 +2216,8 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
|
||||
inst->report.rx_tss_char = tss_chars[local_receive.source];
|
||||
|
||||
inst->report.total_valid_count++;
|
||||
if (good_packet)
|
||||
inst->report.total_good_count++;
|
||||
}
|
||||
|
||||
/* Do measurement logging */
|
||||
|
||||
4
ntp_io.c
4
ntp_io.c
@@ -4,7 +4,7 @@
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Timo Teras 2009
|
||||
* Copyright (C) Miroslav Lichvar 2009, 2013-2016, 2018-2020
|
||||
* Copyright (C) Miroslav Lichvar 2009, 2013-2016, 2018-2021
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -594,7 +594,7 @@ NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
|
||||
#if !defined(HAVE_IN_PKTINFO) && defined(IP_SENDSRCADDR)
|
||||
/* On FreeBSD a local IPv4 address cannot be specified on bound socket */
|
||||
if (message.local_addr.ip.family == IPADDR_INET4 &&
|
||||
(local_addr->sock_fd != server_sock_fd4 || bound_server_sock_fd4))
|
||||
(bound_server_sock_fd4 || !NIO_IsServerSocket(local_addr->sock_fd)))
|
||||
message.local_addr.ip.family = IPADDR_UNSPEC;
|
||||
#endif
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Miroslav Lichvar 2016-2019
|
||||
* Copyright (C) Miroslav Lichvar 2016-2019, 2021-2022
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -59,8 +59,6 @@ struct Interface {
|
||||
/* Start of UDP data at layer 2 for IPv4 and IPv6 */
|
||||
int l2_udp4_ntp_start;
|
||||
int l2_udp6_ntp_start;
|
||||
/* Precision of PHC readings */
|
||||
double precision;
|
||||
/* Compensation of errors in TX and RX timestamping */
|
||||
double tx_comp;
|
||||
double rx_comp;
|
||||
@@ -68,7 +66,7 @@ struct Interface {
|
||||
};
|
||||
|
||||
/* Number of PHC readings per HW clock sample */
|
||||
#define PHC_READINGS 10
|
||||
#define PHC_READINGS 25
|
||||
|
||||
/* Minimum interval between PHC readings */
|
||||
#define MIN_PHC_POLL -6
|
||||
@@ -244,12 +242,12 @@ add_interface(CNF_HwTsInterface *conf_iface)
|
||||
iface->l2_udp4_ntp_start = 42;
|
||||
iface->l2_udp6_ntp_start = 62;
|
||||
|
||||
iface->precision = conf_iface->precision;
|
||||
iface->tx_comp = conf_iface->tx_comp;
|
||||
iface->rx_comp = conf_iface->rx_comp;
|
||||
|
||||
iface->clock = HCL_CreateInstance(conf_iface->min_samples, conf_iface->max_samples,
|
||||
UTI_Log2ToDouble(MAX(conf_iface->minpoll, MIN_PHC_POLL)));
|
||||
UTI_Log2ToDouble(MAX(conf_iface->minpoll, MIN_PHC_POLL)),
|
||||
conf_iface->precision);
|
||||
|
||||
LOG(LOGS_INFO, "Enabled HW timestamping %son %s",
|
||||
ts_config.rx_filter == HWTSTAMP_FILTER_NONE ? "(TX only) " : "", iface->name);
|
||||
@@ -566,19 +564,22 @@ process_hw_timestamp(struct Interface *iface, struct timespec *hw_ts,
|
||||
int l2_length)
|
||||
{
|
||||
struct timespec sample_phc_ts, sample_sys_ts, sample_local_ts, ts;
|
||||
struct timespec phc_readings[PHC_READINGS][3];
|
||||
double rx_correction, ts_delay, phc_err, local_err;
|
||||
int n_readings;
|
||||
|
||||
if (HCL_NeedsNewSample(iface->clock, &local_ts->ts)) {
|
||||
if (!SYS_Linux_GetPHCSample(iface->phc_fd, iface->phc_nocrossts, iface->precision,
|
||||
&iface->phc_mode, &sample_phc_ts, &sample_sys_ts,
|
||||
&phc_err))
|
||||
return;
|
||||
n_readings = SYS_Linux_GetPHCReadings(iface->phc_fd, iface->phc_nocrossts,
|
||||
&iface->phc_mode, PHC_READINGS, phc_readings);
|
||||
if (n_readings > 0 &&
|
||||
HCL_ProcessReadings(iface->clock, n_readings, phc_readings,
|
||||
&sample_phc_ts, &sample_sys_ts, &phc_err)) {
|
||||
LCL_CookTime(&sample_sys_ts, &sample_local_ts, &local_err);
|
||||
HCL_AccumulateSample(iface->clock, &sample_phc_ts, &sample_local_ts,
|
||||
phc_err + local_err);
|
||||
|
||||
LCL_CookTime(&sample_sys_ts, &sample_local_ts, &local_err);
|
||||
HCL_AccumulateSample(iface->clock, &sample_phc_ts, &sample_local_ts,
|
||||
phc_err + local_err);
|
||||
|
||||
update_interface_speed(iface);
|
||||
update_interface_speed(iface);
|
||||
}
|
||||
}
|
||||
|
||||
/* We need to transpose RX timestamps as hardware timestamps are normally
|
||||
|
||||
@@ -353,7 +353,6 @@ add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type,
|
||||
record_lock = 1;
|
||||
|
||||
record = get_record(slot);
|
||||
assert(!name || !UTI_IsStringIP(name));
|
||||
record->name = Strdup(name ? name : UTI_IPToString(&remote_addr->ip_addr));
|
||||
record->data = NCR_CreateInstance(remote_addr, type, params, record->name);
|
||||
record->remote_addr = NCR_GetRemoteAddress(record->data);
|
||||
@@ -698,21 +697,25 @@ static int get_unused_pool_id(void)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static uint32_t
|
||||
get_next_conf_id(uint32_t *conf_id)
|
||||
{
|
||||
last_conf_id++;
|
||||
|
||||
if (conf_id)
|
||||
*conf_id = last_conf_id;
|
||||
|
||||
return last_conf_id;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
NSR_Status
|
||||
NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
|
||||
SourceParameters *params, uint32_t *conf_id)
|
||||
{
|
||||
NSR_Status s;
|
||||
|
||||
s = add_source(remote_addr, NULL, type, params, INVALID_POOL, last_conf_id + 1);
|
||||
if (s != NSR_Success)
|
||||
return s;
|
||||
|
||||
last_conf_id++;
|
||||
if (conf_id)
|
||||
*conf_id = last_conf_id;
|
||||
|
||||
return s;
|
||||
return add_source(remote_addr, NULL, type, params, INVALID_POOL,
|
||||
get_next_conf_id(conf_id));
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -725,11 +728,13 @@ NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
|
||||
struct SourcePool *sp;
|
||||
NTP_Remote_Address remote_addr;
|
||||
int i, new_sources, pool_id;
|
||||
uint32_t cid;
|
||||
|
||||
/* If the name is an IP address, add the source with the address directly */
|
||||
if (UTI_StringToIP(name, &remote_addr.ip_addr)) {
|
||||
remote_addr.port = port;
|
||||
return NSR_AddSource(&remote_addr, type, params, conf_id);
|
||||
return add_source(&remote_addr, name, type, params, INVALID_POOL,
|
||||
get_next_conf_id(conf_id));
|
||||
}
|
||||
|
||||
/* Make sure the name is at least printable and has no spaces */
|
||||
@@ -770,14 +775,12 @@ NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
|
||||
|
||||
append_unresolved_source(us);
|
||||
|
||||
last_conf_id++;
|
||||
if (conf_id)
|
||||
*conf_id = last_conf_id;
|
||||
cid = get_next_conf_id(conf_id);
|
||||
|
||||
for (i = 0; i < new_sources; i++) {
|
||||
if (i > 0)
|
||||
remote_addr.ip_addr.addr.id = ++last_address_id;
|
||||
if (add_source(&remote_addr, name, type, params, us->pool_id, last_conf_id) != NSR_Success)
|
||||
if (add_source(&remote_addr, name, type, params, us->pool_id, cid) != NSR_Success)
|
||||
return NSR_TooManySources;
|
||||
}
|
||||
|
||||
@@ -1100,8 +1103,10 @@ NSR_ProcessRx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
|
||||
|
||||
assert(initialised);
|
||||
|
||||
/* Must match IP address AND port number */
|
||||
if (find_slot2(remote_addr, &slot) == 2) {
|
||||
/* Avoid unnecessary lookup if the packet cannot be a response from our
|
||||
source. Otherwise, it must match both IP address and port number. */
|
||||
if (NTP_LVM_TO_MODE(message->lvm) != MODE_CLIENT &&
|
||||
find_slot2(remote_addr, &slot) == 2) {
|
||||
record = get_record(slot);
|
||||
|
||||
if (!NCR_ProcessRxKnown(record->data, local_addr, rx_ts, message, length))
|
||||
@@ -1137,8 +1142,10 @@ NSR_ProcessTx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
|
||||
SourceRecord *record;
|
||||
int slot;
|
||||
|
||||
/* Must match IP address AND port number */
|
||||
if (find_slot2(remote_addr, &slot) == 2) {
|
||||
/* Avoid unnecessary lookup if the packet cannot be a request to our
|
||||
source. Otherwise, it must match both IP address and port number. */
|
||||
if (NTP_LVM_TO_MODE(message->lvm) != MODE_SERVER &&
|
||||
find_slot2(remote_addr, &slot) == 2) {
|
||||
record = get_record(slot);
|
||||
NCR_ProcessTxKnown(record->data, local_addr, tx_ts, message, length);
|
||||
} else {
|
||||
|
||||
@@ -594,13 +594,13 @@ handle_step(struct timespec *raw, struct timespec *cooked, double dfreq,
|
||||
|
||||
static int gnutls_initialised = 0;
|
||||
|
||||
static void
|
||||
static int
|
||||
init_gnutls(void)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (gnutls_initialised)
|
||||
return;
|
||||
return 1;
|
||||
|
||||
r = gnutls_global_init();
|
||||
if (r < 0)
|
||||
@@ -611,8 +611,12 @@ init_gnutls(void)
|
||||
r = gnutls_priority_init2(&priority_cache,
|
||||
"-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.2:-VERS-DTLS-ALL",
|
||||
NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND);
|
||||
if (r < 0)
|
||||
LOG_FATAL("Could not initialise %s : %s", "priority cache", gnutls_strerror(r));
|
||||
if (r < 0) {
|
||||
LOG(LOGS_ERR, "Could not initialise %s : %s",
|
||||
"priority cache for TLS", gnutls_strerror(r));
|
||||
gnutls_global_deinit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Use our clock instead of the system clock in certificate verification */
|
||||
gnutls_global_set_time_function(get_time);
|
||||
@@ -621,6 +625,8 @@ init_gnutls(void)
|
||||
DEBUG_LOG("Initialised");
|
||||
|
||||
LCL_AddParameterChangeHandler(handle_step, NULL);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -649,7 +655,8 @@ create_credentials(const char **certs, const char **keys, int n_certs_keys,
|
||||
gnutls_certificate_credentials_t credentials = NULL;
|
||||
int i, r;
|
||||
|
||||
init_gnutls();
|
||||
if (!init_gnutls())
|
||||
return NULL;
|
||||
|
||||
r = gnutls_certificate_allocate_credentials(&credentials);
|
||||
if (r < 0)
|
||||
|
||||
209
quantiles.c
Normal file
209
quantiles.c
Normal file
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Miroslav Lichvar 2022
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
**********************************************************************
|
||||
|
||||
=======================================================================
|
||||
|
||||
Estimation of quantiles using the Frugal-2U streaming algorithm
|
||||
(https://arxiv.org/pdf/1407.1121v1.pdf)
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "logging.h"
|
||||
#include "memory.h"
|
||||
#include "quantiles.h"
|
||||
#include "regress.h"
|
||||
#include "util.h"
|
||||
|
||||
/* Maximum number of repeated estimates for stabilisation */
|
||||
#define MAX_REPEAT 64
|
||||
|
||||
struct Quantile {
|
||||
double est;
|
||||
double step;
|
||||
int sign;
|
||||
};
|
||||
|
||||
struct QNT_Instance_Record {
|
||||
struct Quantile *quants;
|
||||
int n_quants;
|
||||
int repeat;
|
||||
int q;
|
||||
int min_k;
|
||||
double min_step;
|
||||
int n_set;
|
||||
};
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
QNT_Instance
|
||||
QNT_CreateInstance(int min_k, int max_k, int q, int repeat, double min_step)
|
||||
{
|
||||
QNT_Instance inst;
|
||||
long seed;
|
||||
|
||||
if (q < 2 || min_k > max_k || min_k < 1 || max_k >= q ||
|
||||
repeat < 1 || repeat > MAX_REPEAT || min_step <= 0.0)
|
||||
assert(0);
|
||||
|
||||
inst = MallocNew(struct QNT_Instance_Record);
|
||||
inst->n_quants = (max_k - min_k + 1) * repeat;
|
||||
inst->quants = MallocArray(struct Quantile, inst->n_quants);
|
||||
inst->repeat = repeat;
|
||||
inst->q = q;
|
||||
inst->min_k = min_k;
|
||||
inst->min_step = min_step;
|
||||
|
||||
QNT_Reset(inst);
|
||||
|
||||
/* Seed the random number generator, which will not be isolated from
|
||||
other instances and other random() users */
|
||||
UTI_GetRandomBytes(&seed, sizeof (seed));
|
||||
srandom(seed);
|
||||
|
||||
return inst;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
QNT_DestroyInstance(QNT_Instance inst)
|
||||
{
|
||||
Free(inst->quants);
|
||||
Free(inst);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
QNT_Reset(QNT_Instance inst)
|
||||
{
|
||||
int i;
|
||||
|
||||
inst->n_set = 0;
|
||||
|
||||
for (i = 0; i < inst->n_quants; i++) {
|
||||
inst->quants[i].est = 0.0;
|
||||
inst->quants[i].step = inst->min_step;
|
||||
inst->quants[i].sign = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
insert_initial_value(QNT_Instance inst, double value)
|
||||
{
|
||||
int i, j, r = inst->repeat;
|
||||
|
||||
if (inst->n_set * r >= inst->n_quants)
|
||||
assert(0);
|
||||
|
||||
/* Keep the initial estimates repeated and ordered */
|
||||
for (i = inst->n_set; i > 0 && inst->quants[(i - 1) * r].est > value; i--) {
|
||||
for (j = 0; j < r; j++)
|
||||
inst->quants[i * r + j].est = inst->quants[(i - 1) * r].est;
|
||||
}
|
||||
|
||||
for (j = 0; j < r; j++)
|
||||
inst->quants[i * r + j].est = value;
|
||||
inst->n_set++;
|
||||
|
||||
/* Duplicate the largest value in unset quantiles */
|
||||
for (i = inst->n_set * r; i < inst->n_quants; i++)
|
||||
inst->quants[i].est = inst->quants[i - 1].est;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
update_estimate(struct Quantile *quantile, double value, double p, double rand,
|
||||
double min_step)
|
||||
{
|
||||
if (value > quantile->est && rand > (1.0 - p)) {
|
||||
quantile->step += quantile->sign > 0 ? min_step : -min_step;
|
||||
quantile->est += quantile->step > 0.0 ? fabs(quantile->step) : min_step;
|
||||
if (quantile->est > value) {
|
||||
quantile->step += value - quantile->est;
|
||||
quantile->est = value;
|
||||
}
|
||||
if (quantile->sign < 0 && quantile->step > min_step)
|
||||
quantile->step = min_step;
|
||||
quantile->sign = 1;
|
||||
} else if (value < quantile->est && rand > p) {
|
||||
quantile->step += quantile->sign < 0 ? min_step : -min_step;
|
||||
quantile->est -= quantile->step > 0.0 ? fabs(quantile->step) : min_step;
|
||||
if (quantile->est < value) {
|
||||
quantile->step += quantile->est - value;
|
||||
quantile->est = value;
|
||||
}
|
||||
if (quantile->sign > 0 && quantile->step > min_step)
|
||||
quantile->step = min_step;
|
||||
quantile->sign = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
QNT_Accumulate(QNT_Instance inst, double value)
|
||||
{
|
||||
double p, rand;
|
||||
int i;
|
||||
|
||||
/* Initialise the estimates with first received values */
|
||||
if (inst->n_set * inst->repeat < inst->n_quants) {
|
||||
insert_initial_value(inst, value);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < inst->n_quants; i++) {
|
||||
p = (double)(i / inst->repeat + inst->min_k) / inst->q;
|
||||
rand = (double)random() / ((1U << 31) - 1);
|
||||
|
||||
update_estimate(&inst->quants[i], value, p, rand, inst->min_step);
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
QNT_GetMinK(QNT_Instance inst)
|
||||
{
|
||||
return inst->min_k;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
double
|
||||
QNT_GetQuantile(QNT_Instance inst, int k)
|
||||
{
|
||||
double estimates[MAX_REPEAT];
|
||||
int i;
|
||||
|
||||
if (k < inst->min_k || k - inst->min_k >= inst->n_quants)
|
||||
assert(0);
|
||||
|
||||
for (i = 0; i < inst->repeat; i++)
|
||||
estimates[i] = inst->quants[(k - inst->min_k) * inst->repeat + i].est;
|
||||
|
||||
return RGR_FindMedian(estimates, inst->repeat);
|
||||
}
|
||||
41
quantiles.h
Normal file
41
quantiles.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Miroslav Lichvar 2022
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
**********************************************************************
|
||||
|
||||
=======================================================================
|
||||
|
||||
Header file for estimation of quantiles.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef GOT_QUANTILES_H
|
||||
#define GOT_QUANTILES_H
|
||||
|
||||
typedef struct QNT_Instance_Record *QNT_Instance;
|
||||
|
||||
extern QNT_Instance QNT_CreateInstance(int min_k, int max_k, int q, int repeat, double min_step);
|
||||
extern void QNT_DestroyInstance(QNT_Instance inst);
|
||||
|
||||
extern void QNT_Reset(QNT_Instance inst);
|
||||
extern void QNT_Accumulate(QNT_Instance inst, double value);
|
||||
extern int QNT_GetMinK(QNT_Instance inst);
|
||||
extern double QNT_GetQuantile(QNT_Instance inst, int k);
|
||||
|
||||
#endif
|
||||
131
refclock.c
131
refclock.c
@@ -2,7 +2,7 @@
|
||||
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Miroslav Lichvar 2009-2011, 2013-2014, 2016-2019
|
||||
* Copyright (C) Miroslav Lichvar 2009-2011, 2013-2014, 2016-2019, 2022
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -55,21 +55,6 @@ struct FilterSample {
|
||||
struct timespec sample_time;
|
||||
};
|
||||
|
||||
struct MedianFilter {
|
||||
int length;
|
||||
int index;
|
||||
int used;
|
||||
int last;
|
||||
int avg_var_n;
|
||||
double avg_var;
|
||||
double max_var;
|
||||
struct FilterSample *samples;
|
||||
int *selected;
|
||||
double *x_data;
|
||||
double *y_data;
|
||||
double *w_data;
|
||||
};
|
||||
|
||||
struct RCL_Instance_Record {
|
||||
RefclockDriver *driver;
|
||||
void *data;
|
||||
@@ -79,6 +64,7 @@ struct RCL_Instance_Record {
|
||||
int driver_polled;
|
||||
int poll;
|
||||
int leap_status;
|
||||
int local;
|
||||
int pps_forced;
|
||||
int pps_rate;
|
||||
int pps_active;
|
||||
@@ -190,6 +176,7 @@ RCL_AddRefclock(RefclockParameters *params)
|
||||
inst->poll = params->poll;
|
||||
inst->driver_polled = 0;
|
||||
inst->leap_status = LEAP_Normal;
|
||||
inst->local = params->local;
|
||||
inst->pps_forced = params->pps_forced;
|
||||
inst->pps_rate = params->pps_rate;
|
||||
inst->pps_active = 0;
|
||||
@@ -231,6 +218,13 @@ RCL_AddRefclock(RefclockParameters *params)
|
||||
inst->ref_id = (uint32_t)ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3];
|
||||
}
|
||||
|
||||
if (inst->local) {
|
||||
inst->pps_forced = 1;
|
||||
inst->lock_ref = inst->ref_id;
|
||||
inst->leap_status = LEAP_Unsynchronised;
|
||||
inst->max_lock_age = MAX(inst->max_lock_age, 3);
|
||||
}
|
||||
|
||||
if (inst->driver->poll) {
|
||||
int max_samples;
|
||||
|
||||
@@ -300,7 +294,7 @@ RCL_StartRefclocks(void)
|
||||
break;
|
||||
}
|
||||
|
||||
if (lock_index == -1 || lock_index == i)
|
||||
if (lock_index == -1 || (lock_index == i && !inst->local))
|
||||
LOG(LOGS_WARN, "Invalid lock refid %s", UTI_RefidToString(inst->lock_ref));
|
||||
}
|
||||
|
||||
@@ -440,20 +434,24 @@ accumulate_sample(RCL_Instance instance, struct timespec *sample_time, double of
|
||||
}
|
||||
|
||||
int
|
||||
RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset, int leap)
|
||||
RCL_AddSample(RCL_Instance instance, struct timespec *sample_time,
|
||||
struct timespec *ref_time, int leap)
|
||||
{
|
||||
double correction, dispersion;
|
||||
double correction, dispersion, raw_offset, offset;
|
||||
struct timespec cooked_time;
|
||||
|
||||
if (instance->pps_forced)
|
||||
return RCL_AddPulse(instance, sample_time, -offset);
|
||||
return RCL_AddPulse(instance, sample_time,
|
||||
1.0e-9 * (sample_time->tv_nsec - ref_time->tv_nsec));
|
||||
|
||||
raw_offset = UTI_DiffTimespecsToDouble(ref_time, sample_time);
|
||||
|
||||
LCL_GetOffsetCorrection(sample_time, &correction, &dispersion);
|
||||
UTI_AddDoubleToTimespec(sample_time, correction, &cooked_time);
|
||||
dispersion += instance->precision;
|
||||
|
||||
/* Make sure the timestamp and offset provided by the driver are sane */
|
||||
if (!UTI_IsTimeOffsetSane(sample_time, offset) ||
|
||||
if (!UTI_IsTimeOffsetSane(sample_time, raw_offset) ||
|
||||
!valid_sample_time(instance, &cooked_time))
|
||||
return 0;
|
||||
|
||||
@@ -468,18 +466,24 @@ RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Calculate offset = raw_offset - correction + instance->offset
|
||||
in parts to avoid loss of precision if there are large differences */
|
||||
offset = ref_time->tv_sec - sample_time->tv_sec -
|
||||
(time_t)correction + (time_t)instance->offset;
|
||||
offset += 1.0e-9 * (ref_time->tv_nsec - sample_time->tv_nsec) -
|
||||
(correction - (time_t)correction) + (instance->offset - (time_t)instance->offset);
|
||||
|
||||
if (instance->tai && !convert_tai_offset(sample_time, &offset)) {
|
||||
DEBUG_LOG("refclock sample ignored unknown TAI offset");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!accumulate_sample(instance, &cooked_time,
|
||||
offset - correction + instance->offset, dispersion))
|
||||
if (!accumulate_sample(instance, &cooked_time, offset, dispersion))
|
||||
return 0;
|
||||
|
||||
instance->pps_active = 0;
|
||||
|
||||
log_sample(instance, &cooked_time, 0, 0, offset, offset - correction + instance->offset, dispersion);
|
||||
log_sample(instance, &cooked_time, 0, 0, raw_offset, offset, dispersion);
|
||||
|
||||
/* for logging purposes */
|
||||
if (!instance->driver->poll)
|
||||
@@ -558,16 +562,30 @@ RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
|
||||
lock_refclock = get_refclock(instance->lock_ref);
|
||||
|
||||
if (!SPF_GetLastSample(lock_refclock->filter, &ref_sample)) {
|
||||
DEBUG_LOG("refclock pulse ignored no ref sample");
|
||||
return 0;
|
||||
if (instance->local) {
|
||||
/* Make the first sample in order to lock to itself */
|
||||
ref_sample.time = *cooked_time;
|
||||
ref_sample.offset = offset;
|
||||
ref_sample.peer_delay = ref_sample.peer_dispersion = 0;
|
||||
ref_sample.root_delay = ref_sample.root_dispersion = 0;
|
||||
} else {
|
||||
DEBUG_LOG("refclock pulse ignored no ref sample");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
ref_sample.root_dispersion += SPF_GetAvgSampleDispersion(lock_refclock->filter);
|
||||
|
||||
sample_diff = UTI_DiffTimespecsToDouble(cooked_time, &ref_sample.time);
|
||||
if (fabs(sample_diff) >= (double)instance->max_lock_age / rate) {
|
||||
DEBUG_LOG("refclock pulse ignored samplediff=%.9f",
|
||||
sample_diff);
|
||||
DEBUG_LOG("refclock pulse ignored samplediff=%.9f", sample_diff);
|
||||
|
||||
/* Restart the local mode */
|
||||
if (instance->local) {
|
||||
LOG(LOGS_WARN, "Local refclock lost lock");
|
||||
SPF_DropSamples(instance->filter);
|
||||
SRC_ResetInstance(instance->source);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -693,6 +711,52 @@ pps_stratum(RCL_Instance instance, struct timespec *ts)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
get_local_stats(RCL_Instance inst, struct timespec *ref, double *freq, double *offset)
|
||||
{
|
||||
double offset_sd, freq_sd, skew, root_delay, root_disp;
|
||||
SST_Stats stats = SRC_GetSourcestats(inst->source);
|
||||
|
||||
if (SST_Samples(stats) < SST_GetMinSamples(stats)) {
|
||||
UTI_ZeroTimespec(ref);
|
||||
return;
|
||||
}
|
||||
|
||||
SST_GetTrackingData(stats, ref, offset, &offset_sd, freq, &freq_sd,
|
||||
&skew, &root_delay, &root_disp);
|
||||
}
|
||||
|
||||
static void
|
||||
follow_local(RCL_Instance inst, struct timespec *prev_ref_time, double prev_freq,
|
||||
double prev_offset)
|
||||
{
|
||||
SST_Stats stats = SRC_GetSourcestats(inst->source);
|
||||
double freq, dfreq, offset, doffset, elapsed;
|
||||
struct timespec now, ref_time;
|
||||
|
||||
get_local_stats(inst, &ref_time, &freq, &offset);
|
||||
|
||||
if (UTI_IsZeroTimespec(prev_ref_time) || UTI_IsZeroTimespec(&ref_time))
|
||||
return;
|
||||
|
||||
dfreq = (freq - prev_freq) / (1.0 - prev_freq);
|
||||
elapsed = UTI_DiffTimespecsToDouble(&ref_time, prev_ref_time);
|
||||
doffset = offset - elapsed * prev_freq - prev_offset;
|
||||
|
||||
if (!REF_AdjustReference(doffset, dfreq))
|
||||
return;
|
||||
|
||||
LCL_ReadCookedTime(&now, NULL);
|
||||
SST_SlewSamples(stats, &now, dfreq, doffset);
|
||||
SPF_SlewSamples(inst->filter, &now, dfreq, doffset);
|
||||
|
||||
/* Keep the offset close to zero to not lose precision */
|
||||
if (fabs(offset) >= 1.0) {
|
||||
SST_CorrectOffset(stats, -round(offset));
|
||||
SPF_CorrectOffset(inst->filter, -round(offset));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
poll_timeout(void *arg)
|
||||
{
|
||||
@@ -713,17 +777,28 @@ poll_timeout(void *arg)
|
||||
inst->driver_polled = 0;
|
||||
|
||||
if (SPF_GetFilteredSample(inst->filter, &sample)) {
|
||||
double local_freq, local_offset;
|
||||
struct timespec local_ref_time;
|
||||
|
||||
/* Handle special case when PPS is used with the local reference */
|
||||
if (inst->pps_active && inst->lock_ref == -1)
|
||||
stratum = pps_stratum(inst, &sample.time);
|
||||
else
|
||||
stratum = inst->stratum;
|
||||
|
||||
if (inst->local) {
|
||||
get_local_stats(inst, &local_ref_time, &local_freq, &local_offset);
|
||||
inst->leap_status = LEAP_Unsynchronised;
|
||||
}
|
||||
|
||||
SRC_UpdateReachability(inst->source, 1);
|
||||
SRC_UpdateStatus(inst->source, stratum, inst->leap_status);
|
||||
SRC_AccumulateSample(inst->source, &sample);
|
||||
SRC_SelectSource(inst->source);
|
||||
|
||||
if (inst->local)
|
||||
follow_local(inst, &local_ref_time, local_freq, local_offset);
|
||||
|
||||
log_sample(inst, &sample.time, 1, 0, 0.0, sample.offset, sample.peer_dispersion);
|
||||
} else {
|
||||
SRC_UpdateReachability(inst->source, 0);
|
||||
|
||||
@@ -37,6 +37,7 @@ typedef struct {
|
||||
int driver_poll;
|
||||
int poll;
|
||||
int filter_length;
|
||||
int local;
|
||||
int pps_forced;
|
||||
int pps_rate;
|
||||
int min_samples;
|
||||
@@ -74,7 +75,8 @@ extern void *RCL_GetDriverData(RCL_Instance instance);
|
||||
extern char *RCL_GetDriverParameter(RCL_Instance instance);
|
||||
extern void RCL_CheckDriverOptions(RCL_Instance instance, const char **options);
|
||||
extern char *RCL_GetDriverOption(RCL_Instance instance, char *name);
|
||||
extern int RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset, int leap);
|
||||
extern int RCL_AddSample(RCL_Instance instance, struct timespec *sample_time,
|
||||
struct timespec *ref_time, int leap);
|
||||
extern int RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second);
|
||||
extern int RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
|
||||
double second, double dispersion, double raw_correction);
|
||||
|
||||
@@ -75,13 +75,15 @@ static int phc_initialise(RCL_Instance instance)
|
||||
phc->nocrossts = RCL_GetDriverOption(instance, "nocrossts") ? 1 : 0;
|
||||
phc->extpps = RCL_GetDriverOption(instance, "extpps") ? 1 : 0;
|
||||
|
||||
phc->clock = HCL_CreateInstance(0, 16, UTI_Log2ToDouble(RCL_GetDriverPoll(instance)),
|
||||
RCL_GetPrecision(instance));
|
||||
|
||||
if (phc->extpps) {
|
||||
s = RCL_GetDriverOption(instance, "pin");
|
||||
phc->pin = s ? atoi(s) : 0;
|
||||
s = RCL_GetDriverOption(instance, "channel");
|
||||
phc->channel = s ? atoi(s) : 0;
|
||||
rising_edge = RCL_GetDriverOption(instance, "clear") ? 0 : 1;
|
||||
phc->clock = HCL_CreateInstance(0, 16, UTI_Log2ToDouble(RCL_GetDriverPoll(instance)));
|
||||
|
||||
if (!SYS_Linux_SetPHCExtTimestamping(phc->fd, phc->pin, phc->channel,
|
||||
rising_edge, !rising_edge, 1))
|
||||
@@ -90,7 +92,6 @@ static int phc_initialise(RCL_Instance instance)
|
||||
SCH_AddFileHandler(phc->fd, SCH_FILE_INPUT, read_ext_pulse, instance);
|
||||
} else {
|
||||
phc->pin = phc->channel = 0;
|
||||
phc->clock = NULL;
|
||||
}
|
||||
|
||||
RCL_SetDriverData(instance, phc);
|
||||
@@ -106,9 +107,9 @@ static void phc_finalise(RCL_Instance instance)
|
||||
if (phc->extpps) {
|
||||
SCH_RemoveFileHandler(phc->fd);
|
||||
SYS_Linux_SetPHCExtTimestamping(phc->fd, phc->pin, phc->channel, 0, 0, 0);
|
||||
HCL_DestroyInstance(phc->clock);
|
||||
}
|
||||
|
||||
HCL_DestroyInstance(phc->clock);
|
||||
close(phc->fd);
|
||||
Free(phc);
|
||||
}
|
||||
@@ -139,29 +140,35 @@ static void read_ext_pulse(int fd, int event, void *anything)
|
||||
UTI_DiffTimespecsToDouble(&phc_ts, &local_ts));
|
||||
}
|
||||
|
||||
#define PHC_READINGS 25
|
||||
|
||||
static int phc_poll(RCL_Instance instance)
|
||||
{
|
||||
struct timespec phc_ts, sys_ts, local_ts, readings[PHC_READINGS][3];
|
||||
struct phc_instance *phc;
|
||||
struct timespec phc_ts, sys_ts, local_ts;
|
||||
double offset, phc_err, local_err;
|
||||
double phc_err, local_err;
|
||||
int n_readings;
|
||||
|
||||
phc = (struct phc_instance *)RCL_GetDriverData(instance);
|
||||
|
||||
if (!SYS_Linux_GetPHCSample(phc->fd, phc->nocrossts, RCL_GetPrecision(instance),
|
||||
&phc->mode, &phc_ts, &sys_ts, &phc_err))
|
||||
n_readings = SYS_Linux_GetPHCReadings(phc->fd, phc->nocrossts, &phc->mode,
|
||||
PHC_READINGS, readings);
|
||||
if (n_readings < 1)
|
||||
return 0;
|
||||
|
||||
if (phc->extpps) {
|
||||
LCL_CookTime(&sys_ts, &local_ts, &local_err);
|
||||
HCL_AccumulateSample(phc->clock, &phc_ts, &local_ts, phc_err + local_err);
|
||||
if (!HCL_ProcessReadings(phc->clock, n_readings, readings, &phc_ts, &sys_ts, &phc_err))
|
||||
return 0;
|
||||
}
|
||||
|
||||
offset = UTI_DiffTimespecsToDouble(&phc_ts, &sys_ts);
|
||||
LCL_CookTime(&sys_ts, &local_ts, &local_err);
|
||||
HCL_AccumulateSample(phc->clock, &phc_ts, &local_ts, phc_err + local_err);
|
||||
|
||||
DEBUG_LOG("PHC offset: %+.9f err: %.9f", offset, phc_err);
|
||||
if (phc->extpps)
|
||||
return 0;
|
||||
|
||||
return RCL_AddSample(instance, &sys_ts, offset, LEAP_Normal);
|
||||
DEBUG_LOG("PHC offset: %+.9f err: %.9f",
|
||||
UTI_DiffTimespecsToDouble(&phc_ts, &sys_ts), phc_err);
|
||||
|
||||
return RCL_AddSample(instance, &sys_ts, &phc_ts, LEAP_Normal);
|
||||
}
|
||||
|
||||
RefclockDriver RCL_PHC_driver = {
|
||||
|
||||
@@ -95,7 +95,6 @@ static int shm_poll(RCL_Instance instance)
|
||||
{
|
||||
struct timespec receive_ts, clock_ts;
|
||||
struct shmTime t, *shm;
|
||||
double offset;
|
||||
|
||||
shm = (struct shmTime *)RCL_GetDriverData(instance);
|
||||
|
||||
@@ -124,9 +123,8 @@ static int shm_poll(RCL_Instance instance)
|
||||
|
||||
UTI_NormaliseTimespec(&clock_ts);
|
||||
UTI_NormaliseTimespec(&receive_ts);
|
||||
offset = UTI_DiffTimespecsToDouble(&clock_ts, &receive_ts);
|
||||
|
||||
return RCL_AddSample(instance, &receive_ts, offset, t.leap);
|
||||
return RCL_AddSample(instance, &receive_ts, &clock_ts, t.leap);
|
||||
}
|
||||
|
||||
RefclockDriver RCL_SHM_driver = {
|
||||
|
||||
@@ -60,8 +60,8 @@ struct sock_sample {
|
||||
|
||||
static void read_sample(int sockfd, int event, void *anything)
|
||||
{
|
||||
struct timespec sys_ts, ref_ts;
|
||||
struct sock_sample sample;
|
||||
struct timespec ts;
|
||||
RCL_Instance instance;
|
||||
int s;
|
||||
|
||||
@@ -86,13 +86,18 @@ static void read_sample(int sockfd, int event, void *anything)
|
||||
return;
|
||||
}
|
||||
|
||||
UTI_TimevalToTimespec(&sample.tv, &ts);
|
||||
UTI_NormaliseTimespec(&ts);
|
||||
UTI_TimevalToTimespec(&sample.tv, &sys_ts);
|
||||
UTI_NormaliseTimespec(&sys_ts);
|
||||
|
||||
if (!UTI_IsTimeOffsetSane(&sys_ts, sample.offset))
|
||||
return;
|
||||
|
||||
UTI_AddDoubleToTimespec(&sys_ts, sample.offset, &ref_ts);
|
||||
|
||||
if (sample.pulse) {
|
||||
RCL_AddPulse(instance, &ts, sample.offset);
|
||||
RCL_AddPulse(instance, &sys_ts, sample.offset);
|
||||
} else {
|
||||
RCL_AddSample(instance, &ts, sample.offset, sample.leap);
|
||||
RCL_AddSample(instance, &sys_ts, &ref_ts, sample.leap);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
69
reference.c
69
reference.c
@@ -3,7 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2009-2018, 2020
|
||||
* Copyright (C) Miroslav Lichvar 2009-2018, 2020, 2022
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -150,6 +150,9 @@ static SCH_TimeoutID fb_drift_timeout_id;
|
||||
static double last_ref_update;
|
||||
static double last_ref_update_interval;
|
||||
|
||||
static double last_ref_adjustment;
|
||||
static int ref_adjustments;
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static NTP_Leap get_tz_leap(time_t when, int *tai_offset);
|
||||
@@ -286,6 +289,8 @@ REF_Initialise(void)
|
||||
UTI_ZeroTimespec(&our_ref_time);
|
||||
last_ref_update = 0.0;
|
||||
last_ref_update_interval = 0.0;
|
||||
last_ref_adjustment = 0.0;
|
||||
ref_adjustments = 0;
|
||||
|
||||
LCL_AddParameterChangeHandler(handle_slew, NULL);
|
||||
|
||||
@@ -960,6 +965,27 @@ fuzz_ref_time(struct timespec *ts)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static double
|
||||
get_correction_rate(double offset_sd, double update_interval)
|
||||
{
|
||||
/* We want to correct the offset quickly, but we also want to keep the
|
||||
frequency error caused by the correction itself low.
|
||||
|
||||
Define correction rate as the area of the region bounded by the graph of
|
||||
offset corrected in time. Set the rate so that the time needed to correct
|
||||
an offset equal to the current sourcestats stddev will be equal to the
|
||||
update interval multiplied by the correction time ratio (assuming linear
|
||||
adjustment). The offset and the time needed to make the correction are
|
||||
inversely proportional.
|
||||
|
||||
This is only a suggestion and it's up to the system driver how the
|
||||
adjustment will be executed. */
|
||||
|
||||
return correction_time_ratio * 0.5 * offset_sd * update_interval;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
|
||||
uint32_t ref_id, IPAddr *ref_ip, struct timespec *ref_time,
|
||||
@@ -969,7 +995,7 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
|
||||
{
|
||||
double uncorrected_offset, accumulate_offset, step_offset;
|
||||
double residual_frequency, local_abs_frequency;
|
||||
double elapsed, mono_now, update_interval, correction_rate, orig_root_distance;
|
||||
double elapsed, mono_now, update_interval, orig_root_distance;
|
||||
struct timespec now, raw_now;
|
||||
int manual;
|
||||
|
||||
@@ -1024,21 +1050,6 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
|
||||
last_ref_update_interval = update_interval;
|
||||
last_offset = offset;
|
||||
|
||||
/* We want to correct the offset quickly, but we also want to keep the
|
||||
frequency error caused by the correction itself low.
|
||||
|
||||
Define correction rate as the area of the region bounded by the graph of
|
||||
offset corrected in time. Set the rate so that the time needed to correct
|
||||
an offset equal to the current sourcestats stddev will be equal to the
|
||||
update interval multiplied by the correction time ratio (assuming linear
|
||||
adjustment). The offset and the time needed to make the correction are
|
||||
inversely proportional.
|
||||
|
||||
This is only a suggestion and it's up to the system driver how the
|
||||
adjustment will be executed. */
|
||||
|
||||
correction_rate = correction_time_ratio * 0.5 * offset_sd * update_interval;
|
||||
|
||||
/* Check if the clock should be stepped */
|
||||
if (is_step_limit_reached(offset, uncorrected_offset)) {
|
||||
/* Cancel the uncorrected offset and correct the total offset by step */
|
||||
@@ -1050,7 +1061,8 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
|
||||
}
|
||||
|
||||
/* Adjust the clock */
|
||||
LCL_AccumulateFrequencyAndOffset(frequency, accumulate_offset, correction_rate);
|
||||
LCL_AccumulateFrequencyAndOffset(frequency, accumulate_offset,
|
||||
get_correction_rate(offset_sd, update_interval));
|
||||
|
||||
maybe_log_offset(offset, raw_now.tv_sec);
|
||||
|
||||
@@ -1095,6 +1107,27 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
|
||||
avg2_moving = 1;
|
||||
avg2_offset = SQUARE(offset);
|
||||
}
|
||||
|
||||
ref_adjustments = 0;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
REF_AdjustReference(double offset, double frequency)
|
||||
{
|
||||
double adj_corr_rate, ref_corr_rate, mono_now;
|
||||
|
||||
mono_now = SCH_GetLastEventMonoTime();
|
||||
ref_adjustments++;
|
||||
|
||||
adj_corr_rate = get_correction_rate(fabs(offset), mono_now - last_ref_adjustment);
|
||||
ref_corr_rate = get_correction_rate(our_offset_sd, last_ref_update_interval) /
|
||||
ref_adjustments;
|
||||
last_ref_adjustment = mono_now;
|
||||
|
||||
return LCL_AccumulateFrequencyAndOffsetNoHandlers(frequency, offset,
|
||||
MAX(adj_corr_rate, ref_corr_rate));
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
@@ -162,6 +162,10 @@ extern void REF_SetManualReference
|
||||
extern void
|
||||
REF_SetUnsynchronised(void);
|
||||
|
||||
/* Make a small correction of the clock without updating the reference
|
||||
parameters and calling the clock change handlers */
|
||||
extern int REF_AdjustReference(double offset, double frequency);
|
||||
|
||||
/* Announce a leap second before the full reference update */
|
||||
extern void REF_UpdateLeapStatus(NTP_Leap leap);
|
||||
|
||||
|
||||
@@ -174,6 +174,7 @@ typedef struct {
|
||||
uint32_t total_tx_count;
|
||||
uint32_t total_rx_count;
|
||||
uint32_t total_valid_count;
|
||||
uint32_t total_good_count;
|
||||
} RPT_NTPReport;
|
||||
|
||||
typedef struct {
|
||||
|
||||
72
samplefilt.c
72
samplefilt.c
@@ -162,6 +162,14 @@ SPF_GetNumberOfSamples(SPF_Instance filter)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
SPF_GetMaxSamples(SPF_Instance filter)
|
||||
{
|
||||
return filter->max_samples;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
double
|
||||
SPF_GetAvgSampleDispersion(SPF_Instance filter)
|
||||
{
|
||||
@@ -170,11 +178,21 @@ SPF_GetAvgSampleDispersion(SPF_Instance filter)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
SPF_DropSamples(SPF_Instance filter)
|
||||
static void
|
||||
drop_samples(SPF_Instance filter, int keep_last)
|
||||
{
|
||||
filter->index = -1;
|
||||
filter->used = 0;
|
||||
if (!keep_last)
|
||||
filter->last = -1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
SPF_DropSamples(SPF_Instance filter)
|
||||
{
|
||||
drop_samples(filter, 0);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -399,17 +417,40 @@ SPF_GetFilteredSample(SPF_Instance filter, NTP_Sample *sample)
|
||||
|
||||
n = select_samples(filter);
|
||||
|
||||
DEBUG_LOG("selected %d from %d samples", n, filter->used);
|
||||
|
||||
if (n < 1)
|
||||
return 0;
|
||||
|
||||
if (!combine_selected_samples(filter, n, sample))
|
||||
return 0;
|
||||
|
||||
SPF_DropSamples(filter);
|
||||
drop_samples(filter, 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
get_first_last(SPF_Instance filter, int *first, int *last)
|
||||
{
|
||||
if (filter->last < 0)
|
||||
return 0;
|
||||
|
||||
/* Always slew the last sample as it may be returned even if no new
|
||||
samples were accumulated */
|
||||
if (filter->used > 0) {
|
||||
*first = 0;
|
||||
*last = filter->used - 1;
|
||||
} else {
|
||||
*first = *last = filter->last;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
@@ -418,18 +459,9 @@ SPF_SlewSamples(SPF_Instance filter, struct timespec *when, double dfreq, double
|
||||
int i, first, last;
|
||||
double delta_time;
|
||||
|
||||
if (filter->last < 0)
|
||||
if (!get_first_last(filter, &first, &last))
|
||||
return;
|
||||
|
||||
/* Always slew the last sample as it may be returned even if no new
|
||||
samples were accumulated */
|
||||
if (filter->used > 0) {
|
||||
first = 0;
|
||||
last = filter->used - 1;
|
||||
} else {
|
||||
first = last = filter->last;
|
||||
}
|
||||
|
||||
for (i = first; i <= last; i++) {
|
||||
UTI_AdjustTimespec(&filter->samples[i].time, when, &filter->samples[i].time,
|
||||
&delta_time, dfreq, doffset);
|
||||
@@ -439,6 +471,20 @@ SPF_SlewSamples(SPF_Instance filter, struct timespec *when, double dfreq, double
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
SPF_CorrectOffset(SPF_Instance filter, double doffset)
|
||||
{
|
||||
int i, first, last;
|
||||
|
||||
if (!get_first_last(filter, &first, &last))
|
||||
return;
|
||||
|
||||
for (i = first; i <= last; i++)
|
||||
filter->samples[i].offset -= doffset;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
SPF_AddDispersion(SPF_Instance filter, double dispersion)
|
||||
{
|
||||
|
||||
@@ -39,11 +39,13 @@ extern void SPF_DestroyInstance(SPF_Instance filter);
|
||||
extern int SPF_AccumulateSample(SPF_Instance filter, NTP_Sample *sample);
|
||||
extern int SPF_GetLastSample(SPF_Instance filter, NTP_Sample *sample);
|
||||
extern int SPF_GetNumberOfSamples(SPF_Instance filter);
|
||||
extern int SPF_GetMaxSamples(SPF_Instance filter);
|
||||
extern double SPF_GetAvgSampleDispersion(SPF_Instance filter);
|
||||
extern void SPF_DropSamples(SPF_Instance filter);
|
||||
extern int SPF_GetFilteredSample(SPF_Instance filter, NTP_Sample *sample);
|
||||
extern void SPF_SlewSamples(SPF_Instance filter, struct timespec *when,
|
||||
double dfreq, double doffset);
|
||||
extern void SPF_CorrectOffset(SPF_Instance filter, double doffset);
|
||||
extern void SPF_AddDispersion(SPF_Instance filter, double dispersion);
|
||||
|
||||
#endif
|
||||
|
||||
18
siv_gnutls.c
18
siv_gnutls.c
@@ -165,17 +165,29 @@ SIV_SetKey(SIV_Instance instance, const unsigned char *key, int length)
|
||||
datum.data = (unsigned char *)key;
|
||||
datum.size = length;
|
||||
|
||||
/* Initialise a new cipher with the provided key (gnutls does not seem to
|
||||
have a function to change the key directly) */
|
||||
#ifdef HAVE_GNUTLS_AEAD_CIPHER_SET_KEY
|
||||
if (instance->cipher) {
|
||||
r = gnutls_aead_cipher_set_key(instance->cipher, &datum);
|
||||
if (r < 0) {
|
||||
DEBUG_LOG("Could not set cipher key : %s", gnutls_strerror(r));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Initialise a new cipher with the provided key */
|
||||
r = gnutls_aead_cipher_init(&cipher, instance->algorithm, &datum);
|
||||
if (r < 0) {
|
||||
DEBUG_LOG("Could not initialise %s : %s", "cipher", gnutls_strerror(r));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Replace the previous cipher */
|
||||
/* Destroy the previous cipher (if its key could not be changed directly) */
|
||||
if (instance->cipher)
|
||||
gnutls_aead_cipher_deinit(instance->cipher);
|
||||
|
||||
instance->cipher = cipher;
|
||||
|
||||
return 1;
|
||||
|
||||
4
socket.c
4
socket.c
@@ -505,6 +505,8 @@ bind_unix_address(int sock_fd, const char *addr, int flags)
|
||||
{
|
||||
union sockaddr_all saddr;
|
||||
|
||||
memset(&saddr, 0, sizeof (saddr));
|
||||
|
||||
if (snprintf(saddr.un.sun_path, sizeof (saddr.un.sun_path), "%s", addr) >=
|
||||
sizeof (saddr.un.sun_path)) {
|
||||
DEBUG_LOG("Unix socket path %s too long", addr);
|
||||
@@ -537,6 +539,8 @@ connect_unix_address(int sock_fd, const char *addr)
|
||||
{
|
||||
union sockaddr_all saddr;
|
||||
|
||||
memset(&saddr, 0, sizeof (saddr));
|
||||
|
||||
if (snprintf(saddr.un.sun_path, sizeof (saddr.un.sun_path), "%s", addr) >=
|
||||
sizeof (saddr.un.sun_path)) {
|
||||
DEBUG_LOG("Unix socket path %s too long", addr);
|
||||
|
||||
48
sources.c
48
sources.c
@@ -3,7 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2011-2016, 2018, 2020
|
||||
* Copyright (C) Miroslav Lichvar 2011-2016, 2018, 2020-2021
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -68,6 +68,7 @@ struct SelectInfo {
|
||||
typedef enum {
|
||||
SRC_OK, /* OK so far, not a final status! */
|
||||
SRC_UNSELECTABLE, /* Has noselect option set */
|
||||
SRC_UNSYNCHRONISED, /* Provides samples but not unsynchronised */
|
||||
SRC_BAD_STATS, /* Doesn't have valid stats data */
|
||||
SRC_BAD_DISTANCE, /* Has root distance longer than allowed maximum */
|
||||
SRC_JITTERY, /* Had std dev larger than allowed maximum */
|
||||
@@ -177,6 +178,8 @@ static double reselect_distance;
|
||||
static double stratum_weight;
|
||||
static double combine_limit;
|
||||
|
||||
static LOG_FileID logfileid;
|
||||
|
||||
/* Identifier of the dump file */
|
||||
#define DUMP_IDENTIFIER "SRC0\n"
|
||||
|
||||
@@ -188,6 +191,7 @@ static void slew_sources(struct timespec *raw, struct timespec *cooked, double d
|
||||
double doffset, LCL_ChangeType change_type, void *anything);
|
||||
static void add_dispersion(double dispersion, void *anything);
|
||||
static char *source_to_string(SRC_Instance inst);
|
||||
static char get_status_char(SRC_Status status);
|
||||
|
||||
/* ================================================== */
|
||||
/* Initialisation function */
|
||||
@@ -207,6 +211,10 @@ void SRC_Initialise(void) {
|
||||
|
||||
LCL_AddParameterChangeHandler(slew_sources, NULL);
|
||||
LCL_AddDispersionNotifyHandler(add_dispersion, NULL);
|
||||
|
||||
logfileid = CNF_GetLogSelection() ? LOG_FileOpen("selection",
|
||||
" Date (UTC) Time IP Address S EOpts Reach Score Last sample Low end High end")
|
||||
: -1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -639,13 +647,33 @@ source_to_string(SRC_Instance inst)
|
||||
static void
|
||||
mark_source(SRC_Instance inst, SRC_Status status)
|
||||
{
|
||||
struct timespec now;
|
||||
|
||||
inst->status = status;
|
||||
|
||||
DEBUG_LOG("%s status=%d options=%x reach=%o/%d updates=%d distant=%d leap=%d vote=%d lo=%f hi=%f",
|
||||
source_to_string(inst), (int)inst->status, (unsigned int)inst->sel_options,
|
||||
(unsigned int)inst->reachability, inst->reachability_size, inst->updates,
|
||||
DEBUG_LOG("%s status=%c options=%x reach=%o/%d updates=%d distant=%d leap=%d vote=%d lo=%f hi=%f",
|
||||
source_to_string(inst), get_status_char(inst->status),
|
||||
(unsigned int)inst->sel_options, (unsigned int)inst->reachability,
|
||||
inst->reachability_size, inst->updates,
|
||||
inst->distant, (int)inst->leap, inst->leap_vote,
|
||||
inst->sel_info.lo_limit, inst->sel_info.hi_limit);
|
||||
|
||||
if (logfileid == -1)
|
||||
return;
|
||||
|
||||
SCH_GetLastEventTime(&now, NULL, NULL);
|
||||
|
||||
LOG_FileWrite(logfileid,
|
||||
"%s %-15s %c -%c%c%c%c %4o %5.2f %10.3e %10.3e %10.3e",
|
||||
UTI_TimeToLogForm(now.tv_sec), source_to_string(inst),
|
||||
get_status_char(inst->status),
|
||||
inst->sel_options & SRC_SELECT_NOSELECT ? 'N' : '-',
|
||||
inst->sel_options & SRC_SELECT_PREFER ? 'P' : '-',
|
||||
inst->sel_options & SRC_SELECT_TRUST ? 'T' : '-',
|
||||
inst->sel_options & SRC_SELECT_REQUIRE ? 'R' : '-',
|
||||
(unsigned int)inst->reachability, inst->sel_score,
|
||||
inst->sel_info.last_sample_ago,
|
||||
inst->sel_info.lo_limit, inst->sel_info.hi_limit);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -722,8 +750,8 @@ combine_sources(int n_sel_sources, struct timespec *ref_time, double *offset,
|
||||
offset_weight = 1.0 / sources[index]->sel_info.root_distance;
|
||||
frequency_weight = 1.0 / SQUARE(src_frequency_sd);
|
||||
|
||||
DEBUG_LOG("combining index=%d oweight=%e offset=%e osd=%e fweight=%e freq=%e fsd=%e skew=%e",
|
||||
index, offset_weight, src_offset, src_offset_sd,
|
||||
DEBUG_LOG("combining %s oweight=%e offset=%e osd=%e fweight=%e freq=%e fsd=%e skew=%e",
|
||||
source_to_string(sources[index]), offset_weight, src_offset, src_offset_sd,
|
||||
frequency_weight, src_frequency, src_frequency_sd, src_skew);
|
||||
|
||||
sum_offset_weight += offset_weight;
|
||||
@@ -815,6 +843,12 @@ SRC_SelectSource(SRC_Instance updated_inst)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Ignore sources which are not synchronised */
|
||||
if (sources[i]->leap == LEAP_Unsynchronised) {
|
||||
mark_source(sources[i], SRC_UNSYNCHRONISED);
|
||||
continue;
|
||||
}
|
||||
|
||||
si = &sources[i]->sel_info;
|
||||
SST_GetSelectionData(sources[i]->stats, &now,
|
||||
&si->lo_limit, &si->hi_limit, &si->root_distance,
|
||||
@@ -1642,6 +1676,8 @@ get_status_char(SRC_Status status)
|
||||
switch (status) {
|
||||
case SRC_UNSELECTABLE:
|
||||
return 'N';
|
||||
case SRC_UNSYNCHRONISED:
|
||||
return 's';
|
||||
case SRC_BAD_STATS:
|
||||
return 'M';
|
||||
case SRC_BAD_DISTANCE:
|
||||
|
||||
@@ -211,8 +211,8 @@ SST_CreateInstance(uint32_t refid, IPAddr *addr, int min_samples, int max_sample
|
||||
SST_Stats inst;
|
||||
inst = MallocNew(struct SST_Stats_Record);
|
||||
|
||||
inst->min_samples = min_samples;
|
||||
inst->max_samples = max_samples;
|
||||
inst->max_samples = max_samples > 0 ? CLAMP(1, max_samples, MAX_SAMPLES) : MAX_SAMPLES;
|
||||
inst->min_samples = CLAMP(1, min_samples, inst->max_samples);
|
||||
inst->fixed_min_delay = min_delay;
|
||||
inst->fixed_asymmetry = asymmetry;
|
||||
|
||||
@@ -698,7 +698,8 @@ SST_GetSelectionData(SST_Stats inst, struct timespec *now,
|
||||
|
||||
/* If maxsamples is too small to have a successful regression, enable the
|
||||
selection as a special case for a fast update/print-once reference mode */
|
||||
if (!*select_ok && inst->n_samples < 3 && inst->n_samples == inst->max_samples) {
|
||||
if (!*select_ok && inst->n_samples < MIN_SAMPLES_FOR_REGRESS &&
|
||||
inst->n_samples == inst->max_samples) {
|
||||
*std_dev = CNF_GetMaxJitter();
|
||||
*select_ok = 1;
|
||||
}
|
||||
@@ -814,7 +815,7 @@ SST_PredictOffset(SST_Stats inst, struct timespec *when)
|
||||
{
|
||||
double elapsed;
|
||||
|
||||
if (inst->n_samples < 3) {
|
||||
if (inst->n_samples < MIN_SAMPLES_FOR_REGRESS) {
|
||||
/* We don't have any useful statistics, and presumably the poll
|
||||
interval is minimal. We can't do any useful prediction other
|
||||
than use the latest sample or zero if we don't have any samples */
|
||||
@@ -930,6 +931,7 @@ SST_LoadFromFile(SST_Stats inst, FILE *in)
|
||||
|
||||
/* Make sure the samples are sane and they are in order */
|
||||
if (!UTI_IsTimeOffsetSane(&inst->sample_times[i], -inst->offsets[i]) ||
|
||||
UTI_CompareTimespecs(&now, &inst->sample_times[i]) < 0 ||
|
||||
!(fabs(inst->peer_delays[i]) < 1.0e6 && fabs(inst->peer_dispersions[i]) < 1.0e6 &&
|
||||
fabs(inst->root_delays[i]) < 1.0e6 && fabs(inst->root_dispersions[i]) < 1.0e6) ||
|
||||
(i > 0 && UTI_CompareTimespecs(&inst->sample_times[i],
|
||||
@@ -985,6 +987,14 @@ SST_Samples(SST_Stats inst)
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
SST_GetMinSamples(SST_Stats inst)
|
||||
{
|
||||
return inst->min_samples;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
SST_DoSourcestatsReport(SST_Stats inst, RPT_SourcestatsReport *report, struct timespec *now)
|
||||
{
|
||||
|
||||
@@ -133,6 +133,8 @@ extern void SST_DoSourcestatsReport(SST_Stats inst, RPT_SourcestatsReport *repor
|
||||
|
||||
extern int SST_Samples(SST_Stats inst);
|
||||
|
||||
extern int SST_GetMinSamples(SST_Stats inst);
|
||||
|
||||
extern double SST_GetJitterAsymmetry(SST_Stats inst);
|
||||
|
||||
#endif /* GOT_SOURCESTATS_H */
|
||||
|
||||
@@ -61,6 +61,7 @@ typedef struct {
|
||||
double max_delay;
|
||||
double max_delay_ratio;
|
||||
double max_delay_dev_ratio;
|
||||
double max_delay_quant;
|
||||
double min_delay;
|
||||
double asymmetry;
|
||||
double offset;
|
||||
|
||||
@@ -73,13 +73,28 @@ static double slew_freq;
|
||||
/* Time (raw) of last update of slewing frequency and offset */
|
||||
static struct timespec slew_start;
|
||||
|
||||
/* Limits for the slew timeout */
|
||||
#define MIN_SLEW_TIMEOUT 1.0
|
||||
#define MAX_SLEW_TIMEOUT 1.0e4
|
||||
/* Limits for the slew length */
|
||||
#define MIN_SLEW_DURATION 1.0
|
||||
#define MAX_SLEW_DURATION 1.0e4
|
||||
|
||||
/* Scheduler timeout ID for ending of the currently running slew */
|
||||
static SCH_TimeoutID slew_timeout_id;
|
||||
|
||||
/* Scheduled duration of the currently running slew */
|
||||
static double slew_duration;
|
||||
|
||||
/* Expected delay in ending of the slew due to process scheduling and
|
||||
execution time, tracked as a decaying maximum value */
|
||||
static double slew_excess_duration;
|
||||
|
||||
/* Maximum accepted excess duration to ignore large jumps after resuming
|
||||
suspended system and other reasons (which should be handled in the
|
||||
scheduler), a constant to determine the minimum slew duration to avoid
|
||||
oscillations due to the excess, and the decay constant */
|
||||
#define MAX_SLEW_EXCESS_DURATION 100.0
|
||||
#define MIN_SLEW_DURATION_EXCESS_RATIO 5.0
|
||||
#define SLEW_EXCESS_DURATION_DECAY 0.9
|
||||
|
||||
/* Suggested offset correction rate (correction time * offset) */
|
||||
static double correction_rate;
|
||||
|
||||
@@ -109,12 +124,7 @@ static void
|
||||
handle_step(struct timespec *raw, struct timespec *cooked, double dfreq,
|
||||
double doffset, LCL_ChangeType change_type, void *anything)
|
||||
{
|
||||
if (change_type == LCL_ChangeUnknownStep) {
|
||||
/* Reset offset and slewing */
|
||||
slew_start = *raw;
|
||||
offset_register = 0.0;
|
||||
update_slew();
|
||||
} else if (change_type == LCL_ChangeStep) {
|
||||
if (change_type == LCL_ChangeStep) {
|
||||
UTI_AddDoubleToTimespec(&slew_start, -doffset, &slew_start);
|
||||
}
|
||||
}
|
||||
@@ -169,8 +179,8 @@ clamp_freq(double freq)
|
||||
static void
|
||||
update_slew(void)
|
||||
{
|
||||
double old_slew_freq, total_freq, corr_freq, duration, excess_duration;
|
||||
struct timespec now, end_of_slew;
|
||||
double old_slew_freq, total_freq, corr_freq, duration;
|
||||
|
||||
/* Remove currently running timeout */
|
||||
SCH_RemoveTimeout(slew_timeout_id);
|
||||
@@ -183,13 +193,25 @@ update_slew(void)
|
||||
|
||||
stop_fastslew(&now);
|
||||
|
||||
/* Estimate how long should the next slew take */
|
||||
/* Update the maximum excess duration, decaying even when the slew did
|
||||
not time out (i.e. frequency was set or offset accrued), but add a small
|
||||
value to avoid denormals */
|
||||
slew_excess_duration = (slew_excess_duration + 1.0e-9) * SLEW_EXCESS_DURATION_DECAY;
|
||||
excess_duration = duration - slew_duration;
|
||||
if (slew_excess_duration < excess_duration &&
|
||||
excess_duration <= MAX_SLEW_EXCESS_DURATION)
|
||||
slew_excess_duration = excess_duration;
|
||||
|
||||
/* Calculate the duration of the new slew, considering the current correction
|
||||
rate and previous delays in stopping of the slew */
|
||||
if (fabs(offset_register) < MIN_OFFSET_CORRECTION) {
|
||||
duration = MAX_SLEW_TIMEOUT;
|
||||
duration = MAX_SLEW_DURATION;
|
||||
} else {
|
||||
duration = correction_rate / fabs(offset_register);
|
||||
if (duration < MIN_SLEW_TIMEOUT)
|
||||
duration = MIN_SLEW_TIMEOUT;
|
||||
if (duration < MIN_SLEW_DURATION)
|
||||
duration = MIN_SLEW_DURATION;
|
||||
if (duration < MIN_SLEW_DURATION_EXCESS_RATIO * slew_excess_duration)
|
||||
duration = MIN_SLEW_DURATION_EXCESS_RATIO * slew_excess_duration;
|
||||
}
|
||||
|
||||
/* Get frequency offset needed to slew the offset in the duration
|
||||
@@ -232,23 +254,25 @@ update_slew(void)
|
||||
maximum timeout and try again on the next update. */
|
||||
if (fabs(offset_register) < MIN_OFFSET_CORRECTION ||
|
||||
offset_register * slew_freq <= 0.0) {
|
||||
duration = MAX_SLEW_TIMEOUT;
|
||||
duration = MAX_SLEW_DURATION;
|
||||
} else {
|
||||
duration = offset_register / slew_freq;
|
||||
if (duration < MIN_SLEW_TIMEOUT)
|
||||
duration = MIN_SLEW_TIMEOUT;
|
||||
else if (duration > MAX_SLEW_TIMEOUT)
|
||||
duration = MAX_SLEW_TIMEOUT;
|
||||
if (duration < MIN_SLEW_DURATION)
|
||||
duration = MIN_SLEW_DURATION;
|
||||
else if (duration > MAX_SLEW_DURATION)
|
||||
duration = MAX_SLEW_DURATION;
|
||||
}
|
||||
|
||||
/* Restart timer for the next update */
|
||||
UTI_AddDoubleToTimespec(&now, duration, &end_of_slew);
|
||||
slew_timeout_id = SCH_AddTimeout(&end_of_slew, handle_end_of_slew, NULL);
|
||||
slew_start = now;
|
||||
slew_duration = duration;
|
||||
|
||||
DEBUG_LOG("slew offset=%e corr_rate=%e base_freq=%f total_freq=%f slew_freq=%e duration=%f slew_error=%e",
|
||||
offset_register, correction_rate, base_freq, total_freq, slew_freq,
|
||||
duration, slew_error);
|
||||
DEBUG_LOG("slew offset=%e corr_rate=%e base_freq=%f total_freq=%f slew_freq=%e"
|
||||
" duration=%f excess=%f slew_error=%e",
|
||||
offset_register, correction_rate, base_freq, total_freq, slew_freq,
|
||||
slew_duration, slew_excess_duration, slew_error);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -385,6 +409,7 @@ SYS_Generic_CompleteFreqDriver(double max_set_freq_ppm, double max_set_freq_dela
|
||||
base_freq = (*drv_read_freq)();
|
||||
slew_freq = 0.0;
|
||||
offset_register = 0.0;
|
||||
slew_excess_duration = 0.0;
|
||||
|
||||
max_corr_freq = CNF_GetMaxSlewRate() / 1.0e6;
|
||||
|
||||
|
||||
117
sys_linux.c
117
sys_linux.c
@@ -497,6 +497,9 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context)
|
||||
SCMP_SYS(getrlimit),
|
||||
SCMP_SYS(getuid),
|
||||
SCMP_SYS(getuid32),
|
||||
#ifdef __NR_rseq
|
||||
SCMP_SYS(rseq),
|
||||
#endif
|
||||
SCMP_SYS(rt_sigaction),
|
||||
SCMP_SYS(rt_sigreturn),
|
||||
SCMP_SYS(rt_sigprocmask),
|
||||
@@ -791,73 +794,25 @@ SYS_Linux_CheckKernelVersion(int req_major, int req_minor)
|
||||
|
||||
#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
|
||||
|
||||
#define PHC_READINGS 10
|
||||
|
||||
static int
|
||||
process_phc_readings(struct timespec ts[][3], int n, double precision,
|
||||
struct timespec *phc_ts, struct timespec *sys_ts, double *err)
|
||||
get_phc_readings(int phc_fd, int max_samples, struct timespec ts[][3])
|
||||
{
|
||||
double min_delay = 0.0, delays[PTP_MAX_SAMPLES], phc_sum, sys_sum, sys_prec;
|
||||
int i, combined;
|
||||
|
||||
if (n > PTP_MAX_SAMPLES)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
delays[i] = UTI_DiffTimespecsToDouble(&ts[i][2], &ts[i][0]);
|
||||
|
||||
if (delays[i] < 0.0) {
|
||||
/* Step in the middle of a PHC reading? */
|
||||
DEBUG_LOG("Bad PTP_SYS_OFFSET sample delay=%e", delays[i]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!i || delays[i] < min_delay)
|
||||
min_delay = delays[i];
|
||||
}
|
||||
|
||||
sys_prec = LCL_GetSysPrecisionAsQuantum();
|
||||
|
||||
/* Combine best readings */
|
||||
for (i = combined = 0, phc_sum = sys_sum = 0.0; i < n; i++) {
|
||||
if (delays[i] > min_delay + MAX(sys_prec, precision))
|
||||
continue;
|
||||
|
||||
phc_sum += UTI_DiffTimespecsToDouble(&ts[i][1], &ts[0][1]);
|
||||
sys_sum += UTI_DiffTimespecsToDouble(&ts[i][0], &ts[0][0]) + delays[i] / 2.0;
|
||||
combined++;
|
||||
}
|
||||
|
||||
assert(combined);
|
||||
|
||||
UTI_AddDoubleToTimespec(&ts[0][1], phc_sum / combined, phc_ts);
|
||||
UTI_AddDoubleToTimespec(&ts[0][0], sys_sum / combined, sys_ts);
|
||||
*err = MAX(min_delay / 2.0, precision);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
get_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
|
||||
struct timespec *sys_ts, double *err)
|
||||
{
|
||||
struct timespec ts[PHC_READINGS][3];
|
||||
struct ptp_sys_offset sys_off;
|
||||
int i;
|
||||
|
||||
max_samples = CLAMP(0, max_samples, PTP_MAX_SAMPLES);
|
||||
|
||||
/* Silence valgrind */
|
||||
memset(&sys_off, 0, sizeof (sys_off));
|
||||
|
||||
sys_off.n_samples = PHC_READINGS;
|
||||
sys_off.n_samples = max_samples;
|
||||
|
||||
if (ioctl(phc_fd, PTP_SYS_OFFSET, &sys_off)) {
|
||||
DEBUG_LOG("ioctl(%s) failed : %s", "PTP_SYS_OFFSET", strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < PHC_READINGS; i++) {
|
||||
for (i = 0; i < max_samples; i++) {
|
||||
ts[i][0].tv_sec = sys_off.ts[i * 2].sec;
|
||||
ts[i][0].tv_nsec = sys_off.ts[i * 2].nsec;
|
||||
ts[i][1].tv_sec = sys_off.ts[i * 2 + 1].sec;
|
||||
@@ -866,31 +821,31 @@ get_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
|
||||
ts[i][2].tv_nsec = sys_off.ts[i * 2 + 2].nsec;
|
||||
}
|
||||
|
||||
return process_phc_readings(ts, PHC_READINGS, precision, phc_ts, sys_ts, err);
|
||||
return max_samples;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
get_extended_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
|
||||
struct timespec *sys_ts, double *err)
|
||||
get_extended_phc_readings(int phc_fd, int max_samples, struct timespec ts[][3])
|
||||
{
|
||||
#ifdef PTP_SYS_OFFSET_EXTENDED
|
||||
struct timespec ts[PHC_READINGS][3];
|
||||
struct ptp_sys_offset_extended sys_off;
|
||||
int i;
|
||||
|
||||
max_samples = CLAMP(0, max_samples, PTP_MAX_SAMPLES);
|
||||
|
||||
/* Silence valgrind */
|
||||
memset(&sys_off, 0, sizeof (sys_off));
|
||||
|
||||
sys_off.n_samples = PHC_READINGS;
|
||||
sys_off.n_samples = max_samples;
|
||||
|
||||
if (ioctl(phc_fd, PTP_SYS_OFFSET_EXTENDED, &sys_off)) {
|
||||
DEBUG_LOG("ioctl(%s) failed : %s", "PTP_SYS_OFFSET_EXTENDED", strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < PHC_READINGS; i++) {
|
||||
for (i = 0; i < max_samples; i++) {
|
||||
ts[i][0].tv_sec = sys_off.ts[i][0].sec;
|
||||
ts[i][0].tv_nsec = sys_off.ts[i][0].nsec;
|
||||
ts[i][1].tv_sec = sys_off.ts[i][1].sec;
|
||||
@@ -899,7 +854,7 @@ get_extended_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
|
||||
ts[i][2].tv_nsec = sys_off.ts[i][2].nsec;
|
||||
}
|
||||
|
||||
return process_phc_readings(ts, PHC_READINGS, precision, phc_ts, sys_ts, err);
|
||||
return max_samples;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
@@ -908,12 +863,14 @@ get_extended_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
|
||||
/* ================================================== */
|
||||
|
||||
static int
|
||||
get_precise_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
|
||||
struct timespec *sys_ts, double *err)
|
||||
get_precise_phc_readings(int phc_fd, int max_samples, struct timespec ts[][3])
|
||||
{
|
||||
#ifdef PTP_SYS_OFFSET_PRECISE
|
||||
struct ptp_sys_offset_precise sys_off;
|
||||
|
||||
if (max_samples < 1)
|
||||
return 0;
|
||||
|
||||
/* Silence valgrind */
|
||||
memset(&sys_off, 0, sizeof (sys_off));
|
||||
|
||||
@@ -923,11 +880,11 @@ get_precise_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
|
||||
return 0;
|
||||
}
|
||||
|
||||
phc_ts->tv_sec = sys_off.device.sec;
|
||||
phc_ts->tv_nsec = sys_off.device.nsec;
|
||||
sys_ts->tv_sec = sys_off.sys_realtime.sec;
|
||||
sys_ts->tv_nsec = sys_off.sys_realtime.nsec;
|
||||
*err = MAX(LCL_GetSysPrecisionAsQuantum(), precision);
|
||||
ts[0][0].tv_sec = sys_off.sys_realtime.sec;
|
||||
ts[0][0].tv_nsec = sys_off.sys_realtime.nsec;
|
||||
ts[0][1].tv_sec = sys_off.device.sec;
|
||||
ts[0][1].tv_nsec = sys_off.device.nsec;
|
||||
ts[0][2] = ts[0][0];
|
||||
|
||||
return 1;
|
||||
#else
|
||||
@@ -971,23 +928,23 @@ SYS_Linux_OpenPHC(const char *path, int phc_index)
|
||||
/* ================================================== */
|
||||
|
||||
int
|
||||
SYS_Linux_GetPHCSample(int fd, int nocrossts, double precision, int *reading_mode,
|
||||
struct timespec *phc_ts, struct timespec *sys_ts, double *err)
|
||||
SYS_Linux_GetPHCReadings(int fd, int nocrossts, int *reading_mode, int max_readings,
|
||||
struct timespec tss[][3])
|
||||
{
|
||||
if ((*reading_mode == 2 || !*reading_mode) && !nocrossts &&
|
||||
get_precise_phc_sample(fd, precision, phc_ts, sys_ts, err)) {
|
||||
int r = 0;
|
||||
|
||||
if ((*reading_mode == 2 || *reading_mode == 0) && !nocrossts &&
|
||||
(r = get_precise_phc_readings(fd, max_readings, tss)) > 0) {
|
||||
*reading_mode = 2;
|
||||
return 1;
|
||||
} else if ((*reading_mode == 3 || !*reading_mode) &&
|
||||
get_extended_phc_sample(fd, precision, phc_ts, sys_ts, err)) {
|
||||
} else if ((*reading_mode == 3 || *reading_mode == 0) &&
|
||||
(r = get_extended_phc_readings(fd, max_readings, tss)) > 0) {
|
||||
*reading_mode = 3;
|
||||
return 1;
|
||||
} else if ((*reading_mode == 1 || !*reading_mode) &&
|
||||
get_phc_sample(fd, precision, phc_ts, sys_ts, err)) {
|
||||
} else if ((*reading_mode == 1 || *reading_mode == 0) &&
|
||||
(r = get_phc_readings(fd, max_readings, tss)) > 0) {
|
||||
*reading_mode = 1;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
@@ -1005,7 +962,7 @@ SYS_Linux_SetPHCExtTimestamping(int fd, int pin, int channel,
|
||||
pin_desc.func = enable ? PTP_PF_EXTTS : PTP_PF_NONE;
|
||||
pin_desc.chan = channel;
|
||||
|
||||
if (ioctl(fd, PTP_PIN_SETFUNC, &pin_desc)) {
|
||||
if (pin >= 0 && ioctl(fd, PTP_PIN_SETFUNC, &pin_desc)) {
|
||||
DEBUG_LOG("ioctl(%s) failed : %s", "PTP_PIN_SETFUNC", strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -41,8 +41,8 @@ extern int SYS_Linux_CheckKernelVersion(int req_major, int req_minor);
|
||||
|
||||
extern int SYS_Linux_OpenPHC(const char *path, int phc_index);
|
||||
|
||||
extern int SYS_Linux_GetPHCSample(int fd, int nocrossts, double precision, int *reading_mode,
|
||||
struct timespec *phc_ts, struct timespec *sys_ts, double *err);
|
||||
extern int SYS_Linux_GetPHCReadings(int fd, int nocrossts, int *reading_mode, int max_readings,
|
||||
struct timespec tss[][3]);
|
||||
|
||||
extern int SYS_Linux_SetPHCExtTimestamping(int fd, int pin, int channel,
|
||||
int rising, int falling, int enable);
|
||||
|
||||
@@ -21,23 +21,54 @@
|
||||
|
||||
=======================================================================
|
||||
|
||||
Driver file for Solaris operating system
|
||||
Driver file for illumos operating system (previously Solaris)
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "sysincl.h"
|
||||
|
||||
#include "logging.h"
|
||||
#include "privops.h"
|
||||
#include "sys_solaris.h"
|
||||
#include "sys_timex.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <kvm.h>
|
||||
#include <nlist.h>
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
static void
|
||||
set_dosynctodr(int on_off)
|
||||
{
|
||||
struct nlist nl[] = { {"dosynctodr"}, {NULL} };
|
||||
kvm_t *kt;
|
||||
|
||||
kt = kvm_open(NULL, NULL, NULL, O_RDWR, NULL);
|
||||
if (!kt)
|
||||
LOG_FATAL("Could not open kvm");
|
||||
|
||||
if (kvm_nlist(kt, nl) < 0 || !nl[0].n_value)
|
||||
LOG_FATAL("Could not get dosynctodr address");
|
||||
|
||||
if (kvm_kwrite(kt, nl[0].n_value, &on_off, sizeof (on_off)) < 0)
|
||||
LOG_FATAL("Could not write to dosynctodr");
|
||||
|
||||
kvm_close(kt);
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
||||
void
|
||||
SYS_Solaris_Initialise(void)
|
||||
{
|
||||
/* The kernel keeps the system clock and hardware clock synchronised to each
|
||||
other. The dosynctodr variable needs to be set to zero to prevent the
|
||||
the system clock from following the hardware clock when the system clock
|
||||
is not adjusted by adjtime() or ntp_adjtime(modes=MOD_OFFSET). */
|
||||
set_dosynctodr(0);
|
||||
|
||||
/* The kernel allows the frequency to be set in the full range off int32_t */
|
||||
SYS_Timex_InitialiseWithFunctions(32500, 1.0 / 100, NULL, NULL, NULL,
|
||||
0.0, 0.0, NULL, NULL);
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
# Run the unit and simulation tests with different compiler sanitizers
|
||||
# and under valgrind
|
||||
|
||||
valgrind_opts="--leak-check=full --errors-for-leak-kinds=definite"
|
||||
|
||||
cd ../..
|
||||
|
||||
if [ "$(uname -sm)" != "Linux x86_64" ]; then
|
||||
@@ -75,7 +77,7 @@ do
|
||||
pushd test/unit || exit 1
|
||||
make "$@" || exit 1
|
||||
if [ "$san_options" = "" ]; then
|
||||
make check TEST_WRAPPER="valgrind --error-exitcode=1" || exit 1
|
||||
make check TEST_WRAPPER="valgrind $valgrind_opts --error-exitcode=1" || exit 1
|
||||
else
|
||||
make check || exit 1
|
||||
fi
|
||||
@@ -87,7 +89,7 @@ do
|
||||
pushd test/simulation || exit 1
|
||||
export CLKNETSIM_RANDOM_SEED=101
|
||||
if [ "$arch_opts" = "" -a "$san_options" = "" ]; then
|
||||
CLKNETSIM_CLIENT_WRAPPER=valgrind ./run -i 1 || exit 1
|
||||
CLKNETSIM_CLIENT_WRAPPER="valgrind $valgrind_opts" ./run -i 1 || exit 1
|
||||
elif [ "$CC" = "gcc" ] && ! echo $CFLAGS | grep -q "-static-libasan"; then
|
||||
libasan=$(ldd ../../chronyd | grep -o '/.*lib.*/libasan.so.[0-9]')
|
||||
CLKNETSIM_PRELOAD=$libasan ./run -i 1 || exit 1
|
||||
|
||||
@@ -29,7 +29,7 @@ echo "$ntp_start" | grep -q '-' && test_skip
|
||||
|
||||
for time_offset in -1e-1 1e-1; do
|
||||
for start_offset in 0 "2^32 - $limit"; do
|
||||
export CLKNETSIM_START_DATE=$(awk "BEGIN {print $ntp_start + $start_offset}")
|
||||
export CLKNETSIM_START_DATE=$(awk "BEGIN {printf \"%.0f\", $ntp_start + $start_offset}")
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
@@ -38,7 +38,7 @@ for time_offset in -1e-1 1e-1; do
|
||||
done
|
||||
|
||||
for start_offset in -$limit "2^32"; do
|
||||
export CLKNETSIM_START_DATE=$(awk "BEGIN {print $ntp_start + $start_offset}")
|
||||
export CLKNETSIM_START_DATE=$(awk "BEGIN {printf \"%.0f\", $ntp_start + $start_offset}")
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
|
||||
@@ -27,4 +27,30 @@ for poll in $(seq 1 14); do
|
||||
check_sync || test_fail
|
||||
done
|
||||
|
||||
min_sync_time=$default_min_sync_time
|
||||
max_sync_time=$default_max_sync_time
|
||||
client_max_min_out_interval=$default_client_max_min_out_interval
|
||||
client_min_mean_out_interval=$default_client_min_mean_out_interval
|
||||
|
||||
limit=10
|
||||
|
||||
for poll in $(seq -7 2 -1); do
|
||||
client_server_options="minpoll $poll maxpoll $poll"
|
||||
|
||||
base_delay=1e-4
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_file_messages " 2 1 " \
|
||||
$[2**-poll * limit * 9 / 10] $[2**-poll * limit] log.packets || test_fail
|
||||
|
||||
base_delay=2e-2
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_file_messages " 2 1 " $[limit * 9 / 10] $limit log.packets || test_fail
|
||||
done
|
||||
|
||||
test_pass
|
||||
|
||||
@@ -7,6 +7,9 @@ check_config_h 'FEAT_REFCLOCK 1' || test_skip
|
||||
check_config_h 'FEAT_PHC 1' || test_skip
|
||||
check_config_h 'FEAT_CMDMON 1' || test_skip
|
||||
|
||||
export CLKNETSIM_PHC_DELAY=1e-6
|
||||
export CLKNETSIM_PHC_JITTER=1e-7
|
||||
|
||||
servers=0
|
||||
limit=1000
|
||||
refclock_jitter=$jitter
|
||||
@@ -15,7 +18,7 @@ max_sync_time=70
|
||||
chronyc_start=70
|
||||
chronyc_conf="tracking"
|
||||
|
||||
for refclock in "SHM 0" "PHC /dev/ptp0"; do
|
||||
for refclock in "SHM 0" "PHC /dev/ptp0" "PHC /dev/ptp0:nocrossts"; do
|
||||
client_conf="refclock $refclock stratum 3 delay 1e-3 refid GPS
|
||||
logdir tmp
|
||||
log refclocks"
|
||||
@@ -32,7 +35,11 @@ Root delay : 0.001000000 seconds
|
||||
Update interval : 16\.. seconds
|
||||
.*$" || test_fail
|
||||
|
||||
check_file_messages "20.* GPS.*[0-9] N " 997 1001 refclocks.log || test_fail
|
||||
if echo "$refclock" | grep -q 'PHC.*nocrossts'; then
|
||||
check_file_messages "20.* GPS.*[0-9] N " 650 750 refclocks.log || test_fail
|
||||
else
|
||||
check_file_messages "20.* GPS.*[0-9] N " 997 1001 refclocks.log || test_fail
|
||||
fi
|
||||
check_file_messages "20.* GPS.*- N " 61 63 refclocks.log || test_fail
|
||||
rm -f tmp/refclocks.log
|
||||
done
|
||||
@@ -106,4 +113,31 @@ Root delay : 0\.000000001 seconds
|
||||
rm -f tmp/refclocks.log
|
||||
fi
|
||||
|
||||
refclock_offset="(+ 0.399 (sum 1e-3))"
|
||||
refclock_jitter=1e-6
|
||||
servers=1
|
||||
freq_offset="(* 1e-4 (sine 1000))"
|
||||
base_delay="(* -1.0 (equal 0.1 (min time 5000) 5000))"
|
||||
client_server_options="minpoll 4 maxpoll 4 filter 5 minsamples 64"
|
||||
client_conf="
|
||||
refclock PHC /dev/ptp0 local poll 2
|
||||
logdir tmp
|
||||
log refclocks tracking"
|
||||
chronyc_conf=""
|
||||
limit=10000
|
||||
max_sync_time=5000
|
||||
time_max_limit=1e-3
|
||||
time_rms_limit=5e-4
|
||||
freq_max_limit=2e-5
|
||||
freq_rms_limit=5e-6
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_sync || test_fail
|
||||
|
||||
check_file_messages "20.* PHC0 .* [0-9] ? " 9999 10001 refclocks.log || test_fail
|
||||
check_file_messages "20.* PHC0 .* - ? " 2499 2501 refclocks.log || test_fail
|
||||
check_file_messages "20.* PHC0 " 0 0 tracking.log || test_fail
|
||||
rm -f tmp/refclocks.log tmp/tracking.log
|
||||
|
||||
test_pass
|
||||
|
||||
@@ -102,7 +102,7 @@ limit=1
|
||||
for chronyc_conf in \
|
||||
"accheck 1.2.3.4" \
|
||||
"add peer 10.0.0.0 minpoll 2 maxpoll 6" \
|
||||
"add server 10.0.0.0 minpoll 6 maxpoll 10 iburst burst key 1 certset 2 maxdelay 1e-3 maxdelayratio 10.0 maxdelaydevratio 10.0 mindelay 1e-4 asymmetry 0.5 offset 1e-5 minsamples 6 maxsamples 6 filter 3 offline auto_offline prefer noselect trust require xleave polltarget 20 port 123 presend 7 minstratum 3 version 4 nts ntsport 4460 copy extfield F323" \
|
||||
"add server 10.0.0.0 minpoll 6 maxpoll 10 iburst burst key 1 certset 2 maxdelay 1e-3 maxdelayratio 10.0 maxdelaydevratio 10.0 maxdelayquant 0.5 mindelay 1e-4 asymmetry 0.5 offset 1e-5 minsamples 6 maxsamples 6 filter 3 offline auto_offline prefer noselect trust require xleave polltarget 20 port 123 presend 7 minstratum 3 version 4 nts ntsport 4460 copy extfield F323" \
|
||||
"add server node1.net1.clk" \
|
||||
"allow 1.2.3.4" \
|
||||
"allow 1.2" \
|
||||
@@ -231,6 +231,7 @@ RX timestamping : Kernel
|
||||
Total TX : 1
|
||||
Total RX : 1
|
||||
Total valid RX : 1
|
||||
Total good RX : 0
|
||||
S Name/IP Address Auth COpts EOpts Last Score Interval Leap
|
||||
=======================================================================
|
||||
M node1\.net1\.clk N ----- ----- 0 1\.0 \+0ns \+0ns N
|
||||
|
||||
@@ -25,4 +25,18 @@ for client_server_options in "maxpoll 6 maxdelay 2e-5"; do
|
||||
check_sync && test_fail
|
||||
done
|
||||
|
||||
min_sync_time=10
|
||||
client_conf="
|
||||
logdir tmp
|
||||
log rawmeasurements"
|
||||
client_server_options="minpoll 2 maxpoll 2 maxdelayquant 0.1"
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_packet_interval || test_fail
|
||||
check_sync || test_fail
|
||||
|
||||
check_file_messages "20.*123\.1.* 111 111 1111" 200 500 measurements.log || test_fail
|
||||
check_file_messages "20.*123\.1.* 111 111 1101" 2000 2300 measurements.log || test_fail
|
||||
|
||||
test_pass
|
||||
|
||||
@@ -15,10 +15,11 @@ check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_sync || test_fail
|
||||
|
||||
check_file_messages "111 111 1111.* 4I [DKH] [DKH]\$" 0 0 measurements.log || test_fail
|
||||
check_file_messages "111 111 .111.* 4I [DKH] [DKH]\$" 0 0 measurements.log || test_fail
|
||||
rm -f tmp/measurements.log
|
||||
|
||||
server_conf=""
|
||||
max_sync_time=270
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
@@ -28,6 +29,7 @@ check_sync || test_fail
|
||||
|
||||
check_file_messages "111 111 1111.* 4B [DKH] [DKH]\$" 2 2 measurements.log || test_fail
|
||||
check_file_messages "111 111 1111.* 4I [DKH] [DKH]\$" 30 200 measurements.log || test_fail
|
||||
check_file_messages "111 111 0111.* 4I [DKH] [DKH]\$" 1 1 measurements.log || test_fail
|
||||
rm -f tmp/measurements.log
|
||||
|
||||
clients=2
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
test_start "filter option"
|
||||
|
||||
client_server_options="minpoll 4 maxpoll 4 filter 15"
|
||||
client_server_options="minpoll 4 maxpoll 4 filter 15 maxdelay 3.5e-4"
|
||||
min_sync_time=710
|
||||
max_sync_time=720
|
||||
client_max_min_out_interval=16.1
|
||||
@@ -16,4 +16,28 @@ check_source_selection || test_fail
|
||||
check_packet_interval || test_fail
|
||||
check_sync || test_fail
|
||||
|
||||
base_delay="(+ 1e-4 (* -1 (equal 0.3 (uniform) 0.0)))"
|
||||
client_server_options="minpoll 4 maxpoll 4 filter 3"
|
||||
min_sync_time=130
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_packet_interval || test_fail
|
||||
check_sync || test_fail
|
||||
|
||||
limit=10
|
||||
client_server_options="minpoll -6 maxpoll -6 filter 1"
|
||||
|
||||
base_delay=1e-4
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_file_messages " 2 1 " 590 640 log.packets || test_fail
|
||||
|
||||
base_delay=2e-2
|
||||
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_file_messages " 2 1 " 9 10 log.packets || test_fail
|
||||
|
||||
test_pass
|
||||
|
||||
@@ -7,32 +7,53 @@ test_start "hwtimestamp directive"
|
||||
check_config_h 'HAVE_LINUX_TIMESTAMPING 1' || test_skip
|
||||
|
||||
export CLKNETSIM_TIMESTAMPING=2
|
||||
export CLKNETSIM_PHC_DELAY=1e-6
|
||||
export CLKNETSIM_PHC_JITTER=1e-7
|
||||
export CLKNETSIM_PHC_JITTER_ASYM=0.4
|
||||
|
||||
refclock_jitter=1e-8
|
||||
refclock_offset=10.0
|
||||
min_sync_time=4
|
||||
max_sync_time=20
|
||||
time_rms_limit=1e-7
|
||||
freq_rms_limit=3e-8
|
||||
jitter=1e-8
|
||||
freq_offset=1e-5
|
||||
limit=200
|
||||
server_conf="hwtimestamp eth0"
|
||||
client_server_options="minpoll 0 maxpoll 0 minsamples 32 xleave"
|
||||
server_conf="
|
||||
clockprecision 1e-9
|
||||
hwtimestamp eth0"
|
||||
client_server_options="minpoll 0 maxpoll 0 xleave"
|
||||
client_chronyd_options="-d"
|
||||
|
||||
for client_conf in "hwtimestamp eth0" "hwtimestamp eth0
|
||||
acquisitionport 123"; do
|
||||
for client_conf in \
|
||||
"hwtimestamp eth0 nocrossts
|
||||
clockprecision 1e-9" \
|
||||
"hwtimestamp eth0
|
||||
clockprecision 1e-9
|
||||
acquisitionport 123"; do
|
||||
run_test || test_fail
|
||||
check_chronyd_exit || test_fail
|
||||
check_source_selection || test_fail
|
||||
check_sync || test_fail
|
||||
|
||||
if check_config_h 'FEAT_DEBUG 1'; then
|
||||
check_log_messages "Accepted reading" 0 2 || test_fail
|
||||
check_log_messages "Combined .* readings" 190 220 || test_fail
|
||||
check_log_messages "HW clock samples" 190 200 || test_fail
|
||||
check_log_messages "HW clock reset" 0 0 || test_fail
|
||||
check_log_messages "Missing TX timestamp" 1 1 || test_fail
|
||||
check_log_messages "Received message.*tss=KH" 195 200 || test_fail
|
||||
check_log_messages "Received error.*message.*tss=KH" 195 200 || test_fail
|
||||
check_log_messages "Updated RX timestamp.*tss=1" 1 1 || test_fail
|
||||
check_log_messages "Updated RX timestamp.*tss=2" 195 200 || test_fail
|
||||
check_log_messages "update_tx_timestamp.*Updated" 50 140 || test_fail
|
||||
check_log_messages "update_tx_timestamp.*Unacceptable" 50 140 || test_fail
|
||||
if echo "$client_conf" | grep -q nocrossts; then
|
||||
check_log_messages "update_tx_timestamp.*Updated" 180 200 || test_fail
|
||||
check_log_messages "update_tx_timestamp.*Unacceptable" 0 10 || test_fail
|
||||
else
|
||||
check_log_messages "update_tx_timestamp.*Updated" 50 140 || test_fail
|
||||
check_log_messages "update_tx_timestamp.*Unacceptable" 50 140 || test_fail
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ client_server_options="maxpoll 6"
|
||||
client_conf="refclock PHC /dev/ptp0 dpoll 4 poll 6 noselect
|
||||
logbanner 10
|
||||
logdir tmp
|
||||
log tracking rawmeasurements measurements statistics rtc refclocks tempcomp
|
||||
log tracking rawmeasurements measurements selection statistics rtc refclocks tempcomp
|
||||
tempcomp tmp/tempcomp 64 0.0 0.0 0.0 0.0"
|
||||
|
||||
echo 0.0 > tmp/tempcomp
|
||||
@@ -26,6 +26,8 @@ check_file_messages "=============" 31 33 \
|
||||
tracking.log measurements.log tempcomp.log || test_fail
|
||||
check_file_messages "20.*192\.168\.123\.1" 150 160 \
|
||||
tracking.log measurements.log statistics.log || test_fail
|
||||
check_file_messages "20.*PHC0 * N " 300 320 selection.log || test_fail
|
||||
check_file_messages "20.*192\.168\.123\.1 *[M*]" 300 320 selection.log || test_fail
|
||||
check_file_messages "20.*PHC0" 150 160 statistics.log || test_fail
|
||||
check_file_messages "20.*PHC0" 750 800 refclocks.log || test_fail
|
||||
check_file_messages "20.* 0\.0000" 150 160 tempcomp.log || test_fail
|
||||
|
||||
@@ -95,12 +95,13 @@ TX timestamping : (Daemon|Kernel)
|
||||
RX timestamping : (Daemon|Kernel)
|
||||
Total TX : [0-9]+
|
||||
Total RX : [0-9]+
|
||||
Total valid RX : [0-9]+$" || test_fail
|
||||
Total valid RX : [0-9]+
|
||||
Total good RX : [0-9]+$" || test_fail
|
||||
|
||||
run_chronyc "selectdata" || test_fail
|
||||
check_chronyc_output "^S Name/IP Address Auth COpts EOpts Last Score Interval Leap
|
||||
=======================================================================
|
||||
M 127\.0\.0\.1 N ----- ----- 0 1\.0 \+0ns \+0ns \?$" || test_fail
|
||||
s 127\.0\.0\.1 N ----- ----- 0 1\.0 \+0ns \+0ns \?$" || test_fail
|
||||
|
||||
run_chronyc "serverstats" || test_fail
|
||||
check_chronyc_output "^NTP packets received : [0-9]+
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
**********************************************************************
|
||||
* Copyright (C) Miroslav Lichvar 2016
|
||||
* Copyright (C) Miroslav Lichvar 2016, 2021
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
**********************************************************************
|
||||
* Copyright (C) Miroslav Lichvar 2016-2018
|
||||
* Copyright (C) Miroslav Lichvar 2016-2018, 2022
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -18,21 +18,29 @@
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
#include <hwclock.c>
|
||||
#include <config.h>
|
||||
#include "test.h"
|
||||
|
||||
#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
|
||||
|
||||
#include <hwclock.c>
|
||||
|
||||
#define MAX_READINGS 20
|
||||
|
||||
void
|
||||
test_unit(void)
|
||||
{
|
||||
struct timespec start_hw_ts, start_local_ts, hw_ts, local_ts, ts;
|
||||
struct timespec readings[MAX_READINGS][3];
|
||||
HCL_Instance clock;
|
||||
double freq, jitter, interval, dj, sum;
|
||||
int i, j, k, count;
|
||||
double freq, jitter, interval, dj, err, sum;
|
||||
int i, j, k, l, new_sample, n_readings, count;
|
||||
|
||||
LCL_Initialise();
|
||||
TST_RegisterDummyDrivers();
|
||||
|
||||
for (i = 1; i <= 8; i++) {
|
||||
clock = HCL_CreateInstance(random() % (1 << i), 1 << i, 1.0);
|
||||
clock = HCL_CreateInstance(random() % (1 << i), 1 << i, 1.0, 1e-9);
|
||||
|
||||
for (j = 0, count = 0, sum = 0.0; j < 100; j++) {
|
||||
UTI_ZeroTimespec(&start_hw_ts);
|
||||
@@ -48,11 +56,14 @@ test_unit(void)
|
||||
|
||||
clock->n_samples = 0;
|
||||
clock->valid_coefs = 0;
|
||||
QNT_Reset(clock->delay_quants);
|
||||
|
||||
new_sample = 0;
|
||||
|
||||
for (k = 0; k < 100; k++) {
|
||||
UTI_AddDoubleToTimespec(&start_hw_ts, k * interval * freq, &hw_ts);
|
||||
UTI_AddDoubleToTimespec(&start_local_ts, k * interval, &local_ts);
|
||||
if (HCL_CookTime(clock, &hw_ts, &ts, NULL)) {
|
||||
if (HCL_CookTime(clock, &hw_ts, &ts, NULL) && new_sample) {
|
||||
dj = fabs(UTI_DiffTimespecsToDouble(&ts, &local_ts) / jitter);
|
||||
DEBUG_LOG("delta/jitter %f", dj);
|
||||
if (clock->n_samples >= clock->max_samples / 2)
|
||||
@@ -63,10 +74,25 @@ test_unit(void)
|
||||
|
||||
UTI_AddDoubleToTimespec(&start_hw_ts, k * interval * freq + TST_GetRandomDouble(-jitter, jitter), &hw_ts);
|
||||
|
||||
if (HCL_NeedsNewSample(clock, &local_ts))
|
||||
HCL_AccumulateSample(clock, &hw_ts, &local_ts, 2.0 * jitter);
|
||||
if (HCL_NeedsNewSample(clock, &local_ts)) {
|
||||
n_readings = random() % MAX_READINGS + 1;
|
||||
for (l = 0; l < n_readings; l++) {
|
||||
UTI_AddDoubleToTimespec(&local_ts, -TST_GetRandomDouble(0.0, jitter / 10.0), &readings[l][0]);
|
||||
readings[l][1] = hw_ts;
|
||||
UTI_AddDoubleToTimespec(&local_ts, TST_GetRandomDouble(0.0, jitter / 10.0), &readings[l][2]);
|
||||
}
|
||||
|
||||
TEST_CHECK(clock->valid_coefs || clock->n_samples < 2);
|
||||
UTI_ZeroTimespec(&hw_ts);
|
||||
UTI_ZeroTimespec(&local_ts);
|
||||
if (HCL_ProcessReadings(clock, n_readings, readings, &hw_ts, &local_ts, &err)) {
|
||||
HCL_AccumulateSample(clock, &hw_ts, &local_ts, 2.0 * jitter);
|
||||
new_sample = 1;
|
||||
} else {
|
||||
new_sample = 0;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CHECK(clock->valid_coefs == (clock->n_samples >= 2));
|
||||
|
||||
if (!clock->valid_coefs)
|
||||
continue;
|
||||
@@ -82,3 +108,10 @@ test_unit(void)
|
||||
|
||||
LCL_Finalise();
|
||||
}
|
||||
#else
|
||||
void
|
||||
test_unit(void)
|
||||
{
|
||||
TEST_REQUIRE(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -385,7 +385,7 @@ add_dummy_auth(NTP_AuthMode auth_mode, uint32_t key_id, NTP_Packet *packet, NTP_
|
||||
void
|
||||
test_unit(void)
|
||||
{
|
||||
char source_line[] = "127.0.0.1 maxdelaydevratio 1e6";
|
||||
char source_line[] = "127.0.0.1 maxdelaydevratio 1e6 noselect";
|
||||
char conf[][100] = {
|
||||
"allow",
|
||||
"port 0",
|
||||
@@ -411,6 +411,7 @@ test_unit(void)
|
||||
NCR_Initialise();
|
||||
REF_Initialise();
|
||||
KEY_Initialise();
|
||||
CLG_Initialise();
|
||||
|
||||
CNF_SetupAccessRestrictions();
|
||||
|
||||
@@ -422,6 +423,9 @@ test_unit(void)
|
||||
source.params.version = random() % 4 + 1;
|
||||
|
||||
UTI_ZeroTimespec(¤t_time);
|
||||
#if HAVE_LONG_TIME_T
|
||||
advance_time(NTP_ERA_SPLIT);
|
||||
#endif
|
||||
advance_time(TST_GetRandomDouble(1.0, 1e9));
|
||||
|
||||
TST_GetRandomAddress(&remote_addr.ip_addr, IPADDR_UNSPEC, -1);
|
||||
@@ -492,7 +496,10 @@ test_unit(void)
|
||||
|
||||
send_request(inst1);
|
||||
process_request(&remote_addr);
|
||||
proc_response(inst1, 1, 1, 1, 0);
|
||||
proc_response(inst1,
|
||||
!source.params.interleaved || source.params.version != 4 ||
|
||||
inst1->mode == MODE_ACTIVE || j != 2,
|
||||
1, 1, 0);
|
||||
advance_time(1 << inst1->local_poll);
|
||||
}
|
||||
|
||||
@@ -595,6 +602,7 @@ test_unit(void)
|
||||
TEST_CHECK(info.auth.mac.length == 72);
|
||||
TEST_CHECK(info.auth.mac.key_id == 300);
|
||||
|
||||
CLG_Finalise();
|
||||
KEY_Finalise();
|
||||
REF_Finalise();
|
||||
NCR_Finalise();
|
||||
|
||||
@@ -110,7 +110,7 @@ change_remote_address(NCR_Instance inst, NTP_Remote_Address *remote_addr, int nt
|
||||
void
|
||||
test_unit(void)
|
||||
{
|
||||
char source_line[] = "127.0.0.1 offline", conf[] = "port 0", name[64], msg[1];
|
||||
char source_line[] = "127.0.0.1 offline", conf[] = "port 0", name[64];
|
||||
int i, j, k, slot, found, pool, prev_n;
|
||||
uint32_t hash = 0, conf_id;
|
||||
NTP_Remote_Address addrs[256], addr;
|
||||
@@ -120,6 +120,7 @@ test_unit(void)
|
||||
RPT_ActivityReport report;
|
||||
CPS_NTP_Source source;
|
||||
NSR_Status status;
|
||||
NTP_Packet msg;
|
||||
|
||||
CNF_Initialise(0, 0);
|
||||
CNF_ParseLine(NULL, 1, conf);
|
||||
@@ -272,12 +273,14 @@ test_unit(void)
|
||||
|
||||
switch (random() % 5) {
|
||||
case 0:
|
||||
msg.lvm = NTP_LVM(0, NTP_VERSION, random() % 2 ? MODE_CLIENT : MODE_SERVER);
|
||||
NSR_ProcessTx(get_record(slot)->remote_addr, &local_addr,
|
||||
&local_ts, (NTP_Packet *)msg, 0);
|
||||
&local_ts, &msg, 0);
|
||||
break;
|
||||
case 1:
|
||||
msg.lvm = NTP_LVM(0, NTP_VERSION, random() % 2 ? MODE_CLIENT : MODE_SERVER);
|
||||
NSR_ProcessRx(get_record(slot)->remote_addr, &local_addr,
|
||||
&local_ts, (NTP_Packet *)msg, 0);
|
||||
&local_ts, &msg, 0);
|
||||
break;
|
||||
case 2:
|
||||
NSR_HandleBadSource(&get_record(slot)->remote_addr->ip_addr);
|
||||
|
||||
68
test/unit/quantiles.c
Normal file
68
test/unit/quantiles.c
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
**********************************************************************
|
||||
* Copyright (C) Miroslav Lichvar 2022
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
**********************************************************************
|
||||
*/
|
||||
|
||||
#include <local.h>
|
||||
#include "test.h"
|
||||
|
||||
#include <quantiles.c>
|
||||
|
||||
void
|
||||
test_unit(void)
|
||||
{
|
||||
int i, j, k, min_k, max_k, q, r, in_order, out_order;
|
||||
QNT_Instance inst;
|
||||
double x;
|
||||
|
||||
in_order = out_order = 0;
|
||||
|
||||
for (i = 0; i < 100; i++) {
|
||||
r = random() % 10 + 1;
|
||||
q = random() % 20 + 2;
|
||||
do {
|
||||
min_k = random() % (q - 1) + 1;
|
||||
max_k = random() % (q - 1) + 1;
|
||||
} while (min_k > max_k);
|
||||
|
||||
inst = QNT_CreateInstance(min_k, max_k, q, r, 1e-9);
|
||||
|
||||
TEST_CHECK(min_k == QNT_GetMinK(inst));
|
||||
|
||||
for (j = 0; j < 3000; j++) {
|
||||
x = TST_GetRandomDouble(0.0, 2e-6);
|
||||
QNT_Accumulate(inst, x);
|
||||
for (k = min_k; k < max_k; k++)
|
||||
if (j < max_k - min_k) {
|
||||
TEST_CHECK(QNT_GetQuantile(inst, k) <= QNT_GetQuantile(inst, k + 1));
|
||||
} else if (j > 1000) {
|
||||
if (QNT_GetQuantile(inst, k) <= QNT_GetQuantile(inst, k + 1))
|
||||
in_order++;
|
||||
else
|
||||
out_order++;
|
||||
}
|
||||
}
|
||||
|
||||
QNT_Reset(inst);
|
||||
TEST_CHECK(inst->n_set == 0);
|
||||
|
||||
QNT_DestroyInstance(inst);
|
||||
}
|
||||
|
||||
TEST_CHECK(in_order > 100 * out_order);
|
||||
}
|
||||
@@ -45,6 +45,8 @@ test_unit(void)
|
||||
|
||||
filter = SPF_CreateInstance(min_samples, max_samples, 2.0, combine_ratio);
|
||||
|
||||
TEST_CHECK(max_samples == SPF_GetMaxSamples(filter));
|
||||
|
||||
for (j = 0, sum_count = 0, sum_err = 0.0; j < 100; j++) {
|
||||
DEBUG_LOG("iteration %d/%d", i, j);
|
||||
|
||||
@@ -69,6 +71,7 @@ test_unit(void)
|
||||
TEST_CHECK(!memcmp(&sample_in, &sample_out, sizeof (sample_in)));
|
||||
|
||||
SPF_SlewSamples(filter, &sample_in.time, 0.0, 0.0);
|
||||
SPF_CorrectOffset(filter, 0.0);
|
||||
SPF_AddDispersion(filter, 0.0);
|
||||
|
||||
if (k + 1 < min_samples)
|
||||
@@ -102,6 +105,7 @@ test_unit(void)
|
||||
|
||||
} else {
|
||||
SPF_DropSamples(filter);
|
||||
TEST_CHECK(filter->last < 0);
|
||||
}
|
||||
|
||||
TEST_CHECK(SPF_GetNumberOfSamples(filter) == 0);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
**********************************************************************
|
||||
* Copyright (C) Miroslav Lichvar 2016, 2018
|
||||
* Copyright (C) Miroslav Lichvar 2016, 2018, 2022
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
@@ -95,12 +95,14 @@ test_unit(void)
|
||||
double passed_lo = DBL_MAX, passed_hi = DBL_MIN;
|
||||
|
||||
SRC_SelectSource(srcs[k]);
|
||||
DEBUG_LOG("source %d status %u", k, sources[k]->status);
|
||||
DEBUG_LOG("source %d status %c", k, get_status_char(sources[k]->status));
|
||||
|
||||
for (l = 0; l <= j; l++) {
|
||||
TEST_CHECK(sources[l]->status > SRC_OK && sources[l]->status <= SRC_SELECTED);
|
||||
if (sources[l]->sel_options & SRC_SELECT_NOSELECT) {
|
||||
TEST_CHECK(sources[l]->status == SRC_UNSELECTABLE);
|
||||
} else if (sources[l]->leap == LEAP_Unsynchronised) {
|
||||
TEST_CHECK(sources[l]->status == SRC_UNSYNCHRONISED);
|
||||
} else if (sources[l]->status != SRC_BAD_DISTANCE) {
|
||||
if (sources[l]->status >= SRC_NONPREFERRED) {
|
||||
passed++;
|
||||
@@ -149,6 +151,51 @@ test_unit(void)
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
DEBUG_LOG("iteration %d", i);
|
||||
|
||||
for (j = 0; j < sizeof (srcs) / sizeof (srcs[0]); j++) {
|
||||
TEST_CHECK(n_sources == j);
|
||||
|
||||
srcs[j] = create_source(SRC_NTP, &addrs[j], 0, 0);
|
||||
SRC_UpdateReachability(srcs[j], 1);
|
||||
|
||||
samples = 8;
|
||||
for (k = 0; k < samples; k++) {
|
||||
SCH_GetLastEventTime(&sample.time, NULL, NULL);
|
||||
UTI_AddDoubleToTimespec(&sample.time, k - samples, &sample.time);
|
||||
if (j != 0)
|
||||
UTI_AddDoubleToTimespec(&sample.time, -i * (1.0e-3 / LCL_GetMaxClockError()), &sample.time);
|
||||
|
||||
sample.offset = (k % 2) * 1e-8;
|
||||
sample.peer_delay = sample.root_delay = 2.0e-3 * (j + 1);
|
||||
sample.peer_dispersion = sample.root_dispersion = 4.0e-3;
|
||||
|
||||
SRC_AccumulateSample(srcs[j], &sample);
|
||||
SRC_UpdateStatus(srcs[j], 1, LEAP_Normal);
|
||||
}
|
||||
}
|
||||
|
||||
SRC_SelectSource(srcs[0]);
|
||||
|
||||
for (j = 0; j < sizeof (srcs) / sizeof (srcs[0]); j++) {
|
||||
DEBUG_LOG("%d %c %f", j, get_status_char(srcs[j]->status),
|
||||
srcs[j]->sel_info.root_distance);
|
||||
if (j == 0)
|
||||
TEST_CHECK(sources[j]->status == SRC_SELECTED);
|
||||
else if (j < 11 - i)
|
||||
TEST_CHECK(sources[j]->status == SRC_UNSELECTED);
|
||||
else
|
||||
TEST_CHECK(sources[j]->status == SRC_DISTANT);
|
||||
}
|
||||
|
||||
for (j = 0; j < sizeof (srcs) / sizeof (srcs[0]); j++) {
|
||||
SCH_GetLastEventTime(&sample.time, NULL, NULL);
|
||||
SRC_ReportSource(j, &report, &sample.time);
|
||||
SRC_DestroyInstance(srcs[j]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CHECK(CNF_GetAuthSelectMode() == SRC_AUTHSELECT_MIX);
|
||||
|
||||
for (i = 0; i < 1000; i++) {
|
||||
|
||||
2
util.c
2
util.c
@@ -3,7 +3,7 @@
|
||||
|
||||
**********************************************************************
|
||||
* Copyright (C) Richard P. Curnow 1997-2003
|
||||
* Copyright (C) Miroslav Lichvar 2009, 2012-2020
|
||||
* Copyright (C) Miroslav Lichvar 2009, 2012-2021
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of version 2 of the GNU General Public License as
|
||||
|
||||
Reference in New Issue
Block a user