Compare commits

..

30 Commits

Author SHA1 Message Date
Miroslav Lichvar
7a03206222 doc: update NEWS 2023-06-21 11:28:54 +02:00
Miroslav Lichvar
b86c50bb9f ntp: refresh IP addresses periodically
Refresh NTP sources specified by hostname periodically (every 2 weeks
by default) to avoid long-running instances using a server which is no
longer intended for service, even if it is still responding correctly
and would not be replaced as unreachable, and help redistributing load
in large pools like pool.ntp.org. Only one source is refreshed at a time
to not interrupt clock updates if there are multiple selectable servers.

The refresh directive configures the interval. A value of 0 disables
the periodic refreshment.

Suggested-by: Ask Bjørn Hansen <ask@develooper.com>
2023-06-21 11:28:42 +02:00
Miroslav Lichvar
36f9b24dfe doc: remove out-of-date statement in server description
chronyc refresh no longer forces replacement of sources.

Fixes: b2dac47c82 ("ntp: avoid unneccessary replacements on refresh command")
2023-06-20 15:28:07 +02:00
Miroslav Lichvar
e0b75b87bf ntp: remove resolving timeout in finalization
Don't assume NSR_Finalise() can be called only on exit when the
scheduler is finalized.
2023-06-20 13:03:53 +02:00
Miroslav Lichvar
6661a61486 sched: reset timer queue in finalization
Don't leave dangling pointers to timer queue entries when they are
freed in the scheduler finalization in case some code tried to remove
a timer later.

Fixes: 6ea1082a72 ("sched: free timer blocks on exit")
2023-06-19 16:15:07 +02:00
Miroslav Lichvar
bc76291750 examples: don't set ProcSubset=pid in systemd unit files
This option seems to break detection of the FIPS mode, which is needed
by gnutls.
2023-06-15 15:23:40 +02:00
Miroslav Lichvar
2aefadd129 sources: delay source replacement
Wait for four consecutive source selections giving a bad status
(falseticker, bad distance or jittery) before triggering the source
replacement. This should reduce the rate of unnecessary replacements
and shorten the time needed to find a solution when unreplaceable
falsetickers are preventing other sources from forming a majority due
to switching back and forth to unreachable servers.
2023-06-15 14:23:47 +02:00
Miroslav Lichvar
123cb497b9 sources: replace reachable sources in selection
Instead of waiting for the next update of reachability, trigger
replacement of falsetickers, jittery and distant sources as soon as
the selection status is updated in their SRC_SelectSource() call.
2023-06-15 14:23:47 +02:00
Miroslav Lichvar
0c38e4a6ca ntp: reset poll score
When the polling interval is reset (e.g. after replacement), don't
forget to reset also the score impacting the next poll adjustment.
2023-06-15 14:23:47 +02:00
Miroslav Lichvar
0db30fd0b1 main: wait for parent process to terminate
When starting the daemon, wait in the grandparent process for the parent
process to terminate before exiting to avoid systemd logging a warning
"Supervising process $PID which is not our child". Waiting for the pipe
to be closed by the kernel when the parent process exits is not
sufficient.

Reported-by: Jan Pazdziora <jpazdziora@redhat.com>
2023-06-12 16:40:53 +02:00
Miroslav Lichvar
b90d2c084f ntp: randomize replacement interval
Replacement attempts are globally rate limited to one per 7*2^8 seconds
to limit the rate of DNS requests for public servers like pool.ntp.org.
If multiple sources are repeatedly attempting replacement (at their
polling intervals), one source can be getting all attempts for periods
of time.

Use a randomly generated interval to randomize the order of source
replacements without changing the average rate.
2023-06-08 16:14:48 +02:00
Miroslav Lichvar
ab8da7ecb9 ntp: use monotonic time for replacement interval
Avoid errors in the measured interval due to clock steps.
2023-06-08 16:10:26 +02:00
Miroslav Lichvar
05809e937c ntp: add debug message for bad sources 2023-06-08 16:10:26 +02:00
Miroslav Lichvar
8265fe2e30 client: check for allocation errors in tab completition 2023-06-08 16:10:26 +02:00
Miroslav Lichvar
c11a052955 client: avoid passing uninitialized address to format_name()
The clang memory sanitizer seems to trigger on an uninitialized value
passed to format_name() when the source is a refclock, even though the
value is not used for anything. Pass 0 in this case to avoid the error.
2023-06-08 15:56:19 +02:00
Miroslav Lichvar
109970f687 memory: use free() instead of realloc() for size 0
valgrind 3.21.0 reports realloc() of 0 bytes as an error due to having
different behavior on different systems. The only place where this can
happen in chrony is the array, which doesn't care what value realloc()
returns.

Modify the realloc wrapper to call free() in this case to make valgrind
happy.
2023-06-08 14:31:52 +02:00
Miroslav Lichvar
ca10b9e072 sys_linux: allow writev and TIOCGWINSZ in seccomp filter
Allow more syscalls for musl.

Reported-by: jvoisin <julien.voisin@dustri.org>
2023-06-01 14:52:56 +02:00
Miroslav Lichvar
19da1d95a8 test: set root ownership of tmp directory in system tests
Allow the tests to be started under a non-zero GID.
2023-06-01 14:52:56 +02:00
Miroslav Lichvar
61da7d0913 test: modify order of scfilter levels in system tests
Start with positive levels to get the offending system calls in the
system or audit log.
2023-06-01 14:52:56 +02:00
Miroslav Lichvar
105f1f90c1 test: fix 010-nts test for AES-GCM-SIV support 2023-06-01 14:52:51 +02:00
Miroslav Lichvar
c9d791e02d nts: don't load zero-length keys with unsupported algorithm
Don't load keys and cookies from the client's dump file if it has an
unsupported algorithm and unparseable keys (matching the algorithm's
expected length of zero). They would fail all SIV operations and trigger
new NTS-KE session.
2023-05-29 16:08:13 +02:00
Miroslav Lichvar
de678ff780 doc: clarify limitation of refresh command 2023-05-25 10:28:56 +02:00
Miroslav Lichvar
e16bcca617 sys_linux: allow membarrier in seccomp filter
This system call is used by musl.

Reported-by: jvoisin <julien.voisin@dustri.org>
2023-05-25 10:28:56 +02:00
Miroslav Lichvar
b57d7040b3 configure: add option to disable AES-GCM-SIV support 2023-05-25 10:28:56 +02:00
Miroslav Lichvar
c80858f738 nts: remove superfluous semicolon 2023-05-25 10:28:56 +02:00
Miroslav Lichvar
81bf7cdcdc nts: initialize unused part of server key
Initialize the unused part of shorter server NTS keys (AES-128-GCM-SIV)
loaded from ntsdumpdir to avoid sending uninitialized data in requests
to the NTS-KE helper process.

Do that also for newly generated keys in case the memory will be
allocated dynamically.

Fixes: b1230efac3 ("nts: add support for encrypting cookies with AES-128-GCM-SIV")
2023-05-25 10:28:50 +02:00
Miroslav Lichvar
b8b3830dc4 ntp: randomize address selection on all source replacements
If the resolver orders addresses by IP family, there is more than one
address in the preferred IP family, and they are all reachable, but
not selectable (e.g. falsetickers in a small pool which cannot remove
them from DNS), chronyd is unable to switch to addresses in the other IP
family as it follows the resolver's order.

Enable randomization of the address selection for all source
replacements and not just replacement of (unreachable) tentative
sources. If the system doesn't have connectivity in the other family,
the addresses will be skipped and no change in behavior should be
observed.
2023-05-23 09:33:48 +02:00
Miroslav Lichvar
d4738e1259 ntp: set minimum polltarget
The polltarget value is used in a floating-point division in the
calculation of the poll adjustment. Set 1 as the minimum accepted
polltarget value to avoid working with infinite values.
2023-05-18 10:46:46 +02:00
Miroslav Lichvar
5ba42cee45 ntp: reset polling interval when replacing sources
Set the polling interval to minpoll when changing address of a source,
but only if it is reachable to avoid increasing load on server or
network in case that is the reason for the source being unreachable.

This shortens the time needed to replace a falseticker or
unsynchronized source with a selectable source.
2023-05-18 10:46:42 +02:00
Miroslav Lichvar
b2dac47c82 ntp: avoid unneccessary replacements on refresh command
When the refresh command is issued, instead of trying to replace all
NTP sources as if they were unreachable or falsetickers, keep using the
current address if it is still returned by the resolver for the name.
This avoids unnecessary loss of measurements and switching to
potentially unreachable addresses.
2023-05-15 17:23:48 +02:00
28 changed files with 348 additions and 60 deletions

3
NEWS
View File

@@ -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

View File

@@ -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:

13
conf.c
View File

@@ -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)
{

2
conf.h
View File

@@ -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);

7
configure vendored
View File

@@ -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);'

View File

@@ -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_::

View File

@@ -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

View File

@@ -25,7 +25,6 @@ LockPersonality=yes
MemoryDenyWriteExecute=yes
PrivateDevices=yes
PrivateUsers=yes
ProcSubset=pid
ProtectClock=yes
ProtectControlGroups=yes
ProtectHome=yes

View File

@@ -36,7 +36,6 @@ PrivateDevices=yes
PrivateTmp=yes
# This breaks adjtimex()
#PrivateUsers=yes
ProcSubset=pid
ProtectControlGroups=yes
ProtectHome=yes
ProtectHostname=yes

View File

@@ -24,7 +24,6 @@ LockPersonality=yes
MemoryDenyWriteExecute=yes
NoNewPrivileges=yes
PrivateTmp=yes
ProcSubset=pid
ProtectControlGroups=yes
ProtectHome=yes
ProtectHostname=yes

7
main.c
View File

@@ -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 */

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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)

View File

@@ -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;

View File

@@ -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);

View File

@@ -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 */

View File

@@ -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

View File

@@ -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" \

View File

@@ -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

59
test/simulation/147-refresh Executable file
View File

@@ -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

56
test/simulation/148-replacement Executable file
View File

@@ -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

View File

@@ -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

View File

@@ -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"

View File

@@ -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"

View File

@@ -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 $*:"

View File

@@ -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);