summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--FAQ2
-rw-r--r--INSTALL2
-rw-r--r--NEWS3
-rw-r--r--client.c9
-rw-r--r--conf.c13
-rw-r--r--conf.h2
-rwxr-xr-xconfigure7
-rw-r--r--doc/chrony.conf.adoc17
-rw-r--r--doc/chrony.conf.man.in25
-rw-r--r--doc/chronyc.adoc15
-rw-r--r--doc/chronyc.man.in21
-rw-r--r--doc/chronyd.man.in6
-rw-r--r--examples/chrony-wait.service1
-rw-r--r--examples/chronyd-restricted.service1
-rw-r--r--examples/chronyd.service1
-rw-r--r--main.c7
-rw-r--r--memory.c7
-rw-r--r--ntp_core.c12
-rw-r--r--ntp_sources.c102
-rw-r--r--nts_ke_server.c4
-rw-r--r--nts_ntp_client.c1
-rw-r--r--sched.c2
-rw-r--r--sources.c58
-rw-r--r--sys_linux.c6
-rwxr-xr-xtest/compilation/003-sanitizers1
-rwxr-xr-xtest/simulation/139-nts4
-rwxr-xr-xtest/simulation/147-refresh59
-rwxr-xr-xtest/simulation/148-replacement56
-rwxr-xr-xtest/system/010-nts4
-rwxr-xr-xtest/system/099-scfilter2
-rwxr-xr-xtest/system/199-scfilter2
-rw-r--r--test/system/test.common2
-rw-r--r--test/unit/nts_ke_server.c10
-rw-r--r--version.txt2
34 files changed, 386 insertions, 80 deletions
diff --git a/FAQ b/FAQ
index 538a42c..4eb4e86 100644
--- a/FAQ
+++ b/FAQ
@@ -1048,4 +1048,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 2023-05-10 14:28:42 +0200
+Last updated 2023-06-21 11:28:54 +0200
diff --git a/INSTALL b/INSTALL
index 0d5136d..1bec632 100644
--- a/INSTALL
+++ b/INSTALL
@@ -162,4 +162,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 2023-05-10 14:28:42 +0200
+Last updated 2023-06-21 11:28:54 +0200
diff --git a/NEWS b/NEWS
index 0b5a86d..1aa10cd 100644
--- a/NEWS
+++ b/NEWS
@@ -11,9 +11,12 @@ Enhancements
* Add hwtstimeout directive to configure timeout for late timestamps
* Handle late hardware transmit timestamps of NTP requests on all sockets
* Handle mismatched 32/64-bit time_t in SOCK refclock samples
+* Improve source replacement
* Log important changes made by command requests (chronyc)
+* Refresh address of NTP sources periodically
* Set DSCP for IPv6 packets
* Shorten NTS-KE retry interval when network is down
+* Update seccomp filter for musl
* Warn if loading keys from file with unexpected permissions
* Warn if source selection fails or falseticker is detected
* Add selectopts command to modify source-specific selection options
diff --git a/client.c b/client.c
index 80bc7c0..97c842f 100644
--- a/client.c
+++ b/client.c
@@ -1167,7 +1167,7 @@ command_name_generator(const char *text, int state)
while ((name = names[tab_complete_index][list_index++])) {
if (strncmp(name, text, len) == 0) {
- return strdup(name);
+ return Strdup(name);
}
}
@@ -1979,7 +1979,7 @@ process_cmd_sources(char *line)
IPAddr ip_addr;
uint32_t i, mode, n_sources;
char name[256], mode_ch, state_ch;
- int all, verbose;
+ int all, verbose, ref;
parse_sources_options(line, &all, &verbose);
@@ -2016,9 +2016,8 @@ process_cmd_sources(char *line)
if (!all && ip_addr.family == IPADDR_ID)
continue;
- format_name(name, sizeof (name), 25,
- mode == RPY_SD_MD_REF && ip_addr.family == IPADDR_INET4,
- ip_addr.addr.in4, 1, &ip_addr);
+ ref = mode == RPY_SD_MD_REF && ip_addr.family == IPADDR_INET4;
+ format_name(name, sizeof (name), 25, ref, ref ? ip_addr.addr.in4 : 0, 1, &ip_addr);
switch (mode) {
case RPY_SD_MD_CLIENT:
diff --git a/conf.c b/conf.c
index bce06fa..f984060 100644
--- a/conf.c
+++ b/conf.c
@@ -252,6 +252,9 @@ static char *leapsec_tz = NULL;
/* Name of the user to which will be dropped root privileges. */
static char *user;
+/* Address refresh interval */
+static int refresh = 1209600; /* 2 weeks */
+
/* NTS server and client configuration */
static char *nts_dump_dir = NULL;
static char *nts_ntp_server = NULL;
@@ -702,6 +705,8 @@ CNF_ParseLine(const char *filename, int number, char *line)
&ntp_ratelimit_burst, &ntp_ratelimit_leak);
} else if (!strcasecmp(command, "refclock")) {
parse_refclock(p);
+ } else if (!strcasecmp(command, "refresh")) {
+ parse_int(p, &refresh);
} else if (!strcasecmp(command, "reselectdist")) {
parse_double(p, &reselect_distance);
} else if (!strcasecmp(command, "rtcautotrim")) {
@@ -2533,6 +2538,14 @@ CNF_GetPtpPort(void)
/* ================================================== */
+int
+CNF_GetRefresh(void)
+{
+ return refresh;
+}
+
+/* ================================================== */
+
char *
CNF_GetNtsDumpDir(void)
{
diff --git a/conf.h b/conf.h
index ca18abc..58ebdeb 100644
--- a/conf.h
+++ b/conf.h
@@ -159,6 +159,8 @@ extern double CNF_GetHwTsTimeout(void);
extern int CNF_GetPtpPort(void);
+extern int CNF_GetRefresh(void);
+
extern char *CNF_GetNtsDumpDir(void);
extern char *CNF_GetNtsNtpServer(void);
extern int CNF_GetNtsServerCertAndKeyFiles(const char ***certs, const char ***keys);
diff --git a/configure b/configure
index e8d7556..15c1935 100755
--- a/configure
+++ b/configure
@@ -128,6 +128,7 @@ For better control, use the options below.
--without-seccomp Don't use seccomp even if it is available
--disable-asyncdns Disable asynchronous name resolving
--disable-forcednsretry Don't retry on permanent DNS error
+ --without-aes-gcm-siv Don't use AES-GCM-SIV for NTS even if it is available
--without-clock-gettime Don't use clock_gettime() even if it is available
--disable-timestamping Disable support for SW/HW timestamping
--enable-ntp-signd Enable support for MS-SNTP authentication in Samba
@@ -244,6 +245,7 @@ try_setsched=0
try_lockmem=0
feat_asyncdns=1
feat_forcednsretry=1
+try_aes_gcm_siv=1
try_clock_gettime=1
try_arc4random=1
try_recvmmsg=1
@@ -345,6 +347,9 @@ do
--disable-forcednsretry)
feat_forcednsretry=0
;;
+ --without-aes-gcm-siv)
+ try_aes_gcm_siv=0
+ ;;
--without-clock-gettime)
try_clock_gettime=0
;;
@@ -986,7 +991,7 @@ if [ $feat_ntp = "1" ] && [ $feat_nts = "1" ] && [ $try_gnutls = "1" ]; then
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o"
add_def HAVE_SIV
add_def HAVE_NETTLE_SIV_CMAC
- if test_code 'AES-GCM-SIV in nettle' \
+ if [ $try_aes_gcm_siv = "1" ] && test_code 'AES-GCM-SIV in nettle' \
'nettle/siv-gcm.h' "" "$LIBS" \
'siv_gcm_aes128_encrypt_message((void *)1, 0, NULL, 0, (void *)2, 16, (void *)3,
(void *)4);'
diff --git a/doc/chrony.conf.adoc b/doc/chrony.conf.adoc
index 10a2dda..e4d6118 100644
--- a/doc/chrony.conf.adoc
+++ b/doc/chrony.conf.adoc
@@ -72,9 +72,7 @@ newly resolved address when the server becomes unreachable (i.e. no valid
response to last 8 requests), unsynchronised, a falseticker (i.e. does not
agree with a majority of other sources), or the root distance is too large (the
limit can be configured by the <<maxdistance,*maxdistance*>> directive). The
-automatic replacement happens at most once per 30 minutes. It can also be
-triggered manually for all sources by the <<chronyc.adoc#refresh,*refresh*>>
-command in *chronyc*.
+automatic replacement happens at most once per 30 minutes.
+
This directive can be used multiple times to specify multiple servers.
+
@@ -886,6 +884,19 @@ This would disable the time checks until the clock is updated for the first
time, assuming the first update corrects the clock and later checks can work
with correct time.
+[[refresh]]*refresh* _interval_::
+This directive specifies the interval (in seconds) between refreshing IP
+addresses of NTP sources specified by hostname. If the hostname no longer
+resolves to the currently used address, it will be replaced with one of the new
+addresses to avoid using a server which is no longer intended for service, even
+if it is still responding correctly and would not be replaced as unreachable.
+Only one source is refreshed at a time. The default value is 1209600 (2 weeks)
+and the maximum value is 2^31-1 (68 years). A value of 0 disables the periodic
+refreshment.
++
+The <<chronyc.adoc#refresh,*refresh*>> command can be used to refresh all
+sources immediately.
+
=== Source selection
[[authselectmode]]*authselectmode* _mode_::
diff --git a/doc/chrony.conf.man.in b/doc/chrony.conf.man.in
index 385b797..8e91ed3 100644
--- a/doc/chrony.conf.man.in
+++ b/doc/chrony.conf.man.in
@@ -1,13 +1,13 @@
'\" t
.\" Title: chrony.conf
.\" Author: [see the "AUTHOR(S)" section]
-.\" Generator: Asciidoctor 2.0.18
-.\" Date: 2023-05-10
+.\" Generator: Asciidoctor 2.0.20
+.\" Date: 2023-06-21
.\" Manual: Configuration Files
.\" Source: chrony @CHRONY_VERSION@
.\" Language: English
.\"
-.TH "CHRONY.CONF" "5" "2023-05-10" "chrony @CHRONY_VERSION@" "Configuration Files"
+.TH "CHRONY.CONF" "5" "2023-06-21" "chrony @CHRONY_VERSION@" "Configuration Files"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
@@ -74,9 +74,7 @@ newly resolved address when the server becomes unreachable (i.e. no valid
response to last 8 requests), unsynchronised, a falseticker (i.e. does not
agree with a majority of other sources), or the root distance is too large (the
limit can be configured by the \fBmaxdistance\fP directive). The
-automatic replacement happens at most once per 30 minutes. It can also be
-triggered manually for all sources by the \fBrefresh\fP
-command in \fBchronyc\fP.
+automatic replacement happens at most once per 30 minutes.
.sp
This directive can be used multiple times to specify multiple servers.
.sp
@@ -1181,6 +1179,21 @@ This would disable the time checks until the clock is updated for the first
time, assuming the first update corrects the clock and later checks can work
with correct time.
.RE
+.sp
+\fBrefresh\fP \fIinterval\fP
+.RS 4
+This directive specifies the interval (in seconds) between refreshing IP
+addresses of NTP sources specified by hostname. If the hostname no longer
+resolves to the currently used address, it will be replaced with one of the new
+addresses to avoid using a server which is no longer intended for service, even
+if it is still responding correctly and would not be replaced as unreachable.
+Only one source is refreshed at a time. The default value is 1209600 (2 weeks)
+and the maximum value is 2^31\-1 (68 years). A value of 0 disables the periodic
+refreshment.
+.sp
+The \fBrefresh\fP command can be used to refresh all
+sources immediately.
+.RE
.SS "Source selection"
.sp
\fBauthselectmode\fP \fImode\fP
diff --git a/doc/chronyc.adoc b/doc/chronyc.adoc
index 2a37e5c..6948634 100644
--- a/doc/chronyc.adoc
+++ b/doc/chronyc.adoc
@@ -970,12 +970,17 @@ current set of sources. It is equivalent to the *polltarget* option in the
[[refresh]]*refresh*::
The *refresh* command can be used to force *chronyd* to resolve the names of
-configured sources to IP addresses again, e.g. after suspending and resuming
-the machine in a different network.
+configured NTP sources to IP addresses again and replace any addresses missing
+in the list of resolved addresses.
+
-Sources that stop responding will be replaced with newly resolved addresses
-automatically after 8 polling intervals, but this command can still be useful
-to replace them immediately and not wait until they are marked as unreachable.
+Sources that stop responding are replaced with newly resolved addresses
+automatically after 8 polling intervals. This command can be used to replace
+them immediately, e.g. after suspending and resuming the machine in a different
+network.
++
+Note that with pools which have more than 16 addresses, or not all IPv4 or IPv6
+addresses are included in a single DNS response (e.g. pool.ntp.org), this
+command might replace the addresses even if they are still in the pool.
[[reload]]*reload* *sources*::
The *reload sources* command causes *chronyd* to re-read all _*.sources_ files
diff --git a/doc/chronyc.man.in b/doc/chronyc.man.in
index 2d7c39c..4322c55 100644
--- a/doc/chronyc.man.in
+++ b/doc/chronyc.man.in
@@ -1,13 +1,13 @@
'\" t
.\" Title: chronyc
.\" Author: [see the "AUTHOR(S)" section]
-.\" Generator: Asciidoctor 2.0.18
-.\" Date: 2023-05-10
+.\" Generator: Asciidoctor 2.0.20
+.\" Date: 2023-06-21
.\" Manual: User manual
.\" Source: chrony @CHRONY_VERSION@
.\" Language: English
.\"
-.TH "CHRONYC" "1" "2023-05-10" "chrony @CHRONY_VERSION@" "User manual"
+.TH "CHRONYC" "1" "2023-06-21" "chrony @CHRONY_VERSION@" "User manual"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
@@ -1784,12 +1784,17 @@ current set of sources. It is equivalent to the \fBpolltarget\fP option in the
\fBrefresh\fP
.RS 4
The \fBrefresh\fP command can be used to force \fBchronyd\fP to resolve the names of
-configured sources to IP addresses again, e.g. after suspending and resuming
-the machine in a different network.
+configured NTP sources to IP addresses again and replace any addresses missing
+in the list of resolved addresses.
.sp
-Sources that stop responding will be replaced with newly resolved addresses
-automatically after 8 polling intervals, but this command can still be useful
-to replace them immediately and not wait until they are marked as unreachable.
+Sources that stop responding are replaced with newly resolved addresses
+automatically after 8 polling intervals. This command can be used to replace
+them immediately, e.g. after suspending and resuming the machine in a different
+network.
+.sp
+Note that with pools which have more than 16 addresses, or not all IPv4 or IPv6
+addresses are included in a single DNS response (e.g. pool.ntp.org), this
+command might replace the addresses even if they are still in the pool.
.RE
.sp
\fBreload\fP \fBsources\fP
diff --git a/doc/chronyd.man.in b/doc/chronyd.man.in
index b8d6906..e725fec 100644
--- a/doc/chronyd.man.in
+++ b/doc/chronyd.man.in
@@ -1,13 +1,13 @@
'\" t
.\" Title: chronyd
.\" Author: [see the "AUTHOR(S)" section]
-.\" Generator: Asciidoctor 2.0.18
-.\" Date: 2023-05-10
+.\" Generator: Asciidoctor 2.0.20
+.\" Date: 2023-06-21
.\" Manual: System Administration
.\" Source: chrony @CHRONY_VERSION@
.\" Language: English
.\"
-.TH "CHRONYD" "8" "2023-05-10" "chrony @CHRONY_VERSION@" "System Administration"
+.TH "CHRONYD" "8" "2023-06-21" "chrony @CHRONY_VERSION@" "System Administration"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
diff --git a/examples/chrony-wait.service b/examples/chrony-wait.service
index 72b028f..374f633 100644
--- a/examples/chrony-wait.service
+++ b/examples/chrony-wait.service
@@ -25,7 +25,6 @@ LockPersonality=yes
MemoryDenyWriteExecute=yes
PrivateDevices=yes
PrivateUsers=yes
-ProcSubset=pid
ProtectClock=yes
ProtectControlGroups=yes
ProtectHome=yes
diff --git a/examples/chronyd-restricted.service b/examples/chronyd-restricted.service
index 5099833..30ba7d9 100644
--- a/examples/chronyd-restricted.service
+++ b/examples/chronyd-restricted.service
@@ -36,7 +36,6 @@ PrivateDevices=yes
PrivateTmp=yes
# This breaks adjtimex()
#PrivateUsers=yes
-ProcSubset=pid
ProtectControlGroups=yes
ProtectHome=yes
ProtectHostname=yes
diff --git a/examples/chronyd.service b/examples/chronyd.service
index 4fb930e..a42eb92 100644
--- a/examples/chronyd.service
+++ b/examples/chronyd.service
@@ -24,7 +24,6 @@ LockPersonality=yes
MemoryDenyWriteExecute=yes
NoNewPrivileges=yes
PrivateTmp=yes
-ProcSubset=pid
ProtectControlGroups=yes
ProtectHome=yes
ProtectHostname=yes
diff --git a/main.c b/main.c
index 31e3c8f..3233707 100644
--- a/main.c
+++ b/main.c
@@ -331,6 +331,9 @@ go_daemon(void)
char message[1024];
int r;
+ /* Don't exit before the 'parent' */
+ waitpid(pid, NULL, 0);
+
close(pipefd[1]);
r = read(pipefd[0], message, sizeof (message));
if (r) {
@@ -353,7 +356,9 @@ go_daemon(void)
if (pid < 0) {
LOG_FATAL("fork() failed : %s", strerror(errno));
} else if (pid > 0) {
- exit(0); /* In the 'parent' */
+ /* In the 'parent' */
+ close(pipefd[1]);
+ exit(0);
} else {
/* In the child we want to leave running as the daemon */
diff --git a/memory.c b/memory.c
index 03366e5..25a484d 100644
--- a/memory.c
+++ b/memory.c
@@ -47,8 +47,13 @@ Realloc(void *ptr, size_t size)
{
void *r;
+ if (size == 0) {
+ Free(ptr);
+ return NULL;
+ }
+
r = realloc(ptr, size);
- if (!r && size)
+ if (!r)
LOG_FATAL("Could not allocate memory");
return r;
diff --git a/ntp_core.c b/ntp_core.c
index 4e2289a..9128d77 100644
--- a/ntp_core.c
+++ b/ntp_core.c
@@ -647,7 +647,7 @@ NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
result->auto_burst = params->burst;
result->auto_offline = params->auto_offline;
result->copy = params->copy && result->mode == MODE_CLIENT;
- result->poll_target = params->poll_target;
+ result->poll_target = MAX(1, params->poll_target);
result->ext_field_flags = params->ext_fields;
if (params->nts) {
@@ -803,6 +803,8 @@ NCR_ResetInstance(NCR_Instance instance)
void
NCR_ResetPoll(NCR_Instance instance)
{
+ instance->poll_score = 0.0;
+
if (instance->local_poll != instance->minpoll) {
instance->local_poll = instance->minpoll;
@@ -833,6 +835,12 @@ NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr, int
inst->local_addr.sock_fd = NIO_OpenServerSocket(remote_addr);
}
+ /* Reset the polling interval only if the source wasn't unreachable to
+ avoid increasing server/network load in case that is what caused
+ the source to be unreachable */
+ if (SRC_IsReachable(inst->source))
+ NCR_ResetPoll(inst);
+
/* Update the reference ID and reset the source/sourcestats instances */
SRC_SetRefid(inst->source, UTI_IPToRefid(&remote_addr->ip_addr),
&inst->remote_addr.ip_addr);
@@ -2894,7 +2902,7 @@ NCR_ModifyMinstratum(NCR_Instance inst, int new_min_stratum)
void
NCR_ModifyPolltarget(NCR_Instance inst, int new_poll_target)
{
- inst->poll_target = new_poll_target;
+ inst->poll_target = MAX(1, new_poll_target);
LOG(LOGS_INFO, "Source %s new polltarget %d",
UTI_IPToString(&inst->remote_addr.ip_addr), new_poll_target);
}
diff --git a/ntp_sources.c b/ntp_sources.c
index fa562a1..0ffa972 100644
--- a/ntp_sources.c
+++ b/ntp_sources.c
@@ -32,6 +32,7 @@
#include "sysincl.h"
#include "array.h"
+#include "conf.h"
#include "ntp_sources.h"
#include "ntp_core.h"
#include "ntp_io.h"
@@ -64,6 +65,7 @@ typedef struct {
received from the source yet */
uint32_t conf_id; /* Configuration ID, which can be shared with
different sources in case of a pool */
+ double last_resolving; /* Time of last name resolving (monotonic) */
} SourceRecord;
/* Hash table of SourceRecord, its size is a power of two and it's never
@@ -96,6 +98,9 @@ struct UnresolvedSource {
char *name;
/* Flag indicating addresses should be used in a random order */
int random_order;
+ /* Flag indicating current address should be replaced only if it is
+ no longer returned by the resolver */
+ int refreshment;
/* Next unresolved source in the list */
struct UnresolvedSource *next;
};
@@ -103,7 +108,7 @@ struct UnresolvedSource {
#define RESOLVE_INTERVAL_UNIT 7
#define MIN_RESOLVE_INTERVAL 2
#define MAX_RESOLVE_INTERVAL 9
-#define MIN_REPLACEMENT_INTERVAL 8
+#define MAX_REPLACEMENT_INTERVAL 9
static struct UnresolvedSource *unresolved_sources = NULL;
static int resolving_interval = 0;
@@ -184,6 +189,7 @@ void
NSR_Initialise(void)
{
n_sources = 0;
+ resolving_id = 0;
initialised = 1;
records = ARR_CreateInstance(sizeof (SourceRecord));
@@ -206,6 +212,7 @@ NSR_Finalise(void)
ARR_DestroyInstance(records);
ARR_DestroyInstance(pools);
+ SCH_RemoveTimeout(resolving_id);
while (unresolved_sources)
remove_unresolved_source(unresolved_sources);
@@ -384,6 +391,7 @@ add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type,
record->pool_id = pool_id;
record->tentative = 1;
record->conf_id = conf_id;
+ record->last_resolving = SCH_GetLastEventMonoTime();
record_lock = 0;
@@ -517,6 +525,19 @@ process_resolved_name(struct UnresolvedSource *us, IPAddr *ip_addrs, int n_addrs
unsigned short first = 0;
int i, j;
+ /* Keep using the current address if it is being refreshed and it is
+ still included in the resolved addresses */
+ if (us->refreshment) {
+ assert(us->pool_id == INVALID_POOL);
+
+ for (i = 0; i < n_addrs; i++) {
+ if (UTI_CompareIPs(&us->address.ip_addr, &ip_addrs[i], NULL) == 0) {
+ DEBUG_LOG("%s still fresh", UTI_IPToString(&us->address.ip_addr));
+ return;
+ }
+ }
+ }
+
if (us->random_order)
UTI_GetRandomBytes(&first, sizeof (first));
@@ -773,6 +794,7 @@ NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
us = MallocNew(struct UnresolvedSource);
us->name = Strdup(name);
us->random_order = 0;
+ us->refreshment = 0;
remote_addr.ip_addr.family = IPADDR_ID;
remote_addr.ip_addr.addr.id = ++last_address_id;
@@ -962,20 +984,23 @@ NSR_RemoveAllSources(void)
/* ================================================== */
static void
-resolve_source_replacement(SourceRecord *record)
+resolve_source_replacement(SourceRecord *record, int refreshment)
{
struct UnresolvedSource *us;
- DEBUG_LOG("trying to replace %s (%s)",
+ DEBUG_LOG("%s %s (%s)", refreshment ? "refreshing" : "trying to replace",
UTI_IPToString(&record->remote_addr->ip_addr), record->name);
+ record->last_resolving = SCH_GetLastEventMonoTime();
+
us = MallocNew(struct UnresolvedSource);
us->name = Strdup(record->name);
- /* If there never was a valid reply from this source (e.g. it was a bad
- replacement), ignore the order of addresses from the resolver to not get
- stuck to a pair of addresses if the order doesn't change, or a group of
- IPv4/IPv6 addresses if the resolver prefers inaccessible IP family */
- us->random_order = record->tentative;
+ /* Ignore the order of addresses from the resolver to not get
+ stuck with a pair of unreachable or otherwise unusable servers
+ (e.g. falsetickers) in case the order doesn't change, or a group
+ of servers if they are ordered by IP family */
+ us->random_order = 1;
+ us->refreshment = refreshment;
us->pool_id = INVALID_POOL;
us->address = *record->remote_addr;
@@ -988,11 +1013,11 @@ resolve_source_replacement(SourceRecord *record)
void
NSR_HandleBadSource(IPAddr *address)
{
- static struct timespec last_replacement;
- struct timespec now;
+ static double next_replacement = 0.0;
SourceRecord *record;
IPAddr ip_addr;
- double diff;
+ uint32_t rnd;
+ double now;
int slot;
if (!find_slot(address, &slot))
@@ -1007,15 +1032,56 @@ NSR_HandleBadSource(IPAddr *address)
return;
/* Don't resolve names too frequently */
- SCH_GetLastEventTime(NULL, NULL, &now);
- diff = UTI_DiffTimespecsToDouble(&now, &last_replacement);
- if (fabs(diff) < RESOLVE_INTERVAL_UNIT * (1 << MIN_REPLACEMENT_INTERVAL)) {
+ now = SCH_GetLastEventMonoTime();
+ if (now < next_replacement) {
DEBUG_LOG("replacement postponed");
return;
}
- last_replacement = now;
- resolve_source_replacement(record);
+ UTI_GetRandomBytes(&rnd, sizeof (rnd));
+ next_replacement = now + ((double)rnd / (uint32_t)-1) *
+ (RESOLVE_INTERVAL_UNIT * (1 << MAX_REPLACEMENT_INTERVAL));
+
+ resolve_source_replacement(record, 0);
+}
+
+/* ================================================== */
+
+static void
+maybe_refresh_source(void)
+{
+ static double last_refreshment = 0.0;
+ SourceRecord *record, *oldest_record;
+ int i, min_interval;
+ double now;
+
+ min_interval = CNF_GetRefresh();
+
+ now = SCH_GetLastEventMonoTime();
+ if (min_interval <= 0 || now < last_refreshment + min_interval)
+ return;
+
+ last_refreshment = now;
+
+ for (i = 0, oldest_record = NULL; i < ARR_GetSize(records); i++) {
+ record = get_record(i);
+ if (!record->remote_addr || UTI_IsStringIP(record->name))
+ continue;
+
+ if (!oldest_record || oldest_record->last_resolving > record->last_resolving)
+ oldest_record = record;
+ }
+
+ if (!oldest_record)
+ return;
+
+ /* Check if the name wasn't already resolved in the last interval */
+ if (now < oldest_record->last_resolving + min_interval) {
+ last_refreshment = oldest_record->last_resolving;
+ return;
+ }
+
+ resolve_source_replacement(oldest_record, 1);
}
/* ================================================== */
@@ -1031,7 +1097,7 @@ NSR_RefreshAddresses(void)
if (!record->remote_addr)
continue;
- resolve_source_replacement(record);
+ resolve_source_replacement(record, 1);
}
}
@@ -1157,6 +1223,8 @@ NSR_ProcessRx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
remove_pool_sources(record->pool_id, 1, 0);
}
}
+
+ maybe_refresh_source();
} else {
NCR_ProcessRxUnknown(remote_addr, local_addr, rx_ts, message, length);
}
diff --git a/nts_ke_server.c b/nts_ke_server.c
index 6c60a5b..f786e86 100644
--- a/nts_ke_server.c
+++ b/nts_ke_server.c
@@ -448,7 +448,7 @@ process_request(NKSN_Instance session)
aead_algorithm_values++;
/* Use the first supported algorithm */
if (aead_algorithm < 0 && SIV_GetKeyLength(ntohs(data[i])) > 0)
- aead_algorithm = ntohs(data[i]);;
+ aead_algorithm = ntohs(data[i]);
}
break;
case NKE_RECORD_ERROR:
@@ -512,6 +512,7 @@ generate_key(int index)
assert(0);
UTI_GetRandomBytesUrandom(key->key, key_length);
+ memset(key->key + key_length, 0, sizeof (key->key) - key_length);
UTI_GetRandomBytes(&key->id, sizeof (key->id));
/* Encode the index in the lowest bits of the ID */
@@ -628,6 +629,7 @@ load_keys(void)
key_length <= 0 ||
UTI_HexToBytes(words[1], new_keys[i].key, sizeof (new_keys[i].key)) != key_length)
goto error;
+ memset(new_keys[i].key + key_length, 0, sizeof (new_keys[i].key) - key_length);
}
if (i < MAX_SERVER_KEYS)
diff --git a/nts_ntp_client.c b/nts_ntp_client.c
index 10cf071..2f4b728 100644
--- a/nts_ntp_client.c
+++ b/nts_ntp_client.c
@@ -650,6 +650,7 @@ load_cookies(NNC_Instance inst)
sizeof (inst->context.c2s.key));
if (inst->context.s2c.length != SIV_GetKeyLength(algorithm) ||
+ inst->context.s2c.length <= 0 ||
inst->context.c2s.length != inst->context.s2c.length)
goto error;
diff --git a/sched.c b/sched.c
index 1505dc3..676866e 100644
--- a/sched.c
+++ b/sched.c
@@ -163,6 +163,8 @@ SCH_Finalise(void) {
ARR_DestroyInstance(file_handlers);
+ timer_queue.next = &timer_queue;
+ timer_queue.prev = &timer_queue;
for (i = 0; i < ARR_GetSize(tqe_blocks); i++)
Free(*(TimerQueueEntry **)ARR_GetElement(tqe_blocks, i));
ARR_DestroyInstance(tqe_blocks);
diff --git a/sources.c b/sources.c
index 730ac55..0d6b1c0 100644
--- a/sources.c
+++ b/sources.c
@@ -112,6 +112,9 @@ struct SRC_Instance_Record {
/* Updates left before allowing combining */
int distant;
+ /* Updates with a status requiring source replacement */
+ int bad;
+
/* Flag indicating the status of the source */
SRC_Status status;
@@ -178,12 +181,17 @@ static int reported_no_majority; /* Flag to avoid repeated log message
/* Number of updates needed to reset the distant status */
#define DISTANT_PENALTY 32
+/* Number of updates needed to trigger handling of bad sources */
+#define BAD_HANDLE_THRESHOLD 4
+
static double max_distance;
static double max_jitter;
static double reselect_distance;
static double stratum_weight;
static double combine_limit;
+static SRC_Instance last_updated_inst;
+
static LOG_FileID logfileid;
/* Identifier of the dump file */
@@ -218,6 +226,8 @@ void SRC_Initialise(void) {
LCL_AddParameterChangeHandler(slew_sources, NULL);
LCL_AddDispersionNotifyHandler(add_dispersion, NULL);
+ last_updated_inst = NULL;
+
logfileid = CNF_GetLogSelection() ? LOG_FileOpen("selection",
" Date (UTC) Time IP Address S EOpts Reach Score Last sample Low end High end")
: -1;
@@ -301,6 +311,9 @@ void SRC_DestroyInstance(SRC_Instance instance)
{
int dead_index, i;
+ if (last_updated_inst == instance)
+ last_updated_inst = NULL;
+
assert(initialised);
if (instance->index < 0 || instance->index >= n_sources ||
instance != sources[instance->index])
@@ -333,6 +346,7 @@ SRC_ResetInstance(SRC_Instance instance)
instance->reachability = 0;
instance->reachability_size = 0;
instance->distant = 0;
+ instance->bad = 0;
instance->status = SRC_BAD_STATS;
instance->sel_score = 1.0;
instance->stratum = 0;
@@ -478,6 +492,19 @@ special_mode_end(void)
return 1;
}
+/* ================================================== */
+
+static void
+handle_bad_source(SRC_Instance inst)
+{
+ if (inst->type == SRC_NTP) {
+ DEBUG_LOG("Bad source status=%c", get_status_char(inst->status));
+ NSR_HandleBadSource(inst->ip_addr);
+ }
+}
+
+/* ================================================== */
+
void
SRC_UpdateReachability(SRC_Instance inst, int reachable)
{
@@ -498,14 +525,9 @@ SRC_UpdateReachability(SRC_Instance inst, int reachable)
REF_SetUnsynchronised();
}
- /* Try to replace NTP sources that are unreachable, falsetickers, or
- have root distance or jitter larger than the allowed maximums */
- if (inst->type == SRC_NTP &&
- ((!inst->reachability && inst->reachability_size == SOURCE_REACH_BITS) ||
- inst->status == SRC_BAD_DISTANCE || inst->status == SRC_JITTERY ||
- inst->status == SRC_FALSETICKER)) {
- NSR_HandleBadSource(inst->ip_addr);
- }
+ /* Try to replace unreachable NTP sources */
+ if (inst->reachability == 0 && inst->reachability_size == SOURCE_REACH_BITS)
+ handle_bad_source(inst);
}
/* ================================================== */
@@ -661,11 +683,23 @@ mark_source(SRC_Instance inst, SRC_Status status)
inst->status = status;
- DEBUG_LOG("%s status=%c options=%x reach=%o/%d updates=%d distant=%d leap=%d vote=%d lo=%f hi=%f",
+ /* Try to replace NTP sources that are falsetickers, or have a root
+ distance or jitter larger than the allowed maximums */
+ if (inst == last_updated_inst) {
+ if (inst->bad < INT_MAX &&
+ (status == SRC_FALSETICKER || status == SRC_BAD_DISTANCE || status == SRC_JITTERY))
+ inst->bad++;
+ else
+ inst->bad = 0;
+ if (inst->bad >= BAD_HANDLE_THRESHOLD)
+ handle_bad_source(inst);
+ }
+
+ DEBUG_LOG("%s status=%c options=%x reach=%o/%d updates=%d distant=%d bad=%d leap=%d vote=%d lo=%f hi=%f",
source_to_string(inst), get_status_char(inst->status),
(unsigned int)inst->sel_options, (unsigned int)inst->reachability,
inst->reachability_size, inst->updates,
- inst->distant, (int)inst->leap, inst->leap_vote,
+ inst->distant, inst->bad, (int)inst->leap, inst->leap_vote,
inst->sel_info.lo_limit, inst->sel_info.hi_limit);
if (logfileid == -1)
@@ -811,8 +845,10 @@ SRC_SelectSource(SRC_Instance updated_inst)
double first_sample_ago, max_reach_sample_ago;
NTP_Leap leap_status;
- if (updated_inst)
+ if (updated_inst) {
updated_inst->updates++;
+ last_updated_inst = updated_inst;
+ }
if (n_sources == 0) {
/* In this case, we clearly cannot synchronise to anything */
diff --git a/sys_linux.c b/sys_linux.c
index 2a5eb8f..6849637 100644
--- a/sys_linux.c
+++ b/sys_linux.c
@@ -498,6 +498,9 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context)
SCMP_SYS(getrlimit),
SCMP_SYS(getuid),
SCMP_SYS(getuid32),
+#ifdef __NR_membarrier
+ SCMP_SYS(membarrier),
+#endif
#ifdef __NR_rseq
SCMP_SYS(rseq),
#endif
@@ -600,6 +603,7 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context)
SCMP_SYS(select),
SCMP_SYS(set_robust_list),
SCMP_SYS(write),
+ SCMP_SYS(writev),
/* Miscellaneous */
SCMP_SYS(getrandom),
@@ -654,7 +658,7 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context)
const static int fcntls[] = { F_GETFD, F_SETFD, F_GETFL, F_SETFL };
const static unsigned long ioctls[] = {
- FIONREAD, TCGETS,
+ FIONREAD, TCGETS, TIOCGWINSZ,
#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
PTP_EXTTS_REQUEST, PTP_SYS_OFFSET,
#ifdef PTP_PIN_SETFUNC
diff --git a/test/compilation/003-sanitizers b/test/compilation/003-sanitizers
index fad7831..2edd6e6 100755
--- a/test/compilation/003-sanitizers
+++ b/test/compilation/003-sanitizers
@@ -26,6 +26,7 @@ for extra_config_opts in \
"--all-privops" \
"--disable-ipv6" \
"--disable-scfilter" \
+ "--without-aes-gcm-siv" \
"--without-gnutls" \
"--without-nettle" \
"--without-nettle --without-nss" \
diff --git a/test/simulation/139-nts b/test/simulation/139-nts
index 6a2112d..0cfb874 100755
--- a/test/simulation/139-nts
+++ b/test/simulation/139-nts
@@ -160,8 +160,8 @@ for dns in 1 0; do
check_file_messages " 2 1 .* 4460 " 50 100 log.packets || test_fail
check_file_messages " 2 2 .* 4460 " 0 0 log.packets || test_fail
- check_log_messages "Source 192.168.123.1 changed to 192.168.123.2" 6 8 || test_fail
- check_log_messages "Source 192.168.123.2 replaced with 192.168.123.1" 6 8 || test_fail
+ check_log_messages "Source 192.168.123.1 changed to 192.168.123.2" 4 10 || test_fail
+ check_log_messages "Source 192.168.123.2 replaced with 192.168.123.1" 4 10 || test_fail
servers=2
diff --git a/test/simulation/147-refresh b/test/simulation/147-refresh
new file mode 100755
index 0000000..ea091e6
--- /dev/null
+++ b/test/simulation/147-refresh
@@ -0,0 +1,59 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "address refreshment"
+
+limit=1000
+servers=5
+client_conf="logdir tmp
+log measurements"
+client_server_conf="server nodes-1-2.net1.clk maxpoll 6
+pool nodes-3-4-5.net1.clk maxpoll 6 maxsources 2"
+client_chronyd_options="-d"
+chronyc_conf="refresh"
+chronyc_start=500
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+check_file_messages "20.*192.168.123.1" 0 0 measurements.log || test_fail
+check_file_messages "20.*192.168.123.2" 15 17 measurements.log || test_fail
+check_file_messages "20.*192.168.123.[345]" 31 33 measurements.log || test_fail
+rm -f tmp/measurements.log
+if check_config_h 'FEAT_DEBUG 1'; then
+ check_log_messages "refreshing 192.168.123" 3 3 || test_fail
+ check_log_messages "resolved_name.*still fresh" 3 3 || test_fail
+fi
+
+limit=1100
+client_server_conf="
+server nodes-1-2.net1.clk maxpoll 6
+pool nodes-3-4-5.net1.clk maxpoll 6 maxsources 3"
+client_conf+="
+refresh 128"
+chronyc_conf=""
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+check_file_messages "20.*192.168.123.1" 0 0 measurements.log || test_fail
+check_file_messages "20.*192.168.123.2" 16 18 measurements.log || test_fail
+check_file_messages "20.*192.168.123.[345]" 50 55 measurements.log || test_fail
+rm -f tmp/measurements.log
+if check_config_h 'FEAT_DEBUG 1'; then
+ check_log_messages "refreshing 192.168.123" 8 8 || test_fail
+ check_log_messages "resolved_name.*still fresh" 8 8 || test_fail
+ check_log_messages "refreshing 192.168.123.2" 2 2 || test_fail
+ check_log_messages "refreshing 192.168.123.3" 2 2 || test_fail
+ check_log_messages "refreshing 192.168.123.4" 2 2 || test_fail
+ check_log_messages "refreshing 192.168.123.5" 2 2 || test_fail
+fi
+
+test_pass
diff --git a/test/simulation/148-replacement b/test/simulation/148-replacement
new file mode 100755
index 0000000..b4be53b
--- /dev/null
+++ b/test/simulation/148-replacement
@@ -0,0 +1,56 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "source replacement"
+
+limit=5000
+client_conf="logdir tmp
+log measurements"
+
+servers=6
+falsetickers=2
+client_server_conf="pool nodes-1-2-3-4-5-6.net1.clk maxsources 5 polltarget 1 iburst"
+wander=1e-12
+jitter=1e-6
+min_sync_time=7
+
+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 "Detected falseticker" 2 10 || test_fail
+check_log_messages "Source 192.168.123.. replaced with" 1 3 || test_fail
+check_file_messages "20.*192.168.123.* 11.1 6 6 " 15 17 measurements.log || test_fail
+check_file_messages "20.*00:[1-5].:.. 192.168.123.* 11.1 6 6 " 1 4 measurements.log || test_fail
+rm -f tmp/measurements.log
+
+# 1 unreplaceable falseticker against 2 replaceable unreachable servers
+servers=5
+falsetickers=1
+limit=200000
+base_delay="(+ 1e-4 (* -1 (equal 0.6 to 4.5)))"
+client_conf+="
+minsources 2"
+client_server_conf="
+server 192.168.123.1
+server nodes-2-4.net1.clk
+server nodes-3-5.net1.clk"
+max_sync_time=150000
+
+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 "Detected falseticker" 2 10 || test_fail
+check_log_messages "Source 192.168.123.. replaced with" 2 70 || test_fail
+check_log_messages "2010-01-01T0[0-4]:.*Source 192.168.123.. replaced with" 2 15 || test_fail
+check_log_messages "2010-01-01T0[5-9]:.*Source 192.168.123.. replaced with" 0 15 || test_fail
+check_file_messages "20.*192.168.123.* 11.1 6 6 " 20 500 measurements.log || test_fail
+rm -f tmp/measurements.log
+
+test_pass
diff --git a/test/system/010-nts b/test/system/010-nts
index a0b366e..8d92bbc 100755
--- a/test/system/010-nts
+++ b/test/system/010-nts
@@ -43,7 +43,7 @@ wait_for_sync || test_fail
run_chronyc "authdata" || test_fail
check_chronyc_output "^Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen
=========================================================================
-127\.0\.0\.1 NTS 1 15 256 [0-9] 0 0 [78] 100$" || test_fail
+127\.0\.0\.1 NTS 1 (30|15) (128|256) [0-9] 0 0 [78] ( 64|100)$" || test_fail
stop_chronyd || test_fail
check_chronyd_messages || test_fail
@@ -57,7 +57,7 @@ wait_for_sync || test_fail
run_chronyc "authdata" || test_fail
check_chronyc_output "^Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen
=========================================================================
-127\.0\.0\.1 NTS 1 15 256 [0-9] 0 0 [78] 100$" || test_fail
+127\.0\.0\.1 NTS 1 (30|15) (128|256) [0-9] 0 0 [78] ( 64|100)$" || test_fail
stop_chronyd || test_fail
check_chronyd_messages || test_fail
diff --git a/test/system/099-scfilter b/test/system/099-scfilter
index 6b098ac..7be02bd 100755
--- a/test/system/099-scfilter
+++ b/test/system/099-scfilter
@@ -6,7 +6,7 @@ check_chronyd_features SCFILTER || test_skip "SCFILTER support disabled"
test_start "system call filter in non-destructive tests"
-for level in "-1" "1" "-2" "2"; do
+for level in 1 2 -1 -2; do
test_message 1 1 "level $level:"
for test in 0[0-8][0-9]-*[^_]; do
test_message 2 0 "$test"
diff --git a/test/system/199-scfilter b/test/system/199-scfilter
index 29b7cc3..40441b7 100755
--- a/test/system/199-scfilter
+++ b/test/system/199-scfilter
@@ -6,7 +6,7 @@ check_chronyd_features SCFILTER || test_skip "SCFILTER support disabled"
test_start "system call filter in destructive tests"
-for level in "-1" "1" "-2" "2"; do
+for level in 1 2 -1 -2; do
test_message 1 1 "level $level:"
for test in 1[0-8][0-9]-*[^_]; do
test_message 2 0 "$test"
diff --git a/test/system/test.common b/test/system/test.common
index 7005c9e..aa48ac6 100644
--- a/test/system/test.common
+++ b/test/system/test.common
@@ -42,6 +42,8 @@ test_start() {
su "$user" -s /bin/sh -c "touch $TEST_DIR/test" 2> /dev/null || \
test_skip "$user cannot access $TEST_DIR"
rm "$TEST_DIR/test"
+ else
+ chown 0:0 "$TEST_DIR" || test_skip "could not chown $TEST_DIR"
fi
echo "Testing $*:"
diff --git a/test/unit/nts_ke_server.c b/test/unit/nts_ke_server.c
index 01156c1..3d2f295 100644
--- a/test/unit/nts_ke_server.c
+++ b/test/unit/nts_ke_server.c
@@ -139,7 +139,7 @@ test_unit(void)
NKSN_Instance session;
NKE_Context context, context2;
NKE_Cookie cookie;
- int i, valid, l;
+ int i, j, valid, l;
uint32_t sum, sum2;
char conf[][100] = {
@@ -200,7 +200,9 @@ test_unit(void)
save_keys();
for (i = 0, sum = 0; i < MAX_SERVER_KEYS; i++) {
- sum += server_keys[i].id + server_keys[i].key[0];
+ sum += server_keys[i].id;
+ for (j = 0; j < sizeof (server_keys[i].key); j++)
+ sum += server_keys[i].key[j];
generate_key(i);
}
@@ -208,7 +210,9 @@ test_unit(void)
TEST_CHECK(unlink("ntskeys") == 0);
for (i = 0, sum2 = 0; i < MAX_SERVER_KEYS; i++) {
- sum2 += server_keys[i].id + server_keys[i].key[0];
+ sum2 += server_keys[i].id;
+ for (j = 0; j < sizeof (server_keys[i].key); j++)
+ sum2 += server_keys[i].key[j];
}
TEST_CHECK(sum == sum2);
diff --git a/version.txt b/version.txt
index a20eaa3..4ef0cd3 100644
--- a/version.txt
+++ b/version.txt
@@ -1 +1 @@
-4.4-pre1
+4.4-pre2