summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--FAQ60
-rw-r--r--INSTALL2
-rw-r--r--Makefile.in2
-rw-r--r--NEWS21
-rw-r--r--README6
-rw-r--r--candm.h25
-rw-r--r--client.c89
-rw-r--r--clientlog.c42
-rw-r--r--clientlog.h8
-rw-r--r--cmdmon.c79
-rw-r--r--cmdparse.c11
-rw-r--r--cmdparse.h3
-rw-r--r--conf.c125
-rw-r--r--conf.h6
-rw-r--r--doc/chrony.conf.adoc76
-rw-r--r--doc/chrony.conf.man.in93
-rw-r--r--doc/chronyc.adoc27
-rw-r--r--doc/chronyc.man.in47
-rw-r--r--doc/chronyd.man.in4
-rw-r--r--doc/contributing.adoc74
-rw-r--r--doc/faq.adoc60
-rw-r--r--examples/chrony.conf.example24
-rw-r--r--examples/chrony.conf.example38
-rw-r--r--getdate.c1
-rw-r--r--getdate.y1
-rw-r--r--leapdb.c272
-rw-r--r--leapdb.h37
-rw-r--r--logging.c13
-rw-r--r--logging.h5
-rw-r--r--main.c12
-rw-r--r--ntp_core.c48
-rw-r--r--ntp_core.h2
-rw-r--r--ntp_io.c10
-rw-r--r--ntp_signd.c13
-rw-r--r--ntp_sources.c78
-rw-r--r--ntp_sources.h11
-rw-r--r--nts_ke_server.c2
-rw-r--r--nts_ntp_server.c2
-rw-r--r--pktlength.c7
-rw-r--r--ptp.h5
-rw-r--r--refclock.c20
-rw-r--r--refclock.h1
-rw-r--r--refclock_phc.c2
-rw-r--r--reference.c122
-rw-r--r--reference.h2
-rw-r--r--regress.c2
-rw-r--r--reports.h4
-rw-r--r--rtc_linux.c1
-rw-r--r--sources.c47
-rw-r--r--sourcestats.c4
-rw-r--r--stubs.c14
-rw-r--r--sys_linux.c8
-rw-r--r--tempcomp.c7
-rwxr-xr-xtest/compilation/002-scanbuild1
-rwxr-xr-xtest/simulation/008-ntpera1
-rwxr-xr-xtest/simulation/106-refclock13
-rwxr-xr-xtest/simulation/108-peer2
-rwxr-xr-xtest/simulation/110-chronyc20
-rwxr-xr-xtest/simulation/113-leapsecond88
-rwxr-xr-xtest/simulation/118-maxdelay1
-rwxr-xr-xtest/simulation/119-smoothtime2
-rwxr-xr-xtest/simulation/121-local90
-rwxr-xr-xtest/simulation/121-orphan26
-rwxr-xr-xtest/simulation/122-xleave1
-rwxr-xr-xtest/simulation/124-tai16
-rwxr-xr-xtest/simulation/126-burst2
-rwxr-xr-xtest/simulation/127-filter2
-rwxr-xr-xtest/simulation/135-ratelimit11
-rwxr-xr-xtest/simulation/142-ntpoverptp17
-rwxr-xr-xtest/simulation/144-monoroot4
-rwxr-xr-xtest/simulation/148-replacement24
-rwxr-xr-xtest/simulation/203-initreload26
-rwxr-xr-xtest/system/007-cmdmon9
-rwxr-xr-xtest/system/008-confload25
-rwxr-xr-xtest/system/010-nts5
-rw-r--r--test/unit/clientlog.c60
-rw-r--r--test/unit/leapdb.c106
-rw-r--r--test/unit/leapdb.list22
-rw-r--r--test/unit/ntp_sources.c10
-rw-r--r--version.txt2
80 files changed, 1807 insertions, 404 deletions
diff --git a/FAQ b/FAQ
index c96acfa..cd2adc4 100644
--- a/FAQ
+++ b/FAQ
@@ -64,6 +64,7 @@ Table of Contents
each another?
o 7. Operation
? 7.1. What clocks does chronyd use?
+ ? 7.2. How accurate is my system clock?
o 8. Operating systems
? 8.1. Does chrony support Windows?
? 8.2. Are there any plans to support Windows?
@@ -765,6 +766,17 @@ all sources, even those that do not have a known address yet, with their names
as they were specified in the configuration. This can be useful to verify that
the names specified in the configuration are used as expected.
+When DNSSEC is enabled, it will not work until the time is synchronized, as it
+requires validating a signature timestamp and its expiration date, so if the
+system time is too far in the future or the past DNSSEC validation will fail
+and chronyd will be unable to resolve the address of the NTP server. In such
+cases, if hostnames are the only options and bare IP addresses cannot be used,
+DNSSEC can be disabled for chronyd using resolver-specific mechanisms, if
+available, although of course that means losing the protection afforded by
+DNSSEC. For example, when using systemd-resolved, the
+SYSTEMD_NSS_RESOLVE_VALIDATE=0 environment variable can be set, for example in
+the chronyd systemd unit via Environment=SYSTEMD_NSS_RESOLVE_VALIDATE=0.
+
3.4. Is chronyd allowed to step the system clock?
By default, chronyd adjusts the clock gradually by slowing it down or speeding
@@ -1141,6 +1153,52 @@ There are several different clocks used by chronyd:
synchronised by chronyd. Its offset is tracked relative to the NTP clock in
order to convert the hardware timestamps.
+7.2. How accurate is my system clock?
+
+chronyd does not know how accurate really is the clock it is synchronizing.
+Even if the measured offset of the clock is stable to nanoseconds, it could be
+off by milliseconds due to asymmetric network delay, e.g. caused by asymmetric
+routing or queuing delays in network switches. NTP provides root delay and root
+dispersion to enable clients to estimate the maximum error of their clock.
+
+Root delay measures the sum of round-trip times between all NTP servers on the
+path from the client to the primary time source (e.g. a GPS receiver). Half of
+the root delay is the maximum error due to asymmetric delays, assuming one
+direction (e.g. from the client to the server) has a zero delay and the other
+direction (from the server to the client) takes all of the measured delay. The
+root delay also covers timestamping errors if the server implementation and
+hardware meet the NTP requirement for transmit timestamps to never be late and
+receive timestamps to never be early.
+
+If you have additional information about the hardware and network between the
+client and primary time source, you could modify the root delay to get a better
+estimate of the maximum error. For example, from the physical distance of the
+server and signal propagation speed in the cables a minimum symmetric
+round-trip delay can be calculated and subtracted from the root delay measured
+by NTP.
+
+Root dispersion estimates errors due to instability of clocks and NTP
+measurements. chronyd adjusts the rate at which root dispersion grows between
+updates of the clock according to the stability of its NTP measurements. The
+minimum rate is set by the the maxclockerror directive. By default it is 1 ppm
+(1 microsecond per second).
+
+The estimated maximum error of the NTP clock is the sum of the root dispersion
+and half of the root delay. This value is called root distance. The current
+values of root dispersion and delay are included in the tracking report.
+
+The estimated maximum error of the system clock, which is synchronized to the
+NTP clock, is the sum of the root distance and remaining correction of the
+system clock provided as System time in the tracking report. A maximum value of
+this estimate between updates of the clock is included in the tracking log.
+
+Note that the resolution of the root delay and root dispersion fields in NTP
+messages is about 15 microseconds and chronyd rounds the values up, i.e. the
+minimum root distance an NTP client can normally observe is about 22.5
+microseconds. An NTP extension field containing root delay and dispersion in a
+better resolution of about 4 nanoseconds can be enabled by the extfield F323
+option.
+
8. Operating systems
8.1. Does chrony support Windows?
@@ -1156,4 +1214,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-12-05 14:22:10 +0100
+Last updated 2024-08-29 10:28:49 +0200
diff --git a/INSTALL b/INSTALL
index 9ca6e22..b94f070 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-12-05 14:22:10 +0100
+Last updated 2024-08-29 10:28:49 +0200
diff --git a/Makefile.in b/Makefile.in
index 101e0c6..318109b 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -37,7 +37,7 @@ GETDATE_CFLAGS = @GETDATE_CFLAGS@
EXTRA_OBJS = @EXTRA_OBJS@
-OBJS = array.o cmdparse.o conf.o local.o logging.o main.o memory.o quantiles.o \
+OBJS = array.o cmdparse.o conf.o leapdb.o local.o logging.o main.o memory.o quantiles.o \
reference.o regress.o rtc.o samplefilt.o sched.o socket.o sources.o sourcestats.o \
stubs.o smooth.o sys.o sys_null.o tempcomp.o util.o $(EXTRA_OBJS)
diff --git a/NEWS b/NEWS
index 93b21ed..c95bd2f 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,24 @@
+New in version 4.6
+==================
+
+Enhancements
+------------
+* Add activate option to local directive to set activation threshold
+* Add ipv4 and ipv6 options to server/pool/peer directive
+* Add kod option to ratelimit directive for server KoD RATE support
+* Add leapseclist directive to read NIST/IERS leap-seconds.list file
+* Add ptpdomain directive to set PTP domain for NTP over PTP
+* Allow disabling pidfile
+* Improve copy server option to accept unsynchronised status instantly
+* Log one selection failure on start
+* Add offset command to modify source offset correction
+* Add timestamp sources to ntpdata report
+
+Bug fixes
+---------
+* Fix crash on sources reload during initstepslew or RTC initialisation
+* Fix source refreshment to not repeat failed name resolving attempts
+
New in version 4.5
==================
diff --git a/README b/README
index 1eeac1b..79b515b 100644
--- a/README
+++ b/README
@@ -12,7 +12,7 @@ a time service to other computers in the network.
It is designed to perform well in a wide range of conditions, including
intermittent network connections, heavily congested networks, changing
temperatures (ordinary computer clocks are sensitive to temperature),
-and systems that do not run continuosly, or run on a virtual machine.
+and systems that do not run continuously, or run on a virtual machine.
Typical accuracy between two machines synchronised over the Internet is
within a few milliseconds; on a LAN, accuracy is typically in tens of
@@ -75,6 +75,7 @@ Lonnie Abelbeck <lonnie@abelbeck.com>
Benny Lyne Amorsen <benny@amorsen.dk>
Andrew Bishop <amb@gedanken.demon.co.uk>
Vincent Blut <vincent.debian@free.fr>
+Luca Boccassi <bluca@debian.org>
Stephan I. Boettcher <stephan@nevis1.columbia.edu>
David Bohman <debohman@gmail.com>
Goswin Brederlow <brederlo@informatik.uni-tuebingen.de>
@@ -89,7 +90,9 @@ Christian Ehrhardt <christian.ehrhardt@canonical.com>
Paul Elliott <pelliott@io.com>
Robert Fairley <rfairley@redhat.com>
Stefan R. Filipek <srfilipek@gmail.com>
+Andy Fiddaman <illumos@fiddaman.net>
Mike Fleetwood <mike@rockover.demon.co.uk>
+Rob Gill <rrobgill@protonmail.com>
Alexander Gretencord <arutha@gmx.de>
Andrew Griffiths <agriffit@redhat.com>
Walter Haidinger <walter.haidinger@gmx.at>
@@ -111,6 +114,7 @@ Paul Menzel <paulepanter@users.sourceforge.net>
Vladimir Michl <vladimir.michl@seznam.cz>
Victor Moroz <vim@prv.adlum.ru>
Kalle Olavi Niemitalo <tosi@stekt.oulu.fi>
+Patrick Oppenlander <patrick.oppenlander@gmail.com>
Frank Otto <sandwichmacher@web.de>
Denny Page <dennypage@me.com>
Rupesh Patel <rupatel@redhat.com>
diff --git a/candm.h b/candm.h
index 033cdb9..401c015 100644
--- a/candm.h
+++ b/candm.h
@@ -110,7 +110,9 @@
#define REQ_RELOAD_SOURCES 70
#define REQ_DOFFSET2 71
#define REQ_MODIFY_SELECTOPTS 72
-#define N_REQUEST_TYPES 73
+#define REQ_MODIFY_OFFSET 73
+#define REQ_LOCAL3 74
+#define N_REQUEST_TYPES 75
/* Structure used to exchange timespecs independent of time_t size */
typedef struct {
@@ -236,6 +238,8 @@ typedef struct {
int32_t stratum;
Float distance;
int32_t orphan;
+ Float activate;
+ uint32_t reserved[2];
int32_t EOR;
} REQ_Local;
@@ -279,6 +283,8 @@ typedef struct {
#define REQ_ADDSRC_COPY 0x400
#define REQ_ADDSRC_EF_EXP_MONO_ROOT 0x800
#define REQ_ADDSRC_EF_EXP_NET_CORRECTION 0x1000
+#define REQ_ADDSRC_IPV4 0x2000
+#define REQ_ADDSRC_IPV6 0x4000
typedef struct {
uint32_t type;
@@ -388,6 +394,13 @@ typedef struct {
int32_t EOR;
} REQ_Modify_SelectOpts;
+typedef struct {
+ IPAddr address;
+ uint32_t ref_id;
+ Float new_offset;
+ int32_t EOR;
+} REQ_Modify_Offset;
+
/* ================================================== */
#define PKT_TYPE_CMD_REQUEST 1
@@ -495,6 +508,7 @@ typedef struct {
REQ_AuthData auth_data;
REQ_SelectData select_data;
REQ_Modify_SelectOpts modify_select_opts;
+ REQ_Modify_Offset modify_offset;
} data; /* Command specific parameters */
/* Padding used to prevent traffic amplification. It only defines the
@@ -538,7 +552,8 @@ typedef struct {
#define RPY_SELECT_DATA 23
#define RPY_SERVER_STATS3 24
#define RPY_SERVER_STATS4 25
-#define N_REPLY_TYPES 26
+#define RPY_NTP_DATA2 26
+#define N_REPLY_TYPES 27
/* Status codes */
#define STT_SUCCESS 0
@@ -761,7 +776,11 @@ typedef struct {
uint32_t total_rx_count;
uint32_t total_valid_count;
uint32_t total_good_count;
- uint32_t reserved[3];
+ uint32_t total_kernel_tx_ts;
+ uint32_t total_kernel_rx_ts;
+ uint32_t total_hw_tx_ts;
+ uint32_t total_hw_rx_ts;
+ uint32_t reserved[4];
int32_t EOR;
} RPY_NTPData;
diff --git a/client.c b/client.c
index 7cfefba..52906d3 100644
--- a/client.c
+++ b/client.c
@@ -4,7 +4,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Lonnie Abelbeck 2016, 2018
- * Copyright (C) Miroslav Lichvar 2009-2023
+ * Copyright (C) Miroslav Lichvar 2009-2024
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -345,6 +345,24 @@ parse_source_address(char *word, IPAddr *address)
/* ================================================== */
static int
+parse_source_address_or_refid(char *s, IPAddr *address, uint32_t *ref_id)
+{
+ address->family = IPADDR_UNSPEC;
+ *ref_id = 0;
+
+ /* Don't allow hostnames to avoid conflicts with reference IDs */
+ if (UTI_StringToIdIP(s, address) || UTI_StringToIP(s, address))
+ return 1;
+
+ if (CPS_ParseRefid(s, ref_id) > 0)
+ return 1;
+
+ return 0;
+}
+
+/* ================================================== */
+
+static int
read_mask_address(char *line, IPAddr *mask, IPAddr *address)
{
unsigned int bits;
@@ -737,22 +755,24 @@ static int
process_cmd_local(CMD_Request *msg, char *line)
{
int on_off, stratum = 0, orphan = 0;
- double distance = 0.0;
+ double distance = 0.0, activate = 0.0;
if (!strcmp(line, "off")) {
on_off = 0;
- } else if (CPS_ParseLocal(line, &stratum, &orphan, &distance)) {
+ } else if (CPS_ParseLocal(line, &stratum, &orphan, &distance, &activate)) {
on_off = 1;
} else {
LOG(LOGS_ERR, "Invalid syntax for local command");
return 0;
}
- msg->command = htons(REQ_LOCAL2);
+ msg->command = htons(REQ_LOCAL3);
msg->data.local.on_off = htonl(on_off);
msg->data.local.stratum = htonl(stratum);
msg->data.local.distance = UTI_FloatHostToNetwork(distance);
msg->data.local.orphan = htonl(orphan);
+ msg->data.local.activate = UTI_FloatHostToNetwork(activate);
+ memset(msg->data.local.reserved, 0, sizeof (msg->data.local.reserved));
return 1;
}
@@ -962,6 +982,8 @@ process_cmd_add_source(CMD_Request *msg, char *line)
REQ_ADDSRC_EF_EXP_MONO_ROOT : 0) |
(data.params.ext_fields & NTP_EF_FLAG_EXP_NET_CORRECTION ?
REQ_ADDSRC_EF_EXP_NET_CORRECTION : 0) |
+ (data.family == IPADDR_INET4 ? REQ_ADDSRC_IPV4 : 0) |
+ (data.family == IPADDR_INET6 ? REQ_ADDSRC_IPV6 : 0) |
convert_addsrc_sel_options(data.params.sel_options));
msg->data.ntp_source.filter_length = htonl(data.params.filter_length);
msg->data.ntp_source.cert_set = htonl(data.params.cert_set);
@@ -1029,6 +1051,7 @@ give_help(void)
"selectopts <address|refid> <+|-options>\0Modify selection options\0"
"reselect\0Force reselecting synchronisation source\0"
"reselectdist <dist>\0Modify reselection distance\0"
+ "offset <address|refid> <offset>\0Modify offset correction\0"
"\0\0"
"NTP sources:\0\0"
"activity\0Check how many NTP sources are online/offline\0"
@@ -1139,7 +1162,8 @@ command_name_generator(const char *text, int state)
"clients", "cmdaccheck", "cmdallow", "cmddeny", "cyclelogs", "delete",
"deny", "dns", "dump", "exit", "help", "keygen", "local", "makestep",
"manual", "maxdelay", "maxdelaydevratio", "maxdelayratio", "maxpoll",
- "maxupdateskew", "minpoll", "minstratum", "ntpdata", "offline", "online", "onoffline",
+ "maxupdateskew", "minpoll", "minstratum", "ntpdata",
+ "offline", "offset", "online", "onoffline",
"polltarget", "quit", "refresh", "rekey", "reload", "reselect", "reselectdist", "reset",
"retries", "rtcdata", "selectdata", "selectopts", "serverstats", "settime",
"shutdown", "smoothing", "smoothtime", "sourcename", "sources", "sourcestats",
@@ -2329,7 +2353,7 @@ process_cmd_ntpdata(char *line)
request.command = htons(REQ_NTP_DATA);
UTI_IPHostToNetwork(&remote_addr, &request.data.ntp_data.ip_addr);
- if (!request_reply(&request, &reply, RPY_NTP_DATA, 0))
+ if (!request_reply(&request, &reply, RPY_NTP_DATA2, 0))
return 0;
UTI_IPNetworkToHost(&reply.data.ntp_data.remote_addr, &remote_addr);
@@ -2365,7 +2389,11 @@ process_cmd_ntpdata(char *line)
"Total TX : %U\n"
"Total RX : %U\n"
"Total valid RX : %U\n"
- "Total good RX : %U\n",
+ "Total good RX : %U\n"
+ "Total kernel TX : %U\n"
+ "Total kernel RX : %U\n"
+ "Total HW TX : %U\n"
+ "Total HW RX : %U\n",
UTI_IPToString(&remote_addr), UTI_IPToRefid(&remote_addr),
ntohs(reply.data.ntp_data.remote_port),
UTI_IPToString(&local_addr), UTI_IPToRefid(&local_addr),
@@ -2393,6 +2421,10 @@ process_cmd_ntpdata(char *line)
ntohl(reply.data.ntp_data.total_rx_count),
ntohl(reply.data.ntp_data.total_valid_count),
ntohl(reply.data.ntp_data.total_good_count),
+ ntohl(reply.data.ntp_data.total_kernel_tx_ts),
+ ntohl(reply.data.ntp_data.total_kernel_rx_ts),
+ ntohl(reply.data.ntp_data.total_hw_tx_ts),
+ ntohl(reply.data.ntp_data.total_hw_rx_ts),
REPORT_END);
}
@@ -2849,6 +2881,34 @@ process_cmd_activity(const char *line)
/* ================================================== */
static int
+process_cmd_offset(CMD_Request *msg, char *line)
+{
+ uint32_t ref_id;
+ IPAddr ip_addr;
+ double offset;
+ char *src;
+
+ src = line;
+ line = CPS_SplitWord(line);
+
+ if (!parse_source_address_or_refid(src, &ip_addr, &ref_id) ||
+ sscanf(line, "%lf", &offset) != 1) {
+ LOG(LOGS_ERR, "Invalid syntax for offset command");
+ return 0;
+ }
+
+ UTI_IPHostToNetwork(&ip_addr, &msg->data.modify_offset.address);
+ msg->data.modify_offset.ref_id = htonl(ref_id);
+ msg->data.modify_offset.new_offset = UTI_FloatHostToNetwork(offset);
+
+ msg->command = htons(REQ_MODIFY_OFFSET);
+
+ return 1;
+}
+
+/* ================================================== */
+
+static int
process_cmd_reselectdist(CMD_Request *msg, char *line)
{
double dist;
@@ -2929,15 +2989,10 @@ process_cmd_selectopts(CMD_Request *msg, char *line)
src = line;
line = CPS_SplitWord(line);
- ref_id = 0;
- /* Don't allow hostnames to avoid conflicts with reference IDs */
- if (!UTI_StringToIdIP(src, &ip_addr) && !UTI_StringToIP(src, &ip_addr)) {
- ip_addr.family = IPADDR_UNSPEC;
- if (CPS_ParseRefid(src, &ref_id) == 0) {
- LOG(LOGS_ERR, "Invalid syntax for selectopts command");
- return 0;
- }
+ if (!parse_source_address_or_refid(src, &ip_addr, &ref_id)) {
+ LOG(LOGS_ERR, "Invalid syntax for selectopts command");
+ return 0;
}
mask = options = 0;
@@ -3239,6 +3294,8 @@ process_line(char *line)
ret = process_cmd_ntpdata(line);
} else if (!strcmp(command, "offline")) {
do_normal_submit = process_cmd_offline(&tx_message, line);
+ } else if (!strcmp(command, "offset")) {
+ do_normal_submit = process_cmd_offset(&tx_message, line);
} else if (!strcmp(command, "online")) {
do_normal_submit = process_cmd_online(&tx_message, line);
} else if (!strcmp(command, "onoffline")) {
@@ -3383,7 +3440,7 @@ static void
display_gpl(void)
{
printf("chrony version %s\n"
- "Copyright (C) 1997-2003, 2007, 2009-2023 Richard P. Curnow and others\n"
+ "Copyright (C) 1997-2003, 2007, 2009-2024 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",
diff --git a/clientlog.c b/clientlog.c
index c408e8d..903f190 100644
--- a/clientlog.c
+++ b/clientlog.c
@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
- * Copyright (C) Miroslav Lichvar 2009, 2015-2017, 2021
+ * Copyright (C) Miroslav Lichvar 2009, 2015-2017, 2021, 2024
*
* 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
@@ -117,6 +117,14 @@ static int token_shift[MAX_SERVICES];
static int leak_rate[MAX_SERVICES];
+/* Rates at which responses requesting clients to reduce their rate
+ (e.g. NTP KoD RATE) are randomly allowed (in log2, but 0 means disabled) */
+
+#define MIN_KOD_RATE 0
+#define MAX_KOD_RATE 4
+
+static int kod_rate[MAX_SERVICES];
+
/* Limit intervals in log2 */
static int limit_interval[MAX_SERVICES];
@@ -354,18 +362,19 @@ set_bucket_params(int interval, int burst, uint16_t *max_tokens,
void
CLG_Initialise(void)
{
- int i, interval, burst, lrate, slots2;
+ int i, interval, burst, lrate, krate, slots2;
for (i = 0; i < MAX_SERVICES; i++) {
max_tokens[i] = 0;
tokens_per_hit[i] = 0;
token_shift[i] = 0;
leak_rate[i] = 0;
+ kod_rate[i] = 0;
limit_interval[i] = MIN_LIMIT_INTERVAL;
switch (i) {
case CLG_NTP:
- if (!CNF_GetNTPRateLimit(&interval, &burst, &lrate))
+ if (!CNF_GetNTPRateLimit(&interval, &burst, &lrate, &krate))
continue;
break;
case CLG_NTSKE:
@@ -382,6 +391,7 @@ CLG_Initialise(void)
set_bucket_params(interval, burst, &max_tokens[i], &tokens_per_hit[i], &token_shift[i]);
leak_rate[i] = CLAMP(MIN_LEAK_RATE, lrate, MAX_LEAK_RATE);
+ kod_rate[i] = CLAMP(MIN_KOD_RATE, krate, MAX_KOD_RATE);
limit_interval[i] = CLAMP(MIN_LIMIT_INTERVAL, interval, MAX_LIMIT_INTERVAL);
}
@@ -579,28 +589,28 @@ CLG_LogServiceAccess(CLG_Service service, IPAddr *client, struct timespec *now)
/* ================================================== */
static int
-limit_response_random(int leak_rate)
+limit_response_random(int rate)
{
static uint32_t rnd;
static int bits_left = 0;
int r;
- if (bits_left < leak_rate) {
+ if (bits_left < rate) {
UTI_GetRandomBytes(&rnd, sizeof (rnd));
bits_left = 8 * sizeof (rnd);
}
- /* Return zero on average once per 2^leak_rate */
- r = rnd % (1U << leak_rate) ? 1 : 0;
- rnd >>= leak_rate;
- bits_left -= leak_rate;
+ /* Return zero on average once per 2^rate */
+ r = rnd % (1U << rate) ? 1 : 0;
+ rnd >>= rate;
+ bits_left -= rate;
return r;
}
/* ================================================== */
-int
+CLG_Limit
CLG_LimitServiceRate(CLG_Service service, int index)
{
Record *record;
@@ -609,14 +619,14 @@ CLG_LimitServiceRate(CLG_Service service, int index)
check_service_number(service);
if (tokens_per_hit[service] == 0)
- return 0;
+ return CLG_PASS;
record = ARR_GetElement(records, index);
record->drop_flags &= ~(1U << service);
if (record->tokens[service] >= tokens_per_hit[service]) {
record->tokens[service] -= tokens_per_hit[service];
- return 0;
+ return CLG_PASS;
}
drop = limit_response_random(leak_rate[service]);
@@ -632,14 +642,18 @@ CLG_LimitServiceRate(CLG_Service service, int index)
if (!drop) {
record->tokens[service] = 0;
- return 0;
+ return CLG_PASS;
+ }
+
+ if (kod_rate[service] > 0 && !limit_response_random(kod_rate[service])) {
+ return CLG_KOD;
}
record->drop_flags |= 1U << service;
record->drops[service]++;
total_drops[service]++;
- return 1;
+ return CLG_DROP;
}
/* ================================================== */
diff --git a/clientlog.h b/clientlog.h
index 9ea0a3f..2e4d99c 100644
--- a/clientlog.h
+++ b/clientlog.h
@@ -37,11 +37,17 @@ typedef enum {
CLG_CMDMON,
} CLG_Service;
+typedef enum {
+ CLG_PASS = 0,
+ CLG_DROP,
+ CLG_KOD,
+} CLG_Limit;
+
extern void CLG_Initialise(void);
extern void CLG_Finalise(void);
extern int CLG_GetClientIndex(IPAddr *client);
extern int CLG_LogServiceAccess(CLG_Service service, IPAddr *client, struct timespec *now);
-extern int CLG_LimitServiceRate(CLG_Service service, int index);
+extern CLG_Limit CLG_LimitServiceRate(CLG_Service service, int index);
extern void CLG_UpdateNtpStats(int auth, NTP_Timestamp_Source rx_ts_src,
NTP_Timestamp_Source tx_ts_src);
extern int CLG_GetNtpMinPoll(void);
diff --git a/cmdmon.c b/cmdmon.c
index 9adc9d6..4accdf6 100644
--- a/cmdmon.c
+++ b/cmdmon.c
@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
- * Copyright (C) Miroslav Lichvar 2009-2016, 2018-2023
+ * Copyright (C) Miroslav Lichvar 2009-2016, 2018-2024
*
* 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
@@ -145,6 +145,8 @@ static const char permissions[] = {
PERMIT_AUTH, /* RELOAD_SOURCES */
PERMIT_AUTH, /* DOFFSET2 */
PERMIT_AUTH, /* MODIFY_SELECTOPTS */
+ PERMIT_AUTH, /* MODIFY_OFFSET */
+ PERMIT_AUTH, /* LOCAL3 */
};
/* ================================================== */
@@ -530,7 +532,8 @@ handle_local(CMD_Request *rx_message, CMD_Reply *tx_message)
if (ntohl(rx_message->data.local.on_off)) {
REF_EnableLocal(ntohl(rx_message->data.local.stratum),
UTI_FloatNetworkToHost(rx_message->data.local.distance),
- ntohl(rx_message->data.local.orphan));
+ ntohl(rx_message->data.local.orphan),
+ UTI_FloatNetworkToHost(rx_message->data.local.activate));
} else {
REF_DisableLocal();
}
@@ -720,9 +723,10 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
{
NTP_Source_Type type;
SourceParameters params;
+ int family, pool, port;
NSR_Status status;
+ uint32_t flags;
char *name;
- int pool, port;
switch (ntohl(rx_message->data.ntp_source.type)) {
case REQ_ADDSRC_SERVER:
@@ -750,6 +754,10 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
return;
}
+ flags = ntohl(rx_message->data.ntp_source.flags);
+
+ family = flags & REQ_ADDSRC_IPV4 ? IPADDR_INET4 :
+ flags & REQ_ADDSRC_IPV6 ? IPADDR_INET6 : IPADDR_UNSPEC;
port = ntohl(rx_message->data.ntp_source.port);
params.minpoll = ntohl(rx_message->data.ntp_source.minpoll);
params.maxpoll = ntohl(rx_message->data.ntp_source.maxpoll);
@@ -775,21 +783,19 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
params.asymmetry = UTI_FloatNetworkToHost(rx_message->data.ntp_source.asymmetry);
params.offset = UTI_FloatNetworkToHost(rx_message->data.ntp_source.offset);
- 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;
- params.burst = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_BURST ? 1 : 0;
- params.nts = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_NTS ? 1 : 0;
- params.copy = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_COPY ? 1 : 0;
- params.ext_fields = (ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_EF_EXP_MONO_ROOT ?
- NTP_EF_FLAG_EXP_MONO_ROOT : 0) |
- (ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_EF_EXP_NET_CORRECTION ?
- NTP_EF_FLAG_EXP_NET_CORRECTION : 0);
+ params.connectivity = flags & REQ_ADDSRC_ONLINE ? SRC_ONLINE : SRC_OFFLINE;
+ params.auto_offline = !!(flags & REQ_ADDSRC_AUTOOFFLINE);
+ params.iburst = !!(flags & REQ_ADDSRC_IBURST);
+ params.interleaved = !!(flags & REQ_ADDSRC_INTERLEAVED);
+ params.burst = !!(flags & REQ_ADDSRC_BURST);
+ params.nts = !!(flags & REQ_ADDSRC_NTS);
+ params.copy = !!(flags & REQ_ADDSRC_COPY);
+ params.ext_fields = (flags & REQ_ADDSRC_EF_EXP_MONO_ROOT ? NTP_EF_FLAG_EXP_MONO_ROOT : 0) |
+ (flags & REQ_ADDSRC_EF_EXP_NET_CORRECTION ?
+ NTP_EF_FLAG_EXP_NET_CORRECTION : 0);
params.sel_options = convert_addsrc_select_options(ntohl(rx_message->data.ntp_source.flags));
- status = NSR_AddSourceByName(name, port, pool, type, &params, NULL);
+ status = NSR_AddSourceByName(name, family, port, pool, type, &params, NULL);
switch (status) {
case NSR_Success:
break;
@@ -807,6 +813,8 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
tx_message->status = htons(STT_INVALIDNAME);
break;
case NSR_InvalidAF:
+ tx_message->status = htons(STT_INVALIDAF);
+ break;
case NSR_NoSuchSource:
assert(0);
break;
@@ -1225,7 +1233,7 @@ handle_ntp_data(CMD_Request *rx_message, CMD_Reply *tx_message)
return;
}
- tx_message->reply = htons(RPY_NTP_DATA);
+ tx_message->reply = htons(RPY_NTP_DATA2);
UTI_IPHostToNetwork(&report.remote_addr, &tx_message->data.ntp_data.remote_addr);
UTI_IPHostToNetwork(&report.local_addr, &tx_message->data.ntp_data.local_addr);
tx_message->data.ntp_data.remote_port = htons(report.remote_port);
@@ -1253,6 +1261,10 @@ handle_ntp_data(CMD_Request *rx_message, CMD_Reply *tx_message)
tx_message->data.ntp_data.total_rx_count = htonl(report.total_rx_count);
tx_message->data.ntp_data.total_valid_count = htonl(report.total_valid_count);
tx_message->data.ntp_data.total_good_count = htonl(report.total_good_count);
+ tx_message->data.ntp_data.total_kernel_tx_ts = htonl(report.total_kernel_tx_ts);
+ tx_message->data.ntp_data.total_kernel_rx_ts = htonl(report.total_kernel_rx_ts);
+ tx_message->data.ntp_data.total_hw_tx_ts = htonl(report.total_hw_tx_ts);
+ tx_message->data.ntp_data.total_hw_rx_ts = htonl(report.total_hw_rx_ts);
memset(tx_message->data.ntp_data.reserved, 0xff, sizeof (tx_message->data.ntp_data.reserved));
}
@@ -1410,6 +1422,24 @@ handle_modify_selectopts(CMD_Request *rx_message, CMD_Reply *tx_message)
}
/* ================================================== */
+
+static void
+handle_modify_offset(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ uint32_t ref_id;
+ IPAddr ip_addr;
+ double offset;
+
+ UTI_IPNetworkToHost(&rx_message->data.modify_offset.address, &ip_addr);
+ ref_id = ntohl(rx_message->data.modify_offset.ref_id);
+ offset = UTI_FloatNetworkToHost(rx_message->data.modify_offset.new_offset);
+
+ if ((ip_addr.family != IPADDR_UNSPEC && !NSR_ModifyOffset(&ip_addr, offset)) ||
+ (ip_addr.family == IPADDR_UNSPEC && !RCL_ModifyOffset(ref_id, offset)))
+ tx_message->status = htons(STT_NOSUCHSOURCE);
+}
+
+/* ================================================== */
/* Read a packet and process it */
static void
@@ -1483,9 +1513,10 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
/* Don't reply to all requests from hosts other than localhost if the rate
is excessive */
- if (!localhost && log_index >= 0 && CLG_LimitServiceRate(CLG_CMDMON, log_index)) {
- DEBUG_LOG("Command packet discarded to limit response rate");
- return;
+ if (!localhost && log_index >= 0 &&
+ CLG_LimitServiceRate(CLG_CMDMON, log_index) != CLG_PASS) {
+ DEBUG_LOG("Command packet discarded to limit response rate");
+ return;
}
expected_length = PKL_CommandLength(&rx_message);
@@ -1620,8 +1651,8 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
case REQ_SETTIME:
handle_settime(&rx_message, &tx_message);
break;
-
- case REQ_LOCAL2:
+
+ case REQ_LOCAL3:
handle_local(&rx_message, &tx_message);
break;
@@ -1809,6 +1840,10 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
handle_modify_selectopts(&rx_message, &tx_message);
break;
+ case REQ_MODIFY_OFFSET:
+ handle_modify_offset(&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 ac5ace2..77447dc 100644
--- a/cmdparse.c
+++ b/cmdparse.c
@@ -46,6 +46,7 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
uint32_t ef_type;
int n, sel_option;
+ src->family = IPADDR_UNSPEC;
src->port = SRC_DEFAULT_PORT;
src->params.minpoll = SRC_DEFAULT_MINPOLL;
src->params.maxpoll = SRC_DEFAULT_MAXPOLL;
@@ -127,6 +128,10 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
} else if (!strcasecmp(cmd, "filter")) {
if (sscanf(line, "%d%n", &src->params.filter_length, &n) != 1)
return 0;
+ } else if (!strcasecmp(cmd, "ipv4")) {
+ src->family = IPADDR_INET4;
+ } else if (!strcasecmp(cmd, "ipv6")) {
+ src->family = IPADDR_INET6;
} else if (!strcasecmp(cmd, "maxdelay")) {
if (sscanf(line, "%lf%n", &src->params.max_delay, &n) != 1)
return 0;
@@ -291,13 +296,14 @@ CPS_ParseAllowDeny(char *line, int *all, IPAddr *ip, int *subnet_bits)
/* ================================================== */
int
-CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance)
+CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance, double *activate)
{
int n;
char *cmd;
*stratum = 10;
*distance = 1.0;
+ *activate = 0.0;
*orphan = 0;
while (*line) {
@@ -314,6 +320,9 @@ CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance)
} else if (!strcasecmp(cmd, "distance")) {
if (sscanf(line, "%lf%n", distance, &n) != 1)
return 0;
+ } else if (!strcasecmp(cmd, "activate")) {
+ if (sscanf(line, "%lf%n", activate, &n) != 1)
+ return 0;
} else {
return 0;
}
diff --git a/cmdparse.h b/cmdparse.h
index 7a87979..f0bc7a4 100644
--- a/cmdparse.h
+++ b/cmdparse.h
@@ -32,6 +32,7 @@
typedef struct {
char *name;
+ int family;
int port;
SourceParameters params;
} CPS_NTP_Source;
@@ -46,7 +47,7 @@ extern int CPS_GetSelectOption(char *option);
extern int CPS_ParseAllowDeny(char *line, int *all, IPAddr *ip, int *subnet_bits);
/* Parse a command to enable local reference */
-extern int CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance);
+extern int CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance, double *activate);
/* Remove extra white-space and comments */
extern void CPS_NormalizeLine(char *line);
diff --git a/conf.c b/conf.c
index fa74459..a06423e 100644
--- a/conf.c
+++ b/conf.c
@@ -78,8 +78,9 @@ static void parse_makestep(char *);
static void parse_maxchange(char *);
static void parse_ntsserver(char *, ARR_Instance files);
static void parse_ntstrustedcerts(char *);
+static void parse_pidfile(char *line);
static void parse_ratelimit(char *line, int *enabled, int *interval,
- int *burst, int *leak);
+ int *burst, int *leak, int *kod);
static void parse_refclock(char *);
static void parse_smoothtime(char *);
static void parse_source(char *line, char *type, int fatal);
@@ -129,6 +130,7 @@ static int enable_local=0;
static int local_stratum;
static int local_orphan;
static double local_distance;
+static double local_activate;
/* Threshold (in seconds) - if absolute value of initial error is less
than this, slew instead of stepping */
@@ -220,6 +222,7 @@ static int ntp_ratelimit_enabled = 0;
static int ntp_ratelimit_interval = 3;
static int ntp_ratelimit_burst = 8;
static int ntp_ratelimit_leak = 2;
+static int ntp_ratelimit_kod = 0;
static int nts_ratelimit_enabled = 0;
static int nts_ratelimit_interval = 6;
static int nts_ratelimit_burst = 8;
@@ -249,6 +252,9 @@ static REF_LeapMode leapsec_mode = REF_LeapModeSystem;
/* Name of a system timezone containing leap seconds occuring at midnight */
static char *leapsec_tz = NULL;
+/* File name of leap seconds list, usually /usr/share/zoneinfo/leap-seconds.list */
+static char *leapsec_list = NULL;
+
/* Name of the user to which will be dropped root privileges. */
static char *user;
@@ -282,19 +288,23 @@ static double hwts_timeout = 0.001;
/* PTP event port (disabled by default) */
static int ptp_port = 0;
+/* PTP domain number of NTP-over-PTP messages */
+static int ptp_domain = 123;
typedef struct {
NTP_Source_Type type;
int pool;
CPS_NTP_Source params;
+ NSR_Status status;
+ uint32_t conf_id;
} NTP_Source;
/* Array of NTP_Source */
static ARR_Instance ntp_sources;
/* Array of (char *) */
static ARR_Instance ntp_source_dirs;
-/* Array of uint32_t corresponding to ntp_sources (for sourcedirs reload) */
-static ARR_Instance ntp_source_ids;
+/* Flag indicating ntp_sources is used for sourcedirs after config load */
+static int conf_ntp_sources_added = 0;
/* Array of RefclockParameters */
static ARR_Instance refclock_sources;
@@ -394,7 +404,6 @@ CNF_Initialise(int r, int client_only)
init_sources = ARR_CreateInstance(sizeof (IPAddr));
ntp_sources = ARR_CreateInstance(sizeof (NTP_Source));
ntp_source_dirs = ARR_CreateInstance(sizeof (char *));
- ntp_source_ids = ARR_CreateInstance(sizeof (uint32_t));
refclock_sources = ARR_CreateInstance(sizeof (RefclockParameters));
broadcasts = ARR_CreateInstance(sizeof (NTP_Broadcast_Destination));
@@ -454,7 +463,6 @@ CNF_Finalise(void)
ARR_DestroyInstance(init_sources);
ARR_DestroyInstance(ntp_sources);
ARR_DestroyInstance(ntp_source_dirs);
- ARR_DestroyInstance(ntp_source_ids);
ARR_DestroyInstance(refclock_sources);
ARR_DestroyInstance(broadcasts);
@@ -471,6 +479,7 @@ CNF_Finalise(void)
Free(hwclock_file);
Free(keys_file);
Free(leapsec_tz);
+ Free(leapsec_list);
Free(logdir);
Free(bind_ntp_iface);
Free(bind_acq_iface);
@@ -585,7 +594,7 @@ CNF_ParseLine(const char *filename, int number, char *line)
parse_int(p, &cmd_port);
} else if (!strcasecmp(command, "cmdratelimit")) {
parse_ratelimit(p, &cmd_ratelimit_enabled, &cmd_ratelimit_interval,
- &cmd_ratelimit_burst, &cmd_ratelimit_leak);
+ &cmd_ratelimit_burst, &cmd_ratelimit_leak, NULL);
} else if (!strcasecmp(command, "combinelimit")) {
parse_double(p, &combine_limit);
} else if (!strcasecmp(command, "confdir")) {
@@ -620,6 +629,8 @@ CNF_ParseLine(const char *filename, int number, char *line)
parse_leapsecmode(p);
} else if (!strcasecmp(command, "leapsectz")) {
parse_string(p, &leapsec_tz);
+ } else if (!strcasecmp(command, "leapseclist")) {
+ parse_string(p, &leapsec_list);
} else if (!strcasecmp(command, "local")) {
parse_local(p);
} else if (!strcasecmp(command, "lock_all")) {
@@ -670,7 +681,7 @@ CNF_ParseLine(const char *filename, int number, char *line)
parse_string(p, &ntp_signd_socket);
} else if (!strcasecmp(command, "ntsratelimit")) {
parse_ratelimit(p, &nts_ratelimit_enabled, &nts_ratelimit_interval,
- &nts_ratelimit_burst, &nts_ratelimit_leak);
+ &nts_ratelimit_burst, &nts_ratelimit_leak, NULL);
} else if (!strcasecmp(command, "ntscachedir") ||
!strcasecmp(command, "ntsdumpdir")) {
parse_string(p, &nts_dump_dir);
@@ -693,16 +704,18 @@ CNF_ParseLine(const char *filename, int number, char *line)
} else if (!strcasecmp(command, "peer")) {
parse_source(p, command, 1);
} else if (!strcasecmp(command, "pidfile")) {
- parse_string(p, &pidfile);
+ parse_pidfile(p);
} else if (!strcasecmp(command, "pool")) {
parse_source(p, command, 1);
} else if (!strcasecmp(command, "port")) {
parse_int(p, &ntp_port);
+ } else if (!strcasecmp(command, "ptpdomain")) {
+ parse_int(p, &ptp_domain);
} else if (!strcasecmp(command, "ptpport")) {
parse_int(p, &ptp_port);
} else if (!strcasecmp(command, "ratelimit")) {
parse_ratelimit(p, &ntp_ratelimit_enabled, &ntp_ratelimit_interval,
- &ntp_ratelimit_burst, &ntp_ratelimit_leak);
+ &ntp_ratelimit_burst, &ntp_ratelimit_leak, &ntp_ratelimit_kod);
} else if (!strcasecmp(command, "refclock")) {
parse_refclock(p);
} else if (!strcasecmp(command, "refresh")) {
@@ -823,6 +836,9 @@ parse_source(char *line, char *type, int fatal)
}
source.params.name = Strdup(source.params.name);
+ source.status = NSR_NoSuchSource;
+ source.conf_id = 0;
+
ARR_AppendElement(ntp_sources, &source);
}
@@ -840,7 +856,7 @@ parse_sourcedir(char *line)
/* ================================================== */
static void
-parse_ratelimit(char *line, int *enabled, int *interval, int *burst, int *leak)
+parse_ratelimit(char *line, int *enabled, int *interval, int *burst, int *leak, int *kod)
{
int n, val;
char *opt;
@@ -861,6 +877,8 @@ parse_ratelimit(char *line, int *enabled, int *interval, int *burst, int *leak)
*burst = val;
else if (!strcasecmp(opt, "leak"))
*leak = val;
+ else if (!strcasecmp(opt, "kod") && kod)
+ *kod = val;
else
command_parse_error();
}
@@ -1058,7 +1076,7 @@ parse_log(char *line)
static void
parse_local(char *line)
{
- if (!CPS_ParseLocal(line, &local_stratum, &local_orphan, &local_distance))
+ if (!CPS_ParseLocal(line, &local_stratum, &local_orphan, &local_distance, &local_activate))
command_parse_error();
enable_local = 1;
}
@@ -1512,6 +1530,20 @@ parse_hwtimestamp(char *line)
/* ================================================== */
+static void
+parse_pidfile(char *line)
+{
+ parse_string(line, &pidfile);
+
+ /* / disables the PID file handling */
+ if (strcmp(pidfile, "/") == 0) {
+ Free(pidfile);
+ pidfile = NULL;
+ }
+}
+
+/* ================================================== */
+
static const char *
get_basename(const char *path)
{
@@ -1664,6 +1696,8 @@ compare_sources(const void *a, const void *b)
return d;
if ((d = (int)sa->pool - (int)sb->pool) != 0)
return d;
+ if ((d = (int)sa->params.family - (int)sb->params.family) != 0)
+ return d;
if ((d = (int)sa->params.port - (int)sb->params.port) != 0)
return d;
return memcmp(&sa->params.params, &sb->params.params, sizeof (sa->params.params));
@@ -1676,18 +1710,17 @@ reload_source_dirs(void)
{
NTP_Source *prev_sources, *new_sources, *source;
unsigned int i, j, prev_size, new_size, unresolved;
- uint32_t *prev_ids, *new_ids;
char buf[MAX_LINE_LENGTH];
NSR_Status s;
int d, pass;
- prev_size = ARR_GetSize(ntp_source_ids);
- if (prev_size > 0 && ARR_GetSize(ntp_sources) != prev_size)
- assert(0);
+ /* Ignore reload command before adding configured sources */
+ if (!conf_ntp_sources_added)
+ return;
+
+ prev_size = ARR_GetSize(ntp_sources);
- /* Save the current sources and their configuration IDs */
- prev_ids = MallocArray(uint32_t, prev_size);
- memcpy(prev_ids, ARR_GetElements(ntp_source_ids), prev_size * sizeof (prev_ids[0]));
+ /* Save the current sources */
prev_sources = MallocArray(NTP_Source, prev_size);
memcpy(prev_sources, ARR_GetElements(ntp_sources), prev_size * sizeof (prev_sources[0]));
@@ -1705,8 +1738,6 @@ reload_source_dirs(void)
new_size = ARR_GetSize(ntp_sources);
new_sources = ARR_GetElements(ntp_sources);
- ARR_SetSize(ntp_source_ids, new_size);
- new_ids = ARR_GetElements(ntp_source_ids);
unresolved = 0;
LOG_SetContext(LOGC_SourceFile);
@@ -1721,30 +1752,31 @@ reload_source_dirs(void)
d = i < prev_size ? -1 : 1;
/* Remove missing sources before adding others to avoid conflicts */
- if (pass == 0 && d < 0 && prev_sources[i].params.name[0] != '\0') {
- NSR_RemoveSourcesById(prev_ids[i]);
+ if (pass == 0 && d < 0 && prev_sources[i].status == NSR_Success) {
+ NSR_RemoveSourcesById(prev_sources[i].conf_id);
}
- /* Add new sources */
- if (pass == 1 && d > 0) {
+ /* Add new sources and sources that could not be added before */
+ if (pass == 1 && (d > 0 || (d == 0 && prev_sources[i].status != NSR_Success))) {
source = &new_sources[j];
- s = NSR_AddSourceByName(source->params.name, source->params.port, source->pool,
- source->type, &source->params.params, &new_ids[j]);
+ s = NSR_AddSourceByName(source->params.name, source->params.family, source->params.port,
+ source->pool, source->type, &source->params.params,
+ &source->conf_id);
+ source->status = s;
if (s == NSR_UnresolvedName) {
unresolved++;
- } else if (s != NSR_Success) {
+ } else if (s != NSR_Success && (d > 0 || s != prev_sources[i].status)) {
LOG(LOGS_ERR, "Could not add source %s : %s",
source->params.name, NSR_StatusToString(s));
-
- /* Mark the source as not present */
- source->params.name[0] = '\0';
}
}
/* Keep unchanged sources */
- if (pass == 1 && d == 0)
- new_ids[j] = prev_ids[i];
+ if (pass == 1 && d == 0) {
+ new_sources[j].status = prev_sources[i].status;
+ new_sources[j].conf_id = prev_sources[i].conf_id;
+ }
}
}
@@ -1753,7 +1785,6 @@ reload_source_dirs(void)
for (i = 0; i < prev_size; i++)
Free(prev_sources[i].params.name);
Free(prev_sources);
- Free(prev_ids);
if (unresolved > 0)
NSR_ResolveSources();
@@ -1842,15 +1873,17 @@ CNF_AddSources(void)
for (i = 0; i < ARR_GetSize(ntp_sources); i++) {
source = (NTP_Source *)ARR_GetElement(ntp_sources, i);
- s = NSR_AddSourceByName(source->params.name, source->params.port, source->pool,
- source->type, &source->params.params, NULL);
+ s = NSR_AddSourceByName(source->params.name, source->params.family, source->params.port,
+ source->pool, source->type, &source->params.params, NULL);
if (s != NSR_Success && s != NSR_UnresolvedName)
LOG(LOGS_ERR, "Could not add source %s", source->params.name);
Free(source->params.name);
}
+ /* The arrays will be used for sourcedir (re)loading */
ARR_SetSize(ntp_sources, 0);
+ conf_ntp_sources_added = 1;
reload_source_dirs();
}
@@ -2148,12 +2181,13 @@ CNF_GetCommandPort(void) {
/* ================================================== */
int
-CNF_AllowLocalReference(int *stratum, int *orphan, double *distance)
+CNF_AllowLocalReference(int *stratum, int *orphan, double *distance, double *activate)
{
if (enable_local) {
*stratum = local_stratum;
*orphan = local_orphan;
*distance = local_distance;
+ *activate = local_activate;
return 1;
} else {
return 0;
@@ -2386,6 +2420,14 @@ CNF_GetLeapSecTimezone(void)
/* ================================================== */
+char *
+CNF_GetLeapSecList(void)
+{
+ return leapsec_list;
+}
+
+/* ================================================== */
+
int
CNF_GetSchedPriority(void)
{
@@ -2402,11 +2444,12 @@ CNF_GetLockMemory(void)
/* ================================================== */
-int CNF_GetNTPRateLimit(int *interval, int *burst, int *leak)
+int CNF_GetNTPRateLimit(int *interval, int *burst, int *leak, int *kod)
{
*interval = ntp_ratelimit_interval;
*burst = ntp_ratelimit_burst;
*leak = ntp_ratelimit_leak;
+ *kod = ntp_ratelimit_kod;
return ntp_ratelimit_enabled;
}
@@ -2541,6 +2584,14 @@ CNF_GetPtpPort(void)
/* ================================================== */
int
+CNF_GetPtpDomain(void)
+{
+ return ptp_domain;
+}
+
+/* ================================================== */
+
+int
CNF_GetRefresh(void)
{
return refresh;
diff --git a/conf.h b/conf.h
index 58ebdeb..cc0f17e 100644
--- a/conf.h
+++ b/conf.h
@@ -91,6 +91,7 @@ extern char *CNF_GetNtpSigndSocket(void);
extern char *CNF_GetPidFile(void);
extern REF_LeapMode CNF_GetLeapSecMode(void);
extern char *CNF_GetLeapSecTimezone(void);
+extern char *CNF_GetLeapSecList(void);
/* Value returned in ppm, as read from file */
extern double CNF_GetMaxUpdateSkew(void);
@@ -107,14 +108,14 @@ extern double CNF_GetReselectDistance(void);
extern double CNF_GetStratumWeight(void);
extern double CNF_GetCombineLimit(void);
-extern int CNF_AllowLocalReference(int *stratum, int *orphan, double *distance);
+extern int CNF_AllowLocalReference(int *stratum, int *orphan, double *distance, double *activate);
extern void CNF_SetupAccessRestrictions(void);
extern int CNF_GetSchedPriority(void);
extern int CNF_GetLockMemory(void);
-extern int CNF_GetNTPRateLimit(int *interval, int *burst, int *leak);
+extern int CNF_GetNTPRateLimit(int *interval, int *burst, int *leak, int *kod);
extern int CNF_GetNtsRateLimit(int *interval, int *burst, int *leak);
extern int CNF_GetCommandRateLimit(int *interval, int *burst, int *leak);
extern void CNF_GetSmooth(double *max_freq, double *max_wander, int *leap_only);
@@ -158,6 +159,7 @@ extern int CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface);
extern double CNF_GetHwTsTimeout(void);
extern int CNF_GetPtpPort(void);
+extern int CNF_GetPtpDomain(void);
extern int CNF_GetRefresh(void);
diff --git a/doc/chrony.conf.adoc b/doc/chrony.conf.adoc
index cb3f95c..2c993db 100644
--- a/doc/chrony.conf.adoc
+++ b/doc/chrony.conf.adoc
@@ -3,7 +3,7 @@
// Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Stephen Wadeley 2016
// Copyright (C) Bryan Christianson 2017
-// Copyright (C) Miroslav Lichvar 2009-2023
+// Copyright (C) Miroslav Lichvar 2009-2024
//
// 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
@@ -126,6 +126,15 @@ mechanism. Unlike with the *key* option, the server and client do not need to
share a key in a key file. NTS has a Key Establishment (NTS-KE) protocol using
the Transport Layer Security (TLS) protocol to get the keys and cookies
required by NTS for authentication of NTP packets.
++
+With this option, the hostname specified in the server or pool directive is the
+NTS-KE server or pool of NTS-KE servers respectively. The NTP server usually
+runs on the same host, but it can be separated from the NTS-KE server (the
+hostname or address of the NTP server is provided to the client by the NTS-KE
+server).
++
+The NTS-KE server can be specified by IP address if it is included in the
+server's certificate as a Subject Alternative Name (SAN).
*certset* _ID_:::
This option specifies which set of trusted certificates should be used to verify
the server's certificate when the *nts* option is enabled. Sets of certificates
@@ -220,7 +229,7 @@ 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.
+Prefer this source over other selectable sources without the *prefer* option.
*noselect*:::
Never select this source. This is particularly useful for monitoring.
*trust*:::
@@ -343,6 +352,12 @@ the PTP port. The corrections are applied only to NTP measurements with HW
timestamps (enabled by the <<hwtimestamp,*hwtimestamp*>> directive). This
field should be enabled only for servers known to be running *chronyd* version
4.5 or later.
+*ipv4*:::
+*ipv6*:::
+These options force *chronyd* to use only IPv4 or IPv6 addresses respectively
+for this source. They do not override the *-4* or *-6* option on the *chronyd*
+command line.
+
{blank}:::
[[pool]]*pool* _name_ [_option_]...::
@@ -655,7 +670,7 @@ default is 64. With drivers that perform their own polling (PPS, PHC, SHM), the
maximum value is adjusted to the number of driver polls per source poll, i.e.
2^(_poll_ - _dpoll_).
*prefer*:::
-Prefer this source over sources without the prefer option.
+Prefer this source over other selectable sources without the *prefer* option.
*noselect*:::
Never select this source. This is useful for monitoring or with sources which
are not very accurate, but are locked with a PPS refclock.
@@ -674,9 +689,10 @@ trusted and required source.
*tai*:::
This option indicates that the reference clock keeps time in TAI instead of UTC
and that *chronyd* should correct its offset by the current TAI-UTC offset. The
-<<leapsectz,*leapsectz*>> directive must be used with this option and the
-database must be kept up to date in order for this correction to work as
-expected. This option does not make sense with PPS refclocks.
+<<leapsectz,*leapsectz*>> or <<leapseclist,*leapseclist*>> directive must be
+used with this option and the database must be kept up to date in order for
+this correction to work as expected. This option does not make sense with PPS
+refclocks.
*local*:::
This option specifies that the reference clock is an unsynchronised clock which
is more stable than the system clock (e.g. TCXO, OCXO, or atomic clock) and
@@ -1263,6 +1279,19 @@ $ TZ=right/UTC date -d 'Dec 31 2008 23:59:60'
Wed Dec 31 23:59:60 UTC 2008
----
+[[leapseclist]]*leapseclist* _file_::
+This directive specifies the path to a file containing a list of leap seconds
+and TAI-UTC offsets in NIST/IERS format. It is recommended to use
+the file _leap-seconds.list_ usually included with the system timezone
+database. The behaviour of this directive is otherwise equivalent to
+<<leapsectz,*leapsectz*>>.
++
+An example of this directive is:
++
+----
+leapseclist /usr/share/zoneinfo/leap-seconds.list
+----
+
[[makestep]]*makestep* _threshold_ _limit_::
Normally *chronyd* will cause the system to gradually correct any time offset,
by slowing down or speeding up the clock as required. In certain situations,
@@ -1655,6 +1684,14 @@ The current root distance can be calculated from root delay and root dispersion
----
distance = delay / 2 + dispersion
----
+*activate* _distance_:::
+This option sets an activating root distance for the local reference. The
+local reference will not be used until the root distance drops below the
+configured value for the first time. This can be used to prevent the local
+reference from being activated on a server which has never been synchronised
+with an upstream server. The default value of 0.0 causes no activating
+distance to be used, such that the local reference is always eligible for
+activation.
*orphan*:::
This option enables a special '`orphan`' mode, where sources with stratum equal
to the local _stratum_ are assumed to not serve real time. They are ignored
@@ -1677,7 +1714,7 @@ The *orphan* mode is compatible with the *ntpd*'s orphan mode (enabled by the
An example of the directive is:
+
----
-local stratum 10 orphan distance 0.1
+local stratum 10 orphan distance 0.1 activate 0.5
----
[[ntpsigndsocket]]*ntpsigndsocket* _directory_::
@@ -1841,6 +1878,14 @@ source address from completely blocking responses to that address. The leak
rate is defined as a power of 1/2 and it is 2 by default, i.e. on average at
least every fourth request has a response. The minimum value is 1 and the
maximum value is 4.
+*kod* _rate_:::
+This option sets the rate at which Kiss-o'-Death (KoD) RATE responses are
+randomly sent when the limits specified by the *interval* and *burst* options
+are exceeded. It is an additional stream of responses to the *leak* option. A
+KoD RATE response is a request for the client to reduce its polling rate. Few
+implementations actually support it. The rate is defined as a power of 1/2. The
+default value is 0, which means disabled. The minimum value is 0 and the
+maximum value is 4.
{blank}::
+
An example use of the directive is:
@@ -1856,7 +1901,7 @@ packets, by up to 75% (with default *leak* of 2).
[[ntsratelimit]]*ntsratelimit* [_option_]...::
This directive enables rate limiting of NTS-KE requests. It is similar to the
<<ratelimit,*ratelimit*>> directive, except the default interval is 6
-(1 connection per 64 seconds).
+(1 connection per 64 seconds) and the *kod* option is not supported.
+
An example of the use of the directive is:
+
@@ -2004,8 +2049,8 @@ need to be run with the *-p 257* option to inter-operate correctly.)
[[cmdratelimit]]*cmdratelimit* [_option_]...::
This directive enables response rate limiting for command packets. It is
similar to the <<ratelimit,*ratelimit*>> directive, except responses to
-localhost are never limited and the default interval is -4 (16 packets per
-second).
+localhost are never limited, the default interval is -4 (16 packets per
+second), and the *kod* option is not supported.
+
An example of the use of the directive is:
+
@@ -2143,8 +2188,8 @@ from the example line above):
. Results of the *maxdelay*, *maxdelayratio*, and *maxdelaydevratio* (or
*maxdelayquant*) tests, and a test for synchronisation loop (1=pass,
0=fail). The first test from these four also checks the server precision,
- response time, and whether an interleaved response is acceptable for
- synchronisation. [1111]
+ response time, validity of the measured offset, and whether an interleaved
+ response is acceptable for synchronisation. [1111]
. Local poll [10]
. Remote poll [10]
. '`Score`' (an internal score within each polling level used to decide when to
@@ -2736,6 +2781,8 @@ e.g.:
----
pidfile /run/chronyd.pid
----
++
+Setting this directive to _/_ disables writing and checking of the PID file.
[[ptpport]]*ptpport* _port_::
The *ptpport* directive enables *chronyd* to send and receive NTP messages
@@ -2766,6 +2813,11 @@ hwtimestamp * rxfilter ptp
ptpport 319
----
+[[ptpdomain]]*ptpdomain* _domain_::
+The *ptpdomain* directive sets the PTP domain number of transmitted and
+accepted NTP-over-PTP messages. Messages from other domains are ignored.
+The default is 123, the minimum is 0, and the maximum is 255.
+
[[sched_priority]]*sched_priority* _priority_::
On Linux, FreeBSD, NetBSD, and illumos, the *sched_priority* directive will
select the SCHED_FIFO real-time scheduler at the specified priority (which must
diff --git a/doc/chrony.conf.man.in b/doc/chrony.conf.man.in
index 66d2358..8b04427 100644
--- a/doc/chrony.conf.man.in
+++ b/doc/chrony.conf.man.in
@@ -2,12 +2,12 @@
.\" Title: chrony.conf
.\" Author: [see the "AUTHOR(S)" section]
.\" Generator: Asciidoctor 2.0.20
-.\" Date: 2023-12-05
+.\" Date: 2024-08-29
.\" Manual: Configuration Files
.\" Source: chrony @CHRONY_VERSION@
.\" Language: English
.\"
-.TH "CHRONY.CONF" "5" "2023-12-05" "chrony @CHRONY_VERSION@" "Configuration Files"
+.TH "CHRONY.CONF" "5" "2024-08-29" "chrony @CHRONY_VERSION@" "Configuration Files"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
@@ -144,6 +144,15 @@ mechanism. Unlike with the \fBkey\fP option, the server and client do not need t
share a key in a key file. NTS has a Key Establishment (NTS\-KE) protocol using
the Transport Layer Security (TLS) protocol to get the keys and cookies
required by NTS for authentication of NTP packets.
+.sp
+With this option, the hostname specified in the server or pool directive is the
+NTS\-KE server or pool of NTS\-KE servers respectively. The NTP server usually
+runs on the same host, but it can be separated from the NTS\-KE server (the
+hostname or address of the NTP server is provided to the client by the NTS\-KE
+server).
+.sp
+The NTS\-KE server can be specified by IP address if it is included in the
+server\(cqs certificate as a Subject Alternative Name (SAN).
.RE
.sp
\fBcertset\fP \fIID\fP
@@ -280,7 +289,7 @@ enable measurements to start.)
.sp
\fBprefer\fP
.RS 4
-Prefer this source over sources without the \fBprefer\fP option.
+Prefer this source over other selectable sources without the \fBprefer\fP option.
.RE
.sp
\fBnoselect\fP
@@ -450,6 +459,13 @@ field should be enabled only for servers known to be running \fBchronyd\fP versi
.RE
.RE
.sp
+\fBipv4\fP, \fBipv6\fP
+.RS 4
+These options force \fBchronyd\fP to use only IPv4 or IPv6 addresses respectively
+for this source. They do not override the \fB\-4\fP or \fB\-6\fP option on the \fBchronyd\fP
+command line.
+.RE
+.sp
.RS 4
.RE
@@ -877,7 +893,7 @@ maximum value is adjusted to the number of driver polls per source poll, i.e.
.sp
\fBprefer\fP
.RS 4
-Prefer this source over sources without the prefer option.
+Prefer this source over other selectable sources without the \fBprefer\fP option.
.RE
.sp
\fBnoselect\fP
@@ -908,9 +924,10 @@ trusted and required source.
.RS 4
This option indicates that the reference clock keeps time in TAI instead of UTC
and that \fBchronyd\fP should correct its offset by the current TAI\-UTC offset. The
-\fBleapsectz\fP directive must be used with this option and the
-database must be kept up to date in order for this correction to work as
-expected. This option does not make sense with PPS refclocks.
+\fBleapsectz\fP or \fBleapseclist\fP directive must be
+used with this option and the database must be kept up to date in order for
+this correction to work as expected. This option does not make sense with PPS
+refclocks.
.RE
.sp
\fBlocal\fP
@@ -1652,6 +1669,25 @@ Wed Dec 31 23:59:60 UTC 2008
.if n .RE
.RE
.sp
+\fBleapseclist\fP \fIfile\fP
+.RS 4
+This directive specifies the path to a file containing a list of leap seconds
+and TAI\-UTC offsets in NIST/IERS format. It is recommended to use
+the file \fIleap\-seconds.list\fP usually included with the system timezone
+database. The behaviour of this directive is otherwise equivalent to
+\fBleapsectz\fP.
+.sp
+An example of this directive is:
+.sp
+.if n .RS 4
+.nf
+.fam C
+leapseclist /usr/share/zoneinfo/leap\-seconds.list
+.fam
+.fi
+.if n .RE
+.RE
+.sp
\fBmakestep\fP \fIthreshold\fP \fIlimit\fP
.RS 4
Normally \fBchronyd\fP will cause the system to gradually correct any time offset,
@@ -2132,6 +2168,17 @@ distance = delay / 2 + dispersion
.if n .RE
.RE
.sp
+\fBactivate\fP \fIdistance\fP
+.RS 4
+This option sets an activating root distance for the local reference. The
+local reference will not be used until the root distance drops below the
+configured value for the first time. This can be used to prevent the local
+reference from being activated on a server which has never been synchronised
+with an upstream server. The default value of 0.0 causes no activating
+distance to be used, such that the local reference is always eligible for
+activation.
+.RE
+.sp
\fBorphan\fP
.RS 4
This option enables a special \(oqorphan\(cq mode, where sources with stratum equal
@@ -2161,7 +2208,7 @@ An example of the directive is:
.if n .RS 4
.nf
.fam C
-local stratum 10 orphan distance 0.1
+local stratum 10 orphan distance 0.1 activate 0.5
.fam
.fi
.if n .RE
@@ -2369,6 +2416,17 @@ rate is defined as a power of 1/2 and it is 2 by default, i.e. on average at
least every fourth request has a response. The minimum value is 1 and the
maximum value is 4.
.RE
+.sp
+\fBkod\fP \fIrate\fP
+.RS 4
+This option sets the rate at which Kiss\-o\*(Aq\-Death (KoD) RATE responses are
+randomly sent when the limits specified by the \fBinterval\fP and \fBburst\fP options
+are exceeded. It is an additional stream of responses to the \fBleak\fP option. A
+KoD RATE response is a request for the client to reduce its polling rate. Few
+implementations actually support it. The rate is defined as a power of 1/2. The
+default value is 0, which means disabled. The minimum value is 0 and the
+maximum value is 4.
+.RE
.RE
.sp
@@ -2393,7 +2451,7 @@ packets, by up to 75% (with default \fBleak\fP of 2).
.RS 4
This directive enables rate limiting of NTS\-KE requests. It is similar to the
\fBratelimit\fP directive, except the default interval is 6
-(1 connection per 64 seconds).
+(1 connection per 64 seconds) and the \fBkod\fP option is not supported.
.sp
An example of the use of the directive is:
.sp
@@ -2582,8 +2640,8 @@ need to be run with the \fB\-p 257\fP option to inter\-operate correctly.)
.RS 4
This directive enables response rate limiting for command packets. It is
similar to the \fBratelimit\fP directive, except responses to
-localhost are never limited and the default interval is \-4 (16 packets per
-second).
+localhost are never limited, the default interval is \-4 (16 packets per
+second), and the \fBkod\fP option is not supported.
.sp
An example of the use of the directive is:
.sp
@@ -2861,8 +2919,8 @@ RFC 5905 tests 5 through 7 (1=pass, 0=fail) [111]
Results of the \fBmaxdelay\fP, \fBmaxdelayratio\fP, and \fBmaxdelaydevratio\fP (or
\fBmaxdelayquant\fP) tests, and a test for synchronisation loop (1=pass,
0=fail). The first test from these four also checks the server precision,
-response time, and whether an interleaved response is acceptable for
-synchronisation. [1111]
+response time, validity of the measured offset, and whether an interleaved
+response is acceptable for synchronisation. [1111]
.RE
.sp
.RS 4
@@ -4520,6 +4578,8 @@ pidfile /run/chronyd.pid
.fam
.fi
.if n .RE
+.sp
+Setting this directive to \fI/\fP disables writing and checking of the PID file.
.RE
.sp
\fBptpport\fP \fIport\fP
@@ -4557,6 +4617,13 @@ ptpport 319
.if n .RE
.RE
.sp
+\fBptpdomain\fP \fIdomain\fP
+.RS 4
+The \fBptpdomain\fP directive sets the PTP domain number of transmitted and
+accepted NTP\-over\-PTP messages. Messages from other domains are ignored.
+The default is 123, the minimum is 0, and the maximum is 255.
+.RE
+.sp
\fBsched_priority\fP \fIpriority\fP
.RS 4
On Linux, FreeBSD, NetBSD, and illumos, the \fBsched_priority\fP directive will
diff --git a/doc/chronyc.adoc b/doc/chronyc.adoc
index 96a0551..1a843c3 100644
--- a/doc/chronyc.adoc
+++ b/doc/chronyc.adoc
@@ -459,8 +459,8 @@ states are reported.
The following states indicate the source is not considered selectable for
synchronisation:
* _N_ - has the *noselect* option.
-* _s_ - is not synchronised.
* _M_ - does not have enough measurements.
+* _s_ - is not synchronised.
* _d_ - has a root distance larger than the maximum distance (configured by the
<<chrony.conf.adoc#maxdistance,*maxdistance*>> directive).
* _~_ - has a jitter larger than the maximum jitter (configured by the
@@ -492,7 +492,7 @@ local clock:
This column shows the name or IP address of the source if it is an NTP server,
or the reference ID if it is a reference clock.
*Auth*:::
-This column indicites whether an authentication mechanism is enabled for the
+This column indicates whether an authentication mechanism is enabled for the
source. _Y_ means yes and _N_ means no.
*COpts*:::
This column displays the configured selection options of the source.
@@ -556,6 +556,13 @@ The *reselectdist* command sets the reselection distance. It is equivalent to
the <<chrony.conf.adoc#reselectdist,*reselectdist*>> directive in the
configuration file.
+[[offset]]*offset* _address|refid_ _offset_::
+The *offset* command modifies the offset correction of an NTP source specified
+by IP address (or the _ID#XXXXXXXXXX_ identifier used for unknown addresses),
+or a reference clock specified by reference ID as a string. It is equivalent to
+the *offset* option in the <<chrony.conf.adoc#server,*server*>> or
+<<chrony.conf.adoc#refclock,*refclock*>> directive respectively.
+
=== NTP sources
[[activity]]*activity*::
@@ -689,6 +696,10 @@ Total TX : 24
Total RX : 24
Total valid RX : 24
Total good RX : 22
+Total kernel TX : 24
+Total kernel RX : 24
+Total HW TX : 0
+Total HW RX : 0
----
+
The fields are explained as follows:
@@ -746,6 +757,18 @@ The number of packets which passed the first two groups of NTP tests.
*Total good RX*:::
The number of packets which passed all three groups of NTP tests, i.e. the NTP
measurement was accepted.
+*Total kernel TX*:::
+The number of packets sent to the source for which a timestamp was captured by
+the kernel.
+*Total kernel RX*:::
+The number of packets received from the source for which a timestamp was
+captured by the kernel.
+*Total HW TX*:::
+The number of packets sent to the source for which a timestamp was captured by
+the NIC.
+*Total HW RX*:::
+The number of packets received from the source for which a timestamp was
+captured by the NIC.
[[add_peer]]*add peer* _name_ [_option_]...::
The *add peer* command allows a new NTP peer to be added whilst
diff --git a/doc/chronyc.man.in b/doc/chronyc.man.in
index 4541fc6..3e52218 100644
--- a/doc/chronyc.man.in
+++ b/doc/chronyc.man.in
@@ -2,12 +2,12 @@
.\" Title: chronyc
.\" Author: [see the "AUTHOR(S)" section]
.\" Generator: Asciidoctor 2.0.20
-.\" Date: 2023-12-05
+.\" Date: 2024-08-29
.\" Manual: User manual
.\" Source: chrony @CHRONY_VERSION@
.\" Language: English
.\"
-.TH "CHRONYC" "1" "2023-12-05" "chrony @CHRONY_VERSION@" "User manual"
+.TH "CHRONYC" "1" "2024-08-29" "chrony @CHRONY_VERSION@" "User manual"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
@@ -695,7 +695,7 @@ synchronisation:
. sp -1
. IP \(bu 2.3
.\}
-\fIs\fP \- is not synchronised.
+\fIM\fP \- does not have enough measurements.
.RE
.sp
.RS 4
@@ -706,7 +706,7 @@ synchronisation:
. sp -1
. IP \(bu 2.3
.\}
-\fIM\fP \- does not have enough measurements.
+\fIs\fP \- is not synchronised.
.RE
.sp
.RS 4
@@ -881,7 +881,7 @@ or the reference ID if it is a reference clock.
.sp
\fBAuth\fP
.RS 4
-This column indicites whether an authentication mechanism is enabled for the
+This column indicates whether an authentication mechanism is enabled for the
source. \fIY\fP means yes and \fIN\fP means no.
.RE
.sp
@@ -1054,6 +1054,15 @@ The \fBreselectdist\fP command sets the reselection distance. It is equivalent t
the \fBreselectdist\fP directive in the
configuration file.
.RE
+.sp
+\fBoffset\fP \fIaddress|refid\fP \fIoffset\fP
+.RS 4
+The \fBoffset\fP command modifies the offset correction of an NTP source specified
+by IP address (or the \fIID#XXXXXXXXXX\fP identifier used for unknown addresses),
+or a reference clock specified by reference ID as a string. It is equivalent to
+the \fBoffset\fP option in the \fBserver\fP or
+\fBrefclock\fP directive respectively.
+.RE
.SS "NTP sources"
.sp
\fBactivity\fP
@@ -1391,6 +1400,10 @@ Total TX : 24
Total RX : 24
Total valid RX : 24
Total good RX : 22
+Total kernel TX : 24
+Total kernel RX : 24
+Total HW TX : 0
+Total HW RX : 0
.fam
.fi
.if n .RE
@@ -1486,6 +1499,30 @@ The number of packets which passed the first two groups of NTP tests.
The number of packets which passed all three groups of NTP tests, i.e. the NTP
measurement was accepted.
.RE
+.sp
+\fBTotal kernel TX\fP
+.RS 4
+The number of packets sent to the source for which a timestamp was captured by
+the kernel.
+.RE
+.sp
+\fBTotal kernel RX\fP
+.RS 4
+The number of packets received from the source for which a timestamp was
+captured by the kernel.
+.RE
+.sp
+\fBTotal HW TX\fP
+.RS 4
+The number of packets sent to the source for which a timestamp was captured by
+the NIC.
+.RE
+.sp
+\fBTotal HW RX\fP
+.RS 4
+The number of packets received from the source for which a timestamp was
+captured by the NIC.
+.RE
.RE
.sp
\fBadd peer\fP \fIname\fP [\fIoption\fP]...
diff --git a/doc/chronyd.man.in b/doc/chronyd.man.in
index 96e87a0..35d6819 100644
--- a/doc/chronyd.man.in
+++ b/doc/chronyd.man.in
@@ -2,12 +2,12 @@
.\" Title: chronyd
.\" Author: [see the "AUTHOR(S)" section]
.\" Generator: Asciidoctor 2.0.20
-.\" Date: 2023-12-05
+.\" Date: 2024-08-29
.\" Manual: System Administration
.\" Source: chrony @CHRONY_VERSION@
.\" Language: English
.\"
-.TH "CHRONYD" "8" "2023-12-05" "chrony @CHRONY_VERSION@" "System Administration"
+.TH "CHRONYD" "8" "2024-08-29" "chrony @CHRONY_VERSION@" "System Administration"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
diff --git a/doc/contributing.adoc b/doc/contributing.adoc
new file mode 100644
index 0000000..7804e26
--- /dev/null
+++ b/doc/contributing.adoc
@@ -0,0 +1,74 @@
+// This file is part of chrony
+//
+// Copyright (C) Miroslav Lichvar 2024
+//
+// 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.
+
+= Contributing
+
+== Patches
+
+The source code of `chrony` is maintained in a git repository at
+https://gitlab.com/chrony/chrony. Patches can be submitted to the `chrony-dev`
+mailing list, or as a merge request on gitlab. Before spending a lot of time
+implementing a new major feature, it is recommended to ask on the mailing list
+for comments about its design and whether such feature fits the goals of the
+project.
+
+Each commit should be a self-contained logical change, which does not break
+the build or tests. New functionality and fixed bugs should be covered by a new
+test or an extended existing test in the test suite. The test can be included
+in the same commit or added as a separate commit. The same rule applies to
+documentation. All command-line options, configuration directives, and
+`chronyc` commands should be documented.
+
+The most important tests can be executed by running `make check` or `make
+quickcheck`. The unit and system tests run on all supported systems. The system
+tests require root privileges. The simulation tests run only on Linux and
+require https://gitlab.com/chrony/clknetsim[clknetsim] to be compiled in the
+directory containing the tests, but they are executed with a merge request on
+gitlab.
+
+The commit message should explain any non-trivial changes, e.g. what problem is
+the commit solving and how. The commit subject (first line of the message)
+should be written in an imperative form, prefixed with the component name if it
+is not a more general change, starting in lower case, and no period at the end.
+See the git log for examples.
+
+Simpler code is better. Less code is better. Security is a top priority.
+
+Assertions should catch only bugs in the `chrony` code. Unexpected values in
+external input (e.g. anything received from network) must be handled correctly
+without crashing and memory corruption. Fuzzing support is available at
+https://gitlab.com/chrony/chrony-fuzz. The fuzzing coverage is checked by the
+project maintainer before each release.
+
+The code should mostly be self-documenting. Comments should explain the
+less obvious things.
+
+== Coding style
+
+The code uses two spaces for indentation. No tabs. The line length should
+normally not exceed 95 characters. Too much indentation indicates the code will
+not be very readable.
+
+Function names are in an imperative form. Names of static functions use
+lowercase characters and underscores. Public functions, structures, typedefs
+are in CamelCase with a prefix specific to the module (e.g. LCL - local, NCR
+- NTP core, NKS - NTS-KE server, SST - sourcestats).
+
+Function names are not followed by space, but keywords of the language (e.g.
+`if`, `for`, `while`, `sizeof`) are followed by space.
+
+Have a look at the existing code to get a better idea what is expected.
diff --git a/doc/faq.adoc b/doc/faq.adoc
index 8fd350f..fa1b6ad 100644
--- a/doc/faq.adoc
+++ b/doc/faq.adoc
@@ -2,7 +2,7 @@
//
// Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Luke Valenta 2023
-// Copyright (C) Miroslav Lichvar 2014-2016, 2020-2023
+// Copyright (C) Miroslav Lichvar 2014-2016, 2020-2024
//
// 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
@@ -772,6 +772,17 @@ print all sources, even those that do not have a known address yet, with their
names as they were specified in the configuration. This can be useful to verify
that the names specified in the configuration are used as expected.
+When DNSSEC is enabled, it will not work until the time is synchronized, as it
+requires validating a signature timestamp and its expiration date, so if the
+system time is too far in the future or the past DNSSEC validation will fail and
+`chronyd` will be unable to resolve the address of the NTP server. In such cases,
+if hostnames are the only options and bare IP addresses cannot be used, DNSSEC
+can be disabled for `chronyd` using resolver-specific mechanisms, if available,
+although of course that means losing the protection afforded by DNSSEC.
+For example, when using systemd-resolved, the `SYSTEMD_NSS_RESOLVE_VALIDATE=0`
+environment variable can be set, for example in the `chronyd` systemd unit via
+`Environment=SYSTEMD_NSS_RESOLVE_VALIDATE=0`.
+
=== Is `chronyd` allowed to step the system clock?
By default, `chronyd` adjusts the clock gradually by slowing it down or
@@ -1155,6 +1166,53 @@ There are several different clocks used by `chronyd`:
synchronised by `chronyd`. Its offset is tracked relative to the NTP clock in
order to convert the hardware timestamps.
+=== How accurate is my system clock?
+
+`chronyd` does not know how accurate really is the clock it is synchronizing.
+Even if the measured offset of the clock is stable to nanoseconds, it could be
+off by milliseconds due to asymmetric network delay, e.g. caused by asymmetric
+routing or queuing delays in network switches. NTP provides root delay and root
+dispersion to enable clients to estimate the maximum error of their clock.
+
+Root delay measures the sum of round-trip times between all NTP servers on the
+path from the client to the primary time source (e.g. a GPS receiver). Half of
+the root delay is the maximum error due to asymmetric delays, assuming one
+direction (e.g. from the client to the server) has a zero delay and the other
+direction (from the server to the client) takes all of the measured delay. The
+root delay also covers timestamping errors if the server implementation and
+hardware meet the NTP requirement for transmit timestamps to never be late and
+receive timestamps to never be early.
+
+If you have additional information about the hardware and network between the
+client and primary time source, you could modify the root delay to get a better
+estimate of the maximum error. For example, from the physical distance of the
+server and signal propagation speed in the cables a minimum symmetric
+round-trip delay can be calculated and subtracted from the root delay measured
+by NTP.
+
+Root dispersion estimates errors due to instability of clocks and NTP
+measurements. `chronyd` adjusts the rate at which root dispersion grows between
+updates of the clock according to the stability of its NTP measurements. The
+minimum rate is set by the the `maxclockerror` directive. By default it is 1
+ppm (1 microsecond per second).
+
+The estimated maximum error of the NTP clock is the sum of the root dispersion
+and half of the root delay. This value is called root distance. The current
+values of root dispersion and delay are included in the `tracking` report.
+
+The estimated maximum error of the system clock, which is synchronized to the
+NTP clock, is the sum of the root distance and remaining correction of the
+system clock provided as `System time` in the `tracking` report. A maximum
+value of this estimate between updates of the clock is included in the
+`tracking` log.
+
+Note that the resolution of the root delay and root dispersion fields in NTP
+messages is about 15 microseconds and `chronyd` rounds the values up, i.e. the
+minimum root distance an NTP client can normally observe is about 22.5
+microseconds. An NTP extension field containing root delay and dispersion in a
+better resolution of about 4 nanoseconds can be enabled by the `extfield F323`
+option.
+
== Operating systems
=== Does `chrony` support Windows?
diff --git a/examples/chrony.conf.example2 b/examples/chrony.conf.example2
index bf2bbdd..03e7d47 100644
--- a/examples/chrony.conf.example2
+++ b/examples/chrony.conf.example2
@@ -37,8 +37,8 @@ ntsdumpdir /var/lib/chrony
# Insert/delete leap seconds by slewing instead of stepping.
#leapsecmode slew
-# Get TAI-UTC offset and leap seconds from the system tz database.
-#leapsectz right/UTC
+# Set the TAI-UTC offset of the system clock.
+#leapseclist /usr/share/zoneinfo/leap-seconds.list
# Specify directory for log files.
logdir /var/log/chrony
diff --git a/examples/chrony.conf.example3 b/examples/chrony.conf.example3
index 6d84c01..8d895d0 100644
--- a/examples/chrony.conf.example3
+++ b/examples/chrony.conf.example3
@@ -126,11 +126,11 @@ ntsdumpdir /var/lib/chrony
! pidfile /var/run/chrony/chronyd.pid
-# If the system timezone database is kept up to date and includes the
-# right/UTC timezone, chronyd can use it to determine the current
-# TAI-UTC offset and when will the next leap second occur.
+# The system timezone database usually comes with a list of leap seconds and
+# corresponding TAI-UTC offsets. chronyd can use it to set the offset of the
+# system TAI clock and have an additional source of leap seconds.
-! leapsectz right/UTC
+! leapseclist /usr/share/zoneinfo/leap-seconds.list
#######################################################################
### INITIAL CLOCK CORRECTION
diff --git a/getdate.c b/getdate.c
index 65d1b99..bca3833 100644
--- a/getdate.c
+++ b/getdate.c
@@ -2505,6 +2505,7 @@ get_date (const char *p, const time_t *now)
tm.tm_hour += yyRelHour;
tm.tm_min += yyRelMinutes;
tm.tm_sec += yyRelSeconds;
+ tm.tm_wday = 0;
/* Let mktime deduce tm_isdst if we have an absolute timestamp,
or if the relative timestamp mentions days, months, or years. */
diff --git a/getdate.y b/getdate.y
index 47d368d..c305fb7 100644
--- a/getdate.y
+++ b/getdate.y
@@ -943,6 +943,7 @@ get_date (const char *p, const time_t *now)
tm.tm_hour += yyRelHour;
tm.tm_min += yyRelMinutes;
tm.tm_sec += yyRelSeconds;
+ tm.tm_wday = 0;
/* Let mktime deduce tm_isdst if we have an absolute timestamp,
or if the relative timestamp mentions days, months, or years. */
diff --git a/leapdb.c b/leapdb.c
new file mode 100644
index 0000000..e4b2f9f
--- /dev/null
+++ b/leapdb.c
@@ -0,0 +1,272 @@
+/*
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar 2009-2018, 2020, 2022
+ * Copyright (C) Patrick Oppenlander 2023, 2024
+ *
+ * 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.
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ This module provides leap second information. */
+
+#include "config.h"
+
+#include "sysincl.h"
+
+#include "conf.h"
+#include "leapdb.h"
+#include "logging.h"
+#include "util.h"
+
+/* ================================================== */
+
+/* Source of leap second data */
+enum {
+ SRC_NONE,
+ SRC_TIMEZONE,
+ SRC_LIST,
+} leap_src;
+
+/* Offset between leap-seconds.list timestamp epoch and Unix epoch.
+ leap-seconds.list epoch is 1 Jan 1900, 00:00:00 */
+#define LEAP_SEC_LIST_OFFSET 2208988800
+
+/* ================================================== */
+
+static NTP_Leap
+get_tz_leap(time_t when, int *tai_offset)
+{
+ struct tm stm, *tm;
+ time_t t;
+ char *tz_env, tz_orig[128];
+ NTP_Leap tz_leap = LEAP_Normal;
+
+ tm = gmtime(&when);
+ if (!tm)
+ return tz_leap;
+
+ stm = *tm;
+
+ /* Temporarily switch to the timezone containing leap seconds */
+ tz_env = getenv("TZ");
+ if (tz_env) {
+ if (strlen(tz_env) >= sizeof (tz_orig))
+ return tz_leap;
+ strcpy(tz_orig, tz_env);
+ }
+ setenv("TZ", CNF_GetLeapSecTimezone(), 1);
+ tzset();
+
+ /* Get the TAI-UTC offset, which started at the epoch at 10 seconds */
+ t = mktime(&stm);
+ if (t != -1)
+ *tai_offset = t - when + 10;
+
+ /* Set the time to 23:59:60 and see how it overflows in mktime() */
+ stm.tm_sec = 60;
+ stm.tm_min = 59;
+ stm.tm_hour = 23;
+
+ t = mktime(&stm);
+
+ if (tz_env)
+ setenv("TZ", tz_orig, 1);
+ else
+ unsetenv("TZ");
+ tzset();
+
+ if (t == -1)
+ return tz_leap;
+
+ if (stm.tm_sec == 60)
+ tz_leap = LEAP_InsertSecond;
+ else if (stm.tm_sec == 1)
+ tz_leap = LEAP_DeleteSecond;
+
+ return tz_leap;
+}
+
+/* ================================================== */
+
+static NTP_Leap
+get_list_leap(time_t when, int *tai_offset)
+{
+ FILE *f;
+ char line[1024];
+ NTP_Leap ret_leap = LEAP_Normal;
+ int ret_tai_offset = 0, prev_lsl_tai_offset = 10;
+ int64_t when1900, lsl_updated = 0, lsl_expiry = 0;
+ const char *leap_sec_list = CNF_GetLeapSecList();
+
+ if (!(f = UTI_OpenFile(NULL, leap_sec_list, NULL, 'r', 0))) {
+ LOG(LOGS_ERR, "Failed to open leap seconds list %s", leap_sec_list);
+ goto out;
+ }
+
+ /* Leap second happens at midnight */
+ when = (when / (24 * 3600) + 1) * (24 * 3600);
+
+ /* leap-seconds.list timestamps are relative to 1 Jan 1900, 00:00:00 */
+ when1900 = (int64_t)when + LEAP_SEC_LIST_OFFSET;
+
+ while (fgets(line, sizeof line, f) > 0) {
+ int64_t lsl_when;
+ int lsl_tai_offset;
+ char *p;
+
+ /* Ignore blank lines */
+ for (p = line; *p && isspace(*p); ++p)
+ ;
+ if (!*p)
+ continue;
+
+ if (*line == '#') {
+ /* Update time line starts with #$ */
+ if (line[1] == '$' && sscanf(line + 2, "%"SCNd64, &lsl_updated) != 1)
+ goto error;
+ /* Expiration time line starts with #@ */
+ if (line[1] == '@' && sscanf(line + 2, "%"SCNd64, &lsl_expiry) != 1)
+ goto error;
+ /* Comment or a special comment we don't care about */
+ continue;
+ }
+
+ /* Leap entry */
+ if (sscanf(line, "%"SCNd64" %d", &lsl_when, &lsl_tai_offset) != 2)
+ goto error;
+
+ if (when1900 == lsl_when) {
+ if (lsl_tai_offset > prev_lsl_tai_offset)
+ ret_leap = LEAP_InsertSecond;
+ else if (lsl_tai_offset < prev_lsl_tai_offset)
+ ret_leap = LEAP_DeleteSecond;
+ /* When is rounded to the end of the day, so offset hasn't changed yet! */
+ ret_tai_offset = prev_lsl_tai_offset;
+ } else if (when1900 > lsl_when) {
+ ret_tai_offset = lsl_tai_offset;
+ }
+
+ prev_lsl_tai_offset = lsl_tai_offset;
+ }
+
+ /* Make sure the file looks sensible */
+ if (!feof(f) || !lsl_updated || !lsl_expiry)
+ goto error;
+
+ if (when1900 >= lsl_expiry)
+ LOG(LOGS_WARN, "Leap second list %s needs update", leap_sec_list);
+
+ goto out;
+
+error:
+ if (f)
+ fclose(f);
+ LOG(LOGS_ERR, "Failed to parse leap seconds list %s", leap_sec_list);
+ return LEAP_Normal;
+
+out:
+ if (f)
+ fclose(f);
+ *tai_offset = ret_tai_offset;
+ return ret_leap;
+}
+
+/* ================================================== */
+
+static int
+check_leap_source(NTP_Leap (*src)(time_t when, int *tai_offset))
+{
+ int tai_offset = 0;
+
+ /* Check that the leap second source has good data for Jun 30 2012 and Dec 31 2012 */
+ if (src(1341014400, &tai_offset) == LEAP_InsertSecond && tai_offset == 34 &&
+ src(1356912000, &tai_offset) == LEAP_Normal && tai_offset == 35)
+ return 1;
+
+ return 0;
+}
+
+/* ================================================== */
+
+void
+LDB_Initialise(void)
+{
+ const char *leap_tzname, *leap_sec_list;
+
+ leap_tzname = CNF_GetLeapSecTimezone();
+ if (leap_tzname && !check_leap_source(get_tz_leap)) {
+ LOG(LOGS_WARN, "Timezone %s failed leap second check, ignoring", leap_tzname);
+ leap_tzname = NULL;
+ }
+
+ leap_sec_list = CNF_GetLeapSecList();
+ if (leap_sec_list && !check_leap_source(get_list_leap)) {
+ LOG(LOGS_WARN, "Leap second list %s failed check, ignoring", leap_sec_list);
+ leap_sec_list = NULL;
+ }
+
+ if (leap_sec_list) {
+ LOG(LOGS_INFO, "Using leap second list %s", leap_sec_list);
+ leap_src = SRC_LIST;
+ } else if (leap_tzname) {
+ LOG(LOGS_INFO, "Using %s timezone to obtain leap second data", leap_tzname);
+ leap_src = SRC_TIMEZONE;
+ }
+}
+
+/* ================================================== */
+
+NTP_Leap
+LDB_GetLeap(time_t when, int *tai_offset)
+{
+ static time_t last_ldb_leap_check;
+ static NTP_Leap ldb_leap;
+ static int ldb_tai_offset;
+
+ /* Do this check at most twice a day */
+ when = when / (12 * 3600) * (12 * 3600);
+ if (last_ldb_leap_check == when)
+ goto out;
+
+ last_ldb_leap_check = when;
+ ldb_leap = LEAP_Normal;
+ ldb_tai_offset = 0;
+
+ switch (leap_src) {
+ case SRC_NONE:
+ break;
+ case SRC_TIMEZONE:
+ ldb_leap = get_tz_leap(when, &ldb_tai_offset);
+ break;
+ case SRC_LIST:
+ ldb_leap = get_list_leap(when, &ldb_tai_offset);
+ break;
+ }
+
+out:
+ *tai_offset = ldb_tai_offset;
+ return ldb_leap;
+}
+
+/* ================================================== */
+
+void
+LDB_Finalise(void)
+{
+ /* Nothing to do */
+}
diff --git a/leapdb.h b/leapdb.h
new file mode 100644
index 0000000..d4725b9
--- /dev/null
+++ b/leapdb.h
@@ -0,0 +1,37 @@
+/*
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Patrick Oppenlander 2024
+ *
+ * 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.
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ This module provides leap second information.
+
+ */
+
+#ifndef GOT_LEAPDB_H
+#define GOT_LEAPDB_H
+
+#include "ntp.h"
+
+extern void LDB_Initialise(void);
+extern NTP_Leap LDB_GetLeap(time_t when, int *tai_offset);
+extern void LDB_Finalise(void);
+
+#endif /* GOT_LEAPDB_H */
diff --git a/logging.c b/logging.c
index ec9ec53..732165e 100644
--- a/logging.c
+++ b/logging.c
@@ -185,7 +185,7 @@ void LOG_Message(LOG_Severity severity,
/* Send the message also to the foreground process if it is
still running, or stderr if it is still open */
if (parent_fd > 0) {
- if (write(parent_fd, buf, strlen(buf) + 1) < 0)
+ if (!LOG_NotifyParent(buf))
; /* Not much we can do here */
} else if (system_log && parent_fd == 0) {
system_log = 0;
@@ -291,6 +291,17 @@ LOG_SetParentFd(int fd)
/* ================================================== */
+int
+LOG_NotifyParent(const char *message)
+{
+ if (parent_fd <= 0)
+ return 1;
+
+ return write(parent_fd, message, strlen(message) + 1) > 0;
+}
+
+/* ================================================== */
+
void
LOG_CloseParentFd()
{
diff --git a/logging.h b/logging.h
index ff2e30e..4dfef0b 100644
--- a/logging.h
+++ b/logging.h
@@ -126,7 +126,10 @@ extern void LOG_OpenSystemLog(void);
/* Stop using stderr and send fatal message to the foreground process */
extern void LOG_SetParentFd(int fd);
-/* Close the pipe to the foreground process so it can exit */
+/* Send a message to the foreground process */
+extern int LOG_NotifyParent(const char *message);
+
+/* Close the pipe to the foreground process */
extern void LOG_CloseParentFd(void);
/* File logging functions */
diff --git a/main.c b/main.c
index 21d0fe7..88ada20 100644
--- a/main.c
+++ b/main.c
@@ -32,6 +32,7 @@
#include "main.h"
#include "sched.h"
+#include "leapdb.h"
#include "local.h"
#include "sys.h"
#include "ntp_io.h"
@@ -134,6 +135,7 @@ MAI_CleanupAndExit(void)
RCL_Finalise();
SRC_Finalise();
REF_Finalise();
+ LDB_Finalise();
RTC_Finalise();
SYS_Finalise();
@@ -213,7 +215,10 @@ post_init_ntp_hook(void *anything)
REF_SetMode(ref_mode);
}
- /* Close the pipe to the foreground process so it can exit */
+ /* Send an empty message to the foreground process so it can exit.
+ If that fails, indicating the process was killed, exit too. */
+ if (!LOG_NotifyParent(""))
+ SCH_QuitProgram();
LOG_CloseParentFd();
CNF_AddSources();
@@ -336,8 +341,8 @@ go_daemon(void)
close(pipefd[1]);
r = read(pipefd[0], message, sizeof (message));
- if (r) {
- if (r > 0) {
+ if (r != 1 || message[0] != '\0') {
+ if (r > 1) {
/* Print the error message from the child */
message[sizeof (message) - 1] = '\0';
fprintf(stderr, "%s\n", message);
@@ -655,6 +660,7 @@ int main
if (!geteuid())
LOG(LOGS_WARN, "Running with root privileges");
+ LDB_Initialise();
REF_Initialise();
SST_Initialise();
NSR_Initialise();
diff --git a/ntp_core.c b/ntp_core.c
index 023e60b..2965413 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-2023
+ * Copyright (C) Miroslav Lichvar 2009-2024
*
* 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
@@ -221,7 +221,7 @@ struct NCR_Instance_Record {
int burst_good_samples_to_go;
int burst_total_samples_to_go;
- /* Report from last valid response */
+ /* Report from last valid response and packet/timestamp statistics */
RPT_NTPReport report;
};
@@ -2211,7 +2211,8 @@ process_response(NCR_Instance inst, int saved, NTP_Local_Address *local_addr,
/* Test A combines multiple tests to avoid changing the measurements log
format and ntpdata report. It requires that the minimum estimate of the
peer delay is not larger than the configured maximum, it is not a
- response in the 'warm up' exchange, in both client modes that the server
+ response in the 'warm up' exchange, the configured offset correction is
+ within the supported NTP interval, both client modes that the server
processing time is sane, in interleaved client/server mode that the
previous response was not in basic mode (which prevents using timestamps
that minimise delay error), and in interleaved symmetric mode that the
@@ -2220,6 +2221,7 @@ process_response(NCR_Instance inst, int saved, NTP_Local_Address *local_addr,
testA = sample.peer_delay - sample.peer_dispersion <= inst->max_delay &&
precision <= inst->max_delay &&
inst->presend_done <= 0 &&
+ UTI_IsTimeOffsetSane(&sample.time, sample.offset) &&
!(inst->mode == MODE_CLIENT && response_time > MAX_SERVER_INTERVAL) &&
!(inst->mode == MODE_CLIENT && interleaved_packet &&
UTI_IsZeroTimespec(&inst->prev_local_tx.ts) &&
@@ -2372,13 +2374,17 @@ process_response(NCR_Instance inst, int saved, NTP_Local_Address *local_addr,
SRC_UpdateReachability(inst->source, synced_packet);
- if (synced_packet) {
- if (inst->copy && inst->remote_stratum > 0) {
- /* Assume the reference ID and stratum of the server */
+ if (inst->copy) {
+ /* Assume the reference ID and stratum of the server */
+ if (synced_packet && inst->remote_stratum > 0) {
inst->remote_stratum--;
SRC_SetRefid(inst->source, ntohl(message->reference_id), &inst->remote_addr.ip_addr);
+ } else {
+ SRC_ResetInstance(inst->source);
}
+ }
+ if (synced_packet) {
SRC_UpdateStatus(inst->source, MAX(inst->remote_stratum, inst->min_stratum), pkt_leap);
if (inst->delay_quant)
@@ -2530,6 +2536,10 @@ NCR_ProcessRxKnown(NCR_Instance inst, NTP_Local_Address *local_addr,
NTP_PacketInfo info;
inst->report.total_rx_count++;
+ if (rx_ts->source == NTP_TS_KERNEL)
+ inst->report.total_kernel_rx_ts++;
+ else if (rx_ts->source == NTP_TS_HARDWARE)
+ inst->report.total_hw_rx_ts++;
if (!parse_packet(message, length, &info))
return 0;
@@ -2652,6 +2662,7 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
NTP_Local_Timestamp local_tx, *tx_ts;
NTP_int64 ntp_rx, *local_ntp_rx;
int log_index, interleaved, poll, version;
+ CLG_Limit limit;
uint32_t kod;
/* Ignore the packet if it wasn't received by server socket */
@@ -2697,7 +2708,8 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
log_index = CLG_LogServiceAccess(CLG_NTP, &remote_addr->ip_addr, &rx_ts->ts);
/* Don't reply to all requests if the rate is excessive */
- if (log_index >= 0 && CLG_LimitServiceRate(CLG_NTP, log_index)) {
+ limit = log_index >= 0 ? CLG_LimitServiceRate(CLG_NTP, log_index) : CLG_PASS;
+ if (limit == CLG_DROP) {
DEBUG_LOG("NTP packet discarded to limit response rate");
return;
}
@@ -2711,6 +2723,13 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
return;
}
+ if (limit == CLG_KOD) {
+ /* Don't respond if there is a conflict with the NTS NAK */
+ if (kod != 0)
+ return;
+ kod = KOD_RATE;
+ }
+
local_ntp_rx = NULL;
tx_ts = NULL;
interleaved = 0;
@@ -2736,7 +2755,7 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
CLG_DisableNtpTimestamps(&ntp_rx);
}
- CLG_UpdateNtpStats(kod != 0 && info.auth.mode != NTP_AUTH_NONE &&
+ CLG_UpdateNtpStats(kod == 0 && info.auth.mode != NTP_AUTH_NONE &&
info.auth.mode != NTP_AUTH_MSSNTP,
rx_ts->source, interleaved ? tx_ts->source : NTP_TS_DAEMON);
@@ -2812,8 +2831,11 @@ NCR_ProcessTxKnown(NCR_Instance inst, NTP_Local_Address *local_addr,
message);
if (tx_ts->source == NTP_TS_HARDWARE) {
+ inst->report.total_hw_tx_ts++;
if (has_saved_response(inst))
process_saved_response(inst);
+ } else if (tx_ts->source == NTP_TS_KERNEL) {
+ inst->report.total_kernel_tx_ts++;
}
}
@@ -3018,6 +3040,16 @@ NCR_ModifyMinstratum(NCR_Instance inst, int new_min_stratum)
/* ================================================== */
void
+NCR_ModifyOffset(NCR_Instance inst, double new_offset)
+{
+ inst->offset_correction = new_offset;
+ LOG(LOGS_INFO, "Source %s new offset %f",
+ UTI_IPToString(&inst->remote_addr.ip_addr), new_offset);
+}
+
+/* ================================================== */
+
+void
NCR_ModifyPolltarget(NCR_Instance inst, int new_poll_target)
{
inst->poll_target = MAX(1, new_poll_target);
diff --git a/ntp_core.h b/ntp_core.h
index 5c5a614..67971ea 100644
--- a/ntp_core.h
+++ b/ntp_core.h
@@ -113,6 +113,8 @@ extern void NCR_ModifyMaxdelaydevratio(NCR_Instance inst, double new_max_delay_d
extern void NCR_ModifyMinstratum(NCR_Instance inst, int new_min_stratum);
+extern void NCR_ModifyOffset(NCR_Instance inst, double new_offset);
+
extern void NCR_ModifyPolltarget(NCR_Instance inst, int new_poll_target);
extern void NCR_InitiateSampleBurst(NCR_Instance inst, int n_good_samples, int n_total_samples);
diff --git a/ntp_io.c b/ntp_io.c
index ec2fd7b..7a034f7 100644
--- a/ntp_io.c
+++ b/ntp_io.c
@@ -513,9 +513,11 @@ NIO_UnwrapMessage(SCK_Message *message, int sock_fd, double *net_correction)
msg = message->data;
- if (msg->header.type != PTP_TYPE_DELAY_REQ || msg->header.version != PTP_VERSION ||
+ if ((msg->header.type != PTP_TYPE_DELAY_REQ && msg->header.type != PTP_TYPE_SYNC) ||
+ (msg->header.version != PTP_VERSION_2 &&
+ (msg->header.version != PTP_VERSION_2_1 || msg->header.min_sdoid != 0)) ||
ntohs(msg->header.length) != message->length ||
- msg->header.domain != PTP_DOMAIN_NTP ||
+ msg->header.domain != CNF_GetPtpDomain() ||
ntohs(msg->header.flags) != PTP_FLAG_UNICAST ||
ntohs(msg->tlv_header.type) != PTP_TLV_NTP ||
ntohs(msg->tlv_header.length) != message->length - PTP_NTP_PREFIX_LENGTH) {
@@ -561,9 +563,9 @@ wrap_message(SCK_Message *message, int sock_fd)
memset(ptp_message, 0, PTP_NTP_PREFIX_LENGTH);
ptp_message->header.type = PTP_TYPE_DELAY_REQ;
- ptp_message->header.version = PTP_VERSION;
+ ptp_message->header.version = PTP_VERSION_2;
ptp_message->header.length = htons(PTP_NTP_PREFIX_LENGTH + message->length);
- ptp_message->header.domain = PTP_DOMAIN_NTP;
+ ptp_message->header.domain = CNF_GetPtpDomain();
ptp_message->header.flags = htons(PTP_FLAG_UNICAST);
ptp_message->header.sequence_id = htons(sequence_id++);
ptp_message->tlv_header.type = htons(PTP_TLV_NTP);
diff --git a/ntp_signd.c b/ntp_signd.c
index 77b3249..e52c8f5 100644
--- a/ntp_signd.c
+++ b/ntp_signd.c
@@ -99,6 +99,9 @@ static int sock_fd;
/* Flag indicating if the MS-SNTP authentication is enabled */
static int enabled;
+/* Flag limiting logging of connection error messages */
+static int logged_connection_error;
+
/* ================================================== */
static void read_write_socket(int sock_fd, int event, void *anything);
@@ -134,6 +137,14 @@ open_socket(void)
sock_fd = SCK_OpenUnixStreamSocket(path, NULL, 0);
if (sock_fd < 0) {
sock_fd = INVALID_SOCK_FD;
+
+ /* Log an error only once before a successful exchange to avoid
+ flooding the system log */
+ if (!logged_connection_error) {
+ LOG(LOGS_ERR, "Could not connect to signd socket %s : %s", path, strerror(errno));
+ logged_connection_error = 1;
+ }
+
return 0;
}
@@ -160,6 +171,8 @@ process_response(SignInstance *inst)
return;
}
+ logged_connection_error = 0;
+
/* Check if the file descriptor is still valid */
if (!NIO_IsServerSocket(inst->local_addr.sock_fd)) {
DEBUG_LOG("Invalid NTP socket");
diff --git a/ntp_sources.c b/ntp_sources.c
index d8bd2d8..f117e33 100644
--- a/ntp_sources.c
+++ b/ntp_sources.c
@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
- * Copyright (C) Miroslav Lichvar 2011-2012, 2014, 2016, 2020-2023
+ * Copyright (C) Miroslav Lichvar 2011-2012, 2014, 2016, 2020-2024
*
* 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
@@ -61,6 +61,8 @@ typedef struct {
(may be an IP address) */
IPAddr resolved_addr; /* Address resolved from the name, which can be
different from remote_addr (e.g. NTS-KE) */
+ int family; /* IP family of acceptable resolved addresses
+ (IPADDR_UNSPEC if any) */
int pool_id; /* ID of the pool from which was this source
added or INVALID_POOL */
int tentative; /* Flag indicating there was no valid response
@@ -98,6 +100,8 @@ struct UnresolvedSource {
int pool_id;
/* Name to be resolved */
char *name;
+ /* Address family to filter resolved addresses */
+ int family;
/* Flag indicating addresses should be used in a random order */
int random_order;
/* Flag indicating current address should be replaced only if it is
@@ -215,8 +219,14 @@ NSR_Finalise(void)
ARR_DestroyInstance(pools);
SCH_RemoveTimeout(resolving_id);
- while (unresolved_sources)
- remove_unresolved_source(unresolved_sources);
+
+ /* Leave the unresolved sources allocated if the async resolver is running
+ to avoid reading the name from freed memory. The handler will not be
+ called as the scheduler should no longer be running at this point. */
+ if (!resolving_source) {
+ while (unresolved_sources)
+ remove_unresolved_source(unresolved_sources);
+ }
initialised = 0;
}
@@ -353,7 +363,7 @@ log_source(SourceRecord *record, int addition, int once_per_pool)
/* Procedure to add a new source */
static NSR_Status
-add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type,
+add_source(NTP_Remote_Address *remote_addr, char *name, int family, NTP_Source_Type type,
SourceParameters *params, int pool_id, uint32_t conf_id)
{
SourceRecord *record;
@@ -391,6 +401,7 @@ add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type,
record->data = NCR_CreateInstance(remote_addr, type, params, record->name);
record->remote_addr = NCR_GetRemoteAddress(record->data);
record->resolved_addr = remote_addr->ip_addr;
+ record->family = family;
record->pool_id = pool_id;
record->tentative = 1;
record->conf_id = conf_id;
@@ -552,6 +563,10 @@ process_resolved_name(struct UnresolvedSource *us, IPAddr *ip_addrs, int n_addrs
DEBUG_LOG("(%d) %s", i + 1, UTI_IPToString(&new_addr.ip_addr));
+ /* Skip addresses not from the requested family */
+ if (us->family != IPADDR_UNSPEC && us->family != new_addr.ip_addr.family)
+ continue;
+
if (us->pool_id != INVALID_POOL) {
/* In the pool resolving mode, try to replace a source from
the pool which does not have a real address yet */
@@ -629,13 +644,16 @@ name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *any
next = us->next;
/* Don't repeat the resolving if it (permanently) failed, it was a
- replacement of a real address, or all addresses are already resolved */
- if (status == DNS_Failure || UTI_IsIPReal(&us->address.ip_addr) || is_resolved(us))
+ replacement of a real address, a refreshment, or all addresses are
+ already resolved */
+ if (status == DNS_Failure || UTI_IsIPReal(&us->address.ip_addr) ||
+ us->refreshment || is_resolved(us))
remove_unresolved_source(us);
/* If a restart was requested and this was the last source in the list,
start with the first source again (if there still is one) */
if (!next && resolving_restart) {
+ DEBUG_LOG("Restarting");
next = unresolved_sources;
resolving_restart = 0;
}
@@ -700,11 +718,15 @@ static void
append_unresolved_source(struct UnresolvedSource *us)
{
struct UnresolvedSource **i;
+ int n;
- for (i = &unresolved_sources; *i; i = &(*i)->next)
+ for (i = &unresolved_sources, n = 0; *i; i = &(*i)->next, n++)
;
*i = us;
us->next = NULL;
+
+ DEBUG_LOG("Added unresolved source #%d pool_id=%d random=%d refresh=%d",
+ n + 1, us->pool_id, us->random_order, us->refreshment);
}
/* ================================================== */
@@ -754,8 +776,19 @@ static int get_unused_pool_id(void)
static uint32_t
get_next_conf_id(uint32_t *conf_id)
{
+ SourceRecord *record;
+ unsigned int i;
+
+again:
last_conf_id++;
+ /* Make sure the ID is not already used (after 32-bit wraparound) */
+ for (i = 0; i < ARR_GetSize(records); i++) {
+ record = get_record(i);
+ if (record->remote_addr && record->conf_id == last_conf_id)
+ goto again;
+ }
+
if (conf_id)
*conf_id = last_conf_id;
@@ -768,14 +801,14 @@ NSR_Status
NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
SourceParameters *params, uint32_t *conf_id)
{
- return add_source(remote_addr, NULL, type, params, INVALID_POOL,
+ return add_source(remote_addr, NULL, IPADDR_UNSPEC, type, params, INVALID_POOL,
get_next_conf_id(conf_id));
}
/* ================================================== */
NSR_Status
-NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
+NSR_AddSourceByName(char *name, int family, int port, int pool, NTP_Source_Type type,
SourceParameters *params, uint32_t *conf_id)
{
struct UnresolvedSource *us;
@@ -787,7 +820,9 @@ NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
/* If the name is an IP address, add the source with the address directly */
if (UTI_StringToIP(name, &remote_addr.ip_addr)) {
remote_addr.port = port;
- return add_source(&remote_addr, name, type, params, INVALID_POOL,
+ if (family != IPADDR_UNSPEC && family != remote_addr.ip_addr.family)
+ return NSR_InvalidAF;
+ return add_source(&remote_addr, name, IPADDR_UNSPEC, type, params, INVALID_POOL,
get_next_conf_id(conf_id));
}
@@ -799,6 +834,7 @@ NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
us = MallocNew(struct UnresolvedSource);
us->name = Strdup(name);
+ us->family = family;
us->random_order = 0;
us->refreshment = 0;
@@ -835,7 +871,7 @@ NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
for (i = 0; i < new_sources; i++) {
if (i > 0)
remote_addr.ip_addr.addr.id = ++last_address_id;
- if (add_source(&remote_addr, name, type, params, us->pool_id, cid) != NSR_Success)
+ if (add_source(&remote_addr, name, family, type, params, us->pool_id, cid) != NSR_Success)
return NSR_TooManySources;
}
@@ -1026,6 +1062,7 @@ resolve_source_replacement(SourceRecord *record, int refreshment)
us = MallocNew(struct UnresolvedSource);
us->name = Strdup(record->name);
+ us->family = record->family;
/* 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
@@ -1036,7 +1073,10 @@ resolve_source_replacement(SourceRecord *record, int refreshment)
us->address = *record->remote_addr;
append_unresolved_source(us);
- NSR_ResolveSources();
+
+ /* Don't restart resolving round if already running */
+ if (!resolving_source)
+ NSR_ResolveSources();
}
/* ================================================== */
@@ -1432,6 +1472,20 @@ NSR_ModifyMinstratum(IPAddr *address, int new_min_stratum)
/* ================================================== */
int
+NSR_ModifyOffset(IPAddr *address, double new_offset)
+{
+ int slot;
+
+ if (!find_slot(address, &slot))
+ return 0;
+
+ NCR_ModifyOffset(get_record(slot)->data, new_offset);
+ return 1;
+}
+
+/* ================================================== */
+
+int
NSR_ModifyPolltarget(IPAddr *address, int new_poll_target)
{
int slot;
diff --git a/ntp_sources.h b/ntp_sources.h
index 5aeb1a3..89a322e 100644
--- a/ntp_sources.h
+++ b/ntp_sources.h
@@ -55,9 +55,12 @@ extern NSR_Status NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type
/* Procedure to add a new server, peer source, or pool of servers specified by
name instead of address. The name is resolved in exponentially increasing
- intervals until it succeeds or fails with a non-temporary error. If the
- name is an address, it is equivalent to NSR_AddSource(). */
-extern NSR_Status NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
+ intervals until it succeeds or fails with a non-temporary error. The
+ specified family filters resolved addresses. If the name is an address
+ and its family does not conflict with the specified family, it is equivalent
+ to NSR_AddSource(). */
+extern NSR_Status NSR_AddSourceByName(char *name, int family, int port, int pool,
+ NTP_Source_Type type,
SourceParameters *params, uint32_t *conf_id);
extern const char *NSR_StatusToString(NSR_Status status);
@@ -137,6 +140,8 @@ extern int NSR_ModifyMaxdelaydevratio(IPAddr *address, double new_max_delay_rati
extern int NSR_ModifyMinstratum(IPAddr *address, int new_min_stratum);
+extern int NSR_ModifyOffset(IPAddr *address, double new_offset);
+
extern int NSR_ModifyPolltarget(IPAddr *address, int new_poll_target);
extern int NSR_InitiateSampleBurst(int n_good_samples, int n_total_samples, IPAddr *mask, IPAddr *address);
diff --git a/nts_ke_server.c b/nts_ke_server.c
index 3fe99db..6d617b4 100644
--- a/nts_ke_server.c
+++ b/nts_ke_server.c
@@ -242,7 +242,7 @@ accept_connection(int listening_fd, int event, void *arg)
SCH_GetLastEventTime(&now, NULL, NULL);
log_index = CLG_LogServiceAccess(CLG_NTSKE, &addr.ip_addr, &now);
- if (log_index >= 0 && CLG_LimitServiceRate(CLG_NTSKE, log_index)) {
+ if (log_index >= 0 && CLG_LimitServiceRate(CLG_NTSKE, log_index) != CLG_PASS) {
DEBUG_LOG("Rejected connection from %s (%s)",
UTI_IPSockAddrToString(&addr), "rate limit");
SCK_CloseSocket(sock_fd);
diff --git a/nts_ntp_server.c b/nts_ntp_server.c
index be69a2b..5d46c29 100644
--- a/nts_ntp_server.c
+++ b/nts_ntp_server.c
@@ -279,7 +279,7 @@ NNS_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *req_info,
}
/* NTS NAK response does not have any other fields */
- if (kod)
+ if (kod == NTP_KOD_NTS_NAK)
return 1;
for (i = 0, plaintext_length = 0; i < server->num_cookies; i++) {
diff --git a/pktlength.c b/pktlength.c
index d7ed272..8d5ffa7 100644
--- a/pktlength.c
+++ b/pktlength.c
@@ -111,7 +111,7 @@ static const struct request_length request_lengths[] = {
REQ_LENGTH_ENTRY(null, null), /* REFRESH */
REQ_LENGTH_ENTRY(null, server_stats), /* SERVER_STATS */
{ 0, 0 }, /* CLIENT_ACCESSES_BY_INDEX2 - not supported */
- REQ_LENGTH_ENTRY(local, null), /* LOCAL2 */
+ { 0, 0 }, /* LOCAL2 - not supported */
REQ_LENGTH_ENTRY(ntp_data, ntp_data), /* NTP_DATA */
{ 0, 0 }, /* ADD_SERVER2 */
{ 0, 0 }, /* ADD_PEER2 */
@@ -130,6 +130,8 @@ static const struct request_length request_lengths[] = {
REQ_LENGTH_ENTRY(null, null), /* RELOAD_SOURCES */
REQ_LENGTH_ENTRY(doffset, null), /* DOFFSET2 */
REQ_LENGTH_ENTRY(modify_select_opts, null), /* MODIFY_SELECTOPTS */
+ REQ_LENGTH_ENTRY(modify_offset, null), /* MODIFY_OFFSET */
+ REQ_LENGTH_ENTRY(local, null), /* LOCAL3 */
};
static const uint16_t reply_lengths[] = {
@@ -149,7 +151,7 @@ static const uint16_t reply_lengths[] = {
RPY_LENGTH_ENTRY(smoothing), /* SMOOTHING */
0, /* SERVER_STATS - not supported */
0, /* CLIENT_ACCESSES_BY_INDEX2 - not supported */
- RPY_LENGTH_ENTRY(ntp_data), /* NTP_DATA */
+ 0, /* NTP_DATA - not supported */
RPY_LENGTH_ENTRY(manual_timestamp), /* MANUAL_TIMESTAMP2 */
RPY_LENGTH_ENTRY(manual_list), /* MANUAL_LIST2 */
RPY_LENGTH_ENTRY(ntp_source_name), /* NTP_SOURCE_NAME */
@@ -159,6 +161,7 @@ static const uint16_t reply_lengths[] = {
RPY_LENGTH_ENTRY(select_data), /* SELECT_DATA */
0, /* SERVER_STATS3 - not supported */
RPY_LENGTH_ENTRY(server_stats), /* SERVER_STATS4 */
+ RPY_LENGTH_ENTRY(ntp_data), /* NTP_DATA2 */
};
/* ================================================== */
diff --git a/ptp.h b/ptp.h
index 8034a2c..8bf639a 100644
--- a/ptp.h
+++ b/ptp.h
@@ -31,9 +31,10 @@
#include "ntp.h"
-#define PTP_VERSION 2
+#define PTP_VERSION_2 2
+#define PTP_VERSION_2_1 (2 | 1 << 4)
+#define PTP_TYPE_SYNC 0
#define PTP_TYPE_DELAY_REQ 1
-#define PTP_DOMAIN_NTP 123
#define PTP_FLAG_UNICAST (1 << (2 + 8))
#define PTP_TLV_NTP 0x2023
diff --git a/refclock.c b/refclock.c
index 84f7439..22d775a 100644
--- a/refclock.c
+++ b/refclock.c
@@ -166,8 +166,8 @@ RCL_AddRefclock(RefclockParameters *params)
if (!inst->driver->init && !inst->driver->poll)
LOG_FATAL("refclock driver %s is not compiled in", params->driver_name);
- if (params->tai && !CNF_GetLeapSecTimezone())
- LOG_FATAL("refclock tai option requires leapsectz");
+ if (params->tai && !CNF_GetLeapSecList() && !CNF_GetLeapSecTimezone())
+ LOG_FATAL("refclock tai option requires leapseclist or leapsectz");
inst->data = NULL;
inst->driver_parameter = Strdup(params->driver_parameter);
@@ -321,6 +321,22 @@ RCL_ReportSource(RPT_SourceReport *report, struct timespec *now)
}
}
+int
+RCL_ModifyOffset(uint32_t ref_id, double offset)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARR_GetSize(refclocks); i++) {
+ RCL_Instance inst = get_refclock(i);
+ if (inst->ref_id == ref_id) {
+ inst->offset = offset;
+ LOG(LOGS_INFO, "Source %s new offset %f", UTI_RefidToString(ref_id), offset);
+ return 1;
+ }
+ }
+ return 0;
+}
+
void
RCL_SetDriverData(RCL_Instance instance, void *data)
{
diff --git a/refclock.h b/refclock.h
index 7947b62..40c852d 100644
--- a/refclock.h
+++ b/refclock.h
@@ -68,6 +68,7 @@ extern void RCL_Finalise(void);
extern int RCL_AddRefclock(RefclockParameters *params);
extern void RCL_StartRefclocks(void);
extern void RCL_ReportSource(RPT_SourceReport *report, struct timespec *now);
+extern int RCL_ModifyOffset(uint32_t ref_id, double offset);
/* functions used by drivers */
extern void RCL_SetDriverData(RCL_Instance instance, void *data);
diff --git a/refclock_phc.c b/refclock_phc.c
index abda2e8..e12f225 100644
--- a/refclock_phc.c
+++ b/refclock_phc.c
@@ -175,7 +175,7 @@ static void read_ext_pulse(int fd, int event, void *anything)
instance = anything;
phc1 = RCL_GetDriverData(instance);
- /* The Linux kernel (as of 6.2) has one shared queue of timestamps for all
+ /* Linux versions before 6.7 had one shared queue of timestamps for all
descriptors of the same PHC. Search for all refclocks that expect
the timestamp. */
diff --git a/reference.c b/reference.c
index 97dfbe9..bb2c48a 100644
--- a/reference.c
+++ b/reference.c
@@ -4,6 +4,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2018, 2020, 2022
+ * Copyright (C) Andy Fiddaman 2024
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -33,6 +34,7 @@
#include "reference.h"
#include "util.h"
#include "conf.h"
+#include "leapdb.h"
#include "logging.h"
#include "local.h"
#include "sched.h"
@@ -53,6 +55,8 @@ static int enable_local_stratum;
static int local_stratum;
static int local_orphan;
static double local_distance;
+static int local_activate_ok;
+static double local_activate;
static struct timespec local_ref_time;
static NTP_Leap our_leap_status;
static int our_leap_sec;
@@ -122,9 +126,6 @@ static int leap_in_progress;
/* Timer for the leap second handler */
static SCH_TimeoutID leap_timeout_id;
-/* Name of a system timezone containing leap seconds occuring at midnight */
-static char *leap_tzname;
-
/* ================================================== */
static LOG_FileID logfileid;
@@ -155,7 +156,6 @@ static int ref_adjustments;
/* ================================================== */
-static NTP_Leap get_tz_leap(time_t when, int *tai_offset);
static void update_leap_status(NTP_Leap leap, time_t now, int reset);
/* ================================================== */
@@ -195,7 +195,6 @@ REF_Initialise(void)
FILE *in;
double file_freq_ppm, file_skew_ppm;
double our_frequency_ppm;
- int tai_offset;
mode = REF_ModeNormal;
are_we_synchronised = 0;
@@ -211,6 +210,7 @@ REF_Initialise(void)
our_frequency_sd = 0.0;
our_offset_sd = 0.0;
drift_file_age = 0.0;
+ local_activate_ok = 0;
/* Now see if we can get the drift file opened */
drift_file = CNF_GetDriftFile();
@@ -249,7 +249,8 @@ REF_Initialise(void)
correction_time_ratio = CNF_GetCorrectionTimeRatio();
- enable_local_stratum = CNF_AllowLocalReference(&local_stratum, &local_orphan, &local_distance);
+ enable_local_stratum = CNF_AllowLocalReference(&local_stratum, &local_orphan,
+ &local_distance, &local_activate);
UTI_ZeroTimespec(&local_ref_time);
leap_when = 0;
@@ -260,18 +261,6 @@ REF_Initialise(void)
if (leap_mode == REF_LeapModeSystem && !LCL_CanSystemLeap())
leap_mode = REF_LeapModeStep;
- leap_tzname = CNF_GetLeapSecTimezone();
- if (leap_tzname) {
- /* Check that the timezone has good data for Jun 30 2012 and Dec 31 2012 */
- if (get_tz_leap(1341014400, &tai_offset) == LEAP_InsertSecond && tai_offset == 34 &&
- get_tz_leap(1356912000, &tai_offset) == LEAP_Normal && tai_offset == 35) {
- LOG(LOGS_INFO, "Using %s timezone to obtain leap second data", leap_tzname);
- } else {
- LOG(LOGS_WARN, "Timezone %s failed leap second check, ignoring", leap_tzname);
- leap_tzname = NULL;
- }
- }
-
CNF_GetMakeStep(&make_step_limit, &make_step_threshold);
CNF_GetMaxChange(&max_offset_delay, &max_offset_ignore, &max_offset);
CNF_GetMailOnChange(&do_mail_change, &mail_change_threshold, &mail_change_user);
@@ -593,77 +582,6 @@ is_leap_second_day(time_t when)
/* ================================================== */
-static NTP_Leap
-get_tz_leap(time_t when, int *tai_offset)
-{
- static time_t last_tz_leap_check;
- static NTP_Leap tz_leap;
- static int tz_tai_offset;
-
- struct tm stm, *tm;
- time_t t;
- char *tz_env, tz_orig[128];
-
- *tai_offset = tz_tai_offset;
-
- /* Do this check at most twice a day */
- when = when / (12 * 3600) * (12 * 3600);
- if (last_tz_leap_check == when)
- return tz_leap;
-
- last_tz_leap_check = when;
- tz_leap = LEAP_Normal;
- tz_tai_offset = 0;
-
- tm = gmtime(&when);
- if (!tm)
- return tz_leap;
-
- stm = *tm;
-
- /* Temporarily switch to the timezone containing leap seconds */
- tz_env = getenv("TZ");
- if (tz_env) {
- if (strlen(tz_env) >= sizeof (tz_orig))
- return tz_leap;
- strcpy(tz_orig, tz_env);
- }
- setenv("TZ", leap_tzname, 1);
- tzset();
-
- /* Get the TAI-UTC offset, which started at the epoch at 10 seconds */
- t = mktime(&stm);
- if (t != -1)
- tz_tai_offset = t - when + 10;
-
- /* Set the time to 23:59:60 and see how it overflows in mktime() */
- stm.tm_sec = 60;
- stm.tm_min = 59;
- stm.tm_hour = 23;
-
- t = mktime(&stm);
-
- if (tz_env)
- setenv("TZ", tz_orig, 1);
- else
- unsetenv("TZ");
- tzset();
-
- if (t == -1)
- return tz_leap;
-
- if (stm.tm_sec == 60)
- tz_leap = LEAP_InsertSecond;
- else if (stm.tm_sec == 1)
- tz_leap = LEAP_DeleteSecond;
-
- *tai_offset = tz_tai_offset;
-
- return tz_leap;
-}
-
-/* ================================================== */
-
static void
leap_end_timeout(void *arg)
{
@@ -751,16 +669,16 @@ set_leap_timeout(time_t now)
static void
update_leap_status(NTP_Leap leap, time_t now, int reset)
{
- NTP_Leap tz_leap;
+ NTP_Leap ldb_leap;
int leap_sec, tai_offset;
leap_sec = 0;
tai_offset = 0;
- if (leap_tzname && now) {
- tz_leap = get_tz_leap(now, &tai_offset);
+ if (now) {
+ ldb_leap = LDB_GetLeap(now, &tai_offset);
if (leap == LEAP_Normal)
- leap = tz_leap;
+ leap = ldb_leap;
}
if (leap == LEAP_InsertSecond || leap == LEAP_DeleteSecond) {
@@ -1219,7 +1137,7 @@ REF_GetReferenceParams
double *root_dispersion
)
{
- double dispersion, delta;
+ double dispersion, delta, distance;
assert(initialised);
@@ -1229,11 +1147,16 @@ REF_GetReferenceParams
dispersion = 0.0;
}
+ distance = our_root_delay / 2 + dispersion;
+
+ if (local_activate == 0.0 || (are_we_synchronised && distance < local_activate))
+ local_activate_ok = 1;
+
/* Local reference is active when enabled and the clock is not synchronised
or the root distance exceeds the threshold */
if (are_we_synchronised &&
- !(enable_local_stratum && our_root_delay / 2 + dispersion > local_distance)) {
+ !(enable_local_stratum && local_activate_ok && distance > local_distance)) {
*is_synchronised = 1;
@@ -1245,7 +1168,7 @@ REF_GetReferenceParams
*root_delay = our_root_delay;
*root_dispersion = dispersion;
- } else if (enable_local_stratum) {
+ } else if (enable_local_stratum && local_activate_ok) {
*is_synchronised = 0;
@@ -1345,12 +1268,13 @@ REF_ModifyMakestep(int limit, double threshold)
/* ================================================== */
void
-REF_EnableLocal(int stratum, double distance, int orphan)
+REF_EnableLocal(int stratum, double distance, int orphan, double activate)
{
enable_local_stratum = 1;
local_stratum = CLAMP(1, stratum, NTP_MAX_STRATUM - 1);
local_distance = distance;
local_orphan = !!orphan;
+ local_activate = activate;
LOG(LOGS_INFO, "%s local reference mode", "Enabled");
}
@@ -1368,7 +1292,7 @@ REF_DisableLocal(void)
#define LEAP_SECOND_CLOSE 5
static int
-is_leap_close(time_t t)
+is_leap_close(double t)
{
return leap_when != 0 &&
t >= leap_when - LEAP_SECOND_CLOSE && t < leap_when + LEAP_SECOND_CLOSE;
@@ -1398,7 +1322,7 @@ REF_GetTaiOffset(struct timespec *ts)
{
int tai_offset;
- get_tz_leap(ts->tv_sec, &tai_offset);
+ LDB_GetLeap(ts->tv_sec, &tai_offset);
return tai_offset;
}
diff --git a/reference.h b/reference.h
index 73454d4..2eddcae 100644
--- a/reference.h
+++ b/reference.h
@@ -185,7 +185,7 @@ extern void REF_ModifyMaxupdateskew(double new_max_update_skew);
/* Modify makestep settings */
extern void REF_ModifyMakestep(int limit, double threshold);
-extern void REF_EnableLocal(int stratum, double distance, int orphan);
+extern void REF_EnableLocal(int stratum, double distance, int orphan, double activate);
extern void REF_DisableLocal(void);
/* Check if either of the current raw and cooked time, and optionally a
diff --git a/regress.c b/regress.c
index e767e2f..1c86457 100644
--- a/regress.c
+++ b/regress.c
@@ -377,7 +377,7 @@ find_ordered_entry_with_flags(double *x, int n, int index, char *flags)
r = v;
do {
while (l < v && x[l] < piv) l++;
- while (x[r] > piv) r--;
+ while (r > 0 && x[r] > piv) r--;
if (r <= l) break;
EXCH(x[l], x[r]);
l++;
diff --git a/reports.h b/reports.h
index 0150fc6..0f4751d 100644
--- a/reports.h
+++ b/reports.h
@@ -181,6 +181,10 @@ typedef struct {
uint32_t total_rx_count;
uint32_t total_valid_count;
uint32_t total_good_count;
+ uint32_t total_kernel_tx_ts;
+ uint32_t total_kernel_rx_ts;
+ uint32_t total_hw_tx_ts;
+ uint32_t total_hw_rx_ts;
} RPT_NTPReport;
typedef struct {
diff --git a/rtc_linux.c b/rtc_linux.c
index 58b625b..8c42840 100644
--- a/rtc_linux.c
+++ b/rtc_linux.c
@@ -802,6 +802,7 @@ read_from_device(int fd_, int event, void *any)
rtc_tm.tm_mday = rtc_raw.tm_mday;
rtc_tm.tm_mon = rtc_raw.tm_mon;
rtc_tm.tm_year = rtc_raw.tm_year;
+ rtc_tm.tm_wday = 0;
rtc_t = t_from_rtc(&rtc_tm);
diff --git a/sources.c b/sources.c
index e7ec4b8..e292e4d 100644
--- a/sources.c
+++ b/sources.c
@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
- * Copyright (C) Miroslav Lichvar 2011-2016, 2018, 2020-2023
+ * Copyright (C) Miroslav Lichvar 2011-2016, 2018, 2020-2024
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -68,8 +68,8 @@ struct SelectInfo {
typedef enum {
SRC_OK, /* OK so far, not a final status! */
SRC_UNSELECTABLE, /* Has noselect option set */
- SRC_UNSYNCHRONISED, /* Provides samples but not unsynchronised */
SRC_BAD_STATS, /* Doesn't have valid stats data */
+ SRC_UNSYNCHRONISED, /* Provides samples, but not synchronised */
SRC_BAD_DISTANCE, /* Has root distance longer than allowed maximum */
SRC_JITTERY, /* Had std dev larger than allowed maximum */
SRC_WAITS_STATS, /* Others have bad stats, selection postponed */
@@ -177,6 +177,8 @@ static int reported_no_majority; /* Flag to avoid repeated log message
static int report_selection_loss; /* Flag to force logging a message if
selection is lost in a transient state
(SRC_WAITS_STATS, SRC_WAITS_UPDATE) */
+static int forced_first_report; /* Flag to allow one failed selection to be
+ logged before a successful selection */
/* Score needed to replace the currently selected source */
#define SCORE_LIMIT 10.0
@@ -524,11 +526,6 @@ SRC_UpdateReachability(SRC_Instance inst, int reachable)
if (inst->reachability_size < SOURCE_REACH_BITS)
inst->reachability_size++;
- if (!reachable && inst->index == selected_source_index) {
- /* Try to select a better source */
- SRC_SelectSource(NULL);
- }
-
/* Check if special reference update mode failed */
if (REF_GetMode() != REF_ModeNormal && special_mode_end()) {
REF_SetUnsynchronised();
@@ -537,6 +534,10 @@ SRC_UpdateReachability(SRC_Instance inst, int reachable)
/* Try to replace unreachable NTP sources */
if (inst->reachability == 0 && inst->reachability_size == SOURCE_REACH_BITS)
handle_bad_source(inst);
+
+ /* Source selection can change with unreachable sources */
+ if (inst->reachability == 0)
+ SRC_SelectSource(NULL);
}
/* ================================================== */
@@ -862,7 +863,8 @@ 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, sel_req_source;
- int n_badstats_sources, max_sel_reach, max_sel_reach_size, max_badstat_reach;
+ int max_badstat_reach, max_badstat_reach_size, n_badstats_sources;
+ int max_sel_reach, max_sel_reach_size;
int depth, best_depth, trust_depth, best_trust_depth, n_sel_trust_sources;
int combined, stratum, min_stratum, max_score_index;
int orphan_stratum, orphan_source;
@@ -893,7 +895,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_sel_reach_size = max_badstat_reach_size = 0;
max_reach_sample_ago = 0.0;
for (i = 0; i < n_sources; i++) {
@@ -913,12 +915,6 @@ SRC_SelectSource(SRC_Instance updated_inst)
continue;
}
- /* Ignore sources which are not synchronised */
- if (sources[i]->leap == LEAP_Unsynchronised) {
- mark_source(sources[i], SRC_UNSYNCHRONISED);
- continue;
- }
-
si = &sources[i]->sel_info;
SST_GetSelectionData(sources[i]->stats, &now,
&si->lo_limit, &si->hi_limit, &si->root_distance,
@@ -930,6 +926,14 @@ SRC_SelectSource(SRC_Instance updated_inst)
mark_source(sources[i], SRC_BAD_STATS);
if (max_badstat_reach < sources[i]->reachability)
max_badstat_reach = sources[i]->reachability;
+ if (max_badstat_reach_size < sources[i]->reachability_size)
+ max_badstat_reach_size = sources[i]->reachability_size;
+ continue;
+ }
+
+ /* Ignore sources which are not synchronised */
+ if (sources[i]->leap == LEAP_Unsynchronised) {
+ mark_source(sources[i], SRC_UNSYNCHRONISED);
continue;
}
@@ -1068,6 +1072,14 @@ SRC_SelectSource(SRC_Instance updated_inst)
return;
}
+ /* Wait for a source to have full reachability register to allow one
+ failed selection to be logged before first successful selection */
+ if (!forced_first_report &&
+ MAX(max_sel_reach_size, max_badstat_reach_size) == SOURCE_REACH_BITS) {
+ report_selection_loss = 1;
+ forced_first_report = 1;
+ }
+
if (n_endpoints == 0) {
/* No sources provided valid endpoints */
unselect_selected_source(LOGS_INFO, "Can't synchronise: no selectable sources", NULL);
@@ -1334,6 +1346,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
reported_no_majority = 0;
report_selection_loss = 0;
+ forced_first_report = 1;
}
mark_source(sources[selected_source_index], SRC_SELECTED);
@@ -1796,10 +1809,10 @@ get_status_char(SRC_Status status)
switch (status) {
case SRC_UNSELECTABLE:
return 'N';
- case SRC_UNSYNCHRONISED:
- return 's';
case SRC_BAD_STATS:
return 'M';
+ case SRC_UNSYNCHRONISED:
+ return 's';
case SRC_BAD_DISTANCE:
return 'd';
case SRC_JITTERY:
diff --git a/sourcestats.c b/sourcestats.c
index ce326e9..fcc501a 100644
--- a/sourcestats.c
+++ b/sourcestats.c
@@ -549,9 +549,9 @@ SST_DoNewRegression(SST_Stats inst)
sd_weight += (peer_distances[i] - min_distance) / sd;
weights[i] = SQUARE(sd_weight);
}
- }
- correct_asymmetry(inst, times_back, offsets);
+ correct_asymmetry(inst, times_back, offsets);
+ }
inst->regression_ok = RGR_FindBestRegression(times_back + inst->runs_samples,
offsets + inst->runs_samples, weights,
diff --git a/stubs.c b/stubs.c
index b729c2f..9d0f848 100644
--- a/stubs.c
+++ b/stubs.c
@@ -201,7 +201,7 @@ NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
}
NSR_Status
-NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
+NSR_AddSourceByName(char *name, int family, int port, int pool, NTP_Source_Type type,
SourceParameters *params, uint32_t *conf_id)
{
return NSR_TooManySources;
@@ -321,6 +321,12 @@ NSR_ModifyMinstratum(IPAddr *address, int new_min_stratum)
}
int
+NSR_ModifyOffset(IPAddr *address, double new_offset)
+{
+ return 0;
+}
+
+int
NSR_ModifyPolltarget(IPAddr *address, int new_poll_target)
{
return 0;
@@ -419,6 +425,12 @@ RCL_ReportSource(RPT_SourceReport *report, struct timespec *now)
memset(report, 0, sizeof (*report));
}
+int
+RCL_ModifyOffset(uint32_t ref_id, double offset)
+{
+ return 0;
+}
+
#endif /* !FEAT_REFCLOCK */
#ifndef FEAT_SIGND
diff --git a/sys_linux.c b/sys_linux.c
index 6849637..5fe9c0e 100644
--- a/sys_linux.c
+++ b/sys_linux.c
@@ -990,6 +990,14 @@ SYS_Linux_SetPHCExtTimestamping(int fd, int pin, int channel,
return 0;
}
+#if defined(PTP_MASK_CLEAR_ALL) && defined(PTP_MASK_EN_SINGLE)
+ /* Disable events from other channels on this descriptor */
+ if (ioctl(fd, PTP_MASK_CLEAR_ALL))
+ DEBUG_LOG("ioctl(%s) failed : %s", "PTP_MASK_CLEAR_ALL", strerror(errno));
+ else if (ioctl(fd, PTP_MASK_EN_SINGLE, &channel))
+ DEBUG_LOG("ioctl(%s) failed : %s", "PTP_MASK_EN_SINGLE", strerror(errno));
+#endif
+
return 1;
}
diff --git a/tempcomp.c b/tempcomp.c
index a468e33..4c36367 100644
--- a/tempcomp.c
+++ b/tempcomp.c
@@ -66,10 +66,9 @@ get_tempcomp(double temp)
return k0 + (temp - T0) * k1 + (temp - T0) * (temp - T0) * k2;
/* Otherwise interpolate/extrapolate between two nearest points */
-
- for (i = 1; i < ARR_GetSize(points); i++) {
- p2 = (struct Point *)ARR_GetElement(points, i);
- if (p2->temp >= temp)
+ for (i = 1; ; i++) {
+ p2 = ARR_GetElement(points, i);
+ if (p2->temp >= temp || i + 1 >= ARR_GetSize(points))
break;
}
p1 = p2 - 1;
diff --git a/test/compilation/002-scanbuild b/test/compilation/002-scanbuild
index 0395df9..ff7e2aa 100755
--- a/test/compilation/002-scanbuild
+++ b/test/compilation/002-scanbuild
@@ -3,6 +3,7 @@
cd ../..
for opts in \
+ "--enable-debug" \
"--host-system=Linux" \
"--host-system=NetBSD" \
"--host-system=FreeBSD" \
diff --git a/test/simulation/008-ntpera b/test/simulation/008-ntpera
index 2eea63b..ab11819 100755
--- a/test/simulation/008-ntpera
+++ b/test/simulation/008-ntpera
@@ -41,7 +41,6 @@ for time_offset in -1e-1 1e-1; do
export CLKNETSIM_START_DATE=$(awk "BEGIN {printf \"%.0f\", $ntp_start + $start_offset}")
run_test || test_fail
check_chronyd_exit || test_fail
- check_source_selection || test_fail
check_packet_interval || test_fail
check_sync && test_fail
done
diff --git a/test/simulation/106-refclock b/test/simulation/106-refclock
index ba7c482..dedab9b 100755
--- a/test/simulation/106-refclock
+++ b/test/simulation/106-refclock
@@ -36,7 +36,7 @@ Update interval : 16\.. seconds
.*$" || test_fail
if echo "$refclock" | grep -q 'PHC.*nocrossts'; then
- check_file_messages "20.* GPS.*[0-9] N " 650 750 refclocks.log || test_fail
+ check_file_messages "20.* GPS.*[0-9] N " 620 750 refclocks.log || test_fail
else
check_file_messages "20.* GPS.*[0-9] N " 997 1001 refclocks.log || test_fail
fi
@@ -64,7 +64,7 @@ Stratum.*: 1
Root delay : 0\.000000001 seconds
.*$" || test_fail
- check_file_messages "20.* PPS1.*[0-9] N " 620 740 refclocks.log || test_fail
+ check_file_messages "20.* PPS1.*[0-9] N " 610 740 refclocks.log || test_fail
check_file_messages "20.* PPS1.*- N " 60 63 refclocks.log || test_fail
rm -f tmp/refclocks.log
@@ -89,14 +89,15 @@ Root delay : 0\.000000001 seconds
check_file_messages "20.* PPS1.*- N " 60 63 refclocks.log || test_fail
rm -f tmp/refclocks.log
- min_sync_time=100
- max_sync_time=220
+ min_sync_time=80
+ max_sync_time=180
chronyc_start=220
client_conf="
refclock SHM 0 refid NMEA offset 0.35 delay 0.1
refclock PPS /dev/pps0
logdir tmp
-log refclocks"
+log refclocks
+maxupdateskew 10000"
run_test || test_fail
check_chronyd_exit || test_fail
@@ -108,7 +109,7 @@ Stratum.*: 1
Root delay : 0\.000000001 seconds
.*$" || test_fail
- check_file_messages "20.* PPS1.*[0-9] N " 800 940 refclocks.log || test_fail
+ check_file_messages "20.* PPS1.*[0-9] N " 800 960 refclocks.log || test_fail
check_file_messages "20.* PPS1.*- N " 50 63 refclocks.log || test_fail
rm -f tmp/refclocks.log
fi
diff --git a/test/simulation/108-peer b/test/simulation/108-peer
index 906de17..2c914e1 100755
--- a/test/simulation/108-peer
+++ b/test/simulation/108-peer
@@ -21,6 +21,7 @@ EOF
clients=2
peers=2
+freq_max_limit=1e-3
max_sync_time=1000
client_server_options="minpoll 6 maxpoll 6"
client_peer_options="minpoll 6 maxpoll 6"
@@ -30,6 +31,7 @@ check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
+freq_max_limit=$default_freq_max_limit
base_delay="(+ 1e-4 (* -1 (equal 0.1 from 3) (equal 0.1 to 1)))"
client_peer_options=""
diff --git a/test/simulation/110-chronyc b/test/simulation/110-chronyc
index 46b0a3f..5b32931 100755
--- a/test/simulation/110-chronyc
+++ b/test/simulation/110-chronyc
@@ -58,7 +58,7 @@ MS Name/IP address Stratum Poll Reach LastRx Last sample
\^\? 192\.168\.123\.2 0 [0-9]+ 0 - \+0ns\[ \+0ns\] \+/- 0ns
Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev
==============================================================================
-SHM0 [0-9 ]+ [0-9 ]+ [0-9 ]+ [ +-][01]\.... [0-9 ]+\.... [0-9 +-]+[un]s [0-9 ]+[un]s
+SHM0 [0-9 ]+ [0-9 ]+ [0-9 ]+ [ +-][012]\.... [0-9 ]+\.... [0-9 +-]+[un]s [0-9 ]+[un]s
192\.168\.123\.1 [0-9 ]+ [0-9 ]+ [0-9 ]+ [ +-][01]\.... [0-9 ]+\.... [0-9 +-]+[un]s [0-9 ]+[un]s
192\.168\.123\.2 0 0 0 \+0\.000 2000\.000 \+0ns 4000ms
210 n_samples = 0
@@ -114,7 +114,7 @@ limit=1
for chronyc_conf in \
"accheck 1.2.3.4" \
"add peer 10.0.0.0 minpoll 2 maxpoll 6" \
- "add server 10.0.0.0 minpoll 6 maxpoll 10 iburst burst key 1 certset 2 maxdelay 1e-3 maxdelayratio 10.0 maxdelaydevratio 10.0 maxdelayquant 0.5 mindelay 1e-4 asymmetry 0.5 offset 1e-5 minsamples 6 maxsamples 6 filter 3 offline auto_offline prefer noselect trust require xleave polltarget 20 port 123 presend 7 minstratum 3 version 4 nts ntsport 4460 copy extfield F323 extfield F324" \
+ "add server 10.0.0.0 minpoll 6 maxpoll 10 iburst burst key 1 certset 2 maxdelay 1e-3 maxdelayratio 10.0 maxdelaydevratio 10.0 maxdelayquant 0.5 mindelay 1e-4 asymmetry 0.5 offset 1e-5 minsamples 6 maxsamples 6 filter 3 offline auto_offline prefer noselect trust require xleave polltarget 20 port 123 presend 7 minstratum 3 version 4 nts ntsport 4460 copy extfield F323 extfield F324 ipv6 ipv4" \
"add server node1.net1.clk" \
"allow 1.2.3.4" \
"allow 1.2" \
@@ -145,7 +145,7 @@ for chronyc_conf in \
"dfreq 1.0e-3" \
"doffset -1.0" \
"dump" \
- "local stratum 5 distance 1.0 orphan" \
+ "local stratum 5 distance 1.0 activate 0.5 orphan" \
"local off" \
"makestep 10.0 3" \
"makestep" \
@@ -165,6 +165,7 @@ for chronyc_conf in \
"offline" \
"offline 255.255.255.0/1.2.3.0" \
"offline 1.2.3.0/24" \
+ "offset 1.2.3.4 1.0" \
"online" \
"online 1.2.3.0/24" \
"onoffline" \
@@ -247,6 +248,10 @@ Total TX : 1
Total RX : 1
Total valid RX : 1
Total good RX : 0
+Total kernel TX : [01]
+Total kernel RX : 1
+Total HW TX : 0
+Total HW RX : 0
S Name/IP Address Auth COpts EOpts Last Score Interval Leap
=======================================================================
M node1\.net1\.clk N ----- ----- 0 1\.0 \+0ns \+0ns N
@@ -347,6 +352,7 @@ maxpoll 192.168.123.1 5
maxupdateskew 192.168.123.1 10.0
minpoll 192.168.123.1 3
minstratum 192.168.123.1 1
+offset 192.168.123.1 -1.0
polltarget 192.168.123.1 10
selectopts 192.168.123.1 +trust +prefer -require
selectdata
@@ -371,6 +377,7 @@ check_chronyc_output "^200 OK
200 OK
200 OK
200 OK
+200 OK
S Name/IP Address Auth COpts EOpts Last Score Interval Leap
=======================================================================
M node1\.net1\.clk N \-PT\-\- \-PT\-\- 0 1\.0 \+0ns \+0ns \?
@@ -433,7 +440,12 @@ server_conf="
server 192.168.123.1
noclientlog"
-commands=(
+check_config_h 'FEAT_IPV6 1' && commands=(
+ "add server ::1 ipv4" "^515 Invalid address family$"
+ ) || commands=()
+
+commands+=(
+ "add server 192.168.123.1 ipv6" "^515 Invalid address family$"
"add server nosuchnode.net1.clk" "^Invalid host/IP address$"
"allow nosuchnode.net1.clk" "^Could not read address$"
"allow 192.168.123.0/2 4" "^Could not read address$"
diff --git a/test/simulation/113-leapsecond b/test/simulation/113-leapsecond
index 394440b..63da734 100755
--- a/test/simulation/113-leapsecond
+++ b/test/simulation/113-leapsecond
@@ -8,54 +8,86 @@ check_config_h 'FEAT_REFCLOCK 1' || test_skip
export CLKNETSIM_START_DATE=$(TZ=UTC date -d 'Dec 30 2008 0:00:00' +'%s')
-leap=$[2 * 24 * 3600]
limit=$[4 * 24 * 3600]
client_start=$[2 * 3600]
-server_conf="refclock SHM 0 dpoll 10 poll 10
-leapsectz right/UTC"
refclock_jitter=1e-9
-refclock_offset="(* -1.0 (equal 0.1 (max (sum 1.0) $leap) $leap))"
-for leapmode in system step slew; do
- client_conf="leapsecmode $leapmode"
- if [ $leapmode = slew ]; then
- max_sync_time=$[$leap + 12]
- else
- max_sync_time=$[$leap]
- fi
+for dir in "+1" "-1"; do
+ leap=$[2 * 24 * 3600 + 1 + $dir]
+ server_conf="refclock SHM 0 dpoll 10 poll 10
+ leapseclist tmp/leap.list"
+ refclock_offset="(* $dir (equal 0.1 (max (sum 1.0) $leap) $leap))"
+
+ cat > tmp/leap.list <<-EOF
+ #$ 3676924800
+ #@ 3928521600
+ 3345062400 33 # 1 Jan 2006
+ 3439756800 $[33 - $dir] # 1 Jan 2009 $(
+ [ "$dir" = "+1" ] && echo -e "\n3471292800 33\n3502828800 34")
+ 3550089600 35 # 1 Jul 2012
+ EOF
+
+ for leapmode in system step slew; do
+ client_conf="leapsecmode $leapmode"
+ if [ $leapmode = slew ]; then
+ max_sync_time=$[2 * 24 * 3600 + 13]
+ else
+ max_sync_time=$[2 * 24 * 3600 + 1]
+ fi
+ min_sync_time=$[$max_sync_time - 2]
+
+ 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 "System clock TAI offset set to" 1 1 log.1 || test_fail
+ check_file_messages "System clock TAI offset set to 33" 1 1 log.1 || test_fail
+ done
+
+ client_server_options="trust"
+ client_conf="refclock SHM 0 dpoll 10 poll 10 delay 1e-3"
+ min_sync_time=$[$leap - 2]
+ max_sync_time=$[$leap]
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
-done
-client_server_options="trust"
-client_conf="refclock SHM 0 dpoll 10 poll 10 delay 1e-3"
+ client_server_options=""
+ client_conf="leapsecmode system"
+ min_sync_time=230000
+ max_sync_time=240000
-run_test || test_fail
-check_chronyd_exit || test_fail
-check_source_selection || test_fail
-check_packet_interval || test_fail
-check_sync || test_fail
+ for smoothmode in "" "leaponly"; do
+ server_conf="refclock SHM 0 dpoll 10 poll 10
+ leapseclist tmp/leap.list
+ leapsecmode slew
+ smoothtime 400 0.001 $smoothmode"
-client_server_options=""
-client_conf="leapsecmode system"
-min_sync_time=230000
-max_sync_time=240000
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_source_selection || test_fail
+ check_packet_interval || test_fail
+ check_sync || test_fail
+ done
+done
-for smoothmode in "" "leaponly"; do
+if TZ=right/UTC date -d 'Dec 31 2008 23:59:60' 2> /dev/null | grep :60; then
server_conf="refclock SHM 0 dpoll 10 poll 10
- leapsectz right/UTC
- leapsecmode slew
- smoothtime 400 0.001 $smoothmode"
+ leapsectz right/UTC"
+ refclock_offset="(* -1 (equal 0.1 (max (sum 1.0) $leap) $leap))"
+ client_conf="leapsecmode system"
+ min_sync_time=$[$leap - 2]
+ max_sync_time=$[$leap]
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
-done
+fi
test_pass
diff --git a/test/simulation/118-maxdelay b/test/simulation/118-maxdelay
index 117b170..8ab0a62 100755
--- a/test/simulation/118-maxdelay
+++ b/test/simulation/118-maxdelay
@@ -14,7 +14,6 @@ client_server_options="maxpoll 6 maxdelay 3e-5 maxdelayratio 2.0 maxdelaydevrati
run_test || test_fail
check_chronyd_exit || test_fail
-check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
diff --git a/test/simulation/119-smoothtime b/test/simulation/119-smoothtime
index 7f5114c..fc2c6be 100755
--- a/test/simulation/119-smoothtime
+++ b/test/simulation/119-smoothtime
@@ -14,7 +14,7 @@ max_sync_time=800
run_test || test_fail
check_chronyd_exit || test_fail
-check_source_selection || test_fail
+check_source_selection && test_fail
check_sync || test_fail
limit=10000
diff --git a/test/simulation/121-local b/test/simulation/121-local
new file mode 100755
index 0000000..ba99efc
--- /dev/null
+++ b/test/simulation/121-local
@@ -0,0 +1,90 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "local options"
+
+check_config_h 'FEAT_CMDMON 1' || test_skip
+
+server_strata=3
+server_conf="local stratum 5 orphan
+server 192.168.123.1
+server 192.168.123.2
+server 192.168.123.3"
+max_sync_time=900
+client_start=140
+chronyc_start=700
+chronyc_conf="tracking"
+time_rms_limit=5e-4
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+check_chronyc_output "^.*Stratum *: 7.*$" || test_fail
+
+limit=4000
+wander=0.0
+jitter=0.0
+server_strata=1
+server_conf=""
+client_server_options="minpoll 6 maxpoll 6 minsamples 64"
+chronyc_start=1
+chronyc_conf="timeout 1000000
+tracking
+tracking
+tracking
+tracking"
+base_delay=$(cat <<-EOF | tr -d '\n'
+ (+ 1e-4
+ (* 990
+ (equal 0.1 from 3))
+ (* -1
+ (equal 0.1 from 1)
+ (equal 0.1 (max (% time 2000) 1000) 1000)))
+EOF
+)
+
+client_conf="local
+maxclockerror 1000"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_chronyc_output "^.*7F7F0101.*C0A87B01.*7F7F0101.*C0A87B01.*$" || test_fail
+
+client_conf="local distance 0.5
+maxclockerror 1000"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_chronyc_output "^.*7F7F0101.*C0A87B01.*7F7F0101.*C0A87B01.*$" || test_fail
+
+client_conf="local distance 2.0
+maxclockerror 1000"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_chronyc_output "^.*7F7F0101.*C0A87B01.*C0A87B01.*C0A87B01.*$" || test_fail
+
+client_conf="local activate 1e-4
+maxclockerror 1000"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_chronyc_output "^.* 00000000 .*C0A87B01.*C0A87B01.*C0A87B01.*$" || test_fail
+
+client_conf="local activate 1e-1
+maxclockerror 1000"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_chronyc_output "^.* 00000000 .*C0A87B01.*7F7F0101.*C0A87B01.*$" || test_fail
+
+client_conf="local activate 1e-1 distance 2.0
+maxclockerror 1000"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_chronyc_output "^.* 00000000 .*C0A87B01.*C0A87B01.*C0A87B01.*$" || test_fail
+
+test_pass
diff --git a/test/simulation/121-orphan b/test/simulation/121-orphan
deleted file mode 100755
index 7579997..0000000
--- a/test/simulation/121-orphan
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/usr/bin/env bash
-
-. ./test.common
-
-test_start "orphan option"
-
-check_config_h 'FEAT_CMDMON 1' || test_skip
-
-server_strata=3
-server_conf="local stratum 5 orphan
-server 192.168.123.1
-server 192.168.123.2
-server 192.168.123.3"
-max_sync_time=900
-client_start=140
-chronyc_start=700
-chronyc_conf="tracking"
-time_rms_limit=5e-4
-
-run_test || test_fail
-check_chronyd_exit || test_fail
-check_source_selection || test_fail
-check_sync || test_fail
-check_chronyc_output "^.*Stratum *: 7.*$" || test_fail
-
-test_pass
diff --git a/test/simulation/122-xleave b/test/simulation/122-xleave
index c19063a..77059d8 100755
--- a/test/simulation/122-xleave
+++ b/test/simulation/122-xleave
@@ -53,7 +53,6 @@ for rpoll in 4 5 6; do
run_test || test_fail
check_chronyd_exit || test_fail
- check_source_selection || test_fail
check_sync || test_fail
if [ $rpoll -le 5 ]; then
diff --git a/test/simulation/124-tai b/test/simulation/124-tai
index 97064f7..e8c909d 100755
--- a/test/simulation/124-tai
+++ b/test/simulation/124-tai
@@ -18,9 +18,17 @@ servers=0
refclock_offset="(+ -34 (equal 0.1 (max (sum 1.0) $leap) $leap))"
client_conf="
refclock SHM 0 dpoll 0 poll 0 tai
-leapsectz right/UTC
+leapseclist tmp/leap.list
leapsecmode ignore
-maxchange 1e-3 1 0"
+maxchange 1e-3 10 0"
+
+cat > tmp/leap.list <<-EOF
+ #$ 3676924800
+ #@ 3928521600
+ 3345062400 33 # 1 Jan 2006
+ 3439756800 34 # 1 Jan 2009
+ 3550089600 35 # 1 Jul 2012
+EOF
run_test || test_fail
check_chronyd_exit || test_fail
@@ -33,9 +41,9 @@ time_offset=-1000
refclock_offset="(+ -34)"
client_conf="
refclock SHM 0 dpoll 0 poll 0 tai
-leapsectz right/UTC
+leapseclist tmp/leap.list
makestep 1 1
-maxchange 1e-3 1 0"
+maxchange 1e-3 10 0"
run_test || test_fail
check_chronyd_exit || test_fail
diff --git a/test/simulation/126-burst b/test/simulation/126-burst
index 1cb6f9c..baae3d5 100755
--- a/test/simulation/126-burst
+++ b/test/simulation/126-burst
@@ -22,7 +22,7 @@ client_min_mean_out_interval=150.0
run_test || test_fail
check_chronyd_exit || test_fail
-check_source_selection || test_fail
+check_source_selection && test_fail
check_packet_interval || test_fail
check_sync || test_fail
diff --git a/test/simulation/127-filter b/test/simulation/127-filter
index 739dd91..68afec2 100755
--- a/test/simulation/127-filter
+++ b/test/simulation/127-filter
@@ -12,7 +12,7 @@ client_min_mean_out_interval=15.9
run_test || test_fail
check_chronyd_exit || test_fail
-check_source_selection || test_fail
+check_source_selection && test_fail
check_packet_interval || test_fail
check_sync || test_fail
diff --git a/test/simulation/135-ratelimit b/test/simulation/135-ratelimit
index 86c435d..0756f19 100755
--- a/test/simulation/135-ratelimit
+++ b/test/simulation/135-ratelimit
@@ -15,4 +15,15 @@ check_sync || test_fail
check_file_messages " 2 1 " 1200 1300 log.packets || test_fail
check_file_messages " 1 2 " 180 220 log.packets || test_fail
+server_conf="ratelimit interval 6 burst 2 leak 4 kod 2"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+check_file_messages " 2 1 " 700 850 log.packets || test_fail
+check_file_messages " 1 2 " 350 450 log.packets || test_fail
+check_log_messages "Received KoD RATE.*\.123.1" 100 140 || test_fail
+
test_pass
diff --git a/test/simulation/142-ntpoverptp b/test/simulation/142-ntpoverptp
index 2996dc0..74cb8f7 100755
--- a/test/simulation/142-ntpoverptp
+++ b/test/simulation/142-ntpoverptp
@@ -16,6 +16,7 @@ peers=2
max_sync_time=420
server_conf="
+ptpdomain 123
ptpport 319"
client_conf="
ptpport 319
@@ -103,4 +104,20 @@ if check_config_h 'FEAT_DEBUG 1'; then
check_log_messages "apply_net_correction.*Applied" 0 0 || test_fail
fi
+freq_offset=-1e-4
+delay_correction=""
+server_conf="ptpport 319"
+client_conf="ptpport 319
+ptpdomain 124
+authselectmode ignore
+keyfile tmp/peer.keys"
+time_max_limit=$default_time_max_limit
+time_rms_limit=$default_time_rms_limit
+freq_max_limit=$default_freq_max_limit
+freq_rms_limit=$default_freq_rms_limit
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_sync && test_fail
+
test_pass
diff --git a/test/simulation/144-monoroot b/test/simulation/144-monoroot
index 20fae12..d97f30d 100755
--- a/test/simulation/144-monoroot
+++ b/test/simulation/144-monoroot
@@ -20,7 +20,7 @@ for options in "extfield F323" "xleave extfield F323"; do
run_test || test_fail
check_chronyd_exit || test_fail
- check_source_selection || test_fail
+ check_source_selection && test_fail
check_sync || test_fail
done
@@ -47,7 +47,7 @@ for lpoll in 5 6 7; do
run_test || test_fail
check_chronyd_exit || test_fail
- check_source_selection || test_fail
+ check_source_selection && test_fail
check_sync || test_fail
done
done
diff --git a/test/simulation/148-replacement b/test/simulation/148-replacement
index f15fc4d..d09fba6 100755
--- a/test/simulation/148-replacement
+++ b/test/simulation/148-replacement
@@ -53,4 +53,28 @@ check_log_messages "2010-01-01T0[5-9]:.*Source 192.168.123.. replaced with" 0 15
check_file_messages "20.*192.168.123.* 11.1 6 6 " 20 500 measurements.log || test_fail
rm -f tmp/measurements.log
+# 2 replaceable falsetickers and 1 replaceable unreachable server
+servers=6
+falsetickers=2
+base_delay="(+ 1e-4 (* -1 (equal 0.1 to 3)))"
+client_server_conf="
+server nodes-4-1.net1.clk
+server nodes-5-2.net1.clk
+server nodes-6-3.net1.clk"
+
+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 "Can't synchronise: no majority" 1 1 || test_fail
+check_log_messages "Detected falseticker" 0 2 || test_fail
+check_log_messages "Source 192.168.123.. replaced with" 3 60 || test_fail
+check_log_messages "Source 192.168.123.1 replaced with" 1 25 || test_fail
+check_log_messages "Source 192.168.123.2 replaced with" 1 25 || test_fail
+check_log_messages "Source 192.168.123.3 replaced with" 1 25 || test_fail
+check_file_messages "20.*192.168.123.* 11.1 6 6 " 50 800 measurements.log || test_fail
+rm -f tmp/measurements.log
+
test_pass
diff --git a/test/simulation/203-initreload b/test/simulation/203-initreload
new file mode 100755
index 0000000..cf7924b
--- /dev/null
+++ b/test/simulation/203-initreload
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+check_config_h 'FEAT_CMDMON 1' || test_skip
+
+# Test fix "conf: don't load sourcedir during initstepslew and RTC init"
+
+test_start "reload during initstepslew"
+
+client_conf="initstepslew 5 192.168.123.1
+sourcedir tmp"
+client_server_conf="#"
+chronyc_conf="reload sources"
+chronyc_start=4
+
+echo 'server 192.168.123.1' > tmp/sources.sources
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+
+check_log_messages "Added source 192\.168\.123\.1" 1 1 || test_fail
+
+test_pass
diff --git a/test/system/007-cmdmon b/test/system/007-cmdmon
index f9541d3..954a15b 100755
--- a/test/system/007-cmdmon
+++ b/test/system/007-cmdmon
@@ -28,6 +28,7 @@ for command in \
"local" \
"online" \
"onoffline" \
+ "offset $server 0.0" \
"maxdelay $server 1e-1" \
"maxdelaydevratio $server 5.0" \
"maxdelayratio $server 3.0" \
@@ -97,12 +98,16 @@ RX timestamping : (Daemon|Kernel)
Total TX : [0-9]+
Total RX : [0-9]+
Total valid RX : [0-9]+
-Total good RX : [0-9]+$" || test_fail
+Total good RX : [0-9]+
+Total kernel TX : [0-9]+
+Total kernel RX : [0-9]+
+Total HW TX : 0
+Total HW RX : 0$" || test_fail
run_chronyc "selectdata" || test_fail
check_chronyc_output "^S Name/IP Address Auth COpts EOpts Last Score Interval Leap
=======================================================================
-s 127\.0\.0\.1 N -PTR- -PTR- 0 1\.0 \+0ns \+0ns \?$" || test_fail
+M 127\.0\.0\.1 N -PTR- -PTR- 0 1\.0 \+0ns \+0ns \?$" || test_fail
run_chronyc "serverstats" || test_fail
check_chronyc_output "^NTP packets received : [0-9]+
diff --git a/test/system/008-confload b/test/system/008-confload
index 7e80698..b107d70 100755
--- a/test/system/008-confload
+++ b/test/system/008-confload
@@ -77,7 +77,32 @@ check_chronyc_output "^[^=]*
.. 127\.123\.5\.3 *[05] 7 [^^]*
.. 127\.123\.5\.6 [^^]*$" || test_fail
+run_chronyc "reload sources" || test_fail
+run_chronyc "reload sources" || test_fail
+
+rm $TEST_DIR/conf5.d/{3,5,6}.sources
+echo "server 127.123.5.7" > $TEST_DIR/conf5.d/7.sources
+
+run_chronyc "reload sources" || test_fail
+
+run_chronyc "sources" || test_fail
+check_chronyc_output "^[^=]*
+=*
+.. 127\.123\.1\.1 [^^]*
+.. 127\.123\.1\.3 [^^]*
+.. 127\.123\.1\.4 [^^]*
+.. 127\.123\.3\.1 [^^]*
+.. 127\.123\.2\.2 [^^]*
+.. 127\.123\.2\.3 [^^]*
+.. 127\.123\.4\.4 [^^]*
+.. 127\.123\.1\.2 *[05] 6 [^^]*
+.. 127\.123\.5\.2 *[05] 5 [^^]*
+.. 127\.123\.5\.7 [^^]*$" || test_fail
+
+run_chronyc "reload sources" || test_fail
+
stop_chronyd || test_fail
+check_chronyd_message_count "Could not add source.*\.5\.5.*in use" 1 1 || test_fail
check_chronyd_message_count "Could not add source" 1 1 || test_fail
test_pass
diff --git a/test/system/010-nts b/test/system/010-nts
index 8d92bbc..b215efa 100755
--- a/test/system/010-nts
+++ b/test/system/010-nts
@@ -45,6 +45,11 @@ check_chronyc_output "^Name/IP address Mode KeyID Type KLen Last Atm
=========================================================================
127\.0\.0\.1 NTS 1 (30|15) (128|256) [0-9] 0 0 [78] ( 64|100)$" || test_fail
+run_chronyc "serverstats" || test_fail
+check_chronyc_output "NTS-KE connections accepted: 1
+NTS-KE connections dropped : 0
+Authenticated NTP packets : [1-9][0-9]*" || test_fail
+
stop_chronyd || test_fail
check_chronyd_messages || test_fail
check_chronyd_files || test_fail
diff --git a/test/unit/clientlog.c b/test/unit/clientlog.c
index e5bf1f4..96818b2 100644
--- a/test/unit/clientlog.c
+++ b/test/unit/clientlog.c
@@ -1,6 +1,6 @@
/*
**********************************************************************
- * Copyright (C) Miroslav Lichvar 2016, 2021
+ * Copyright (C) Miroslav Lichvar 2016, 2021, 2024
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -35,18 +35,18 @@ void
test_unit(void)
{
uint64_t ts64, prev_first_ts64, prev_last_ts64, max_step;
+ int i, j, k, kod, passes, kods, drops, index, shift;
uint32_t index2, prev_first, prev_size;
NTP_Timestamp_Source ts_src, ts_src2;
struct timespec ts, ts2;
- int i, j, k, index, shift;
CLG_Service s;
NTP_int64 ntp_ts;
IPAddr ip;
char conf[][100] = {
"clientloglimit 20000",
"ratelimit interval 3 burst 4 leak 3",
- "cmdratelimit interval 3 burst 4 leak 3",
- "ntsratelimit interval 6 burst 8 leak 3",
+ "ntsratelimit interval 4 burst 8 leak 3",
+ "cmdratelimit interval 6 burst 4 leak 3",
};
CNF_Initialise(0, 0);
@@ -80,19 +80,51 @@ test_unit(void)
DEBUG_LOG("records %u", ARR_GetSize(records));
TEST_CHECK(ARR_GetSize(records) == 128);
- s = CLG_NTP;
+ for (kod = 0; kod <= 2; kod += 2) {
+ for (s = CLG_NTP; s <= CLG_CMDMON; s++) {
+ for (i = passes = kods = drops = 0; i < 10000; i++) {
+ kod_rate[s] = kod;
+ ts.tv_sec += 1;
+ index = CLG_LogServiceAccess(s, &ip, &ts);
+ TEST_CHECK(index >= 0);
+ switch (CLG_LimitServiceRate(s, index)) {
+ case CLG_PASS:
+ passes += 1;
+ break;
+ case CLG_DROP:
+ drops += 1;
+ break;
+ case CLG_KOD:
+ kods += 1;
+ break;
+ default:
+ assert(0);
+ }
+ }
- for (i = j = 0; i < 10000; i++) {
- ts.tv_sec += 1;
- index = CLG_LogServiceAccess(s, &ip, &ts);
- TEST_CHECK(index >= 0);
- if (!CLG_LimitServiceRate(s, index))
- j++;
+ DEBUG_LOG("service %d requests %d passes %d kods %d drops %d",
+ (int)s, i, passes, kods, drops);
+ if (kod)
+ TEST_CHECK(kods * 2.5 < drops && kods * 3.5 > drops);
+ else
+ TEST_CHECK(kods == 0);
+
+ switch (s) {
+ case CLG_NTP:
+ TEST_CHECK(passes > 1750 && passes < 2050);
+ break;
+ case CLG_NTSKE:
+ TEST_CHECK(passes > 1300 && passes < 1600);
+ break;
+ case CLG_CMDMON:
+ TEST_CHECK(passes > 1100 && passes < 1400);
+ break;
+ default:
+ assert(0);
+ }
+ }
}
- DEBUG_LOG("requests %d responses %d", i, j);
- TEST_CHECK(j * 4 < i && j * 6 > i);
-
TEST_CHECK(!ntp_ts_map.timestamps);
UTI_ZeroNtp64(&ntp_ts);
diff --git a/test/unit/leapdb.c b/test/unit/leapdb.c
new file mode 100644
index 0000000..cb27387
--- /dev/null
+++ b/test/unit/leapdb.c
@@ -0,0 +1,106 @@
+/*
+ **********************************************************************
+ * Copyright (C) Patrick Oppenlander 2024
+ *
+ * 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 <leapdb.c>
+#include "test.h"
+
+struct test_vector {
+ time_t when;
+ int tai_offset;
+ NTP_Leap leap;
+ int fake;
+} tests[] = {
+ /* leapdb.list is a cut down version of leap-seconds.list */
+ {3439756800, 34, LEAP_InsertSecond, 0}, /* 1 Jan 2009 */
+ {3550089600, 35, LEAP_InsertSecond, 0}, /* 1 Jul 2012 */
+ {3644697600, 36, LEAP_InsertSecond, 0}, /* 1 Jul 2015 */
+ {3692217600, 37, LEAP_InsertSecond, 0}, /* 1 Jan 2017 */
+ {3786825600, 36, LEAP_DeleteSecond, 1}, /* 1 Jan 2020 fake in leapdb.list */
+};
+
+static void
+test_leap_source(NTP_Leap (*fn)(time_t when, int *tai_offset),
+ int skip_fakes)
+{
+ int i, prev_tai_offset = 34;
+
+ for (i = 0; i < sizeof tests / sizeof tests[0]; ++i) {
+ struct test_vector *t = tests + i;
+
+ NTP_Leap leap;
+ int tai_offset = -1;
+
+ /* Our unit test leapdb.list contains a fake entry removing a leap second.
+ * Skip this when testing with the right/UTC timezone using mktime(). */
+ if (skip_fakes && t->fake)
+ continue;
+
+ /* One second before leap second */
+ leap = fn(t->when - LEAP_SEC_LIST_OFFSET - 1, &tai_offset);
+ TEST_CHECK(leap == t->leap);
+ TEST_CHECK(tai_offset = prev_tai_offset);
+
+ /* Exactly on leap second */
+ leap = fn(t->when - LEAP_SEC_LIST_OFFSET, &tai_offset);
+ TEST_CHECK(leap == LEAP_Normal);
+ TEST_CHECK(tai_offset == t->tai_offset);
+
+ /* One second after leap second */
+ leap = fn(t->when - LEAP_SEC_LIST_OFFSET + 1, &tai_offset);
+ TEST_CHECK(leap == LEAP_Normal);
+ TEST_CHECK(tai_offset == t->tai_offset);
+
+ prev_tai_offset = t->tai_offset;
+ }
+}
+
+void
+test_unit(void)
+{
+ char conf[][100] = {
+ "leapsectz right/UTC",
+ "leapseclist leapdb.list"
+ };
+ int i;
+
+ CNF_Initialise(0, 0);
+ for (i = 0; i < sizeof conf / sizeof conf[0]; i++)
+ CNF_ParseLine(NULL, i + 1, conf[i]);
+ LDB_Initialise();
+
+ if (check_leap_source(get_tz_leap)) {
+ DEBUG_LOG("testing get_tz_leap");
+ test_leap_source(get_tz_leap, 1);
+ } else {
+ DEBUG_LOG("Skipping get_tz_leap test. Either the right/UTC timezone is "
+ "missing, or mktime() doesn't support leap seconds.");
+ }
+
+ DEBUG_LOG("testing get_list_leap");
+ TEST_CHECK(check_leap_source(get_list_leap));
+ test_leap_source(get_list_leap, 0);
+
+ /* This exercises the twice-per-day logic */
+ DEBUG_LOG("testing LDB_GetLeap");
+ test_leap_source(LDB_GetLeap, 1);
+
+ LDB_Finalise();
+ CNF_Finalise();
+}
diff --git a/test/unit/leapdb.list b/test/unit/leapdb.list
new file mode 100644
index 0000000..5dc2188
--- /dev/null
+++ b/test/unit/leapdb.list
@@ -0,0 +1,22 @@
+#
+# Cut down version of leap-seconds.list for unit test.
+#
+# Blank lines need to be ignored, so include a few for testing.
+# Whitespace errors on non-blank lines below are copied from the original file.
+#
+
+# Leap second data update time
+#$ 3676924800
+#
+# File update time
+#@ 3928521600
+
+3439756800 34 # 1 Jan 2009
+3550089600 35 # 1 Jul 2012
+3644697600 36 # 1 Jul 2015
+3692217600 37 # 1 Jan 2017
+3786825600 36 # 1 Jan 2020 (fake entry to test negative leap second)
+
+# FIPS 180-1 hash
+# NOTE! this value has not been recomputed for this unit test file.
+#h 16edd0f0 3666784f 37db6bdd e74ced87 59af48f1
diff --git a/test/unit/ntp_sources.c b/test/unit/ntp_sources.c
index e3d7c4d..a9bdbad 100644
--- a/test/unit/ntp_sources.c
+++ b/test/unit/ntp_sources.c
@@ -125,7 +125,7 @@ void
test_unit(void)
{
char source_line[] = "127.0.0.1 offline", conf[] = "port 0", name[64];
- int i, j, k, slot, found, pool, prev_n;
+ int i, j, k, family, slot, found, pool, prev_n;
uint32_t hash = 0, conf_id;
NTP_Remote_Address addrs[256], addr;
NTP_Local_Address local_addr;
@@ -216,7 +216,7 @@ test_unit(void)
TEST_CHECK(n_sources == 0);
- status = NSR_AddSourceByName("a b", 0, 0, 0, &source.params, &conf_id);
+ status = NSR_AddSourceByName("a b", IPADDR_UNSPEC, 0, 0, 0, &source.params, &conf_id);
TEST_CHECK(status == NSR_InvalidName);
local_addr.ip_addr.family = IPADDR_INET4;
@@ -228,11 +228,13 @@ test_unit(void)
for (i = 0; i < 500; i++) {
for (j = 0; j < 20; j++) {
snprintf(name, sizeof (name), "ntp%d.example.net", (int)(random() % 10));
+ family = random() % 2 ? IPADDR_UNSPEC : random() % 2 ? IPADDR_INET4 : IPADDR_INET6;
pool = random() % 2;
prev_n = n_sources;
DEBUG_LOG("%d/%d adding source %s pool=%d", i, j, name, pool);
- status = NSR_AddSourceByName(name, 0, pool, random() % 2 ? NTP_SERVER : NTP_PEER,
+ status = NSR_AddSourceByName(name, family, 0, pool,
+ random() % 2 ? NTP_SERVER : NTP_PEER,
&source.params, &conf_id);
TEST_CHECK(status == NSR_UnresolvedName);
@@ -242,11 +244,13 @@ test_unit(void)
for (us = unresolved_sources; us->next; us = us->next)
;
TEST_CHECK(strcmp(us->name, name) == 0);
+ TEST_CHECK(us->family == family);
if (pool) {
TEST_CHECK(us->address.ip_addr.family == IPADDR_UNSPEC && us->pool_id >= 0);
} else {
TEST_CHECK(strcmp(NSR_GetName(&us->address.ip_addr), name) == 0);
TEST_CHECK(find_slot2(&us->address, &slot) == 2);
+ TEST_CHECK(get_record(slot)->family == family);
}
if (random() % 2) {
diff --git a/version.txt b/version.txt
index 4caecc7..b3d791d 100644
--- a/version.txt
+++ b/version.txt
@@ -1 +1 @@
-4.5
+4.6