summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--FAQ111
-rw-r--r--INSTALL38
-rw-r--r--Makefile.in2
-rw-r--r--NEWS27
-rw-r--r--candm.h6
-rw-r--r--client.c89
-rw-r--r--cmdmon.c25
-rw-r--r--cmdparse.c8
-rw-r--r--conf.c12
-rw-r--r--conf.h2
-rwxr-xr-xconfigure24
-rw-r--r--doc/chrony.conf.adoc80
-rw-r--r--doc/chrony.conf.man.in93
-rw-r--r--doc/chronyc.adoc12
-rw-r--r--doc/chronyc.man.in17
-rw-r--r--doc/chronyd.adoc6
-rw-r--r--doc/chronyd.man.in10
-rw-r--r--doc/faq.adoc112
-rw-r--r--doc/installation.adoc64
-rw-r--r--examples/chrony.nm-dispatcher36
-rw-r--r--examples/chrony.spec2
-rw-r--r--examples/chronyd.service2
-rw-r--r--hash_intmd5.c9
-rw-r--r--hash_nss.c9
-rw-r--r--hash_tomcrypt.c14
-rw-r--r--hwclock.c40
-rw-r--r--hwclock.h3
-rw-r--r--keys.c4
-rw-r--r--logging.c23
-rw-r--r--logging.h2
-rw-r--r--main.c8
-rw-r--r--memory.h2
-rw-r--r--ntp.h15
-rw-r--r--ntp_core.c348
-rw-r--r--ntp_core.h9
-rw-r--r--ntp_io.c74
-rw-r--r--ntp_io.h3
-rw-r--r--ntp_io_linux.c15
-rw-r--r--ntp_sources.c62
-rw-r--r--ntp_sources.h11
-rw-r--r--pktlength.c1
-rw-r--r--privops.c7
-rw-r--r--refclock.c434
-rw-r--r--refclock_phc.c2
-rw-r--r--refclock_pps.c12
-rw-r--r--refclock_shm.c4
-rw-r--r--refclock_sock.c15
-rw-r--r--reference.c216
-rw-r--r--reference.h1
-rw-r--r--rtc_linux.c14
-rw-r--r--samplefilt.c453
-rw-r--r--samplefilt.h49
-rw-r--r--sched.c9
-rw-r--r--smooth.c2
-rw-r--r--sources.c96
-rw-r--r--sources.h30
-rw-r--r--sourcestats.c66
-rw-r--r--sourcestats.h19
-rw-r--r--srcparams.h9
-rw-r--r--stubs.c8
-rw-r--r--sys_linux.c36
-rw-r--r--sysincl.h9
-rwxr-xr-xtest/compilation/001-features4
-rwxr-xr-xtest/compilation/003-sanitizers87
-rwxr-xr-xtest/simulation/101-poll4
-rwxr-xr-xtest/simulation/106-refclock15
-rwxr-xr-xtest/simulation/108-peer21
-rwxr-xr-xtest/simulation/110-chronyc74
-rwxr-xr-xtest/simulation/121-orphan1
-rwxr-xr-xtest/simulation/126-burst16
-rwxr-xr-xtest/simulation/127-filter19
-rwxr-xr-xtest/simulation/128-nocontrol25
-rwxr-xr-xtest/simulation/129-reload25
-rwxr-xr-xtest/simulation/130-quit23
-rwxr-xr-xtest/simulation/131-maxchange20
-rwxr-xr-xtest/simulation/132-logchange21
-rwxr-xr-xtest/simulation/133-hwtimestamp32
-rw-r--r--test/simulation/test.common44
-rw-r--r--test/unit/clientlog.c4
-rw-r--r--test/unit/hash.c2
-rw-r--r--test/unit/hwclock.c82
-rw-r--r--test/unit/ntp_core.c55
-rw-r--r--test/unit/ntp_core.keys6
-rw-r--r--test/unit/samplefilt.c113
-rw-r--r--test/unit/sources.c31
-rw-r--r--test/unit/test.c10
-rw-r--r--test/unit/test.h3
-rw-r--r--test/unit/util.c70
-rw-r--r--util.c41
-rw-r--r--util.h4
-rw-r--r--version.txt2
91 files changed, 2515 insertions, 1260 deletions
diff --git a/FAQ b/FAQ
index 781ad05..76e62fc 100644
--- a/FAQ
+++ b/FAQ
@@ -20,6 +20,8 @@ Table of Contents
? 3.1. Behind a firewall?
? 3.2. Are NTP servers specified with the offline option?
? 3.3. Is chronyd allowed to step the system clock?
+ ? 3.4. Using a Windows NTP server?
+ ? 3.5. Using a PPS reference clock?
o 4. Issues with chronyc
? 4.1. I keep getting the error 506 Cannot talk to daemon
? 4.2. I keep getting the error 501 Not authorised
@@ -32,8 +34,9 @@ Table of Contents
? 5.3. I just keep getting the 513 RTC driver not running message
? 5.4. I get Could not open /dev/rtc, Device or resource busy in my
syslog file
+ ? 5.5. What if my computer does not have an RTC or backup battery?
o 6. NTP-specific issues
- ? 6.1. Can chronyd be driven from broadcast NTP servers?
+ ? 6.1. Can chronyd be driven from broadcast/multicast NTP servers?
? 6.2. Can chronyd transmit broadcast NTP packets?
? 6.3. Can chronyd keep the system clock a fixed offset away from real
time?
@@ -187,32 +190,50 @@ network latency and stability of the system clock (which mainly depends on the
temperature sensitivity of the crystal oscillator and the maximum rate of the
temperature change).
+Generally, if the sourcestats command usually reports a small number of samples
+retained for a source (e.g. fewer than 16), a shorter polling interval should
+be considered. If the number of samples is usually at the maximum of 64, a
+longer polling interval may work better.
+
An example of the directive for an NTP server on the Internet that you are
allowed to poll frequently could be
server foo.example.net minpoll 4 maxpoll 6 polltarget 16
-An example using very short polling intervals for a server located in the same
+An example using shorter polling intervals with a server located in the same
LAN could be
server ntp.local minpoll 2 maxpoll 4 polltarget 30
-The maxdelay options are useful to ignore measurements with larger delay (e.g.
-due to congestion in the network) and improve the stability of the
+The maxdelay options are useful to ignore measurements with an unusally large
+delay (e.g. due to congestion in the network) and improve the stability of the
synchronisation. The maxdelaydevratio option could be added to the example with
local NTP server
server ntp.local minpoll 2 maxpoll 4 polltarget 30 maxdelaydevratio 2
-If your server supports the interleaved mode, the xleave option should be added
-to the server directive in order to allow the server to send the client more
-accurate hardware or kernel transmit timestamps. When combined with local
-hardware timestamping, sub-microsecond accuracy may be possible. An example
-could be
+If your server supports the interleaved mode (e.g. it is running chronyd), the
+xleave option should be added to the server directive in order to allow the
+server to send the client more accurate transmit timestamps (kernel or
+preferably hardware). For example:
+
+server ntp.local minpoll 2 maxpoll 4 xleave
+
+When combined with local hardware timestamping, good network switches, and even
+shorter polling intervals, a sub-microsecond accuracy and stability of a few
+tens of nanoseconds may be possible. For example:
-server ntp.local minpoll 2 maxpoll 2 xleave
+server ntp.local minpoll 0 maxpoll 0 xleave
hwtimestamp eth0
+If it is acceptable for NTP clients in the network to send requests at an
+excessive rate, a sub-second polling interval may be specified. A median filter
+can be enabled in order to update the clock at a reduced rate with more stable
+measurements. For example:
+
+server ntp.local minpoll -6 maxpoll -6 filter 15 xleave
+hwtimestamp eth0 minpoll -6
+
2.7. Does chronyd have an ntpdate mode?
Yes. With the -q option chronyd will set the system clock once and exit. With
@@ -289,6 +310,45 @@ to
makestep 1 -1
+3.4. Using a Windows NTP server?
+
+A common issue with Windows NTP servers is that they report a very large root
+dispersion (e.g. three seconds or more), which causes chronyd to ignore the
+server for being too inaccurate. The sources command may show a valid
+measurement, but the server is not selected for synchronisation. You can check
+the root dispersion of the server with the chronyc's ntpdata command.
+
+The maxdistance value needs to be increased in chrony.conf to enable
+synchronisation to such a server. For example:
+
+maxdistance 16.0
+
+3.5. 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
+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.
+
+One approach to find out a good value of the offset option is to configure the
+reference clocks with the noselect option and compare them to an NTP server.
+For example, if the sourcestats command showed
+
+Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev
+==============================================================================
+PPS0 0 0 0 +0.000 2000.000 +0ns 4000ms
+NMEA 58 30 231 -96.494 38.406 +504ms 6080us
+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
+able to determine the seconds corresponding to the pulses and allow the samples
+to be used for synchronisation.
+
4. Issues with chronyc
4.1. I keep getting the error 506 Cannot talk to daemon
@@ -391,16 +451,31 @@ For the real-time clock support to work, you need the following three things
Some other program running on the system may be using the device.
+5.5. What if my computer does not have an RTC or backup battery?
+
+In this case you can still use the -s option to set the system clock to the
+last modification time of the drift file, which should correspond to the system
+time when chronyd was previously stopped. The initial system time will be
+increasing across reboots and applications started after chronyd will not
+observe backward steps.
+
6. NTP-specific issues
-6.1. Can chronyd be driven from broadcast NTP servers?
+6.1. Can chronyd be driven from broadcast/multicast NTP servers?
+
+No, the broadcast/multicast client mode is not supported and there is currently
+no plan to implement it. While the mode may be useful to simplify configuration
+of clients in large networks, it is inherently less accurate and less secure
+(even with authentication) than the ordinary client/server mode.
+
+When configuring a large number of clients in a network, it is recommended to
+use the pool directive with a DNS name which resolves to addresses of multiple
+NTP servers. The clients will automatically replace the servers when they
+become unreachable, or otherwise unsuitable for synchronisation, with new
+servers from the pool.
-No, the broadcast client mode is not supported and there is currently no plan
-to implement it. The broadcast and multicast modes are inherently less accurate
-and less secure (even with authentication) than the ordinary server/client mode
-and they are not as useful as they used to be. Even with very modest hardware a
-single NTP server can serve time to hundreds of thousands of clients using the
-ordinary mode.
+Even with very modest hardware, an NTP server can serve time to hundreds of
+thousands of clients using the ordinary client/server mode.
6.2. Can chronyd transmit broadcast NTP packets?
@@ -441,4 +516,4 @@ needs to be made to work as a service.
We have no plans to do this. Anyone is welcome to pick this work up and
contribute it back to the project.
-Last updated 2018-03-15 09:00:47 CET
+Last updated 2018-08-31 10:11:17 CEST
diff --git a/INSTALL b/INSTALL
index d0f17b6..250dd45 100644
--- a/INSTALL
+++ b/INSTALL
@@ -9,8 +9,8 @@ After unpacking the source code, change directory into it, and type
./configure
This is a shell script that automatically determines the system type. There is
-a single optional parameter, --prefix which indicates the directory tree where
-the software should be installed. For example,
+an optional parameter --prefix, which indicates the directory tree where the
+software should be installed. For example,
./configure --prefix=/opt/free
@@ -20,7 +20,7 @@ program into /opt/free/bin. The default value for the prefix is /usr/local.
The configure script assumes you want to use gcc as your compiler. If you want
to use a different compiler, you can configure this way:
-CC=cc CFLAGS=-O ./configure --prefix=/opt/free
+CC=cc ./configure --prefix=/opt/free
for Bourne-family shells, or
@@ -38,6 +38,18 @@ will be built with support for dropping root privileges. On other systems no
extra library is needed. The default user which chronyd should run as can be
specified with the --with-user option of the configure script.
+If development files for the POSIX threads library are available, chronyd will
+be built with support for asynchronous resolving of hostnames specified in the
+server, peer, and pool directives. This allows chronyd operating as a server to
+respond to client requests when resolving a hostname. If you don't want to
+enable the support, specify the --disable-asyncdns flag to configure.
+
+If development files for the Nettle, NSS, or libtomcrypt library are available,
+chronyd will be built with support for other cryptographic hash functions than
+MD5, which can be used for NTP authentication with a symmetric key. If you
+don't want to enable the support, specify the --disable-sechash flag to
+configure.
+
If development files for the editline or readline library are available,
chronyc will be built with line editing support. If you don't want this,
specify the --disable-readline flag to configure.
@@ -47,6 +59,9 @@ will be built with PPS API reference clock driver. If the header is installed
in a location that isn't normally searched by the compiler, you can add it to
the searched locations by setting the CPPFLAGS variable to -I/path/to/timepps.
+The --help option can be specified to configure to print all options supported
+by the script.
+
Now type
make
@@ -84,6 +99,15 @@ unprivileged user for chronyd and specify it with the -u command-line option or
the user directive in the configuration file, or set the default user with the
--with-user configure option before building.
+Support for system call filtering
+
+chronyd can be built with support for the Linux secure computing (seccomp)
+facility. This requires development files for the libseccomp library and the
+--enable-scfilter option specified to configure. The -F option of chronyd will
+enable a system call filter, which should significantly reduce the kernel
+attack surface and possibly prevent kernel exploits from chronyd if it is
+compromised.
+
Support for line editing libraries
chronyc can be built with support for line editing, this allows you to use the
@@ -127,9 +151,9 @@ Extra options for package builders
The configure and make procedures have some extra options that may be useful if
you are building a distribution package for chrony.
-The --mandir=DIR option to configure specifies an install directory for the man
-pages. This overrides the man subdirectory of the argument to the --prefix
-option.
+The --mandir=DIR option to configure specifies an installation directory for
+the man pages. This overrides the man subdirectory of the argument to the
+--prefix option.
./configure --prefix=/usr --mandir=/usr/share/man
@@ -147,4 +171,4 @@ tar cvf - . | gzip -9 > chrony.tar.gz
to build a package. When untarred within the root directory, this will install
the files to the intended final locations.
-Last updated 2018-03-15 09:00:47 CET
+Last updated 2018-08-31 10:11:17 CEST
diff --git a/Makefile.in b/Makefile.in
index 5a4aeee..e748968 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -36,7 +36,7 @@ DESTDIR=
HASH_OBJ = @HASH_OBJ@
OBJS = array.o cmdparse.o conf.o local.o logging.o main.o memory.o \
- reference.o regress.o rtc.o sched.o sources.o sourcestats.o stubs.o \
+ reference.o regress.o rtc.o samplefilt.o sched.o sources.o sourcestats.o stubs.o \
smooth.o sys.o sys_null.o tempcomp.o util.o $(HASH_OBJ)
EXTRA_OBJS=@EXTRA_OBJECTS@
diff --git a/NEWS b/NEWS
index d6ce33a..51a0c22 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,28 @@
+New in version 3.4
+==================
+
+Enhancements
+------------
+* Add filter option to server/pool/peer directive
+* Add minsamples and maxsamples options to hwtimestamp directive
+* Add support for faster frequency adjustments in Linux 4.19
+* Change default pidfile to /var/run/chrony/chronyd.pid to allow
+ chronyd without root privileges to remove it on exit
+* Disable sub-second polling intervals for distant NTP sources
+* Extend range of supported sub-second polling intervals
+* Get/set IPv4 destination/source address of NTP packets on FreeBSD
+* Make burst options and command useful with short polling intervals
+* Modify auto_offline option to activate when sending request failed
+* Respond from interface that received NTP request if possible
+* Add onoffline command to switch between online and offline state
+ according to current system network configuration
+* Improve example NetworkManager dispatcher script
+
+Bug fixes
+---------
+* Avoid waiting in Linux getrandom system call
+* Fix PPS support on FreeBSD and NetBSD
+
New in version 3.3
==================
@@ -19,6 +44,8 @@ Bug fixes
---------
* Respond to NTPv1 client requests with zero mode
* Fix -x option to not require CAP_SYS_TIME under non-root user
+* Fix acquisitionport directive to work with privilege separation
+* Fix handling of socket errors on Linux to avoid high CPU usage
* Fix chronyc to not get stuck in infinite loop after clock step
New in version 3.2
diff --git a/candm.h b/candm.h
index 6ffbb39..cb79739 100644
--- a/candm.h
+++ b/candm.h
@@ -100,7 +100,8 @@
#define REQ_ADD_SERVER3 60
#define REQ_ADD_PEER3 61
#define REQ_SHUTDOWN 62
-#define N_REQUEST_TYPES 63
+#define REQ_ONOFFLINE 63
+#define N_REQUEST_TYPES 64
/* Structure used to exchange timespecs independent of time_t size */
typedef struct {
@@ -275,7 +276,8 @@ typedef struct {
Float asymmetry;
Float offset;
uint32_t flags;
- uint32_t reserved[4];
+ int32_t filter_length;
+ uint32_t reserved[3];
int32_t EOR;
} REQ_NTP_Source;
diff --git a/client.c b/client.c
index 4fa7d2d..5b6056e 100644
--- a/client.c
+++ b/client.c
@@ -4,7 +4,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Lonnie Abelbeck 2016
- * Copyright (C) Miroslav Lichvar 2009-2017
+ * Copyright (C) Miroslav Lichvar 2009-2018
*
* 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
@@ -111,7 +111,7 @@ read_line(void)
char *cmd;
rl_attempted_completion_function = command_name_completion;
- rl_basic_word_break_characters = "\t\n\r";
+ rl_basic_word_break_characters = " \t\n\r";
/* save line only if not empty */
cmd = readline(prompt);
@@ -426,6 +426,14 @@ process_cmd_online(CMD_Request *msg, char *line)
/* ================================================== */
+static void
+process_cmd_onoffline(CMD_Request *msg, char *line)
+{
+ msg->command = htons(REQ_ONOFFLINE);
+}
+
+/* ================================================== */
+
static int
read_address_integer(char *line, IPAddr *address, int *value)
{
@@ -1105,7 +1113,7 @@ process_cmd_add_server_or_peer(CMD_Request *msg, char *line)
msg->data.ntp_source.asymmetry = UTI_FloatHostToNetwork(data.params.asymmetry);
msg->data.ntp_source.offset = UTI_FloatHostToNetwork(data.params.offset);
msg->data.ntp_source.flags = htonl(
- (data.params.online ? REQ_ADDSRC_ONLINE : 0) |
+ (data.params.connectivity == SRC_ONLINE ? REQ_ADDSRC_ONLINE : 0) |
(data.params.auto_offline ? REQ_ADDSRC_AUTOOFFLINE : 0) |
(data.params.iburst ? REQ_ADDSRC_IBURST : 0) |
(data.params.interleaved ? REQ_ADDSRC_INTERLEAVED : 0) |
@@ -1114,6 +1122,7 @@ process_cmd_add_server_or_peer(CMD_Request *msg, char *line)
(data.params.sel_options & SRC_SELECT_NOSELECT ? REQ_ADDSRC_NOSELECT : 0) |
(data.params.sel_options & SRC_SELECT_TRUST ? REQ_ADDSRC_TRUST : 0) |
(data.params.sel_options & SRC_SELECT_REQUIRE ? REQ_ADDSRC_REQUIRE : 0));
+ msg->data.ntp_source.filter_length = htonl(data.params.filter_length);
memset(msg->data.ntp_source.reserved, 0, sizeof (msg->data.ntp_source.reserved));
result = 1;
@@ -1208,6 +1217,8 @@ give_help(void)
"minstratum <address> <stratum>\0Modify minimum stratum\0"
"offline [<mask>/<address>]\0Set sources in subnet to offline status\0"
"online [<mask>/<address>]\0Set sources in subnet to online status\0"
+ "onoffline\0Set all sources to online or offline status\0"
+ "\0according to network configuration\0"
"polltarget <address> <target>\0Modify poll target\0"
"refresh\0Refresh IP addresses\0"
"\0\0"
@@ -1270,30 +1281,52 @@ give_help(void)
/* Tab-completion when editline/readline is available */
#ifdef FEAT_READLINE
+
+enum {
+ TAB_COMPLETE_BASE_CMDS,
+ TAB_COMPLETE_ADD_OPTS,
+ TAB_COMPLETE_MANUAL_OPTS,
+ TAB_COMPLETE_SOURCES_OPTS,
+ TAB_COMPLETE_SOURCESTATS_OPTS,
+ TAB_COMPLETE_MAX_INDEX
+};
+
+static int tab_complete_index;
+
static char *
command_name_generator(const char *text, int state)
{
- const char *name, *names[] = {
- "accheck", "activity", "add peer", "add server", "allow", "burst",
+ const char *name, **names[TAB_COMPLETE_MAX_INDEX];
+ const char *base_commands[] = {
+ "accheck", "activity", "add", "allow", "burst",
"clients", "cmdaccheck", "cmdallow", "cmddeny", "cyclelogs", "delete",
"deny", "dns", "dump", "exit", "help", "keygen", "local", "makestep",
- "manual on", "manual off", "manual delete", "manual list", "manual reset",
- "maxdelay", "maxdelaydevratio", "maxdelayratio", "maxpoll",
- "maxupdateskew", "minpoll", "minstratum", "ntpdata", "offline", "online",
+ "manual", "maxdelay", "maxdelaydevratio", "maxdelayratio", "maxpoll",
+ "maxupdateskew", "minpoll", "minstratum", "ntpdata", "offline", "online", "onoffline",
"polltarget", "quit", "refresh", "rekey", "reselect", "reselectdist",
"retries", "rtcdata", "serverstats", "settime", "shutdown", "smoothing",
- "smoothtime", "sources", "sources -v", "sourcestats", "sourcestats -v",
+ "smoothtime", "sources", "sourcestats",
"timeout", "tracking", "trimrtc", "waitsync", "writertc",
NULL
};
+ const char *add_options[] = { "peer", "server", NULL };
+ const char *manual_options[] = { "on", "off", "delete", "list", "reset", NULL };
+ const char *sources_options[] = { "-v", NULL };
+ const char *sourcestats_options[] = { "-v", NULL };
static int list_index, len;
+ names[TAB_COMPLETE_BASE_CMDS] = base_commands;
+ names[TAB_COMPLETE_ADD_OPTS] = add_options;
+ names[TAB_COMPLETE_MANUAL_OPTS] = manual_options;
+ names[TAB_COMPLETE_SOURCES_OPTS] = sources_options;
+ names[TAB_COMPLETE_SOURCESTATS_OPTS] = sourcestats_options;
+
if (!state) {
list_index = 0;
len = strlen(text);
}
- while ((name = names[list_index++])) {
+ while ((name = names[tab_complete_index][list_index++])) {
if (strncmp(name, text, len) == 0) {
return strdup(name);
}
@@ -1307,7 +1340,25 @@ command_name_generator(const char *text, int state)
static char **
command_name_completion(const char *text, int start, int end)
{
+ char first[32];
+
+ snprintf(first, MIN(sizeof (first), start + 1), "%s", rl_line_buffer);
rl_attempted_completion_over = 1;
+
+ if (!strcmp(first, "add ")) {
+ tab_complete_index = TAB_COMPLETE_ADD_OPTS;
+ } else if (!strcmp(first, "manual ")) {
+ tab_complete_index = TAB_COMPLETE_MANUAL_OPTS;
+ } else if (!strcmp(first, "sources ")) {
+ tab_complete_index = TAB_COMPLETE_SOURCES_OPTS;
+ } else if (!strcmp(first, "sourcestats ")) {
+ tab_complete_index = TAB_COMPLETE_SOURCESTATS_OPTS;
+ } else if (first[0] == '\0') {
+ tab_complete_index = TAB_COMPLETE_BASE_CMDS;
+ } else {
+ return NULL;
+ }
+
return rl_completion_matches(text, command_name_generator);
}
#endif
@@ -1588,17 +1639,17 @@ print_seconds(unsigned long s)
if (s == (uint32_t)-1) {
printf(" -");
} else if (s < 1200) {
- printf("%4ld", s);
+ printf("%4lu", s);
} else if (s < 36000) {
- printf("%3ldm", s / 60);
+ printf("%3lum", s / 60);
} else if (s < 345600) {
- printf("%3ldh", s / 3600);
+ printf("%3luh", s / 3600);
} else {
d = s / 86400;
if (d > 999) {
- printf("%3ldy", d / 365);
+ printf("%3luy", d / 365);
} else {
- printf("%3ldd", d);
+ printf("%3lud", d);
}
}
}
@@ -2840,7 +2891,7 @@ process_cmd_keygen(char *line)
snprintf(hash_name, sizeof (hash_name), "MD5");
#endif
- if (sscanf(line, "%u %16s %d", &id, hash_name, &bits))
+ if (sscanf(line, "%u %16s %u", &id, hash_name, &bits))
;
length = CLAMP(10, (bits + 7) / 8, sizeof (key));
@@ -2984,6 +3035,8 @@ process_line(char *line)
do_normal_submit = process_cmd_offline(&tx_message, line);
} else if (!strcmp(command, "online")) {
do_normal_submit = process_cmd_online(&tx_message, line);
+ } else if (!strcmp(command, "onoffline")) {
+ process_cmd_onoffline(&tx_message, line);
} else if (!strcmp(command, "polltarget")) {
do_normal_submit = process_cmd_polltarget(&tx_message, line);
} else if (!strcmp(command, "quit")) {
@@ -3106,7 +3159,7 @@ static void
display_gpl(void)
{
printf("chrony version %s\n"
- "Copyright (C) 1997-2003, 2007, 2009-2017 Richard P. Curnow and others\n"
+ "Copyright (C) 1997-2003, 2007, 2009-2018 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",
@@ -3206,7 +3259,7 @@ main(int argc, char **argv)
hostnames = DEFAULT_COMMAND_SOCKET",127.0.0.1,::1";
}
- UTI_SetQuitSignalsHandler(signal_handler);
+ UTI_SetQuitSignalsHandler(signal_handler, 0);
sockaddrs = get_sockaddrs(hostnames, port);
diff --git a/cmdmon.c b/cmdmon.c
index 3bd8d91..e069588 100644
--- a/cmdmon.c
+++ b/cmdmon.c
@@ -139,6 +139,7 @@ static const char permissions[] = {
PERMIT_AUTH, /* ADD_SERVER3 */
PERMIT_AUTH, /* ADD_PEER3 */
PERMIT_AUTH, /* SHUTDOWN */
+ PERMIT_AUTH, /* ONOFFLINE */
};
/* ================================================== */
@@ -424,7 +425,7 @@ handle_online(CMD_Request *rx_message, CMD_Reply *tx_message)
UTI_IPNetworkToHost(&rx_message->data.online.mask, &mask);
UTI_IPNetworkToHost(&rx_message->data.online.address, &address);
- if (!NSR_TakeSourcesOnline(&mask, &address))
+ if (!NSR_SetConnectivity(&mask, &address, SRC_ONLINE))
tx_message->status = htons(STT_NOSUCHSOURCE);
}
@@ -437,13 +438,25 @@ handle_offline(CMD_Request *rx_message, CMD_Reply *tx_message)
UTI_IPNetworkToHost(&rx_message->data.offline.mask, &mask);
UTI_IPNetworkToHost(&rx_message->data.offline.address, &address);
- if (!NSR_TakeSourcesOffline(&mask, &address))
+ if (!NSR_SetConnectivity(&mask, &address, SRC_OFFLINE))
tx_message->status = htons(STT_NOSUCHSOURCE);
}
/* ================================================== */
static void
+handle_onoffline(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ IPAddr address, mask;
+
+ address.family = mask.family = IPADDR_UNSPEC;
+ if (!NSR_SetConnectivity(&mask, &address, SRC_MAYBE_ONLINE))
+ ;
+}
+
+/* ================================================== */
+
+static void
handle_burst(CMD_Request *rx_message, CMD_Reply *tx_message)
{
IPAddr address, mask;
@@ -787,6 +800,7 @@ handle_add_source(NTP_Source_Type type, CMD_Request *rx_message, CMD_Reply *tx_m
params.max_sources = ntohl(rx_message->data.ntp_source.max_sources);
params.min_samples = ntohl(rx_message->data.ntp_source.min_samples);
params.max_samples = ntohl(rx_message->data.ntp_source.max_samples);
+ params.filter_length = ntohl(rx_message->data.ntp_source.filter_length);
params.authkey = ntohl(rx_message->data.ntp_source.authkey);
params.max_delay = UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay);
params.max_delay_ratio =
@@ -797,7 +811,8 @@ handle_add_source(NTP_Source_Type type, CMD_Request *rx_message, CMD_Reply *tx_m
params.asymmetry = UTI_FloatNetworkToHost(rx_message->data.ntp_source.asymmetry);
params.offset = UTI_FloatNetworkToHost(rx_message->data.ntp_source.offset);
- params.online = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_ONLINE ? 1 : 0;
+ params.connectivity = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_ONLINE ?
+ SRC_ONLINE : SRC_OFFLINE;
params.auto_offline = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_AUTOOFFLINE ? 1 : 0;
params.iburst = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_IBURST ? 1 : 0;
params.interleaved = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_INTERLEAVED ? 1 : 0;
@@ -1636,6 +1651,10 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
handle_shutdown(&rx_message, &tx_message);
break;
+ case REQ_ONOFFLINE:
+ handle_onoffline(&rx_message, &tx_message);
+ break;
+
default:
DEBUG_LOG("Unhandled command %d", rx_command);
tx_message.status = htons(STT_FAILED);
diff --git a/cmdparse.c b/cmdparse.c
index a228530..6fae81c 100644
--- a/cmdparse.c
+++ b/cmdparse.c
@@ -48,7 +48,7 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
src->port = SRC_DEFAULT_PORT;
src->params.minpoll = SRC_DEFAULT_MINPOLL;
src->params.maxpoll = SRC_DEFAULT_MAXPOLL;
- src->params.online = 1;
+ src->params.connectivity = SRC_ONLINE;
src->params.auto_offline = 0;
src->params.presend_minpoll = SRC_DEFAULT_PRESEND_MINPOLL;
src->params.burst = 0;
@@ -59,6 +59,7 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
src->params.max_sources = SRC_DEFAULT_MAXSOURCES;
src->params.min_samples = SRC_DEFAULT_MINSAMPLES;
src->params.max_samples = SRC_DEFAULT_MAXSAMPLES;
+ src->params.filter_length = 0;
src->params.interleaved = 0;
src->params.sel_options = 0;
src->params.authkey = INACTIVE_AUTHKEY;
@@ -90,7 +91,7 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
} else if (!strcasecmp(cmd, "iburst")) {
src->params.iburst = 1;
} else if (!strcasecmp(cmd, "offline")) {
- src->params.online = 0;
+ src->params.connectivity = SRC_OFFLINE;
} else if (!strcasecmp(cmd, "noselect")) {
src->params.sel_options |= SRC_SELECT_NOSELECT;
} else if (!strcasecmp(cmd, "prefer")) {
@@ -106,6 +107,9 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
} else if (!strcasecmp(cmd, "asymmetry")) {
if (sscanf(line, "%lf%n", &src->params.asymmetry, &n) != 1)
return 0;
+ } else if (!strcasecmp(cmd, "filter")) {
+ if (sscanf(line, "%d%n", &src->params.filter_length, &n) != 1)
+ return 0;
} else if (!strcasecmp(cmd, "maxdelay")) {
if (sscanf(line, "%lf%n", &src->params.max_delay, &n) != 1)
return 0;
diff --git a/conf.c b/conf.c
index 3859d74..85a7f2e 100644
--- a/conf.c
+++ b/conf.c
@@ -1291,6 +1291,8 @@ parse_hwtimestamp(char *line)
iface = ARR_GetNewElement(hwts_interfaces);
iface->name = Strdup(p);
iface->minpoll = 0;
+ iface->min_samples = 2;
+ iface->max_samples = 16;
iface->nocrossts = 0;
iface->rxfilter = CNF_HWTS_RXFILTER_ANY;
iface->precision = 100.0e-9;
@@ -1300,9 +1302,15 @@ parse_hwtimestamp(char *line)
for (p = line; *p; line += n, p = line) {
line = CPS_SplitWord(line);
- if (!strcasecmp(p, "minpoll")) {
+ if (!strcasecmp(p, "maxsamples")) {
+ if (sscanf(line, "%d%n", &iface->max_samples, &n) != 1)
+ break;
+ } else if (!strcasecmp(p, "minpoll")) {
if (sscanf(line, "%d%n", &iface->minpoll, &n) != 1)
break;
+ } else if (!strcasecmp(p, "minsamples")) {
+ if (sscanf(line, "%d%n", &iface->min_samples, &n) != 1)
+ break;
} else if (!strcasecmp(p, "precision")) {
if (sscanf(line, "%lf%n", &iface->precision, &n) != 1)
break;
@@ -1411,7 +1419,7 @@ CNF_AddInitSources(void)
ntp_addr.ip_addr = *(IPAddr *)ARR_GetElement(init_sources, i);
ntp_addr.port = cps_source.port;
cps_source.params.iburst = 1;
- cps_source.params.online = 0;
+ cps_source.params.connectivity = SRC_OFFLINE;
NSR_AddSource(&ntp_addr, NTP_SERVER, &cps_source.params);
}
diff --git a/conf.h b/conf.h
index 25c98e1..43217fc 100644
--- a/conf.h
+++ b/conf.h
@@ -128,6 +128,8 @@ typedef enum {
typedef struct {
char *name;
int minpoll;
+ int min_samples;
+ int max_samples;
int nocrossts;
CNF_HwTs_RxFilter rxfilter;
double precision;
diff --git a/configure b/configure
index a97ac78..33aacdf 100755
--- a/configure
+++ b/configure
@@ -5,7 +5,7 @@
#
# Copyright (C) Richard P. Curnow 1997-2003
# Copyright (C) Bryan Christianson 2016
-# Copyright (C) Miroslav Lichvar 2009, 2012-2016
+# Copyright (C) Miroslav Lichvar 2009, 2012-2018
#
# =======================================================================
@@ -108,7 +108,7 @@ For better control, use the options below.
since 1970-01-01 [50*365 days ago]
--with-user=USER Specify default chronyd user [root]
--with-hwclockfile=PATH Specify default path to hwclock(8) adjtime file
- --with-pidfile=PATH Specify default pidfile [/var/run/chronyd.pid]
+ --with-pidfile=PATH Specify default pidfile [/var/run/chrony/chronyd.pid]
--with-rtcdevice=PATH Specify default path to RTC device [/dev/rtc]
--with-sendmail=PATH Path to sendmail binary [/usr/lib/sendmail]
--enable-debug Enable debugging support
@@ -229,7 +229,7 @@ feat_ntp_signd=0
ntp_era_split=""
default_user="root"
default_hwclockfile=""
-default_pidfile="/var/run/chronyd.pid"
+default_pidfile="/var/run/chrony/chronyd.pid"
default_rtcdevice="/dev/rtc"
mail_program="/usr/lib/sendmail"
@@ -595,14 +595,6 @@ else
fi
fi
-if test_code '<stdint.h>' 'stdint.h' '' '' ''; then
- add_def HAVE_STDINT_H
-fi
-
-if test_code '<inttypes.h>' 'inttypes.h' '' '' ''; then
- add_def HAVE_INTTYPES_H
-fi
-
if test_code 'struct in_pktinfo' 'sys/socket.h netinet/in.h' '' '' '
struct in_pktinfo ipi;
return sizeof (ipi.ipi_spec_dst.s_addr) + IP_PKTINFO;'
@@ -715,11 +707,11 @@ fi
timepps_h=""
if [ $feat_refclock = "1" ] && [ $feat_pps = "1" ]; then
- if test_code '<sys/timepps.h>' 'sys/timepps.h' '' '' ''; then
+ if test_code '<sys/timepps.h>' 'inttypes.h time.h sys/timepps.h' '' '' ''; then
timepps_h="sys/timepps.h"
add_def HAVE_SYS_TIMEPPS_H
else
- if test_code '<timepps.h>' 'timepps.h' '' '' ''; then
+ if test_code '<timepps.h>' 'inttypes.h time.h timepps.h' '' '' ''; then
timepps_h="timepps.h"
add_def HAVE_TIMEPPS_H
fi
@@ -727,7 +719,7 @@ if [ $feat_refclock = "1" ] && [ $feat_pps = "1" ]; then
fi
if [ "x$timepps_h" != "x" ] && \
- test_code 'PPSAPI' "string.h $timepps_h" '' '' '
+ test_code 'PPSAPI' "inttypes.h string.h time.h $timepps_h" '' '' '
pps_handle_t h = 0;
pps_info_t i;
struct timespec ts;
@@ -967,9 +959,9 @@ add_def DEFAULT_USER "\"$default_user\""
add_def DEFAULT_COMMAND_SOCKET "\"$CHRONYRUNDIR/chronyd.sock\""
add_def MAIL_PROGRAM "\"$mail_program\""
-common_features="`get_features IPV6 DEBUG`"
+common_features="`get_features SECHASH IPV6 DEBUG`"
chronyc_features="`get_features READLINE`"
-chronyd_features="`get_features CMDMON NTP REFCLOCK RTC PRIVDROP SCFILTER SECHASH SIGND ASYNCDNS`"
+chronyd_features="`get_features CMDMON NTP REFCLOCK RTC PRIVDROP SCFILTER SIGND ASYNCDNS`"
add_def CHRONYC_FEATURES "\"$chronyc_features $common_features\""
add_def CHRONYD_FEATURES "\"$chronyd_features $common_features\""
echo "Features : $chronyd_features $chronyc_features $common_features"
diff --git a/doc/chrony.conf.adoc b/doc/chrony.conf.adoc
index d89b529..4a39c21 100644
--- a/doc/chrony.conf.adoc
+++ b/doc/chrony.conf.adoc
@@ -69,38 +69,44 @@ options:
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 -4 (1/16th of a second), and the maximum is 24 (6
+seconds), the minimum is -6 (1/64th 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.
+abuse. A sub-second interval will be enabled only when the server is reachable
+and the round-trip delay is shorter than 10 milliseconds, i.e. the server
+should be in a local network.
*maxpoll* _poll_:::
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 0 (1 second), and the maximum is 24 (6 months).
+seconds), the minimum is -6 (1/64th of a second), and the maximum is 24 (6
+months).
*iburst*:::
With this option, the interval between the first four requests sent to the
-server will be 2 seconds instead of the interval specified by the *minpoll*
-option, which allows *chronyd* to make the first update of the clock shortly
-after start.
+server will be 2 seconds or less instead of the interval specified by the
+*minpoll* option, which allows *chronyd* to make the first update of the clock
+shortly after start.
*burst*:::
With this option, *chronyd* will shorten the interval between up to four
-requests to 2 seconds when it cannot get a good measurement from the server.
-The number of requests in the burst is limited by the current polling interval
-to keep the average interval at or above the minimum interval, i.e. the current
-interval needs to be at least two times longer than the minimum interval in
-order to allow a burst with two requests.
+requests to 2 seconds or less when it cannot get a good measurement from the
+server. The number of requests in the burst is limited by the current polling
+interval to keep the average interval at or above the minimum interval, i.e.
+the current interval needs to be at least two times longer than the minimum
+interval in order to allow a burst with two requests.
*key* _ID_:::
-The NTP protocol supports the inclusion of checksums in the packets, to prevent
+The NTP protocol supports a message authentication code (MAC) to prevent
computers having their system time upset by rogue packets being sent to them.
-The checksums are generated as a function of a password, using the
-cryptographic hash function set in the key file, which is specified by the
-<<keyfile,*keyfile*>> directive.
+The MAC is generated as a function of a password specified in the key file,
+which is specified by the <<keyfile,*keyfile*>> directive.
+
The *key* option specifies which key (with an ID in the range 1 through 2^32-1)
should *chronyd* use to authenticate requests sent to the server and verify its
responses. The server must have the same key for this number configured,
otherwise no relationship between the computers will be possible.
++
+If the server is running *ntpd* and the output size of the hash function used
+by the key is longer than 160 bits (e.g. SHA256), the *version* option needs to
+be set to 4 for compatibility.
*maxdelay* _delay_:::
*chronyd* uses the network round-trip delay to the server to determine how
accurate a particular measurement is likely to be. Long round-trip delays
@@ -155,19 +161,23 @@ 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_:::
+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.
*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
enabled to do so (by using the <<chronyc.adoc#online,*online*>> command in
*chronyc*).
*auto_offline*:::
-With this option, the server will be assumed to have gone offline when two
-requests have been sent to it without receiving a response. This option avoids
+With this option, the server will be assumed to have gone offline when sending
+a request fails, e.g. due to a missing route to the network. This option avoids
the need to run the <<chronyc.adoc#offline,*offline*>> command from *chronyc*
-when disconnecting the network link, if it is safe to assume that the requests
-and responses will not be dropped in the network, e.g. in a trusted local
-network. (It will still be necessary to use the <<chronyc.adoc#online,*online*>>
-command when the link has been established, to enable measurements to start.)
+when disconnecting the network link. (It will still be necessary to use the
+<<chronyc.adoc#online,*online*>> command when the link has been established, to
+enable measurements to start.)
*prefer*:::
Prefer this source over sources without the *prefer* option.
*noselect*:::
@@ -768,9 +778,10 @@ driftfile @CHRONYVARDIR@/drift
[[fallbackdrift]]*fallbackdrift* _min-interval_ _max-interval_::
Fallback drifts are long-term averages of the system clock drift calculated
-over exponentially increasing intervals. They are used when the clock is no
-longer synchronised to avoid quickly drifting away from true time if there was
-a short-term deviation in the drift before the synchronisation was lost.
+over exponentially increasing intervals. They are used to avoid quickly
+drifting away from true time when the clock was not updated for a longer period
+of time and there was a short-term deviation in the drift before the updates
+stopped.
+
The directive specifies the minimum and maximum interval since the last clock
update to switch between fallback drifts. They are defined as a power of 2 (in
@@ -782,8 +793,10 @@ fallbackdrift 16 19
+
In this example, the minimum interval is 16 (18 hours) and the maximum interval is
19 (6 days). The system clock frequency will be set to the first fallback 18
-hours after last clock update, to the second after 36 hours, etc. This might be
-a good setting to cover daily and weekly temperature fluctuations.
+hours after last clock update, to the second after 36 hours, and so on. This
+might be a good setting to cover frequency changes due to daily and weekly
+temperature fluctuations. When the frequency is set to a fallback, the state of
+the clock will change to '`Not synchronised`'.
+
By default (or if the specified maximum or minimum is 0), no fallbacks are used
and the clock frequency changes only with new measurements from NTP sources,
@@ -1932,6 +1945,12 @@ It's defined as a power of two. It should correspond to the minimum polling
interval of all NTP sources and the minimum expected polling interval of NTP
clients. The default value is 0 (1 second) and the minimum value is -6 (1/64th
of a second).
+*minsamples* _samples_:::
+This option specifies the minimum number of readings kept for tracking of the
+NIC clock. The default value is 2.
+*maxsamples* _samples_:::
+This option specifies the maximum number of readings kept for tracking of the
+NIC clock. The default value is 16.
*precision* _precision_:::
This option specifies the assumed precision of reading of the NIC clock. The
default value is 100e-9 (100 nanoseconds).
@@ -2040,10 +2059,11 @@ significant impact on performance as *chronyd's* memory usage is modest. The
*mlockall(2)* man page has more details.
[[pidfile]]*pidfile* _file_::
-*chronyd* always writes its process ID (PID) to a file, and checks this file on
-startup to see if another *chronyd* might already be running on the system. By
-default, the file used is _@DEFAULT_PID_FILE@_. The *pidfile* directive
-allows the name to be changed, e.g.:
+Unless *chronyd* is started with the *-Q* option, it writes its process ID
+(PID) to a file, and checks this file on startup to see if another *chronyd*
+might already be running on the system. By default, the file used is
+_@DEFAULT_PID_FILE@_. The *pidfile* directive allows the name to be changed,
+e.g.:
+
----
pidfile /run/chronyd.pid
diff --git a/doc/chrony.conf.man.in b/doc/chrony.conf.man.in
index 9f65136..28fb647 100644
--- a/doc/chrony.conf.man.in
+++ b/doc/chrony.conf.man.in
@@ -2,12 +2,12 @@
.\" Title: chrony.conf
.\" Author: [see the "AUTHORS" section]
.\" Generator: Asciidoctor 1.5.6.1
-.\" Date: 2018-03-15
+.\" Date: 2018-08-31
.\" Manual: Configuration Files
.\" Source: chrony @CHRONY_VERSION@
.\" Language: English
.\"
-.TH "CHRONY.CONF" "5" "2018-03-15" "chrony @CHRONY_VERSION@" "Configuration Files"
+.TH "CHRONY.CONF" "5" "2018-08-31" "chrony @CHRONY_VERSION@" "Configuration Files"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
@@ -63,10 +63,12 @@ options:
This option specifies the minimum interval between requests sent to the server
as a power of 2 in seconds. For example, \fBminpoll 5\fP would mean that the
polling interval should not drop below 32 seconds. The default is 6 (64
-seconds), the minimum is \-4 (1/16th of a second), and the maximum is 24 (6
+seconds), the minimum is \-6 (1/64th 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.
+abuse. A sub\-second interval will be enabled only when the server is reachable
+and the round\-trip delay is shorter than 10 milliseconds, i.e. the server
+should be in a local network.
.RE
.sp
\fBmaxpoll\fP \fIpoll\fP
@@ -74,39 +76,43 @@ abuse.
This option specifies the maximum interval between requests sent to the server
as a power of 2 in seconds. For example, \fBmaxpoll 9\fP indicates that the polling
interval should stay at or below 9 (512 seconds). The default is 10 (1024
-seconds), the minimum is 0 (1 second), and the maximum is 24 (6 months).
+seconds), the minimum is \-6 (1/64th of a second), and the maximum is 24 (6
+months).
.RE
.sp
\fBiburst\fP
.RS 4
With this option, the interval between the first four requests sent to the
-server will be 2 seconds instead of the interval specified by the \fBminpoll\fP
-option, which allows \fBchronyd\fP to make the first update of the clock shortly
-after start.
+server will be 2 seconds or less instead of the interval specified by the
+\fBminpoll\fP option, which allows \fBchronyd\fP to make the first update of the clock
+shortly after start.
.RE
.sp
\fBburst\fP
.RS 4
With this option, \fBchronyd\fP will shorten the interval between up to four
-requests to 2 seconds when it cannot get a good measurement from the server.
-The number of requests in the burst is limited by the current polling interval
-to keep the average interval at or above the minimum interval, i.e. the current
-interval needs to be at least two times longer than the minimum interval in
-order to allow a burst with two requests.
+requests to 2 seconds or less when it cannot get a good measurement from the
+server. The number of requests in the burst is limited by the current polling
+interval to keep the average interval at or above the minimum interval, i.e.
+the current interval needs to be at least two times longer than the minimum
+interval in order to allow a burst with two requests.
.RE
.sp
\fBkey\fP \fIID\fP
.RS 4
-The NTP protocol supports the inclusion of checksums in the packets, to prevent
+The NTP protocol supports a message authentication code (MAC) to prevent
computers having their system time upset by rogue packets being sent to them.
-The checksums are generated as a function of a password, using the
-cryptographic hash function set in the key file, which is specified by the
-\fBkeyfile\fP directive.
+The MAC is generated as a function of a password specified in the key file,
+which is specified by the \fBkeyfile\fP directive.
.sp
The \fBkey\fP option specifies which key (with an ID in the range 1 through 2^32\-1)
should \fBchronyd\fP use to authenticate requests sent to the server and verify its
responses. The server must have the same key for this number configured,
otherwise no relationship between the computers will be possible.
+.sp
+If the server is running \fBntpd\fP and the output size of the hash function used
+by the key is longer than 160 bits (e.g. SHA256), the \fBversion\fP option needs to
+be set to 4 for compatibility.
.RE
.sp
\fBmaxdelay\fP \fIdelay\fP
@@ -187,6 +193,14 @@ Set the maximum number of samples kept for this source. This overrides the
\fBmaxsamples\fP directive.
.RE
.sp
+\fBfilter\fP \fIsamples\fP
+.RS 4
+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.
+.RE
+.sp
\fBoffline\fP
.RS 4
If the server will not be reachable when \fBchronyd\fP is started, the \fBoffline\fP
@@ -197,13 +211,12 @@ enabled to do so (by using the \fBonline\fP command in
.sp
\fBauto_offline\fP
.RS 4
-With this option, the server will be assumed to have gone offline when two
-requests have been sent to it without receiving a response. This option avoids
+With this option, the server will be assumed to have gone offline when sending
+a request fails, e.g. due to a missing route to the network. This option avoids
the need to run the \fBoffline\fP command from \fBchronyc\fP
-when disconnecting the network link, if it is safe to assume that the requests
-and responses will not be dropped in the network, e.g. in a trusted local
-network. (It will still be necessary to use the \fBonline\fP
-command when the link has been established, to enable measurements to start.)
+when disconnecting the network link. (It will still be necessary to use the
+\fBonline\fP command when the link has been established, to
+enable measurements to start.)
.RE
.sp
\fBprefer\fP
@@ -1029,9 +1042,10 @@ driftfile @CHRONYVARDIR@/drift
\fBfallbackdrift\fP \fImin\-interval\fP \fImax\-interval\fP
.RS 4
Fallback drifts are long\-term averages of the system clock drift calculated
-over exponentially increasing intervals. They are used when the clock is no
-longer synchronised to avoid quickly drifting away from true time if there was
-a short\-term deviation in the drift before the synchronisation was lost.
+over exponentially increasing intervals. They are used to avoid quickly
+drifting away from true time when the clock was not updated for a longer period
+of time and there was a short\-term deviation in the drift before the updates
+stopped.
.sp
The directive specifies the minimum and maximum interval since the last clock
update to switch between fallback drifts. They are defined as a power of 2 (in
@@ -1049,8 +1063,10 @@ fallbackdrift 16 19
.sp
In this example, the minimum interval is 16 (18 hours) and the maximum interval is
19 (6 days). The system clock frequency will be set to the first fallback 18
-hours after last clock update, to the second after 36 hours, etc. This might be
-a good setting to cover daily and weekly temperature fluctuations.
+hours after last clock update, to the second after 36 hours, and so on. This
+might be a good setting to cover frequency changes due to daily and weekly
+temperature fluctuations. When the frequency is set to a fallback, the state of
+the clock will change to \(oqNot synchronised\(cq.
.sp
By default (or if the specified maximum or minimum is 0), no fallbacks are used
and the clock frequency changes only with new measurements from NTP sources,
@@ -3262,6 +3278,18 @@ clients. The default value is 0 (1 second) and the minimum value is \-6 (1/64th
of a second).
.RE
.sp
+\fBminsamples\fP \fIsamples\fP
+.RS 4
+This option specifies the minimum number of readings kept for tracking of the
+NIC clock. The default value is 2.
+.RE
+.sp
+\fBmaxsamples\fP \fIsamples\fP
+.RS 4
+This option specifies the maximum number of readings kept for tracking of the
+NIC clock. The default value is 16.
+.RE
+.sp
\fBprecision\fP \fIprecision\fP
.RS 4
This option specifies the assumed precision of reading of the NIC clock. The
@@ -3431,10 +3459,11 @@ significant impact on performance as \fBchronyd\(cqs\fP memory usage is modest.
.sp
\fBpidfile\fP \fIfile\fP
.RS 4
-\fBchronyd\fP always writes its process ID (PID) to a file, and checks this file on
-startup to see if another \fBchronyd\fP might already be running on the system. By
-default, the file used is \fI@DEFAULT_PID_FILE@\fP. The \fBpidfile\fP directive
-allows the name to be changed, e.g.:
+Unless \fBchronyd\fP is started with the \fB\-Q\fP option, it writes its process ID
+(PID) to a file, and checks this file on startup to see if another \fBchronyd\fP
+might already be running on the system. By default, the file used is
+\fI@DEFAULT_PID_FILE@\fP. The \fBpidfile\fP directive allows the name to be changed,
+e.g.:
.sp
.if n \{\
.RS 4
diff --git a/doc/chronyc.adoc b/doc/chronyc.adoc
index c987907..b80cc1c 100644
--- a/doc/chronyc.adoc
+++ b/doc/chronyc.adoc
@@ -338,8 +338,9 @@ 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.
*LastRx*:::
-This column shows how long ago the last sample was received from the source.
-This is normally in seconds. The letters _m_, _h_, _d_ or _y_ indicate
+This column shows how long ago the last good sample (which is shown in the next
+column) was received from the source. Measurements that failed some tests are
+ignored. This is normally in seconds. The letters _m_, _h_, _d_ or _y_ indicate
minutes, hours, days, or years.
*Last sample*:::
This column shows the offset between the local clock and the source at the
@@ -722,6 +723,13 @@ particular source or sources has been restored.
+
The syntax is identical to that of the <<offline,*offline*>> command.
+[[onoffline]]
+*onoffline*::
+The *onoffline* command tells *chronyd* to switch all sources to the online or
+offline status according to the current network configuration. A source is
+considered online if it is possible to send requests to it, i.e. a route to the
+network is present.
+
[[polltarget]]*polltarget* _address_ _polltarget_::
The *polltarget* command is used to modify the poll target for one of the
current set of sources. It is equivalent to the *polltarget* option in the
diff --git a/doc/chronyc.man.in b/doc/chronyc.man.in
index 581fa00..5c76fe4 100644
--- a/doc/chronyc.man.in
+++ b/doc/chronyc.man.in
@@ -2,12 +2,12 @@
.\" Title: chronyc
.\" Author: [see the "AUTHORS" section]
.\" Generator: Asciidoctor 1.5.6.1
-.\" Date: 2018-03-15
+.\" Date: 2018-08-31
.\" Manual: User manual
.\" Source: chrony @CHRONY_VERSION@
.\" Language: English
.\"
-.TH "CHRONYC" "1" "2018-03-15" "chrony @CHRONY_VERSION@" "User manual"
+.TH "CHRONYC" "1" "2018-08-31" "chrony @CHRONY_VERSION@" "User manual"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
@@ -504,8 +504,9 @@ from the last eight transmissions.
.sp
\fBLastRx\fP
.RS 4
-This column shows how long ago the last sample was received from the source.
-This is normally in seconds. The letters \fIm\fP, \fIh\fP, \fId\fP or \fIy\fP indicate
+This column shows how long ago the last good sample (which is shown in the next
+column) was received from the source. Measurements that failed some tests are
+ignored. This is normally in seconds. The letters \fIm\fP, \fIh\fP, \fId\fP or \fIy\fP indicate
minutes, hours, days, or years.
.RE
.sp
@@ -1061,6 +1062,14 @@ particular source or sources has been restored.
The syntax is identical to that of the \fBoffline\fP command.
.RE
.sp
+\fBonoffline\fP
+.RS 4
+The \fBonoffline\fP command tells \fBchronyd\fP to switch all sources to the online or
+offline status according to the current network configuration. A source is
+considered online if it is possible to send requests to it, i.e. a route to the
+network is present.
+.RE
+.sp
\fBpolltarget\fP \fIaddress\fP \fIpolltarget\fP
.RS 4
The \fBpolltarget\fP command is used to modify the poll target for one of the
diff --git a/doc/chronyd.adoc b/doc/chronyd.adoc
index fc79078..d3b78a4 100644
--- a/doc/chronyd.adoc
+++ b/doc/chronyd.adoc
@@ -81,9 +81,9 @@ started without root privileges.
*-r*::
This option will try to reload and then delete files containing sample
-histories for each of the servers and reference clocks being used. These
-histories are created by using the <<chronyc.adoc#dump,*dump*>> command in
-*chronyc*, or by setting the <<chrony.conf.adoc#dumponexit,*dumponexit*>>
+histories for each of the servers and reference clocks being used. The
+files are expected to be in the directory specified by the
+<<chrony.conf.adoc#dumpdir,*dumpdir*>>
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
diff --git a/doc/chronyd.man.in b/doc/chronyd.man.in
index f1f7c90..a95de6a 100644
--- a/doc/chronyd.man.in
+++ b/doc/chronyd.man.in
@@ -2,12 +2,12 @@
.\" Title: chronyd
.\" Author: [see the "AUTHORS" section]
.\" Generator: Asciidoctor 1.5.6.1
-.\" Date: 2018-03-15
+.\" Date: 2018-08-31
.\" Manual: System Administration
.\" Source: chrony @CHRONY_VERSION@
.\" Language: English
.\"
-.TH "CHRONYD" "8" "2018-03-15" "chrony @CHRONY_VERSION@" "System Administration"
+.TH "CHRONYD" "8" "2018-08-31" "chrony @CHRONY_VERSION@" "System Administration"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
@@ -91,9 +91,9 @@ started without root privileges.
\fB\-r\fP
.RS 4
This option will try to reload and then delete files containing sample
-histories for each of the servers and reference clocks being used. These
-histories are created by using the \fBdump\fP command in
-\fBchronyc\fP, or by setting the \fBdumponexit\fP
+histories for each of the servers and reference clocks being used. The
+files are expected to be in the directory specified by the
+\fBdumpdir\fP
directive in the configuration file. This option is useful if you want to stop
and restart \fBchronyd\fP 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
diff --git a/doc/faq.adoc b/doc/faq.adoc
index 0b76dfe..8e6926b 100644
--- a/doc/faq.adoc
+++ b/doc/faq.adoc
@@ -171,6 +171,11 @@ network latency and stability of the system clock (which mainly depends on the
temperature sensitivity of the crystal oscillator and the maximum rate of the
temperature change).
+Generally, if the `sourcestats` command usually reports a small number of
+samples retained for a source (e.g. fewer than 16), a shorter polling interval
+should be considered. If the number of samples is usually at the maximum of 64,
+a longer polling interval may work better.
+
An example of the directive for an NTP server on the Internet that you are
allowed to poll frequently could be
@@ -178,15 +183,15 @@ allowed to poll frequently could be
server foo.example.net minpoll 4 maxpoll 6 polltarget 16
----
-An example using very short polling intervals for a server located in the same
+An example using shorter polling intervals with a server located in the same
LAN could be
----
server ntp.local minpoll 2 maxpoll 4 polltarget 30
----
-The maxdelay options are useful to ignore measurements with larger delay (e.g.
-due to congestion in the network) and improve the stability of the
+The maxdelay options are useful to ignore measurements with an unusally large
+delay (e.g. due to congestion in the network) and improve the stability of the
synchronisation. The `maxdelaydevratio` option could be added to the example
with local NTP server
@@ -194,17 +199,34 @@ with local NTP server
server ntp.local minpoll 2 maxpoll 4 polltarget 30 maxdelaydevratio 2
----
-If your server supports the interleaved mode, the `xleave` option should be
-added to the `server` directive in order to allow the server to send the
-client more accurate hardware or kernel transmit timestamps. When combined with
-local hardware timestamping, sub-microsecond accuracy may be possible. An
-example could be
+If your server supports the interleaved mode (e.g. it is running `chronyd`),
+the `xleave` option should be added to the `server` directive in order to allow
+the server to send the client more accurate transmit timestamps (kernel or
+preferably hardware). For example:
----
-server ntp.local minpoll 2 maxpoll 2 xleave
+server ntp.local minpoll 2 maxpoll 4 xleave
+----
+
+When combined with local hardware timestamping, good network switches, and even
+shorter polling intervals, a sub-microsecond accuracy and stability of a few
+tens of nanoseconds may be possible. For example:
+
+----
+server ntp.local minpoll 0 maxpoll 0 xleave
hwtimestamp eth0
----
+If it is acceptable for NTP clients in the network to send requests at an
+excessive rate, a sub-second polling interval may be specified. A median filter
+can be enabled in order to update the clock at a reduced rate with more stable
+measurements. For example:
+
+----
+server ntp.local minpoll -6 maxpoll -6 filter 15 xleave
+hwtimestamp eth0 minpoll -6
+----
+
=== Does `chronyd` have an ntpdate mode?
Yes. With the `-q` option `chronyd` will set the system clock once and exit.
@@ -292,6 +314,49 @@ to
makestep 1 -1
----
+=== Using a Windows NTP server?
+
+A common issue with Windows NTP servers is that they report a very large root
+dispersion (e.g. three seconds or more), which causes `chronyd` to ignore the
+server for being too inaccurate. The `sources` command may show a valid
+measurement, but the server is not selected for synchronisation. You can check
+the root dispersion of the server with the ``chronyc``'s `ntpdata` command.
+
+The `maxdistance` value needs to be increased in _chrony.conf_ to enable
+synchronisation to such a server. For example:
+
+----
+maxdistance 16.0
+----
+
+=== 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
+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.
+
+One approach to find out a good value of the `offset` option is to configure
+the reference clocks with the `noselect` option and compare them to an NTP
+server. For example, if the `sourcestats` command showed
+
+----
+Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev
+==============================================================================
+PPS0 0 0 0 +0.000 2000.000 +0ns 4000ms
+NMEA 58 30 231 -96.494 38.406 +504ms 6080us
+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
+able to determine the seconds corresponding to the pulses and allow the samples
+to be used for synchronisation.
+
== Issues with `chronyc`
=== I keep getting the error `506 Cannot talk to daemon`
@@ -393,16 +458,31 @@ things
Some other program running on the system may be using the device.
+=== What if my computer does not have an RTC or backup battery?
+
+In this case you can still use the `-s` option to set the system clock to the
+last modification time of the drift file, which should correspond to the system
+time when `chronyd` was previously stopped. The initial system time will be
+increasing across reboots and applications started after `chronyd` will not
+observe backward steps.
+
== NTP-specific issues
-=== Can `chronyd` be driven from broadcast NTP servers?
+=== Can `chronyd` be driven from broadcast/multicast NTP servers?
+
+No, the broadcast/multicast client mode is not supported and there is currently
+no plan to implement it. While the mode may be useful to simplify configuration
+of clients in large networks, it is inherently less accurate and less secure
+(even with authentication) than the ordinary client/server mode.
+
+When configuring a large number of clients in a network, it is recommended to
+use the `pool` directive with a DNS name which resolves to addresses of
+multiple NTP servers. The clients will automatically replace the servers when
+they become unreachable, or otherwise unsuitable for synchronisation, with new
+servers from the pool.
-No, the broadcast client mode is not supported and there is currently no plan
-to implement it. The broadcast and multicast modes are inherently less
-accurate and less secure (even with authentication) than the ordinary
-server/client mode and they are not as useful as they used to be. Even with
-very modest hardware a single NTP server can serve time to hundreds of
-thousands of clients using the ordinary mode.
+Even with very modest hardware, an NTP server can serve time to hundreds of
+thousands of clients using the ordinary client/server mode.
=== Can `chronyd` transmit broadcast NTP packets?
diff --git a/doc/installation.adoc b/doc/installation.adoc
index 151674a..eea9088 100644
--- a/doc/installation.adoc
+++ b/doc/installation.adoc
@@ -29,8 +29,8 @@ After unpacking the source code, change directory into it, and type
----
This is a shell script that automatically determines the system type. There is
-a single optional parameter, `--prefix` which indicates the directory tree
-where the software should be installed. For example,
+an optional parameter `--prefix`, which indicates the directory tree where the
+software should be installed. For example,
----
./configure --prefix=/opt/free
@@ -40,11 +40,11 @@ will install the `chronyd` daemon into `/opt/free/sbin` and the `chronyc`
control program into `/opt/free/bin`. The default value for the prefix is
`/usr/local`.
-The configure script assumes you want to use gcc as your compiler. If you want
-to use a different compiler, you can configure this way:
+The `configure` script assumes you want to use `gcc` as your compiler. If you
+want to use a different compiler, you can configure this way:
----
-CC=cc CFLAGS=-O ./configure --prefix=/opt/free
+CC=cc ./configure --prefix=/opt/free
----
for Bourne-family shells, or
@@ -63,11 +63,26 @@ shown. Otherwise, `Makefile` will be generated.
On Linux, if development files for the libcap library are available, `chronyd`
will be built with support for dropping root privileges. On other systems no
extra library is needed. The default user which `chronyd` should run as can be
-specified with the `--with-user` option of the configure script.
+specified with the `--with-user` option of the `configure` script.
+
+If development files for the POSIX threads library are available, `chronyd`
+will be built with support for asynchronous resolving of hostnames specified in
+the `server`, `peer`, and `pool` directives. This allows `chronyd` operating as
+a server to respond to client requests when resolving a hostname. If you don't
+want to enable the support, specify the `--disable-asyncdns` flag to
+`configure`.
+
+If development files for the https://www.lysator.liu.se/~nisse/nettle/[Nettle],
+https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS[NSS], or
+http://www.libtom.net/LibTomCrypt/[libtomcrypt] library are available,
+`chronyd` will be built with support for other cryptographic hash functions
+than MD5, which can be used for NTP authentication with a symmetric key. If you
+don't want to enable the support, specify the `--disable-sechash` flag to
+`configure`.
If development files for the editline or readline library are available,
`chronyc` will be built with line editing support. If you don't want this,
-specify the `--disable-readline` flag to configure.
+specify the `--disable-readline` flag to `configure`.
If a `timepps.h` header is available (e.g. from the
http://linuxpps.org[LinuxPPS project]), `chronyd` will be built with PPS API
@@ -75,6 +90,9 @@ reference clock driver. If the header is installed in a location that isn't
normally searched by the compiler, you can add it to the searched locations by
setting the `CPPFLAGS` variable to `-I/path/to/timepps`.
+The `--help` option can be specified to `configure` to print all options
+supported by the script.
+
Now type
----
@@ -122,6 +140,16 @@ unprivileged user for `chronyd` and specify it with the `-u` command-line
option or the `user` directive in the configuration file, or set the default
user with the `--with-user` configure option before building.
+== Support for system call filtering
+
+`chronyd` can be built with support for the Linux secure computing (seccomp)
+facility. This requires development files for the
+https://github.com/seccomp/libseccomp[libseccomp] library and the
+`--enable-scfilter` option specified to `configure`. The `-F` option of
+`chronyd` will enable a system call filter, which should significantly reduce
+the kernel attack surface and possibly prevent kernel exploits from `chronyd`
+if it is compromised.
+
== Support for line editing libraries
`chronyc` can be built with support for line editing, this allows you to use
@@ -132,12 +160,12 @@ Please note that readline since version 6.0 is licensed under GPLv3+ which is
incompatible with chrony's license GPLv2. You should use editline instead if
you don't want to use older readline versions.
-The configure script will automatically enable the line editing support if one
-of the supported libraries is available. If they are both available, the
+The `configure` script will automatically enable the line editing support if
+one of the supported libraries is available. If they are both available, the
editline library will be used.
-If you don't want to use it (in which case chronyc will use a minimal command
-line interface), invoke configure like this:
+If you don't want to use it (in which case `chronyc` will use a minimal command
+line interface), invoke `configure` like this:
----
./configure --disable-readline other-options...
@@ -161,12 +189,12 @@ normally searched by the compiler and linker, you need to use extra options:
== Extra options for package builders
-The configure and make procedures have some extra options that may be useful if
-you are building a distribution package for chrony.
+The `configure` and `make` procedures have some extra options that may be
+useful if you are building a distribution package for `chrony`.
-The `--mandir=DIR` option to configure specifies an install directory for the
-man pages. This overrides the `man` subdirectory of the argument to the
---prefix option.
+The `--mandir=DIR` option to `configure` specifies an installation directory
+for the man pages. This overrides the `man` subdirectory of the argument to the
+`--prefix` option.
----
./configure --prefix=/usr --mandir=/usr/share/man
@@ -174,8 +202,8 @@ man pages. This overrides the `man` subdirectory of the argument to the
to set both options together.
-The final option is the `DESTDIR` option to the make command. For example, you
-could use the commands
+The final option is the `DESTDIR` option to the `make` command. For example,
+you could use the commands
----
./configure --prefix=/usr --mandir=/usr/share/man
diff --git a/examples/chrony.nm-dispatcher b/examples/chrony.nm-dispatcher
index a609a66..0b0c3e7 100644
--- a/examples/chrony.nm-dispatcher
+++ b/examples/chrony.nm-dispatcher
@@ -1,37 +1,15 @@
#!/bin/sh
-# This is a NetworkManager dispatcher script for chronyd to set its NTP sources
-# online or offline when a network interface is configured or removed
+# This is a NetworkManager dispatcher / networkd-dispatcher script for
+# chronyd to set its NTP sources online or offline when a network interface
+# is configured or removed
export LC_ALL=C
-[ "$2" != "up" ] && [ "$2" != "down" ] && exit 0
+# For NetworkManager consider only up/down events
+[ $# -ge 2 ] && [ "$2" != "up" ] && [ "$2" != "down" ] && exit 0
-# Check if there is a default route
+# Note: for networkd-dispatcher routable.d ~= on and off.d ~= off
-if /sbin/ip route list 2> /dev/null | grep -q '^default'; then
- chronyc online > /dev/null 2>&1
- exit 0
-fi
-
-sources=$(chronyc -c -n sources 2> /dev/null)
-
-[ $? -ne 0 ] && exit 0
-
-# Check each configured source if it has a route
-
-echo "$sources" | while IFS=, read mode state address rest; do
- [ "$mode" != '^' ] && [ "$mode" != '=' ] && continue
-
- /sbin/ip route get "$address" > /dev/null 2>&1 && command="online" || command="offline"
-
- # Set priority of sources so that the selected source is set as
- # last if offline to avoid unnecessary reselection
- [ "$state" != '*' ] && priority=1 || priority=2
-
- echo "$priority $command $address"
-
-done | sort | while read priority command address; do
- echo "$command $address"
-done | chronyc > /dev/null 2>&1
+chronyc onoffline > /dev/null 2>&1
exit 0
diff --git a/examples/chrony.spec b/examples/chrony.spec
index 6f911c8..94454a5 100644
--- a/examples/chrony.spec
+++ b/examples/chrony.spec
@@ -1,4 +1,4 @@
-%global chrony_version 3.3-pre1
+%global chrony_version 3.4-pre1
%if 0%(echo %{chrony_version} | grep -q pre && echo 1)
%global prerelease %(echo %{chrony_version} | sed 's/.*-//')
%endif
diff --git a/examples/chronyd.service b/examples/chronyd.service
index 4ffe3b1..1777413 100644
--- a/examples/chronyd.service
+++ b/examples/chronyd.service
@@ -7,7 +7,7 @@ ConditionCapability=CAP_SYS_TIME
[Service]
Type=forking
-PIDFile=/var/run/chronyd.pid
+PIDFile=/var/run/chrony/chronyd.pid
EnvironmentFile=-/etc/sysconfig/chronyd
ExecStart=/usr/sbin/chronyd $OPTIONS
PrivateTmp=yes
diff --git a/hash_intmd5.c b/hash_intmd5.c
index 64e0b9c..0b60f9b 100644
--- a/hash_intmd5.c
+++ b/hash_intmd5.c
@@ -49,18 +49,17 @@ HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len,
const unsigned char *in2, unsigned int in2_len,
unsigned char *out, unsigned int out_len)
{
- if (out_len < 16)
- return 0;
-
MD5Init(&ctx);
MD5Update(&ctx, in1, in1_len);
if (in2)
MD5Update(&ctx, in2, in2_len);
MD5Final(&ctx);
- memcpy(out, ctx.digest, 16);
+ out_len = MIN(out_len, 16);
+
+ memcpy(out, ctx.digest, out_len);
- return 16;
+ return out_len;
}
void
diff --git a/hash_nss.c b/hash_nss.c
index 71c8930..9967bf8 100644
--- a/hash_nss.c
+++ b/hash_nss.c
@@ -32,6 +32,7 @@
#include <nsslowhash.h>
#include "hash.h"
+#include "util.h"
static NSSLOWInitContext *ictx;
@@ -78,13 +79,17 @@ HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len,
const unsigned char *in2, unsigned int in2_len,
unsigned char *out, unsigned int out_len)
{
- unsigned int ret;
+ unsigned char buf[MAX_HASH_LENGTH];
+ unsigned int ret = 0;
NSSLOWHASH_Begin(hashes[id].context);
NSSLOWHASH_Update(hashes[id].context, in1, in1_len);
if (in2)
NSSLOWHASH_Update(hashes[id].context, in2, in2_len);
- NSSLOWHASH_End(hashes[id].context, out, &ret, out_len);
+ NSSLOWHASH_End(hashes[id].context, buf, &ret, sizeof (buf));
+
+ ret = MIN(ret, out_len);
+ memcpy(out, buf, ret);
return ret;
}
diff --git a/hash_tomcrypt.c b/hash_tomcrypt.c
index 5e16233..51b63d9 100644
--- a/hash_tomcrypt.c
+++ b/hash_tomcrypt.c
@@ -29,6 +29,7 @@
#include "config.h"
#include "hash.h"
+#include "util.h"
struct hash {
const char *name;
@@ -105,19 +106,24 @@ HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len,
const unsigned char *in2, unsigned int in2_len,
unsigned char *out, unsigned int out_len)
{
+ unsigned char buf[MAX_HASH_LENGTH];
unsigned long len;
int r;
- len = out_len;
+ len = sizeof (buf);
if (in2)
- r = hash_memory_multi(id, out, &len,
- in1, (unsigned long)in1_len, in2, (unsigned long)in2_len, NULL, 0);
+ r = hash_memory_multi(id, buf, &len,
+ in1, (unsigned long)in1_len,
+ in2, (unsigned long)in2_len, NULL, 0);
else
- r = hash_memory(id, in1, in1_len, out, &len);
+ r = hash_memory(id, in1, in1_len, buf, &len);
if (r != CRYPT_OK)
return 0;
+ len = MIN(len, out_len);
+ memcpy(out, buf, len);
+
return len;
}
diff --git a/hwclock.c b/hwclock.c
index 007d19d..6122ab2 100644
--- a/hwclock.c
+++ b/hwclock.c
@@ -36,8 +36,9 @@
#include "regress.h"
#include "util.h"
-/* Maximum number of samples per clock */
-#define MAX_SAMPLES 16
+/* Minimum and maximum number of samples per clock */
+#define MIN_SAMPLES 2
+#define MAX_SAMPLES 64
/* Maximum acceptable frequency offset of the clock */
#define MAX_FREQ_OFFSET (2.0 / 3.0)
@@ -49,10 +50,12 @@ struct HCL_Instance_Record {
/* Samples stored as intervals (uncorrected for frequency error)
relative to local_ref and hw_ref */
- double x_data[MAX_SAMPLES];
- double y_data[MAX_SAMPLES];
+ double *x_data;
+ double *y_data;
- /* Number of samples */
+ /* Minimum, maximum and current number of samples */
+ int min_samples;
+ int max_samples;
int n_samples;
/* Maximum error of the last sample */
@@ -89,13 +92,21 @@ handle_slew(struct timespec *raw, struct timespec *cooked, double dfreq,
/* ================================================== */
HCL_Instance
-HCL_CreateInstance(double min_separation)
+HCL_CreateInstance(int min_samples, int max_samples, double min_separation)
{
HCL_Instance clock;
+ min_samples = CLAMP(MIN_SAMPLES, min_samples, MAX_SAMPLES);
+ max_samples = CLAMP(MIN_SAMPLES, max_samples, MAX_SAMPLES);
+ max_samples = MAX(min_samples, max_samples);
+
clock = MallocNew(struct HCL_Instance_Record);
- clock->x_data[MAX_SAMPLES - 1] = 0.0;
- clock->y_data[MAX_SAMPLES - 1] = 0.0;
+ clock->x_data = MallocArray(double, max_samples);
+ clock->y_data = MallocArray(double, max_samples);
+ clock->x_data[max_samples - 1] = 0.0;
+ clock->y_data[max_samples - 1] = 0.0;
+ clock->min_samples = min_samples;
+ clock->max_samples = max_samples;
clock->n_samples = 0;
clock->valid_coefs = 0;
clock->min_separation = min_separation;
@@ -110,6 +121,8 @@ HCL_CreateInstance(double min_separation)
void HCL_DestroyInstance(HCL_Instance clock)
{
LCL_RemoveParameterChangeHandler(handle_slew, clock);
+ Free(clock->y_data);
+ Free(clock->x_data);
Free(clock);
}
@@ -138,7 +151,7 @@ HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
/* Shift old samples */
if (clock->n_samples) {
- if (clock->n_samples >= MAX_SAMPLES)
+ if (clock->n_samples >= clock->max_samples)
clock->n_samples--;
hw_delta = UTI_DiffTimespecsToDouble(hw_ts, &clock->hw_ref);
@@ -149,7 +162,7 @@ HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
DEBUG_LOG("HW clock reset interval=%f", local_delta);
}
- for (i = MAX_SAMPLES - clock->n_samples; i < MAX_SAMPLES; i++) {
+ for (i = clock->max_samples - clock->n_samples; i < clock->max_samples; i++) {
clock->y_data[i - 1] = clock->y_data[i] - hw_delta;
clock->x_data[i - 1] = clock->x_data[i] - local_delta;
}
@@ -162,8 +175,8 @@ HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
/* Get new coefficients */
clock->valid_coefs =
- RGR_FindBestRobustRegression(clock->x_data + MAX_SAMPLES - clock->n_samples,
- clock->y_data + MAX_SAMPLES - clock->n_samples,
+ RGR_FindBestRobustRegression(clock->x_data + clock->max_samples - clock->n_samples,
+ clock->y_data + clock->max_samples - clock->n_samples,
clock->n_samples, 1.0e-10, &clock->offset, &raw_freq,
&n_runs, &best_start);
@@ -175,7 +188,8 @@ HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
clock->frequency = raw_freq / local_freq;
/* Drop unneeded samples */
- clock->n_samples -= best_start;
+ if (clock->n_samples > clock->min_samples)
+ clock->n_samples -= MIN(best_start, clock->n_samples - clock->min_samples);
/* If the fit doesn't cross the error interval of the last sample,
or the frequency is not sane, drop all samples and start again */
diff --git a/hwclock.h b/hwclock.h
index f80d09a..3005bae 100644
--- a/hwclock.h
+++ b/hwclock.h
@@ -29,7 +29,8 @@
typedef struct HCL_Instance_Record *HCL_Instance;
/* Create a new HW clock instance */
-extern HCL_Instance HCL_CreateInstance(double min_separation);
+extern HCL_Instance HCL_CreateInstance(int min_samples, int max_samples,
+ double min_separation);
/* Destroy a HW clock instance */
extern void HCL_DestroyInstance(HCL_Instance clock);
diff --git a/keys.c b/keys.c
index 74b57c4..7eb5cb3 100644
--- a/keys.c
+++ b/keys.c
@@ -107,6 +107,8 @@ determine_hash_delay(uint32_t key_id)
double diff, min_diff;
int i, nsecs;
+ memset(&pkt, 0, sizeof (pkt));
+
for (i = 0; i < 10; i++) {
LCL_ReadRawTime(&before);
KEY_GenerateAuth(key_id, (unsigned char *)&pkt, NTP_NORMAL_PACKET_LENGTH,
@@ -212,7 +214,7 @@ KEY_Reload(void)
continue;
if (!CPS_ParseKey(line, &key_id, &hashname, &keyval)) {
- LOG(LOGS_WARN, "Could not parse key at line %d in file %s", line_number, key_file);
+ LOG(LOGS_WARN, "Could not parse key at line %u in file %s", line_number, key_file);
continue;
}
diff --git a/logging.c b/logging.c
index 7d9dbb8..828ae30 100644
--- a/logging.c
+++ b/logging.c
@@ -132,14 +132,16 @@ void LOG_Message(LOG_Severity severity,
char buf[2048];
va_list other_args;
time_t t;
- struct tm stm;
+ struct tm *tm;
if (!system_log && file_log) {
/* Don't clutter up syslog with timestamps and internal debugging info */
time(&t);
- stm = *gmtime(&t);
- strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%SZ", &stm);
- fprintf(file_log, "%s ", buf);
+ tm = gmtime(&t);
+ if (tm) {
+ strftime(buf, sizeof (buf), "%Y-%m-%dT%H:%M:%SZ", tm);
+ fprintf(file_log, "%s ", buf);
+ }
#if DEBUG > 0
if (debug_level >= DEBUG_LEVEL_PRINT_FUNCTION)
fprintf(file_log, "%s:%d:(%s) ", filename, line_number, function_name);
@@ -182,13 +184,20 @@ LOG_OpenFileLog(const char *log_file)
{
FILE *f;
- f = fopen(log_file, "a");
- if (!f)
- LOG_FATAL("Could not open log file %s", log_file);
+ if (log_file) {
+ f = fopen(log_file, "a");
+ if (!f)
+ LOG_FATAL("Could not open log file %s", log_file);
+ } else {
+ f = stderr;
+ }
/* Enable line buffering */
setvbuf(f, NULL, _IOLBF, BUFSIZ);
+ if (file_log && file_log != stderr)
+ fclose(file_log);
+
file_log = f;
}
diff --git a/logging.h b/logging.h
index 5bb46f5..67931dc 100644
--- a/logging.h
+++ b/logging.h
@@ -99,7 +99,7 @@ extern void LOG_Message(LOG_Severity severity, const char *format, ...);
*/
extern void LOG_SetDebugLevel(int level);
-/* Log messages to a file instead of stderr */
+/* Log messages to a file instead of stderr, or stderr again if NULL */
extern void LOG_OpenFileLog(const char *log_file);
/* Log messages to syslog instead of stderr */
diff --git a/main.c b/main.c
index a2202e9..c40fb25 100644
--- a/main.c
+++ b/main.c
@@ -530,9 +530,6 @@ int main
/* Check whether another chronyd may already be running */
check_pidfile();
- /* Write our pidfile to prevent other chronyds running */
- write_pidfile();
-
if (!user)
user = CNF_GetUser();
@@ -543,6 +540,9 @@ int main
/* Create directories for sockets, log files, and dump files */
CNF_CreateDirs(pw->pw_uid, pw->pw_gid);
+ /* Write our pidfile to prevent other instances from running */
+ write_pidfile();
+
PRV_Initialise();
LCL_Initialise();
SCH_Initialise();
@@ -586,7 +586,7 @@ int main
/* From now on, it is safe to do finalisation on exit */
initialised = 1;
- UTI_SetQuitSignalsHandler(signal_cleanup);
+ UTI_SetQuitSignalsHandler(signal_cleanup, 1);
CAM_OpenUnixSocket();
diff --git a/memory.h b/memory.h
index 110cf6a..7ae2c03 100644
--- a/memory.h
+++ b/memory.h
@@ -27,6 +27,8 @@
#ifndef GOT_MEMORY_H
#define GOT_MEMORY_H
+#include "sysincl.h"
+
/* Wrappers checking for errors */
extern void *Malloc(size_t size);
extern void *Realloc(void *ptr, size_t size);
diff --git a/ntp.h b/ntp.h
index 801f264..7b7f361 100644
--- a/ntp.h
+++ b/ntp.h
@@ -121,4 +121,19 @@ typedef struct {
#define NTP_REFID_LOCAL 0x7F7F0101UL /* 127.127.1.1 */
#define NTP_REFID_SMOOTH 0x7F7F01FFUL /* 127.127.1.255 */
+/* Structure used to save NTP measurements. time is the local time at which
+ the sample is to be considered to have been made and offset is the offset at
+ the time (positive indicates that the local clock is slow relative to the
+ source). root_delay/root_dispersion include peer_delay/peer_dispersion. */
+typedef struct {
+ struct timespec time;
+ double offset;
+ double peer_delay;
+ double peer_dispersion;
+ double root_delay;
+ double root_dispersion;
+ int stratum;
+ NTP_Leap leap;
+} NTP_Sample;
+
#endif /* GOT_NTP_H */
diff --git a/ntp_core.c b/ntp_core.c
index a71ee43..49d1ae7 100644
--- a/ntp_core.c
+++ b/ntp_core.c
@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
- * Copyright (C) Miroslav Lichvar 2009-2017
+ * Copyright (C) Miroslav Lichvar 2009-2018
*
* 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
@@ -37,6 +37,7 @@
#include "sched.h"
#include "reference.h"
#include "local.h"
+#include "samplefilt.h"
#include "smooth.h"
#include "sources.h"
#include "util.h"
@@ -89,8 +90,8 @@ struct NCR_Instance_Record {
int tx_suspended; /* Boolean indicating we can't transmit yet */
int auto_burst; /* If 1, initiate a burst on each poll */
- int auto_offline; /* If 1, automatically go offline if server/peer
- isn't responding */
+ int auto_offline; /* If 1, automatically go offline when requests
+ cannot be sent */
int local_poll; /* Log2 of polling interval at our end */
int remote_poll; /* Log2 of server/peer's polling interval (recovered
@@ -195,6 +196,9 @@ struct NCR_Instance_Record {
SRC_Instance source;
+ /* Optional median filter for NTP measurements */
+ SPF_Instance filter;
+
int burst_good_samples_to_go;
int burst_total_samples_to_go;
@@ -217,7 +221,7 @@ static ARR_Instance broadcasts;
/* Spacing required between samples for any two servers/peers (to
minimise risk of network collisions) (in seconds) */
-#define MIN_SAMPLING_SEPARATION 0.02
+#define MIN_SAMPLING_SEPARATION 0.002
#define MAX_SAMPLING_SEPARATION 0.2
/* Randomness added to spacing between samples for one server/peer */
@@ -226,12 +230,10 @@ static ARR_Instance broadcasts;
/* Adjustment of the peer polling interval */
#define PEER_SAMPLING_ADJ 1.1
-/* Spacing between samples in burst mode for one server/peer */
-#define BURST_INTERVAL 2.0
-
-/* Time to wait before retransmitting in burst mode, if we did not get
- a reply to the previous probe */
-#define BURST_TIMEOUT 2.0
+/* Maximum spacing between samples in the burst mode as an absolute
+ value and ratio to the normal polling interval */
+#define MAX_BURST_INTERVAL 2.0
+#define MAX_BURST_POLL_RATIO 0.25
/* Number of samples in initial burst */
#define IBURST_GOOD_SAMPLES 4
@@ -266,19 +268,20 @@ static ARR_Instance broadcasts;
#define MAX_MAXDELAYDEVRATIO 1.0e6
/* Minimum and maximum allowed poll interval */
-#define MIN_MINPOLL -4
-#define MIN_MAXPOLL 0
+#define MIN_POLL -6
#define MAX_POLL 24
+/* Enable sub-second polling intervals only when the peer delay is not
+ longer than 10 milliseconds to restrict them to local networks */
+#define MIN_NONLAN_POLL 0
+#define MAX_LAN_PEER_DELAY 0.01
+
/* Kiss-o'-Death codes */
#define KOD_RATE 0x52415445UL /* RATE */
/* Maximum poll interval set by KoD RATE */
#define MAX_KOD_RATE_POLL SRC_DEFAULT_MAXPOLL
-/* Maximum number of missed responses to follow peer's polling interval */
-#define MAX_PEER_POLL_TX 8
-
/* Maximum number of missed responses to accept samples using old timestamps
in the interleaved client/server mode */
#define MAX_CLIENT_INTERLEAVED_TX 4
@@ -537,12 +540,13 @@ NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourcePar
result->interleaved = params->interleaved;
result->minpoll = params->minpoll;
- if (result->minpoll < MIN_MINPOLL)
+ if (result->minpoll < MIN_POLL)
result->minpoll = SRC_DEFAULT_MINPOLL;
else if (result->minpoll > MAX_POLL)
result->minpoll = MAX_POLL;
+
result->maxpoll = params->maxpoll;
- if (result->maxpoll < MIN_MAXPOLL)
+ if (result->maxpoll < MIN_POLL)
result->maxpoll = SRC_DEFAULT_MAXPOLL;
else if (result->maxpoll > MAX_POLL)
result->maxpoll = MAX_POLL;
@@ -600,10 +604,18 @@ NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourcePar
params->min_samples, params->max_samples,
params->min_delay, params->asymmetry);
+ if (params->filter_length >= 1)
+ result->filter = SPF_CreateInstance(params->filter_length, params->filter_length,
+ NTP_MAX_DISPERSION, 0.0);
+ else
+ result->filter = NULL;
+
result->rx_timeout_id = 0;
result->tx_timeout_id = 0;
result->tx_suspended = 1;
- result->opmode = params->online ? MD_ONLINE : MD_OFFLINE;
+ result->opmode = params->connectivity == SRC_ONLINE ||
+ (params->connectivity == SRC_MAYBE_ONLINE &&
+ NIO_IsServerConnectable(remote_addr)) ? MD_ONLINE : MD_OFFLINE;
result->local_poll = result->minpoll;
result->poll_score = 0.0;
zero_local_timestamp(&result->local_tx);
@@ -632,6 +644,9 @@ NCR_DestroyInstance(NCR_Instance instance)
if (instance->mode == MODE_ACTIVE)
NIO_CloseServerSocket(instance->local_addr.sock_fd);
+ if (instance->filter)
+ SPF_DestroyInstance(instance->filter);
+
/* This will destroy the source instance inside the
structure, which will cause reselection if this was the
synchronising source etc. */
@@ -677,6 +692,9 @@ NCR_ResetInstance(NCR_Instance instance)
instance->updated_init_timestamps = 0;
UTI_ZeroNtp64(&instance->init_remote_ntp_tx);
zero_local_timestamp(&instance->init_local_rx);
+
+ if (instance->filter)
+ SPF_DropSamples(instance->filter);
}
/* ================================================== */
@@ -742,6 +760,13 @@ adjust_poll(NCR_Instance inst, double adj)
inst->local_poll = inst->maxpoll;
inst->poll_score = 1.0;
}
+
+ /* Don't allow a sub-second polling interval if the source is not reachable
+ 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))
+ inst->local_poll = MIN_NONLAN_POLL;
}
/* ================================================== */
@@ -753,6 +778,9 @@ get_poll_adj(NCR_Instance inst, double error_in_estimate, double peer_distance)
int samples;
if (error_in_estimate > peer_distance) {
+ /* If the prediction is not even within +/- the peer distance of the peer,
+ we are clearly not tracking the peer at all well, so we back off the
+ sampling rate depending on just how bad the situation is */
poll_adj = -log(error_in_estimate / peer_distance) / log(2.0);
} else {
samples = SST_Samples(SRC_GetSourcestats(inst->source));
@@ -782,7 +810,7 @@ get_transmit_poll(NCR_Instance inst)
/* In symmetric mode, if the peer is responding, use shorter of the local
and remote poll interval, but not shorter than the minimum */
if (inst->mode == MODE_ACTIVE && poll > inst->remote_poll &&
- inst->tx_count < MAX_PEER_POLL_TX)
+ SRC_IsReachable(inst->source))
poll = MAX(inst->remote_poll, inst->minpoll);
return poll;
@@ -850,7 +878,7 @@ get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx)
case MD_BURST_WAS_ONLINE:
case MD_BURST_WAS_OFFLINE:
/* Burst modes */
- delay_time = on_tx ? BURST_TIMEOUT : BURST_INTERVAL;
+ delay_time = MIN(MAX_BURST_INTERVAL, MAX_BURST_POLL_RATIO * delay_time);
break;
default:
assert(0);
@@ -868,6 +896,8 @@ get_separation(int poll)
{
double separation;
+ assert(poll >= MIN_POLL && poll <= MAX_POLL);
+
/* Allow up to 8 sources using the same short interval to not be limited
by the separation */
separation = UTI_Log2ToDouble(poll - 3);
@@ -914,7 +944,7 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
)
{
NTP_Packet message;
- int auth_len, mac_len, length, ret, precision;
+ int auth_len, max_auth_len, length, ret, precision;
struct timespec local_receive, local_transmit;
double smooth_offset, local_transmit_err;
NTP_int64 ts_fuzz;
@@ -1052,24 +1082,21 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
&message.transmit_ts, &ts_fuzz);
if (auth_mode == AUTH_SYMMETRIC) {
+ /* Truncate long MACs in NTPv4 packets to allow deterministic parsing
+ of extension fields (RFC 7822) */
+ max_auth_len = version == 4 ?
+ NTP_MAX_V4_MAC_LENGTH - 4 : sizeof (message.auth_data);
+
auth_len = KEY_GenerateAuth(key_id, (unsigned char *) &message,
offsetof(NTP_Packet, auth_keyid),
- (unsigned char *)&message.auth_data,
- sizeof (message.auth_data));
+ (unsigned char *)&message.auth_data, max_auth_len);
if (!auth_len) {
DEBUG_LOG("Could not generate auth data with key %"PRIu32, key_id);
return 0;
}
message.auth_keyid = htonl(key_id);
- mac_len = sizeof (message.auth_keyid) + auth_len;
-
- /* Truncate MACs in NTPv4 packets to allow deterministic parsing
- of extension fields (RFC 7822) */
- if (version == 4 && mac_len > NTP_MAX_V4_MAC_LENGTH)
- mac_len = NTP_MAX_V4_MAC_LENGTH;
-
- length += mac_len;
+ length += sizeof (message.auth_keyid) + auth_len;
} else if (auth_mode == AUTH_MSSNTP) {
/* MS-SNTP packets are signed (asynchronously) by ntp_signd */
return NSD_SignAndSendPacket(key_id, &message, where_to, from, length);
@@ -1132,7 +1159,7 @@ transmit_timeout(void *arg)
/* Start a new burst if the burst option is enabled and the average
polling interval including the burst will not fall below the
minimum polling interval */
- if (inst->auto_burst && inst->local_poll > inst->minpoll && inst->local_poll > 1)
+ if (inst->auto_burst && inst->local_poll > inst->minpoll)
NCR_InitiateSampleBurst(inst, BURST_GOOD_SAMPLES,
MIN(1 << (inst->local_poll - inst->minpoll),
MAX_BURST_TOTAL_SAMPLES));
@@ -1141,10 +1168,6 @@ transmit_timeout(void *arg)
break;
}
- /* With auto_offline take the source offline on 2nd missed reply */
- if (inst->auto_offline && inst->tx_count >= 2)
- NCR_TakeSourceOffline(inst);
-
if (inst->opmode == MD_OFFLINE) {
return;
}
@@ -1228,6 +1251,10 @@ transmit_timeout(void *arg)
SRC_UpdateReachability(inst->source, 0);
}
+ /* With auto_offline take the source offline if sending failed */
+ if (!sent && inst->auto_offline)
+ NCR_SetConnectivity(inst, SRC_OFFLINE);
+
switch (inst->opmode) {
case MD_BURST_WAS_ONLINE:
/* When not reachable, don't stop online burst until sending succeeds */
@@ -1237,6 +1264,8 @@ transmit_timeout(void *arg)
case MD_BURST_WAS_OFFLINE:
--inst->burst_total_samples_to_go;
break;
+ case MD_OFFLINE:
+ return;
default:
break;
}
@@ -1437,10 +1466,57 @@ check_delay_dev_ratio(NCR_Instance inst, SST_Stats stats,
/* ================================================== */
+static void
+process_sample(NCR_Instance inst, NTP_Sample *sample)
+{
+ double estimated_offset, error_in_estimate, filtered_sample_ago;
+ 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. */
+ if (inst->filter) {
+ SPF_AccumulateSample(inst->filter, sample);
+
+ filtered_samples = SPF_GetNumberOfSamples(inst->filter);
+
+ 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;
+ }
+
+ /* 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. */
+ estimated_offset = SST_PredictOffset(SRC_GetSourcestats(inst->source), &sample->time);
+
+ error_in_estimate = fabs(-sample->offset - estimated_offset);
+
+ SRC_AccumulateSample(inst->source, sample);
+ SRC_SelectSource(inst->source);
+
+ adjust_poll(inst, get_poll_adj(inst, error_in_estimate,
+ sample->peer_dispersion + 0.5 * sample->peer_delay));
+}
+
+/* ================================================== */
+
static int
receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *rx_ts, NTP_Packet *message, int length)
{
+ NTP_Sample sample;
SST_Stats stats;
int pkt_leap, pkt_version;
@@ -1449,24 +1525,6 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
double pkt_root_dispersion;
AuthenticationMode pkt_auth_mode;
- /* The local time to which the (offset, delay, dispersion) triple will
- be taken to relate. For client/server operation this is practically
- the same as either the transmit or receive time. The difference comes
- in symmetric active mode, when the receive may come minutes after the
- transmit, and this time will be midway between the two */
- struct timespec sample_time;
-
- /* The estimated offset in seconds, a positive value indicates that the local
- clock is SLOW of the remote source and a negative value indicates that the
- local clock is FAST of the remote source */
- double offset;
-
- /* The estimated peer delay, dispersion and distance */
- double delay, dispersion, distance;
-
- /* The total root delay and dispersion */
- double root_delay, root_dispersion;
-
/* The skew and estimated frequency offset relative to the remote source */
double skew, source_freq_lo, source_freq_hi;
@@ -1481,15 +1539,6 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
/* Kiss-o'-Death codes */
int kod_rate;
- /* 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. */
- double estimated_offset;
-
- /* The absolute difference between the offset estimate and
- measurement in seconds */
- double error_in_estimate;
-
NTP_Local_Timestamp local_receive, local_transmit;
double remote_interval, local_interval, response_time;
double delay_time, precision;
@@ -1608,23 +1657,23 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
precision = LCL_GetSysPrecisionAsQuantum() + UTI_Log2ToDouble(message->precision);
/* Calculate delay */
- delay = fabs(local_interval - remote_interval);
- if (delay < precision)
- delay = precision;
+ sample.peer_delay = fabs(local_interval - remote_interval);
+ if (sample.peer_delay < precision)
+ sample.peer_delay = precision;
/* Calculate offset. Following the NTP definition, this is negative
if we are fast of the remote source. */
- offset = UTI_DiffTimespecsToDouble(&remote_average, &local_average);
+ sample.offset = UTI_DiffTimespecsToDouble(&remote_average, &local_average);
/* Apply configured correction */
- offset += inst->offset_correction;
+ sample.offset += inst->offset_correction;
/* We treat the time of the sample as being midway through the local
measurement period. An analysis assuming constant relative
frequency and zero network delay shows this is the only possible
choice to estimate the frequency difference correctly for every
sample pair. */
- sample_time = local_average;
+ sample.time = local_average;
SST_GetFrequencyRange(stats, &source_freq_lo, &source_freq_hi);
@@ -1632,8 +1681,8 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
skew = (source_freq_hi - source_freq_lo) / 2.0;
/* and then calculate peer dispersion */
- dispersion = MAX(precision, MAX(local_transmit.err, local_receive.err)) +
- skew * fabs(local_interval);
+ sample.peer_dispersion = MAX(precision, MAX(local_transmit.err, local_receive.err)) +
+ skew * fabs(local_interval);
/* If the source is an active peer, this is the minimum assumed interval
between previous two transmissions (if not constrained by minpoll) */
@@ -1647,10 +1696,11 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
processing time is sane, and in interleaved symmetric mode that the
measured delay and intervals between remote timestamps don't indicate
a missed response */
- testA = delay - dispersion <= inst->max_delay && precision <= inst->max_delay &&
+ 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_ACTIVE && interleaved_packet &&
- (delay > 0.5 * prev_remote_poll_interval ||
+ (sample.peer_delay > 0.5 * prev_remote_poll_interval ||
UTI_CompareNtp64(&message->receive_ts, &message->transmit_ts) <= 0 ||
(inst->remote_poll <= inst->prev_local_poll &&
UTI_DiffTimespecsToDouble(&remote_transmit, &prev_remote_transmit) >
@@ -1659,14 +1709,14 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
/* Test B requires in client mode that the ratio of the round trip delay
to the minimum one currently in the stats data register is less than an
administrator-defined value */
- testB = check_delay_ratio(inst, stats, &sample_time, delay);
+ 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
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, offset, delay);
+ testC = check_delay_dev_ratio(inst, stats, &sample.time, sample.offset, sample.peer_delay);
/* Test D requires that the remote peer is not synchronised to us to
prevent a synchronisation loop */
@@ -1674,8 +1724,8 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
pkt_refid != UTI_IPToRefid(&local_addr->ip_addr);
} else {
remote_interval = local_interval = response_time = 0.0;
- offset = delay = dispersion = 0.0;
- sample_time = rx_ts->ts;
+ sample.offset = sample.peer_delay = sample.peer_dispersion = 0.0;
+ sample.time = rx_ts->ts;
local_receive = *rx_ts;
local_transmit = inst->local_tx;
testA = testB = testC = testD = 0;
@@ -1685,9 +1735,10 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
the additional tests passed */
good_packet = testA && testB && testC && testD;
- root_delay = pkt_root_delay + delay;
- root_dispersion = pkt_root_dispersion + dispersion;
- distance = dispersion + 0.5 * delay;
+ sample.root_delay = pkt_root_delay + sample.peer_delay;
+ sample.root_dispersion = pkt_root_dispersion + sample.peer_dispersion;
+ sample.stratum = MAX(message->stratum, inst->min_stratum);
+ sample.leap = (NTP_Leap)pkt_leap;
/* Update the NTP timestamps. If it's a valid packet from a synchronised
source, the timestamps may be used later when processing a packet in the
@@ -1756,7 +1807,8 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
UTI_Ntp64ToString(&message->receive_ts),
UTI_Ntp64ToString(&message->transmit_ts));
DEBUG_LOG("offset=%.9f delay=%.9f dispersion=%f root_delay=%f root_dispersion=%f",
- offset, delay, dispersion, root_delay, root_dispersion);
+ sample.offset, sample.peer_delay, sample.peer_dispersion,
+ sample.root_delay, sample.root_dispersion);
DEBUG_LOG("remote_interval=%.9f local_interval=%.9f response_time=%.9f txs=%c rxs=%c",
remote_interval, local_interval, response_time,
tss_chars[local_transmit.source], tss_chars[local_receive.source]);
@@ -1778,26 +1830,8 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
SRC_UpdateReachability(inst->source, synced_packet);
if (good_packet) {
- /* Do this before we accumulate a new sample into the stats registers, obviously */
- estimated_offset = SST_PredictOffset(stats, &sample_time);
-
- SRC_AccumulateSample(inst->source,
- &sample_time,
- offset, delay, dispersion,
- root_delay, root_dispersion,
- MAX(message->stratum, inst->min_stratum),
- (NTP_Leap) pkt_leap);
-
- SRC_SelectSource(inst->source);
-
- /* Now examine the registers. First though, if the prediction is
- not even within +/- the peer distance of the peer, we are clearly
- not tracking the peer at all well, so we back off the sampling
- rate depending on just how bad the situation is. */
- error_in_estimate = fabs(-offset - estimated_offset);
-
- /* Now update the polling interval */
- adjust_poll(inst, get_poll_adj(inst, error_in_estimate, distance));
+ /* Adjust the polling interval, accumulate the sample, etc. */
+ process_sample(inst, &sample);
/* If we're in burst mode, check whether the burst is completed and
revert to the previous mode */
@@ -1864,9 +1898,9 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
inst->report.root_dispersion = pkt_root_dispersion;
inst->report.ref_id = pkt_refid;
UTI_Ntp64ToTimespec(&message->reference_ts, &inst->report.ref_time);
- inst->report.offset = offset;
- inst->report.peer_delay = delay;
- inst->report.peer_dispersion = dispersion;
+ inst->report.offset = sample.offset;
+ inst->report.peer_delay = sample.peer_delay;
+ inst->report.peer_dispersion = sample.peer_dispersion;
inst->report.response_time = response_time;
inst->report.jitter_asymmetry = SST_GetJitterAsymmetry(stats);
inst->report.tests = ((((((((test1 << 1 | test2) << 1 | test3) << 1 |
@@ -1883,14 +1917,14 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
/* Do measurement logging */
if (logfileid != -1 && (log_raw_measurements || synced_packet)) {
LOG_FileWrite(logfileid, "%s %-15s %1c %2d %1d%1d%1d %1d%1d%1d %1d%1d%1d%d %2d %2d %4.2f %10.3e %10.3e %10.3e %10.3e %10.3e %08"PRIX32" %1d%1c %1c %1c",
- UTI_TimeToLogForm(sample_time.tv_sec),
+ UTI_TimeToLogForm(sample.time.tv_sec),
UTI_IPToString(&inst->remote_addr.ip_addr),
leap_chars[pkt_leap],
message->stratum,
test1, test2, test3, test5, test6, test7, testA, testB, testC, testD,
inst->local_poll, message->poll,
inst->poll_score,
- offset, delay, dispersion,
+ sample.offset, sample.peer_delay, sample.peer_dispersion,
pkt_root_delay, pkt_root_dispersion, pkt_refid,
NTP_LVM_TO_MODE(message->lvm), interleaved_packet ? 'I' : 'B',
tss_chars[local_transmit.source],
@@ -2039,7 +2073,7 @@ NCR_ProcessRxKnown(NCR_Instance inst, NTP_Local_Address *local_addr,
/* It's not a reply to our request, don't return success */
return 0;
} else {
- DEBUG_LOG("NTP packet discarded pkt_mode=%d our_mode=%d", pkt_mode, inst->mode);
+ DEBUG_LOG("NTP packet discarded pkt_mode=%d our_mode=%u", pkt_mode, inst->mode);
return 0;
}
}
@@ -2098,7 +2132,7 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
/* Fall through */
default:
/* Discard */
- DEBUG_LOG("NTP packet discarded pkt_mode=%d", pkt_mode);
+ DEBUG_LOG("NTP packet discarded pkt_mode=%u", pkt_mode);
return;
}
@@ -2124,7 +2158,7 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
break;
default:
/* Discard packets in other modes */
- DEBUG_LOG("NTP packet discarded auth_mode=%d", auth_mode);
+ DEBUG_LOG("NTP packet discarded auth_mode=%u", auth_mode);
return;
}
}
@@ -2270,53 +2304,67 @@ NCR_SlewTimes(NCR_Instance inst, struct timespec *when, double dfreq, double dof
if (!UTI_IsZeroTimespec(&inst->init_local_rx.ts))
UTI_AdjustTimespec(&inst->init_local_rx.ts, when, &inst->init_local_rx.ts, &delta, dfreq,
doffset);
+
+ if (inst->filter)
+ SPF_SlewSamples(inst->filter, when, dfreq, doffset);
}
/* ================================================== */
void
-NCR_TakeSourceOnline(NCR_Instance inst)
+NCR_SetConnectivity(NCR_Instance inst, SRC_Connectivity connectivity)
{
- switch (inst->opmode) {
- case MD_ONLINE:
- /* Nothing to do */
- break;
- case MD_OFFLINE:
- LOG(LOGS_INFO, "Source %s online", UTI_IPToString(&inst->remote_addr.ip_addr));
- inst->opmode = MD_ONLINE;
- NCR_ResetInstance(inst);
- start_initial_timeout(inst);
- break;
- case MD_BURST_WAS_ONLINE:
- /* Will revert */
- break;
- case MD_BURST_WAS_OFFLINE:
- inst->opmode = MD_BURST_WAS_ONLINE;
- LOG(LOGS_INFO, "Source %s online", UTI_IPToString(&inst->remote_addr.ip_addr));
- break;
- }
-}
+ char *s;
-/* ================================================== */
+ s = UTI_IPToString(&inst->remote_addr.ip_addr);
-void
-NCR_TakeSourceOffline(NCR_Instance inst)
-{
- switch (inst->opmode) {
- case MD_ONLINE:
- LOG(LOGS_INFO, "Source %s offline", UTI_IPToString(&inst->remote_addr.ip_addr));
- take_offline(inst);
- break;
- case MD_OFFLINE:
- break;
- case MD_BURST_WAS_ONLINE:
- inst->opmode = MD_BURST_WAS_OFFLINE;
- LOG(LOGS_INFO, "Source %s offline", UTI_IPToString(&inst->remote_addr.ip_addr));
+ if (connectivity == SRC_MAYBE_ONLINE)
+ connectivity = NIO_IsServerConnectable(&inst->remote_addr) ? SRC_ONLINE : SRC_OFFLINE;
+
+ switch (connectivity) {
+ case SRC_ONLINE:
+ switch (inst->opmode) {
+ case MD_ONLINE:
+ /* Nothing to do */
+ break;
+ case MD_OFFLINE:
+ LOG(LOGS_INFO, "Source %s online", s);
+ inst->opmode = MD_ONLINE;
+ NCR_ResetInstance(inst);
+ start_initial_timeout(inst);
+ break;
+ case MD_BURST_WAS_ONLINE:
+ /* Will revert */
+ break;
+ case MD_BURST_WAS_OFFLINE:
+ inst->opmode = MD_BURST_WAS_ONLINE;
+ LOG(LOGS_INFO, "Source %s online", s);
+ break;
+ default:
+ assert(0);
+ }
break;
- case MD_BURST_WAS_OFFLINE:
+ case SRC_OFFLINE:
+ switch (inst->opmode) {
+ case MD_ONLINE:
+ LOG(LOGS_INFO, "Source %s offline", s);
+ take_offline(inst);
+ break;
+ case MD_OFFLINE:
+ break;
+ case MD_BURST_WAS_ONLINE:
+ inst->opmode = MD_BURST_WAS_OFFLINE;
+ LOG(LOGS_INFO, "Source %s offline", s);
+ break;
+ case MD_BURST_WAS_OFFLINE:
+ break;
+ default:
+ assert(0);
+ }
break;
+ default:
+ assert(0);
}
-
}
/* ================================================== */
@@ -2324,7 +2372,7 @@ NCR_TakeSourceOffline(NCR_Instance inst)
void
NCR_ModifyMinpoll(NCR_Instance inst, int new_minpoll)
{
- if (new_minpoll < MIN_MINPOLL || new_minpoll > MAX_POLL)
+ if (new_minpoll < MIN_POLL || new_minpoll > MAX_POLL)
return;
inst->minpoll = new_minpoll;
LOG(LOGS_INFO, "Source %s new minpoll %d", UTI_IPToString(&inst->remote_addr.ip_addr), new_minpoll);
@@ -2337,7 +2385,7 @@ NCR_ModifyMinpoll(NCR_Instance inst, int new_minpoll)
void
NCR_ModifyMaxpoll(NCR_Instance inst, int new_maxpoll)
{
- if (new_maxpoll < MIN_MAXPOLL || new_maxpoll > MAX_POLL)
+ if (new_maxpoll < MIN_POLL || new_maxpoll > MAX_POLL)
return;
inst->maxpoll = new_maxpoll;
LOG(LOGS_INFO, "Source %s new maxpoll %d", UTI_IPToString(&inst->remote_addr.ip_addr), new_maxpoll);
@@ -2351,7 +2399,7 @@ void
NCR_ModifyMaxdelay(NCR_Instance inst, double new_max_delay)
{
inst->max_delay = CLAMP(0.0, new_max_delay, MAX_MAXDELAY);
- LOG(LOGS_INFO, "Source %s new max delay %f",
+ LOG(LOGS_INFO, "Source %s new maxdelay %f",
UTI_IPToString(&inst->remote_addr.ip_addr), inst->max_delay);
}
@@ -2361,7 +2409,7 @@ void
NCR_ModifyMaxdelayratio(NCR_Instance inst, double new_max_delay_ratio)
{
inst->max_delay_ratio = CLAMP(0.0, new_max_delay_ratio, MAX_MAXDELAYRATIO);
- LOG(LOGS_INFO, "Source %s new max delay ratio %f",
+ LOG(LOGS_INFO, "Source %s new maxdelayratio %f",
UTI_IPToString(&inst->remote_addr.ip_addr), inst->max_delay_ratio);
}
@@ -2371,7 +2419,7 @@ void
NCR_ModifyMaxdelaydevratio(NCR_Instance inst, double new_max_delay_dev_ratio)
{
inst->max_delay_dev_ratio = CLAMP(0.0, new_max_delay_dev_ratio, MAX_MAXDELAYDEVRATIO);
- LOG(LOGS_INFO, "Source %s new max delay dev ratio %f",
+ LOG(LOGS_INFO, "Source %s new maxdelaydevratio %f",
UTI_IPToString(&inst->remote_addr.ip_addr), inst->max_delay_dev_ratio);
}
diff --git a/ntp_core.h b/ntp_core.h
index f788d68..522d59b 100644
--- a/ntp_core.h
+++ b/ntp_core.h
@@ -99,12 +99,9 @@ extern void NCR_ProcessTxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Addr
/* Slew receive and transmit times in instance records */
extern void NCR_SlewTimes(NCR_Instance inst, struct timespec *when, double dfreq, double doffset);
-/* Take a particular source online (i.e. start sampling it) */
-extern void NCR_TakeSourceOnline(NCR_Instance inst);
-
-/* Take a particular source offline (i.e. stop sampling it, without
- marking it unreachable in the source selection stuff) */
-extern void NCR_TakeSourceOffline(NCR_Instance inst);
+/* Take a particular source online (i.e. start sampling it) or offline
+ (i.e. stop sampling it) */
+extern void NCR_SetConnectivity(NCR_Instance inst, SRC_Connectivity connectivity);
extern void NCR_ModifyMinpoll(NCR_Instance inst, int new_minpoll);
diff --git a/ntp_io.c b/ntp_io.c
index d63eb1d..b71a1d4 100644
--- a/ntp_io.c
+++ b/ntp_io.c
@@ -142,6 +142,10 @@ prepare_socket(int family, int port_number, int client_only)
/* Close on exec */
UTI_FdSetCloexec(sock_fd);
+ /* Enable non-blocking mode on server sockets */
+ if (!client_only && fcntl(sock_fd, F_SETFL, O_NONBLOCK))
+ DEBUG_LOG("Could not set O_NONBLOCK : %s", strerror(errno));
+
/* Prepare local address */
memset(&my_addr, 0, sizeof (my_addr));
my_addr_len = 0;
@@ -227,11 +231,11 @@ prepare_socket(int family, int port_number, int client_only)
if (family == AF_INET) {
#ifdef HAVE_IN_PKTINFO
- /* We want the local IP info on server sockets */
- if (setsockopt(sock_fd, IPPROTO_IP, IP_PKTINFO, (char *)&on_off, sizeof(on_off)) < 0) {
+ if (setsockopt(sock_fd, IPPROTO_IP, IP_PKTINFO, (char *)&on_off, sizeof(on_off)) < 0)
LOG(LOGS_ERR, "Could not set %s socket option", "IP_PKTINFO");
- /* Don't quit - we might survive anyway */
- }
+#elif defined(IP_RECVDSTADDR)
+ if (setsockopt(sock_fd, IPPROTO_IP, IP_RECVDSTADDR, (char *)&on_off, sizeof(on_off)) < 0)
+ LOG(LOGS_ERR, "Could not set %s socket option", "IP_RECVDSTADDR");
#endif
}
#ifdef FEAT_IPV6
@@ -570,6 +574,23 @@ NIO_IsServerSocket(int sock_fd)
/* ================================================== */
+int
+NIO_IsServerConnectable(NTP_Remote_Address *remote_addr)
+{
+ int sock_fd, r;
+
+ sock_fd = prepare_separate_client_socket(remote_addr->ip_addr.family);
+ if (sock_fd == INVALID_SOCK_FD)
+ return 0;
+
+ r = connect_socket(sock_fd, remote_addr);
+ close_socket(sock_fd);
+
+ return r;
+}
+
+/* ================================================== */
+
static void
process_message(struct msghdr *hdr, int length, int sock_fd)
{
@@ -621,6 +642,14 @@ process_message(struct msghdr *hdr, int length, int sock_fd)
local_addr.ip_addr.family = IPADDR_INET4;
local_addr.if_index = ipi.ipi_ifindex;
}
+#elif defined(IP_RECVDSTADDR)
+ if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_RECVDSTADDR) {
+ struct in_addr addr;
+
+ memcpy(&addr, CMSG_DATA(cmsg), sizeof (addr));
+ local_addr.ip_addr.addr.in4 = ntohl(addr.s_addr);
+ local_addr.ip_addr.family = IPADDR_INET4;
+ }
#endif
#ifdef HAVE_IN6_PKTINFO
@@ -663,7 +692,7 @@ process_message(struct msghdr *hdr, int length, int sock_fd)
return;
#endif
- DEBUG_LOG("Received %d bytes from %s:%d to %s fd=%d if=%d tss=%d delay=%.9f",
+ DEBUG_LOG("Received %d bytes from %s:%d to %s fd=%d if=%d tss=%u delay=%.9f",
length, UTI_IPToString(&remote_addr.ip_addr), remote_addr.port,
UTI_IPToString(&local_addr.ip_addr), local_addr.sock_fd, local_addr.if_index,
local_ts.source, UTI_DiffTimespecsToDouble(&sched_ts, &local_ts.ts));
@@ -717,6 +746,20 @@ read_from_socket(int sock_fd, int event, void *anything)
#endif
if (status < 0) {
+#ifdef HAVE_LINUX_TIMESTAMPING
+ /* If reading from the error queue failed, the exception should be
+ for a socket error. Clear the error to avoid a busy loop. */
+ if (flags & MSG_ERRQUEUE) {
+ int error = 0;
+ socklen_t len = sizeof (error);
+
+ if (getsockopt(sock_fd, SOL_SOCKET, SO_ERROR, &error, &len))
+ DEBUG_LOG("Could not get SO_ERROR");
+ if (error)
+ errno = error;
+ }
+#endif
+
DEBUG_LOG("Could not receive from fd %d : %s", sock_fd,
strerror(errno));
return;
@@ -778,8 +821,8 @@ NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
msg.msg_flags = 0;
cmsglen = 0;
-#ifdef HAVE_IN_PKTINFO
if (local_addr->ip_addr.family == IPADDR_INET4) {
+#ifdef HAVE_IN_PKTINFO
struct in_pktinfo *ipi;
cmsg = CMSG_FIRSTHDR(&msg);
@@ -792,8 +835,23 @@ NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
ipi = (struct in_pktinfo *) CMSG_DATA(cmsg);
ipi->ipi_spec_dst.s_addr = htonl(local_addr->ip_addr.addr.in4);
- }
+ if (local_addr->if_index != INVALID_IF_INDEX)
+ ipi->ipi_ifindex = local_addr->if_index;
+#elif defined(IP_SENDSRCADDR)
+ struct in_addr *addr;
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ memset(cmsg, 0, CMSG_SPACE(sizeof (struct in_addr)));
+ cmsglen += CMSG_SPACE(sizeof (struct in_addr));
+
+ cmsg->cmsg_level = IPPROTO_IP;
+ cmsg->cmsg_type = IP_SENDSRCADDR;
+ cmsg->cmsg_len = CMSG_LEN(sizeof (struct in_addr));
+
+ addr = (struct in_addr *)CMSG_DATA(cmsg);
+ addr->s_addr = htonl(local_addr->ip_addr.addr.in4);
#endif
+ }
#ifdef HAVE_IN6_PKTINFO
if (local_addr->ip_addr.family == IPADDR_INET6) {
@@ -810,6 +868,8 @@ NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
ipi = (struct in6_pktinfo *) CMSG_DATA(cmsg);
memcpy(&ipi->ipi6_addr.s6_addr, &local_addr->ip_addr.addr.in6,
sizeof(ipi->ipi6_addr.s6_addr));
+ if (local_addr->if_index != INVALID_IF_INDEX)
+ ipi->ipi6_ifindex = local_addr->if_index;
}
#endif
diff --git a/ntp_io.h b/ntp_io.h
index 1bdcf12..628f736 100644
--- a/ntp_io.h
+++ b/ntp_io.h
@@ -53,6 +53,9 @@ extern void NIO_CloseServerSocket(int sock_fd);
/* Function to check if socket is a server socket */
extern int NIO_IsServerSocket(int sock_fd);
+/* Function to check if client packets can be sent to a server */
+extern int NIO_IsServerConnectable(NTP_Remote_Address *remote_addr);
+
/* Function to transmit a packet */
extern int NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
NTP_Local_Address *local_addr, int length, int process_tx);
diff --git a/ntp_io_linux.c b/ntp_io_linux.c
index df944d7..819792a 100644
--- a/ntp_io_linux.c
+++ b/ntp_io_linux.c
@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
- * Copyright (C) Miroslav Lichvar 2016-2017
+ * Copyright (C) Miroslav Lichvar 2016-2018
*
* 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
@@ -171,6 +171,12 @@ add_interface(CNF_HwTsInterface *conf_iface)
return 0;
}
+ if (ts_info.phc_index < 0) {
+ DEBUG_LOG("PHC missing on %s", req.ifr_name);
+ close(sock_fd);
+ return 0;
+ }
+
ts_config.flags = 0;
ts_config.tx_type = HWTSTAMP_TX_ON;
@@ -230,7 +236,8 @@ add_interface(CNF_HwTsInterface *conf_iface)
iface->tx_comp = conf_iface->tx_comp;
iface->rx_comp = conf_iface->rx_comp;
- iface->clock = HCL_CreateInstance(UTI_Log2ToDouble(MAX(conf_iface->minpoll, MIN_PHC_POLL)));
+ iface->clock = HCL_CreateInstance(conf_iface->min_samples, conf_iface->max_samples,
+ UTI_Log2ToDouble(MAX(conf_iface->minpoll, MIN_PHC_POLL)));
LOG(LOGS_INFO, "Enabled HW timestamping %son %s",
ts_config.rx_filter == HWTSTAMP_FILTER_NONE ? "(TX only) " : "", iface->name);
@@ -315,7 +322,7 @@ check_timestamping_option(int option)
return 0;
if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPING, &option, sizeof (option)) < 0) {
- DEBUG_LOG("Could not enable timestamping option %x", option);
+ DEBUG_LOG("Could not enable timestamping option %x", (unsigned int)option);
close(sock_fd);
return 0;
}
@@ -784,7 +791,7 @@ NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *loc
l2_length = length;
length = extract_udp_data(hdr->msg_iov[0].iov_base, remote_addr, length);
- DEBUG_LOG("Received %d (%d) bytes from error queue for %s:%d fd=%d if=%d tss=%d",
+ DEBUG_LOG("Received %d (%d) bytes from error queue for %s:%d fd=%d if=%d tss=%u",
l2_length, length, UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
local_addr->sock_fd, local_addr->if_index, local_ts->source);
diff --git a/ntp_sources.c b/ntp_sources.c
index ff0be6e..fa21654 100644
--- a/ntp_sources.c
+++ b/ntp_sources.c
@@ -206,12 +206,13 @@ find_slot(NTP_Remote_Address *remote_addr, int *slot, int *found)
unsigned short port;
size = ARR_GetSize(records);
+
+ *slot = 0;
+ *found = 0;
if (remote_addr->ip_addr.family != IPADDR_INET4 &&
- remote_addr->ip_addr.family != IPADDR_INET6) {
- *found = *slot = 0;
+ remote_addr->ip_addr.family != IPADDR_INET6)
return;
- }
hash = UTI_IPToHash(&remote_addr->ip_addr);
port = remote_addr->port;
@@ -230,8 +231,6 @@ find_slot(NTP_Remote_Address *remote_addr, int *slot, int *found)
return;
}
}
-
- *found = 0;
}
/* ================================================== */
@@ -861,48 +860,14 @@ slew_sources(struct timespec *raw,
/* ================================================== */
int
-NSR_TakeSourcesOnline(IPAddr *mask, IPAddr *address)
-{
- SourceRecord *record;
- unsigned int i;
- int any;
-
- NSR_ResolveSources();
-
- any = 0;
- for (i = 0; i < ARR_GetSize(records); i++) {
- record = get_record(i);
- if (record->remote_addr) {
- if (address->family == IPADDR_UNSPEC ||
- !UTI_CompareIPs(&record->remote_addr->ip_addr, address, mask)) {
- any = 1;
- NCR_TakeSourceOnline(record->data);
- }
- }
- }
-
- if (address->family == IPADDR_UNSPEC) {
- struct UnresolvedSource *us;
-
- for (us = unresolved_sources; us; us = us->next) {
- if (us->replacement)
- continue;
- any = 1;
- us->new_source.params.online = 1;
- }
- }
-
- return any;
-}
-
-/* ================================================== */
-
-int
-NSR_TakeSourcesOffline(IPAddr *mask, IPAddr *address)
+NSR_SetConnectivity(IPAddr *mask, IPAddr *address, SRC_Connectivity connectivity)
{
SourceRecord *record, *syncpeer;
unsigned int i, any;
+ if (connectivity != SRC_OFFLINE)
+ NSR_ResolveSources();
+
any = 0;
syncpeer = NULL;
for (i = 0; i < ARR_GetSize(records); i++) {
@@ -915,15 +880,14 @@ NSR_TakeSourcesOffline(IPAddr *mask, IPAddr *address)
syncpeer = record;
continue;
}
- NCR_TakeSourceOffline(record->data);
+ NCR_SetConnectivity(record->data, connectivity);
}
}
}
- /* Take sync peer offline as last to avoid reference switching */
- if (syncpeer) {
- NCR_TakeSourceOffline(syncpeer->data);
- }
+ /* Set the sync peer last to avoid unnecessary reference switching */
+ if (syncpeer)
+ NCR_SetConnectivity(syncpeer->data, connectivity);
if (address->family == IPADDR_UNSPEC) {
struct UnresolvedSource *us;
@@ -932,7 +896,7 @@ NSR_TakeSourcesOffline(IPAddr *mask, IPAddr *address)
if (us->replacement)
continue;
any = 1;
- us->new_source.params.online = 0;
+ us->new_source.params.connectivity = connectivity;
}
}
diff --git a/ntp_sources.h b/ntp_sources.h
index 23e9612..16b62be 100644
--- a/ntp_sources.h
+++ b/ntp_sources.h
@@ -102,14 +102,9 @@ extern void NSR_Initialise(void);
extern void NSR_Finalise(void);
/* This routine is used to indicate that sources whose IP addresses
- match a particular subnet should be set online again. Returns a
- flag indicating whether any hosts matched the address */
-extern int NSR_TakeSourcesOnline(IPAddr *mask, IPAddr *address);
-
-/* This routine is used to indicate that sources whose IP addresses
- match a particular subnet should be set offline. Returns a flag
- indicating whether any hosts matched the address */
-extern int NSR_TakeSourcesOffline(IPAddr *mask, IPAddr *address);
+ match a particular subnet should be set online or offline. It returns
+ a flag indicating whether any hosts matched the address. */
+extern int NSR_SetConnectivity(IPAddr *mask, IPAddr *address, SRC_Connectivity connectivity);
extern int NSR_ModifyMinpoll(IPAddr *address, int new_minpoll);
diff --git a/pktlength.c b/pktlength.c
index 14a43f7..3fd4a0b 100644
--- a/pktlength.c
+++ b/pktlength.c
@@ -119,6 +119,7 @@ static const struct request_length request_lengths[] = {
REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_SERVER3 */
REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_PEER3 */
REQ_LENGTH_ENTRY(null, null), /* SHUTDOWN */
+ REQ_LENGTH_ENTRY(null, null), /* ONOFFLINE */
};
static const uint16_t reply_lengths[] = {
diff --git a/privops.c b/privops.c
index a7ba890..8133351 100644
--- a/privops.c
+++ b/privops.c
@@ -268,7 +268,7 @@ do_bind_socket(ReqBindSocket *req, PrvResponse *res)
sock_fd = req->sock;
UTI_SockaddrToIPAndPort(sa, &ip, &port);
- if (port && port != CNF_GetNTPPort()) {
+ if (port && port != CNF_GetNTPPort() && port != CNF_GetAcquisitionPort()) {
close(sock_fd);
res_fatal(res, "Invalid port %d", port);
return;
@@ -579,7 +579,8 @@ PRV_BindSocket(int sock, struct sockaddr *address, socklen_t address_len)
unsigned short port;
UTI_SockaddrToIPAndPort(address, &ip, &port);
- assert(!port || port == CNF_GetNTPPort());
+ if (port && port != CNF_GetNTPPort() && port != CNF_GetAcquisitionPort())
+ assert(0);
if (!have_helper())
return bind(sock, address, address_len);
@@ -699,7 +700,7 @@ PRV_StartHelper(void)
}
/* ignore signals, the process will exit on OP_QUIT request */
- UTI_SetQuitSignalsHandler(SIG_IGN);
+ UTI_SetQuitSignalsHandler(SIG_IGN, 1);
helper_main(sock_pair[1]);
diff --git a/refclock.c b/refclock.c
index 0791d47..66224d5 100644
--- a/refclock.c
+++ b/refclock.c
@@ -37,6 +37,7 @@
#include "sources.h"
#include "logging.h"
#include "regress.h"
+#include "samplefilt.h"
#include "sched.h"
/* list of refclock drivers */
@@ -81,13 +82,13 @@ struct RCL_Instance_Record {
int max_lock_age;
int stratum;
int tai;
- struct MedianFilter filter;
uint32_t ref_id;
uint32_t lock_ref;
double offset;
double delay;
double precision;
double pulse_width;
+ SPF_Instance filter;
SCH_TimeoutID timeout_id;
SRC_Instance source;
};
@@ -105,18 +106,6 @@ static void slew_samples(struct timespec *raw, struct timespec *cooked, double d
static void add_dispersion(double dispersion, void *anything);
static void log_sample(RCL_Instance instance, struct timespec *sample_time, int filtered, int pulse, double raw_offset, double cooked_offset, double dispersion);
-static void filter_init(struct MedianFilter *filter, int length, double max_dispersion);
-static void filter_fini(struct MedianFilter *filter);
-static void filter_reset(struct MedianFilter *filter);
-static double filter_get_avg_sample_dispersion(struct MedianFilter *filter);
-static void filter_add_sample(struct MedianFilter *filter, struct timespec *sample_time, double offset, double dispersion);
-static int filter_get_last_sample(struct MedianFilter *filter, struct timespec *sample_time, double *offset, double *dispersion);
-static int filter_get_samples(struct MedianFilter *filter);
-static int filter_select_samples(struct MedianFilter *filter);
-static int filter_get_sample(struct MedianFilter *filter, struct timespec *sample_time, double *offset, double *dispersion);
-static void filter_slew_samples(struct MedianFilter *filter, struct timespec *when, double dfreq, double doffset);
-static void filter_add_dispersion(struct MedianFilter *filter, double dispersion);
-
static RCL_Instance
get_refclock(unsigned int index)
{
@@ -151,7 +140,7 @@ RCL_Finalise(void)
if (inst->driver->fini)
inst->driver->fini(inst);
- filter_fini(&inst->filter);
+ SPF_DestroyInstance(inst->filter);
Free(inst->driver_parameter);
SRC_DestroyInstance(inst->source);
Free(inst);
@@ -258,7 +247,11 @@ RCL_AddRefclock(RefclockParameters *params)
if (inst->driver->init && !inst->driver->init(inst))
LOG_FATAL("refclock %s initialisation failed", params->driver_name);
- filter_init(&inst->filter, params->filter_length, params->max_dispersion);
+ /* Require the filter to have at least 4 samples to produce a filtered
+ sample, or be full for shorter lengths, and combine 60% of samples
+ closest to the median */
+ inst->filter = SPF_CreateInstance(MIN(params->filter_length, 4), params->filter_length,
+ params->max_dispersion, 0.6);
inst->source = SRC_CreateNewInstance(inst->ref_id, SRC_REFCLOCK, params->sel_options, NULL,
params->min_samples, params->max_samples, 0.0, 0.0);
@@ -379,6 +372,28 @@ convert_tai_offset(struct timespec *sample_time, double *offset)
return 1;
}
+static int
+accumulate_sample(RCL_Instance instance, struct timespec *sample_time, double offset, double dispersion)
+{
+ NTP_Sample sample;
+
+ sample.time = *sample_time;
+ sample.offset = offset;
+ sample.peer_delay = instance->delay;
+ sample.root_delay = instance->delay;
+ sample.peer_dispersion = dispersion;
+ sample.root_dispersion = dispersion;
+ sample.leap = instance->leap_status;
+
+ /* Handle special case when PPS is used with the local reference */
+ if (instance->pps_active && instance->lock_ref == -1)
+ sample.stratum = pps_stratum(instance, &sample.time);
+ else
+ sample.stratum = instance->stratum;
+
+ return SPF_AccumulateSample(instance->filter, &sample);
+}
+
int
RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset, int leap)
{
@@ -413,7 +428,10 @@ RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset
return 0;
}
- filter_add_sample(&instance->filter, &cooked_time, offset - correction + instance->offset, dispersion);
+ if (!accumulate_sample(instance, &cooked_time,
+ offset - correction + instance->offset, dispersion))
+ return 0;
+
instance->pps_active = 0;
log_sample(instance, &cooked_time, 0, 0, offset, offset - correction + instance->offset, dispersion);
@@ -489,20 +507,19 @@ RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
if (instance->lock_ref != -1) {
RCL_Instance lock_refclock;
- struct timespec ref_sample_time;
- double sample_diff, ref_offset, ref_dispersion, shift;
+ NTP_Sample ref_sample;
+ double sample_diff, shift;
lock_refclock = get_refclock(instance->lock_ref);
- if (!filter_get_last_sample(&lock_refclock->filter,
- &ref_sample_time, &ref_offset, &ref_dispersion)) {
+ if (!SPF_GetLastSample(lock_refclock->filter, &ref_sample)) {
DEBUG_LOG("refclock pulse ignored no ref sample");
return 0;
}
- ref_dispersion += filter_get_avg_sample_dispersion(&lock_refclock->filter);
+ ref_sample.root_dispersion += SPF_GetAvgSampleDispersion(lock_refclock->filter);
- sample_diff = UTI_DiffTimespecsToDouble(cooked_time, &ref_sample_time);
+ 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);
@@ -510,26 +527,27 @@ RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
}
/* Align the offset to the reference sample */
- if ((ref_offset - offset) >= 0.0)
- shift = (long)((ref_offset - offset) * rate + 0.5) / (double)rate;
+ if ((ref_sample.offset - offset) >= 0.0)
+ shift = (long)((ref_sample.offset - offset) * rate + 0.5) / (double)rate;
else
- shift = (long)((ref_offset - offset) * rate - 0.5) / (double)rate;
+ shift = (long)((ref_sample.offset - offset) * rate - 0.5) / (double)rate;
offset += shift;
- if (fabs(ref_offset - offset) + ref_dispersion + dispersion >= 0.2 / rate) {
+ if (fabs(ref_sample.offset - offset) +
+ ref_sample.root_dispersion + dispersion >= 0.2 / rate) {
DEBUG_LOG("refclock pulse ignored offdiff=%.9f refdisp=%.9f disp=%.9f",
- ref_offset - offset, ref_dispersion, dispersion);
+ ref_sample.offset - offset, ref_sample.root_dispersion, dispersion);
return 0;
}
- if (!check_pulse_edge(instance, ref_offset - offset, 0.0))
+ if (!check_pulse_edge(instance, ref_sample.offset - offset, 0.0))
return 0;
leap = lock_refclock->leap_status;
DEBUG_LOG("refclock pulse offset=%.9f offdiff=%.9f samplediff=%.9f",
- offset, ref_offset - offset, sample_diff);
+ offset, ref_sample.offset - offset, sample_diff);
} else {
struct timespec ref_time;
int is_synchronised, stratum;
@@ -547,7 +565,7 @@ RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
DEBUG_LOG("refclock pulse ignored offset=%.9f sync=%d dist=%.9f",
offset, leap != LEAP_Unsynchronised, distance);
/* Drop also all stored samples */
- filter_reset(&instance->filter);
+ SPF_DropSamples(instance->filter);
return 0;
}
@@ -555,7 +573,9 @@ RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
return 0;
}
- filter_add_sample(&instance->filter, cooked_time, offset, dispersion);
+ if (!accumulate_sample(instance, cooked_time, offset, dispersion))
+ return 0;
+
instance->leap_status = leap;
instance->pps_active = 1;
@@ -584,17 +604,13 @@ RCL_GetDriverPoll(RCL_Instance instance)
static int
valid_sample_time(RCL_Instance instance, struct timespec *sample_time)
{
- struct timespec now, last_sample_time;
- double diff, last_offset, last_dispersion;
+ struct timespec now;
+ double diff;
LCL_ReadCookedTime(&now, NULL);
diff = UTI_DiffTimespecsToDouble(&now, sample_time);
- if (diff < 0.0 || diff > UTI_Log2ToDouble(instance->poll + 1) ||
- (filter_get_samples(&instance->filter) > 0 &&
- filter_get_last_sample(&instance->filter, &last_sample_time,
- &last_offset, &last_dispersion) &&
- UTI_CompareTimespecs(&last_sample_time, sample_time) >= 0)) {
+ if (diff < 0.0 || diff > UTI_Log2ToDouble(instance->poll + 1)) {
DEBUG_LOG("%s refclock sample time %s not valid age=%.6f",
UTI_RefidToString(instance->ref_id),
UTI_TimespecToString(sample_time), diff);
@@ -638,6 +654,7 @@ pps_stratum(RCL_Instance instance, struct timespec *ts)
static void
poll_timeout(void *arg)
{
+ NTP_Sample sample;
int poll;
RCL_Instance inst = (RCL_Instance)arg;
@@ -651,26 +668,14 @@ poll_timeout(void *arg)
}
if (!(inst->driver->poll && inst->driver_polled < (1 << (inst->poll - inst->driver_poll)))) {
- double offset, dispersion;
- struct timespec sample_time;
- int sample_ok, stratum;
-
- sample_ok = filter_get_sample(&inst->filter, &sample_time, &offset, &dispersion);
inst->driver_polled = 0;
- if (sample_ok) {
- if (inst->pps_active && inst->lock_ref == -1)
- /* Handle special case when PPS is used with local stratum */
- stratum = pps_stratum(inst, &sample_time);
- else
- stratum = inst->stratum;
-
+ if (SPF_GetFilteredSample(inst->filter, &sample)) {
SRC_UpdateReachability(inst->source, 1);
- SRC_AccumulateSample(inst->source, &sample_time, offset,
- inst->delay, dispersion, inst->delay, dispersion, stratum, inst->leap_status);
+ SRC_AccumulateSample(inst->source, &sample);
SRC_SelectSource(inst->source);
- log_sample(inst, &sample_time, 1, 0, 0.0, offset, dispersion);
+ log_sample(inst, &sample.time, 1, 0, 0.0, sample.offset, sample.peer_dispersion);
} else {
SRC_UpdateReachability(inst->source, 0);
}
@@ -687,9 +692,9 @@ slew_samples(struct timespec *raw, struct timespec *cooked, double dfreq,
for (i = 0; i < ARR_GetSize(refclocks); i++) {
if (change_type == LCL_ChangeUnknownStep)
- filter_reset(&get_refclock(i)->filter);
+ SPF_DropSamples(get_refclock(i)->filter);
else
- filter_slew_samples(&get_refclock(i)->filter, cooked, dfreq, doffset);
+ SPF_SlewSamples(get_refclock(i)->filter, cooked, dfreq, doffset);
}
}
@@ -699,7 +704,7 @@ add_dispersion(double dispersion, void *anything)
unsigned int i;
for (i = 0; i < ARR_GetSize(refclocks); i++)
- filter_add_dispersion(&get_refclock(i)->filter, dispersion);
+ SPF_AddDispersion(get_refclock(i)->filter, dispersion);
}
static void
@@ -731,320 +736,3 @@ log_sample(RCL_Instance instance, struct timespec *sample_time, int filtered, in
dispersion);
}
}
-
-static void
-filter_init(struct MedianFilter *filter, int length, double max_dispersion)
-{
- if (length < 1)
- length = 1;
-
- filter->length = length;
- filter->index = -1;
- filter->used = 0;
- filter->last = -1;
- /* set first estimate to system precision */
- filter->avg_var_n = 0;
- filter->avg_var = LCL_GetSysPrecisionAsQuantum() * LCL_GetSysPrecisionAsQuantum();
- filter->max_var = max_dispersion * max_dispersion;
- filter->samples = MallocArray(struct FilterSample, filter->length);
- filter->selected = MallocArray(int, filter->length);
- filter->x_data = MallocArray(double, filter->length);
- filter->y_data = MallocArray(double, filter->length);
- filter->w_data = MallocArray(double, filter->length);
-}
-
-static void
-filter_fini(struct MedianFilter *filter)
-{
- Free(filter->samples);
- Free(filter->selected);
- Free(filter->x_data);
- Free(filter->y_data);
- Free(filter->w_data);
-}
-
-static void
-filter_reset(struct MedianFilter *filter)
-{
- filter->index = -1;
- filter->used = 0;
-}
-
-static double
-filter_get_avg_sample_dispersion(struct MedianFilter *filter)
-{
- return sqrt(filter->avg_var);
-}
-
-static void
-filter_add_sample(struct MedianFilter *filter, struct timespec *sample_time, double offset, double dispersion)
-{
- filter->index++;
- filter->index %= filter->length;
- filter->last = filter->index;
- if (filter->used < filter->length)
- filter->used++;
-
- filter->samples[filter->index].sample_time = *sample_time;
- filter->samples[filter->index].offset = offset;
- filter->samples[filter->index].dispersion = dispersion;
-
- DEBUG_LOG("filter sample %d t=%s offset=%.9f dispersion=%.9f",
- filter->index, UTI_TimespecToString(sample_time), offset, dispersion);
-}
-
-static int
-filter_get_last_sample(struct MedianFilter *filter, struct timespec *sample_time, double *offset, double *dispersion)
-{
- if (filter->last < 0)
- return 0;
-
- *sample_time = filter->samples[filter->last].sample_time;
- *offset = filter->samples[filter->last].offset;
- *dispersion = filter->samples[filter->last].dispersion;
- return 1;
-}
-
-static int
-filter_get_samples(struct MedianFilter *filter)
-{
- return filter->used;
-}
-
-static const struct FilterSample *tmp_sorted_array;
-
-static int
-sample_compare(const void *a, const void *b)
-{
- const struct FilterSample *s1, *s2;
-
- s1 = &tmp_sorted_array[*(int *)a];
- s2 = &tmp_sorted_array[*(int *)b];
-
- if (s1->offset < s2->offset)
- return -1;
- else if (s1->offset > s2->offset)
- return 1;
- return 0;
-}
-
-int
-filter_select_samples(struct MedianFilter *filter)
-{
- int i, j, k, o, from, to, *selected;
- double min_dispersion;
-
- if (filter->used < 1)
- return 0;
-
- /* for lengths below 4 require full filter,
- for 4 and above require at least 4 samples */
- if ((filter->length < 4 && filter->used != filter->length) ||
- (filter->length >= 4 && filter->used < 4))
- return 0;
-
- selected = filter->selected;
-
- if (filter->used > 4) {
- /* select samples with dispersion better than 1.5 * minimum */
-
- for (i = 1, min_dispersion = filter->samples[0].dispersion; i < filter->used; i++) {
- if (min_dispersion > filter->samples[i].dispersion)
- min_dispersion = filter->samples[i].dispersion;
- }
-
- for (i = j = 0; i < filter->used; i++) {
- if (filter->samples[i].dispersion <= 1.5 * min_dispersion)
- selected[j++] = i;
- }
- } else {
- j = 0;
- }
-
- if (j < 4) {
- /* select all samples */
-
- for (j = 0; j < filter->used; j++)
- selected[j] = j;
- }
-
- /* and sort their indices by offset */
- tmp_sorted_array = filter->samples;
- qsort(selected, j, sizeof (int), sample_compare);
-
- /* select 60 percent of the samples closest to the median */
- if (j > 2) {
- from = j / 5;
- if (from < 1)
- from = 1;
- to = j - from;
- } else {
- from = 0;
- to = j;
- }
-
- /* mark unused samples and sort the rest from oldest to newest */
-
- o = filter->used - filter->index - 1;
-
- for (i = 0; i < from; i++)
- selected[i] = -1;
- for (; i < to; i++)
- selected[i] = (selected[i] + o) % filter->used;
- for (; i < filter->used; i++)
- selected[i] = -1;
-
- for (i = from; i < to; i++) {
- j = selected[i];
- selected[i] = -1;
- while (j != -1 && selected[j] != j) {
- k = selected[j];
- selected[j] = j;
- j = k;
- }
- }
-
- for (i = j = 0, k = -1; i < filter->used; i++) {
- if (selected[i] != -1)
- selected[j++] = (selected[i] + filter->used - o) % filter->used;
- }
-
- return j;
-}
-
-static int
-filter_get_sample(struct MedianFilter *filter, struct timespec *sample_time, double *offset, double *dispersion)
-{
- struct FilterSample *s, *ls;
- int i, n, dof;
- double x, y, d, e, var, prev_avg_var;
-
- n = filter_select_samples(filter);
-
- if (n < 1)
- return 0;
-
- ls = &filter->samples[filter->selected[n - 1]];
-
- /* prepare data */
- for (i = 0; i < n; i++) {
- s = &filter->samples[filter->selected[i]];
-
- filter->x_data[i] = UTI_DiffTimespecsToDouble(&s->sample_time, &ls->sample_time);
- filter->y_data[i] = s->offset;
- filter->w_data[i] = s->dispersion;
- }
-
- /* mean offset, sample time and sample dispersion */
- for (i = 0, x = y = e = 0.0; i < n; i++) {
- x += filter->x_data[i];
- y += filter->y_data[i];
- e += filter->w_data[i];
- }
- x /= n;
- y /= n;
- e /= n;
-
- if (n >= 4) {
- double b0, b1, s2, sb0, sb1;
-
- /* set y axis to the mean sample time */
- for (i = 0; i < n; i++)
- filter->x_data[i] -= x;
-
- /* make a linear fit and use the estimated standard deviation of intercept
- as dispersion */
- RGR_WeightedRegression(filter->x_data, filter->y_data, filter->w_data, n,
- &b0, &b1, &s2, &sb0, &sb1);
- var = s2;
- d = sb0;
- dof = n - 2;
- } else if (n >= 2) {
- for (i = 0, d = 0.0; i < n; i++)
- d += (filter->y_data[i] - y) * (filter->y_data[i] - y);
- var = d / (n - 1);
- d = sqrt(var);
- dof = n - 1;
- } else {
- var = filter->avg_var;
- d = sqrt(var);
- dof = 1;
- }
-
- /* avoid having zero dispersion */
- if (var < 1e-20) {
- var = 1e-20;
- d = sqrt(var);
- }
-
- /* drop the sample if variance is larger than allowed maximum */
- if (filter->max_var > 0.0 && var > filter->max_var) {
- DEBUG_LOG("filter dispersion too large disp=%.9f max=%.9f",
- sqrt(var), sqrt(filter->max_var));
- return 0;
- }
-
- prev_avg_var = filter->avg_var;
-
- /* update exponential moving average of the variance */
- if (filter->avg_var_n > 50) {
- filter->avg_var += dof / (dof + 50.0) * (var - filter->avg_var);
- } else {
- filter->avg_var = (filter->avg_var * filter->avg_var_n + var * dof) /
- (dof + filter->avg_var_n);
- if (filter->avg_var_n == 0)
- prev_avg_var = filter->avg_var;
- filter->avg_var_n += dof;
- }
-
- /* reduce noise in sourcestats weights by using the long-term average
- instead of the estimated variance if it's not significantly lower */
- if (var * dof / RGR_GetChi2Coef(dof) < prev_avg_var)
- d = sqrt(filter->avg_var) * d / sqrt(var);
-
- if (d < e)
- d = e;
-
- UTI_AddDoubleToTimespec(&ls->sample_time, x, sample_time);
- *offset = y;
- *dispersion = d;
-
- filter_reset(filter);
-
- return 1;
-}
-
-static void
-filter_slew_samples(struct MedianFilter *filter, struct timespec *when, double dfreq, double doffset)
-{
- int i, first, last;
- double delta_time;
- struct timespec *sample;
-
- if (filter->last < 0)
- return;
-
- /* always slew the last sample as it may be needed by PPS refclocks */
- if (filter->used > 0) {
- first = 0;
- last = filter->used - 1;
- } else {
- first = last = filter->last;
- }
-
- for (i = first; i <= last; i++) {
- sample = &filter->samples[i].sample_time;
- UTI_AdjustTimespec(sample, when, sample, &delta_time, dfreq, doffset);
- filter->samples[i].offset -= delta_time;
- }
-}
-
-static void
-filter_add_dispersion(struct MedianFilter *filter, double dispersion)
-{
- int i;
-
- for (i = 0; i < filter->used; i++) {
- filter->samples[i].dispersion += dispersion;
- }
-}
diff --git a/refclock_phc.c b/refclock_phc.c
index 6aa5edd..03450db 100644
--- a/refclock_phc.c
+++ b/refclock_phc.c
@@ -80,7 +80,7 @@ static int phc_initialise(RCL_Instance instance)
s = RCL_GetDriverOption(instance, "channel");
phc->channel = s ? atoi(s) : 0;
rising_edge = RCL_GetDriverOption(instance, "clear") ? 0 : 1;
- phc->clock = HCL_CreateInstance(UTI_Log2ToDouble(RCL_GetDriverPoll(instance)));
+ 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))
diff --git a/refclock_pps.c b/refclock_pps.c
index 6f0565e..85ff9e9 100644
--- a/refclock_pps.c
+++ b/refclock_pps.c
@@ -59,24 +59,24 @@ static int pps_initialise(RCL_Instance instance) {
fd = open(path, O_RDWR);
if (fd < 0) {
- LOG_FATAL("open() failed on %s", path);
+ LOG_FATAL("Could not open %s : %s", path, strerror(errno));
return 0;
}
UTI_FdSetCloexec(fd);
if (time_pps_create(fd, &handle) < 0) {
- LOG_FATAL("time_pps_create() failed on %s", path);
+ LOG_FATAL("time_pps_create() failed on %s : %s", path, strerror(errno));
return 0;
}
if (time_pps_getcap(handle, &mode) < 0) {
- LOG_FATAL("time_pps_getcap() failed on %s", path);
+ LOG_FATAL("time_pps_getcap() failed on %s : %s", path, strerror(errno));
return 0;
}
if (time_pps_getparams(handle, &params) < 0) {
- LOG_FATAL("time_pps_getparams() failed on %s", path);
+ LOG_FATAL("time_pps_getparams() failed on %s : %s", path, strerror(errno));
return 0;
}
@@ -97,7 +97,7 @@ static int pps_initialise(RCL_Instance instance) {
}
if (time_pps_setparams(handle, &params) < 0) {
- LOG_FATAL("time_pps_setparams() failed on %s", path);
+ LOG_FATAL("time_pps_setparams() failed on %s : %s", path, strerror(errno));
return 0;
}
@@ -147,7 +147,7 @@ static int pps_poll(RCL_Instance instance)
if (seq == pps->last_seq || UTI_IsZeroTimespec(&ts)) {
DEBUG_LOG("PPS sample ignored seq=%lu ts=%s",
- seq, UTI_TimespecToString(&ts));
+ (unsigned long)seq, UTI_TimespecToString(&ts));
return 0;
}
diff --git a/refclock_shm.c b/refclock_shm.c
index 7cced1d..e8f6256 100644
--- a/refclock_shm.c
+++ b/refclock_shm.c
@@ -69,13 +69,13 @@ static int shm_initialise(RCL_Instance instance) {
id = shmget(SHMKEY + param, sizeof (struct shmTime), IPC_CREAT | perm);
if (id == -1) {
- LOG_FATAL("shmget() failed");
+ LOG_FATAL("shmget() failed : %s", strerror(errno));
return 0;
}
shm = (struct shmTime *)shmat(id, 0, 0);
if ((long)shm == -1) {
- LOG_FATAL("shmat() failed");
+ LOG_FATAL("shmat() failed : %s", strerror(errno));
return 0;
}
diff --git a/refclock_sock.c b/refclock_sock.c
index eb96147..176310c 100644
--- a/refclock_sock.c
+++ b/refclock_sock.c
@@ -69,20 +69,19 @@ static void read_sample(int sockfd, int event, void *anything)
s = recv(sockfd, &sample, sizeof (sample), 0);
if (s < 0) {
- LOG(LOGS_ERR, "Could not read SOCK sample : %s",
- strerror(errno));
+ DEBUG_LOG("Could not read SOCK sample : %s", strerror(errno));
return;
}
if (s != sizeof (sample)) {
- LOG(LOGS_WARN, "Unexpected length of SOCK sample : %d != %ld",
- s, (long)sizeof (sample));
+ DEBUG_LOG("Unexpected length of SOCK sample : %d != %ld",
+ s, (long)sizeof (sample));
return;
}
if (sample.magic != SOCK_MAGIC) {
- LOG(LOGS_WARN, "Unexpected magic number in SOCK sample : %x != %x",
- sample.magic, SOCK_MAGIC);
+ DEBUG_LOG("Unexpected magic number in SOCK sample : %x != %x",
+ (unsigned int)sample.magic, (unsigned int)SOCK_MAGIC);
return;
}
@@ -106,7 +105,7 @@ static int sock_initialise(RCL_Instance instance)
s.sun_family = AF_UNIX;
if (snprintf(s.sun_path, sizeof (s.sun_path), "%s", path) >= sizeof (s.sun_path)) {
- LOG_FATAL("path %s is too long", path);
+ LOG_FATAL("Path %s too long", path);
return 0;
}
@@ -120,7 +119,7 @@ static int sock_initialise(RCL_Instance instance)
unlink(path);
if (bind(sockfd, (struct sockaddr *)&s, sizeof (s)) < 0) {
- LOG_FATAL("bind() failed");
+ LOG_FATAL("bind(%s) failed : %s", path, strerror(errno));
return 0;
}
diff --git a/reference.c b/reference.c
index dff7ab9..2f1ffc8 100644
--- a/reference.c
+++ b/reference.c
@@ -326,23 +326,6 @@ REF_GetLeapMode(void)
}
/* ================================================== */
-
-static double
-Sqr(double x)
-{
- return x*x;
-}
-
-/* ================================================== */
-#if 0
-static double
-Cube(double x)
-{
- return x*x*x;
-}
-#endif
-
-/* ================================================== */
/* Update the drift coefficients to the file. */
static void
@@ -528,7 +511,7 @@ maybe_log_offset(double offset, time_t now)
double abs_offset;
FILE *p;
char buffer[BUFLEN], host[BUFLEN];
- struct tm stm;
+ struct tm *tm;
abs_offset = fabs(offset);
@@ -539,17 +522,25 @@ maybe_log_offset(double offset, time_t now)
if (do_mail_change &&
(abs_offset > mail_change_threshold)) {
- snprintf(buffer, sizeof(buffer), "%s %." S_MAX_USER_LEN "s", MAIL_PROGRAM, mail_change_user);
+ snprintf(buffer, sizeof (buffer), "%s -t", MAIL_PROGRAM);
p = popen(buffer, "w");
if (p) {
if (gethostname(host, sizeof(host)) < 0) {
strcpy(host, "<UNKNOWN>");
}
+ host[sizeof (host) - 1] = '\0';
+
+ fprintf(p, "To: %s\n", mail_change_user);
fprintf(p, "Subject: chronyd reports change to system clock on node [%s]\n", host);
fputs("\n", p);
- stm = *localtime(&now);
- strftime(buffer, sizeof(buffer), "On %A, %d %B %Y\n with the system clock reading %H:%M:%S (%Z)", &stm);
- fputs(buffer, p);
+
+ tm = localtime(&now);
+ if (tm) {
+ strftime(buffer, sizeof (buffer),
+ "On %A, %d %B %Y\n with the system clock reading %H:%M:%S (%Z)", tm);
+ fputs(buffer, p);
+ }
+
/* If offset < 0 the local clock is slow, so we are applying a
positive change to it to bring it into line, hence the
negation of 'offset' in the next statement (and earlier) */
@@ -929,34 +920,58 @@ special_mode_sync(int valid, double offset)
/* ================================================== */
+static void
+get_clock_estimates(int manual,
+ double measured_freq, double measured_skew,
+ double *estimated_freq, double *estimated_skew,
+ double *residual_freq)
+{
+ double gain, expected_freq, expected_skew, extra_skew;
+
+ /* We assume that the local clock is running according to our previously
+ determined value */
+ expected_freq = 0.0;
+ expected_skew = our_skew;
+
+ /* Set new frequency based on weighted average of the expected and measured
+ skew. Disable updates that are based on totally unreliable frequency
+ information unless it is a manual reference. */
+ if (manual) {
+ gain = 1.0;
+ } else if (fabs(measured_skew) > max_update_skew) {
+ DEBUG_LOG("Skew %f too large to track", measured_skew);
+ gain = 0.0;
+ } else {
+ gain = 3.0 * SQUARE(expected_skew) /
+ (3.0 * SQUARE(expected_skew) + SQUARE(measured_skew));
+ }
+
+ gain = CLAMP(0.0, gain, 1.0);
+
+ *estimated_freq = expected_freq + gain * (measured_freq - expected_freq);
+ *residual_freq = measured_freq - *estimated_freq;
+
+ extra_skew = sqrt(SQUARE(expected_freq - *estimated_freq) * (1.0 - gain) +
+ SQUARE(measured_freq - *estimated_freq) * gain);
+
+ *estimated_skew = expected_skew + gain * (measured_skew - expected_skew) + extra_skew;
+}
+
+/* ================================================== */
+
void
-REF_SetReference(int stratum,
- NTP_Leap leap,
- int combined_sources,
- uint32_t ref_id,
- IPAddr *ref_ip,
- struct timespec *ref_time,
- double offset,
- double offset_sd,
- double frequency,
- double skew,
- double root_delay,
- double root_dispersion
- )
+REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
+ uint32_t ref_id, IPAddr *ref_ip, struct timespec *ref_time,
+ double offset, double offset_sd,
+ double frequency, double frequency_sd, double skew,
+ double root_delay, double root_dispersion)
{
- double previous_skew, new_skew;
- double previous_freq, new_freq;
- double old_weight, new_weight, sum_weight;
- double delta_freq1, delta_freq2;
- double skew1, skew2;
- double our_offset;
- double our_frequency;
- double abs_freq_ppm;
- double update_interval;
- double elapsed, correction_rate, orig_root_distance;
double uncorrected_offset, accumulate_offset, step_offset;
+ double residual_frequency, local_abs_frequency;
+ double elapsed, update_interval, correction_rate, orig_root_distance;
struct timespec now, raw_now;
NTP_int64 ref_fuzz;
+ int manual;
assert(initialised);
@@ -966,23 +981,33 @@ REF_SetReference(int stratum,
return;
}
- /* Guard against dividing by zero and NaN */
- if (!(skew > MIN_SKEW))
- skew = MIN_SKEW;
+ manual = leap == LEAP_Unsynchronised;
LCL_ReadRawTime(&raw_now);
LCL_GetOffsetCorrection(&raw_now, &uncorrected_offset, NULL);
UTI_AddDoubleToTimespec(&raw_now, uncorrected_offset, &now);
elapsed = UTI_DiffTimespecsToDouble(&now, ref_time);
- our_offset = offset + elapsed * frequency;
+ offset += elapsed * frequency;
+ offset_sd += elapsed * frequency_sd;
- if (!is_offset_ok(our_offset))
+ if (last_ref_update.tv_sec) {
+ update_interval = UTI_DiffTimespecsToDouble(&now, &last_ref_update);
+ update_interval = MAX(update_interval, 0.0);
+ } else {
+ update_interval = 0.0;
+ }
+
+ /* Get new estimates of the frequency and skew including the new data */
+ get_clock_estimates(manual, frequency, skew,
+ &frequency, &skew, &residual_frequency);
+
+ if (!is_offset_ok(offset))
return;
orig_root_distance = our_root_delay / 2.0 + get_root_dispersion(&now);
- are_we_synchronised = leap != LEAP_Unsynchronised ? 1 : 0;
+ are_we_synchronised = leap != LEAP_Unsynchronised;
our_stratum = stratum + 1;
our_ref_id = ref_id;
if (ref_ip)
@@ -990,17 +1015,13 @@ REF_SetReference(int stratum,
else
our_ref_ip.family = IPADDR_UNSPEC;
our_ref_time = *ref_time;
+ our_skew = skew;
+ our_residual_freq = residual_frequency;
our_root_delay = root_delay;
our_root_dispersion = root_dispersion;
-
- if (last_ref_update.tv_sec) {
- update_interval = UTI_DiffTimespecsToDouble(&now, &last_ref_update);
- if (update_interval < 0.0)
- update_interval = 0.0;
- } else {
- update_interval = 0.0;
- }
last_ref_update = now;
+ 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.
@@ -1018,68 +1039,28 @@ REF_SetReference(int stratum,
correction_rate = correction_time_ratio * 0.5 * offset_sd * update_interval;
/* Check if the clock should be stepped */
- if (is_step_limit_reached(our_offset, uncorrected_offset)) {
+ if (is_step_limit_reached(offset, uncorrected_offset)) {
/* Cancel the uncorrected offset and correct the total offset by step */
accumulate_offset = uncorrected_offset;
- step_offset = our_offset - uncorrected_offset;
+ step_offset = offset - uncorrected_offset;
} else {
- accumulate_offset = our_offset;
+ accumulate_offset = offset;
step_offset = 0.0;
}
- /* Eliminate updates that are based on totally unreliable frequency
- information. Ignore this limit with manual reference. */
-
- if (fabs(skew) < max_update_skew || leap == LEAP_Unsynchronised) {
-
- previous_skew = our_skew;
- new_skew = skew;
-
- previous_freq = 0.0; /* We assume that the local clock is running
- according to our previously determined
- value; note that this is a delta frequency
- --- absolute frequencies are only known in
- the local module. */
- new_freq = frequency;
-
- /* Set new frequency based on weighted average of old and new skew. With
- manual reference the old frequency has no weight. */
-
- old_weight = leap != LEAP_Unsynchronised ? 1.0 / Sqr(previous_skew) : 0.0;
- new_weight = 3.0 / Sqr(new_skew);
-
- sum_weight = old_weight + new_weight;
-
- our_frequency = (previous_freq * old_weight + new_freq * new_weight) / sum_weight;
-
- delta_freq1 = previous_freq - our_frequency;
- delta_freq2 = new_freq - our_frequency;
-
- skew1 = sqrt((Sqr(delta_freq1) * old_weight + Sqr(delta_freq2) * new_weight) / sum_weight);
- skew2 = (previous_skew * old_weight + new_skew * new_weight) / sum_weight;
- our_skew = skew1 + skew2;
-
- our_residual_freq = new_freq - our_frequency;
-
- LCL_AccumulateFrequencyAndOffset(our_frequency, accumulate_offset, correction_rate);
+ /* Adjust the clock */
+ LCL_AccumulateFrequencyAndOffset(frequency, accumulate_offset, correction_rate);
- } else {
- DEBUG_LOG("Skew %f too large to track, offset=%f", skew, accumulate_offset);
-
- LCL_AccumulateOffset(accumulate_offset, correction_rate);
-
- our_residual_freq = frequency;
- }
-
update_leap_status(leap, raw_now.tv_sec, 0);
- maybe_log_offset(our_offset, raw_now.tv_sec);
+ maybe_log_offset(offset, raw_now.tv_sec);
if (step_offset != 0.0) {
if (LCL_ApplyStepOffset(step_offset))
LOG(LOGS_WARN, "System clock was stepped by %.6f seconds", -step_offset);
}
- LCL_SetSyncStatus(are_we_synchronised, offset_sd, offset_sd + root_delay / 2.0 + root_dispersion);
+ LCL_SetSyncStatus(are_we_synchronised, offset_sd,
+ root_delay / 2.0 + get_root_dispersion(&now));
/* Add a random error of up to one second to the reference time to make it
less useful when disclosed to NTP and cmdmon clients for estimating
@@ -1090,36 +1071,33 @@ REF_SetReference(int stratum,
if (UTI_CompareTimespecs(&our_ref_time, ref_time) >= 0)
our_ref_time.tv_sec--;
- abs_freq_ppm = LCL_ReadAbsoluteFrequency();
+ local_abs_frequency = LCL_ReadAbsoluteFrequency();
- write_log(&now, combined_sources, abs_freq_ppm, our_offset, offset_sd,
- uncorrected_offset, orig_root_distance);
+ write_log(&now, combined_sources, local_abs_frequency,
+ offset, offset_sd, uncorrected_offset, orig_root_distance);
if (drift_file) {
/* Update drift file at most once per hour */
drift_file_age += update_interval;
if (drift_file_age < 0.0 || drift_file_age > 3600.0) {
- update_drift_file(abs_freq_ppm, our_skew);
+ update_drift_file(local_abs_frequency, our_skew);
drift_file_age = 0.0;
}
}
/* Update fallback drifts */
if (fb_drifts && are_we_synchronised) {
- update_fb_drifts(abs_freq_ppm, update_interval);
+ update_fb_drifts(local_abs_frequency, update_interval);
schedule_fb_drift(&now);
}
- last_ref_update_interval = update_interval;
- last_offset = our_offset;
-
/* Update the moving average of squares of offset, quickly on start */
if (avg2_moving) {
- avg2_offset += 0.1 * (our_offset * our_offset - avg2_offset);
+ avg2_offset += 0.1 * (SQUARE(offset) - avg2_offset);
} else {
- if (avg2_offset > 0.0 && avg2_offset < our_offset * our_offset)
+ if (avg2_offset > 0.0 && avg2_offset < SQUARE(offset))
avg2_moving = 1;
- avg2_offset = our_offset * our_offset;
+ avg2_offset = SQUARE(offset);
}
}
@@ -1138,7 +1116,7 @@ REF_SetManualReference
only supposed to be used with the local source option, really.
Log as MANU in the tracking log, packets will have NTP_REFID_LOCAL. */
REF_SetReference(0, LEAP_Unsynchronised, 1, 0x4D414E55UL, NULL,
- ref_time, offset, 0.0, frequency, skew, 0.0, 0.0);
+ ref_time, offset, 0.0, frequency, skew, skew, 0.0, 0.0);
}
/* ================================================== */
diff --git a/reference.h b/reference.h
index 6ee4953..4f57f0e 100644
--- a/reference.h
+++ b/reference.h
@@ -144,6 +144,7 @@ extern void REF_SetReference
double offset,
double offset_sd,
double frequency,
+ double frequency_sd,
double skew,
double root_delay,
double root_dispersion
diff --git a/rtc_linux.c b/rtc_linux.c
index 89855c2..d4d9bd0 100644
--- a/rtc_linux.c
+++ b/rtc_linux.c
@@ -352,7 +352,7 @@ rtc_from_t(const time_t *t)
static time_t
t_from_rtc(struct tm *stm) {
- struct tm temp1, temp2;
+ struct tm temp1, temp2, *tm;
long diff;
time_t t1, t2;
@@ -360,12 +360,14 @@ t_from_rtc(struct tm *stm) {
temp1.tm_isdst = 0;
t1 = mktime(&temp1);
- if (rtc_on_utc) {
- temp2 = *gmtime(&t1);
- } else {
- temp2 = *localtime(&t1);
+
+ tm = rtc_on_utc ? gmtime(&t1) : localtime(&t1);
+ if (!tm) {
+ DEBUG_LOG("gmtime()/localtime() failed");
+ return -1;
}
-
+
+ temp2 = *tm;
temp2.tm_isdst = 0;
t2 = mktime(&temp2);
diff = t2 - t1;
diff --git a/samplefilt.c b/samplefilt.c
new file mode 100644
index 0000000..7226963
--- /dev/null
+++ b/samplefilt.c
@@ -0,0 +1,453 @@
+/*
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar 2009-2011, 2014, 2016, 2018
+ *
+ * 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.
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Routines implementing a median sample filter.
+
+ */
+
+#include "config.h"
+
+#include "local.h"
+#include "logging.h"
+#include "memory.h"
+#include "regress.h"
+#include "samplefilt.h"
+#include "util.h"
+
+#define MIN_SAMPLES 1
+#define MAX_SAMPLES 256
+
+struct SPF_Instance_Record {
+ int min_samples;
+ int max_samples;
+ int index;
+ int used;
+ int last;
+ int avg_var_n;
+ double avg_var;
+ double max_var;
+ double combine_ratio;
+ NTP_Sample *samples;
+ int *selected;
+ double *x_data;
+ double *y_data;
+ double *w_data;
+};
+
+/* ================================================== */
+
+SPF_Instance
+SPF_CreateInstance(int min_samples, int max_samples, double max_dispersion, double combine_ratio)
+{
+ SPF_Instance filter;
+
+ filter = MallocNew(struct SPF_Instance_Record);
+
+ min_samples = CLAMP(MIN_SAMPLES, min_samples, MAX_SAMPLES);
+ max_samples = CLAMP(MIN_SAMPLES, max_samples, MAX_SAMPLES);
+ max_samples = MAX(min_samples, max_samples);
+ combine_ratio = CLAMP(0.0, combine_ratio, 1.0);
+
+ filter->min_samples = min_samples;
+ filter->max_samples = max_samples;
+ filter->index = -1;
+ filter->used = 0;
+ filter->last = -1;
+ /* Set the first estimate to the system precision */
+ filter->avg_var_n = 0;
+ filter->avg_var = LCL_GetSysPrecisionAsQuantum() * LCL_GetSysPrecisionAsQuantum();
+ filter->max_var = SQUARE(max_dispersion);
+ filter->combine_ratio = combine_ratio;
+ filter->samples = MallocArray(NTP_Sample, filter->max_samples);
+ filter->selected = MallocArray(int, filter->max_samples);
+ filter->x_data = MallocArray(double, filter->max_samples);
+ filter->y_data = MallocArray(double, filter->max_samples);
+ filter->w_data = MallocArray(double, filter->max_samples);
+
+ return filter;
+}
+
+/* ================================================== */
+
+void
+SPF_DestroyInstance(SPF_Instance filter)
+{
+ Free(filter->samples);
+ Free(filter->selected);
+ Free(filter->x_data);
+ Free(filter->y_data);
+ Free(filter->w_data);
+ Free(filter);
+}
+
+/* ================================================== */
+
+/* Check that samples times are strictly increasing */
+
+static int
+check_sample(SPF_Instance filter, NTP_Sample *sample)
+{
+ if (filter->used <= 0)
+ return 1;
+
+ if (UTI_CompareTimespecs(&filter->samples[filter->last].time, &sample->time) >= 0) {
+ DEBUG_LOG("filter non-increasing sample time %s", UTI_TimespecToString(&sample->time));
+ return 0;
+ }
+
+ return 1;
+}
+
+/* ================================================== */
+
+int
+SPF_AccumulateSample(SPF_Instance filter, NTP_Sample *sample)
+{
+ if (!check_sample(filter, sample))
+ return 0;
+
+ filter->index++;
+ filter->index %= filter->max_samples;
+ filter->last = filter->index;
+ if (filter->used < filter->max_samples)
+ filter->used++;
+
+ filter->samples[filter->index] = *sample;
+
+ DEBUG_LOG("filter sample %d t=%s offset=%.9f peer_disp=%.9f",
+ filter->index, UTI_TimespecToString(&sample->time),
+ sample->offset, sample->peer_dispersion);
+ return 1;
+}
+
+/* ================================================== */
+
+int
+SPF_GetLastSample(SPF_Instance filter, NTP_Sample *sample)
+{
+ if (filter->last < 0)
+ return 0;
+
+ *sample = filter->samples[filter->last];
+ return 1;
+}
+
+/* ================================================== */
+
+int
+SPF_GetNumberOfSamples(SPF_Instance filter)
+{
+ return filter->used;
+}
+
+/* ================================================== */
+
+double
+SPF_GetAvgSampleDispersion(SPF_Instance filter)
+{
+ return sqrt(filter->avg_var);
+}
+
+/* ================================================== */
+
+void
+SPF_DropSamples(SPF_Instance filter)
+{
+ filter->index = -1;
+ filter->used = 0;
+}
+
+/* ================================================== */
+
+static const NTP_Sample *tmp_sort_samples;
+
+static int
+compare_samples(const void *a, const void *b)
+{
+ const NTP_Sample *s1, *s2;
+
+ s1 = &tmp_sort_samples[*(int *)a];
+ s2 = &tmp_sort_samples[*(int *)b];
+
+ if (s1->offset < s2->offset)
+ return -1;
+ else if (s1->offset > s2->offset)
+ return 1;
+ return 0;
+}
+
+/* ================================================== */
+
+static int
+select_samples(SPF_Instance filter)
+{
+ int i, j, k, o, from, to, *selected;
+ double min_dispersion;
+
+ if (filter->used < filter->min_samples)
+ return 0;
+
+ selected = filter->selected;
+
+ /* With 4 or more samples, select those that have peer dispersion smaller
+ than 1.5x of the minimum dispersion */
+ if (filter->used > 4) {
+ for (i = 1, min_dispersion = filter->samples[0].peer_dispersion; i < filter->used; i++) {
+ if (min_dispersion > filter->samples[i].peer_dispersion)
+ min_dispersion = filter->samples[i].peer_dispersion;
+ }
+
+ for (i = j = 0; i < filter->used; i++) {
+ if (filter->samples[i].peer_dispersion <= 1.5 * min_dispersion)
+ selected[j++] = i;
+ }
+ } else {
+ j = 0;
+ }
+
+ if (j < 4) {
+ /* Select all samples */
+
+ for (j = 0; j < filter->used; j++)
+ selected[j] = j;
+ }
+
+ /* And sort their indices by offset */
+ tmp_sort_samples = filter->samples;
+ qsort(selected, j, sizeof (int), compare_samples);
+
+ /* Select samples closest to the median */
+ if (j > 2) {
+ from = j * (1.0 - filter->combine_ratio) / 2.0;
+ from = CLAMP(1, from, (j - 1) / 2);
+ } else {
+ from = 0;
+ }
+
+ to = j - from;
+
+ /* Mark unused samples and sort the rest by their time */
+
+ o = filter->used - filter->index - 1;
+
+ for (i = 0; i < from; i++)
+ selected[i] = -1;
+ for (; i < to; i++)
+ selected[i] = (selected[i] + o) % filter->used;
+ for (; i < filter->used; i++)
+ selected[i] = -1;
+
+ for (i = from; i < to; i++) {
+ j = selected[i];
+ selected[i] = -1;
+ while (j != -1 && selected[j] != j) {
+ k = selected[j];
+ selected[j] = j;
+ j = k;
+ }
+ }
+
+ for (i = j = 0, k = -1; i < filter->used; i++) {
+ if (selected[i] != -1)
+ selected[j++] = (selected[i] + filter->used - o) % filter->used;
+ }
+
+ assert(j > 0 && j <= filter->max_samples);
+
+ return j;
+}
+
+/* ================================================== */
+
+static int
+combine_selected_samples(SPF_Instance filter, int n, NTP_Sample *result)
+{
+ double mean_peer_dispersion, mean_root_dispersion, mean_peer_delay, mean_root_delay;
+ double mean_x, mean_y, disp, var, prev_avg_var;
+ NTP_Sample *sample, *last_sample;
+ int i, dof;
+
+ last_sample = &filter->samples[filter->selected[n - 1]];
+
+ /* Prepare data */
+ for (i = 0; i < n; i++) {
+ sample = &filter->samples[filter->selected[i]];
+
+ filter->x_data[i] = UTI_DiffTimespecsToDouble(&sample->time, &last_sample->time);
+ filter->y_data[i] = sample->offset;
+ filter->w_data[i] = sample->peer_dispersion;
+ }
+
+ /* Calculate mean offset and interval since the last sample */
+ for (i = 0, mean_x = mean_y = 0.0; i < n; i++) {
+ mean_x += filter->x_data[i];
+ mean_y += filter->y_data[i];
+ }
+ mean_x /= n;
+ mean_y /= n;
+
+ if (n >= 4) {
+ double b0, b1, s2, sb0, sb1;
+
+ /* Set y axis to the mean sample time */
+ for (i = 0; i < n; i++)
+ filter->x_data[i] -= mean_x;
+
+ /* Make a linear fit and use the estimated standard deviation of the
+ intercept as dispersion */
+ RGR_WeightedRegression(filter->x_data, filter->y_data, filter->w_data, n,
+ &b0, &b1, &s2, &sb0, &sb1);
+ var = s2;
+ disp = sb0;
+ dof = n - 2;
+ } else if (n >= 2) {
+ for (i = 0, disp = 0.0; i < n; i++)
+ disp += (filter->y_data[i] - mean_y) * (filter->y_data[i] - mean_y);
+ var = disp / (n - 1);
+ disp = sqrt(var);
+ dof = n - 1;
+ } else {
+ var = filter->avg_var;
+ disp = sqrt(var);
+ dof = 1;
+ }
+
+ /* Avoid working with zero dispersion */
+ if (var < 1e-20) {
+ var = 1e-20;
+ disp = sqrt(var);
+ }
+
+ /* Drop the sample if the variance is larger than the maximum */
+ if (filter->max_var > 0.0 && var > filter->max_var) {
+ DEBUG_LOG("filter dispersion too large disp=%.9f max=%.9f",
+ sqrt(var), sqrt(filter->max_var));
+ return 0;
+ }
+
+ prev_avg_var = filter->avg_var;
+
+ /* Update the exponential moving average of the variance */
+ if (filter->avg_var_n > 50) {
+ filter->avg_var += dof / (dof + 50.0) * (var - filter->avg_var);
+ } else {
+ filter->avg_var = (filter->avg_var * filter->avg_var_n + var * dof) /
+ (dof + filter->avg_var_n);
+ if (filter->avg_var_n == 0)
+ prev_avg_var = filter->avg_var;
+ filter->avg_var_n += dof;
+ }
+
+ /* Use the long-term average of variance instead of the estimated value
+ unless it is significantly smaller in order to reduce the noise in
+ sourcestats weights */
+ if (var * dof / RGR_GetChi2Coef(dof) < prev_avg_var)
+ disp = sqrt(filter->avg_var) * disp / sqrt(var);
+
+ mean_peer_dispersion = mean_root_dispersion = mean_peer_delay = mean_root_delay = 0.0;
+
+ for (i = 0; i < n; i++) {
+ sample = &filter->samples[filter->selected[i]];
+
+ mean_peer_dispersion += sample->peer_dispersion;
+ mean_root_dispersion += sample->root_dispersion;
+ mean_peer_delay += sample->peer_delay;
+ mean_root_delay += sample->root_delay;
+ }
+
+ mean_peer_dispersion /= n;
+ mean_root_dispersion /= n;
+ mean_peer_delay /= n;
+ mean_root_delay /= n;
+
+ UTI_AddDoubleToTimespec(&last_sample->time, mean_x, &result->time);
+ result->offset = mean_y;
+ result->peer_dispersion = MAX(disp, mean_peer_dispersion);
+ result->root_dispersion = MAX(disp, mean_root_dispersion);
+ result->peer_delay = mean_peer_delay;
+ result->root_delay = mean_root_delay;
+ result->stratum = last_sample->stratum;
+ result->leap = last_sample->leap;
+
+ return 1;
+}
+
+/* ================================================== */
+
+int
+SPF_GetFilteredSample(SPF_Instance filter, NTP_Sample *sample)
+{
+ int n;
+
+ n = select_samples(filter);
+
+ if (n < 1)
+ return 0;
+
+ if (!combine_selected_samples(filter, n, sample))
+ return 0;
+
+ SPF_DropSamples(filter);
+
+ return 1;
+}
+
+/* ================================================== */
+
+void
+SPF_SlewSamples(SPF_Instance filter, struct timespec *when, double dfreq, double doffset)
+{
+ int i, first, last;
+ double delta_time;
+
+ if (filter->last < 0)
+ 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);
+ filter->samples[i].offset -= delta_time;
+ }
+}
+
+/* ================================================== */
+
+void
+SPF_AddDispersion(SPF_Instance filter, double dispersion)
+{
+ int i;
+
+ for (i = 0; i < filter->used; i++) {
+ filter->samples[i].peer_dispersion += dispersion;
+ filter->samples[i].root_dispersion += dispersion;
+ }
+}
diff --git a/samplefilt.h b/samplefilt.h
new file mode 100644
index 0000000..3b4350a
--- /dev/null
+++ b/samplefilt.h
@@ -0,0 +1,49 @@
+/*
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar 2018
+ *
+ * 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 sample filter.
+
+ */
+
+#ifndef GOT_SAMPLEFILT_H
+#define GOT_SAMPLEFILT_H
+
+#include "ntp.h"
+
+typedef struct SPF_Instance_Record *SPF_Instance;
+
+extern SPF_Instance SPF_CreateInstance(int min_samples, int max_samples,
+ double max_dispersion, double combine_ratio);
+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 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_AddDispersion(SPF_Instance filter, double dispersion);
+
+#endif
diff --git a/sched.c b/sched.c
index 45ef11c..fce9f06 100644
--- a/sched.c
+++ b/sched.c
@@ -534,7 +534,8 @@ dispatch_filehandlers(int nfd, fd_set *read_fds, fd_set *write_fds, fd_set *exce
if (except_fds && FD_ISSET(fd, except_fds)) {
/* This descriptor has an exception, dispatch its handler */
ptr = (FileHandlerEntry *)ARR_GetElement(file_handlers, fd);
- (ptr->handler)(fd, SCH_FILE_EXCEPTION, ptr->arg);
+ if (ptr->handler)
+ (ptr->handler)(fd, SCH_FILE_EXCEPTION, ptr->arg);
nfd--;
/* Don't try to read from it now */
@@ -547,14 +548,16 @@ dispatch_filehandlers(int nfd, fd_set *read_fds, fd_set *write_fds, fd_set *exce
if (read_fds && FD_ISSET(fd, read_fds)) {
/* This descriptor can be read from, dispatch its handler */
ptr = (FileHandlerEntry *)ARR_GetElement(file_handlers, fd);
- (ptr->handler)(fd, SCH_FILE_INPUT, ptr->arg);
+ if (ptr->handler)
+ (ptr->handler)(fd, SCH_FILE_INPUT, ptr->arg);
nfd--;
}
if (write_fds && FD_ISSET(fd, write_fds)) {
/* This descriptor can be written to, dispatch its handler */
ptr = (FileHandlerEntry *)ARR_GetElement(file_handlers, fd);
- (ptr->handler)(fd, SCH_FILE_OUTPUT, ptr->arg);
+ if (ptr->handler)
+ (ptr->handler)(fd, SCH_FILE_OUTPUT, ptr->arg);
nfd--;
}
}
diff --git a/smooth.c b/smooth.c
index a21dcd8..4125ea0 100644
--- a/smooth.c
+++ b/smooth.c
@@ -144,7 +144,7 @@ update_stages(void)
is equal to the offset that should be smoothed out */
s1 = smooth_offset / max_wander;
- s2 = smooth_freq * smooth_freq / (2.0 * max_wander * max_wander);
+ s2 = SQUARE(smooth_freq) / (2.0 * SQUARE(max_wander));
/* Calculate the lengths of the 1st and 3rd stage assuming there is no
frequency limit. The direction of the 1st stage is selected so that
diff --git a/sources.c b/sources.c
index 8aaa4a6..47d6638 100644
--- a/sources.c
+++ b/sources.c
@@ -54,6 +54,7 @@ static int initialised = 0;
/* ================================================== */
/* Structure used to hold info for selecting between sources */
struct SelectInfo {
+ NTP_Leap leap;
int stratum;
int select_ok;
double std_dev;
@@ -91,7 +92,6 @@ typedef enum {
source */
struct SRC_Instance_Record {
SST_Stats stats;
- NTP_Leap leap_status; /* Leap status */
int index; /* Index back into the array of source */
uint32_t ref_id; /* The reference ID of this source
(i.e. from its IP address, NOT the
@@ -291,7 +291,6 @@ void SRC_DestroyInstance(SRC_Instance instance)
void
SRC_ResetInstance(SRC_Instance instance)
{
- instance->leap_status = LEAP_Normal;
instance->active = 0;
instance->updates = 0;
instance->reachability = 0;
@@ -330,39 +329,24 @@ SRC_GetSourcestats(SRC_Instance instance)
This function causes the frequency estimation to be re-run for the
designated source, and the clock selection procedure to be re-run
afterwards.
-
- Parameters are described in sources.h
-
*/
-void SRC_AccumulateSample
-(SRC_Instance inst,
- struct timespec *sample_time,
- double offset,
- double peer_delay,
- double peer_dispersion,
- double root_delay,
- double root_dispersion,
- int stratum,
- NTP_Leap leap_status)
+void
+SRC_AccumulateSample(SRC_Instance inst, NTP_Sample *sample)
{
assert(initialised);
- inst->leap_status = leap_status;
-
DEBUG_LOG("ip=[%s] t=%s ofs=%f del=%f disp=%f str=%d",
- source_to_string(inst), UTI_TimespecToString(sample_time), -offset,
- root_delay, root_dispersion, stratum);
+ source_to_string(inst), UTI_TimespecToString(&sample->time), -sample->offset,
+ sample->root_delay, sample->root_dispersion, sample->stratum);
if (REF_IsLeapSecondClose()) {
LOG(LOGS_INFO, "Dropping sample around leap second");
return;
}
- /* WE HAVE TO NEGATE OFFSET IN THIS CALL, IT IS HERE THAT THE SENSE OF OFFSET
- IS FLIPPED */
- SST_AccumulateSample(inst->stats, sample_time, -offset, peer_delay, peer_dispersion, root_delay, root_dispersion, stratum);
+ SST_AccumulateSample(inst->stats, sample);
SST_DoNewRegression(inst->stats);
}
@@ -512,20 +496,21 @@ mark_ok_sources(SRC_Status status)
static int
combine_sources(int n_sel_sources, struct timespec *ref_time, double *offset,
- double *offset_sd, double *frequency, double *skew)
+ double *offset_sd, double *frequency, double *frequency_sd, double *skew)
{
struct timespec src_ref_time;
- double src_offset, src_offset_sd, src_frequency, src_skew;
+ double src_offset, src_offset_sd, src_frequency, src_frequency_sd, src_skew;
double src_root_delay, src_root_dispersion, sel_src_distance, elapsed;
double offset_weight, sum_offset_weight, sum_offset, sum2_offset_sd;
- double frequency_weight, sum_frequency_weight, sum_frequency, inv_sum2_skew;
+ double frequency_weight, sum_frequency_weight, sum_frequency;
+ double inv_sum2_frequency_sd, inv_sum2_skew;
int i, index, combined;
if (n_sel_sources == 1)
return 1;
sum_offset_weight = sum_offset = sum2_offset_sd = 0.0;
- sum_frequency_weight = sum_frequency = inv_sum2_skew = 0.0;
+ sum_frequency_weight = sum_frequency = inv_sum2_frequency_sd = inv_sum2_skew = 0.0;
sel_src_distance = sources[selected_source_index]->sel_info.root_distance;
if (sources[selected_source_index]->type == SRC_NTP)
@@ -535,7 +520,7 @@ combine_sources(int n_sel_sources, struct timespec *ref_time, double *offset,
index = sel_sources[i];
SST_GetTrackingData(sources[index]->stats, &src_ref_time,
&src_offset, &src_offset_sd,
- &src_frequency, &src_skew,
+ &src_frequency, &src_frequency_sd, &src_skew,
&src_root_delay, &src_root_dispersion);
/* Don't include this source if its distance is longer than the distance of
@@ -563,20 +548,23 @@ combine_sources(int n_sel_sources, struct timespec *ref_time, double *offset,
elapsed = UTI_DiffTimespecsToDouble(ref_time, &src_ref_time);
src_offset += elapsed * src_frequency;
+ src_offset_sd += elapsed * src_frequency_sd;
offset_weight = 1.0 / sources[index]->sel_info.root_distance;
- frequency_weight = 1.0 / src_skew;
+ frequency_weight = 1.0 / SQUARE(src_frequency_sd);
- DEBUG_LOG("combining index=%d oweight=%e offset=%e sd=%e fweight=%e freq=%e skew=%e",
- index, offset_weight, src_offset, src_offset_sd, frequency_weight, src_frequency, src_skew);
+ 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,
+ frequency_weight, src_frequency, src_frequency_sd, src_skew);
sum_offset_weight += offset_weight;
sum_offset += offset_weight * src_offset;
- sum2_offset_sd += offset_weight * (src_offset_sd * src_offset_sd +
- (src_offset - *offset) * (src_offset - *offset));
+ sum2_offset_sd += offset_weight * (SQUARE(src_offset_sd) +
+ SQUARE(src_offset - *offset));
sum_frequency_weight += frequency_weight;
sum_frequency += frequency_weight * src_frequency;
- inv_sum2_skew += 1.0 / (src_skew * src_skew);
+ inv_sum2_frequency_sd += 1.0 / SQUARE(src_frequency_sd);
+ inv_sum2_skew += 1.0 / SQUARE(src_skew);
combined++;
}
@@ -585,10 +573,11 @@ combine_sources(int n_sel_sources, struct timespec *ref_time, double *offset,
*offset = sum_offset / sum_offset_weight;
*offset_sd = sqrt(sum2_offset_sd / sum_offset_weight);
*frequency = sum_frequency / sum_frequency_weight;
+ *frequency_sd = 1.0 / sqrt(inv_sum2_frequency_sd);
*skew = 1.0 / sqrt(inv_sum2_skew);
- DEBUG_LOG("combined result offset=%e sd=%e freq=%e skew=%e",
- *offset, *offset_sd, *frequency, *skew);
+ DEBUG_LOG("combined result offset=%e osd=%e freq=%e fsd=%e skew=%e",
+ *offset, *offset_sd, *frequency, *frequency_sd, *skew);
return combined;
}
@@ -602,12 +591,12 @@ SRC_SelectSource(SRC_Instance updated_inst)
{
struct SelectInfo *si;
struct timespec now, ref_time;
- int i, j, j1, j2, index, sel_prefer, n_endpoints, n_sel_sources;
- int n_badstats_sources, max_sel_reach, max_badstat_reach, sel_req_source;
+ int i, j, j1, j2, index, sel_prefer, n_endpoints, n_sel_sources, sel_req_source;
+ int n_badstats_sources, max_sel_reach, max_sel_reach_size, max_badstat_reach;
int depth, best_depth, trust_depth, best_trust_depth;
int combined, stratum, min_stratum, max_score_index;
int orphan_stratum, orphan_source, leap_votes, leap_ins, leap_del;
- double src_offset, src_offset_sd, src_frequency, src_skew;
+ double src_offset, src_offset_sd, src_frequency, src_frequency_sd, src_skew;
double src_root_delay, src_root_dispersion;
double best_lo, best_hi, distance, sel_src_distance, max_score;
double first_sample_ago, max_reach_sample_ago;
@@ -635,6 +624,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
n_badstats_sources = 0;
sel_req_source = 0;
max_sel_reach = max_badstat_reach = 0;
+ max_sel_reach_size = 0;
max_reach_sample_ago = 0.0;
for (i = 0; i < n_sources; i++) {
@@ -652,7 +642,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
}
si = &sources[i]->sel_info;
- SST_GetSelectionData(sources[i]->stats, &now, &si->stratum,
+ SST_GetSelectionData(sources[i]->stats, &now, &si->stratum, &si->leap,
&si->lo_limit, &si->hi_limit, &si->root_distance,
&si->std_dev, &first_sample_ago,
&si->last_sample_ago, &si->select_ok);
@@ -694,6 +684,9 @@ SRC_SelectSource(SRC_Instance updated_inst)
if (max_sel_reach < sources[i]->reachability)
max_sel_reach = sources[i]->reachability;
+
+ if (max_sel_reach_size < sources[i]->reachability_size)
+ max_sel_reach_size = sources[i]->reachability_size;
}
orphan_stratum = REF_GetOrphanStratum();
@@ -777,18 +770,17 @@ SRC_SelectSource(SRC_Instance updated_inst)
n_endpoints += 2;
}
- DEBUG_LOG("badstat=%d sel=%d badstat_reach=%x sel_reach=%x max_reach_ago=%f",
- n_badstats_sources, n_sel_sources, max_badstat_reach,
- max_sel_reach, max_reach_sample_ago);
+ DEBUG_LOG("badstat=%d sel=%d badstat_reach=%x sel_reach=%x size=%d max_reach_ago=%f",
+ n_badstats_sources, n_sel_sources, (unsigned int)max_badstat_reach,
+ (unsigned int)max_sel_reach, max_sel_reach_size, max_reach_sample_ago);
/* Wait for the next call if we have no source selected and there is
a source with bad stats (has less than 3 samples) with reachability
equal to shifted maximum reachability of sources with valid stats.
This delays selecting source on start with servers using the same
polling interval until they all have valid stats. */
- if (n_badstats_sources && n_sel_sources &&
- selected_source_index == INVALID_SOURCE &&
- max_sel_reach >> 1 == max_badstat_reach) {
+ if (n_badstats_sources && n_sel_sources && selected_source_index == INVALID_SOURCE &&
+ max_sel_reach_size < SOURCE_REACH_BITS && max_sel_reach >> 1 == max_badstat_reach) {
mark_ok_sources(SRC_WAITS_STATS);
return;
}
@@ -929,9 +921,9 @@ SRC_SelectSource(SRC_Instance updated_inst)
if (best_trust_depth && !(sources[index]->sel_options & SRC_SELECT_TRUST))
continue;
leap_votes++;
- if (sources[index]->leap_status == LEAP_InsertSecond)
+ if (sources[index]->sel_info.leap == LEAP_InsertSecond)
leap_ins++;
- else if (sources[index]->leap_status == LEAP_DeleteSecond)
+ else if (sources[index]->sel_info.leap == LEAP_DeleteSecond)
leap_del++;
}
@@ -1015,7 +1007,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
sources[i]->sel_score = 1.0 / distance;
}
- DEBUG_LOG("select score=%f refid=%"PRIx32" match_refid=%"PRIx32" status=%d dist=%f",
+ DEBUG_LOG("select score=%f refid=%"PRIx32" match_refid=%"PRIx32" status=%u dist=%f",
sources[i]->sel_score, sources[i]->ref_id,
updated_inst ? updated_inst->ref_id : 0,
sources[i]->status, distance);
@@ -1078,18 +1070,18 @@ SRC_SelectSource(SRC_Instance updated_inst)
SST_GetTrackingData(sources[selected_source_index]->stats, &ref_time,
&src_offset, &src_offset_sd,
- &src_frequency, &src_skew,
+ &src_frequency, &src_frequency_sd, &src_skew,
&src_root_delay, &src_root_dispersion);
- combined = combine_sources(n_sel_sources, &ref_time, &src_offset,
- &src_offset_sd, &src_frequency, &src_skew);
+ combined = combine_sources(n_sel_sources, &ref_time, &src_offset, &src_offset_sd,
+ &src_frequency, &src_frequency_sd, &src_skew);
REF_SetReference(sources[selected_source_index]->sel_info.stratum,
leap_status, combined,
sources[selected_source_index]->ref_id,
sources[selected_source_index]->ip_addr,
&ref_time, src_offset, src_offset_sd,
- src_frequency, src_skew,
+ src_frequency, src_frequency_sd, src_skew,
src_root_delay, src_root_dispersion);
}
diff --git a/sources.h b/sources.h
index 60d28fd..c160029 100644
--- a/sources.h
+++ b/sources.h
@@ -80,34 +80,8 @@ extern void SRC_SetRefid(SRC_Instance instance, uint32_t ref_id, IPAddr *addr);
extern SST_Stats SRC_GetSourcestats(SRC_Instance instance);
/* This function is called by one of the source drivers when it has
- a new sample that is to be accumulated.
-
- This function causes the frequency estimation to be re-run for the
- designated source, and the clock selection procedure to be re-run
- afterwards.
-
- sample_time is the local time at which the sample is to be
- considered to have been made, in terms of doing a regression fit of
- offset against local time.
-
- offset is the offset at the time, in seconds. Positive indicates
- that the local clock is SLOW relative to the source, negative
- indicates that the local clock is FAST relative to it.
-
- root_delay and root_dispersion are in seconds, and are as per
- RFC 5905. root_dispersion only includes the peer's root dispersion
- + local sampling precision + skew dispersion accrued during the
- measurement. It is the job of the source statistics algorithms +
- track.c to add on the extra dispersion due to the residual standard
- deviation of the offsets from this source after regression, to form
- the root_dispersion field in the packets transmitted to clients or
- peers.
-
- stratum is the stratum of the source that supplied the sample.
-
- */
-
-extern void SRC_AccumulateSample(SRC_Instance instance, struct timespec *sample_time, double offset, double peer_delay, double peer_dispersion, double root_delay, double root_dispersion, int stratum, NTP_Leap leap_status);
+ a new sample that is to be accumulated */
+extern void SRC_AccumulateSample(SRC_Instance instance, NTP_Sample *sample);
/* This routine sets the source as receiving reachability updates */
extern void SRC_SetActive(SRC_Instance inst);
diff --git a/sourcestats.c b/sourcestats.c
index ff4c652..7150f6e 100644
--- a/sourcestats.c
+++ b/sourcestats.c
@@ -132,6 +132,7 @@ struct SST_Stats_Record {
source per unit local time. (Positive => local clock fast,
negative => local clock slow) */
double estimated_frequency;
+ double estimated_frequency_sd;
/* This is the assumed worst case bounds on the estimated frequency.
We assume that the true frequency lies within +/- half this much
@@ -174,10 +175,11 @@ struct SST_Stats_Record {
time of the measurements */
double root_dispersions[MAX_SAMPLES];
- /* This array contains the strata that were associated with the sources
- at the times the samples were generated */
- int strata[MAX_SAMPLES];
+ /* The stratum from the last accumulated sample */
+ int stratum;
+ /* The leap status from the last accumulated sample */
+ NTP_Leap leap;
};
/* ================================================== */
@@ -244,7 +246,8 @@ SST_ResetInstance(SST_Stats inst)
inst->best_single_sample = 0;
inst->min_delay_sample = 0;
inst->estimated_frequency = 0;
- inst->skew = 2000.0e-6;
+ inst->estimated_frequency_sd = WORST_CASE_FREQ_BOUND;
+ inst->skew = WORST_CASE_FREQ_BOUND;
inst->estimated_offset = 0.0;
inst->estimated_offset_sd = 86400.0; /* Assume it's at least within a day! */
UTI_ZeroTimespec(&inst->offset_time);
@@ -252,6 +255,7 @@ SST_ResetInstance(SST_Stats inst)
inst->nruns = 0;
inst->asymmetry_run = 0;
inst->asymmetry = 0.0;
+ inst->leap = LEAP_Unsynchronised;
}
/* ================================================== */
@@ -287,11 +291,7 @@ prune_register(SST_Stats inst, int new_oldest)
/* ================================================== */
void
-SST_AccumulateSample(SST_Stats inst, struct timespec *sample_time,
- double offset,
- double peer_delay, double peer_dispersion,
- double root_delay, double root_dispersion,
- int stratum)
+SST_AccumulateSample(SST_Stats inst, NTP_Sample *sample)
{
int n, m;
@@ -303,7 +303,7 @@ SST_AccumulateSample(SST_Stats inst, struct timespec *sample_time,
/* Make sure it's newer than the last sample */
if (inst->n_samples &&
- UTI_CompareTimespecs(&inst->sample_times[inst->last_sample], sample_time) >= 0) {
+ UTI_CompareTimespecs(&inst->sample_times[inst->last_sample], &sample->time) >= 0) {
LOG(LOGS_WARN, "Out of order sample detected, discarding history for %s",
inst->ip_addr ? UTI_IPToString(inst->ip_addr) : UTI_RefidToString(inst->refid));
SST_ResetInstance(inst);
@@ -313,14 +313,17 @@ SST_AccumulateSample(SST_Stats inst, struct timespec *sample_time,
(MAX_SAMPLES * REGRESS_RUNS_RATIO);
m = n % MAX_SAMPLES;
- inst->sample_times[n] = *sample_time;
- inst->offsets[n] = offset;
- inst->orig_offsets[m] = offset;
- inst->peer_delays[n] = peer_delay;
- inst->peer_dispersions[m] = peer_dispersion;
- inst->root_delays[m] = root_delay;
- inst->root_dispersions[m] = root_dispersion;
- inst->strata[m] = stratum;
+ /* WE HAVE TO NEGATE OFFSET IN THIS CALL, IT IS HERE THAT THE SENSE OF OFFSET
+ IS FLIPPED */
+ inst->sample_times[n] = sample->time;
+ inst->offsets[n] = -sample->offset;
+ inst->orig_offsets[m] = -sample->offset;
+ inst->peer_delays[n] = sample->peer_delay;
+ inst->peer_dispersions[m] = sample->peer_dispersion;
+ inst->root_delays[m] = sample->root_delay;
+ inst->root_dispersions[m] = sample->root_dispersion;
+ inst->stratum = sample->stratum;
+ inst->leap = sample->leap;
if (inst->peer_delays[n] < inst->fixed_min_delay)
inst->peer_delays[n] = 2.0 * inst->fixed_min_delay - inst->peer_delays[n];
@@ -550,7 +553,7 @@ SST_DoNewRegression(SST_Stats inst)
sd_weight = 1.0;
if (peer_distances[i] > min_distance)
sd_weight += (peer_distances[i] - min_distance) / sd;
- weights[i] = sd_weight * sd_weight;
+ weights[i] = SQUARE(sd_weight);
}
}
@@ -570,6 +573,7 @@ SST_DoNewRegression(SST_Stats inst)
old_freq = inst->estimated_frequency;
inst->estimated_frequency = est_slope;
+ inst->estimated_frequency_sd = CLAMP(MIN_SKEW, est_slope_sd, MAX_SKEW);
inst->skew = est_slope_sd * RGR_GetTCoef(degrees_of_freedom);
inst->estimated_offset = est_intercept;
inst->offset_time = inst->sample_times[inst->last_sample];
@@ -600,6 +604,7 @@ SST_DoNewRegression(SST_Stats inst)
prune_register(inst, best_start);
} else {
inst->estimated_frequency = 0.0;
+ inst->estimated_frequency_sd = WORST_CASE_FREQ_BOUND;
inst->skew = WORST_CASE_FREQ_BOUND;
times_back_start = 0;
}
@@ -636,7 +641,7 @@ SST_GetFrequencyRange(SST_Stats inst,
void
SST_GetSelectionData(SST_Stats inst, struct timespec *now,
- int *stratum,
+ int *stratum, NTP_Leap *leap,
double *offset_lo_limit,
double *offset_hi_limit,
double *root_distance,
@@ -656,7 +661,8 @@ SST_GetSelectionData(SST_Stats inst, struct timespec *now,
i = get_runsbuf_index(inst, inst->best_single_sample);
j = get_buf_index(inst, inst->best_single_sample);
- *stratum = inst->strata[get_buf_index(inst, inst->n_samples - 1)];
+ *stratum = inst->stratum;
+ *leap = inst->leap;
*std_dev = inst->std_dev;
sample_elapsed = fabs(UTI_DiffTimespecsToDouble(now, &inst->sample_times[i]));
@@ -698,7 +704,7 @@ SST_GetSelectionData(SST_Stats inst, struct timespec *now,
void
SST_GetTrackingData(SST_Stats inst, struct timespec *ref_time,
double *average_offset, double *offset_sd,
- double *frequency, double *skew,
+ double *frequency, double *frequency_sd, double *skew,
double *root_delay, double *root_dispersion)
{
int i, j;
@@ -713,16 +719,16 @@ SST_GetTrackingData(SST_Stats inst, struct timespec *ref_time,
*average_offset = inst->estimated_offset;
*offset_sd = inst->estimated_offset_sd;
*frequency = inst->estimated_frequency;
+ *frequency_sd = inst->estimated_frequency_sd;
*skew = inst->skew;
*root_delay = inst->root_delays[j];
elapsed_sample = UTI_DiffTimespecsToDouble(&inst->offset_time, &inst->sample_times[i]);
- *root_dispersion = inst->root_dispersions[j] + inst->skew * elapsed_sample;
-
- DEBUG_LOG("n=%d freq=%f (%.3fppm) skew=%f (%.3fppm) avoff=%f offsd=%f disp=%f",
- inst->n_samples, *frequency, 1.0e6* *frequency, *skew, 1.0e6* *skew,
- *average_offset, *offset_sd, *root_dispersion);
+ *root_dispersion = inst->root_dispersions[j] + inst->skew * elapsed_sample + *offset_sd;
+ DEBUG_LOG("n=%d off=%f offsd=%f freq=%e freqsd=%e skew=%e delay=%f disp=%f",
+ inst->n_samples, *average_offset, *offset_sd,
+ *frequency, *frequency_sd, *skew, *root_delay, *root_dispersion);
}
/* ================================================== */
@@ -864,7 +870,7 @@ SST_SaveToFile(SST_Stats inst, FILE *out)
inst->root_delays[j],
inst->root_dispersions[j],
1.0, /* used to be inst->weights[i] */
- inst->strata[j]);
+ inst->stratum /* used to be an array */);
}
@@ -909,7 +915,7 @@ SST_LoadFromFile(SST_Stats inst, FILE *in)
&(inst->root_delays[i]),
&(inst->root_dispersions[i]),
&weight, /* not used anymore */
- &(inst->strata[i])) != 10)) {
+ &inst->stratum) != 10)) {
/* This is the branch taken if the read FAILED */
@@ -957,7 +963,7 @@ SST_DoSourceReport(SST_Stats inst, RPT_SourceReport *report, struct timespec *no
report->orig_latest_meas = inst->orig_offsets[j];
report->latest_meas = inst->offsets[i];
report->latest_meas_err = 0.5*inst->root_delays[j] + inst->root_dispersions[j];
- report->stratum = inst->strata[j];
+ report->stratum = inst->stratum;
/* Align the sample time to reduce the leak of the receive timestamp */
last_sample_time = inst->sample_times[i];
diff --git a/sourcestats.h b/sourcestats.h
index f0a5ca9..a321d3a 100644
--- a/sourcestats.h
+++ b/sourcestats.h
@@ -51,19 +51,8 @@ extern void SST_ResetInstance(SST_Stats inst);
/* This function changes the reference ID and IP address */
extern void SST_SetRefid(SST_Stats inst, uint32_t refid, IPAddr *addr);
-/* This function accumulates a single sample into the statistics handler
-
- sample_time is the epoch at which the sample is to be considered to
- have been made.
-
- offset is the offset of the local clock relative to the source in
- seconds. Positive indicates that the local clock if FAST (contrary
- to the NTP parts of the software)
-
- stratum is the stratum of the source from which the sample came.
- */
-
-extern void SST_AccumulateSample(SST_Stats inst, struct timespec *sample_time, double offset, double peer_delay, double peer_dispersion, double root_delay, double root_dispersion, int stratum);
+/* This function accumulates a single sample into the statistics handler */
+extern void SST_AccumulateSample(SST_Stats inst, NTP_Sample *sample);
/* This function runs the linear regression operation on the data. It
finds the set of most recent samples that give the tightest
@@ -80,7 +69,7 @@ extern void SST_GetFrequencyRange(SST_Stats inst, double *lo, double *hi);
/* Get data needed for selection */
extern void
SST_GetSelectionData(SST_Stats inst, struct timespec *now,
- int *stratum,
+ int *stratum, NTP_Leap *leap,
double *offset_lo_limit,
double *offset_hi_limit,
double *root_distance,
@@ -93,7 +82,7 @@ SST_GetSelectionData(SST_Stats inst, struct timespec *now,
extern void
SST_GetTrackingData(SST_Stats inst, struct timespec *ref_time,
double *average_offset, double *offset_sd,
- double *frequency, double *skew,
+ double *frequency, double *frequency_sd, double *skew,
double *root_delay, double *root_dispersion);
/* This routine is called when the local machine clock parameters are
diff --git a/srcparams.h b/srcparams.h
index 7a73bad..deabd23 100644
--- a/srcparams.h
+++ b/srcparams.h
@@ -29,10 +29,16 @@
#include "sources.h"
+typedef enum {
+ SRC_OFFLINE,
+ SRC_ONLINE,
+ SRC_MAYBE_ONLINE,
+} SRC_Connectivity;
+
typedef struct {
int minpoll;
int maxpoll;
- int online;
+ SRC_Connectivity connectivity;
int auto_offline;
int presend_minpoll;
int burst;
@@ -43,6 +49,7 @@ typedef struct {
int max_sources;
int min_samples;
int max_samples;
+ int filter_length;
int interleaved;
int sel_options;
uint32_t authkey;
diff --git a/stubs.c b/stubs.c
index eac81c0..00b07ac 100644
--- a/stubs.c
+++ b/stubs.c
@@ -254,13 +254,7 @@ NSR_GetLocalRefid(IPAddr *address)
}
int
-NSR_TakeSourcesOnline(IPAddr *mask, IPAddr *address)
-{
- return 0;
-}
-
-int
-NSR_TakeSourcesOffline(IPAddr *mask, IPAddr *address)
+NSR_SetConnectivity(IPAddr *mask, IPAddr *address, SRC_Connectivity connectivity)
{
return 0;
}
diff --git a/sys_linux.c b/sys_linux.c
index 202e7c2..b829022 100644
--- a/sys_linux.c
+++ b/sys_linux.c
@@ -42,11 +42,6 @@
#include <sys/resource.h>
#endif
-#ifdef FEAT_PRIVDROP
-#include <sys/prctl.h>
-#include <sys/capability.h>
-#endif
-
#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
#include <linux/ptp_clock.h>
#endif
@@ -66,6 +61,11 @@
#endif
#endif
+#ifdef FEAT_PRIVDROP
+#include <sys/prctl.h>
+#include <sys/capability.h>
+#endif
+
#include "sys_linux.h"
#include "sys_timex.h"
#include "conf.h"
@@ -309,9 +309,9 @@ get_version_specific_details(void)
nominal_tick = (1000000L + (hz/2))/hz; /* Mirror declaration in kernel */
max_tick_bias = nominal_tick / 10;
- /* We can't reliably detect the internal kernel HZ, it may not even be fixed
- (CONFIG_NO_HZ aka tickless), assume the lowest commonly used fixed rate */
- tick_update_hz = 100;
+ /* In modern kernels the frequency of the clock is updated immediately in the
+ adjtimex() system call. Assume a maximum delay of 10 microseconds. */
+ tick_update_hz = 100000;
get_kernel_version(&major, &minor, &patch);
DEBUG_LOG("Linux kernel major=%d minor=%d patch=%d", major, minor, patch);
@@ -322,9 +322,15 @@ get_version_specific_details(void)
if (kernelvercmp(major, minor, patch, 2, 6, 27) >= 0 &&
kernelvercmp(major, minor, patch, 2, 6, 33) < 0) {
- /* Tickless kernels before 2.6.33 accumulated ticks only in
- half-second intervals */
+ /* In tickless kernels before 2.6.33 the frequency is updated in
+ a half-second interval */
tick_update_hz = 2;
+ } else if (kernelvercmp(major, minor, patch, 4, 19, 0) < 0) {
+ /* In kernels before 4.19 the frequency is updated only on internal ticks
+ (CONFIG_HZ). As their rate cannot be reliably detected from the user
+ space, and it may not even be constant (CONFIG_NO_HZ - aka tickless),
+ assume the lowest commonly used constant rate */
+ tick_update_hz = 100;
}
/* ADJ_SETOFFSET support */
@@ -334,8 +340,8 @@ get_version_specific_details(void)
have_setoffset = 1;
}
- DEBUG_LOG("hz=%d nominal_tick=%d max_tick_bias=%d",
- hz, nominal_tick, max_tick_bias);
+ DEBUG_LOG("hz=%d nominal_tick=%d max_tick_bias=%d tick_update_hz=%d",
+ hz, nominal_tick, max_tick_bias, tick_update_hz);
}
/* ================================================== */
@@ -385,7 +391,7 @@ test_step_offset(void)
static void
report_time_adjust_blockers(void)
{
-#ifdef FEAT_PRIVDROP
+#if defined(FEAT_PRIVDROP) && defined(CAP_IS_SUPPORTED)
if (CAP_IS_SUPPORTED(CAP_SYS_TIME) && cap_get_bound(CAP_SYS_TIME))
return;
LOG(LOGS_WARN, "CAP_SYS_TIME not present");
@@ -497,7 +503,7 @@ SYS_Linux_EnableSystemCallFilter(int level)
SCMP_SYS(lseek), SCMP_SYS(rename), SCMP_SYS(stat), SCMP_SYS(stat64),
SCMP_SYS(statfs), SCMP_SYS(statfs64), SCMP_SYS(unlink),
/* Socket */
- SCMP_SYS(bind), SCMP_SYS(connect), SCMP_SYS(getsockname),
+ SCMP_SYS(bind), SCMP_SYS(connect), SCMP_SYS(getsockname), SCMP_SYS(getsockopt),
SCMP_SYS(recvfrom), SCMP_SYS(recvmmsg), SCMP_SYS(recvmsg),
SCMP_SYS(sendmmsg), SCMP_SYS(sendmsg), SCMP_SYS(sendto),
/* TODO: check socketcall arguments */
@@ -529,7 +535,7 @@ SYS_Linux_EnableSystemCallFilter(int level)
#endif
};
- const static int fcntls[] = { F_GETFD, F_SETFD };
+ const static int fcntls[] = { F_GETFD, F_SETFD, F_SETFL };
const static unsigned long ioctls[] = {
FIONREAD, TCGETS,
diff --git a/sysincl.h b/sysincl.h
index a9e4da0..54431de 100644
--- a/sysincl.h
+++ b/sysincl.h
@@ -36,6 +36,7 @@
#include <float.h>
#include <glob.h>
#include <grp.h>
+#include <inttypes.h>
#include <math.h>
#include <netdb.h>
#include <netinet/in.h>
@@ -63,14 +64,6 @@
#include <sys/timex.h>
#endif
-#ifdef HAVE_INTTYPES_H
-#include <inttypes.h>
-#elif HAVE_STDINT_H
-#include <stdint.h>
-#else
-/* Tough */
-#endif
-
#ifdef FEAT_IPV6
/* For inet_ntop() */
#include <arpa/inet.h>
diff --git a/test/compilation/001-features b/test/compilation/001-features
index d61c07f..125ed31 100755
--- a/test/compilation/001-features
+++ b/test/compilation/001-features
@@ -4,6 +4,8 @@
cd ../..
+export CFLAGS="-O2 -Werror -Wpointer-arith -Wformat-signedness -Wno-unknown-warning-option -D_FORTIFY_SOURCE=2"
+
for opts in \
"--enable-debug" \
"--enable-ntp-signd" \
@@ -23,6 +25,6 @@ for opts in \
"--disable-cmdmon --disable-refclock" \
"--disable-cmdmon --disable-ntp --disable-refclock"
do
- ./configure $opts
+ ./configure $opts || exit 1
make "$@" || exit 1
done
diff --git a/test/compilation/003-sanitizers b/test/compilation/003-sanitizers
new file mode 100755
index 0000000..54f0a87
--- /dev/null
+++ b/test/compilation/003-sanitizers
@@ -0,0 +1,87 @@
+#!/bin/bash
+# Run the unit and simulation tests with different compiler sanitizers
+# and under valgrind
+
+cd ../..
+
+if [ "$(uname -sm)" != "Linux x86_64" ]; then
+ echo Test supported on Linux x86_64 only
+ exit 1
+fi
+
+[ -f /etc/os-release ] && . /etc/os-release
+
+if [ "$ID" = "fedora" ]; then
+ echo Checking test dependencies:
+ rpm -q {valgrind,gcc,clang}.x86_64 {libgcc,clang-libs}.{x86_64,i686} || exit 1
+ rpm -q {libseccomp,nettle,nss-softokn-freebl,libtomcrypt}-devel.{x86_64,i686} || exit 1
+ echo
+fi
+
+touch Makefile
+
+for CC in gcc clang; do
+ export CC
+
+ for arch_opts in "-m32" ""; do
+ pushd test/simulation/clknetsim || exit 1
+ make clean > /dev/null 2>&1
+ CFLAGS="$arch_opts -DCLKNETSIM_DISABLE_SYSCALL" make "$@" || exit 1
+ echo
+
+ popd
+
+ for extra_config_opts in \
+ "--all-privops" \
+ "--disable-scfilter" \
+ "--without-nettle" \
+ "--without-nettle --without-nss" \
+ "--without-nettle --without-nss --without-tomcrypt"; \
+ do
+ for san_options in "" "-fsanitize=address" "-fsanitize=memory"; do
+ export CFLAGS="-O2 -g -fsanitize=undefined -fsanitize=float-divide-by-zero -fno-sanitize-recover=undefined,float-divide-by-zero $san_options $arch_opts"
+
+ # clang msan doesn't work on i686 and otherwise requires patches
+ echo $CFLAGS | grep -q 'sanitize=memory' && continue
+
+ # build fails with clang ubsan on i686 (Fedora only?)
+ [ "$arch_opts" = "-m32" -a "$CC" = "clang" ] && continue
+
+ [ "$CC" = "gcc" ] && echo $CFLAGS | grep -q 'sanitize=address' && CFLAGS="$CFLAGS -static-libasan"
+
+ config_opts="--with-user=chrony --enable-debug --enable-scfilter --enable-ntp-signd $extra_config_opts"
+
+ echo -----------------------------------------------------------------------------
+ echo CC=\"$CC\" CFLAGS=\"$CFLAGS\" ./configure $config_opts
+
+ make distclean > /dev/null 2>&1
+
+ ./configure $config_opts || exit 1
+
+ if echo "$config_opts" | grep -q all-privops; then
+ for op in ADJUSTTIME ADJUSTTIMEX SETTIME BINDSOCKET; do
+ echo "#define PRIVOPS_$op 1" >> config.h
+ done
+ fi
+
+ make "$@" || exit 1
+
+ [ -n "$BUILD_TEST_ONLY" ] && continue
+
+ echo
+ pushd test/unit || exit 1
+ if [ "$san_options" = "" ]; then
+ make check TEST_WRAPPER="valgrind --error-exitcode=1" || exit 1
+ else
+ make check || exit 1
+ fi
+ popd
+
+ echo
+ pushd test/simulation || exit 1
+ CLKNETSIM_RANDOM_SEED=101 ./run -i 1 || exit 1
+ popd
+ done
+ done
+ done
+done
diff --git a/test/simulation/101-poll b/test/simulation/101-poll
index f350777..cb3647f 100755
--- a/test/simulation/101-poll
+++ b/test/simulation/101-poll
@@ -12,11 +12,13 @@ time_rms_limit=5e-6
freq_rms_limit=5e-6
client_conf="makestep 1e-2 1"
-for poll in $(seq 2 14); do
+for poll in $(seq 1 14); do
client_server_options="minpoll $poll maxpoll $poll"
limit=$[2**$poll * 10]
min_sync_time=$[2**$poll * 2]
max_sync_time=$[2**$poll * 21 / 10 + 1]
+ client_max_min_out_interval=$(awk "BEGIN {print 2^$poll * 1.1}")
+ client_min_mean_out_interval=$(awk "BEGIN {print 2^$poll * 0.99}")
run_test || test_fail
check_chronyd_exit || test_fail
diff --git a/test/simulation/106-refclock b/test/simulation/106-refclock
index c22cd42..5c5794c 100755
--- a/test/simulation/106-refclock
+++ b/test/simulation/106-refclock
@@ -9,19 +9,22 @@ refclock_jitter=$jitter
min_sync_time=45
max_sync_time=70
chronyc_start=70
-client_conf="refclock SHM 0 stratum 3 delay 1e-3 refid GPS"
chronyc_conf="tracking"
-run_test || test_fail
-check_chronyd_exit || test_fail
-check_source_selection || test_fail
-check_sync || test_fail
-check_chronyc_output "^Reference ID.*47505300 \(GPS\)
+for refclock in "SHM 0" "PHC /dev/ptp0"; do
+ client_conf="refclock $refclock stratum 3 delay 1e-3 refid GPS"
+
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_source_selection || test_fail
+ check_sync || test_fail
+ check_chronyc_output "^Reference ID.*47505300 \(GPS\)
Stratum.*: 4
.*
Root delay : 0.001000000 seconds
.*
Update interval : 16\.. seconds
.*$" || test_fail
+done
test_pass
diff --git a/test/simulation/108-peer b/test/simulation/108-peer
index 0679f6e..20e2254 100755
--- a/test/simulation/108-peer
+++ b/test/simulation/108-peer
@@ -30,4 +30,25 @@ check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
+base_delay="(+ 1e-4 (* -1 (equal 0.1 from 3) (equal 0.1 to 1)))"
+client_peer_options=""
+
+while read lminpoll lmaxpoll rminpoll rmaxpoll max_sync_time; do
+ client_lpeer_options="minpoll $lminpoll maxpoll $lmaxpoll"
+ client_rpeer_options="minpoll $rminpoll maxpoll $rmaxpoll"
+ limit=$[$max_sync_time * 10]
+
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_sync || test_fail
+done <<-EOF
+ 3 6 3 6 400
+ 3 3 6 6 450
+ 6 6 3 3 450
+ 3 6 6 6 450
+ 6 6 3 6 450
+ -2 -2 2 2 220
+ 2 2 -2 -2 220
+EOF
+
test_pass
diff --git a/test/simulation/110-chronyc b/test/simulation/110-chronyc
index d56e724..944cf3f 100755
--- a/test/simulation/110-chronyc
+++ b/test/simulation/110-chronyc
@@ -63,4 +63,78 @@ try: 1, refid: C0A87B01, correction: 0\.000......, skew: .\....
513 RTC driver not running$" \
|| test_fail
+server_strata=0
+chronyc_start=0
+client_conf=""
+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 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" \
+ "allow 1.2.3.4" \
+ "allow 1.2" \
+ "allow 3.4.5" \
+ "allow 6.7.8/22" \
+ "allow 6.7.8.9/22" \
+ "allow 2001:db8::/32" \
+ "allow 0/0" \
+ "allow ::/0" \
+ "allow" \
+ "allow all 10/24" \
+ "burst 5/10" \
+ "burst 3/5 255.255.255.0/1.2.3.0" \
+ "burst 1/2 1.2.3.0/24" \
+ "clients" \
+ "cmdaccheck 1.2.3.4" \
+ "cmdallow 1.2.3.4" \
+ "cmdallow all 1.2.3.0/24" \
+ "cmddeny 1.2.3.4" \
+ "cmddeny all 1.2.3.0/24" \
+ "cyclelogs" \
+ "delete 10.0.0.0" \
+ "deny 1.2.3.4" \
+ "deny all 1.2.3.0/24" \
+ "dump" \
+ "local stratum 5 distance 1.0 orphan" \
+ "local off" \
+ "makestep 10.0 3" \
+ "makestep" \
+ "manual delete 0" \
+ "manual off" \
+ "manual on" \
+ "manual reset" \
+ "maxdelay 1.2.3.4 1e-2" \
+ "maxdelaydevratio 1.2.3.4 5.0" \
+ "maxdelayratio 1.2.3.4 3.0" \
+ "maxpoll 1.2.3.4 5" \
+ "maxupdateskew 1.2.3.4 10.0" \
+ "minpoll 1.2.3.4 3" \
+ "minstratum 1.2.3.4 1" \
+ "ntpdata 1.2.3.4" \
+ "offline" \
+ "offline 255.255.255.0/1.2.3.0" \
+ "offline 1.2.3.0/24" \
+ "online" \
+ "online 1.2.3.0/24" \
+ "onoffline" \
+ "polltarget 1.2.3.4 10" \
+ "refresh" \
+ "rekey" \
+ "reselect" \
+ "reselectdist 1e-3" \
+ "settime 16:30" \
+ "settime 16:30:05" \
+ "settime Nov 21, 2015 16:30:05" \
+ "shutdown" \
+ "smoothtime reset" \
+ "smoothtime activate" \
+ "trimrtc" \
+ "writertc"
+do
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_chronyc_output "501 Not authorised" || test_fail
+done
+
test_pass
diff --git a/test/simulation/121-orphan b/test/simulation/121-orphan
index ed92153..1b47f76 100755
--- a/test/simulation/121-orphan
+++ b/test/simulation/121-orphan
@@ -10,6 +10,7 @@ server 192.168.123.1
server 192.168.123.2
server 192.168.123.3"
max_sync_time=500
+client_start=140
chronyc_start=300
chronyc_conf="tracking"
time_rms_limit=5e-4
diff --git a/test/simulation/126-burst b/test/simulation/126-burst
index 3173c78..d63f290 100755
--- a/test/simulation/126-burst
+++ b/test/simulation/126-burst
@@ -26,4 +26,20 @@ check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
+# Add a significant delay to 70% of packets on the 2->1 path after 6th packet
+base_delay=$(cat <<-EOF | tr -d '\n'
+ (+ 1e-4
+ (* 0.15
+ (equal 0.1 from 2)
+ (equal 0.1 to 1)
+ (equal 0.1 (min (sum 1) 7) 7)
+ (equal 0.7 (uniform) 0.0)))
+EOF
+)
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+
test_pass
diff --git a/test/simulation/127-filter b/test/simulation/127-filter
new file mode 100755
index 0000000..737d1f9
--- /dev/null
+++ b/test/simulation/127-filter
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+. ./test.common
+
+test_start "filter option"
+
+client_server_options="minpoll 4 maxpoll 4 filter 15"
+min_sync_time=710
+max_sync_time=720
+client_max_min_out_interval=16.1
+client_min_mean_out_interval=15.9
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+test_pass
diff --git a/test/simulation/128-nocontrol b/test/simulation/128-nocontrol
new file mode 100755
index 0000000..0a98cd7
--- /dev/null
+++ b/test/simulation/128-nocontrol
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+. ./test.common
+
+test_start "-x option"
+
+wander=0.0
+time_offset=0.0
+freq_offset=0.0
+time_max_limit=1e-6
+freq_max_limit=1e-9
+min_sync_time=0
+max_sync_time=0
+client_chronyd_options="-x"
+chronyc_start=300
+chronyc_conf="tracking"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+check_chronyc_output "^.*Stratum *: 2.*$" || test_fail
+
+test_pass
diff --git a/test/simulation/129-reload b/test/simulation/129-reload
new file mode 100755
index 0000000..9c4e985
--- /dev/null
+++ b/test/simulation/129-reload
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+. ./test.common
+
+test_start "-r option"
+
+wander=0.0
+limit=100
+min_sync_time=100
+max_sync_time=104
+client_chronyd_options="-r"
+client_conf="dumpdir tmp"
+
+run_test || test_fail
+
+client_start=$limit
+limit=1000
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+test_pass
diff --git a/test/simulation/130-quit b/test/simulation/130-quit
new file mode 100755
index 0000000..0183885
--- /dev/null
+++ b/test/simulation/130-quit
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+. ./test.common
+
+test_start "-q/-Q option"
+
+wander=0.0
+freq_offset=0.0
+min_sync_time=5
+max_sync_time=10
+client_chronyd_options="-q"
+client_server_options="iburst"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+client_chronyd_options="-Q"
+run_test || test_fail
+check_sync && test_fail
+
+test_pass
diff --git a/test/simulation/131-maxchange b/test/simulation/131-maxchange
new file mode 100755
index 0000000..969f71f
--- /dev/null
+++ b/test/simulation/131-maxchange
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+. ./test.common
+
+test_start "maxchange directive"
+
+time_offset=2
+max_sync_time=5000
+client_conf="maxchange 0.1 1 3"
+client_step="(* $step (equal 0.1 (sum 1.0) 300))"
+
+run_test || test_fail
+check_chronyd_exit && test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync && test_fail
+check_log_messages "seconds exceeds.*ignored" 3 3 || test_fail
+check_log_messages "seconds exceeds.*exiting" 1 1 || test_fail
+
+test_pass
diff --git a/test/simulation/132-logchange b/test/simulation/132-logchange
new file mode 100755
index 0000000..ca6244e
--- /dev/null
+++ b/test/simulation/132-logchange
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+. ./test.common
+
+test_start "logchange directive"
+
+time_offset=2
+min_sync_time=600
+max_sync_time=700
+client_server_options="maxsamples 6"
+client_conf="logchange 0.1"
+client_step="(* $step (equal 0.1 (sum 1.0) 300))"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+check_log_messages "clock wrong by" 4 8 || test_fail
+
+test_pass
diff --git a/test/simulation/133-hwtimestamp b/test/simulation/133-hwtimestamp
new file mode 100755
index 0000000..1448c21
--- /dev/null
+++ b/test/simulation/133-hwtimestamp
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+. ./test.common
+
+test_start "hwtimestamp directive"
+
+export CLKNETSIM_TIMESTAMPING=2
+
+refclock_jitter=1e-8
+refclock_offset=10.0
+min_sync_time=4
+max_sync_time=20
+limit=200
+client_conf="hwtimestamp eth0"
+client_server_options="minpoll 0 maxpoll 0 minsamples 32"
+client_chronyd_options="-d"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+
+if grep -q 'FEAT_DEBUG 1' ../../config.h; then
+ check_log_messages "HW clock samples" 190 200 || test_fail
+ check_log_messages "HW clock reset" 0 0 || test_fail
+ check_log_messages "Received.*tss=1" 1 1 || test_fail
+ check_log_messages "Received.*tss=2" 390 400 || test_fail
+ check_log_messages "update_tx_timestamp.*Updated" 50 140 || test_fail
+ check_log_messages "update_tx_timestamp.*Unacceptable" 50 140 || test_fail
+fi
+
+test_pass
diff --git a/test/simulation/test.common b/test/simulation/test.common
index 0cd0fce..c5758c6 100644
--- a/test/simulation/test.common
+++ b/test/simulation/test.common
@@ -62,7 +62,8 @@ default_client_rpeer_options=""
default_server_conf=""
default_client_conf=""
default_chronyc_conf=""
-default_chronyd_options=""
+default_server_chronyd_options=""
+default_client_chronyd_options=""
default_time_max_limit=1e-3
default_freq_max_limit=5e-4
@@ -75,10 +76,9 @@ default_client_min_mean_out_interval=0.0
default_client_max_min_out_interval=inf
# Initialize test settings from their defaults
-for defopt in $(declare | grep '^default_'); do
- defoptname=${defopt%%=*}
+for defoptname in ${!default_*}; do
optname=${defoptname#default_}
- eval "[ -z \"\${$optname:+a}\" ] && $optname=\"\$$defoptname\""
+ [ -z "${!optname}" ] && declare "$optname"="${!defoptname}"
done
test_start() {
@@ -253,7 +253,7 @@ check_chronyd_exit() {
for i in $(seq 1 $(get_chronyd_nodes)); do
test_message 3 0 "node $i:"
- tail -n 1 tmp/log.$i | grep -q 'chronyd exiting' && \
+ grep -q 'chronyd exiting' tmp/log.$i && \
! grep -q 'Adjustment.*exceeds.*exiting' tmp/log.$i && \
test_ok || test_bad
[ $? -eq 0 ] || ret=1
@@ -337,6 +337,24 @@ check_chronyc_output() {
return $ret
}
+# Check the number of messages matching a matter in the client logs
+check_log_messages() {
+ local i count ret=0 pattern=$1 min=$2 max=$3
+
+ test_message 2 1 "checking number of messages \"$pattern\":"
+
+ for i in $(seq $[$servers * $server_strata + 1] $(get_chronyd_nodes)); do
+ count=$(grep "$pattern" tmp/log.$i | wc -l)
+ test_message 3 0 "node $i: $count"
+
+ [ "$min" -le "$count" ] && [ "$count" -le "$max" ] && \
+ test_ok || test_bad
+ [ $? -eq 0 ] || ret=1
+ done
+
+ return $ret
+}
+
# Check if only NTP port (123) was used
check_packet_port() {
local i ret=0 port=123
@@ -358,14 +376,13 @@ check_packet_port() {
# Print test settings which differ from default value
print_nondefaults() {
- local defopt defoptname optname
+ local defoptname optname
test_message 2 1 "non-default settings:"
- declare | grep '^default_*' | while read defopt; do
- defoptname=${defopt%%=*}
+ for defoptname in ${!default_*}; do
optname=${defoptname#default_}
- eval "[ \"\$$optname\" = \"\$$defoptname\" ]" || \
- test_message 3 1 $(eval "echo $optname=\$$optname")
+ [ "${!defoptname}" = "${!optname}" ] || \
+ test_message 3 1 $optname=${!optname}
done
}
@@ -382,7 +399,7 @@ run_simulation() {
}
run_test() {
- local i j n stratum node nodes step start freq offset conf
+ local i j n stratum node nodes step start freq offset conf options
test_message 1 1 "network with $servers*$server_strata servers and $clients clients:"
print_nondefaults
@@ -411,16 +428,19 @@ run_test() {
start=$server_start
freq=""
[ $i -le $falsetickers ] && offset=$i.0 || offset=0.0
+ options=$server_chronyd_options
elif [ $stratum -le $server_strata ]; then
step=$server_step
start=$server_start
freq=$(get_wander_expr)
offset=0.0
+ options=$server_chronyd_options
else
step=$client_step
start=$client_start
freq=$(get_wander_expr)
offset=$time_offset
+ options=$client_chronyd_options
fi
conf=$(get_chronyd_conf $stratum $i $n)
@@ -431,7 +451,7 @@ run_test() {
echo "node${node}_refclock = $(get_refclock_expr)" >> tmp/conf
echo "node${node}_offset = $offset" >> tmp/conf
echo "node${node}_start = $start" >> tmp/conf
- start_client $node chronyd "$conf" "" "$chronyd_options" && \
+ start_client $node chronyd "$conf" "" "$options" && \
test_ok || test_error
[ $? -ne 0 ] && return 1
diff --git a/test/unit/clientlog.c b/test/unit/clientlog.c
index 515ad1a..a412b69 100644
--- a/test/unit/clientlog.c
+++ b/test/unit/clientlog.c
@@ -65,7 +65,7 @@ test_unit(void)
}
}
- DEBUG_LOG("records %d", ARR_GetSize(records));
+ DEBUG_LOG("records %u", ARR_GetSize(records));
TEST_CHECK(ARR_GetSize(records) == 64);
for (i = j = 0; i < 10000; i++) {
@@ -76,7 +76,7 @@ test_unit(void)
j++;
}
- DEBUG_LOG("requests %u responses %u", i, j);
+ DEBUG_LOG("requests %d responses %d", i, j);
TEST_CHECK(j * 4 < i && j * 6 > i);
CLG_Finalise();
diff --git a/test/unit/hash.c b/test/unit/hash.c
index 8aab74f..5cde039 100644
--- a/test/unit/hash.c
+++ b/test/unit/hash.c
@@ -106,7 +106,7 @@ test_unit(void)
if (j >= tests[i].length)
TEST_CHECK(length == tests[i].length);
else
- TEST_CHECK(length == 0 || length == j);
+ TEST_CHECK(length == j);
TEST_CHECK(!memcmp(out, tests[i].out, length));
}
diff --git a/test/unit/hwclock.c b/test/unit/hwclock.c
index 804e600..216dbfb 100644
--- a/test/unit/hwclock.c
+++ b/test/unit/hwclock.c
@@ -1,6 +1,6 @@
/*
**********************************************************************
- * Copyright (C) Miroslav Lichvar 2016
+ * Copyright (C) Miroslav Lichvar 2016-2017
*
* 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
@@ -27,56 +27,58 @@ test_unit(void)
struct timespec start_hw_ts, start_local_ts, hw_ts, local_ts, ts;
HCL_Instance clock;
double freq, jitter, interval, dj, sum;
- int i, j, count;
+ int i, j, k, count;
LCL_Initialise();
- clock = HCL_CreateInstance(1.0);
-
- for (i = 0, count = 0, sum = 0.0; i < 2000; i++) {
- UTI_ZeroTimespec(&start_hw_ts);
- UTI_ZeroTimespec(&start_local_ts);
- UTI_AddDoubleToTimespec(&start_hw_ts, TST_GetRandomDouble(0.0, 1e9), &start_hw_ts);
- UTI_AddDoubleToTimespec(&start_local_ts, TST_GetRandomDouble(0.0, 1e9), &start_local_ts);
-
- DEBUG_LOG("iteration %d", i);
-
- freq = TST_GetRandomDouble(0.9, 1.1);
- jitter = TST_GetRandomDouble(10.0e-9, 1000.0e-9);
- interval = TST_GetRandomDouble(0.1, 10.0);
-
- clock->n_samples = 0;
- clock->valid_coefs = 0;
-
- for (j = 0; j < 100; j++) {
- UTI_AddDoubleToTimespec(&start_hw_ts, j * interval * freq, &hw_ts);
- UTI_AddDoubleToTimespec(&start_local_ts, j * interval, &local_ts);
- if (HCL_CookTime(clock, &hw_ts, &ts, NULL)) {
- dj = fabs(UTI_DiffTimespecsToDouble(&ts, &local_ts) / jitter);
- DEBUG_LOG("delta/jitter %f", dj);
- if (clock->n_samples >= 8)
- sum += dj, count++;
- TEST_CHECK(clock->n_samples < 4 || dj <= 4.0);
- TEST_CHECK(clock->n_samples < 8 || dj <= 3.0);
- }
+ for (i = 1; i <= 8; i++) {
+ clock = HCL_CreateInstance(random() % (1 << i), 1 << i, 1.0);
+
+ for (j = 0, count = 0, sum = 0.0; j < 100; j++) {
+ UTI_ZeroTimespec(&start_hw_ts);
+ UTI_ZeroTimespec(&start_local_ts);
+ UTI_AddDoubleToTimespec(&start_hw_ts, TST_GetRandomDouble(0.0, 1e9), &start_hw_ts);
+ UTI_AddDoubleToTimespec(&start_local_ts, TST_GetRandomDouble(0.0, 1e9), &start_local_ts);
+
+ DEBUG_LOG("iteration %d", j);
+
+ freq = TST_GetRandomDouble(0.9, 1.1);
+ jitter = TST_GetRandomDouble(10.0e-9, 1000.0e-9);
+ interval = TST_GetRandomDouble(0.1, 10.0);
+
+ clock->n_samples = 0;
+ clock->valid_coefs = 0;
- UTI_AddDoubleToTimespec(&start_hw_ts, j * interval * freq + TST_GetRandomDouble(-jitter, jitter), &hw_ts);
+ 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)) {
+ dj = fabs(UTI_DiffTimespecsToDouble(&ts, &local_ts) / jitter);
+ DEBUG_LOG("delta/jitter %f", dj);
+ if (clock->n_samples >= clock->max_samples / 2)
+ sum += dj, count++;
+ TEST_CHECK(clock->n_samples < 4 || dj <= 4.0);
+ TEST_CHECK(clock->n_samples < 8 || dj <= 3.0);
+ }
- if (HCL_NeedsNewSample(clock, &local_ts))
- HCL_AccumulateSample(clock, &hw_ts, &local_ts, 2.0 * jitter);
+ UTI_AddDoubleToTimespec(&start_hw_ts, k * interval * freq + TST_GetRandomDouble(-jitter, jitter), &hw_ts);
- TEST_CHECK(clock->valid_coefs || clock->n_samples < 2);
+ if (HCL_NeedsNewSample(clock, &local_ts))
+ HCL_AccumulateSample(clock, &hw_ts, &local_ts, 2.0 * jitter);
- if (!clock->valid_coefs)
- continue;
+ TEST_CHECK(clock->valid_coefs || clock->n_samples < 2);
- TEST_CHECK(fabs(clock->offset) <= 2.0 * jitter);
+ if (!clock->valid_coefs)
+ continue;
+
+ TEST_CHECK(fabs(clock->offset) <= 2.0 * jitter);
+ }
}
- }
- TEST_CHECK(sum / count < 0.4);
+ TEST_CHECK(sum / count < 2.4 / sqrt(clock->max_samples));
- HCL_DestroyInstance(clock);
+ HCL_DestroyInstance(clock);
+ }
LCL_Finalise();
}
diff --git a/test/unit/ntp_core.c b/test/unit/ntp_core.c
index 9587306..5e519e5 100644
--- a/test/unit/ntp_core.c
+++ b/test/unit/ntp_core.c
@@ -1,6 +1,6 @@
/*
**********************************************************************
- * Copyright (C) Miroslav Lichvar 2017
+ * Copyright (C) Miroslav Lichvar 2017-2018
*
* 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,6 +63,18 @@ advance_time(double x)
UTI_AddDoubleToTimespec(&current_time, x, &current_time);
}
+static uint32_t
+get_random_key_id(void)
+{
+ uint32_t id;
+
+ do {
+ id = random() % 6 + 2;
+ } while (!KEY_KeyKnown(id));
+
+ return id;
+}
+
static void
send_request(NCR_Instance inst)
{
@@ -122,6 +134,7 @@ static void
send_response(int interleaved, int authenticated, int allow_update, int valid_ts, int valid_auth)
{
NTP_Packet *req, *res;
+ int auth_len = 0;
req = &req_buffer.ntp_pkt;
res = &res_buffer.ntp_pkt;
@@ -168,24 +181,41 @@ send_response(int interleaved, int authenticated, int allow_update, int valid_ts
}
if (authenticated) {
- res->auth_keyid = req->auth_keyid;
- KEY_GenerateAuth(ntohl(res->auth_keyid), (unsigned char *)res, NTP_NORMAL_PACKET_LENGTH,
- res->auth_data, 16);
- res_length = NTP_NORMAL_PACKET_LENGTH + 4 + 16;
+ res->auth_keyid = req->auth_keyid ? req->auth_keyid : htonl(get_random_key_id());
+ auth_len = KEY_GetAuthLength(ntohl(res->auth_keyid));
+ assert(auth_len);
+ if (NTP_LVM_TO_VERSION(res->lvm) == 4 && random() % 2)
+ auth_len = MIN(auth_len, NTP_MAX_V4_MAC_LENGTH - 4);
+
+ if (KEY_GenerateAuth(ntohl(res->auth_keyid), (unsigned char *)res,
+ NTP_NORMAL_PACKET_LENGTH, res->auth_data, auth_len) != auth_len)
+ assert(0);
+ res_length = NTP_NORMAL_PACKET_LENGTH + 4 + auth_len;
} else {
res_length = NTP_NORMAL_PACKET_LENGTH;
}
- if (!valid_auth) {
- switch (random() % 3) {
+ if (!valid_auth && authenticated) {
+ assert(auth_len);
+
+ switch (random() % 4) {
case 0:
- res->auth_keyid++;
+ res->auth_keyid = htonl(ntohl(res->auth_keyid) + 1);
break;
case 1:
- res->auth_data[random() % 16]++;
+ res->auth_keyid = htonl(ntohl(res->auth_keyid) ^ 1);
+ if (KEY_GenerateAuth(ntohl(res->auth_keyid), (unsigned char *)res,
+ NTP_NORMAL_PACKET_LENGTH, res->auth_data, auth_len) != auth_len)
+ assert(0);
break;
case 2:
- res_length = NTP_NORMAL_PACKET_LENGTH;
+ res->auth_data[random() % auth_len]++;
+ break;
+ case 3:
+ res_length = NTP_NORMAL_PACKET_LENGTH + 4 * (random() % ((4 + auth_len) / 4));
+ if (NTP_LVM_TO_VERSION(res->lvm) == 4 &&
+ res_length == NTP_NORMAL_PACKET_LENGTH + NTP_MAX_V4_MAC_LENGTH)
+ res_length -= 4;
break;
default:
assert(0);
@@ -292,7 +322,10 @@ test_unit(void)
NIO_Initialise(IPADDR_UNSPEC);
NCR_Initialise();
REF_Initialise();
+
+ TST_SuspendLogging();
KEY_Initialise();
+ TST_ResumeLogging();
CNF_SetupAccessRestrictions();
@@ -302,7 +335,7 @@ test_unit(void)
if (random() % 2)
source.params.interleaved = 1;
if (random() % 2)
- source.params.authkey = 1;
+ source.params.authkey = get_random_key_id();
source.params.version = random() % 4 + 1;
UTI_ZeroTimespec(&current_time);
diff --git a/test/unit/ntp_core.keys b/test/unit/ntp_core.keys
index 7a70e52..f06237f 100644
--- a/test/unit/ntp_core.keys
+++ b/test/unit/ntp_core.keys
@@ -1,2 +1,6 @@
-1 MD5 HEX:38979C567358C0896F4D9D459A3C8B8478654579
2 MD5 HEX:38979C567358C0896F4D9D459A3C8B8478654579
+3 MD5 HEX:38979C567358C0896F4D9D459A3C8B8478654579
+4 SHA1 HEX:B71744EA01FBF01CA30D173ECDDF901952AE356A
+5 SHA1 HEX:B71744EA01FBF01CA30D173ECDDF901952AE356A
+6 SHA512 HEX:DE027482F22B201FC20863F58C74095E7906089F
+7 SHA512 HEX:DE027482F22B201FC20863F58C74095E7906089F
diff --git a/test/unit/samplefilt.c b/test/unit/samplefilt.c
new file mode 100644
index 0000000..294a9bf
--- /dev/null
+++ b/test/unit/samplefilt.c
@@ -0,0 +1,113 @@
+/*
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar 2018
+ *
+ * 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 <samplefilt.c>
+#include "test.h"
+
+void
+test_unit(void)
+{
+ NTP_Sample sample_in, sample_out;
+ SPF_Instance filter;
+ int i, j, k, sum_count, min_samples, max_samples;
+ double mean, combine_ratio, sum_err;
+
+ LCL_Initialise();
+
+ for (i = 0; i <= 100; i++) {
+ max_samples = random() % 20 + 1;
+ min_samples = random() % (max_samples) + 1;
+ combine_ratio = TST_GetRandomDouble(0.0, 1.0);
+
+ filter = SPF_CreateInstance(min_samples, max_samples, 2.0, combine_ratio);
+
+ for (j = 0, sum_count = 0, sum_err = 0.0; j < 100; j++) {
+ DEBUG_LOG("iteration %d/%d", i, j);
+
+ mean = TST_GetRandomDouble(-1.0e3, 1.0e3);
+ UTI_ZeroTimespec(&sample_in.time);
+
+ for (k = 0; k < 100; k++) {
+ UTI_AddDoubleToTimespec(&sample_in.time, TST_GetRandomDouble(1.0e-1, 1.0e2),
+ &sample_in.time);
+ sample_in.offset = mean + TST_GetRandomDouble(-1.0, 1.0);
+ sample_in.peer_dispersion = TST_GetRandomDouble(1.0e-4, 2.0e-4);
+ sample_in.root_dispersion = TST_GetRandomDouble(1.0e-3, 2.0e-3);
+ sample_in.peer_delay = TST_GetRandomDouble(1.0e-2, 2.0e-2);
+ sample_in.root_delay = TST_GetRandomDouble(1.0e-1, 2.0e-1);
+ sample_in.stratum = random() % 16;
+ sample_in.leap = random() % 4;
+
+ TEST_CHECK(SPF_AccumulateSample(filter, &sample_in));
+ TEST_CHECK(!SPF_AccumulateSample(filter, &sample_in));
+
+ TEST_CHECK(SPF_GetNumberOfSamples(filter) == MIN(k + 1, max_samples));
+
+ SPF_GetLastSample(filter, &sample_out);
+ TEST_CHECK(!memcmp(&sample_in, &sample_out, sizeof (sample_in)));
+
+ SPF_SlewSamples(filter, &sample_in.time, 0.0, 0.0);
+ SPF_AddDispersion(filter, 0.0);
+
+ if (k + 1 < min_samples)
+ TEST_CHECK(!SPF_GetFilteredSample(filter, &sample_out));
+
+ TEST_CHECK(SPF_GetNumberOfSamples(filter) == MIN(k + 1, max_samples));
+ }
+
+ if (random() % 10) {
+ TEST_CHECK(SPF_GetFilteredSample(filter, &sample_out));
+
+ TEST_CHECK(SPF_GetAvgSampleDispersion(filter) <= 2.0);
+
+ sum_err += sample_out.offset - mean;
+ sum_count++;
+
+ TEST_CHECK(UTI_CompareTimespecs(&sample_out.time, &sample_in.time) <= 0 &&
+ sample_out.time.tv_sec >= 0);
+ TEST_CHECK(fabs(sample_out.offset - mean) <= 1.0);
+ TEST_CHECK(sample_out.peer_dispersion >= 1.0e-4 &&
+ (sample_out.peer_dispersion <= 2.0e-4 || filter->max_samples > 1));
+ TEST_CHECK(sample_out.root_dispersion >= 1.0e-3 &&
+ (sample_out.root_dispersion <= 2.0e-3 || filter->max_samples > 1));
+ TEST_CHECK(sample_out.peer_delay >= 1.0e-2 &&
+ sample_out.peer_delay <= 2.0e-2);
+ TEST_CHECK(sample_out.root_delay >= 1.0e-1 &&
+ sample_out.root_delay <= 2.0e-1);
+ TEST_CHECK(sample_out.leap >= 0 && sample_out.leap <= 3);
+ TEST_CHECK(sample_out.stratum >= 0 && sample_out.stratum <= 15);
+
+ if (max_samples == 1)
+ TEST_CHECK(!memcmp(&sample_in, &sample_out, sizeof (sample_in)));
+
+ } else {
+ SPF_DropSamples(filter);
+ }
+
+ TEST_CHECK(SPF_GetNumberOfSamples(filter) == 0);
+ }
+
+ TEST_CHECK(fabs(sum_err / sum_count) < 0.3);
+
+ SPF_DestroyInstance(filter);
+ }
+
+ LCL_Finalise();
+}
diff --git a/test/unit/sources.c b/test/unit/sources.c
index 341e22e..b81f808 100644
--- a/test/unit/sources.c
+++ b/test/unit/sources.c
@@ -26,10 +26,9 @@ test_unit(void)
{
SRC_Instance srcs[16];
RPT_SourceReport report;
+ NTP_Sample sample;
IPAddr addr;
int i, j, k, l, samples, sel_options;
- double offset, delay, disp;
- struct timespec ts;
CNF_Initialise(0, 0);
LCL_Initialise();
@@ -59,21 +58,25 @@ test_unit(void)
samples = (i + j) % 5 + 3;
- offset = TST_GetRandomDouble(-1.0, 1.0);
+ sample.offset = TST_GetRandomDouble(-1.0, 1.0);
for (k = 0; k < samples; k++) {
- SCH_GetLastEventTime(&ts, NULL, NULL);
- UTI_AddDoubleToTimespec(&ts, TST_GetRandomDouble(k - samples, k - samples + 1), &ts);
-
- offset += TST_GetRandomDouble(-1.0e-2, 1.0e-2);
- delay = TST_GetRandomDouble(1.0e-6, 1.0e-1);
- disp = TST_GetRandomDouble(1.0e-6, 1.0e-1);
+ SCH_GetLastEventTime(&sample.time, NULL, NULL);
+ UTI_AddDoubleToTimespec(&sample.time, TST_GetRandomDouble(k - samples, k - samples + 1),
+ &sample.time);
+
+ sample.offset += TST_GetRandomDouble(-1.0e-2, 1.0e-2);
+ sample.peer_delay = TST_GetRandomDouble(1.0e-6, 1.0e-1);
+ sample.peer_dispersion = TST_GetRandomDouble(1.0e-6, 1.0e-1);
+ sample.root_delay = sample.peer_delay;
+ sample.root_dispersion = sample.peer_dispersion;
+ sample.stratum = 1;
+ sample.leap = LEAP_Normal;
DEBUG_LOG("source %d sample %d offset %f delay %f disp %f", j, k,
- offset, delay, disp);
+ sample.offset, sample.peer_delay, sample.peer_dispersion);
- SRC_AccumulateSample(srcs[j], &ts, offset, delay, disp, delay, disp,
- 1, LEAP_Normal);
+ SRC_AccumulateSample(srcs[j], &sample);
}
for (k = 0; k <= j; k++) {
@@ -82,7 +85,7 @@ test_unit(void)
double passed_lo = DBL_MAX, passed_hi = DBL_MIN;
SRC_SelectSource(srcs[k]);
- DEBUG_LOG("source %d status %d", k, sources[k]->status);
+ DEBUG_LOG("source %d status %u", k, sources[k]->status);
for (l = 0; l <= j; l++) {
TEST_CHECK(sources[l]->status > SRC_OK && sources[l]->status <= SRC_SELECTED);
@@ -125,7 +128,7 @@ test_unit(void)
}
for (j = 0; j < sizeof (srcs) / sizeof (srcs[0]); j++) {
- SRC_ReportSource(j, &report, &ts);
+ SRC_ReportSource(j, &report, &sample.time);
SRC_DestroyInstance(srcs[j]);
}
}
diff --git a/test/unit/test.c b/test/unit/test.c
index 67f7678..3a9ec74 100644
--- a/test/unit/test.c
+++ b/test/unit/test.c
@@ -75,6 +75,16 @@ main(int argc, char **argv)
return 0;
}
+void TST_SuspendLogging(void)
+{
+ LOG_OpenFileLog("/dev/null");
+}
+
+void TST_ResumeLogging(void)
+{
+ LOG_OpenFileLog(NULL);
+}
+
double
TST_GetRandomDouble(double min, double max)
{
diff --git a/test/unit/test.h b/test/unit/test.h
index d96f3af..f409252 100644
--- a/test/unit/test.h
+++ b/test/unit/test.h
@@ -35,6 +35,9 @@ extern void test_unit(void);
extern void TST_Fail(int line);
+extern void TST_SuspendLogging(void);
+extern void TST_ResumeLogging(void);
+
extern double TST_GetRandomDouble(double min, double max);
extern void TST_GetRandomAddress(IPAddr *ip, int family, int bits);
extern void TST_SwapAddressBit(IPAddr *ip, unsigned int b);
diff --git a/test/unit/util.c b/test/unit/util.c
index 5f1a653..6ce6f90 100644
--- a/test/unit/util.c
+++ b/test/unit/util.c
@@ -3,13 +3,17 @@
void test_unit(void) {
NTP_int64 ntp_ts, ntp_fuzz;
+ NTP_int32 ntp32_ts;
struct timespec ts, ts2;
struct timeval tv;
struct sockaddr_un sun;
- double x, y;
+ double x, y, nan, inf;
+ Timespec tspec;
Float f;
int i, j, c;
char buf[16], *s;
+ uid_t uid;
+ gid_t gid;
for (i = -31; i < 31; i++) {
x = pow(2.0, i);
@@ -33,6 +37,11 @@ void test_unit(void) {
TEST_CHECK(x > 0.0 || x <= 0.0);
}
+ for (i = 0; i < 100000; i++) {
+ UTI_GetRandomBytes(&ntp32_ts, sizeof (ntp32_ts));
+ TEST_CHECK(UTI_DoubleToNtp32(UTI_Ntp32ToDouble(ntp32_ts)) == ntp32_ts);
+ }
+
TEST_CHECK(UTI_DoubleToNtp32(1.0) == htonl(65536));
TEST_CHECK(UTI_DoubleToNtp32(0.0) == htonl(0));
TEST_CHECK(UTI_DoubleToNtp32(1.0 / (65536.0)) == htonl(1));
@@ -175,6 +184,23 @@ void test_unit(void) {
TEST_CHECK(c > 400 && c < 600);
}
+ ts.tv_nsec = 0;
+
+ ts.tv_sec = 10;
+ TEST_CHECK(!UTI_IsTimeOffsetSane(&ts, -20.0));
+
+#ifdef HAVE_LONG_TIME_T
+ ts.tv_sec = NTP_ERA_SPLIT + (1LL << 32);
+#else
+ ts.tv_sec = 0x7fffffff - MIN_ENDOFTIME_DISTANCE;
+#endif
+ TEST_CHECK(!UTI_IsTimeOffsetSane(&ts, 10.0));
+ TEST_CHECK(UTI_IsTimeOffsetSane(&ts, -20.0));
+
+ UTI_TimespecHostToNetwork(&ts, &tspec);
+ UTI_TimespecNetworkToHost(&tspec, &ts2);
+ TEST_CHECK(!UTI_CompareTimespecs(&ts, &ts2));
+
for (i = c = 0; i < 100000; i++) {
j = random() % (sizeof (buf) + 1);
UTI_GetRandomBytes(buf, j);
@@ -196,4 +222,46 @@ void test_unit(void) {
TEST_CHECK(s[BUFFER_LENGTH - 2] == '>');
}
}
+
+ s = UTI_PathToDir("/aaa/bbb/ccc/ddd");
+ TEST_CHECK(!strcmp(s, "/aaa/bbb/ccc"));
+ Free(s);
+ s = UTI_PathToDir("aaa");
+ TEST_CHECK(!strcmp(s, "."));
+ Free(s);
+ s = UTI_PathToDir("/aaaa");
+ TEST_CHECK(!strcmp(s, "/"));
+ Free(s);
+
+ nan = strtod("nan", NULL);
+ inf = strtod("inf", NULL);
+
+ TEST_CHECK(MIN(2.0, -1.0) == -1.0);
+ TEST_CHECK(MIN(-1.0, 2.0) == -1.0);
+ TEST_CHECK(MIN(inf, 2.0) == 2.0);
+
+ TEST_CHECK(MAX(2.0, -1.0) == 2.0);
+ TEST_CHECK(MAX(-1.0, 2.0) == 2.0);
+ TEST_CHECK(MAX(inf, 2.0) == inf);
+
+ TEST_CHECK(CLAMP(1.0, -1.0, 2.0) == 1.0);
+ TEST_CHECK(CLAMP(1.0, 3.0, 2.0) == 2.0);
+ TEST_CHECK(CLAMP(1.0, inf, 2.0) == 2.0);
+ TEST_CHECK(CLAMP(1.0, nan, 2.0) == 2.0);
+
+ TEST_CHECK(SQUARE(3.0) == 3.0 * 3.0);
+
+ rmdir("testdir");
+
+ uid = geteuid();
+ gid = getegid();
+
+ TEST_CHECK(UTI_CreateDirAndParents("testdir", 0700, uid, gid));
+
+ TST_SuspendLogging();
+ TEST_CHECK(UTI_CheckDirPermissions("testdir", 0700, uid, gid));
+ TEST_CHECK(!UTI_CheckDirPermissions("testdir", 0300, uid, gid));
+ TEST_CHECK(!UTI_CheckDirPermissions("testdir", 0700, uid + 1, gid));
+ TEST_CHECK(!UTI_CheckDirPermissions("testdir", 0700, uid, gid + 1));
+ TST_ResumeLogging();
}
diff --git a/util.c b/util.c
index 4b3e455..bfbae33 100644
--- a/util.c
+++ b/util.c
@@ -298,16 +298,17 @@ UTI_IPToString(IPAddr *addr)
b = (ip>>16) & 0xff;
c = (ip>> 8) & 0xff;
d = (ip>> 0) & 0xff;
- snprintf(result, BUFFER_LENGTH, "%ld.%ld.%ld.%ld", a, b, c, d);
+ snprintf(result, BUFFER_LENGTH, "%lu.%lu.%lu.%lu", a, b, c, d);
break;
case IPADDR_INET6:
ip6 = addr->addr.in6;
#ifdef FEAT_IPV6
inet_ntop(AF_INET6, ip6, result, BUFFER_LENGTH);
#else
- snprintf(result, BUFFER_LENGTH, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
- ip6[0], ip6[1], ip6[2], ip6[3], ip6[4], ip6[5], ip6[6], ip6[7],
- ip6[8], ip6[9], ip6[10], ip6[11], ip6[12], ip6[13], ip6[14], ip6[15]);
+ assert(BUFFER_LENGTH >= 40);
+ for (a = 0; a < 8; a++)
+ snprintf(result + a * 5, 40 - a * 5, "%04x:",
+ (unsigned int)(ip6[2 * a] << 8 | ip6[2 * a + 1]));
#endif
break;
default:
@@ -994,34 +995,38 @@ UTI_FdSetCloexec(int fd)
/* ================================================== */
-int
-UTI_SetQuitSignalsHandler(void (*handler)(int))
+void
+UTI_SetQuitSignalsHandler(void (*handler)(int), int ignore_sigpipe)
{
struct sigaction sa;
sa.sa_handler = handler;
sa.sa_flags = SA_RESTART;
if (sigemptyset(&sa.sa_mask) < 0)
- return 0;
+ LOG_FATAL("sigemptyset() failed");
#ifdef SIGINT
if (sigaction(SIGINT, &sa, NULL) < 0)
- return 0;
+ LOG_FATAL("sigaction(%d) failed", SIGINT);
#endif
#ifdef SIGTERM
if (sigaction(SIGTERM, &sa, NULL) < 0)
- return 0;
+ LOG_FATAL("sigaction(%d) failed", SIGTERM);
#endif
#ifdef SIGQUIT
if (sigaction(SIGQUIT, &sa, NULL) < 0)
- return 0;
+ LOG_FATAL("sigaction(%d) failed", SIGQUIT);
#endif
#ifdef SIGHUP
if (sigaction(SIGHUP, &sa, NULL) < 0)
- return 0;
+ LOG_FATAL("sigaction(%d) failed", SIGHUP);
#endif
- return 1;
+ if (ignore_sigpipe)
+ sa.sa_handler = SIG_IGN;
+
+ if (sigaction(SIGPIPE, &sa, NULL) < 0)
+ LOG_FATAL("sigaction(%d) failed", SIGPIPE);
}
/* ================================================== */
@@ -1160,12 +1165,12 @@ UTI_CheckDirPermissions(const char *path, mode_t perm, uid_t uid, gid_t gid)
}
if (buf.st_uid != uid) {
- LOG(LOGS_ERR, "Wrong owner of %s (%s != %d)", path, "UID", uid);
+ LOG(LOGS_ERR, "Wrong owner of %s (%s != %u)", path, "UID", uid);
return 0;
}
if (buf.st_gid != gid) {
- LOG(LOGS_ERR, "Wrong owner of %s (%s != %d)", path, "GID", gid);
+ LOG(LOGS_ERR, "Wrong owner of %s (%s != %u)", path, "GID", gid);
return 0;
}
@@ -1183,13 +1188,13 @@ UTI_DropRoot(uid_t uid, gid_t gid)
/* Set effective, saved and real group ID */
if (setgid(gid))
- LOG_FATAL("setgid(%d) failed : %s", gid, strerror(errno));
+ LOG_FATAL("setgid(%u) failed : %s", gid, strerror(errno));
/* Set effective, saved and real user ID */
if (setuid(uid))
- LOG_FATAL("setuid(%d) failed : %s", uid, strerror(errno));
+ LOG_FATAL("setuid(%u) failed : %s", uid, strerror(errno));
- DEBUG_LOG("Dropped root privileges: UID %d GID %d", uid, gid);
+ DEBUG_LOG("Dropped root privileges: UID %u GID %u", uid, gid);
}
/* ================================================== */
@@ -1224,7 +1229,7 @@ get_random_bytes_getrandom(char *buf, unsigned int len)
if (disabled)
break;
- if (getrandom(rand_buf, sizeof (rand_buf), 0) != sizeof (rand_buf)) {
+ if (getrandom(rand_buf, sizeof (rand_buf), GRND_NONBLOCK) != sizeof (rand_buf)) {
disabled = 1;
break;
}
diff --git a/util.h b/util.h
index 40ff729..e3d6767 100644
--- a/util.h
+++ b/util.h
@@ -161,7 +161,7 @@ extern Float UTI_FloatHostToNetwork(double x);
/* Set FD_CLOEXEC on descriptor */
extern int UTI_FdSetCloexec(int fd);
-extern int UTI_SetQuitSignalsHandler(void (*handler)(int));
+extern void UTI_SetQuitSignalsHandler(void (*handler)(int), int ignore_sigpipe);
/* Get directory (as an allocated string) for a path */
extern char *UTI_PathToDir(const char *path);
@@ -200,4 +200,6 @@ extern void UTI_GetRandomBytes(void *buf, unsigned int len);
/* Macro to clamp a value between two values */
#define CLAMP(min, x, max) (MAX((min), MIN((x), (max))))
+#define SQUARE(x) ((x) * (x))
+
#endif /* GOT_UTIL_H */
diff --git a/version.txt b/version.txt
index e7dee8c..37969b7 100644
--- a/version.txt
+++ b/version.txt
@@ -1 +1 @@
-3.3-pre1
+3.4-pre1