summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVincent Blut <vincent.debian@free.fr>2018-03-15 12:42:27 +0100
committerVincent Blut <vincent.debian@free.fr>2018-03-15 12:42:27 +0100
commit164712c8ac6154d9d5b39ccc7cf151b97742903b (patch)
tree7dde6985ca6b6124745fa5c2b067467ae3cf6dc9
parent3f9054c74b7de46c754ffe179dd357371de64c6f (diff)
New upstream version 3.3-pre1
-rw-r--r--FAQ2
-rw-r--r--INSTALL2
-rw-r--r--NEWS23
-rw-r--r--README45
-rw-r--r--candm.h13
-rw-r--r--client.c100
-rw-r--r--cmdmon.c31
-rw-r--r--cmdparse.c3
-rw-r--r--conf.c19
-rwxr-xr-xconfigure29
-rw-r--r--doc/chrony.conf.adoc96
-rw-r--r--doc/chrony.conf.man.in111
-rw-r--r--doc/chronyc.adoc4
-rw-r--r--doc/chronyc.man.in12
-rw-r--r--doc/chronyd.adoc14
-rw-r--r--doc/chronyd.man.in20
-rw-r--r--examples/chrony.conf.example23
-rw-r--r--examples/chrony.conf.example36
-rw-r--r--examples/chrony.nm-dispatcher2
-rw-r--r--examples/chrony.spec2
-rw-r--r--hash_nettle.c120
-rw-r--r--hash_tomcrypt.c6
-rw-r--r--logging.c30
-rw-r--r--logging.h2
-rw-r--r--main.c29
-rw-r--r--nameserv.c14
-rw-r--r--ntp_core.c165
-rw-r--r--ntp_io.c8
-rw-r--r--ntp_io_linux.c157
-rw-r--r--ntp_io_linux.h9
-rw-r--r--ntp_signd.c6
-rw-r--r--pktlength.c19
-rw-r--r--refclock.c48
-rw-r--r--refclock.h2
-rw-r--r--reference.c31
-rw-r--r--reference.h3
-rw-r--r--sched.c9
-rw-r--r--sched.h2
-rw-r--r--sources.c10
-rw-r--r--sourcestats.c8
-rw-r--r--srcparams.h1
-rw-r--r--sys.c2
-rw-r--r--sys_linux.c27
-rw-r--r--sys_linux.h2
-rwxr-xr-xtest/compilation/002-scanbuild5
-rwxr-xr-xtest/simulation/106-refclock11
-rwxr-xr-xtest/simulation/110-chronyc41
-rwxr-xr-xtest/simulation/119-smoothtime6
-rwxr-xr-xtest/simulation/124-tai42
-rwxr-xr-xtest/simulation/125-packetloss29
-rwxr-xr-xtest/simulation/126-burst29
-rw-r--r--test/simulation/test.common10
-rw-r--r--test/unit/hash.c123
-rw-r--r--test/unit/keys.c4
-rw-r--r--test/unit/ntp_core.c208
-rw-r--r--test/unit/util.c32
-rw-r--r--util.c41
-rw-r--r--util.h4
-rw-r--r--version.txt2
59 files changed, 1473 insertions, 361 deletions
diff --git a/FAQ b/FAQ
index 9683764..781ad05 100644
--- a/FAQ
+++ b/FAQ
@@ -441,4 +441,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 2017-08-29 14:17:35 CEST
+Last updated 2018-03-15 09:00:47 CET
diff --git a/INSTALL b/INSTALL
index 5ade973..d0f17b6 100644
--- a/INSTALL
+++ b/INSTALL
@@ -147,4 +147,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 2017-08-29 14:17:35 CEST
+Last updated 2018-03-15 09:00:47 CET
diff --git a/NEWS b/NEWS
index e261098..d6ce33a 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,26 @@
+New in version 3.3
+==================
+
+Enhancements
+------------
+* Add burst option to server/pool directive
+* Add stratum and tai options to refclock directive
+* Add support for Nettle crypto library
+* Add workaround for missing kernel receive timestamps on Linux
+* Wait for late hardware transmit timestamps
+* Improve source selection with unreachable sources
+* Improve protection against replay attacks on symmetric mode
+* Allow PHC refclock to use socket in /var/run/chrony
+* Add shutdown command to stop chronyd
+* Simplify format of response to manual list command
+* Improve handling of unknown responses in chronyc
+
+Bug fixes
+---------
+* Respond to NTPv1 client requests with zero mode
+* Fix -x option to not require CAP_SYS_TIME under non-root user
+* Fix chronyc to not get stuck in infinite loop after clock step
+
New in version 3.2
==================
diff --git a/README b/README
index d868660..aad727e 100644
--- a/README
+++ b/README
@@ -37,20 +37,16 @@ How do I set it up?
===================
The file INSTALL gives instructions. On supported systems the
-compilation process should be automatic.
-
-You will need an ANSI C compiler -- gcc is recommended.
-
-The manual (in texinfo and text formats) describes how to set the
-software up for the less straightforward cases.
+compilation process should be automatic. You will need a C compiler,
+e.g. gcc or clang.
What documentation is there?
============================
-A manual is supplied in Texinfo format (chrony.texi) and
-ready-formatted plain text (chrony.txt) in the distribution.
+The distribution includes manual pages and a document containing
+Frequently Asked Questions (FAQ).
-There is also information available on the chrony web pages, accessible
+The documentation is also available on the chrony web pages, accessible
through the URL
https://chrony.tuxfamily.org/
@@ -126,7 +122,7 @@ Andrew Bishop <amb@gedanken.demon.co.uk>
Improvements to 'sources' and 'sourcestats' output from chronyc
Improvements to documentation
Investigation of required dosynctodr behaviour for various Solaris
- versions.
+ versions
Stephan I. Boettcher <stephan@nevis1.columbia.edu>
Entries in contrib directory
@@ -140,27 +136,27 @@ Bryan Christianson <bryan@whatroute.net>
Entries in contrib directory
Juliusz Chroboczek <jch@pps.jussieu.fr>
- Fix install rule in Makefile if chronyd file is in use.
+ Patch to fix install rule in Makefile if chronyd file is in use
+
+Christian Ehrhardt <christian.ehrhardt@canonical.com>
+ Patch to generate a warning message when CAP_SYS_TIME is missing
Paul Elliott <pelliott@io.com>
- DNSchrony (in contrib directory), a tool for handling NTP servers
- with variable IP addresses.
+ Entries in contrib directory
Mike Fleetwood <mike@rockover.demon.co.uk>
Fixes for compiler warnings
Alexander Gretencord <arutha@gmx.de>
Changes to installation directory system to make it easier for
- package builders.
+ package builders
Andrew Griffiths <agriffit@redhat.com>
Patch to add support for seccomp filter
Walter Haidinger <walter.haidinger@gmx.at>
- Providing me with login access to a Linux installation where v1.12
- wouldn't compile, so I could develop the fixes for v1.13. Also, for
- providing the disc space so I can keep an independent backup of the
- sources.
+ Access to a Linux installation where v1.12 wouldn't compile
+ Disc space for an independent backup of the sources
Juergen Hannken-Illjes <hannken@eis.cs.tu-bs.de>
Port to NetBSD
@@ -170,7 +166,7 @@ John Hasler <john@dhh.gt.org>
Changes to support 64 bit machines (i.e. those where
sizeof(unsigned long) > 4)
Bug fix to initstepslew directive
- Fix to remove potential buffer overrun errors.
+ Fix to remove potential buffer overrun errors
Memory locking and real-time scheduler support
Fix fault where chronyd enters an endless loop
@@ -198,7 +194,7 @@ Victor Moroz <vim@prv.adlum.ru>
Patch to support Linux with HZ!=100
Kalle Olavi Niemitalo <tosi@stekt.oulu.fi>
- acquisitionport support
+ Patch to add acquisitionport directive
Frank Otto <sandwichmacher@web.de>
Handling arbitrary HZ values
@@ -206,12 +202,18 @@ Frank Otto <sandwichmacher@web.de>
Denny Page <dennypage@me.com>
Advice on support for hardware timestamping
+Chris Perl <cperl@janestreet.com>
+ Patches to improve support for refclocks keeping time in TAI
+
Gautier PHILIPPON <gautier.philippon@ensimag.grenoble-inp.fr>
Patch to add refresh command to chronyc
Andreas Piesk <apiesk@virbus.de>
Patch to make chronyc use the readline library if available
+Andreas Steinmetz <ast@domdv.de>
+ Patch to make stratum of refclocks configurable
+
Timo Teras <timo.teras@iki.fi>
Patch to reply correctly on multihomed hosts
@@ -228,8 +230,7 @@ Ralf Wildenhues <Ralf.Wildenhues@gmx.de>
Many robustness and security improvements
Ulrich Windl <ulrich.windl@rz.uni-regensburg.de> for the
- Providing me with information about the Linux 2.2 kernel
- functionality compared to 2.0.
+ Information about the Linux 2.2 kernel functionality compared to 2.0
Doug Woodward <dougw@whistler.com>
Advice on configuring for Solaris 2.8 on x86
diff --git a/candm.h b/candm.h
index b03448c..6ffbb39 100644
--- a/candm.h
+++ b/candm.h
@@ -99,7 +99,8 @@
#define REQ_ADD_PEER2 59
#define REQ_ADD_SERVER3 60
#define REQ_ADD_PEER3 61
-#define N_REQUEST_TYPES 62
+#define REQ_SHUTDOWN 62
+#define N_REQUEST_TYPES 63
/* Structure used to exchange timespecs independent of time_t size */
typedef struct {
@@ -252,6 +253,7 @@ typedef struct {
#define REQ_ADDSRC_TRUST 0x20
#define REQ_ADDSRC_REQUIRE 0x40
#define REQ_ADDSRC_INTERLEAVED 0x80
+#define REQ_ADDSRC_BURST 0x100
typedef struct {
IPAddr ip_addr;
@@ -367,9 +369,9 @@ typedef struct {
domain socket.
Version 6 (no authentication) : changed format of client accesses by index
- (using new request/reply types) and manual timestamp, new fields and flags
- in NTP source request and report, new commands: ntpdata, refresh,
- serverstats
+ (using new request/reply types) and manual timestamp, added new fields and
+ flags to NTP source request and report, made length of manual list constant,
+ added new commands: ntpdata, refresh, serverstats, shutdown
*/
#define PROTO_VERSION_NUMBER 6
@@ -468,7 +470,8 @@ typedef struct {
#define RPY_CLIENT_ACCESSES_BY_INDEX2 15
#define RPY_NTP_DATA 16
#define RPY_MANUAL_TIMESTAMP2 17
-#define N_REPLY_TYPES 18
+#define RPY_MANUAL_LIST2 18
+#define N_REPLY_TYPES 19
/* Status codes */
#define STT_SUCCESS 0
diff --git a/client.c b/client.c
index 5c3a99e..4fa7d2d 100644
--- a/client.c
+++ b/client.c
@@ -1109,6 +1109,7 @@ process_cmd_add_server_or_peer(CMD_Request *msg, char *line)
(data.params.auto_offline ? REQ_ADDSRC_AUTOOFFLINE : 0) |
(data.params.iburst ? REQ_ADDSRC_IBURST : 0) |
(data.params.interleaved ? REQ_ADDSRC_INTERLEAVED : 0) |
+ (data.params.burst ? REQ_ADDSRC_BURST : 0) |
(data.params.sel_options & SRC_SELECT_PREFER ? REQ_ADDSRC_PREFER : 0) |
(data.params.sel_options & SRC_SELECT_NOSELECT ? REQ_ADDSRC_NOSELECT : 0) |
(data.params.sel_options & SRC_SELECT_TRUST ? REQ_ADDSRC_TRUST : 0) |
@@ -1245,6 +1246,7 @@ give_help(void)
"cyclelogs\0Close and re-open log files\0"
"dump\0Dump all measurements to save files\0"
"rekey\0Re-read keys from key file\0"
+ "shutdown\0Stop daemon\0"
"\0\0"
"Client commands:\0\0"
"dns -n|+n\0Disable/enable resolving IP addresses to hostnames\0"
@@ -1279,9 +1281,9 @@ command_name_generator(const char *text, int state)
"maxdelay", "maxdelaydevratio", "maxdelayratio", "maxpoll",
"maxupdateskew", "minpoll", "minstratum", "ntpdata", "offline", "online",
"polltarget", "quit", "refresh", "rekey", "reselect", "reselectdist",
- "retries", "rtcdata", "serverstats", "settime", "smoothing", "smoothtime",
- "sources", "sources -v", "sourcestats", "sourcestats -v", "timeout",
- "tracking", "trimrtc", "waitsync", "writertc",
+ "retries", "rtcdata", "serverstats", "settime", "shutdown", "smoothing",
+ "smoothtime", "sources", "sources -v", "sourcestats", "sourcestats -v",
+ "timeout", "tracking", "trimrtc", "waitsync", "writertc",
NULL
};
static int list_index, len;
@@ -1324,18 +1326,16 @@ static int proto_version = PROTO_VERSION_NUMBER;
static int
submit_request(CMD_Request *request, CMD_Reply *reply)
{
- int bad_length, bad_sequence, bad_header;
int select_status;
int recv_status;
int read_length;
- int expected_length;
int command_length;
int padding_length;
struct timespec ts_now, ts_start;
struct timeval tv;
int n_attempts, new_attempt;
double timeout;
- fd_set rdfd, wrfd, exfd;
+ fd_set rdfd;
request->pkt_type = PKT_TYPE_CMD_REQUEST;
request->res1 = 0;
@@ -1347,15 +1347,15 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
new_attempt = 1;
do {
+ if (gettimeofday(&tv, NULL))
+ return 0;
+
if (new_attempt) {
new_attempt = 0;
if (n_attempts > max_retries)
return 0;
- if (gettimeofday(&tv, NULL))
- return 0;
-
UTI_TimevalToTimespec(&tv, &ts_start);
UTI_GetRandomBytes(&request->sequence, sizeof (request->sequence));
@@ -1383,9 +1383,6 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
DEBUG_LOG("Sent %d bytes", command_length);
}
- if (gettimeofday(&tv, NULL))
- return 0;
-
UTI_TimevalToTimespec(&tv, &ts_now);
/* Check if the clock wasn't stepped back */
@@ -1394,22 +1391,27 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
timeout = initial_timeout / 1000.0 * (1U << (n_attempts - 1)) -
UTI_DiffTimespecsToDouble(&ts_now, &ts_start);
- UTI_DoubleToTimeval(timeout, &tv);
DEBUG_LOG("Timeout %f seconds", timeout);
- FD_ZERO(&rdfd);
- FD_ZERO(&wrfd);
- FD_ZERO(&exfd);
+ /* Avoid calling select() with an invalid timeout */
+ if (timeout <= 0.0) {
+ new_attempt = 1;
+ continue;
+ }
+
+ UTI_DoubleToTimeval(timeout, &tv);
+ FD_ZERO(&rdfd);
FD_SET(sock_fd, &rdfd);
if (quit)
return 0;
- select_status = select(sock_fd + 1, &rdfd, &wrfd, &exfd, &tv);
+ select_status = select(sock_fd + 1, &rdfd, NULL, NULL, &tv);
if (select_status < 0) {
DEBUG_LOG("select failed : %s", strerror(errno));
+ return 0;
} else if (select_status == 0) {
/* Timeout must have elapsed, try a resend? */
new_attempt = 1;
@@ -1425,34 +1427,18 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
DEBUG_LOG("Received %d bytes", recv_status);
read_length = recv_status;
- if (read_length >= offsetof(CMD_Reply, data)) {
- expected_length = PKL_ReplyLength(reply);
- } else {
- expected_length = 0;
- }
-
- bad_length = (read_length < expected_length ||
- expected_length < offsetof(CMD_Reply, data));
-
- if (!bad_length) {
- bad_sequence = reply->sequence != request->sequence;
- } else {
- bad_sequence = 0;
- }
-
- if (bad_length || bad_sequence) {
- continue;
- }
-
- bad_header = ((reply->version != proto_version &&
- !(reply->version >= PROTO_VERSION_MISMATCH_COMPAT_CLIENT &&
- ntohs(reply->status) == STT_BADPKTVERSION)) ||
- (reply->pkt_type != PKT_TYPE_CMD_REPLY) ||
- (reply->res1 != 0) ||
- (reply->res2 != 0) ||
- (reply->command != request->command));
- if (bad_header) {
+ /* Check if the header is valid */
+ if (read_length < offsetof(CMD_Reply, data) ||
+ (reply->version != proto_version &&
+ !(reply->version >= PROTO_VERSION_MISMATCH_COMPAT_CLIENT &&
+ ntohs(reply->status) == STT_BADPKTVERSION)) ||
+ reply->pkt_type != PKT_TYPE_CMD_REPLY ||
+ reply->res1 != 0 ||
+ reply->res2 != 0 ||
+ reply->command != request->command ||
+ reply->sequence != request->sequence) {
+ DEBUG_LOG("Invalid reply");
continue;
}
@@ -1471,6 +1457,15 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
#error unknown compatibility with PROTO_VERSION - 1
#endif
+ /* Check that the packet contains all data it is supposed to have.
+ Unknown responses will always pass this test as their expected
+ length is zero. */
+ if (read_length < PKL_ReplyLength(reply)) {
+ DEBUG_LOG("Reply too short");
+ new_attempt = 1;
+ continue;
+ }
+
/* Good packet received, print out results */
DEBUG_LOG("Reply cmd=%d reply=%d stat=%d",
ntohs(reply->command), ntohs(reply->reply), ntohs(reply->status));
@@ -1577,6 +1572,9 @@ request_reply(CMD_Request *request, CMD_Reply *reply, int requested_reply, int v
return 0;
}
+ /* Make sure an unknown response was not requested */
+ assert(PKL_ReplyLength(reply));
+
return 1;
}
@@ -2540,7 +2538,7 @@ process_cmd_manual_list(const char *line)
struct timespec when;
request.command = htons(REQ_MANUAL_LIST);
- if (!request_reply(&request, &reply, RPY_MANUAL_LIST, 0))
+ if (!request_reply(&request, &reply, RPY_MANUAL_LIST2, 0))
return 0;
n_samples = ntohl(reply.data.manual_list.n_samples);
@@ -2548,7 +2546,7 @@ process_cmd_manual_list(const char *line)
print_header("# Date Time(UTC) Slewed Original Residual");
- for (i = 0; i < n_samples; i++) {
+ for (i = 0; i < n_samples && i < MAX_MANUAL_LIST_SAMPLES; i++) {
sample = &reply.data.manual_list.samples[i];
UTI_TimespecNetworkToHost(&sample->when, &when);
@@ -2709,6 +2707,14 @@ process_cmd_refresh(CMD_Request *msg, char *line)
/* ================================================== */
+static void
+process_cmd_shutdown(CMD_Request *msg, char *line)
+{
+ msg->command = htons(REQ_SHUTDOWN);
+}
+
+/* ================================================== */
+
static int
process_cmd_waitsync(char *line)
{
@@ -3004,6 +3010,8 @@ process_line(char *line)
} else if (!strcmp(command, "settime")) {
do_normal_submit = 0;
ret = process_cmd_settime(line);
+ } else if (!strcmp(command, "shutdown")) {
+ process_cmd_shutdown(&tx_message, line);
} else if (!strcmp(command, "smoothing")) {
do_normal_submit = 0;
ret = process_cmd_smoothing(line);
diff --git a/cmdmon.c b/cmdmon.c
index 4ed2189..3bd8d91 100644
--- a/cmdmon.c
+++ b/cmdmon.c
@@ -138,6 +138,7 @@ static const char permissions[] = {
PERMIT_AUTH, /* ADD_PEER2 */
PERMIT_AUTH, /* ADD_SERVER3 */
PERMIT_AUTH, /* ADD_PEER3 */
+ PERMIT_AUTH, /* SHUTDOWN */
};
/* ================================================== */
@@ -278,7 +279,6 @@ do_size_checks(void)
for (i = 1; i < N_REPLY_TYPES; i++) {
reply.reply = htons(i);
reply.status = STT_SUCCESS;
- reply.data.manual_list.n_samples = htonl(MAX_MANUAL_LIST_SAMPLES);
reply_length = PKL_ReplyLength(&reply);
if ((reply_length && reply_length < offsetof(CMD_Reply, data)) ||
reply_length > sizeof (CMD_Reply))
@@ -801,6 +801,7 @@ handle_add_source(NTP_Source_Type type, CMD_Request *rx_message, CMD_Reply *tx_m
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.sel_options =
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_PREFER ? SRC_SELECT_PREFER : 0) |
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_NOSELECT ? SRC_SELECT_NOSELECT : 0) |
@@ -1068,9 +1069,6 @@ handle_client_accesses_by_index(CMD_Request *rx_message, CMD_Reply *tx_message)
tx_message->reply = htons(RPY_CLIENT_ACCESSES_BY_INDEX2);
tx_message->data.client_accesses_by_index.n_indices = htonl(n_indices);
- memset(tx_message->data.client_accesses_by_index.clients, 0,
- sizeof (tx_message->data.client_accesses_by_index.clients));
-
for (i = req_first_index, j = 0; i < (uint32_t)n_indices && j < req_n_clients; i++) {
if (!CLG_GetClientAccessReportByIndex(i, &report, &now))
continue;
@@ -1103,10 +1101,11 @@ handle_manual_list(CMD_Request *rx_message, CMD_Reply *tx_message)
RPY_ManualListSample *sample;
RPT_ManualSamplesReport report[MAX_MANUAL_LIST_SAMPLES];
- tx_message->reply = htons(RPY_MANUAL_LIST);
+ tx_message->reply = htons(RPY_MANUAL_LIST2);
MNL_ReportSamples(report, MAX_MANUAL_LIST_SAMPLES, &n_samples);
tx_message->data.manual_list.n_samples = htonl(n_samples);
+
for (i=0; i<n_samples; i++) {
sample = &tx_message->data.manual_list.samples[i];
UTI_TimespecHostToNetwork(&report[i].when, &sample->when);
@@ -1239,6 +1238,15 @@ handle_ntp_data(CMD_Request *rx_message, CMD_Reply *tx_message)
}
/* ================================================== */
+
+static void
+handle_shutdown(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ LOG(LOGS_INFO, "Received shutdown command");
+ SCH_QuitProgram();
+}
+
+/* ================================================== */
/* Read a packet and process it */
static void
@@ -1329,19 +1337,14 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
expected_length = PKL_CommandLength(&rx_message);
rx_command = ntohs(rx_message.command);
+ memset(&tx_message, 0, sizeof (tx_message));
+
tx_message.version = PROTO_VERSION_NUMBER;
tx_message.pkt_type = PKT_TYPE_CMD_REPLY;
- tx_message.res1 = 0;
- tx_message.res2 = 0;
tx_message.command = rx_message.command;
tx_message.reply = htons(RPY_NULL);
tx_message.status = htons(STT_SUCCESS);
- tx_message.pad1 = 0;
- tx_message.pad2 = 0;
- tx_message.pad3 = 0;
tx_message.sequence = rx_message.sequence;
- tx_message.pad4 = 0;
- tx_message.pad5 = 0;
if (rx_message.version != PROTO_VERSION_NUMBER) {
DEBUG_LOG("Command packet has invalid version (%d != %d)",
@@ -1629,6 +1632,10 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
handle_ntp_data(&rx_message, &tx_message);
break;
+ case REQ_SHUTDOWN:
+ handle_shutdown(&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 c4e26bf..a228530 100644
--- a/cmdparse.c
+++ b/cmdparse.c
@@ -51,6 +51,7 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
src->params.online = 1;
src->params.auto_offline = 0;
src->params.presend_minpoll = SRC_DEFAULT_PRESEND_MINPOLL;
+ src->params.burst = 0;
src->params.iburst = 0;
src->params.min_stratum = SRC_DEFAULT_MINSTRATUM;
src->params.poll_target = SRC_DEFAULT_POLLTARGET;
@@ -84,6 +85,8 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
if (!strcasecmp(cmd, "auto_offline")) {
src->params.auto_offline = 1;
+ } else if (!strcasecmp(cmd, "burst")) {
+ src->params.burst = 1;
} else if (!strcasecmp(cmd, "iburst")) {
src->params.iburst = 1;
} else if (!strcasecmp(cmd, "offline")) {
diff --git a/conf.c b/conf.c
index 8a7080c..3859d74 100644
--- a/conf.c
+++ b/conf.c
@@ -681,7 +681,7 @@ static void
parse_refclock(char *line)
{
int n, poll, dpoll, filter_length, pps_rate, min_samples, max_samples, sel_options;
- int max_lock_age, pps_forced;
+ int max_lock_age, pps_forced, stratum, tai;
uint32_t ref_id, lock_ref_id;
double offset, delay, precision, max_dispersion, pulse_width;
char *p, *cmd, *name, *param;
@@ -704,6 +704,8 @@ parse_refclock(char *line)
ref_id = 0;
max_lock_age = 2;
lock_ref_id = 0;
+ stratum = 0;
+ tai = 0;
if (!*line) {
command_parse_error();
@@ -774,6 +776,13 @@ parse_refclock(char *line)
} else if (!strcasecmp(cmd, "maxdispersion")) {
if (sscanf(line, "%lf%n", &max_dispersion, &n) != 1)
break;
+ } else if (!strcasecmp(cmd, "stratum")) {
+ if (sscanf(line, "%d%n", &stratum, &n) != 1 ||
+ stratum >= NTP_MAX_STRATUM || stratum < 0)
+ break;
+ } else if (!strcasecmp(cmd, "tai")) {
+ n = 0;
+ tai = 1;
} else if (!strcasecmp(cmd, "width")) {
if (sscanf(line, "%lf%n", &pulse_width, &n) != 1)
break;
@@ -811,6 +820,8 @@ parse_refclock(char *line)
refclock->min_samples = min_samples;
refclock->max_samples = max_samples;
refclock->sel_options = sel_options;
+ refclock->stratum = stratum;
+ refclock->tai = tai;
refclock->offset = offset;
refclock->delay = delay;
refclock->precision = precision;
@@ -1335,7 +1346,11 @@ parse_include(char *line)
check_number_of_args(line, 1);
- if ((r = glob(line, GLOB_ERR | GLOB_NOMAGIC, NULL, &gl)) != 0) {
+ if ((r = glob(line,
+#ifdef GLOB_NOMAGIC
+ GLOB_NOMAGIC |
+#endif
+ GLOB_ERR, NULL, &gl)) != 0) {
if (r != GLOB_NOMATCH)
LOG_FATAL("Could not search for files matching %s", line);
diff --git a/configure b/configure
index 45eddf1..a97ac78 100755
--- a/configure
+++ b/configure
@@ -85,6 +85,7 @@ For better control, use the options below.
--with-readline-library=DIR Specify where readline lib directory is
--with-ncurses-library=DIR Specify where ncurses lib directory is
--disable-sechash Disable support for hashes other than MD5
+ --without-nettle Don't use nettle even if it is available
--without-nss Don't use NSS even if it is available
--without-tomcrypt Don't use libtomcrypt even if it is available
--disable-cmdmon Disable command and monitoring support
@@ -198,6 +199,7 @@ feat_readline=1
try_readline=1
try_editline=1
feat_sechash=1
+try_nettle=1
try_nss=1
try_tomcrypt=1
feat_rtc=1
@@ -360,6 +362,9 @@ do
--disable-sechash )
feat_sechash=0
;;
+ --without-nettle )
+ try_nettle=0
+ ;;
--without-nss )
try_nss=0
;;
@@ -550,7 +555,11 @@ then
split_seconds=$ntp_era_split
split_days=0
else
- split_seconds=`date '+%s'`
+ if [ "x$SOURCE_DATE_EPOCH" != "x" ]; then
+ split_seconds=$SOURCE_DATE_EPOCH
+ else
+ split_seconds=`date '+%s'`
+ fi
if [ "x$split_seconds" = "x" ]; then
echo "error: could not get current time, --with-ntp-era option is needed"
exit 1
@@ -696,6 +705,7 @@ then
struct scm_ts_pktinfo pktinfo;
pktinfo.if_index = pktinfo.pkt_length = 0;
return pktinfo.if_index + pktinfo.pkt_length + HWTSTAMP_FILTER_NTP_ALL +
+ SCM_TIMESTAMPING_PKTINFO +
SOF_TIMESTAMPING_OPT_PKTINFO + SOF_TIMESTAMPING_OPT_TX_SWHW;'; then
add_def HAVE_LINUX_TIMESTAMPING_RXFILTER_NTP 1
add_def HAVE_LINUX_TIMESTAMPING_OPT_PKTINFO 1
@@ -852,7 +862,22 @@ fi
HASH_OBJ="hash_intmd5.o"
HASH_LINK=""
-if [ $feat_sechash = "1" ] && [ $try_nss = "1" ]; then
+if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_nettle = "1" ]; then
+ test_cflags="`pkg_config --cflags nettle`"
+ test_link="`pkg_config --libs nettle`"
+ if test_code 'nettle' 'nettle/nettle-meta.h nettle/sha2.h' \
+ "$test_cflags" "$test_link" \
+ 'return nettle_hashes[0]->context_size;'
+ then
+ HASH_OBJ="hash_nettle.o"
+ HASH_LINK="$test_link"
+ LIBS="$LIBS $HASH_LINK"
+ MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
+ add_def FEAT_SECHASH
+ fi
+fi
+
+if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_nss = "1" ]; then
test_cflags="`pkg_config --cflags nss`"
test_link="`pkg_config --libs-only-L nss` -lfreebl3"
if test_code 'NSS' 'nss.h hasht.h nsslowhash.h' \
diff --git a/doc/chrony.conf.adoc b/doc/chrony.conf.adoc
index d72b943..d89b529 100644
--- a/doc/chrony.conf.adoc
+++ b/doc/chrony.conf.adoc
@@ -66,33 +66,40 @@ server, or its IP address. The *server* directive supports the following
options:
+
*minpoll* _poll_:::
-Although *chronyd* will trim the rate at which it samples the server during
-normal operation, the user might want to constrain the minimum polling interval.
-This is always defined as a power of 2, so *minpoll 5* would mean that the
-polling interval cannot drop below 32 seconds. The default is 6 (64 seconds),
-the minimum is -4 (1/16th of a second), and the maximum is 24 (6 months). Note
-that intervals shorter than 6 (64 seconds) should generally not be used with
-public servers on the Internet, because it might be considered abuse.
+This option specifies the minimum interval between requests sent to the server
+as a power of 2 in seconds. For example, *minpoll 5* would mean that the
+polling interval should not drop below 32 seconds. The default is 6 (64
+seconds), the minimum is -4 (1/16th of a second), and the maximum is 24 (6
+months). Note that intervals shorter than 6 (64 seconds) should generally not
+be used with public servers on the Internet, because it might be considered
+abuse.
*maxpoll* _poll_:::
-In a similar way, the user might want to constrain the maximum polling interval.
-Again this is specified as a power of 2, *maxpoll 9* indicates that the polling
-interval must stay at or below 512 seconds. The default is 10 (1024 seconds),
-the minimum is 0 (1 second), and the maximum is 24 (6 months).
+This option specifies the maximum interval between requests sent to the server
+as a power of 2 in seconds. For example, *maxpoll 9* indicates that the polling
+interval should stay at or below 9 (512 seconds). The default is 10 (1024
+seconds), the minimum is 0 (1 second), and the maximum is 24 (6 months).
*iburst*:::
-If this option is set, the interval between the first four polls will be 2
-seconds instead of _minpoll_. This is useful to quickly get the first update of
-the clock after *chronyd* is started.
-*key* _id_:::
+With this option, the interval between the first four requests sent to the
+server will be 2 seconds instead of the interval specified by the *minpoll*
+option, which allows *chronyd* to make the first update of the clock shortly
+after start.
+*burst*:::
+With this option, *chronyd* will shorten the interval between up to four
+requests to 2 seconds when it cannot get a good measurement from the server.
+The number of requests in the burst is limited by the current polling interval
+to keep the average interval at or above the minimum interval, i.e. the current
+interval needs to be at least two times longer than the minimum interval in
+order to allow a burst with two requests.
+*key* _ID_:::
The NTP protocol supports the inclusion of checksums in the packets, to prevent
computers having their system time upset by rogue packets being sent to them.
The checksums are generated as a function of a password, using the
cryptographic hash function set in the key file, which is specified by the
<<keyfile,*keyfile*>> directive.
+
-If the key option is present, *chronyd* will attempt to use authenticated
-packets when communicating with this server. The key number used will be the
-single argument to the key option (an unsigned integer in the range 1 through
-2^32-1). The server must have the same password for this key number configured,
+The *key* option specifies which key (with an ID in the range 1 through 2^32-1)
+should *chronyd* use to authenticate requests sent to the server and verify its
+responses. The server must have the same key for this number configured,
otherwise no relationship between the computers will be possible.
*maxdelay* _delay_:::
*chronyd* uses the network round-trip delay to the server to determine how
@@ -113,7 +120,7 @@ option. For example, *maxdelay 0.3* would indicate that measurements with a
round-trip delay of 0.3 seconds or more should be ignored. The default value is
3 seconds and the maximum value is 1000 seconds.
*maxdelayratio* _ratio_:::
-This option is similar to the maxdelay option above. *chronyd* keeps a record
+This option is similar to the *maxdelay* option above. *chronyd* keeps a record
of the minimum round-trip delay amongst the previous measurements that it has
buffered. If a measurement has a round trip delay that is greater than the
maxdelayratio times the minimum delay, it will be rejected.
@@ -123,14 +130,14 @@ minimum delay amongst the previous measurements to the standard deviation of
the previous measurements that is greater than the specified ratio, it will be
rejected. The default is 10.0.
*mindelay* _delay_:::
-This options specifies a fixed minimum round-trip delay to be used instead of
+This option specifies a fixed minimum round-trip delay to be used instead of
the minimum amongst the previous measurements. This can be useful in networks
with static configuration to improve the stability of corrections for
asymmetric jitter, weighting of the measurements, and the *maxdelayratio* and
*maxdelaydevratio* tests. The value should be set accurately in order to have a
positive effect on the synchronisation.
*asymmetry* _ratio_:::
-This options specifies the asymmetry of the network jitter on the path to the
+This option specifies the asymmetry of the network jitter on the path to the
source, which is used to correct the measured offset according to the delay.
The asymmetry can be between -0.5 and +0.5. A negative value means the delay of
packets sent to the source is more variable than the delay of packets sent from
@@ -154,14 +161,15 @@ option can be specified. *chronyd* will not try to poll the server until it is
enabled to do so (by using the <<chronyc.adoc#online,*online*>> command in
*chronyc*).
*auto_offline*:::
-If this option is set, the server will be assumed to have gone offline when 2
+With this option, the server will be assumed to have gone offline when two
requests have been sent to it without receiving a response. This option avoids
the need to run the <<chronyc.adoc#offline,*offline*>> command from *chronyc*
-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.)
+when disconnecting the network link, if it is safe to assume that the requests
+and responses will not be dropped in the network, e.g. in a trusted local
+network. (It will still be necessary to use the <<chronyc.adoc#online,*online*>>
+command when the link has been established, to enable measurements to start.)
*prefer*:::
-Prefer this source over sources without prefer option.
+Prefer this source over sources without the *prefer* option.
*noselect*:::
Never select this source. This is particularly useful for monitoring.
*trust*:::
@@ -500,7 +508,7 @@ This option specifies the width of the pulses (in seconds). It is used to
filter PPS samples when the driver provides samples for both rising and falling
edges. Note that it reduces the maximum allowed error of the time source which
completes the PPS samples. If the duty cycle is configurable, 50% should be
-prefered in order to maximise the allowed error.
+preferred in order to maximise the allowed error.
*pps*:::
This options forces *chronyd* to treat any refclock (e.g. SHM or PHC) as a PPS
refclock. This can be useful when the refclock provides time with a variable
@@ -516,6 +524,9 @@ is included in the maximum assumed error which is used in the source selection
algorithm. Increasing the delay is useful to avoid having no majority in the
source selection or to make it prefer other sources. The default is 1e-9 (1
nanosecond).
+*stratum* _stratum_:::
+This option sets the NTP stratum of the refclock. This can be useful when the
+refclock provides time with a stratum other than 0. The default is 0.
*precision* _precision_:::
This option sets the precision of the reference clock (in seconds). The default
value is the estimated precision of the system clock.
@@ -546,6 +557,12 @@ but not very precise, reference clock to be safely combined with
unauthenticated NTP sources in order to improve the accuracy of the clock. They
can be selected and used for synchronisation only if they agree with the
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.
*minsamples* _samples_:::
Set the minimum number of samples kept for this source. This overrides the
<<minsamples,*minsamples*>> directive.
@@ -862,6 +879,11 @@ It is also useful when the system clock is required to have correct TAI-UTC
offset. Note that the offset is set only when leap seconds are handled by the
kernel, i.e. <<leapsecmode,*leapsecmode*>> is set to *system*.
+
+The specified timezone is not used as an exclusive source of information about
+leap seconds. If a majority of time sources announce on the last day of June or
+December that a leap second should be inserted or deleted, it will be accepted
+even if it is not included in the timezone.
++
An example of the directive is:
+
----
@@ -1985,12 +2007,18 @@ format of the file is shown below:
+
Each line consists of an ID, name of an authentication hash function (optional),
and a password. The ID can be any unsigned integer in the range 1 through
-2^32-1. The default hash function is *MD5*. Depending on how *chronyd*
-was compiled, other supported functions might be *SHA1*, *SHA256*, *SHA384*,
-*SHA512*, *RMD128*, *RMD160*, *RMD256*, *RMD320*, *TIGER*, and *WHIRLPOOL*. The
-password can be specified as a string of characters not containing white space
-with an optional *ASCII:* prefix, or as a hexadecimal number with the *HEX:*
-prefix. The maximum length of the line is 2047 characters.
+2^32-1. The default hash function is *MD5*, which is always supported.
++
+If *chronyd* was built with enabled support for hashing using a crypto library
+(nettle, nss, or libtomcrypt), the following functions are available: *MD5*,
+*SHA1*, *SHA256*, *SHA384*, *SHA512*. Depending on which library and version is
+*chronyd* using, some or all of the following functions may also be available:
+*SHA3-224*, *SHA3-256*, *SHA3-384*, *SHA3-512*, *RMD128*, *RMD160*, *RMD256*,
+*RMD320*, *TIGER*, *WHIRLPOOL*.
++
+The password can be specified as a string of characters not containing white
+space with an optional *ASCII:* prefix, or as a hexadecimal number with the
+*HEX:* prefix. The maximum length of the line is 2047 characters.
+
The password is used with the hash function to generate and verify a message
authentication code (MAC) in NTP packets. It is recommended to use SHA1, or
diff --git a/doc/chrony.conf.man.in b/doc/chrony.conf.man.in
index accbe8f..9f65136 100644
--- a/doc/chrony.conf.man.in
+++ b/doc/chrony.conf.man.in
@@ -1,13 +1,13 @@
'\" t
.\" Title: chrony.conf
.\" Author: [see the "AUTHORS" section]
-.\" Generator: Asciidoctor 1.5.4
-.\" Date: 2017-08-29
+.\" Generator: Asciidoctor 1.5.6.1
+.\" Date: 2018-03-15
.\" Manual: Configuration Files
.\" Source: chrony @CHRONY_VERSION@
.\" Language: English
.\"
-.TH "CHRONY.CONF" "5" "2017-08-29" "chrony @CHRONY_VERSION@" "Configuration Files"
+.TH "CHRONY.CONF" "5" "2018-03-15" "chrony @CHRONY_VERSION@" "Configuration Files"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
@@ -60,31 +60,42 @@ options:
.sp
\fBminpoll\fP \fIpoll\fP
.RS 4
-Although \fBchronyd\fP will trim the rate at which it samples the server during
-normal operation, the user might want to constrain the minimum polling interval.
-This is always defined as a power of 2, so \fBminpoll 5\fP would mean that the
-polling interval cannot drop below 32 seconds. The default is 6 (64 seconds),
-the minimum is \-4 (1/16th of a second), and the maximum is 24 (6 months). Note
-that intervals shorter than 6 (64 seconds) should generally not be used with
-public servers on the Internet, because it might be considered abuse.
+This option specifies the minimum interval between requests sent to the server
+as a power of 2 in seconds. For example, \fBminpoll 5\fP would mean that the
+polling interval should not drop below 32 seconds. The default is 6 (64
+seconds), the minimum is \-4 (1/16th of a second), and the maximum is 24 (6
+months). Note that intervals shorter than 6 (64 seconds) should generally not
+be used with public servers on the Internet, because it might be considered
+abuse.
.RE
.sp
\fBmaxpoll\fP \fIpoll\fP
.RS 4
-In a similar way, the user might want to constrain the maximum polling interval.
-Again this is specified as a power of 2, \fBmaxpoll 9\fP indicates that the polling
-interval must stay at or below 512 seconds. The default is 10 (1024 seconds),
-the minimum is 0 (1 second), and the maximum is 24 (6 months).
+This option specifies the maximum interval between requests sent to the server
+as a power of 2 in seconds. For example, \fBmaxpoll 9\fP indicates that the polling
+interval should stay at or below 9 (512 seconds). The default is 10 (1024
+seconds), the minimum is 0 (1 second), and the maximum is 24 (6 months).
.RE
.sp
\fBiburst\fP
.RS 4
-If this option is set, the interval between the first four polls will be 2
-seconds instead of \fIminpoll\fP. This is useful to quickly get the first update of
-the clock after \fBchronyd\fP is started.
+With this option, the interval between the first four requests sent to the
+server will be 2 seconds instead of the interval specified by the \fBminpoll\fP
+option, which allows \fBchronyd\fP to make the first update of the clock shortly
+after start.
.RE
.sp
-\fBkey\fP \fIid\fP
+\fBburst\fP
+.RS 4
+With this option, \fBchronyd\fP will shorten the interval between up to four
+requests to 2 seconds when it cannot get a good measurement from the server.
+The number of requests in the burst is limited by the current polling interval
+to keep the average interval at or above the minimum interval, i.e. the current
+interval needs to be at least two times longer than the minimum interval in
+order to allow a burst with two requests.
+.RE
+.sp
+\fBkey\fP \fIID\fP
.RS 4
The NTP protocol supports the inclusion of checksums in the packets, to prevent
computers having their system time upset by rogue packets being sent to them.
@@ -92,10 +103,9 @@ The checksums are generated as a function of a password, using the
cryptographic hash function set in the key file, which is specified by the
\fBkeyfile\fP directive.
.sp
-If the key option is present, \fBchronyd\fP will attempt to use authenticated
-packets when communicating with this server. The key number used will be the
-single argument to the key option (an unsigned integer in the range 1 through
-2^32\-1). The server must have the same password for this key number configured,
+The \fBkey\fP option specifies which key (with an ID in the range 1 through 2^32\-1)
+should \fBchronyd\fP use to authenticate requests sent to the server and verify its
+responses. The server must have the same key for this number configured,
otherwise no relationship between the computers will be possible.
.RE
.sp
@@ -122,7 +132,7 @@ round\-trip delay of 0.3 seconds or more should be ignored. The default value is
.sp
\fBmaxdelayratio\fP \fIratio\fP
.RS 4
-This option is similar to the maxdelay option above. \fBchronyd\fP keeps a record
+This option is similar to the \fBmaxdelay\fP option above. \fBchronyd\fP keeps a record
of the minimum round\-trip delay amongst the previous measurements that it has
buffered. If a measurement has a round trip delay that is greater than the
maxdelayratio times the minimum delay, it will be rejected.
@@ -138,7 +148,7 @@ rejected. The default is 10.0.
.sp
\fBmindelay\fP \fIdelay\fP
.RS 4
-This options specifies a fixed minimum round\-trip delay to be used instead of
+This option specifies a fixed minimum round\-trip delay to be used instead of
the minimum amongst the previous measurements. This can be useful in networks
with static configuration to improve the stability of corrections for
asymmetric jitter, weighting of the measurements, and the \fBmaxdelayratio\fP and
@@ -148,7 +158,7 @@ positive effect on the synchronisation.
.sp
\fBasymmetry\fP \fIratio\fP
.RS 4
-This options specifies the asymmetry of the network jitter on the path to the
+This option specifies the asymmetry of the network jitter on the path to the
source, which is used to correct the measured offset according to the delay.
The asymmetry can be between \-0.5 and +0.5. A negative value means the delay of
packets sent to the source is more variable than the delay of packets sent from
@@ -187,17 +197,18 @@ enabled to do so (by using the \fBonline\fP command in
.sp
\fBauto_offline\fP
.RS 4
-If this option is set, the server will be assumed to have gone offline when 2
+With this option, the server will be assumed to have gone offline when two
requests have been sent to it without receiving a response. This option avoids
the need to run the \fBoffline\fP command from \fBchronyc\fP
-when disconnecting the network link. (It will still be necessary to use the
-\fBonline\fP command when the link has been established, to
-enable measurements to start.)
+when disconnecting the network link, if it is safe to assume that the requests
+and responses will not be dropped in the network, e.g. in a trusted local
+network. (It will still be necessary to use the \fBonline\fP
+command when the link has been established, to enable measurements to start.)
.RE
.sp
\fBprefer\fP
.RS 4
-Prefer this source over sources without prefer option.
+Prefer this source over sources without the \fBprefer\fP option.
.RE
.sp
\fBnoselect\fP
@@ -669,7 +680,7 @@ This option specifies the width of the pulses (in seconds). It is used to
filter PPS samples when the driver provides samples for both rising and falling
edges. Note that it reduces the maximum allowed error of the time source which
completes the PPS samples. If the duty cycle is configurable, 50% should be
-prefered in order to maximise the allowed error.
+preferred in order to maximise the allowed error.
.RE
.sp
\fBpps\fP
@@ -696,6 +707,12 @@ source selection or to make it prefer other sources. The default is 1e\-9 (1
nanosecond).
.RE
.sp
+\fBstratum\fP \fIstratum\fP
+.RS 4
+This option sets the NTP stratum of the refclock. This can be useful when the
+refclock provides time with a stratum other than 0. The default is 0.
+.RE
+.sp
\fBprecision\fP \fIprecision\fP
.RS 4
This option sets the precision of the reference clock (in seconds). The default
@@ -747,6 +764,15 @@ can be selected and used for synchronisation only if they agree with the
trusted and required source.
.RE
.sp
+\fBtai\fP
+.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.
+.RE
+.sp
\fBminsamples\fP \fIsamples\fP
.RS 4
Set the minimum number of samples kept for this source. This overrides the
@@ -1144,6 +1170,11 @@ It is also useful when the system clock is required to have correct TAI\-UTC
offset. Note that the offset is set only when leap seconds are handled by the
kernel, i.e. \fBleapsecmode\fP is set to \fBsystem\fP.
.sp
+The specified timezone is not used as an exclusive source of information about
+leap seconds. If a majority of time sources announce on the last day of June or
+December that a leap second should be inserted or deleted, it will be accepted
+even if it is not included in the timezone.
+.sp
An example of the directive is:
.sp
.if n \{\
@@ -3363,12 +3394,18 @@ format of the file is shown below:
.sp
Each line consists of an ID, name of an authentication hash function (optional),
and a password. The ID can be any unsigned integer in the range 1 through
-2^32\-1. The default hash function is \fBMD5\fP. Depending on how \fBchronyd\fP
-was compiled, other supported functions might be \fBSHA1\fP, \fBSHA256\fP, \fBSHA384\fP,
-\fBSHA512\fP, \fBRMD128\fP, \fBRMD160\fP, \fBRMD256\fP, \fBRMD320\fP, \fBTIGER\fP, and \fBWHIRLPOOL\fP. The
-password can be specified as a string of characters not containing white space
-with an optional \fBASCII:\fP prefix, or as a hexadecimal number with the \fBHEX:\fP
-prefix. The maximum length of the line is 2047 characters.
+2^32\-1. The default hash function is \fBMD5\fP, which is always supported.
+.sp
+If \fBchronyd\fP was built with enabled support for hashing using a crypto library
+(nettle, nss, or libtomcrypt), the following functions are available: \fBMD5\fP,
+\fBSHA1\fP, \fBSHA256\fP, \fBSHA384\fP, \fBSHA512\fP. Depending on which library and version is
+\fBchronyd\fP using, some or all of the following functions may also be available:
+\fBSHA3\-224\fP, \fBSHA3\-256\fP, \fBSHA3\-384\fP, \fBSHA3\-512\fP, \fBRMD128\fP, \fBRMD160\fP, \fBRMD256\fP,
+\fBRMD320\fP, \fBTIGER\fP, \fBWHIRLPOOL\fP.
+.sp
+The password can be specified as a string of characters not containing white
+space with an optional \fBASCII:\fP prefix, or as a hexadecimal number with the
+\fBHEX:\fP prefix. The maximum length of the line is 2047 characters.
.sp
The password is used with the hash function to generate and verify a message
authentication code (MAC) in NTP packets. It is recommended to use SHA1, or
diff --git a/doc/chronyc.adoc b/doc/chronyc.adoc
index 9c5ac5c..c987907 100644
--- a/doc/chronyc.adoc
+++ b/doc/chronyc.adoc
@@ -1128,6 +1128,10 @@ running.
The *rekey* command causes *chronyd* to re-read the key file specified in the
configuration file by the <<chrony.conf.adoc#keyfile,*keyfile*>> directive.
+[[rekey]]*shutdown*::
+The *shutdown* command causes *chronyd* to exit. This is equivalent to sending
+the process the SIGTERM signal.
+
=== Client commands
[[dns]]*dns* _option_::
diff --git a/doc/chronyc.man.in b/doc/chronyc.man.in
index 40d24b9..581fa00 100644
--- a/doc/chronyc.man.in
+++ b/doc/chronyc.man.in
@@ -1,13 +1,13 @@
'\" t
.\" Title: chronyc
.\" Author: [see the "AUTHORS" section]
-.\" Generator: Asciidoctor 1.5.4
-.\" Date: 2017-08-29
+.\" Generator: Asciidoctor 1.5.6.1
+.\" Date: 2018-03-15
.\" Manual: User manual
.\" Source: chrony @CHRONY_VERSION@
.\" Language: English
.\"
-.TH "CHRONYC" "1" "2017-08-29" "chrony @CHRONY_VERSION@" "User manual"
+.TH "CHRONYC" "1" "2018-03-15" "chrony @CHRONY_VERSION@" "User manual"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
@@ -1787,6 +1787,12 @@ running.
The \fBrekey\fP command causes \fBchronyd\fP to re\-read the key file specified in the
configuration file by the \fBkeyfile\fP directive.
.RE
+.sp
+\fBshutdown\fP
+.RS 4
+The \fBshutdown\fP command causes \fBchronyd\fP to exit. This is equivalent to sending
+the process the SIGTERM signal.
+.RE
.SS "Client commands"
.sp
\fBdns\fP \fIoption\fP
diff --git a/doc/chronyd.adoc b/doc/chronyd.adoc
index 7b00865..fc79078 100644
--- a/doc/chronyd.adoc
+++ b/doc/chronyd.adoc
@@ -135,7 +135,7 @@ range of privileged system calls on behalf of the parent.
*-F* _level_::
This option configures a system call filter when *chronyd* is compiled with
support for the Linux secure computing (seccomp) facility. In level 1 the
-process is killed when a forbidden system call is made, in level -1 the SYSSIG
+process is killed when a forbidden system call is made, in level -1 the SIGSYS
signal is thrown instead and in level 0 the filter is disabled (default 0).
+
It's recommended to enable the filter only when it's known to work on the
@@ -157,11 +157,13 @@ This option will lock *chronyd* into RAM so that it will never be paged out.
This mode is only supported on Linux.
*-x*::
-This option disables the control of the system clock. *chronyd* will not make
-any adjustments of the clock, but it will still track its offset and frequency
-relative to the estimated true time, and be able to operate as an NTP server.
-This allows *chronyd* to run without the capability to adjust or set the system
-clock (e.g. in some containers).
+This option disables the control of the system clock. *chronyd* will not try to
+make any adjustments of the clock. It will assume the clock is free running and
+still track its offset and frequency relative to the estimated true time. This
+option allows *chronyd* to run without the capability to adjust or set the
+system clock (e.g. in some containers) in order to operate as an NTP server. It
+is not recommended to run *chronyd* (with or without *-x*) when another process
+is controlling the system clock.
*-v*::
With this option *chronyd* will print version number to the terminal and exit.
diff --git a/doc/chronyd.man.in b/doc/chronyd.man.in
index 04b6701..f1f7c90 100644
--- a/doc/chronyd.man.in
+++ b/doc/chronyd.man.in
@@ -1,13 +1,13 @@
'\" t
.\" Title: chronyd
.\" Author: [see the "AUTHORS" section]
-.\" Generator: Asciidoctor 1.5.4
-.\" Date: 2017-08-29
+.\" Generator: Asciidoctor 1.5.6.1
+.\" Date: 2018-03-15
.\" Manual: System Administration
.\" Source: chrony @CHRONY_VERSION@
.\" Language: English
.\"
-.TH "CHRONYD" "8" "2017-08-29" "chrony @CHRONY_VERSION@" "System Administration"
+.TH "CHRONYD" "8" "2018-03-15" "chrony @CHRONY_VERSION@" "System Administration"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
@@ -155,7 +155,7 @@ range of privileged system calls on behalf of the parent.
.RS 4
This option configures a system call filter when \fBchronyd\fP is compiled with
support for the Linux secure computing (seccomp) facility. In level 1 the
-process is killed when a forbidden system call is made, in level \-1 the SYSSIG
+process is killed when a forbidden system call is made, in level \-1 the SIGSYS
signal is thrown instead and in level 0 the filter is disabled (default 0).
.sp
It\(cqs recommended to enable the filter only when it\(cqs known to work on the
@@ -183,11 +183,13 @@ This mode is only supported on Linux.
.sp
\fB\-x\fP
.RS 4
-This option disables the control of the system clock. \fBchronyd\fP will not make
-any adjustments of the clock, but it will still track its offset and frequency
-relative to the estimated true time, and be able to operate as an NTP server.
-This allows \fBchronyd\fP to run without the capability to adjust or set the system
-clock (e.g. in some containers).
+This option disables the control of the system clock. \fBchronyd\fP will not try to
+make any adjustments of the clock. It will assume the clock is free running and
+still track its offset and frequency relative to the estimated true time. This
+option allows \fBchronyd\fP to run without the capability to adjust or set the
+system clock (e.g. in some containers) in order to operate as an NTP server. It
+is not recommended to run \fBchronyd\fP (with or without \fB\-x\fP) when another process
+is controlling the system clock.
.RE
.sp
\fB\-v\fP
diff --git a/examples/chrony.conf.example2 b/examples/chrony.conf.example2
index cbd065e..21be153 100644
--- a/examples/chrony.conf.example2
+++ b/examples/chrony.conf.example2
@@ -28,6 +28,9 @@ rtcsync
# Specify file containing keys for NTP authentication.
#keyfile /etc/chrony.keys
+# Get TAI-UTC offset and leap seconds from the system tz database.
+#leapsectz right/UTC
+
# Specify directory for log files.
logdir /var/log/chrony
diff --git a/examples/chrony.conf.example3 b/examples/chrony.conf.example3
index fdc656a..05a4e98 100644
--- a/examples/chrony.conf.example3
+++ b/examples/chrony.conf.example3
@@ -97,6 +97,12 @@ driftfile /var/lib/chrony/drift
! pidfile /var/run/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.
+
+! leapsectz right/UTC
+
#######################################################################
### INITIAL CLOCK CORRECTION
# This option is useful to quickly correct the clock on start if it's
diff --git a/examples/chrony.nm-dispatcher b/examples/chrony.nm-dispatcher
index 51d7fa2..a609a66 100644
--- a/examples/chrony.nm-dispatcher
+++ b/examples/chrony.nm-dispatcher
@@ -4,6 +4,8 @@
export LC_ALL=C
+[ "$2" != "up" ] && [ "$2" != "down" ] && exit 0
+
# Check if there is a default route
if /sbin/ip route list 2> /dev/null | grep -q '^default'; then
diff --git a/examples/chrony.spec b/examples/chrony.spec
index e919681..6f911c8 100644
--- a/examples/chrony.spec
+++ b/examples/chrony.spec
@@ -1,4 +1,4 @@
-%global chrony_version 3.2-pre2
+%global chrony_version 3.3-pre1
%if 0%(echo %{chrony_version} | grep -q pre && echo 1)
%global prerelease %(echo %{chrony_version} | sed 's/.*-//')
%endif
diff --git a/hash_nettle.c b/hash_nettle.c
new file mode 100644
index 0000000..2c3501d
--- /dev/null
+++ b/hash_nettle.c
@@ -0,0 +1,120 @@
+/*
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar 2018
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Routines implementing crypto hashing using the nettle library.
+
+ */
+
+#include "config.h"
+
+#include "sysincl.h"
+
+#include <nettle/nettle-meta.h>
+
+#include "hash.h"
+#include "memory.h"
+
+struct hash {
+ const char *name;
+ const char *int_name;
+ const struct nettle_hash *nettle_hash;
+ void *context;
+};
+
+static struct hash hashes[] = {
+ { "MD5", "md5", NULL, NULL },
+ { "RMD160", "ripemd160", NULL, NULL },
+ { "SHA1", "sha1", NULL, NULL },
+ { "SHA256", "sha256", NULL, NULL },
+ { "SHA384", "sha384", NULL, NULL },
+ { "SHA512", "sha512", NULL, NULL },
+ { "SHA3-224", "sha3_224", NULL, NULL },
+ { "SHA3-256", "sha3_256", NULL, NULL },
+ { "SHA3-384", "sha3_384", NULL, NULL },
+ { "SHA3-512", "sha3_512", NULL, NULL },
+ { NULL, NULL, NULL, NULL }
+};
+
+int
+HSH_GetHashId(const char *name)
+{
+ int id, nid;
+
+ for (id = 0; hashes[id].name; id++) {
+ if (!strcmp(name, hashes[id].name))
+ break;
+ }
+
+ if (!hashes[id].name)
+ return -1;
+
+ if (hashes[id].context)
+ return id;
+
+ for (nid = 0; nettle_hashes[nid]; nid++) {
+ if (!strcmp(hashes[id].int_name, nettle_hashes[nid]->name))
+ break;
+ }
+
+ if (!nettle_hashes[nid] || !nettle_hashes[nid]->context_size || !nettle_hashes[nid]->init)
+ return -1;
+
+ hashes[id].nettle_hash = nettle_hashes[nid];
+ hashes[id].context = Malloc(hashes[id].nettle_hash->context_size);
+
+ return id;
+}
+
+unsigned int
+HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len,
+ const unsigned char *in2, unsigned int in2_len,
+ unsigned char *out, unsigned int out_len)
+{
+ const struct nettle_hash *hash;
+ void *context;
+
+ hash = hashes[id].nettle_hash;
+ context = hashes[id].context;
+
+ if (out_len > hash->digest_size)
+ out_len = hash->digest_size;
+
+ hash->init(context);
+ hash->update(context, in1_len, in1);
+ if (in2)
+ hash->update(context, in2_len, in2);
+ hash->digest(context, out_len, out);
+
+ return out_len;
+}
+
+void
+HSH_Finalise(void)
+{
+ int i;
+
+ for (i = 0; hashes[i].name; i++) {
+ if (hashes[i].context)
+ Free(hashes[i].context);
+ }
+}
diff --git a/hash_tomcrypt.c b/hash_tomcrypt.c
index db2f9f0..5e16233 100644
--- a/hash_tomcrypt.c
+++ b/hash_tomcrypt.c
@@ -62,6 +62,12 @@ static const struct hash hashes[] = {
#ifdef LTC_SHA512
{ "SHA512", "sha512", &sha512_desc },
#endif
+#ifdef LTC_SHA3
+ { "SHA3-224", "sha3-224", &sha3_224_desc },
+ { "SHA3-256", "sha3-256", &sha3_256_desc },
+ { "SHA3-384", "sha3-384", &sha3_384_desc },
+ { "SHA3-512", "sha3-512", &sha3_512_desc },
+#endif
#ifdef LTC_TIGER
{ "TIGER", "tiger", &tiger_desc },
#endif
diff --git a/logging.c b/logging.c
index b39c4d5..7d9dbb8 100644
--- a/logging.c
+++ b/logging.c
@@ -79,11 +79,11 @@ LOG_Initialise(void)
void
LOG_Finalise(void)
{
- if (system_log) {
+ if (system_log)
closelog();
- } else {
+
+ if (file_log)
fclose(file_log);
- }
LOG_CycleLogFiles();
@@ -116,7 +116,7 @@ static void log_message(int fatal, LOG_Severity severity, const char *message)
assert(0);
}
syslog(priority, fatal ? "Fatal error : %s" : "%s", message);
- } else {
+ } else if (file_log) {
fprintf(file_log, fatal ? "Fatal error : %s\n" : "%s\n", message);
}
}
@@ -134,7 +134,7 @@ void LOG_Message(LOG_Severity severity,
time_t t;
struct tm stm;
- if (!system_log) {
+ if (!system_log && file_log) {
/* Don't clutter up syslog with timestamps and internal debugging info */
time(&t);
stm = *gmtime(&t);
@@ -160,16 +160,14 @@ void LOG_Message(LOG_Severity severity,
case LOGS_FATAL:
log_message(1, severity, buf);
- /* With syslog, send the message also to the grandparent
- process or write it to stderr if not detached */
- if (system_log) {
- if (parent_fd > 0) {
- if (write(parent_fd, buf, strlen(buf) + 1) < 0)
- ; /* Not much we can do here */
- } else if (parent_fd == 0) {
- system_log = 0;
- log_message(1, severity, buf);
- }
+ /* 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)
+ ; /* Not much we can do here */
+ } else if (system_log && parent_fd == 0) {
+ system_log = 0;
+ log_message(1, severity, buf);
}
break;
default:
@@ -220,6 +218,8 @@ void
LOG_SetParentFd(int fd)
{
parent_fd = fd;
+ if (file_log == stderr)
+ file_log = NULL;
}
/* ================================================== */
diff --git a/logging.h b/logging.h
index c50bcf5..5bb46f5 100644
--- a/logging.h
+++ b/logging.h
@@ -105,7 +105,7 @@ extern void LOG_OpenFileLog(const char *log_file);
/* Log messages to syslog instead of stderr */
extern void LOG_OpenSystemLog(void);
-/* Send fatal message also to the foreground process */
+/* 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 */
diff --git a/main.c b/main.c
index ab1b71a..a2202e9 100644
--- a/main.c
+++ b/main.c
@@ -292,6 +292,8 @@ write_pidfile(void)
/* ================================================== */
+#define DEV_NULL "/dev/null"
+
static void
go_daemon(void)
{
@@ -352,6 +354,13 @@ go_daemon(void)
}
LOG_SetParentFd(pipefd[1]);
+
+ /* Open /dev/null as new stdin/out/err */
+ errno = 0;
+ if (open(DEV_NULL, O_RDONLY) != STDIN_FILENO ||
+ open(DEV_NULL, O_WRONLY) != STDOUT_FILENO ||
+ open(DEV_NULL, O_RDWR) != STDERR_FILENO)
+ LOG_FATAL("Could not open %s : %s", DEV_NULL, strerror(errno));
}
}
}
@@ -524,6 +533,16 @@ int main
/* Write our pidfile to prevent other chronyds running */
write_pidfile();
+ if (!user)
+ user = CNF_GetUser();
+
+ pw = getpwnam(user);
+ if (!pw)
+ LOG_FATAL("Could not get user/group ID of %s", user);
+
+ /* Create directories for sockets, log files, and dump files */
+ CNF_CreateDirs(pw->pw_uid, pw->pw_gid);
+
PRV_Initialise();
LCL_Initialise();
SCH_Initialise();
@@ -551,16 +570,6 @@ int main
SYS_LockMemory();
}
- if (!user) {
- user = CNF_GetUser();
- }
-
- if ((pw = getpwnam(user)) == NULL)
- LOG_FATAL("Could not get %s uid/gid", user);
-
- /* Create all directories before dropping root */
- CNF_CreateDirs(pw->pw_uid, pw->pw_gid);
-
/* Drop root privileges if the specified user has a non-zero UID */
if (!geteuid() && (pw->pw_uid || pw->pw_gid))
SYS_DropRoot(pw->pw_uid, pw->pw_gid);
diff --git a/nameserv.c b/nameserv.c
index b5899e7..1cb9608 100644
--- a/nameserv.c
+++ b/nameserv.c
@@ -53,7 +53,19 @@ DNS_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs)
max_addrs = MIN(max_addrs, DNS_MAX_ADDRESSES);
memset(&hints, 0, sizeof (hints));
- hints.ai_family = AF_UNSPEC;
+
+ switch (address_family) {
+ case IPADDR_INET4:
+ hints.ai_family = AF_INET;
+ break;
+#ifdef FEAT_IPV6
+ case IPADDR_INET6:
+ hints.ai_family = AF_INET6;
+ break;
+#endif
+ default:
+ hints.ai_family = AF_UNSPEC;
+ }
hints.ai_socktype = SOCK_STREAM;
result = getaddrinfo(name, NULL, &hints, &res);
diff --git a/ntp_core.c b/ntp_core.c
index d73111f..a71ee43 100644
--- a/ntp_core.c
+++ b/ntp_core.c
@@ -88,6 +88,7 @@ struct NCR_Instance_Record {
SCH_TimeoutID tx_timeout_id; /* Timeout ID for next transmission */
int tx_suspended; /* Boolean indicating we can't transmit yet */
+ int auto_burst; /* If 1, initiate a burst on each poll */
int auto_offline; /* If 1, automatically go offline if server/peer
isn't responding */
@@ -149,14 +150,11 @@ struct NCR_Instance_Record {
be used for synchronisation */
int valid_timestamps;
- /* Flag indicating the timestamps below were updated since last request */
- int updated_timestamps;
-
- /* Receive and transmit timestamps from the last received packet */
+ /* Receive and transmit timestamps from the last valid response */
NTP_int64 remote_ntp_rx;
NTP_int64 remote_ntp_tx;
- /* Local timestamp when the last packet was received from the
+ /* Local timestamp when the last valid response was received from the
source. We have to be prepared to tinker with this if the local
clock has its frequency adjusted before we repond. The value we
store here is what our own local time was when the same arrived.
@@ -183,6 +181,15 @@ struct NCR_Instance_Record {
int prev_local_poll;
unsigned int prev_tx_count;
+ /* Flag indicating the two timestamps below were updated since the
+ last transmission */
+ int updated_init_timestamps;
+
+ /* Timestamps used for (re)starting the symmetric protocol, when we
+ need to respond to a packet which is not a valid response */
+ NTP_int64 init_remote_ntp_tx;
+ NTP_Local_Timestamp init_local_rx;
+
/* The instance record in the main source management module. This
performs the statistical analysis on the samples we generate */
@@ -230,6 +237,10 @@ static ARR_Instance broadcasts;
#define IBURST_GOOD_SAMPLES 4
#define IBURST_TOTAL_SAMPLES SOURCE_REACH_BITS
+/* Number of samples in automatic burst */
+#define BURST_GOOD_SAMPLES 1
+#define MAX_BURST_TOTAL_SAMPLES 4
+
/* Time to wait after sending packet to 'warm up' link */
#define WARM_UP_DELAY 2.0
@@ -551,6 +562,7 @@ NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourcePar
result->max_delay_ratio = CLAMP(0.0, params->max_delay_ratio, MAX_MAXDELAYRATIO);
result->max_delay_dev_ratio = CLAMP(0.0, params->max_delay_dev_ratio, MAX_MAXDELAYDEVRATIO);
result->offset_correction = params->offset;
+ result->auto_burst = params->burst;
result->auto_offline = params->auto_offline;
result->poll_target = params->poll_target;
@@ -652,7 +664,6 @@ NCR_ResetInstance(NCR_Instance instance)
instance->valid_rx = 0;
instance->valid_timestamps = 0;
- instance->updated_timestamps = 0;
UTI_ZeroNtp64(&instance->remote_ntp_rx);
UTI_ZeroNtp64(&instance->remote_ntp_tx);
UTI_ZeroNtp64(&instance->local_ntp_rx);
@@ -662,6 +673,10 @@ NCR_ResetInstance(NCR_Instance instance)
zero_local_timestamp(&instance->prev_local_tx);
instance->prev_local_poll = 0;
instance->prev_tx_count = 0;
+
+ instance->updated_init_timestamps = 0;
+ UTI_ZeroNtp64(&instance->init_remote_ntp_tx);
+ zero_local_timestamp(&instance->init_local_rx);
}
/* ================================================== */
@@ -890,8 +905,10 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
NTP_Local_Timestamp *local_rx, /* The RX time of the received packet */
NTP_Local_Timestamp *local_tx, /* The TX time of the previous packet
RESULT : TX time of this packet */
- NTP_int64 *local_ntp_rx, /* RESULT : receive timestamp from this packet */
- NTP_int64 *local_ntp_tx, /* RESULT : transmit timestamp from this packet */
+ NTP_int64 *local_ntp_rx, /* The receive timestamp from the previous packet
+ RESULT : receive timestamp from this packet */
+ NTP_int64 *local_ntp_tx, /* The transmit timestamp from the previous packet
+ RESULT : transmit timestamp from this packet */
NTP_Remote_Address *where_to, /* Where to address the reponse to */
NTP_Local_Address *from /* From what address to send it */
)
@@ -914,8 +931,8 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
version = NTP_VERSION;
}
- /* Allow interleaved mode only if there was a prior transmission */
- if (interleaved && (!local_tx || UTI_IsZeroTimespec(&local_tx->ts)))
+ /* Check if the packet can be formed in the interleaved mode */
+ if (interleaved && (!remote_ntp_rx || !local_tx || UTI_IsZeroTimespec(&local_tx->ts)))
interleaved = 0;
smooth_time = 0;
@@ -989,14 +1006,21 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
/* Originate - this comes from the last packet the source sent us */
message.originate_ts = interleaved ? *remote_ntp_rx : *remote_ntp_tx;
- /* Prepare random bits which will be added to the receive timestamp */
- UTI_GetNtp64Fuzz(&ts_fuzz, precision);
-
- /* Receive - this is when we received the last packet from the source.
- This timestamp will have been adjusted so that it will now look to
- the source like we have been running on our latest estimate of
- frequency all along */
- UTI_TimespecToNtp64(&local_receive, &message.receive_ts, &ts_fuzz);
+ do {
+ /* Prepare random bits which will be added to the receive timestamp */
+ UTI_GetNtp64Fuzz(&ts_fuzz, precision);
+
+ /* Receive - this is when we received the last packet from the source.
+ This timestamp will have been adjusted so that it will now look to
+ the source like we have been running on our latest estimate of
+ frequency all along */
+ UTI_TimespecToNtp64(&local_receive, &message.receive_ts, &ts_fuzz);
+
+ /* Do not send a packet with a non-zero receive timestamp equal to the
+ originate timestamp or previous receive timestamp */
+ } while (!UTI_IsZeroNtp64(&message.receive_ts) &&
+ UTI_IsEqualAnyNtp64(&message.receive_ts, &message.originate_ts,
+ local_ntp_rx, NULL));
} else {
UTI_ZeroNtp64(&message.originate_ts);
UTI_ZeroNtp64(&message.receive_ts);
@@ -1055,10 +1079,16 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
&message.transmit_ts, &ts_fuzz);
}
- /* Avoid sending messages with non-zero transmit timestamp equal to the
- receive timestamp to allow reliable detection of the interleaved mode */
- } while (!UTI_CompareNtp64(&message.transmit_ts, &message.receive_ts) &&
- !UTI_IsZeroNtp64(&message.transmit_ts));
+ /* Do not send a packet with a non-zero transmit timestamp which is
+ equal to any of the following timestamps:
+ - receive (to allow reliable detection of the interleaved mode)
+ - originate (to prevent the packet from being its own valid response
+ in the symmetric mode)
+ - previous transmit (to invalidate responses to the previous packet)
+ (the precision must be at least -30 to prevent an infinite loop!) */
+ } while (!UTI_IsZeroNtp64(&message.transmit_ts) &&
+ UTI_IsEqualAnyNtp64(&message.transmit_ts, &message.receive_ts,
+ &message.originate_ts, local_ntp_tx));
ret = NIO_SendPacket(&message, where_to, from, length, local_tx != NULL);
@@ -1084,7 +1114,7 @@ transmit_timeout(void *arg)
{
NCR_Instance inst = (NCR_Instance) arg;
NTP_Local_Address local_addr;
- int interleaved, sent;
+ int interleaved, initial, sent;
inst->tx_timeout_id = 0;
@@ -1093,10 +1123,20 @@ transmit_timeout(void *arg)
/* With online burst switch to online before last packet */
if (inst->burst_total_samples_to_go <= 1)
inst->opmode = MD_ONLINE;
+ break;
case MD_BURST_WAS_OFFLINE:
if (inst->burst_total_samples_to_go <= 0)
take_offline(inst);
break;
+ case MD_ONLINE:
+ /* Start a new burst if the burst option is enabled and the average
+ polling interval including the burst will not fall below the
+ minimum polling interval */
+ if (inst->auto_burst && inst->local_poll > inst->minpoll && inst->local_poll > 1)
+ NCR_InitiateSampleBurst(inst, BURST_GOOD_SAMPLES,
+ MIN(1 << (inst->local_poll - inst->minpoll),
+ MAX_BURST_TOTAL_SAMPLES));
+ break;
default:
break;
}
@@ -1137,6 +1177,19 @@ transmit_timeout(void *arg)
(inst->mode == MODE_ACTIVE &&
inst->prev_tx_count == 1 && inst->tx_count == 0));
+ /* In symmetric mode, if no valid response was received since the previous
+ transmission, respond to the last received packet even if it failed some
+ specific NTP tests. This is necessary for starting and restarting the
+ protocol, e.g. when a packet was lost. */
+ initial = inst->mode == MODE_ACTIVE && !inst->valid_rx &&
+ !UTI_IsZeroNtp64(&inst->init_remote_ntp_tx);
+
+ /* Prepare for the response */
+ inst->valid_rx = 0;
+ inst->updated_init_timestamps = 0;
+ if (initial)
+ inst->valid_timestamps = 0;
+
/* Check whether we need to 'warm up' the link to the other end by
sending an NTP exchange to ensure both ends' ARP caches are
primed or whether we need to send two packets first to ensure a
@@ -1148,18 +1201,16 @@ transmit_timeout(void *arg)
inst->presend_done--;
}
- sent = transmit_packet(inst->mode, interleaved, inst->local_poll,
- inst->version,
+ /* Send the request (which may also be a response in the symmetric mode) */
+ sent = transmit_packet(inst->mode, interleaved, inst->local_poll, inst->version,
inst->auth_mode, inst->auth_key_id,
- &inst->remote_ntp_rx, &inst->remote_ntp_tx,
- &inst->local_rx, &inst->local_tx,
- &inst->local_ntp_rx, &inst->local_ntp_tx,
- &inst->remote_addr,
- &local_addr);
+ initial ? NULL : &inst->remote_ntp_rx,
+ initial ? &inst->init_remote_ntp_tx : &inst->remote_ntp_tx,
+ initial ? &inst->init_local_rx : &inst->local_rx,
+ &inst->local_tx, &inst->local_ntp_rx, &inst->local_ntp_tx,
+ &inst->remote_addr, &local_addr);
++inst->tx_count;
- inst->valid_rx = 0;
- inst->updated_timestamps = 0;
if (sent)
inst->report.total_tx_count++;
@@ -1442,6 +1493,7 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
NTP_Local_Timestamp local_receive, local_transmit;
double remote_interval, local_interval, response_time;
double delay_time, precision;
+ int updated_timestamps;
/* ==================== */
@@ -1459,7 +1511,8 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
The test values are 1 when passed and 0 when failed. */
/* Test 1 checks for duplicate packet */
- test1 = !!UTI_CompareNtp64(&message->transmit_ts, &inst->remote_ntp_tx);
+ test1 = UTI_CompareNtp64(&message->receive_ts, &inst->remote_ntp_rx) ||
+ UTI_CompareNtp64(&message->transmit_ts, &inst->remote_ntp_tx);
/* Test 2 checks for bogus packet in the basic and interleaved modes. This
ensures the source is responding to the latest packet we sent to it. */
@@ -1644,21 +1697,34 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
The authentication test (test5) is required to prevent DoS attacks using
unauthenticated packets on authenticated symmetric associations. */
if ((inst->mode == MODE_CLIENT && valid_packet && !inst->valid_rx) ||
- (inst->mode == MODE_ACTIVE && (valid_packet || !inst->valid_rx) &&
- test5 && !UTI_IsZeroNtp64(&message->transmit_ts) &&
- (!inst->updated_timestamps || (valid_packet && !inst->valid_rx) ||
+ (inst->mode == MODE_ACTIVE && valid_packet &&
+ (!inst->valid_rx ||
UTI_CompareNtp64(&inst->remote_ntp_tx, &message->transmit_ts) < 0))) {
inst->remote_ntp_rx = message->receive_ts;
inst->remote_ntp_tx = message->transmit_ts;
inst->local_rx = *rx_ts;
inst->valid_timestamps = synced_packet;
- inst->updated_timestamps = 1;
+
+ UTI_ZeroNtp64(&inst->init_remote_ntp_tx);
+ zero_local_timestamp(&inst->init_local_rx);
+ inst->updated_init_timestamps = 0;
+ updated_timestamps = 2;
/* Don't use the same set of timestamps for the next sample */
if (interleaved_packet)
inst->prev_local_tx = inst->local_tx;
else
zero_local_timestamp(&inst->prev_local_tx);
+ } else if (inst->mode == MODE_ACTIVE &&
+ test1 && !UTI_IsZeroNtp64(&message->transmit_ts) && test5 &&
+ (!inst->updated_init_timestamps ||
+ UTI_CompareNtp64(&inst->init_remote_ntp_tx, &message->transmit_ts) < 0)) {
+ inst->init_remote_ntp_tx = message->transmit_ts;
+ inst->init_local_rx = *rx_ts;
+ inst->updated_init_timestamps = 1;
+ updated_timestamps = 1;
+ } else {
+ updated_timestamps = 0;
}
/* Accept at most one response per request. The NTP specification recommends
@@ -1694,10 +1760,11 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
DEBUG_LOG("remote_interval=%.9f local_interval=%.9f response_time=%.9f txs=%c rxs=%c",
remote_interval, local_interval, response_time,
tss_chars[local_transmit.source], tss_chars[local_receive.source]);
- DEBUG_LOG("test123=%d%d%d test567=%d%d%d testABCD=%d%d%d%d kod_rate=%d interleaved=%d presend=%d valid=%d good=%d updated=%d",
+ DEBUG_LOG("test123=%d%d%d test567=%d%d%d testABCD=%d%d%d%d kod_rate=%d interleaved=%d"
+ " presend=%d valid=%d good=%d updated=%d",
test1, test2, test3, test5, test6, test7, testA, testB, testC, testD,
kod_rate, interleaved_packet, inst->presend_done, valid_packet, good_packet,
- !UTI_CompareTimespecs(&inst->local_rx.ts, &rx_ts->ts));
+ updated_timestamps);
if (valid_packet) {
inst->remote_poll = message->poll;
@@ -1988,7 +2055,7 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
NTP_Mode pkt_mode, my_mode;
NTP_int64 *local_ntp_rx, *local_ntp_tx;
NTP_Local_Timestamp local_tx, *tx_ts;
- int valid_auth, log_index, interleaved, poll;
+ int pkt_version, valid_auth, log_index, interleaved, poll;
AuthenticationMode auth_mode;
uint32_t key_id;
@@ -2009,6 +2076,7 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
}
pkt_mode = NTP_LVM_TO_MODE(message->lvm);
+ pkt_version = NTP_LVM_TO_VERSION(message->lvm);
switch (pkt_mode) {
case MODE_ACTIVE:
@@ -2019,6 +2087,15 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
/* Reply with server packet */
my_mode = MODE_SERVER;
break;
+ case MODE_UNDEFINED:
+ /* Check if it is an NTPv1 client request (NTPv1 packets have a reserved
+ field instead of the mode field and the actual mode is determined from
+ the port numbers). Don't ever respond with a mode 0 packet! */
+ if (pkt_version == 1 && remote_addr->port != NTP_PORT) {
+ my_mode = MODE_SERVER;
+ break;
+ }
+ /* Fall through */
default:
/* Discard */
DEBUG_LOG("NTP packet discarded pkt_mode=%d", pkt_mode);
@@ -2064,7 +2141,8 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
if (log_index >= 0) {
CLG_GetNtpTimestamps(log_index, &local_ntp_rx, &local_ntp_tx);
interleaved = !UTI_IsZeroNtp64(local_ntp_rx) &&
- !UTI_CompareNtp64(&message->originate_ts, local_ntp_rx);
+ !UTI_CompareNtp64(&message->originate_ts, local_ntp_rx) &&
+ UTI_CompareNtp64(&message->receive_ts, &message->transmit_ts);
if (interleaved) {
UTI_Ntp64ToTimespec(local_ntp_tx, &local_tx.ts);
@@ -2081,7 +2159,7 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
poll = MAX(poll, message->poll);
/* Send a reply */
- transmit_packet(my_mode, interleaved, poll, NTP_LVM_TO_VERSION(message->lvm),
+ transmit_packet(my_mode, interleaved, poll, pkt_version,
auth_mode, key_id, &message->receive_ts, &message->transmit_ts,
rx_ts, tx_ts, local_ntp_rx, NULL, remote_addr, local_addr);
@@ -2189,6 +2267,9 @@ NCR_SlewTimes(NCR_Instance inst, struct timespec *when, double dfreq, double dof
if (!UTI_IsZeroTimespec(&inst->prev_local_tx.ts))
UTI_AdjustTimespec(&inst->prev_local_tx.ts, when, &inst->prev_local_tx.ts, &delta, dfreq,
doffset);
+ if (!UTI_IsZeroTimespec(&inst->init_local_rx.ts))
+ UTI_AdjustTimespec(&inst->init_local_rx.ts, when, &inst->init_local_rx.ts, &delta, dfreq,
+ doffset);
}
/* ================================================== */
diff --git a/ntp_io.c b/ntp_io.c
index c93e52f..d63eb1d 100644
--- a/ntp_io.c
+++ b/ntp_io.c
@@ -318,6 +318,9 @@ close_socket(int sock_fd)
if (sock_fd == INVALID_SOCK_FD)
return;
+#ifdef HAVE_LINUX_TIMESTAMPING
+ NIO_Linux_NotifySocketClosing(sock_fd);
+#endif
SCH_RemoveFileHandler(sock_fd);
close(sock_fd);
}
@@ -685,6 +688,11 @@ read_from_socket(int sock_fd, int event, void *anything)
unsigned int i, n;
int status, flags = 0;
+#ifdef HAVE_LINUX_TIMESTAMPING
+ if (NIO_Linux_ProcessEvent(sock_fd, event))
+ return;
+#endif
+
hdr = ARR_GetElements(recv_headers);
n = ARR_GetSize(recv_headers);
assert(n >= 1);
diff --git a/ntp_io_linux.c b/ntp_io_linux.c
index 00caed0..df944d7 100644
--- a/ntp_io_linux.c
+++ b/ntp_io_linux.c
@@ -94,6 +94,27 @@ static int ts_tx_flags;
/* Flag indicating the socket options can't be changed in control messages */
static int permanent_ts_options;
+/* When sending client requests to a close and fast server, it is possible that
+ a response will be received before the HW transmit timestamp of the request
+ itself. To avoid processing of the response without the HW timestamp, we
+ monitor events returned by select() and suspend reading of packets from the
+ receive queue for up to 200 microseconds. As the requests are normally
+ separated by at least 200 milliseconds, it is sufficient to monitor and
+ suspend one socket at a time. */
+static int monitored_socket;
+static int suspended_socket;
+static SCH_TimeoutID resume_timeout_id;
+
+#define RESUME_TIMEOUT 200.0e-6
+
+/* Unbound socket keeping the kernel RX timestamping permanently enabled
+ in order to avoid a race condition between receiving a server response
+ and the kernel actually starting to timestamp received packets after
+ enabling the timestamping and sending a request */
+static int dummy_rxts_socket;
+
+#define INVALID_SOCK_FD -3
+
/* ================================================== */
static int
@@ -252,7 +273,7 @@ update_interface_speed(struct Interface *iface)
{
struct ethtool_cmd cmd;
struct ifreq req;
- int sock_fd;
+ int sock_fd, link_speed;
sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (sock_fd < 0)
@@ -273,7 +294,12 @@ update_interface_speed(struct Interface *iface)
close(sock_fd);
- iface->link_speed = ethtool_cmd_speed(&cmd);
+ link_speed = ethtool_cmd_speed(&cmd);
+
+ if (iface->link_speed != link_speed) {
+ iface->link_speed = link_speed;
+ DEBUG_LOG("Updated speed of %s to %d Mb/s", iface->name, link_speed);
+ }
}
/* ================================================== */
@@ -301,6 +327,29 @@ check_timestamping_option(int option)
/* ================================================== */
+static int
+open_dummy_socket(void)
+{
+ int sock_fd, events = 0;
+
+ if ((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0
+#ifdef FEAT_IPV6
+ && (sock_fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0
+#endif
+ )
+ return INVALID_SOCK_FD;
+
+ if (!NIO_Linux_SetTimestampSocketOptions(sock_fd, 1, &events)) {
+ close(sock_fd);
+ return INVALID_SOCK_FD;
+ }
+
+ UTI_FdSetCloexec(sock_fd);
+ return sock_fd;
+}
+
+/* ================================================== */
+
void
NIO_Linux_Initialise(void)
{
@@ -350,6 +399,10 @@ NIO_Linux_Initialise(void)
/* Kernels before 4.7 ignore timestamping flags set in control messages */
permanent_ts_options = !SYS_Linux_CheckKernelVersion(4, 7);
+
+ monitored_socket = INVALID_SOCK_FD;
+ suspended_socket = INVALID_SOCK_FD;
+ dummy_rxts_socket = INVALID_SOCK_FD;
}
/* ================================================== */
@@ -360,6 +413,9 @@ NIO_Linux_Finalise(void)
struct Interface *iface;
unsigned int i;
+ if (dummy_rxts_socket != INVALID_SOCK_FD)
+ close(dummy_rxts_socket);
+
for (i = 0; i < ARR_GetSize(interfaces); i++) {
iface = ARR_GetElement(interfaces, i);
HCL_DestroyInstance(iface->clock);
@@ -406,6 +462,73 @@ NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events)
/* ================================================== */
+static void
+resume_socket(int sock_fd)
+{
+ if (monitored_socket == sock_fd)
+ monitored_socket = INVALID_SOCK_FD;
+
+ if (sock_fd == INVALID_SOCK_FD || sock_fd != suspended_socket)
+ return;
+
+ suspended_socket = INVALID_SOCK_FD;
+
+ SCH_SetFileHandlerEvent(sock_fd, SCH_FILE_INPUT, 1);
+
+ DEBUG_LOG("Resumed RX processing %s timeout fd=%d",
+ resume_timeout_id ? "before" : "on", sock_fd);
+
+ if (resume_timeout_id) {
+ SCH_RemoveTimeout(resume_timeout_id);
+ resume_timeout_id = 0;
+ }
+}
+
+/* ================================================== */
+
+static void
+resume_timeout(void *arg)
+{
+ resume_timeout_id = 0;
+ resume_socket(suspended_socket);
+}
+
+/* ================================================== */
+
+static void
+suspend_socket(int sock_fd)
+{
+ resume_socket(suspended_socket);
+
+ suspended_socket = sock_fd;
+
+ SCH_SetFileHandlerEvent(suspended_socket, SCH_FILE_INPUT, 0);
+ resume_timeout_id = SCH_AddTimeoutByDelay(RESUME_TIMEOUT, resume_timeout, NULL);
+
+ DEBUG_LOG("Suspended RX processing fd=%d", sock_fd);
+}
+
+/* ================================================== */
+
+int
+NIO_Linux_ProcessEvent(int sock_fd, int event)
+{
+ if (sock_fd != monitored_socket)
+ return 0;
+
+ if (event == SCH_FILE_INPUT) {
+ suspend_socket(monitored_socket);
+ monitored_socket = INVALID_SOCK_FD;
+
+ /* Don't process the message yet */
+ return 1;
+ }
+
+ return 0;
+}
+
+/* ================================================== */
+
static struct Interface *
get_interface(int if_index)
{
@@ -614,6 +737,11 @@ NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *loc
} else {
DEBUG_LOG("HW clock not found for interface %d", ts_if_index);
}
+
+ /* If a HW transmit timestamp was received, resume processing
+ of non-error messages on this socket */
+ if (is_tx)
+ resume_socket(local_addr->sock_fd);
}
if (local_ts->source == NTP_TS_DAEMON && !UTI_IsZeroTimespec(&ts3.ts[0]) &&
@@ -638,6 +766,14 @@ NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *loc
}
}
+ /* If the kernel is slow with enabling RX timestamping, open a dummy
+ socket to keep the kernel RX timestamping permanently enabled */
+ if (!is_tx && local_ts->source == NTP_TS_DAEMON && ts_flags) {
+ DEBUG_LOG("Missing kernel RX timestamp");
+ if (dummy_rxts_socket == INVALID_SOCK_FD)
+ dummy_rxts_socket = open_dummy_socket();
+ }
+
/* Return the message if it's not received from the error queue */
if (!is_tx)
return 0;
@@ -682,6 +818,15 @@ NIO_Linux_RequestTxTimestamp(struct msghdr *msg, int cmsglen, int sock_fd)
{
struct cmsghdr *cmsg;
+ if (!ts_flags)
+ return cmsglen;
+
+ /* If a HW transmit timestamp is requested on a client socket, monitor
+ events on the socket in order to avoid processing of a fast response
+ without the HW timestamp of the request */
+ if (ts_tx_flags & SOF_TIMESTAMPING_TX_HARDWARE && !NIO_IsServerSocket(sock_fd))
+ monitored_socket = sock_fd;
+
/* Check if TX timestamping is disabled on this socket */
if (permanent_ts_options || !NIO_IsServerSocket(sock_fd))
return cmsglen;
@@ -701,3 +846,11 @@ NIO_Linux_RequestTxTimestamp(struct msghdr *msg, int cmsglen, int sock_fd)
return cmsglen;
}
+
+/* ================================================== */
+
+void
+NIO_Linux_NotifySocketClosing(int sock_fd)
+{
+ resume_socket(sock_fd);
+}
diff --git a/ntp_io_linux.h b/ntp_io_linux.h
index cec7887..ed37e6a 100644
--- a/ntp_io_linux.h
+++ b/ntp_io_linux.h
@@ -24,13 +24,22 @@
This is the header file for the Linux-specific NTP socket I/O bits.
*/
+#ifndef GOT_NTP_IO_LINUX_H
+#define GOT_NTP_IO_LINUX_H
+
extern void NIO_Linux_Initialise(void);
extern void NIO_Linux_Finalise(void);
extern int NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events);
+extern int NIO_Linux_ProcessEvent(int sock_fd, int event);
+
extern int NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *local_ts, struct msghdr *hdr, int length);
extern int NIO_Linux_RequestTxTimestamp(struct msghdr *msg, int cmsglen, int sock_fd);
+
+extern void NIO_Linux_NotifySocketClosing(int sock_fd);
+
+#endif
diff --git a/ntp_signd.c b/ntp_signd.c
index 0d88d7b..6328b61 100644
--- a/ntp_signd.c
+++ b/ntp_signd.c
@@ -235,7 +235,7 @@ read_write_socket(int sock_fd, int event, void *anything)
return;
/* Disable output and wait for a response */
- SCH_SetFileHandlerEvents(sock_fd, SCH_FILE_INPUT);
+ SCH_SetFileHandlerEvent(sock_fd, SCH_FILE_OUTPUT, 0);
}
if (event == SCH_FILE_INPUT) {
@@ -283,7 +283,7 @@ read_write_socket(int sock_fd, int event, void *anything)
/* Move the head and enable output for the next packet */
queue_head = NEXT_QUEUE_INDEX(queue_head);
if (!IS_QUEUE_EMPTY())
- SCH_SetFileHandlerEvents(sock_fd, SCH_FILE_INPUT | SCH_FILE_OUTPUT);
+ SCH_SetFileHandlerEvent(sock_fd, SCH_FILE_OUTPUT, 1);
}
}
@@ -369,7 +369,7 @@ NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_Remote_Address *r
/* Enable output if there was no pending request */
if (IS_QUEUE_EMPTY())
- SCH_SetFileHandlerEvents(sock_fd, SCH_FILE_INPUT | SCH_FILE_OUTPUT);
+ SCH_SetFileHandlerEvent(sock_fd, SCH_FILE_OUTPUT, 1);
queue_tail = NEXT_QUEUE_INDEX(queue_tail);
diff --git a/pktlength.c b/pktlength.c
index 23a1b47..14a43f7 100644
--- a/pktlength.c
+++ b/pktlength.c
@@ -118,6 +118,7 @@ static const struct request_length request_lengths[] = {
{ 0, 0 }, /* ADD_PEER2 */
REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_SERVER3 */
REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_PEER3 */
+ REQ_LENGTH_ENTRY(null, null), /* SHUTDOWN */
};
static const uint16_t reply_lengths[] = {
@@ -132,13 +133,14 @@ static const uint16_t reply_lengths[] = {
0, /* SUBNETS_ACCESSED - not supported */
0, /* CLIENT_ACCESSES - not supported */
0, /* CLIENT_ACCESSES_BY_INDEX - not supported */
- 0, /* MANUAL_LIST - variable length */
+ 0, /* MANUAL_LIST - not supported */
RPY_LENGTH_ENTRY(activity), /* ACTIVITY */
RPY_LENGTH_ENTRY(smoothing), /* SMOOTHING */
RPY_LENGTH_ENTRY(server_stats), /* SERVER_STATS */
RPY_LENGTH_ENTRY(client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX2 */
RPY_LENGTH_ENTRY(ntp_data), /* NTP_DATA */
RPY_LENGTH_ENTRY(manual_timestamp), /* MANUAL_TIMESTAMP2 */
+ RPY_LENGTH_ENTRY(manual_list), /* MANUAL_LIST2 */
};
/* ================================================== */
@@ -195,21 +197,6 @@ PKL_ReplyLength(CMD_Reply *r)
if (type < 1 || type >= N_REPLY_TYPES)
return 0;
- /* Length of MANUAL_LIST depends on number of samples stored in it */
- if (type == RPY_MANUAL_LIST) {
- uint32_t ns;
-
- if (r->status != htons(STT_SUCCESS))
- return offsetof(CMD_Reply, data);
-
- ns = ntohl(r->data.manual_list.n_samples);
- if (ns > MAX_MANUAL_LIST_SAMPLES)
- return 0;
-
- return offsetof(CMD_Reply, data.manual_list.samples) +
- ns * sizeof (RPY_ManualListSample);
- }
-
return reply_lengths[type];
}
diff --git a/refclock.c b/refclock.c
index 6ca9118..0791d47 100644
--- a/refclock.c
+++ b/refclock.c
@@ -79,6 +79,8 @@ struct RCL_Instance_Record {
int pps_rate;
int pps_active;
int max_lock_age;
+ int stratum;
+ int tai;
struct MedianFilter filter;
uint32_t ref_id;
uint32_t lock_ref;
@@ -181,13 +183,13 @@ RCL_AddRefclock(RefclockParameters *params)
inst->driver = &RCL_PHC_driver;
} else {
LOG_FATAL("unknown refclock driver %s", params->driver_name);
- return 0;
}
- if (!inst->driver->init && !inst->driver->poll) {
+ if (!inst->driver->init && !inst->driver->poll)
LOG_FATAL("refclock driver %s is not compiled in", params->driver_name);
- return 0;
- }
+
+ if (params->tai && !CNF_GetLeapSecTimezone())
+ LOG_FATAL("refclock tai option requires leapsectz");
inst->data = NULL;
inst->driver_parameter = params->driver_parameter;
@@ -200,6 +202,8 @@ RCL_AddRefclock(RefclockParameters *params)
inst->pps_rate = params->pps_rate;
inst->pps_active = 0;
inst->max_lock_age = params->max_lock_age;
+ inst->stratum = params->stratum;
+ inst->tai = params->tai;
inst->lock_ref = params->lock_ref_id;
inst->offset = params->offset;
inst->delay = params->delay;
@@ -251,11 +255,8 @@ RCL_AddRefclock(RefclockParameters *params)
}
}
- if (inst->driver->init)
- if (!inst->driver->init(inst)) {
- LOG_FATAL("refclock %s initialisation failed", params->driver_name);
- return 0;
- }
+ if (inst->driver->init && !inst->driver->init(inst))
+ LOG_FATAL("refclock %s initialisation failed", params->driver_name);
filter_init(&inst->filter, params->filter_length, params->max_dispersion);
@@ -356,6 +357,28 @@ RCL_GetDriverOption(RCL_Instance instance, char *name)
return NULL;
}
+static int
+convert_tai_offset(struct timespec *sample_time, double *offset)
+{
+ struct timespec tai_ts, utc_ts;
+ int tai_offset;
+
+ /* Get approximate TAI-UTC offset for the reference time in TAI */
+ UTI_AddDoubleToTimespec(sample_time, *offset, &tai_ts);
+ tai_offset = REF_GetTaiOffset(&tai_ts);
+
+ /* Get TAI-UTC offset for the reference time in UTC +/- 1 second */
+ UTI_AddDoubleToTimespec(&tai_ts, -tai_offset, &utc_ts);
+ tai_offset = REF_GetTaiOffset(&utc_ts);
+
+ if (!tai_offset)
+ return 0;
+
+ *offset -= tai_offset;
+
+ return 1;
+}
+
int
RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset, int leap)
{
@@ -385,6 +408,11 @@ RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset
return 0;
}
+ if (instance->tai && !convert_tai_offset(sample_time, &offset)) {
+ DEBUG_LOG("refclock sample ignored unknown TAI offset");
+ return 0;
+ }
+
filter_add_sample(&instance->filter, &cooked_time, offset - correction + instance->offset, dispersion);
instance->pps_active = 0;
@@ -635,7 +663,7 @@ poll_timeout(void *arg)
/* Handle special case when PPS is used with local stratum */
stratum = pps_stratum(inst, &sample_time);
else
- stratum = 0;
+ stratum = inst->stratum;
SRC_UpdateReachability(inst->source, 1);
SRC_AccumulateSample(inst->source, &sample_time, offset,
diff --git a/refclock.h b/refclock.h
index 40ce12d..724f620 100644
--- a/refclock.h
+++ b/refclock.h
@@ -43,6 +43,8 @@ typedef struct {
int max_samples;
int sel_options;
int max_lock_age;
+ int stratum;
+ int tai;
uint32_t ref_id;
uint32_t lock_ref_id;
double offset;
diff --git a/reference.c b/reference.c
index 731f993..dff7ab9 100644
--- a/reference.c
+++ b/reference.c
@@ -609,7 +609,14 @@ is_offset_ok(double offset)
/* ================================================== */
static int
-is_leap_second_day(struct tm *stm) {
+is_leap_second_day(time_t when)
+{
+ struct tm *stm;
+
+ stm = gmtime(&when);
+ if (!stm)
+ return 0;
+
/* Allow leap second only on the last day of June and December */
return (stm->tm_mon == 5 && stm->tm_mday == 30) ||
(stm->tm_mon == 11 && stm->tm_mday == 31);
@@ -624,7 +631,7 @@ get_tz_leap(time_t when, int *tai_offset)
static NTP_Leap tz_leap;
static int tz_tai_offset;
- struct tm stm;
+ struct tm stm, *tm;
time_t t;
char *tz_env, tz_orig[128];
@@ -639,7 +646,11 @@ get_tz_leap(time_t when, int *tai_offset)
tz_leap = LEAP_Normal;
tz_tai_offset = 0;
- stm = *gmtime(&when);
+ tm = gmtime(&when);
+ if (!tm)
+ return tz_leap;
+
+ stm = *tm;
/* Temporarily switch to the timezone containing leap seconds */
tz_env = getenv("TZ");
@@ -784,7 +795,7 @@ update_leap_status(NTP_Leap leap, time_t now, int reset)
if (leap == LEAP_InsertSecond || leap == LEAP_DeleteSecond) {
/* Check that leap second is allowed today */
- if (is_leap_second_day(gmtime(&now))) {
+ if (is_leap_second_day(now)) {
if (leap == LEAP_InsertSecond) {
leap_sec = 1;
} else {
@@ -1345,6 +1356,18 @@ int REF_IsLeapSecondClose(void)
/* ================================================== */
+int
+REF_GetTaiOffset(struct timespec *ts)
+{
+ int tai_offset;
+
+ get_tz_leap(ts->tv_sec, &tai_offset);
+
+ return tai_offset;
+}
+
+/* ================================================== */
+
void
REF_GetTrackingReport(RPT_TrackingReport *rep)
{
diff --git a/reference.h b/reference.h
index e376770..6ee4953 100644
--- a/reference.h
+++ b/reference.h
@@ -184,6 +184,9 @@ extern void REF_DisableLocal(void);
and is better to discard any measurements */
extern int REF_IsLeapSecondClose(void);
+/* Return TAI-UTC offset corresponding to a time in UTC if available */
+extern int REF_GetTaiOffset(struct timespec *ts);
+
extern void REF_GetTrackingReport(RPT_TrackingReport *rep);
#endif /* GOT_REFERENCE_H */
diff --git a/sched.c b/sched.c
index 21b55a7..45ef11c 100644
--- a/sched.c
+++ b/sched.c
@@ -219,13 +219,16 @@ SCH_RemoveFileHandler(int fd)
/* ================================================== */
void
-SCH_SetFileHandlerEvents(int fd, int events)
+SCH_SetFileHandlerEvent(int fd, int event, int enable)
{
FileHandlerEntry *ptr;
- assert(events);
ptr = ARR_GetElement(file_handlers, fd);
- ptr->events = events;
+
+ if (enable)
+ ptr->events |= event;
+ else
+ ptr->events &= ~event;
}
/* ================================================== */
diff --git a/sched.h b/sched.h
index bfb6169..5ff53c7 100644
--- a/sched.h
+++ b/sched.h
@@ -60,7 +60,7 @@ extern void SCH_Finalise(void);
/* Register a handler for when select goes true on a file descriptor */
extern void SCH_AddFileHandler(int fd, int events, SCH_FileHandler handler, SCH_ArbitraryArgument arg);
extern void SCH_RemoveFileHandler(int fd);
-extern void SCH_SetFileHandlerEvents(int fd, int events);
+extern void SCH_SetFileHandlerEvent(int fd, int event, int enable);
/* Get the time stamp taken after a file descriptor became ready or a timeout expired */
extern void SCH_GetLastEventTime(struct timespec *cooked, double *err, struct timespec *raw);
diff --git a/sources.c b/sources.c
index 67b61e7..8aaa4a6 100644
--- a/sources.c
+++ b/sources.c
@@ -665,6 +665,16 @@ SRC_SelectSource(SRC_Instance updated_inst)
continue;
}
+ /* Include extra dispersion in the root distance of sources that don't
+ have new samples (the last sample is older than span of all samples) */
+ if (first_sample_ago < 2.0 * si->last_sample_ago) {
+ double extra_disp = LCL_GetMaxClockError() *
+ (2.0 * si->last_sample_ago - first_sample_ago);
+ si->root_distance += extra_disp;
+ si->lo_limit -= extra_disp;
+ si->hi_limit += extra_disp;
+ }
+
/* Require the root distance to be below the allowed maximum */
if (si->root_distance > max_distance) {
sources[i]->status = SRC_BAD_DISTANCE;
diff --git a/sourcestats.c b/sourcestats.c
index 2aec02c..ff4c652 100644
--- a/sourcestats.c
+++ b/sourcestats.c
@@ -51,6 +51,9 @@
#define MIN_SKEW 1.0e-12
#define MAX_SKEW 1.0e+02
+/* The minimum standard deviation */
+#define MIN_STDDEV 1.0e-9
+
/* The asymmetry of network jitter when all jitter is in one direction */
#define MAX_ASYMMETRY 0.5
@@ -571,7 +574,7 @@ SST_DoNewRegression(SST_Stats inst)
inst->estimated_offset = est_intercept;
inst->offset_time = inst->sample_times[inst->last_sample];
inst->estimated_offset_sd = est_intercept_sd;
- inst->std_dev = sqrt(est_var);
+ inst->std_dev = MAX(MIN_STDDEV, sqrt(est_var));
inst->nruns = nruns;
inst->skew = CLAMP(MIN_SKEW, inst->skew, MAX_SKEW);
@@ -884,7 +887,7 @@ SST_LoadFromFile(SST_Stats inst, FILE *in)
char line[1024];
double weight;
- assert(!inst->n_samples);
+ SST_ResetInstance(inst);
if (fgets(line, sizeof(line), in) &&
sscanf(line, "%d", &inst->n_samples) == 1 &&
@@ -933,7 +936,6 @@ SST_LoadFromFile(SST_Stats inst, FILE *in)
return 1;
inst->last_sample = inst->n_samples - 1;
- inst->runs_samples = 0;
find_min_delay_sample(inst);
SST_DoNewRegression(inst);
diff --git a/srcparams.h b/srcparams.h
index 5bd591d..7a73bad 100644
--- a/srcparams.h
+++ b/srcparams.h
@@ -35,6 +35,7 @@ typedef struct {
int online;
int auto_offline;
int presend_minpoll;
+ int burst;
int iburst;
int min_stratum;
int poll_target;
diff --git a/sys.c b/sys.c
index 5b56aae..4d68b37 100644
--- a/sys.c
+++ b/sys.c
@@ -97,7 +97,7 @@ SYS_Finalise(void)
void SYS_DropRoot(uid_t uid, gid_t gid)
{
#if defined(LINUX) && defined (FEAT_PRIVDROP)
- SYS_Linux_DropRoot(uid, gid);
+ SYS_Linux_DropRoot(uid, gid, !null_driver);
#elif defined(SOLARIS) && defined(FEAT_PRIVDROP)
SYS_Solaris_DropRoot(uid, gid);
#elif (defined(NETBSD) || defined(FREEBSD)) && defined(FEAT_PRIVDROP)
diff --git a/sys_linux.c b/sys_linux.c
index 190b54f..202e7c2 100644
--- a/sys_linux.c
+++ b/sys_linux.c
@@ -381,6 +381,18 @@ test_step_offset(void)
}
/* ================================================== */
+
+static void
+report_time_adjust_blockers(void)
+{
+#ifdef FEAT_PRIVDROP
+ if (CAP_IS_SUPPORTED(CAP_SYS_TIME) && cap_get_bound(CAP_SYS_TIME))
+ return;
+ LOG(LOGS_WARN, "CAP_SYS_TIME not present");
+#endif
+}
+
+/* ================================================== */
/* Initialisation code for this module */
void
@@ -388,6 +400,8 @@ SYS_Linux_Initialise(void)
{
get_version_specific_details();
+ report_time_adjust_blockers();
+
reset_adjtime_offset();
if (have_setoffset && !test_step_offset()) {
@@ -415,9 +429,9 @@ SYS_Linux_Finalise(void)
#ifdef FEAT_PRIVDROP
void
-SYS_Linux_DropRoot(uid_t uid, gid_t gid)
+SYS_Linux_DropRoot(uid_t uid, gid_t gid, int clock_control)
{
- const char *cap_text;
+ char cap_text[256];
cap_t cap;
if (prctl(PR_SET_KEEPCAPS, 1)) {
@@ -426,9 +440,12 @@ SYS_Linux_DropRoot(uid_t uid, gid_t gid)
UTI_DropRoot(uid, gid);
- /* Keep CAP_NET_BIND_SERVICE only if NTP port can be opened */
- cap_text = CNF_GetNTPPort() ?
- "cap_net_bind_service,cap_sys_time=ep" : "cap_sys_time=ep";
+ /* Keep CAP_NET_BIND_SERVICE only if a server NTP port can be opened
+ and keep CAP_SYS_TIME only if the clock control is enabled */
+ if (snprintf(cap_text, sizeof (cap_text), "%s %s",
+ CNF_GetNTPPort() ? "cap_net_bind_service=ep" : "",
+ clock_control ? "cap_sys_time=ep" : "") >= sizeof (cap_text))
+ assert(0);
if ((cap = cap_from_text(cap_text)) == NULL) {
LOG_FATAL("cap_from_text() failed");
diff --git a/sys_linux.h b/sys_linux.h
index dc1cee6..799ae9a 100644
--- a/sys_linux.h
+++ b/sys_linux.h
@@ -31,7 +31,7 @@ extern void SYS_Linux_Initialise(void);
extern void SYS_Linux_Finalise(void);
-extern void SYS_Linux_DropRoot(uid_t uid, gid_t gid);
+extern void SYS_Linux_DropRoot(uid_t uid, gid_t gid, int clock_control);
extern void SYS_Linux_EnableSystemCallFilter(int level);
diff --git a/test/compilation/002-scanbuild b/test/compilation/002-scanbuild
index 085cc65..da87407 100755
--- a/test/compilation/002-scanbuild
+++ b/test/compilation/002-scanbuild
@@ -6,8 +6,9 @@ for opts in \
"--host-system=Linux" \
"--host-system=NetBSD" \
"--host-system=FreeBSD" \
- "--without-nss" \
- "--without-tomcrypt --without-nss"
+ "--without-nettle" \
+ "--without-nettle --without-nss" \
+ "--without-nettle --without-nss --without-tomcrypt"
do
./configure $opts
scan-build make "$@" || exit 1
diff --git a/test/simulation/106-refclock b/test/simulation/106-refclock
index f64f2be..c22cd42 100755
--- a/test/simulation/106-refclock
+++ b/test/simulation/106-refclock
@@ -8,11 +8,20 @@ limit=1000
refclock_jitter=$jitter
min_sync_time=45
max_sync_time=70
-client_conf="refclock SHM 0"
+chronyc_start=70
+client_conf="refclock SHM 0 stratum 3 delay 1e-3 refid GPS"
+chronyc_conf="tracking"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
+check_chronyc_output "^Reference ID.*47505300 \(GPS\)
+Stratum.*: 4
+.*
+Root delay : 0.001000000 seconds
+.*
+Update interval : 16\.. seconds
+.*$" || test_fail
test_pass
diff --git a/test/simulation/110-chronyc b/test/simulation/110-chronyc
index ddd9a22..d56e724 100755
--- a/test/simulation/110-chronyc
+++ b/test/simulation/110-chronyc
@@ -4,14 +4,30 @@
test_start "chronyc"
-chronyc_conf="tracking
+refclock_jitter=$jitter
+client_conf="
+refclock SHM 0 noselect
+smoothtime 400 0.001 leaponly"
+
+chronyc_conf="activity
+tracking
sources
-sourcestats"
+sourcestats
+manual list
+smoothing
+waitsync
+rtcdata"
run_test || test_fail
check_chronyd_exit || test_fail
-check_chronyc_output "^Reference ID : C0A87B01 \(192\.168\.123\.1\)
+check_chronyc_output "^200 OK
+1 sources online
+0 sources offline
+0 sources doing burst \(return to online\)
+0 sources doing burst \(return to offline\)
+0 sources with unknown address
+Reference ID : C0A87B01 \(192\.168\.123\.1\)
Stratum : 2
Ref time \(UTC\) : Fri Jan 01 00:1.:.. 2010
System time : 0\.0000..... seconds (slow|fast) of NTP time
@@ -24,14 +40,27 @@ Root delay : 0\.000...... seconds
Root dispersion : 0\.000...... seconds
Update interval : [0-9]+\.. seconds
Leap status : Normal
-210 Number of sources = 1
+210 Number of sources = 2
MS Name/IP address Stratum Poll Reach LastRx Last sample
===============================================================================
+#\? SHM0 0 4 377 [0-9]+ [0-9 +-]+[un]s\[[0-9 +-]+[un]s\] \+/-[ 0-9]+[un]s
\^\* 192\.168\.123\.1 1 [67] 377 [0-9]+ [0-9 +-]+[un]s\[[0-9 +-]+[un]s\] \+/-[ 0-9]+[un]s
-210 Number of sources = 1
+210 Number of sources = 2
Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev
==============================================================================
-192\.168\.123\.1 [0-9 ]+ [0-9 ]+ [0-9 ]+ [ +-][01]\.... [0-9 ]+\.... [0-9 +-]+[un]s [0-9 ]+[un]s$" \
+SHM0 [0-9 ]+ [0-9 ]+ [0-9 ]+ [ +-][01]\.... [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
+210 n_samples = 0
+# Date Time\(UTC\) Slewed Original Residual
+=======================================================
+Active : Yes \(leap second only\)
+Offset : \+0\.000000000 seconds
+Frequency : \+0\.000000 ppm
+Wander : \+0\.000000 ppm per second
+Last update : [0-9]+\.. seconds ago
+Remaining time : 0\.0 seconds
+try: 1, refid: C0A87B01, correction: 0\.000......, skew: .\....
+513 RTC driver not running$" \
|| test_fail
test_pass
diff --git a/test/simulation/119-smoothtime b/test/simulation/119-smoothtime
index 8ca3c36..6b4ae39 100755
--- a/test/simulation/119-smoothtime
+++ b/test/simulation/119-smoothtime
@@ -5,8 +5,9 @@ test_start "smoothtime option"
server_strata=2
server_conf="smoothtime 400 0.001"
-min_sync_time=250
-max_sync_time=1000
+server_server_options="minpoll 8"
+min_sync_time=600
+max_sync_time=800
run_test || test_fail
check_chronyd_exit || test_fail
@@ -22,6 +23,7 @@ server_conf="refclock SHM 0 dpoll 4 poll 6
smoothtime 2000 1
maxjitter 10.0"
time_offset=-10
+server_server_options=""
client_server_options="minpoll 6 maxpoll 6"
client_conf="corrtimeratio 100"
min_sync_time=8000
diff --git a/test/simulation/124-tai b/test/simulation/124-tai
new file mode 100755
index 0000000..b5be030
--- /dev/null
+++ b/test/simulation/124-tai
@@ -0,0 +1,42 @@
+#!/bin/bash
+
+. ./test.common
+test_start "tai option"
+
+export CLKNETSIM_START_DATE=$(TZ=UTC date -d 'Dec 31 2008 23:50:00' +'%s')
+
+leap=$[10 * 60]
+limit=$[20 * 60]
+min_sync_time=2
+max_sync_time=15
+refclock_jitter=1e-6
+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
+leapsecmode ignore
+maxchange 1e-3 1 0"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+
+export CLKNETSIM_START_DATE=$(TZ=UTC date -d 'Jan 01 2009 00:10:00' +'%s')
+
+time_offset=-1000
+refclock_offset="(+ -34)"
+client_conf="
+refclock SHM 0 dpoll 0 poll 0 tai
+leapsectz right/UTC
+makestep 1 1
+maxchange 1e-3 1 0"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+
+test_pass
diff --git a/test/simulation/125-packetloss b/test/simulation/125-packetloss
new file mode 100755
index 0000000..95604da
--- /dev/null
+++ b/test/simulation/125-packetloss
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+. ./test.common
+
+test_start "packet loss"
+
+# Drop 33% of packets by default and 100% on the 3->1 path
+base_delay=$(cat <<-EOF | tr -d '\n'
+ (+ 1e-4
+ (* -1 (equal 0.33 (uniform) 1.0))
+ (* -1 (equal 0.1 from 3) (equal 0.1 to 1)))
+EOF
+)
+clients=2
+peers=2
+jitter=1e-5
+limit=20000
+max_sync_time=10000
+
+for options in "maxpoll 8" "maxpoll 8 xleave"; do
+ client_server_options=$options
+ client_peer_options=$options
+
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_sync || test_fail
+done
+
+test_pass
diff --git a/test/simulation/126-burst b/test/simulation/126-burst
new file mode 100755
index 0000000..3173c78
--- /dev/null
+++ b/test/simulation/126-burst
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+. ./test.common
+
+test_start "burst option"
+
+# Pass every fourth packet on the 2->1 path
+base_delay=$(cat <<-EOF | tr -d '\n'
+ (+ 1e-4
+ (* -1
+ (equal 0.1 from 2)
+ (equal 0.1 to 1)
+ (equal 0.1 (min (% (sum 1) 4) 1) 1)))
+EOF
+)
+
+client_server_options="burst polltarget 1"
+min_sync_time=700
+max_sync_time=730
+client_max_min_out_interval=2.2
+client_min_mean_out_interval=150.0
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+test_pass
diff --git a/test/simulation/test.common b/test/simulation/test.common
index 2c28e89..0cd0fce 100644
--- a/test/simulation/test.common
+++ b/test/simulation/test.common
@@ -71,6 +71,9 @@ default_freq_rms_limit=1e-5
default_min_sync_time=120
default_max_sync_time=210
+default_client_min_mean_out_interval=0.0
+default_client_max_min_out_interval=inf
+
# Initialize test settings from their defaults
for defopt in $(declare | grep '^default_'); do
defoptname=${defopt%%=*}
@@ -251,6 +254,7 @@ check_chronyd_exit() {
test_message 3 0 "node $i:"
tail -n 1 tmp/log.$i | grep -q 'chronyd exiting' && \
+ ! grep -q 'Adjustment.*exceeds.*exiting' tmp/log.$i && \
test_ok || test_bad
[ $? -eq 0 ] || ret=1
done
@@ -302,10 +306,12 @@ check_packet_interval() {
([ $i -gt $servers ] || \
check_stat $mean_in_interval 0.0 $mean_out_interval 10*$jitter) && \
([ $i -le $[$servers * $server_strata] ] || \
- check_stat $mean_out_interval 0.0 $mean_in_interval 10*$jitter) && \
+ check_stat $mean_out_interval $client_min_mean_out_interval \
+ $mean_in_interval 10*$jitter) && \
([ $i -le $[$servers * $server_strata] ] || \
check_stat $min_out_interval \
- $([ $servers -gt 1 ] && echo 0.18 || echo 1.8) inf) && \
+ $([ $servers -gt 1 ] && echo 0.18 || echo 1.8) \
+ $client_max_min_out_interval) && \
test_ok || test_bad
[ $? -eq 0 ] || ret=1
diff --git a/test/unit/hash.c b/test/unit/hash.c
new file mode 100644
index 0000000..8aab74f
--- /dev/null
+++ b/test/unit/hash.c
@@ -0,0 +1,123 @@
+/*
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar 2018
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ **********************************************************************
+ */
+
+#include <config.h>
+#include <sysincl.h>
+#include <hash.h>
+#include <logging.h>
+#include "test.h"
+
+struct hash_test {
+ const char *name;
+ const unsigned char out[MAX_HASH_LENGTH];
+ unsigned int length;
+};
+
+void
+test_unit(void)
+{
+ unsigned char data1[] = "abcdefghijklmnopqrstuvwxyz";
+ unsigned char data2[] = "12345678910";
+ unsigned char out[MAX_HASH_LENGTH];
+ struct hash_test tests[] = {
+ { "MD5", "\xfc\x24\x97\x1b\x52\x66\xdc\x46\xef\xe0\xe8\x08\x46\x89\xb6\x88", 16 },
+ { "SHA1", "\xd8\x85\xb3\x86\xce\xea\x93\xeb\x92\xcd\x7b\x94\xb9\x8d\xc2\x8e"
+ "\x3e\x31\x13\xdd", 20},
+ { "SHA256", "\x0e\x35\x14\xe7\x15\x7a\x1d\xdd\xea\x11\x78\xd3\x41\xf6\xb9\x3e"
+ "\xa0\x42\x96\x73\x3c\x54\x74\x0b\xfa\x6b\x9e\x29\x59\xad\x69\xd3", 32 },
+ { "SHA384", "\x2c\xeb\xbd\x4d\x95\xed\xad\x03\xed\x80\xc4\xf3\xa6\x10\x21\xde"
+ "\x40\x69\x54\xed\x42\x70\xb8\x95\xb0\x6f\x01\x1d\x04\xdf\x57\xbc"
+ "\x1d\xb5\x85\xbf\x4f\x03\x88\xd5\x83\x93\xbc\x81\x90\xb0\xa9\x9b", 48 },
+ { "SHA512", "\x20\xba\xec\xcb\x68\x98\x33\x5b\x70\x26\x63\x13\xe2\xf7\x0e\x67"
+ "\x08\xf3\x77\x4f\xbd\xeb\xc4\xa8\xc5\x94\xe2\x39\x40\x7e\xed\x0b"
+ "\x69\x0e\x18\xa5\xa2\x03\x73\xe7\x1d\x20\x7d\x3f\xc8\x70\x2d\x64"
+ "\x9e\x89\x6d\x20\x6a\x4a\x5a\x46\xe7\x4f\x2c\xf9\x0f\x0a\x54\xdc", 64 },
+ { "SHA3-224", "\x3b\xa2\x22\x28\xdd\x26\x18\xec\x3b\xb9\x25\x39\x5e\xbd\x94\x25"
+ "\xd4\x20\x8a\x76\x76\xc0\x3c\x5d\x9e\x0a\x06\x46", 28},
+ { "SHA3-256", "\x26\xd1\x19\xb2\xc1\x64\xc8\xb8\x10\xd8\xa8\x1c\xb6\xa4\x0d\x29"
+ "\x09\xc9\x8e\x2e\x2d\xde\x7a\x74\x8c\x43\x70\xb8\xaa\x0f\x09\x17", 32 },
+ { "SHA3-384", "\x6a\x64\xb9\x89\x08\x29\xd0\xa7\x4b\x84\xba\xa6\x65\xf5\xe7\x54"
+ "\xe2\x18\x12\xc3\x63\x34\xc6\xba\x26\xf5\x6e\x99\xe2\x54\xcc\x9d"
+ "\x01\x10\x9d\xee\x35\x38\x04\x83\xe5\x71\x70\xd8\xc8\x99\x96\xd8", 48 },
+ { "SHA3-512", "\xa8\xe3\x2b\x65\x1f\x87\x90\x73\x19\xc8\xa0\x3f\xe3\x85\x60\x3c"
+ "\x39\xfc\xcb\xc1\x29\xe1\x23\x7d\x8b\x56\x54\xe3\x08\x9d\xf9\x74"
+ "\x78\x69\x2e\x3c\x7e\x51\x1e\x9d\xab\x09\xbe\xe7\x6b\x1a\xa1\x22"
+ "\x93\xb1\x2b\x82\x9d\x1e\xcf\xa8\x99\xc5\xec\x7b\x1d\x89\x07\x2b", 64 },
+ { "RMD128", "\x6f\xd7\x1f\x37\x47\x0f\xbd\x42\x57\xc8\xbb\xee\xba\x65\xf9\x35", 16 },
+ { "RMD160", "\x7a\x88\xec\xc7\x09\xc5\x65\x34\x11\x24\xe3\xf9\xf7\xa5\xbf\xc6"
+ "\x01\xe2\xc9\x32", 20},
+ { "RMD256", "\x59\xdf\xd4\xcb\xc9\xbe\x7c\x27\x08\xa7\x23\xf7\xb3\x0c\xf0\x0d"
+ "\xa0\xcf\x5b\x18\x16\x51\x56\x6d\xda\x7b\x87\x24\x9d\x83\x35\xe1", 32 },
+ { "RMD320", "\x68\x98\x10\xf4\xb6\x79\xb6\x15\xf1\x48\x2d\x73\xd0\x23\x84\x01"
+ "\xbf\xaa\x67\xcf\x1e\x35\x5c\xbf\xe9\xb8\xaf\xe1\xee\x0d\xf0\x6b"
+ "\xe2\x3a\x9a\x3a\xa7\x56\xad\x70", 40},
+ { "TIGER", "\x1c\xcd\x68\x74\xca\xd6\xd5\x17\xba\x3e\x82\xaf\xbd\x70\xdc\x66"
+ "\x99\xaa\xae\x16\x72\x59\xd1\x64", 24},
+ { "WHIRLPOOL", "\xe3\xcd\xe6\xbf\xe1\x8c\xe4\x4d\xc8\xb4\xa5\x7c\x36\x8d\xc8\x8a"
+ "\x8b\xad\x52\x24\xc0\x4e\x99\x5b\x7e\x86\x94\x2d\x10\x56\x12\xa3"
+ "\x29\x2a\x65\x0f\x9e\x07\xbc\x15\x21\x14\xe6\x07\xfc\xe6\xb9\x2f"
+ "\x13\xe2\x57\xe9\x0a\xb0\xd2\xf4\xa3\x20\x36\x9c\x88\x92\x8e\xc9", 64 },
+ { "", "", 0 }
+ };
+
+ unsigned int length;
+ int i, j, hash_id;
+
+ for (i = 0; tests[i].name[0] != '\0'; i++) {
+ hash_id = HSH_GetHashId(tests[i].name);
+ if (hash_id < 0) {
+ TEST_CHECK(strcmp(tests[i].name, "MD5"));
+#ifdef FEAT_SECHASH
+ TEST_CHECK(strcmp(tests[i].name, "SHA1"));
+ TEST_CHECK(strcmp(tests[i].name, "SHA256"));
+ TEST_CHECK(strcmp(tests[i].name, "SHA384"));
+ TEST_CHECK(strcmp(tests[i].name, "SHA512"));
+#endif
+ continue;
+ }
+
+ DEBUG_LOG("testing %s", tests[i].name);
+
+ for (j = 0; j <= sizeof (out); j++) {
+ TEST_CHECK(HSH_GetHashId(tests[i].name) == hash_id);
+ TEST_CHECK(HSH_GetHashId("nosuchhash") < 0);
+
+ memset(out, 0, sizeof (out));
+ length = HSH_Hash(hash_id, data1, sizeof (data1) - 1, data2, sizeof (data2) - 1,
+ out, j);
+
+ if (j >= tests[i].length)
+ TEST_CHECK(length == tests[i].length);
+ else
+ TEST_CHECK(length == 0 || length == j);
+
+ TEST_CHECK(!memcmp(out, tests[i].out, length));
+ }
+
+ for (j = 0; j < 10000; j++) {
+ length = HSH_Hash(hash_id, data1, random() % sizeof (data1),
+ random() % 2 ? data2 : NULL, random() % sizeof (data2),
+ out, sizeof (out));
+ TEST_CHECK(length == tests[i].length);
+ }
+ }
+
+ HSH_Finalise();
+}
diff --git a/test/unit/keys.c b/test/unit/keys.c
index f8e01b2..ac995fa 100644
--- a/test/unit/keys.c
+++ b/test/unit/keys.c
@@ -134,8 +134,8 @@ test_unit(void)
UTI_GetRandomBytes(&key, sizeof (key));
if (KEY_KeyKnown(key))
continue;
- TEST_CHECK(!KEY_GenerateAuth(j, data, data_len, auth, sizeof (auth)));
- TEST_CHECK(!KEY_CheckAuth(j, data, data_len, auth, auth_len, auth_len));
+ TEST_CHECK(!KEY_GenerateAuth(key, data, data_len, auth, sizeof (auth)));
+ TEST_CHECK(!KEY_CheckAuth(key, data, data_len, auth, auth_len, auth_len));
}
}
diff --git a/test/unit/ntp_core.c b/test/unit/ntp_core.c
index b227017..9587306 100644
--- a/test/unit/ntp_core.c
+++ b/test/unit/ntp_core.c
@@ -36,6 +36,7 @@ static int req_length, res_length;
#define NIO_CloseServerSocket(fd) assert(fd == 100)
#define NIO_OpenClientSocket(addr) ((addr)->ip_addr.family != IPADDR_UNSPEC ? 101 : 0)
#define NIO_CloseClientSocket(fd) assert(fd == 101)
+#define NIO_IsServerSocket(fd) (fd == 100)
#define NIO_SendPacket(msg, to, from, len, process_tx) (memcpy(&req_buffer, msg, len), req_length = len, 1)
#define SCH_AddTimeoutByDelay(delay, handler, arg) (1 ? 102 : (handler(arg), 1))
#define SCH_AddTimeoutInClass(delay, separation, randomness, class, handler, arg) \
@@ -43,6 +44,7 @@ static int req_length, res_length;
#define SCH_RemoveTimeout(id) assert(!id || id == 102)
#define LCL_ReadRawTime(ts) (*ts = current_time)
#define LCL_ReadCookedTime(ts, err) do {double *p = err; *ts = current_time; if (p) *p = 0.0;} while (0)
+#define LCL_GetSysPrecisionAsLog() (random() % 10 - 30)
#define SRC_UpdateReachability(inst, reach)
#define SRC_ResetReachability(inst)
@@ -55,8 +57,6 @@ add_timeout_in_class(double min_delay, double separation, double randomness,
#include <ntp_core.c>
-static NCR_Instance inst;
-
static void
advance_time(double x)
{
@@ -64,7 +64,7 @@ advance_time(double x)
}
static void
-send_request(void)
+send_request(NCR_Instance inst)
{
NTP_Local_Address local_addr;
NTP_Local_Timestamp local_ts;
@@ -74,19 +74,48 @@ send_request(void)
transmit_timeout(inst);
TEST_CHECK(!inst->valid_rx);
- TEST_CHECK(!inst->updated_timestamps);
TEST_CHECK(prev_tx_count + 1 == inst->report.total_tx_count);
- advance_time(1e-4);
+ advance_time(1e-5);
+
+ if (random() % 2) {
+ local_addr.ip_addr.family = IPADDR_UNSPEC;
+ local_addr.if_index = INVALID_IF_INDEX;
+ local_addr.sock_fd = 101;
+ local_ts.ts = current_time;
+ local_ts.err = 0.0;
+ local_ts.source = NTP_TS_KERNEL;
+
+ NCR_ProcessTxKnown(inst, &local_addr, &local_ts, &req_buffer.ntp_pkt, req_length);
+ }
+}
+
+static void
+process_request(NTP_Remote_Address *remote_addr)
+{
+ NTP_Local_Address local_addr;
+ NTP_Local_Timestamp local_ts;
local_addr.ip_addr.family = IPADDR_UNSPEC;
local_addr.if_index = INVALID_IF_INDEX;
- local_addr.sock_fd = 101;
+ local_addr.sock_fd = 100;
local_ts.ts = current_time;
local_ts.err = 0.0;
- local_ts.source = NTP_TS_DAEMON;
+ local_ts.source = NTP_TS_KERNEL;
+
+ res_length = 0;
+ NCR_ProcessRxUnknown(remote_addr, &local_addr, &local_ts,
+ &req_buffer.ntp_pkt, req_length);
+ res_length = req_length;
+ res_buffer = req_buffer;
+
+ advance_time(1e-5);
- NCR_ProcessTxKnown(inst, &local_addr, &local_ts, &req_buffer.ntp_pkt, req_length);
+ if (random() % 2) {
+ local_ts.ts = current_time;
+ NCR_ProcessTxUnknown(remote_addr, &local_addr, &local_ts,
+ &res_buffer.ntp_pkt, res_length);
+ }
}
static void
@@ -165,30 +194,36 @@ send_response(int interleaved, int authenticated, int allow_update, int valid_ts
}
static void
-process_response(int valid, int updated)
+process_response(NCR_Instance inst, int good, int valid, int updated_sync, int updated_init)
{
NTP_Local_Address local_addr;
NTP_Local_Timestamp local_ts;
NTP_Packet *res;
uint32_t prev_rx_count, prev_valid_count;
- struct timespec prev_rx_ts;
- int prev_open_socket;
+ struct timespec prev_rx_ts, prev_init_rx_ts;
+ int prev_open_socket, ret;
res = &res_buffer.ntp_pkt;
local_addr.ip_addr.family = IPADDR_UNSPEC;
local_addr.if_index = INVALID_IF_INDEX;
- local_addr.sock_fd = NTP_LVM_TO_MODE(res->lvm) == MODE_ACTIVE ? 100 : 101;
+ local_addr.sock_fd = NTP_LVM_TO_MODE(res->lvm) != MODE_SERVER ? 100 : 101;
local_ts.ts = current_time;
local_ts.err = 0.0;
- local_ts.source = NTP_TS_DAEMON;
+ local_ts.source = NTP_TS_KERNEL;
prev_rx_count = inst->report.total_rx_count;
prev_valid_count = inst->report.total_valid_count;
prev_rx_ts = inst->local_rx.ts;
+ prev_init_rx_ts = inst->init_local_rx.ts;
prev_open_socket = inst->local_addr.sock_fd != INVALID_SOCK_FD;
- NCR_ProcessRxKnown(inst, &local_addr, &local_ts, res, res_length);
+ ret = NCR_ProcessRxKnown(inst, &local_addr, &local_ts, res, res_length);
+
+ if (good > 0)
+ TEST_CHECK(ret);
+ else if (!good)
+ TEST_CHECK(!ret);
if (prev_open_socket)
TEST_CHECK(prev_rx_count + 1 == inst->report.total_rx_count);
@@ -200,23 +235,51 @@ process_response(int valid, int updated)
else
TEST_CHECK(prev_valid_count == inst->report.total_valid_count);
- if (updated)
+ if (updated_sync)
TEST_CHECK(UTI_CompareTimespecs(&inst->local_rx.ts, &prev_rx_ts));
else
TEST_CHECK(!UTI_CompareTimespecs(&inst->local_rx.ts, &prev_rx_ts));
+
+ if (updated_init > 0)
+ TEST_CHECK(UTI_CompareTimespecs(&inst->init_local_rx.ts, &prev_init_rx_ts));
+ else if (!updated_init)
+ TEST_CHECK(!UTI_CompareTimespecs(&inst->init_local_rx.ts, &prev_init_rx_ts));
+
+ if (valid) {
+ TEST_CHECK(UTI_IsZeroTimespec(&inst->init_local_rx.ts));
+ TEST_CHECK(UTI_IsZeroNtp64(&inst->init_remote_ntp_tx));
+ }
+}
+
+static void
+process_replay(NCR_Instance inst, NTP_Receive_Buffer *packet_queue,
+ int queue_length, int updated_init)
+{
+ do {
+ res_buffer = packet_queue[random() % queue_length];
+ } while (!UTI_CompareNtp64(&res_buffer.ntp_pkt.transmit_ts,
+ &inst->remote_ntp_tx));
+ process_response(inst, 0, 0, 0, updated_init);
+ advance_time(1e-6);
}
+#define PACKET_QUEUE_LENGTH 10
+
void
test_unit(void)
{
- char source_line[] = "127.0.0.1";
+ char source_line[] = "127.0.0.1 maxdelaydevratio 1e6";
char conf[][100] = {
+ "allow",
"port 0",
+ "local",
"keyfile ntp_core.keys"
};
- int i, j, interleaved, authenticated, valid, updated, has_updated;
+ int i, j, k, interleaved, authenticated, valid, updated, has_updated;
CPS_NTP_Source source;
NTP_Remote_Address remote_addr;
+ NCR_Instance inst1, inst2;
+ NTP_Receive_Buffer packet_queue[PACKET_QUEUE_LENGTH];
CNF_Initialise(0, 0);
for (i = 0; i < sizeof conf / sizeof conf[0]; i++)
@@ -231,12 +294,16 @@ test_unit(void)
REF_Initialise();
KEY_Initialise();
+ CNF_SetupAccessRestrictions();
+
+ CPS_ParseNTPSourceAdd(source_line, &source);
+
for (i = 0; i < 1000; i++) {
- CPS_ParseNTPSourceAdd(source_line, &source);
if (random() % 2)
source.params.interleaved = 1;
if (random() % 2)
source.params.authkey = 1;
+ source.params.version = random() % 4 + 1;
UTI_ZeroTimespec(&current_time);
advance_time(TST_GetRandomDouble(1.0, 1e9));
@@ -244,48 +311,125 @@ test_unit(void)
TST_GetRandomAddress(&remote_addr.ip_addr, IPADDR_UNSPEC, -1);
remote_addr.port = 123;
- inst = NCR_GetInstance(&remote_addr, random() % 2 ? NTP_SERVER : NTP_PEER, &source.params);
- NCR_StartInstance(inst);
+ inst1 = NCR_GetInstance(&remote_addr, random() % 2 ? NTP_SERVER : NTP_PEER, &source.params);
+ NCR_StartInstance(inst1);
has_updated = 0;
for (j = 0; j < 50; j++) {
- DEBUG_LOG("iteration %d, %d", i, j);
+ DEBUG_LOG("client/peer test iteration %d/%d", i, j);
- interleaved = random() % 2 && (inst->mode != MODE_CLIENT ||
- inst->tx_count < MAX_CLIENT_INTERLEAVED_TX);
+ interleaved = random() % 2 && (inst1->mode != MODE_CLIENT ||
+ inst1->tx_count < MAX_CLIENT_INTERLEAVED_TX);
authenticated = random() % 2;
valid = (!interleaved || (source.params.interleaved && has_updated)) &&
(!source.params.authkey || authenticated);
- updated = (valid || inst->mode == MODE_ACTIVE) &&
+ updated = (valid || inst1->mode == MODE_ACTIVE) &&
(!source.params.authkey || authenticated);
has_updated = has_updated || updated;
+ if (inst1->mode == MODE_CLIENT)
+ updated = 0;
- send_request();
+ send_request(inst1);
send_response(interleaved, authenticated, 1, 0, 1);
- process_response(0, inst->mode == MODE_CLIENT ? 0 : updated);
+ DEBUG_LOG("response 1");
+ process_response(inst1, 0, 0, 0, updated);
if (source.params.authkey) {
send_response(interleaved, authenticated, 1, 1, 0);
- process_response(0, 0);
+ DEBUG_LOG("response 2");
+ process_response(inst1, 0, 0, 0, 0);
}
send_response(interleaved, authenticated, 1, 1, 1);
- process_response(valid, updated);
- process_response(0, 0);
+ DEBUG_LOG("response 3");
+ process_response(inst1, -1, valid, valid, updated);
+ DEBUG_LOG("response 4");
+ process_response(inst1, 0, 0, 0, 0);
advance_time(-1.0);
send_response(interleaved, authenticated, 1, 1, 1);
- process_response(0, 0);
+ DEBUG_LOG("response 5");
+ process_response(inst1, 0, 0, 0, updated && valid);
advance_time(1.0);
send_response(interleaved, authenticated, 1, 1, 1);
- process_response(0, inst->mode == MODE_CLIENT ? 0 : updated);
+ DEBUG_LOG("response 6");
+ process_response(inst1, 0, 0, valid && updated, updated);
+ }
+
+ NCR_DestroyInstance(inst1);
+
+ inst1 = NCR_GetInstance(&remote_addr, random() % 2 ? NTP_SERVER : NTP_PEER, &source.params);
+ NCR_StartInstance(inst1);
+
+ for (j = 0; j < 20; j++) {
+ DEBUG_LOG("server test iteration %d/%d", i, j);
+
+ send_request(inst1);
+ process_request(&remote_addr);
+ process_response(inst1, 1, 1, 1, 0);
+ advance_time(1 << inst1->local_poll);
+ }
+
+ NCR_DestroyInstance(inst1);
+
+ inst1 = NCR_GetInstance(&remote_addr, NTP_PEER, &source.params);
+ NCR_StartInstance(inst1);
+ inst2 = NCR_GetInstance(&remote_addr, NTP_PEER, &source.params);
+ NCR_StartInstance(inst2);
+
+ res_length = req_length = 0;
+
+ for (j = 0; j < 20; j++) {
+ DEBUG_LOG("peer replay test iteration %d/%d", i, j);
+
+ send_request(inst1);
+ res_buffer = req_buffer;
+ assert(!res_length || res_length == req_length);
+ res_length = req_length;
+
+ TEST_CHECK(inst1->valid_timestamps == (j > 0));
+
+ DEBUG_LOG("response 1->2");
+ process_response(inst2, j > source.params.interleaved, j > 0, j > 0, 1);
+
+ packet_queue[(j * 2) % PACKET_QUEUE_LENGTH] = res_buffer;
+
+ for (k = 0; k < j % 4 + 1; k++) {
+ DEBUG_LOG("replay ?->1 %d", k);
+ process_replay(inst1, packet_queue, MIN(j * 2 + 1, PACKET_QUEUE_LENGTH), k ? -1 : 1);
+ DEBUG_LOG("replay ?->2 %d", k);
+ process_replay(inst2, packet_queue, MIN(j * 2 + 1, PACKET_QUEUE_LENGTH), -1);
+ }
+
+ advance_time(1 << (source.params.minpoll - 1));
+
+ send_request(inst2);
+ res_buffer = req_buffer;
+ assert(res_length == req_length);
+
+ TEST_CHECK(inst2->valid_timestamps == (j > 0));
+
+ DEBUG_LOG("response 2->1");
+ process_response(inst1, 1, 1, 1, 1);
+
+ packet_queue[(j * 2 + 1) % PACKET_QUEUE_LENGTH] = res_buffer;
+
+ for (k = 0; k < j % 4 + 1; k++) {
+ DEBUG_LOG("replay ?->1 %d", k);
+ process_replay(inst1, packet_queue, MIN(j * 2 + 2, PACKET_QUEUE_LENGTH), k ? -1 : 1);
+ DEBUG_LOG("replay ?->2 %d", k);
+ process_replay(inst2, packet_queue, MIN(j * 2 + 2, PACKET_QUEUE_LENGTH), -1);
+ }
+
+ advance_time(1 << (source.params.minpoll - 1));
}
- NCR_DestroyInstance(inst);
+ NCR_DestroyInstance(inst1);
+ NCR_DestroyInstance(inst2);
}
KEY_Finalise();
diff --git a/test/unit/util.c b/test/unit/util.c
index 17f0c28..5f1a653 100644
--- a/test/unit/util.c
+++ b/test/unit/util.c
@@ -4,6 +4,7 @@
void test_unit(void) {
NTP_int64 ntp_ts, ntp_fuzz;
struct timespec ts, ts2;
+ struct timeval tv;
struct sockaddr_un sun;
double x, y;
Float f;
@@ -40,6 +41,32 @@ void test_unit(void) {
TEST_CHECK(UTI_DoubleToNtp32(1000000) == htonl(0xffffffff));
TEST_CHECK(UTI_DoubleToNtp32(-1.0) == htonl(0));
+ UTI_DoubleToTimeval(0.4e-6, &tv);
+ TEST_CHECK(tv.tv_sec == 0);
+ TEST_CHECK(tv.tv_usec == 0);
+ UTI_DoubleToTimeval(-0.4e-6, &tv);
+ TEST_CHECK(tv.tv_sec == 0);
+ TEST_CHECK(tv.tv_usec == 0);
+ UTI_DoubleToTimeval(0.5e-6, &tv);
+ TEST_CHECK(tv.tv_sec == 0);
+ TEST_CHECK(tv.tv_usec == 1);
+ UTI_DoubleToTimeval(-0.5e-6, &tv);
+ TEST_CHECK(tv.tv_sec == -1);
+ TEST_CHECK(tv.tv_usec == 999999);
+
+ UTI_DoubleToTimespec(0.9e-9, &ts);
+ TEST_CHECK(ts.tv_sec == 0);
+ TEST_CHECK(ts.tv_nsec == 0);
+ UTI_DoubleToTimespec(1.0e-9, &ts);
+ TEST_CHECK(ts.tv_sec == 0);
+ TEST_CHECK(ts.tv_nsec == 1);
+ UTI_DoubleToTimespec(-0.9e-9, &ts);
+ TEST_CHECK(ts.tv_sec == 0);
+ TEST_CHECK(ts.tv_nsec == 0);
+ UTI_DoubleToTimespec(-1.0e-9, &ts);
+ TEST_CHECK(ts.tv_sec == -1);
+ TEST_CHECK(ts.tv_nsec == 999999999);
+
ntp_ts.hi = htonl(JAN_1970);
ntp_ts.lo = 0xffffffff;
UTI_Ntp64ToTimespec(&ntp_ts, &ts);
@@ -109,6 +136,11 @@ void test_unit(void) {
TEST_CHECK(UTI_CompareNtp64(&ntp_ts, &ntp_fuzz) < 0);
TEST_CHECK(UTI_CompareNtp64(&ntp_fuzz, &ntp_ts) > 0);
+ TEST_CHECK(UTI_IsEqualAnyNtp64(&ntp_ts, &ntp_ts, NULL, NULL));
+ TEST_CHECK(UTI_IsEqualAnyNtp64(&ntp_ts, NULL, &ntp_ts, NULL));
+ TEST_CHECK(UTI_IsEqualAnyNtp64(&ntp_ts, NULL, NULL, &ntp_ts));
+ TEST_CHECK(!UTI_IsEqualAnyNtp64(&ntp_ts, &ntp_fuzz, &ntp_fuzz, &ntp_fuzz));
+
ts.tv_sec = 1;
ts.tv_nsec = 2;
ts2.tv_sec = 1;
diff --git a/util.c b/util.c
index 635a090..4b3e455 100644
--- a/util.c
+++ b/util.c
@@ -119,13 +119,11 @@ UTI_TimevalToDouble(struct timeval *tv)
void
UTI_DoubleToTimeval(double a, struct timeval *b)
{
- long int_part;
double frac_part;
- int_part = (long)(a);
- frac_part = 1.0e6 * (a - (double)(int_part));
- frac_part = frac_part > 0 ? frac_part + 0.5 : frac_part - 0.5;
- b->tv_sec = int_part;
- b->tv_usec = (long)frac_part;
+
+ b->tv_sec = a;
+ frac_part = 1.0e6 * (a - b->tv_sec);
+ b->tv_usec = frac_part > 0 ? frac_part + 0.5 : frac_part - 0.5;
UTI_NormaliseTimeval(b);
}
@@ -368,16 +366,14 @@ UTI_IPToRefid(IPAddr *ip)
case IPADDR_INET4:
return ip->addr.in4;
case IPADDR_INET6:
- if (MD5_hash < 0) {
+ if (MD5_hash < 0)
MD5_hash = HSH_GetHashId("MD5");
- assert(MD5_hash >= 0);
- }
- if (HSH_Hash(MD5_hash, (unsigned const char *)ip->addr.in6, sizeof
- (ip->addr.in6), NULL, 0, buf, 16) != 16) {
- assert(0);
- return 0;
- };
+ if (MD5_hash < 0 ||
+ HSH_Hash(MD5_hash, (const unsigned char *)ip->addr.in6, sizeof (ip->addr.in6),
+ NULL, 0, buf, sizeof (buf)) != sizeof (buf))
+ LOG_FATAL("Could not get MD5");
+
return (uint32_t)buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
}
return 0;
@@ -728,6 +724,23 @@ UTI_CompareNtp64(NTP_int64 *a, NTP_int64 *b)
/* ================================================== */
+int
+UTI_IsEqualAnyNtp64(NTP_int64 *a, NTP_int64 *b1, NTP_int64 *b2, NTP_int64 *b3)
+{
+ if (b1 && a->lo == b1->lo && a->hi == b1->hi)
+ return 1;
+
+ if (b2 && a->lo == b2->lo && a->hi == b2->hi)
+ return 1;
+
+ if (b3 && a->lo == b3->lo && a->hi == b3->hi)
+ return 1;
+
+ return 0;
+}
+
+/* ================================================== */
+
/* Seconds part of NTP timestamp correponding to the origin of the time_t format */
#define JAN_1970 0x83aa7e80UL
diff --git a/util.h b/util.h
index 02d598b..40ff729 100644
--- a/util.h
+++ b/util.h
@@ -136,6 +136,10 @@ extern int UTI_IsZeroNtp64(NTP_int64 *ts);
b, and 1 if a is after b. */
extern int UTI_CompareNtp64(NTP_int64 *a, NTP_int64 *b);
+/* Compare an NTP timestamp with up to three other timestamps. Returns 0
+ if a is not equal to any of b1, b2, and b3, 1 otherwise. */
+extern int UTI_IsEqualAnyNtp64(NTP_int64 *a, NTP_int64 *b1, NTP_int64 *b2, NTP_int64 *b3);
+
/* Convert a timespec into an NTP timestamp */
extern void UTI_TimespecToNtp64(struct timespec *src, NTP_int64 *dest, NTP_int64 *fuzz);
diff --git a/version.txt b/version.txt
index a558860..e7dee8c 100644
--- a/version.txt
+++ b/version.txt
@@ -1 +1 @@
-3.2-pre2
+3.3-pre1