summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--FAQ50
-rw-r--r--INSTALL2
-rw-r--r--NEWS22
-rw-r--r--README4
-rw-r--r--array.c15
-rw-r--r--array.h3
-rw-r--r--candm.h51
-rw-r--r--client.c239
-rw-r--r--clientlog.c55
-rw-r--r--clientlog.h12
-rw-r--r--cmdmon.c95
-rw-r--r--cmdparse.c45
-rw-r--r--cmdparse.h6
-rw-r--r--conf.c61
-rw-r--r--conf.h4
-rwxr-xr-xconfigure11
-rw-r--r--debian/changelog22
-rw-r--r--debian/postinst20
-rw-r--r--debian/tests/upstream-simulation-test-suite2
-rw-r--r--debian/usr.sbin.chronyd2
-rw-r--r--doc/chrony.conf.adoc138
-rw-r--r--doc/chrony.conf.man.in245
-rw-r--r--doc/chronyc.adoc58
-rw-r--r--doc/chronyc.man.in111
-rw-r--r--doc/chronyd.adoc6
-rw-r--r--doc/chronyd.man.in14
-rw-r--r--doc/faq.adoc51
-rw-r--r--examples/chrony.keys.example2
-rw-r--r--examples/chrony.nm-dispatcher.onoffline6
-rw-r--r--examples/chronyd-restricted.service59
-rw-r--r--getdate.c19
-rw-r--r--getdate.y19
-rw-r--r--keys.c5
-rw-r--r--logging.c29
-rw-r--r--logging.h14
-rw-r--r--main.c6
-rw-r--r--md5.c15
-rw-r--r--ntp.h7
-rw-r--r--ntp_core.c216
-rw-r--r--ntp_core.h6
-rw-r--r--ntp_io.c21
-rw-r--r--ntp_io_linux.c177
-rw-r--r--ntp_io_linux.h4
-rw-r--r--ntp_sources.c29
-rw-r--r--nts_ke_client.c31
-rw-r--r--nts_ke_server.c203
-rw-r--r--nts_ke_session.c2
-rw-r--r--nts_ntp_auth.c20
-rw-r--r--nts_ntp_auth.h2
-rw-r--r--nts_ntp_client.c17
-rw-r--r--nts_ntp_server.c48
-rw-r--r--pktlength.c4
-rw-r--r--ptp.h7
-rw-r--r--refclock_phc.c74
-rw-r--r--refclock_sock.c44
-rw-r--r--reference.c4
-rw-r--r--reports.h28
-rw-r--r--sched.c14
-rw-r--r--sched.h1
-rw-r--r--siv.h4
-rw-r--r--siv_gnutls.c16
-rw-r--r--siv_nettle.c141
-rw-r--r--smooth.c4
-rw-r--r--sources.c81
-rw-r--r--sources.h4
-rw-r--r--sourcestats.c7
-rw-r--r--sys_linux.c14
-rwxr-xr-xtest/compilation/003-sanitizers2
-rwxr-xr-xtest/simulation/106-refclock2
-rwxr-xr-xtest/simulation/110-chronyc29
-rwxr-xr-xtest/simulation/133-hwtimestamp31
-rwxr-xr-xtest/simulation/145-rtc75
-rwxr-xr-xtest/simulation/146-offline73
-rw-r--r--test/simulation/test.common6
-rwxr-xr-xtest/system/007-cmdmon11
-rw-r--r--test/unit/array.c97
-rw-r--r--test/unit/clientlog.c42
-rw-r--r--test/unit/ntp_core.c62
-rw-r--r--test/unit/nts_ke_client.c7
-rw-r--r--test/unit/nts_ke_server.c3
-rw-r--r--test/unit/nts_ntp_auth.c149
-rw-r--r--test/unit/nts_ntp_client.c30
-rw-r--r--test/unit/nts_ntp_server.c6
-rw-r--r--test/unit/siv.c110
-rw-r--r--test/unit/test.c1
-rw-r--r--test/unit/util.c16
-rw-r--r--util.c73
-rw-r--r--util.h13
-rw-r--r--version.txt2
89 files changed, 2737 insertions, 851 deletions
diff --git a/FAQ b/FAQ
index 2bbb24e..538a42c 100644
--- a/FAQ
+++ b/FAQ
@@ -274,7 +274,7 @@ authenticated servers should be configured as trusted and required to not allow
the unauthenticated servers to override the authenticated servers in the source
selection. Since chrony version 4.0, the selection options are enabled in such
a case automatically. This behaviour can be disabled or modified by the
-authselmode directive.
+authselectmode directive.
An example of a client configuration limiting the impact of the attacks could
be
@@ -511,43 +511,48 @@ is connected to a GPIO pin, or another serial port, the PPS device needs to be
specified on the command line as an additional data source. On Linux, the
ldattach utility can be used to create a PPS device for a serial device.
-The message-based time source provided by gpsd is specified as a SHM 0
-refclock, or other even number if gpsd is configured with multiple receivers.
+The PPS-based time source provided by gpsd is available as a SHM 1 refclock, or
+other odd number if gpsd is configured with multiple receivers, and also as
+SOCK /var/run/chrony.DEV.sock where DEV is the name of the serial device (e.g.
+ttyS0).
-The PPS-based time source is specified as a SHM 1 refclock (or other odd
-number), or SOCK /var/run/chrony.DEV.sock where DEV is the name of the serial
-device (e.g. ttyS0).
+The message-based time source is available as a SHM 0 refclock (or other even
+number) and since gpsd version 3.25 also as SOCK /var/run/chrony.clk.DEV.sock
+where DEV is the name of the serial device.
-With chronyd and gpsd both supporting PPS, and gpsd providing two different
-refclocks for PPS, there are three different recommended configurations:
+The SOCK refclocks should be preferred over SHM for better security (the shared
+memory segment needs to be created by chronyd or gpsd with an expected owner
+and permissions before an untrusted application or user has a chance to create
+its own in order to feed chronyd with false measurements). gpsd needs to be
+started after chronyd in order to connect to the socket.
+
+With chronyd and gpsd both supporting PPS, there are two different recommended
+configurations:
# First option
refclock SOCK /var/run/chrony.ttyS0.sock refid GPS
# Second option
-refclock SHM 1 refid GPS
-
-# Third option
refclock PPS /dev/pps0 lock NMEA refid GPS
-refclock SHM 0 offset 0.5 delay 0.1 refid NMEA noselect
-
-Each option has some advantages:
+refclock SOCK /var/run/chrony.clk.ttyS0.sock offset 0.5 delay 0.1 refid NMEA noselect
- o SOCK does not use polling (i.e. it can get samples earlier than SHM), but
- it requires gpsd to be started after chronyd in order to connect to its
- socket
+They both have some advantages:
- o SOCK and SHM 1 can be more accurate than PPS if gpsd corrects for the
- sawtooth error provided by the receiver in serial data
+ o SOCK can be more accurate than PPS if gpsd corrects for the sawtooth error
+ provided by the receiver in serial data
o PPS can be used with higher PPS rates (specified by the rate option), but
it requires a second refclock or another time source to pair pulses with
- seconds, and the SHM 0 offset needs to be specified correctly to compensate
+ seconds, and the SOCK offset needs to be specified correctly to compensate
for the message delay, while gpsd can apply HW-specific information
If the PPS signal is not available, or cannot be used for some reason, the only
option is the message-based timing
+refclock SOCK /var/run/chrony.clk.ttyS0.sock offset 0.5 delay 0.1 refid GPS
+
+or the SHM equivalent if using gpsd version before 3.25
+
refclock SHM 0 offset 0.5 delay 0.1 refid GPS
2.13. Does chrony support PTP?
@@ -612,7 +617,8 @@ following questions.
Check the Reach value printed by the chronyc's sources command. If it is zero,
it means chronyd did not get any valid responses from the NTP server you are
-trying to use. If there is a firewall between you and the server, the packets
+trying to use. If there is a firewall between you and the server, the requests
+sent to the UDP port 123 of the server or responses sent back from the port
might be blocked. Try using a tool like wireshark or tcpdump to see if you are
getting any responses from the server.
@@ -1042,4 +1048,4 @@ needs to be made to work as a service.
We have no plans to do this. Anyone is welcome to pick this work up and
contribute it back to the project.
-Last updated 2022-08-29 15:04:33 +0200
+Last updated 2023-05-10 14:28:42 +0200
diff --git a/INSTALL b/INSTALL
index e73dcd2..0d5136d 100644
--- a/INSTALL
+++ b/INSTALL
@@ -162,4 +162,4 @@ tar cvf - . | gzip -9 > chrony.tar.gz
to build a package. When untarred within the root directory, this will install
the files to the intended final locations.
-Last updated 2022-08-29 15:04:33 +0200
+Last updated 2023-05-10 14:28:42 +0200
diff --git a/NEWS b/NEWS
index 8673fd6..0b5a86d 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,25 @@
+New in version 4.4
+==================
+
+Enhancements
+------------
+* Add support for AES-GCM-SIV with Nettle >= 3.9 to shorten NTS
+ cookies to avoid some length-specific blocking of NTP on Internet
+* Add support for multiple refclocks using extpps option on one PHC
+* Add maxpoll option to hwtimestamp directive to improve PHC tracking
+ with low packet rates
+* Add hwtstimeout directive to configure timeout for late timestamps
+* Handle late hardware transmit timestamps of NTP requests on all sockets
+* Handle mismatched 32/64-bit time_t in SOCK refclock samples
+* Log important changes made by command requests (chronyc)
+* Set DSCP for IPv6 packets
+* Shorten NTS-KE retry interval when network is down
+* Warn if loading keys from file with unexpected permissions
+* Warn if source selection fails or falseticker is detected
+* Add selectopts command to modify source-specific selection options
+* Add timestamp sources to serverstats report and make its fields 64-bit
+* Add -e option to chronyc to indicate end of response
+
New in version 4.3
==================
diff --git a/README b/README
index 00214a5..c7b10b1 100644
--- a/README
+++ b/README
@@ -108,6 +108,7 @@ Erik Bryer <ebryer@spots.ab.ca>
Jonathan Cameron <jic23@cam.ac.uk>
Bryan Christianson <bryan@whatroute.net>
Juliusz Chroboczek <jch@pps.jussieu.fr>
+Dan Drown <dan-ntp@drown.org>
Kamil Dudka <kdudka@redhat.com>
Christian Ehrhardt <christian.ehrhardt@canonical.com>
Paul Elliott <pelliott@io.com>
@@ -121,6 +122,7 @@ Juergen Hannken-Illjes <hannken@eis.cs.tu-bs.de>
John Hasler <john@dhh.gt.org>
Tjalling Hattink <t.hattink@fugro.nl>
Liam Hatton <me@liamhatton.com>
+Holger Hoffstätte <holger@applied-asynchrony.com>
Jachym Holecek <jakym@volny.cz>
Håkan Johansson <f96hajo@chalmers.se>
Jim Knoble <jmknoble@pobox.com>
@@ -136,9 +138,11 @@ Victor Moroz <vim@prv.adlum.ru>
Kalle Olavi Niemitalo <tosi@stekt.oulu.fi>
Frank Otto <sandwichmacher@web.de>
Denny Page <dennypage@me.com>
+Rupesh Patel <rupatel@redhat.com>
Chris Perl <cperl@janestreet.com>
Gautier PHILIPPON <gautier.philippon@ensimag.grenoble-inp.fr>
Andreas Piesk <apiesk@virbus.de>
+Mike Ryan <msr@hsilop.net>
Baruch Siach <baruch@tkos.co.il>
Foster Snowhill <forst@forstwoof.ru>
Andreas Steinmetz <ast@domdv.de>
diff --git a/array.c b/array.c
index d70cff9..b31ba56 100644
--- a/array.c
+++ b/array.c
@@ -117,6 +117,21 @@ ARR_AppendElement(ARR_Instance array, void *element)
}
void
+ARR_RemoveElement(ARR_Instance array, unsigned int index)
+{
+ void *e, *l;
+
+ e = ARR_GetElement(array, index);
+ l = ARR_GetElement(array, array->used - 1);
+
+ if (e < l)
+ memmove(e, (char *)e + array->elem_size, (char *)l - (char *)e);
+ array->used--;
+
+ realloc_array(array, array->used);
+}
+
+void
ARR_SetSize(ARR_Instance array, unsigned int size)
{
realloc_array(array, size);
diff --git a/array.h b/array.h
index c812e84..f4fbddb 100644
--- a/array.h
+++ b/array.h
@@ -47,6 +47,9 @@ extern void *ARR_GetElements(ARR_Instance array);
/* Add a new element to the end of the array */
extern void ARR_AppendElement(ARR_Instance array, void *element);
+/* Remove element with given index */
+extern void ARR_RemoveElement(ARR_Instance array, unsigned int index);
+
/* Set the size of the array */
extern void ARR_SetSize(ARR_Instance array, unsigned int size);
diff --git a/candm.h b/candm.h
index 3071828..b5b1b7f 100644
--- a/candm.h
+++ b/candm.h
@@ -109,7 +109,8 @@
#define REQ_SELECT_DATA 69
#define REQ_RELOAD_SOURCES 70
#define REQ_DOFFSET2 71
-#define N_REQUEST_TYPES 72
+#define REQ_MODIFY_SELECTOPTS 72
+#define N_REQUEST_TYPES 73
/* Structure used to exchange timespecs independent of time_t size */
typedef struct {
@@ -121,6 +122,12 @@ typedef struct {
/* This is used in tv_sec_high for 32-bit timestamps */
#define TV_NOHIGHSEC 0x7fffffff
+/* Structure for 64-bit integers (not requiring 64-bit alignment) */
+typedef struct {
+ uint32_t high;
+ uint32_t low;
+} Integer64;
+
/* 32-bit floating-point format consisting of 7-bit signed exponent
and 25-bit signed coefficient without hidden bit.
The result is calculated as: 2^(exp - 25) * coef */
@@ -371,6 +378,15 @@ typedef struct {
int32_t EOR;
} REQ_SelectData;
+/* Mask and options reuse the REQ_ADDSRC flags */
+typedef struct {
+ IPAddr address;
+ uint32_t ref_id;
+ uint32_t mask;
+ uint32_t options;
+ int32_t EOR;
+} REQ_Modify_SelectOpts;
+
/* ================================================== */
#define PKT_TYPE_CMD_REQUEST 1
@@ -477,6 +493,7 @@ typedef struct {
REQ_NTPSourceName ntp_source_name;
REQ_AuthData auth_data;
REQ_SelectData select_data;
+ REQ_Modify_SelectOpts modify_select_opts;
} data; /* Command specific parameters */
/* Padding used to prevent traffic amplification. It only defines the
@@ -519,7 +536,8 @@ typedef struct {
#define RPY_SERVER_STATS2 22
#define RPY_SELECT_DATA 23
#define RPY_SERVER_STATS3 24
-#define N_REPLY_TYPES 25
+#define RPY_SERVER_STATS4 25
+#define N_REPLY_TYPES 26
/* Status codes */
#define STT_SUCCESS 0
@@ -654,17 +672,24 @@ typedef struct {
} RPY_ClientAccessesByIndex;
typedef struct {
- uint32_t ntp_hits;
- uint32_t nke_hits;
- uint32_t cmd_hits;
- uint32_t ntp_drops;
- uint32_t nke_drops;
- uint32_t cmd_drops;
- uint32_t log_drops;
- uint32_t ntp_auth_hits;
- uint32_t ntp_interleaved_hits;
- uint32_t ntp_timestamps;
- uint32_t ntp_span_seconds;
+ Integer64 ntp_hits;
+ Integer64 nke_hits;
+ Integer64 cmd_hits;
+ Integer64 ntp_drops;
+ Integer64 nke_drops;
+ Integer64 cmd_drops;
+ Integer64 log_drops;
+ Integer64 ntp_auth_hits;
+ Integer64 ntp_interleaved_hits;
+ Integer64 ntp_timestamps;
+ Integer64 ntp_span_seconds;
+ Integer64 ntp_daemon_rx_timestamps;
+ Integer64 ntp_daemon_tx_timestamps;
+ Integer64 ntp_kernel_rx_timestamps;
+ Integer64 ntp_kernel_tx_timestamps;
+ Integer64 ntp_hw_rx_timestamps;
+ Integer64 ntp_hw_tx_timestamps;
+ Integer64 reserved[4];
int32_t EOR;
} RPY_ServerStats;
diff --git a/client.c b/client.c
index 0a7bcc8..80bc7c0 100644
--- a/client.c
+++ b/client.c
@@ -71,6 +71,8 @@ static int source_names = 0;
static int csv_mode = 0;
+static int end_dot = 0;
+
/* ================================================== */
/* Log a message. This is a minimalistic replacement of the logging.c
implementation to avoid linking with it and other modules. */
@@ -870,6 +872,17 @@ process_cmd_doffset(CMD_Request *msg, char *line)
/* ================================================== */
static int
+convert_addsrc_sel_options(int options)
+{
+ return (options & SRC_SELECT_PREFER ? REQ_ADDSRC_PREFER : 0) |
+ (options & SRC_SELECT_NOSELECT ? REQ_ADDSRC_NOSELECT : 0) |
+ (options & SRC_SELECT_TRUST ? REQ_ADDSRC_TRUST : 0) |
+ (options & SRC_SELECT_REQUIRE ? REQ_ADDSRC_REQUIRE : 0);
+}
+
+/* ================================================== */
+
+static int
process_cmd_add_source(CMD_Request *msg, char *line)
{
CPS_NTP_Source data;
@@ -946,10 +959,7 @@ process_cmd_add_source(CMD_Request *msg, char *line)
(data.params.nts ? REQ_ADDSRC_NTS : 0) |
(data.params.copy ? REQ_ADDSRC_COPY : 0) |
(data.params.ext_fields & NTP_EF_FLAG_EXP1 ? REQ_ADDSRC_EF_EXP1 : 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) |
- (data.params.sel_options & SRC_SELECT_REQUIRE ? REQ_ADDSRC_REQUIRE : 0));
+ convert_addsrc_sel_options(data.params.sel_options));
msg->data.ntp_source.filter_length = htonl(data.params.filter_length);
msg->data.ntp_source.cert_set = htonl(data.params.cert_set);
msg->data.ntp_source.max_delay_quant =
@@ -1013,6 +1023,7 @@ give_help(void)
"sources [-a] [-v]\0Display information about current sources\0"
"sourcestats [-a] [-v]\0Display statistics about collected measurements\0"
"selectdata [-a] [-v]\0Display information about source selection\0"
+ "selectopts <address|refid> <+|-options>\0Modify selection options\0"
"reselect\0Force reselecting synchronisation source\0"
"reselectdist <dist>\0Modify reselection distance\0"
"\0\0"
@@ -1127,8 +1138,8 @@ command_name_generator(const char *text, int state)
"manual", "maxdelay", "maxdelaydevratio", "maxdelayratio", "maxpoll",
"maxupdateskew", "minpoll", "minstratum", "ntpdata", "offline", "online", "onoffline",
"polltarget", "quit", "refresh", "rekey", "reload", "reselect", "reselectdist", "reset",
- "retries", "rtcdata", "selectdata", "serverstats", "settime", "shutdown", "smoothing",
- "smoothtime", "sourcename", "sources", "sourcestats",
+ "retries", "rtcdata", "selectdata", "selectopts", "serverstats", "settime",
+ "shutdown", "smoothing", "smoothtime", "sourcename", "sources", "sourcestats",
"timeout", "tracking", "trimrtc", "waitsync", "writertc",
NULL
};
@@ -1462,24 +1473,24 @@ request_reply(CMD_Request *request, CMD_Reply *reply, int requested_reply, int v
/* ================================================== */
static void
-print_seconds(unsigned long s)
+print_seconds(uint32_t s)
{
- unsigned long d;
+ uint32_t d;
if (s == (uint32_t)-1) {
printf(" -");
} else if (s < 1200) {
- printf("%4lu", s);
+ printf("%4"PRIu32, s);
} else if (s < 36000) {
- printf("%3lum", s / 60);
+ printf("%3"PRIu32"m", s / 60);
} else if (s < 345600) {
- printf("%3luh", s / 3600);
+ printf("%3"PRIu32"h", s / 3600);
} else {
d = s / 86400;
if (d > 999) {
- printf("%3luy", d / 365);
+ printf("%3"PRIu32"y", d / 365);
} else {
- printf("%3lud", d);
+ printf("%3"PRIu32"d", d);
}
}
}
@@ -1610,8 +1621,9 @@ print_report(const char *format, ...)
va_list ap;
int i, field, sign, width, prec, spec;
const char *string;
- unsigned long long_uinteger;
unsigned int uinteger;
+ uint64_t uinteger64;
+ uint32_t uinteger32;
int integer;
struct timespec *ts;
struct tm *tm;
@@ -1709,9 +1721,9 @@ print_report(const char *format, ...)
spec == 'O' ? "seconds" : "ppm",
(dbl > 0.0) ^ (spec != 'O') ? "slow" : "fast");
break;
- case 'I': /* interval with unit */
- long_uinteger = va_arg(ap, unsigned long);
- print_seconds(long_uinteger);
+ case 'I': /* uint32_t interval with unit */
+ uinteger32 = va_arg(ap, uint32_t);
+ print_seconds(uinteger32);
break;
case 'L': /* leap status */
integer = va_arg(ap, int);
@@ -1778,8 +1790,8 @@ print_report(const char *format, ...)
print_freq_ppm(dbl);
break;
case 'R': /* reference ID in hexdecimal */
- long_uinteger = va_arg(ap, unsigned long);
- printf("%08lX", long_uinteger);
+ uinteger32 = va_arg(ap, uint32_t);
+ printf("%08"PRIX32, uinteger32);
break;
case 'S': /* offset with unit */
dbl = va_arg(ap, double);
@@ -1796,14 +1808,18 @@ print_report(const char *format, ...)
strftime(buf, sizeof (buf), "%a %b %d %T %Y", tm);
printf("%s", buf);
break;
- case 'U': /* unsigned long in decimal */
- long_uinteger = va_arg(ap, unsigned long);
- printf("%*lu", width, long_uinteger);
+ case 'U': /* uint32_t in decimal */
+ uinteger32 = va_arg(ap, uint32_t);
+ printf("%*"PRIu32, width, uinteger32);
break;
case 'V': /* timespec as seconds since epoch */
ts = va_arg(ap, struct timespec *);
printf("%s", UTI_TimespecToString(ts));
break;
+ case 'Q': /* uint64_t in decimal */
+ uinteger64 = va_arg(ap, uint64_t);
+ printf("%*"PRIu64, width, uinteger64);
+ break;
case 'b': /* unsigned int in binary */
uinteger = va_arg(ap, unsigned int);
for (i = prec - 1; i >= 0; i--)
@@ -2051,7 +2067,7 @@ process_cmd_sources(char *line)
ntohs(reply.data.source_data.stratum),
(int16_t)ntohs(reply.data.source_data.poll),
ntohs(reply.data.source_data.reachability),
- (unsigned long)ntohl(reply.data.source_data.since_sample),
+ ntohl(reply.data.source_data.since_sample),
UTI_FloatNetworkToHost(reply.data.source_data.latest_meas),
UTI_FloatNetworkToHost(reply.data.source_data.orig_latest_meas),
UTI_FloatNetworkToHost(reply.data.source_data.latest_meas_err),
@@ -2112,9 +2128,9 @@ process_cmd_sourcestats(char *line)
print_report("%-25s %3U %3U %I %+P %P %+S %S\n",
name,
- (unsigned long)ntohl(reply.data.sourcestats.n_samples),
- (unsigned long)ntohl(reply.data.sourcestats.n_runs),
- (unsigned long)ntohl(reply.data.sourcestats.span_seconds),
+ ntohl(reply.data.sourcestats.n_samples),
+ ntohl(reply.data.sourcestats.n_runs),
+ ntohl(reply.data.sourcestats.span_seconds),
UTI_FloatNetworkToHost(reply.data.sourcestats.resid_freq_ppm),
UTI_FloatNetworkToHost(reply.data.sourcestats.skew_ppm),
UTI_FloatNetworkToHost(reply.data.sourcestats.est_offset),
@@ -2162,7 +2178,7 @@ process_cmd_tracking(char *line)
"Root dispersion : %.9f seconds\n"
"Update interval : %.1f seconds\n"
"Leap status : %L\n",
- (unsigned long)ref_id, name,
+ ref_id, name,
ntohs(reply.data.tracking.stratum),
&ref_time,
UTI_FloatNetworkToHost(reply.data.tracking.current_correction),
@@ -2250,10 +2266,10 @@ process_cmd_authdata(char *line)
print_report("%-27s %4s %5U %4d %4d %I %4d %4d %4d %4d\n",
name, mode_str,
- (unsigned long)ntohl(reply.data.auth_data.key_id),
+ ntohl(reply.data.auth_data.key_id),
ntohs(reply.data.auth_data.key_type),
ntohs(reply.data.auth_data.key_length),
- (unsigned long)ntohl(reply.data.auth_data.last_ke_ago),
+ ntohl(reply.data.auth_data.last_ke_ago),
ntohs(reply.data.auth_data.ke_attempts),
ntohs(reply.data.auth_data.nak),
ntohs(reply.data.auth_data.cookies),
@@ -2348,17 +2364,16 @@ process_cmd_ntpdata(char *line)
"Total RX : %U\n"
"Total valid RX : %U\n"
"Total good RX : %U\n",
- UTI_IPToString(&remote_addr), (unsigned long)UTI_IPToRefid(&remote_addr),
+ UTI_IPToString(&remote_addr), UTI_IPToRefid(&remote_addr),
ntohs(reply.data.ntp_data.remote_port),
- UTI_IPToString(&local_addr), (unsigned long)UTI_IPToRefid(&local_addr),
+ UTI_IPToString(&local_addr), UTI_IPToRefid(&local_addr),
reply.data.ntp_data.leap, reply.data.ntp_data.version,
reply.data.ntp_data.mode, reply.data.ntp_data.stratum,
reply.data.ntp_data.poll, UTI_Log2ToDouble(reply.data.ntp_data.poll),
reply.data.ntp_data.precision, UTI_Log2ToDouble(reply.data.ntp_data.precision),
UTI_FloatNetworkToHost(reply.data.ntp_data.root_delay),
UTI_FloatNetworkToHost(reply.data.ntp_data.root_dispersion),
- (unsigned long)ntohl(reply.data.ntp_data.ref_id),
- reply.data.ntp_data.stratum <= 1 ?
+ ntohl(reply.data.ntp_data.ref_id), reply.data.ntp_data.stratum <= 1 ?
UTI_RefidToString(ntohl(reply.data.ntp_data.ref_id)) : "",
&ref_time,
UTI_FloatNetworkToHost(reply.data.ntp_data.offset),
@@ -2372,10 +2387,10 @@ process_cmd_ntpdata(char *line)
ntohs(reply.data.ntp_data.flags) & RPY_NTP_FLAG_INTERLEAVED,
ntohs(reply.data.ntp_data.flags) & RPY_NTP_FLAG_AUTHENTICATED,
reply.data.ntp_data.tx_tss_char, reply.data.ntp_data.rx_tss_char,
- (unsigned long)ntohl(reply.data.ntp_data.total_tx_count),
- (unsigned long)ntohl(reply.data.ntp_data.total_rx_count),
- (unsigned long)ntohl(reply.data.ntp_data.total_valid_count),
- (unsigned long)ntohl(reply.data.ntp_data.total_good_count),
+ ntohl(reply.data.ntp_data.total_tx_count),
+ ntohl(reply.data.ntp_data.total_rx_count),
+ ntohl(reply.data.ntp_data.total_valid_count),
+ ntohl(reply.data.ntp_data.total_good_count),
REPORT_END);
}
@@ -2447,7 +2462,7 @@ process_cmd_selectdata(char *line)
eff_options & RPY_SD_OPTION_TRUST ? 'T' : '-',
eff_options & RPY_SD_OPTION_REQUIRE ? 'R' : '-',
'-',
- (unsigned long)ntohl(reply.data.select_data.last_sample_ago),
+ ntohl(reply.data.select_data.last_sample_ago),
UTI_FloatNetworkToHost(reply.data.select_data.score),
UTI_FloatNetworkToHost(reply.data.select_data.lo_limit),
UTI_FloatNetworkToHost(reply.data.select_data.hi_limit),
@@ -2467,31 +2482,43 @@ process_cmd_serverstats(char *line)
CMD_Reply reply;
request.command = htons(REQ_SERVER_STATS);
- if (!request_reply(&request, &reply, RPY_SERVER_STATS3, 0))
+ if (!request_reply(&request, &reply, RPY_SERVER_STATS4, 0))
return 0;
- print_report("NTP packets received : %U\n"
- "NTP packets dropped : %U\n"
- "Command packets received : %U\n"
- "Command packets dropped : %U\n"
- "Client log records dropped : %U\n"
- "NTS-KE connections accepted: %U\n"
- "NTS-KE connections dropped : %U\n"
- "Authenticated NTP packets : %U\n"
- "Interleaved NTP packets : %U\n"
- "NTP timestamps held : %U\n"
- "NTP timestamp span : %U\n",
- (unsigned long)ntohl(reply.data.server_stats.ntp_hits),
- (unsigned long)ntohl(reply.data.server_stats.ntp_drops),
- (unsigned long)ntohl(reply.data.server_stats.cmd_hits),
- (unsigned long)ntohl(reply.data.server_stats.cmd_drops),
- (unsigned long)ntohl(reply.data.server_stats.log_drops),
- (unsigned long)ntohl(reply.data.server_stats.nke_hits),
- (unsigned long)ntohl(reply.data.server_stats.nke_drops),
- (unsigned long)ntohl(reply.data.server_stats.ntp_auth_hits),
- (unsigned long)ntohl(reply.data.server_stats.ntp_interleaved_hits),
- (unsigned long)ntohl(reply.data.server_stats.ntp_timestamps),
- (unsigned long)ntohl(reply.data.server_stats.ntp_span_seconds),
+ print_report("NTP packets received : %Q\n"
+ "NTP packets dropped : %Q\n"
+ "Command packets received : %Q\n"
+ "Command packets dropped : %Q\n"
+ "Client log records dropped : %Q\n"
+ "NTS-KE connections accepted: %Q\n"
+ "NTS-KE connections dropped : %Q\n"
+ "Authenticated NTP packets : %Q\n"
+ "Interleaved NTP packets : %Q\n"
+ "NTP timestamps held : %Q\n"
+ "NTP timestamp span : %Q\n"
+ "NTP daemon RX timestamps : %Q\n"
+ "NTP daemon TX timestamps : %Q\n"
+ "NTP kernel RX timestamps : %Q\n"
+ "NTP kernel TX timestamps : %Q\n"
+ "NTP hardware RX timestamps : %Q\n"
+ "NTP hardware TX timestamps : %Q\n",
+ UTI_Integer64NetworkToHost(reply.data.server_stats.ntp_hits),
+ UTI_Integer64NetworkToHost(reply.data.server_stats.ntp_drops),
+ UTI_Integer64NetworkToHost(reply.data.server_stats.cmd_hits),
+ UTI_Integer64NetworkToHost(reply.data.server_stats.cmd_drops),
+ UTI_Integer64NetworkToHost(reply.data.server_stats.log_drops),
+ UTI_Integer64NetworkToHost(reply.data.server_stats.nke_hits),
+ UTI_Integer64NetworkToHost(reply.data.server_stats.nke_drops),
+ UTI_Integer64NetworkToHost(reply.data.server_stats.ntp_auth_hits),
+ UTI_Integer64NetworkToHost(reply.data.server_stats.ntp_interleaved_hits),
+ UTI_Integer64NetworkToHost(reply.data.server_stats.ntp_timestamps),
+ UTI_Integer64NetworkToHost(reply.data.server_stats.ntp_span_seconds),
+ UTI_Integer64NetworkToHost(reply.data.server_stats.ntp_daemon_rx_timestamps),
+ UTI_Integer64NetworkToHost(reply.data.server_stats.ntp_daemon_tx_timestamps),
+ UTI_Integer64NetworkToHost(reply.data.server_stats.ntp_kernel_rx_timestamps),
+ UTI_Integer64NetworkToHost(reply.data.server_stats.ntp_kernel_tx_timestamps),
+ UTI_Integer64NetworkToHost(reply.data.server_stats.ntp_hw_rx_timestamps),
+ UTI_Integer64NetworkToHost(reply.data.server_stats.ntp_hw_tx_timestamps),
REPORT_END);
return 1;
@@ -2573,7 +2600,7 @@ process_cmd_rtcreport(char *line)
&ref_time,
ntohs(reply.data.rtc.n_samples),
ntohs(reply.data.rtc.n_runs),
- (unsigned long)ntohl(reply.data.rtc.span_seconds),
+ ntohl(reply.data.rtc.span_seconds),
UTI_FloatNetworkToHost(reply.data.rtc.rtc_seconds_fast),
UTI_FloatNetworkToHost(reply.data.rtc.rtc_gain_rate_ppm),
REPORT_END);
@@ -2648,16 +2675,15 @@ process_cmd_clients(char *line)
print_report("%-25s %6U %5U %C %C %I %6U %5U %C %I\n",
name,
- (unsigned long)ntohl(client->ntp_hits),
- (unsigned long)ntohl(client->ntp_drops),
+ ntohl(client->ntp_hits),
+ ntohl(client->ntp_drops),
client->ntp_interval,
client->ntp_timeout_interval,
- (unsigned long)ntohl(client->last_ntp_hit_ago),
- (unsigned long)ntohl(nke ? client->nke_hits : client->cmd_hits),
- (unsigned long)ntohl(nke ? client->nke_drops : client->cmd_drops),
+ ntohl(client->last_ntp_hit_ago),
+ ntohl(nke ? client->nke_hits : client->cmd_hits),
+ ntohl(nke ? client->nke_drops : client->cmd_drops),
nke ? client->nke_interval : client->cmd_interval,
- (unsigned long)ntohl(nke ? client->last_nke_hit_ago :
- client->last_cmd_hit_ago),
+ ntohl(nke ? client->last_nke_hit_ago : client->last_cmd_hit_ago),
REPORT_END);
}
@@ -2688,7 +2714,7 @@ process_cmd_manual_list(const char *line)
return 0;
n_samples = ntohl(reply.data.manual_list.n_samples);
- print_info_field("210 n_samples = %lu\n", (unsigned long)n_samples);
+ print_info_field("210 n_samples = %"PRIu32"\n", n_samples);
print_header("# Date Time(UTC) Slewed Original Residual");
@@ -2808,11 +2834,11 @@ process_cmd_activity(const char *line)
"%U sources doing burst (return to online)\n"
"%U sources doing burst (return to offline)\n"
"%U sources with unknown address\n",
- (unsigned long)ntohl(reply.data.activity.online),
- (unsigned long)ntohl(reply.data.activity.offline),
- (unsigned long)ntohl(reply.data.activity.burst_online),
- (unsigned long)ntohl(reply.data.activity.burst_offline),
- (unsigned long)ntohl(reply.data.activity.unresolved),
+ ntohl(reply.data.activity.online),
+ ntohl(reply.data.activity.offline),
+ ntohl(reply.data.activity.burst_online),
+ ntohl(reply.data.activity.burst_offline),
+ ntohl(reply.data.activity.unresolved),
REPORT_END);
return 1;
@@ -2892,6 +2918,55 @@ process_cmd_reset(CMD_Request *msg, char *line)
/* ================================================== */
static int
+process_cmd_selectopts(CMD_Request *msg, char *line)
+{
+ int mask, options, option;
+ uint32_t ref_id;
+ IPAddr ip_addr;
+ char *src, *opt;
+
+ src = line;
+ line = CPS_SplitWord(line);
+ ref_id = 0;
+
+ /* Don't allow hostnames to avoid conflicts with reference IDs */
+ if (!UTI_StringToIdIP(src, &ip_addr) && !UTI_StringToIP(src, &ip_addr)) {
+ ip_addr.family = IPADDR_UNSPEC;
+ if (CPS_ParseRefid(src, &ref_id) == 0) {
+ LOG(LOGS_ERR, "Invalid syntax for selectopts command");
+ return 0;
+ }
+ }
+
+ mask = options = 0;
+
+ while (*line != '\0') {
+ opt = line;
+ line = CPS_SplitWord(line);
+
+ if ((opt[0] != '+' && opt[0] != '-') || (option = CPS_GetSelectOption(opt + 1)) == 0) {
+ LOG(LOGS_ERR, "Invalid syntax for selectopts command");
+ return 0;
+ }
+
+ mask |= option;
+ if (opt[0] == '+')
+ options |= option;
+ }
+
+ UTI_IPHostToNetwork(&ip_addr, &msg->data.modify_select_opts.address);
+ msg->data.modify_select_opts.ref_id = htonl(ref_id);
+ msg->data.modify_select_opts.mask = htonl(mask);
+ msg->data.modify_select_opts.options = htonl(convert_addsrc_sel_options(options));
+
+ msg->command = htons(REQ_MODIFY_SELECTOPTS);
+
+ return 1;
+}
+
+/* ================================================== */
+
+static int
process_cmd_waitsync(char *line)
{
CMD_Request request;
@@ -2926,7 +3001,7 @@ process_cmd_waitsync(char *line)
skew_ppm = UTI_FloatNetworkToHost(reply.data.tracking.skew_ppm);
print_report("try: %d, refid: %R, correction: %.9f, skew: %.3f\n",
- i, (unsigned long)ref_id, correction, skew_ppm, REPORT_END);
+ i, ref_id, correction, skew_ppm, REPORT_END);
if ((ip_addr.family != IPADDR_UNSPEC ||
(ref_id != 0 && ref_id != 0x7f7f0101L /* LOCAL refid */)) &&
@@ -3193,6 +3268,8 @@ process_line(char *line)
} else if (!strcmp(command, "selectdata")) {
do_normal_submit = 0;
ret = process_cmd_selectdata(line);
+ } else if (!strcmp(command, "selectopts")) {
+ do_normal_submit = process_cmd_selectopts(&tx_message, line);
} else if (!strcmp(command, "serverstats")) {
do_normal_submit = 0;
ret = process_cmd_serverstats(line);
@@ -3243,6 +3320,10 @@ process_line(char *line)
ret = request_reply(&tx_message, &rx_message, RPY_NULL, 1);
}
+ if (end_dot) {
+ printf(".\n");
+ }
+
fflush(stderr);
if (fflush(stdout) != 0 || ferror(stdout) != 0) {
@@ -3319,6 +3400,7 @@ print_help(const char *progname)
" -n\t\tDon't resolve hostnames\n"
" -N\t\tPrint original source names\n"
" -c\t\tEnable CSV format\n"
+ " -e\t\tEnd responses with dot\n"
#if DEBUG > 0
" -d\t\tEnable debug messages\n"
#endif
@@ -3363,7 +3445,7 @@ main(int argc, char **argv)
optind = 1;
/* Parse short command-line options */
- while ((opt = getopt(argc, argv, "+46acdf:h:mnNp:v")) != -1) {
+ while ((opt = getopt(argc, argv, "+46acdef:h:mnNp:v")) != -1) {
switch (opt) {
case '4':
case '6':
@@ -3381,6 +3463,9 @@ main(int argc, char **argv)
log_min_severity = LOGS_DEBUG;
#endif
break;
+ case 'e':
+ end_dot = 1;
+ break;
case 'h':
hostnames = optarg;
break;
diff --git a/clientlog.c b/clientlog.c
index adf0c59..c408e8d 100644
--- a/clientlog.c
+++ b/clientlog.c
@@ -126,7 +126,8 @@ static int active;
/* RX and TX timestamp saved for clients using interleaved mode */
typedef struct {
uint64_t rx_ts;
- uint16_t flags;
+ uint8_t flags;
+ uint8_t tx_ts_source;
uint16_t slew_epoch;
int32_t tx_ts_offset;
} NtpTimestamps;
@@ -155,12 +156,17 @@ static NtpTimestampMap ntp_ts_map;
/* Maximum number of timestamps moved in the array to insert a new timestamp */
#define NTPTS_INSERT_LIMIT 64
+/* Maximum expected value of the timestamp source */
+#define MAX_NTP_TS NTP_TS_HARDWARE
+
/* Global statistics */
-static uint32_t total_hits[MAX_SERVICES];
-static uint32_t total_drops[MAX_SERVICES];
-static uint32_t total_ntp_auth_hits;
-static uint32_t total_ntp_interleaved_hits;
-static uint32_t total_record_drops;
+static uint64_t total_hits[MAX_SERVICES];
+static uint64_t total_drops[MAX_SERVICES];
+static uint64_t total_ntp_auth_hits;
+static uint64_t total_ntp_interleaved_hits;
+static uint64_t total_record_drops;
+static uint64_t total_ntp_rx_timestamps[MAX_NTP_TS + 1];
+static uint64_t total_ntp_tx_timestamps[MAX_NTP_TS + 1];
#define NSEC_PER_SEC 1000000000U
@@ -639,9 +645,14 @@ CLG_LimitServiceRate(CLG_Service service, int index)
/* ================================================== */
void
-CLG_LogAuthNtpRequest(void)
+CLG_UpdateNtpStats(int auth, NTP_Timestamp_Source rx_ts_src, NTP_Timestamp_Source tx_ts_src)
{
- total_ntp_auth_hits++;
+ if (auth)
+ total_ntp_auth_hits++;
+ if (rx_ts_src >= 0 && rx_ts_src <= MAX_NTP_TS)
+ total_ntp_rx_timestamps[rx_ts_src]++;
+ if (tx_ts_src >= 0 && tx_ts_src <= MAX_NTP_TS)
+ total_ntp_tx_timestamps[tx_ts_src]++;
}
/* ================================================== */
@@ -773,7 +784,8 @@ push_ntp_tss(uint32_t index)
/* ================================================== */
static void
-set_ntp_tx_offset(NtpTimestamps *tss, NTP_int64 *rx_ts, struct timespec *tx_ts)
+set_ntp_tx(NtpTimestamps *tss, NTP_int64 *rx_ts, struct timespec *tx_ts,
+ NTP_Timestamp_Source tx_src)
{
struct timespec ts;
@@ -792,12 +804,13 @@ set_ntp_tx_offset(NtpTimestamps *tss, NTP_int64 *rx_ts, struct timespec *tx_ts)
tss->tx_ts_offset = (int32_t)ts.tv_nsec + (int32_t)ts.tv_sec * (int32_t)NSEC_PER_SEC;
tss->flags |= NTPTS_VALID_TX;
+ tss->tx_ts_source = tx_src;
}
/* ================================================== */
static void
-get_ntp_tx(NtpTimestamps *tss, struct timespec *tx_ts)
+get_ntp_tx(NtpTimestamps *tss, struct timespec *tx_ts, NTP_Timestamp_Source *tx_src)
{
int32_t offset = tss->tx_ts_offset;
NTP_int64 ntp_ts;
@@ -814,12 +827,14 @@ get_ntp_tx(NtpTimestamps *tss, struct timespec *tx_ts)
} else {
UTI_ZeroTimespec(tx_ts);
}
+
+ *tx_src = tss->tx_ts_source;
}
/* ================================================== */
void
-CLG_SaveNtpTimestamps(NTP_int64 *rx_ts, struct timespec *tx_ts)
+CLG_SaveNtpTimestamps(NTP_int64 *rx_ts, struct timespec *tx_ts, NTP_Timestamp_Source tx_src)
{
NtpTimestamps *tss;
uint32_t i, index;
@@ -877,7 +892,7 @@ CLG_SaveNtpTimestamps(NTP_int64 *rx_ts, struct timespec *tx_ts)
tss->rx_ts = rx;
tss->flags = 0;
tss->slew_epoch = ntp_ts_map.slew_epoch;
- set_ntp_tx_offset(tss, rx_ts, tx_ts);
+ set_ntp_tx(tss, rx_ts, tx_ts, tx_src);
DEBUG_LOG("Saved RX+TX index=%"PRIu32" first=%"PRIu32" size=%"PRIu32,
index, ntp_ts_map.first, ntp_ts_map.size);
@@ -921,7 +936,8 @@ CLG_UndoNtpTxTimestampSlew(NTP_int64 *rx_ts, struct timespec *tx_ts)
/* ================================================== */
void
-CLG_UpdateNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts)
+CLG_UpdateNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts,
+ NTP_Timestamp_Source tx_src)
{
uint32_t index;
@@ -931,13 +947,14 @@ CLG_UpdateNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts)
if (!find_ntp_rx_ts(ntp64_to_int64(rx_ts), &index))
return;
- set_ntp_tx_offset(get_ntp_tss(index), rx_ts, tx_ts);
+ set_ntp_tx(get_ntp_tss(index), rx_ts, tx_ts, tx_src);
}
/* ================================================== */
int
-CLG_GetNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts)
+CLG_GetNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts,
+ NTP_Timestamp_Source *tx_src)
{
NtpTimestamps *tss;
uint32_t index;
@@ -953,7 +970,7 @@ CLG_GetNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts)
if (tss->flags & NTPTS_DISABLED)
return 0;
- get_ntp_tx(tss, tx_ts);
+ get_ntp_tx(tss, tx_ts, tx_src);
return 1;
}
@@ -1085,4 +1102,10 @@ CLG_GetServerStatsReport(RPT_ServerStatsReport *report)
report->ntp_span_seconds = ntp_ts_map.size > 1 ?
(get_ntp_tss(ntp_ts_map.size - 1)->rx_ts -
get_ntp_tss(0)->rx_ts) >> 32 : 0;
+ report->ntp_daemon_rx_timestamps = total_ntp_rx_timestamps[NTP_TS_DAEMON];
+ report->ntp_daemon_tx_timestamps = total_ntp_tx_timestamps[NTP_TS_DAEMON];
+ report->ntp_kernel_rx_timestamps = total_ntp_rx_timestamps[NTP_TS_KERNEL];
+ report->ntp_kernel_tx_timestamps = total_ntp_tx_timestamps[NTP_TS_KERNEL];
+ report->ntp_hw_rx_timestamps = total_ntp_rx_timestamps[NTP_TS_HARDWARE];
+ report->ntp_hw_tx_timestamps = total_ntp_tx_timestamps[NTP_TS_HARDWARE];
}
diff --git a/clientlog.h b/clientlog.h
index 2a5565e..9ea0a3f 100644
--- a/clientlog.h
+++ b/clientlog.h
@@ -42,14 +42,18 @@ extern void CLG_Finalise(void);
extern int CLG_GetClientIndex(IPAddr *client);
extern int CLG_LogServiceAccess(CLG_Service service, IPAddr *client, struct timespec *now);
extern int CLG_LimitServiceRate(CLG_Service service, int index);
-extern void CLG_LogAuthNtpRequest(void);
+extern void CLG_UpdateNtpStats(int auth, NTP_Timestamp_Source rx_ts_src,
+ NTP_Timestamp_Source tx_ts_src);
extern int CLG_GetNtpMinPoll(void);
/* Functions to save and retrieve timestamps for server interleaved mode */
-extern void CLG_SaveNtpTimestamps(NTP_int64 *rx_ts, struct timespec *tx_ts);
+extern void CLG_SaveNtpTimestamps(NTP_int64 *rx_ts, struct timespec *tx_ts,
+ NTP_Timestamp_Source tx_src);
extern void CLG_UndoNtpTxTimestampSlew(NTP_int64 *rx_ts, struct timespec *tx_ts);
-extern void CLG_UpdateNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts);
-extern int CLG_GetNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts);
+extern void CLG_UpdateNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts,
+ NTP_Timestamp_Source tx_src);
+extern int CLG_GetNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts,
+ NTP_Timestamp_Source *tx_src);
extern void CLG_DisableNtpTimestamps(NTP_int64 *rx_ts);
/* And some reporting functions, for use by chronyc. */
diff --git a/cmdmon.c b/cmdmon.c
index e48a2fc..588b400 100644
--- a/cmdmon.c
+++ b/cmdmon.c
@@ -144,6 +144,7 @@ static const char permissions[] = {
PERMIT_AUTH, /* SELECT_DATA */
PERMIT_AUTH, /* RELOAD_SOURCES */
PERMIT_AUTH, /* DOFFSET2 */
+ PERMIT_AUTH, /* MODIFY_SELECTOPTS */
};
/* ================================================== */
@@ -703,6 +704,17 @@ handle_cmdaccheck(CMD_Request *rx_message, CMD_Reply *tx_message)
/* ================================================== */
+static int
+convert_addsrc_select_options(int flags)
+{
+ return (flags & REQ_ADDSRC_PREFER ? SRC_SELECT_PREFER : 0) |
+ (flags & REQ_ADDSRC_NOSELECT ? SRC_SELECT_NOSELECT : 0) |
+ (flags & REQ_ADDSRC_TRUST ? SRC_SELECT_TRUST : 0) |
+ (flags & REQ_ADDSRC_REQUIRE ? SRC_SELECT_REQUIRE : 0);
+}
+
+/* ================================================== */
+
static void
handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
{
@@ -773,11 +785,7 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
params.copy = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_COPY ? 1 : 0;
params.ext_fields =
ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_EF_EXP1 ? NTP_EF_FLAG_EXP1 : 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) |
- (ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_TRUST ? SRC_SELECT_TRUST : 0) |
- (ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_REQUIRE ? SRC_SELECT_REQUIRE : 0);
+ params.sel_options = convert_addsrc_select_options(ntohl(rx_message->data.ntp_source.flags));
status = NSR_AddSourceByName(name, port, pool, type, &params, NULL);
switch (status) {
@@ -1169,18 +1177,36 @@ handle_server_stats(CMD_Request *rx_message, CMD_Reply *tx_message)
RPT_ServerStatsReport report;
CLG_GetServerStatsReport(&report);
- tx_message->reply = htons(RPY_SERVER_STATS3);
- tx_message->data.server_stats.ntp_hits = htonl(report.ntp_hits);
- tx_message->data.server_stats.nke_hits = htonl(report.nke_hits);
- tx_message->data.server_stats.cmd_hits = htonl(report.cmd_hits);
- tx_message->data.server_stats.ntp_drops = htonl(report.ntp_drops);
- tx_message->data.server_stats.nke_drops = htonl(report.nke_drops);
- tx_message->data.server_stats.cmd_drops = htonl(report.cmd_drops);
- tx_message->data.server_stats.log_drops = htonl(report.log_drops);
- tx_message->data.server_stats.ntp_auth_hits = htonl(report.ntp_auth_hits);
- tx_message->data.server_stats.ntp_interleaved_hits = htonl(report.ntp_interleaved_hits);
- tx_message->data.server_stats.ntp_timestamps = htonl(report.ntp_timestamps);
- tx_message->data.server_stats.ntp_span_seconds = htonl(report.ntp_span_seconds);
+ tx_message->reply = htons(RPY_SERVER_STATS4);
+ tx_message->data.server_stats.ntp_hits = UTI_Integer64HostToNetwork(report.ntp_hits);
+ tx_message->data.server_stats.nke_hits = UTI_Integer64HostToNetwork(report.nke_hits);
+ tx_message->data.server_stats.cmd_hits = UTI_Integer64HostToNetwork(report.cmd_hits);
+ tx_message->data.server_stats.ntp_drops = UTI_Integer64HostToNetwork(report.ntp_drops);
+ tx_message->data.server_stats.nke_drops = UTI_Integer64HostToNetwork(report.nke_drops);
+ tx_message->data.server_stats.cmd_drops = UTI_Integer64HostToNetwork(report.cmd_drops);
+ tx_message->data.server_stats.log_drops = UTI_Integer64HostToNetwork(report.log_drops);
+ tx_message->data.server_stats.ntp_auth_hits =
+ UTI_Integer64HostToNetwork(report.ntp_auth_hits);
+ tx_message->data.server_stats.ntp_interleaved_hits =
+ UTI_Integer64HostToNetwork(report.ntp_interleaved_hits);
+ tx_message->data.server_stats.ntp_timestamps =
+ UTI_Integer64HostToNetwork(report.ntp_timestamps);
+ tx_message->data.server_stats.ntp_span_seconds =
+ UTI_Integer64HostToNetwork(report.ntp_span_seconds);
+ tx_message->data.server_stats.ntp_daemon_rx_timestamps =
+ UTI_Integer64HostToNetwork(report.ntp_daemon_rx_timestamps);
+ tx_message->data.server_stats.ntp_daemon_tx_timestamps =
+ UTI_Integer64HostToNetwork(report.ntp_daemon_tx_timestamps);
+ tx_message->data.server_stats.ntp_kernel_rx_timestamps =
+ UTI_Integer64HostToNetwork(report.ntp_kernel_rx_timestamps);
+ tx_message->data.server_stats.ntp_kernel_tx_timestamps =
+ UTI_Integer64HostToNetwork(report.ntp_kernel_tx_timestamps);
+ tx_message->data.server_stats.ntp_hw_rx_timestamps =
+ UTI_Integer64HostToNetwork(report.ntp_hw_rx_timestamps);
+ tx_message->data.server_stats.ntp_hw_tx_timestamps =
+ UTI_Integer64HostToNetwork(report.ntp_hw_tx_timestamps);
+ memset(tx_message->data.server_stats.reserved, 0xff,
+ sizeof (tx_message->data.server_stats.reserved));
}
/* ================================================== */
@@ -1328,7 +1354,7 @@ handle_auth_data(CMD_Request *rx_message, CMD_Reply *tx_message)
/* ================================================== */
static uint16_t
-convert_select_options(int options)
+convert_sd_sel_options(int options)
{
return (options & SRC_SELECT_PREFER ? RPY_SD_OPTION_PREFER : 0) |
(options & SRC_SELECT_NOSELECT ? RPY_SD_OPTION_NOSELECT : 0) |
@@ -1355,8 +1381,8 @@ handle_select_data(CMD_Request *rx_message, CMD_Reply *tx_message)
tx_message->data.select_data.state_char = report.state_char;
tx_message->data.select_data.authentication = report.authentication;
tx_message->data.select_data.leap = report.leap;
- tx_message->data.select_data.conf_options = htons(convert_select_options(report.conf_options));
- tx_message->data.select_data.eff_options = htons(convert_select_options(report.eff_options));
+ tx_message->data.select_data.conf_options = htons(convert_sd_sel_options(report.conf_options));
+ tx_message->data.select_data.eff_options = htons(convert_sd_sel_options(report.eff_options));
tx_message->data.select_data.last_sample_ago = htonl(report.last_sample_ago);
tx_message->data.select_data.score = UTI_FloatHostToNetwork(report.score);
tx_message->data.select_data.hi_limit = UTI_FloatHostToNetwork(report.hi_limit);
@@ -1364,6 +1390,24 @@ handle_select_data(CMD_Request *rx_message, CMD_Reply *tx_message)
}
/* ================================================== */
+
+static void
+handle_modify_selectopts(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ int mask, options;
+ uint32_t ref_id;
+ IPAddr ip_addr;
+
+ UTI_IPNetworkToHost(&rx_message->data.modify_select_opts.address, &ip_addr);
+ ref_id = ntohl(rx_message->data.modify_select_opts.ref_id);
+ mask = ntohl(rx_message->data.modify_select_opts.mask);
+ options = convert_addsrc_select_options(ntohl(rx_message->data.modify_select_opts.options));
+
+ if (!SRC_ModifySelectOptions(&ip_addr, ref_id, options, mask))
+ tx_message->status = htons(STT_NOSUCHSOURCE);
+}
+
+/* ================================================== */
/* Read a packet and process it */
static void
@@ -1515,6 +1559,8 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
}
if (allowed) {
+ LOG_SetContext(LOGC_Command);
+
switch(rx_command) {
case REQ_NULL:
/* Do nothing */
@@ -1757,11 +1803,17 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
handle_reload_sources(&rx_message, &tx_message);
break;
+ case REQ_MODIFY_SELECTOPTS:
+ handle_modify_selectopts(&rx_message, &tx_message);
+ break;
+
default:
DEBUG_LOG("Unhandled command %d", rx_command);
tx_message.status = htons(STT_FAILED);
break;
}
+
+ LOG_UnsetContext(LOGC_Command);
} else {
tx_message.status = htons(STT_UNAUTH);
}
@@ -1795,6 +1847,9 @@ CAM_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all)
if (status == ADF_BADSUBNET) {
return 0;
} else if (status == ADF_SUCCESS) {
+ LOG(LOG_GetContextSeverity(LOGC_Command), "%s%s %s access from %s",
+ allow ? "Allowed" : "Denied", all ? " all" : "", "command",
+ UTI_IPSubnetToString(ip_addr, subnet_bits));
return 1;
} else {
return 0;
diff --git a/cmdparse.c b/cmdparse.c
index 1a9e210..c20e8a3 100644
--- a/cmdparse.c
+++ b/cmdparse.c
@@ -44,7 +44,7 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
{
char *hostname, *cmd;
uint32_t ef_type;
- int n;
+ int n, sel_option;
src->port = SRC_DEFAULT_PORT;
src->params.minpoll = SRC_DEFAULT_MINPOLL;
@@ -101,14 +101,6 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
src->params.iburst = 1;
} else if (!strcasecmp(cmd, "offline")) {
src->params.connectivity = SRC_OFFLINE;
- } else if (!strcasecmp(cmd, "noselect")) {
- src->params.sel_options |= SRC_SELECT_NOSELECT;
- } else if (!strcasecmp(cmd, "prefer")) {
- src->params.sel_options |= SRC_SELECT_PREFER;
- } else if (!strcasecmp(cmd, "require")) {
- src->params.sel_options |= SRC_SELECT_REQUIRE;
- } else if (!strcasecmp(cmd, "trust")) {
- src->params.sel_options |= SRC_SELECT_TRUST;
} else if (!strcasecmp(cmd, "certset")) {
if (sscanf(line, "%"SCNu32"%n", &src->params.cert_set, &n) != 1)
return 0;
@@ -187,6 +179,8 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
return 0;
} else if (!strcasecmp(cmd, "xleave")) {
src->params.interleaved = 1;
+ } else if ((sel_option = CPS_GetSelectOption(cmd)) != 0) {
+ src->params.sel_options |= sel_option;
} else {
return 0;
}
@@ -198,6 +192,23 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
/* ================================================== */
int
+CPS_GetSelectOption(char *option)
+{
+ if (!strcasecmp(option, "noselect")) {
+ return SRC_SELECT_NOSELECT;
+ } else if (!strcasecmp(option, "prefer")) {
+ return SRC_SELECT_PREFER;
+ } else if (!strcasecmp(option, "require")) {
+ return SRC_SELECT_REQUIRE;
+ } else if (!strcasecmp(option, "trust")) {
+ return SRC_SELECT_TRUST;
+ }
+ return 0;
+}
+
+/* ================================================== */
+
+int
CPS_ParseAllowDeny(char *line, int *all, IPAddr *ip, int *subnet_bits)
{
char *p, *net, *slash;
@@ -396,3 +407,19 @@ CPS_ParseKey(char *line, uint32_t *id, const char **type, char **key)
return 1;
}
+
+/* ================================================== */
+
+int
+CPS_ParseRefid(char *line, uint32_t *ref_id)
+{
+ int i;
+
+ for (i = *ref_id = 0; line[i] && !isspace((unsigned char)line[i]); i++) {
+ if (i >= 4)
+ return 0;
+ *ref_id |= (uint32_t)line[i] << (24 - i * 8);
+ }
+
+ return i;
+}
diff --git a/cmdparse.h b/cmdparse.h
index fd1eb43..7a87979 100644
--- a/cmdparse.h
+++ b/cmdparse.h
@@ -39,6 +39,9 @@ typedef struct {
/* Parse a command to add an NTP server or peer */
extern int CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src);
+/* Get an NTP/refclock select option */
+extern int CPS_GetSelectOption(char *option);
+
/* Parse a command to allow/deny access */
extern int CPS_ParseAllowDeny(char *line, int *all, IPAddr *ip, int *subnet_bits);
@@ -54,4 +57,7 @@ extern char *CPS_SplitWord(char *line);
/* Parse a key from keyfile */
extern int CPS_ParseKey(char *line, uint32_t *id, const char **type, char **key);
+/* Parse a refclock reference ID (returns number of characters) */
+extern int CPS_ParseRefid(char *line, uint32_t *ref_id);
+
#endif /* GOT_CMDPARSE_H */
diff --git a/conf.c b/conf.c
index 8dd2743..bce06fa 100644
--- a/conf.c
+++ b/conf.c
@@ -274,6 +274,9 @@ static int no_system_cert = 0;
/* Array of CNF_HwTsInterface */
static ARR_Instance hwts_interfaces;
+/* Timeout for resuming reading from sockets waiting for HW TX timestamp */
+static double hwts_timeout = 0.001;
+
/* PTP event port (disabled by default) */
static int ptp_port = 0;
@@ -602,6 +605,8 @@ CNF_ParseLine(const char *filename, int number, char *line)
parse_string(p, &hwclock_file);
} else if (!strcasecmp(command, "hwtimestamp")) {
parse_hwtimestamp(p);
+ } else if (!strcasecmp(command, "hwtstimeout")) {
+ parse_double(p, &hwts_timeout);
} else if (!strcasecmp(command, "include")) {
parse_include(p);
} else if (!strcasecmp(command, "initstepslew")) {
@@ -862,11 +867,10 @@ static void
parse_refclock(char *line)
{
int n, poll, dpoll, filter_length, pps_rate, min_samples, max_samples, sel_options;
- int local, max_lock_age, pps_forced, stratum, tai;
+ int local, max_lock_age, pps_forced, sel_option, stratum, tai;
uint32_t ref_id, lock_ref_id;
double offset, delay, precision, max_dispersion, pulse_width;
char *p, *cmd, *name, *param;
- unsigned char ref[5];
RefclockParameters *refclock;
poll = 4;
@@ -912,13 +916,11 @@ parse_refclock(char *line)
line = CPS_SplitWord(line);
if (!strcasecmp(cmd, "refid")) {
- if (sscanf(line, "%4s%n", (char *)ref, &n) != 1)
+ if ((n = CPS_ParseRefid(line, &ref_id)) == 0)
break;
- ref_id = (uint32_t)ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3];
} else if (!strcasecmp(cmd, "lock")) {
- if (sscanf(line, "%4s%n", (char *)ref, &n) != 1)
+ if ((n = CPS_ParseRefid(line, &lock_ref_id)) == 0)
break;
- lock_ref_id = (uint32_t)ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3];
} else if (!strcasecmp(cmd, "poll")) {
if (sscanf(line, "%d%n", &poll, &n) != 1) {
break;
@@ -971,18 +973,9 @@ parse_refclock(char *line)
} else if (!strcasecmp(cmd, "width")) {
if (sscanf(line, "%lf%n", &pulse_width, &n) != 1)
break;
- } else if (!strcasecmp(cmd, "noselect")) {
- n = 0;
- sel_options |= SRC_SELECT_NOSELECT;
- } else if (!strcasecmp(cmd, "prefer")) {
- n = 0;
- sel_options |= SRC_SELECT_PREFER;
- } else if (!strcasecmp(cmd, "trust")) {
+ } else if ((sel_option = CPS_GetSelectOption(cmd)) != 0) {
n = 0;
- sel_options |= SRC_SELECT_TRUST;
- } else if (!strcasecmp(cmd, "require")) {
- n = 0;
- sel_options |= SRC_SELECT_REQUIRE;
+ sel_options |= sel_option;
} else {
other_parse_error("Invalid refclock option");
return;
@@ -1437,8 +1430,8 @@ static void
parse_hwtimestamp(char *line)
{
CNF_HwTsInterface *iface;
+ int n, maxpoll_set = 0;
char *p, filter[5];
- int n;
if (!*line) {
command_parse_error();
@@ -1468,6 +1461,10 @@ parse_hwtimestamp(char *line)
} else if (!strcasecmp(p, "minpoll")) {
if (sscanf(line, "%d%n", &iface->minpoll, &n) != 1)
break;
+ } else if (!strcasecmp(p, "maxpoll")) {
+ if (sscanf(line, "%d%n", &iface->maxpoll, &n) != 1)
+ break;
+ maxpoll_set = 1;
} else if (!strcasecmp(p, "minsamples")) {
if (sscanf(line, "%d%n", &iface->min_samples, &n) != 1)
break;
@@ -1503,6 +1500,9 @@ parse_hwtimestamp(char *line)
if (*p)
command_parse_error();
+
+ if (!maxpoll_set)
+ iface->maxpoll = iface->minpoll + 1;
}
/* ================================================== */
@@ -1704,6 +1704,8 @@ reload_source_dirs(void)
new_ids = ARR_GetElements(ntp_source_ids);
unresolved = 0;
+ LOG_SetContext(LOGC_SourceFile);
+
qsort(new_sources, new_size, sizeof (new_sources[0]), compare_sources);
for (i = j = 0; i < prev_size || j < new_size; ) {
@@ -1739,6 +1741,8 @@ reload_source_dirs(void)
}
}
+ LOG_UnsetContext(LOGC_SourceFile);
+
for (i = 0; i < prev_size; i++)
Free(prev_sources[i].params.name);
Free(prev_sources);
@@ -1783,6 +1787,19 @@ CNF_CreateDirs(uid_t uid, gid_t gid)
/* ================================================== */
void
+CNF_CheckReadOnlyAccess(void)
+{
+ unsigned int i;
+
+ if (keys_file)
+ UTI_CheckReadOnlyAccess(keys_file);
+ for (i = 0; i < ARR_GetSize(nts_server_key_files); i++)
+ UTI_CheckReadOnlyAccess(*(char **)ARR_GetElement(nts_server_key_files, i));
+}
+
+/* ================================================== */
+
+void
CNF_AddInitSources(void)
{
CPS_NTP_Source cps_source;
@@ -2500,6 +2517,14 @@ CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface)
/* ================================================== */
+double
+CNF_GetHwTsTimeout(void)
+{
+ return hwts_timeout;
+}
+
+/* ================================================== */
+
int
CNF_GetPtpPort(void)
{
diff --git a/conf.h b/conf.h
index 11fd11d..ca18abc 100644
--- a/conf.h
+++ b/conf.h
@@ -44,6 +44,8 @@ extern void CNF_ParseLine(const char *filename, int number, char *line);
extern void CNF_CreateDirs(uid_t uid, gid_t gid);
+extern void CNF_CheckReadOnlyAccess(void);
+
extern void CNF_AddInitSources(void);
extern void CNF_AddSources(void);
extern void CNF_AddBroadcasts(void);
@@ -142,6 +144,7 @@ typedef enum {
typedef struct {
char *name;
int minpoll;
+ int maxpoll;
int min_samples;
int max_samples;
int nocrossts;
@@ -152,6 +155,7 @@ typedef struct {
} CNF_HwTsInterface;
extern int CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface);
+extern double CNF_GetHwTsTimeout(void);
extern int CNF_GetPtpPort(void);
diff --git a/configure b/configure
index fd777c8..e8d7556 100755
--- a/configure
+++ b/configure
@@ -979,15 +979,22 @@ if [ $feat_ntp = "1" ] && [ $feat_nts = "1" ] && [ $try_gnutls = "1" ]; then
gnutls_priority_init2((void *)1, "", NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND) +
gnutls_prf_rfc5705((void *)1, 0, "", 0, "", 16, (void *)2);'
then
- if test_code 'SIV in nettle' \
+ if test_code 'AES-SIV-CMAC in nettle' \
'nettle/siv-cmac.h' "" "$LIBS" \
'siv_cmac_aes128_set_key((void *)1, (void *)2);'
then
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o"
add_def HAVE_SIV
add_def HAVE_NETTLE_SIV_CMAC
+ if test_code 'AES-GCM-SIV in nettle' \
+ 'nettle/siv-gcm.h' "" "$LIBS" \
+ 'siv_gcm_aes128_encrypt_message((void *)1, 0, NULL, 0, (void *)2, 16, (void *)3,
+ (void *)4);'
+ then
+ add_def HAVE_NETTLE_SIV_GCM
+ fi
else
- if test_code 'SIV in gnutls' 'gnutls/crypto.h' \
+ if test_code 'AES-SIV-CMAC in gnutls' 'gnutls/crypto.h' \
"$test_cflags" "$test_link $LIBS" '
return gnutls_aead_cipher_init((void *)1, GNUTLS_CIPHER_AES_128_SIV, (void *)2);'
then
diff --git a/debian/changelog b/debian/changelog
index d423f6b..98aadaf 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,15 @@
+chrony (4.4~pre1-1) experimental; urgency=medium
+
+ * Merge branch 'debian/unstable' into 'debian/latest'.
+
+ * Import upstream version 4.4-pre1:
+ - Please see /usr/share/doc/chrony/NEWS.gz for the release notes.
+
+ * debian/tests/upstream-simulation-test-suite:
+ - Update clknetsim version.
+
+ -- Vincent Blut <vincent.debian@free.fr> Wed, 10 May 2023 16:26:48 +0200
+
chrony (4.3-3) experimental; urgency=medium
* debian/control:
@@ -15,6 +27,16 @@ chrony (4.3-3) experimental; urgency=medium
-- Vincent Blut <vincent.debian@free.fr> Mon, 27 Mar 2023 19:57:20 +0200
+chrony (4.3-2+deb12u1) unstable; urgency=medium
+
+ * debian/usr.sbin.chronyd:
+ - Modify the AppArmor profile to allow more gpsd socket names. This will
+ avoid the need for users to override the profile to let chronyd consume PPS
+ samples or serial time supplied by gpsd over a Unix-domain socket.
+ Thanks to Ryan Govostes for the report. (Closes: #1034519)
+
+ -- Vincent Blut <vincent.debian@free.fr> Mon, 08 May 2023 22:05:00 +0200
+
chrony (4.3-2) unstable; urgency=medium
* debian/control:
diff --git a/debian/postinst b/debian/postinst
index 8a00a77..1379453 100644
--- a/debian/postinst
+++ b/debian/postinst
@@ -11,11 +11,10 @@ set -e
case "$1" in
configure)
- adduser --force-badname \
- --system \
+ adduser --system \
--group \
--quiet \
- --gecos "Chrony daemon" \
+ --comment "Chrony daemon" \
--home /var/lib/chrony \
--no-create-home _chrony
@@ -46,21 +45,6 @@ case "$1" in
fi
done
fi
-
- if [ -n "$2" ] && dpkg --compare-versions "$2" lt 4.0~pre4-1; then
- # Migrate NTP sources obtained from DHCP to /run/chrony-dhcp
- mkdir -p /run/chrony-dhcp
- for file in $(find /var/lib/dhcp/ -type f -name "chrony.servers.*"); do
- sed 's/.*/server &/' < "$file" > /run/chrony-dhcp/"${file##*servers.}.sources"
- done
-
- # Remove the staled PID file resulting from migrating its path from
- # /run to /run/chrony/. Overriding dh_installinit and
- # dh_systemd_start to use the --no-restart-after-upgrade option
- # was a possibility but chronyd would have been down even longer
- # during the upgrade.
- rm -f /run/chronyd.pid
- fi
;;
abort-upgrade|abort-remove|abort-deconfigure)
diff --git a/debian/tests/upstream-simulation-test-suite b/debian/tests/upstream-simulation-test-suite
index a0b8517..a89bd44 100644
--- a/debian/tests/upstream-simulation-test-suite
+++ b/debian/tests/upstream-simulation-test-suite
@@ -7,7 +7,7 @@
set -e
testdir="$PWD/test/simulation"
-clknetsim_ver=f00531b
+clknetsim_ver=13b0a5b
clknetsim_src=https://github.com/mlichvar/clknetsim/archive/"$clknetsim_ver"/clknetsim-"$clknetsim_ver".tar.gz
clknetsim_archive=$(basename "$clknetsim_src")
diff --git a/debian/usr.sbin.chronyd b/debian/usr.sbin.chronyd
index 330e8af..bc52d4f 100644
--- a/debian/usr.sbin.chronyd
+++ b/debian/usr.sbin.chronyd
@@ -59,7 +59,7 @@ abi <abi/3.0>,
# Configs using a 'chrony.' prefix like the tempcomp config file example
/etc/chrony.* r,
# Example gpsd socket is outside @{run}/chrony/
- @{run}/chrony.tty{,*}.sock rw,
+ @{run}/chrony.*.sock rw,
# To sign replies to MS-SNTP clients by the smbd daemon
/var/lib/samba/ntp_signd/socket rw,
diff --git a/doc/chrony.conf.adoc b/doc/chrony.conf.adoc
index 2cf5326..10a2dda 100644
--- a/doc/chrony.conf.adoc
+++ b/doc/chrony.conf.adoc
@@ -473,16 +473,41 @@ instead.
Examples:
+
----
-refclock PPS /dev/pps0 lock NMEA refid GPS
-refclock SHM 0 offset 0.5 delay 0.2 refid NMEA noselect
+refclock PPS /dev/pps0 lock NMEA refid GPS1
+refclock SOCK /var/run/chrony.clk.ttyS0.sock offset 0.5 delay 0.2 refid NMEA noselect
refclock PPS /dev/pps1:clear refid GPS2
----
+
+*SOCK*:::
+Unix domain socket driver. This driver uses a datagram socket to receive
+samples from another application running on the system. The parameter is the
+path to the socket, which *chronyd* will create on start. The format of the
+messages is described in the _refclock_sock.c_ file in the chrony source code.
++
+An application which supports the SOCK protocol is the *gpsd* daemon. It can
+provide accurate measurements using the receiver's PPS signal, and since
+version 3.25 also (much less accurate) measurements based on the timing of
+serial data (e.g. NMEA), which can be useful when the receiver does not provide
+a PPS signal, or it cannot be connected to the computer. The paths where *gpsd*
+expects the sockets to be created by *chronyd* are described in the *gpsd(8)*
+man page. Note that *gpsd* needs to be started after *chronyd* in order to
+connect to the socket.
++
+Examples:
++
+----
+refclock SOCK /var/run/chrony.ttyS0.sock refid GPS1 poll 2 filter 4
+refclock SOCK /var/run/chrony.clk.ttyUSB0.sock refid GPS2 offset 0.2 delay 0.1
+----
++
*SHM*:::
-NTP shared memory driver. This driver uses a shared memory segment to receive
-samples from another process (e.g. *gpsd*). The parameter is the number of the
-shared memory segment, typically a small number like 0, 1, 2, or 3. The driver
-supports the following option:
+NTP shared memory driver. This driver implements the protocol of the *ntpd*
+driver type 28. It is functionally similar to the SOCK driver, but uses a
+shared memory segment instead of a socket. The parameter is the unit number,
+typically a small number like 0, 1, 2, or 3, from which is derived the key of
+the memory segment as 0x4e545030 + unit.
++
+The driver supports the following option:
+
*perm*=_mode_::::
This option specifies the permissions of the shared memory segment created by
@@ -490,6 +515,16 @@ This option specifies the permissions of the shared memory segment created by
(read-write access for owner only).
{blank}:::
+
+Unlike with the SOCK driver, there is no prescribed order of starting *chronyd*
+and the program providing measurements. Both are expected to create the memory
+segment if it does not exist. *chronyd* will attach to an existing segment even
+if it has a different owner than root or different permissions than the
+permissions specified by the *perm* option. The segment needs to be created
+before untrusted applications or users can execute code to prevent an attacker
+from feeding *chronyd* with false measurements. The owner and permissions of
+the segment can be verified with the *ipcs -m* command. For this reason, the
+SHM driver is deprecated in favor of SOCK.
++
Examples:
+
----
@@ -497,23 +532,6 @@ refclock SHM 0 poll 3 refid GPS1
refclock SHM 1:perm=0644 refid GPS2
----
+
-*SOCK*:::
-Unix domain socket driver. It is similar to the SHM driver, but samples are
-received from a Unix domain socket instead of shared memory and the messages
-have a different format. The parameter is the path to the socket, which
-*chronyd* creates on start. An advantage over the SHM driver is that SOCK does
-not require polling and it can receive PPS samples with incomplete time. The
-format of the messages is described in the _refclock_sock.c_ file in the chrony
-source code.
-+
-An application which supports the SOCK protocol is the *gpsd* daemon. The path
-where *gpsd* expects the socket to be created is described in the *gpsd(8)* man
-page. For example:
-+
-----
-refclock SOCK /var/run/chrony.ttyS0.sock
-----
-+
*PHC*:::
PTP hardware clock (PHC) driver. The parameter is the path to the device of
the PTP clock which should be used as a time source. If the clock is kept in
@@ -624,7 +642,9 @@ noise in the measurements. With each poll about 40 percent of the stored
samples are discarded and one final sample is calculated as an average of the
remaining samples. If the length is 4 or more, at least 4 samples have to be
collected between polls. For lengths below 4, the filter has to be full. The
-default is 64.
+default is 64. With drivers that perform their own polling (PPS, PHC, SHM), the
+maximum value is adjusted to the number of driver polls per source poll, i.e.
+2^(_poll_ - _dpoll_).
*prefer*:::
Prefer this source over sources without the prefer option.
*noselect*:::
@@ -809,6 +829,10 @@ This directory is used also by the <<ntsdumpdir2,NTS server>> to save keys.
This directive specifies the maximum interval between NTS-KE handshakes (in
seconds) in order to refresh the keys authenticating NTP packets. The default
value is 2419200 (4 weeks) and the maximum value is 2^31-1 (68 years).
++
+The interval must be longer than polling intervals of all configured NTP
+sources using NTS, otherwise the source with a longer polling interval will
+refresh the keys on each poll and no NTP packets will be exchanged.
[[ntstrustedcerts]]*ntstrustedcerts* [_set-ID_] _file_|_directory_::
This directive specifies a file or directory containing certificates (in the
@@ -923,7 +947,7 @@ As an example, the following configuration using the default *mix* mode:
server foo.example.net nts
server bar.example.net nts
server baz.example.net
-refclock SHM 0
+refclock SOCK /var/run/chrony.ttyS0.sock
----
+
is equivalent to the following configuration using the *ignore* mode:
@@ -933,7 +957,7 @@ authselectmode ignore
server foo.example.net nts require trust
server bar.example.net nts require trust
server baz.example.net
-refclock SHM 0 require trust
+refclock /var/run/chrony.ttyS0.sock require trust
----
[[combinelimit]]*combinelimit* _limit_::
@@ -1302,10 +1326,9 @@ It should be noted that this is not the only means of protection against using
unreliable estimates. At all times, *chronyd* keeps track of both the estimated
gain or loss rate, and the error bound on the estimate. When a new estimate is
generated following another measurement from one of the sources, a weighted
-combination algorithm is used to update the master estimate. So if *chronyd*
-has an existing highly-reliable master estimate and a new estimate is generated
-which has large error bounds, the existing master estimate will dominate in the
-new master estimate.
+combination algorithm is used to update the existing estimate. If it has
+significantly smaller error bounds than the new estimate, the existing estimate
+will dominate in the new combined value.
[[maxslewrate]]*maxslewrate* _rate-in-ppm_::
The *maxslewrate* directive sets the maximum rate at which *chronyd* is allowed
@@ -1735,7 +1758,10 @@ save the keys to the _ntskeys_ file and will reload the keys from the file when
the <<chronyc.adoc#rekey,*rekey*>> command is issued in *chronyc*. The file can
be periodically copied from another server running *chronyd* (which does
not have *ntsrotate* set to 0) in order to have one or more servers dedicated
-to NTS-KE. The NTS-KE servers need to be configured with the
+to NTS-KE. The file includes the subsequent key to which the NTS-KE server will
+switch on the next rotation, i.e. the process copying and reloading the file
+does not need to be timed precisely (it can be delayed by up to one rotation
+interval). The NTS-KE servers need to be configured with the
<<ntsntpserver,*ntsntpserver*>> directive to point the clients to the right NTP
server.
+
@@ -2218,6 +2244,13 @@ Used for synchronisation of the local clock:
* _+_ - combined with the best source.
* _*_ - selected as the best source to update the reference data (e.g. root
delay, root dispersion).
+. Current effective selection options of the source. which can be different
+ from the configured options due to the authentication selection mode
+ (configured by the <<authselectmode,*authselectmode*>> directive). [-----]
+* _N_ indicates the *noselect* option.
+* _P_ indicates the *prefer* option.
+* _T_ indicates the *trust* option.
+* _R_ indicates the *require* option.
. Reachability register printed as an octal number. The register has 8 bits and
is updated on every received or missed packet from the source. A value of 377
indicates that a valid reply was received for all from the last eight
@@ -2503,10 +2536,13 @@ physical clock created by writing to _/sys/class/ptp/ptpX/n_vclocks_. This
feature is available on Linux 5.14 and newer.
+
If the kernel supports software timestamping, it will be enabled for all
-interfaces. The source of timestamps (i.e. hardware, kernel, or daemon) is
-indicated in the _measurements.log_ file if enabled by the <<log,*log
-measurements*>> directive, and the <<chronyc.adoc#ntpdata,*ntpdata*>> report in
-*chronyc*.
+interfaces automatically.
++
+The source of timestamps (i.e. hardware, kernel, or daemon) is indicated on the
+client side in the _measurements.log_ file (if enabled by the <<log,*log*>>
+directive) and the <<chronyc.adoc#ntpdata,*ntpdata*>> report. On the server
+side, the number of served timestamps from each source is provided in the
+<<chronyc.adoc#serverstats,*serverstats*>> report.
+
This directive can be used multiple times to enable HW timestamping on multiple
interfaces. If the specified interface is _*_, *chronyd* will try to enable HW
@@ -2516,10 +2552,15 @@ The *hwtimestamp* directive has the following options:
+
*minpoll* _poll_:::
This option specifies the minimum interval between readings of the NIC clock.
-It's defined as a power of two. It should correspond to the minimum polling
+It's defined as a power of 2. It should correspond to the minimum polling
interval of all NTP sources and the minimum expected polling interval of NTP
-clients. The default value is 0 (1 second) and the minimum value is -6 (1/64th
-of a second).
+clients. The default value is 0 (1 second), the minimum value is -6 (1/64th
+of a second), and the maximum value is 20 (about 12 days).
+*maxpoll* _poll_:::
+This option specifies the maximum interval between readings of the NIC clock,
+as a power of 2. The default value is *minpoll* + 1, i.e. 1 (2 seconds) with
+the default *minpoll* of 0. The minimum and maximum values are the same as with
+the *minpoll* option.
*minsamples* _samples_:::
This option specifies the minimum number of readings kept for tracking of the
NIC clock. The default value is 2.
@@ -2572,6 +2613,27 @@ hwtimestamp eth1 txcomp 300e-9 rxcomp 645e-9
hwtimestamp *
----
+[[hwtstimeout]]*hwtstimeout* _timeout_::
+If hardware timestamping is used with a close NTP server, or the NIC or its
+driver is slow in providing the transmit timestamp of NTP requests, a response
+from the server can be received before the transmit timestamp of the request.
+To avoid calculating the offset with a less accurate transmit timestamp,
+*chronyd* can save the response for later processing and wait for the hardware
+transmit timestamp. There is no guarantee that the timestamp will be provided
+(NICs typically have a limited rate of transmit timestamping). This directive
+configures how long should *chronyd* wait for the timestamp after receiving a
+valid response from the server. If a second valid response is received from the
+server while waiting for the timestamp, they will be both processed
+immediately.
++
+The default value is 0.001 seconds, which should be sufficient with most
+hardware. If you frequently see kernel transmit timestamps in the
+_measurements.log_ file or <<chronyc.adoc#ntpdata,*ntpdata*>> report, and it is
+not a server handling a high rate of requests in the interleaved mode on the
+same interface (which would compete with timestamping of the server's own
+requests), increasing the timeout to 0.01 or possibly even longer might help.
+Note that the maximum timeout is limited by the NTP polling interval.
+
[[keyfile]]*keyfile* _file_::
This directive is used to specify the location of the file containing symmetric
keys which are shared between NTP servers and clients, or peers, in order to
diff --git a/doc/chrony.conf.man.in b/doc/chrony.conf.man.in
index 1a51b24..385b797 100644
--- a/doc/chrony.conf.man.in
+++ b/doc/chrony.conf.man.in
@@ -1,13 +1,13 @@
'\" t
.\" Title: chrony.conf
.\" Author: [see the "AUTHOR(S)" section]
-.\" Generator: Asciidoctor 2.0.15
-.\" Date: 2022-08-29
+.\" Generator: Asciidoctor 2.0.18
+.\" Date: 2023-05-10
.\" Manual: Configuration Files
.\" Source: chrony @CHRONY_VERSION@
.\" Language: English
.\"
-.TH "CHRONY.CONF" "5" "2022-08-29" "chrony @CHRONY_VERSION@" "Configuration Files"
+.TH "CHRONY.CONF" "5" "2023-05-10" "chrony @CHRONY_VERSION@" "Configuration Files"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
@@ -604,20 +604,51 @@ Examples:
.if n .RS 4
.nf
.fam C
-refclock PPS /dev/pps0 lock NMEA refid GPS
-refclock SHM 0 offset 0.5 delay 0.2 refid NMEA noselect
+refclock PPS /dev/pps0 lock NMEA refid GPS1
+refclock SOCK /var/run/chrony.clk.ttyS0.sock offset 0.5 delay 0.2 refid NMEA noselect
refclock PPS /dev/pps1:clear refid GPS2
.fam
.fi
.if n .RE
.RE
.sp
+\fBSOCK\fP
+.RS 4
+Unix domain socket driver. This driver uses a datagram socket to receive
+samples from another application running on the system. The parameter is the
+path to the socket, which \fBchronyd\fP will create on start. The format of the
+messages is described in the \fIrefclock_sock.c\fP file in the chrony source code.
+.sp
+An application which supports the SOCK protocol is the \fBgpsd\fP daemon. It can
+provide accurate measurements using the receiver\(cqs PPS signal, and since
+version 3.25 also (much less accurate) measurements based on the timing of
+serial data (e.g. NMEA), which can be useful when the receiver does not provide
+a PPS signal, or it cannot be connected to the computer. The paths where \fBgpsd\fP
+expects the sockets to be created by \fBchronyd\fP are described in the \fBgpsd(8)\fP
+man page. Note that \fBgpsd\fP needs to be started after \fBchronyd\fP in order to
+connect to the socket.
+.sp
+Examples:
+.sp
+.if n .RS 4
+.nf
+.fam C
+refclock SOCK /var/run/chrony.ttyS0.sock refid GPS1 poll 2 filter 4
+refclock SOCK /var/run/chrony.clk.ttyUSB0.sock refid GPS2 offset 0.2 delay 0.1
+.fam
+.fi
+.if n .RE
+.RE
+.sp
\fBSHM\fP
.RS 4
-NTP shared memory driver. This driver uses a shared memory segment to receive
-samples from another process (e.g. \fBgpsd\fP). The parameter is the number of the
-shared memory segment, typically a small number like 0, 1, 2, or 3. The driver
-supports the following option:
+NTP shared memory driver. This driver implements the protocol of the \fBntpd\fP
+driver type 28. It is functionally similar to the SOCK driver, but uses a
+shared memory segment instead of a socket. The parameter is the unit number,
+typically a small number like 0, 1, 2, or 3, from which is derived the key of
+the memory segment as 0x4e545030 + unit.
+.sp
+The driver supports the following option:
.sp
\fBperm\fP=\fImode\fP
.RS 4
@@ -630,6 +661,16 @@ This option specifies the permissions of the shared memory segment created by
.RS 4
.sp
+Unlike with the SOCK driver, there is no prescribed order of starting \fBchronyd\fP
+and the program providing measurements. Both are expected to create the memory
+segment if it does not exist. \fBchronyd\fP will attach to an existing segment even
+if it has a different owner than root or different permissions than the
+permissions specified by the \fBperm\fP option. The segment needs to be created
+before untrusted applications or users can execute code to prevent an attacker
+from feeding \fBchronyd\fP with false measurements. The owner and permissions of
+the segment can be verified with the \fBipcs \-m\fP command. For this reason, the
+SHM driver is deprecated in favor of SOCK.
+.sp
Examples:
.sp
.if n .RS 4
@@ -642,29 +683,6 @@ refclock SHM 1:perm=0644 refid GPS2
.if n .RE
.RE
.sp
-\fBSOCK\fP
-.RS 4
-Unix domain socket driver. It is similar to the SHM driver, but samples are
-received from a Unix domain socket instead of shared memory and the messages
-have a different format. The parameter is the path to the socket, which
-\fBchronyd\fP creates on start. An advantage over the SHM driver is that SOCK does
-not require polling and it can receive PPS samples with incomplete time. The
-format of the messages is described in the \fIrefclock_sock.c\fP file in the chrony
-source code.
-.sp
-An application which supports the SOCK protocol is the \fBgpsd\fP daemon. The path
-where \fBgpsd\fP expects the socket to be created is described in the \fBgpsd(8)\fP man
-page. For example:
-.sp
-.if n .RS 4
-.nf
-.fam C
-refclock SOCK /var/run/chrony.ttyS0.sock
-.fam
-.fi
-.if n .RE
-.RE
-.sp
\fBPHC\fP
.RS 4
PTP hardware clock (PHC) driver. The parameter is the path to the device of
@@ -840,7 +858,9 @@ noise in the measurements. With each poll about 40 percent of the stored
samples are discarded and one final sample is calculated as an average of the
remaining samples. If the length is 4 or more, at least 4 samples have to be
collected between polls. For lengths below 4, the filter has to be full. The
-default is 64.
+default is 64. With drivers that perform their own polling (PPS, PHC, SHM), the
+maximum value is adjusted to the number of driver polls per source poll, i.e.
+2^(\fIpoll\fP \- \fIdpoll\fP).
.RE
.sp
\fBprefer\fP
@@ -1090,6 +1110,10 @@ This directory is used also by the NTS server to save keys.
This directive specifies the maximum interval between NTS\-KE handshakes (in
seconds) in order to refresh the keys authenticating NTP packets. The default
value is 2419200 (4 weeks) and the maximum value is 2^31\-1 (68 years).
+.sp
+The interval must be longer than polling intervals of all configured NTP
+sources using NTS, otherwise the source with a longer polling interval will
+refresh the keys on each poll and no NTP packets will be exchanged.
.RE
.sp
\fBntstrustedcerts\fP [\fIset\-ID\fP] \fIfile\fP|\fIdirectory\fP
@@ -1235,7 +1259,7 @@ As an example, the following configuration using the default \fBmix\fP mode:
server foo.example.net nts
server bar.example.net nts
server baz.example.net
-refclock SHM 0
+refclock SOCK /var/run/chrony.ttyS0.sock
.fam
.fi
.if n .RE
@@ -1249,7 +1273,7 @@ authselectmode ignore
server foo.example.net nts require trust
server bar.example.net nts require trust
server baz.example.net
-refclock SHM 0 require trust
+refclock /var/run/chrony.ttyS0.sock require trust
.fam
.fi
.if n .RE
@@ -1344,7 +1368,7 @@ The measured value works well in most cases. However, it generally
overestimates the precision and it can be sensitive to the CPU speed, which can
change over time to save power. In some cases with a high\-precision clocksource
(e.g. the Time Stamp Counter of the CPU) and hardware timestamping, setting the
-precision on the server to a smaller value can improve stability of clients\(aq
+precision on the server to a smaller value can improve stability of clients\*(Aq
NTP measurements. The server\(cqs precision is reported on clients by the
\fBntpdata\fP command.
.sp
@@ -1593,7 +1617,7 @@ and can be used with this directive:
.if n .RS 4
.nf
.fam C
-$ TZ=right/UTC date \-d \(aqDec 31 2008 23:59:60\(aq
+$ TZ=right/UTC date \-d \*(AqDec 31 2008 23:59:60\*(Aq
Wed Dec 31 23:59:60 UTC 2008
.fam
.fi
@@ -1684,7 +1708,7 @@ limited by the system driver rather than this directive.
.sp
\fBmaxupdateskew\fP \fIskew\-in\-ppm\fP
.RS 4
-One of \fBchronyd\fP\(aqs tasks is to work out how fast or slow the computer\(cqs clock
+One of \fBchronyd\fP\*(Aqs tasks is to work out how fast or slow the computer\(cqs clock
runs relative to its reference sources. In addition, it computes an estimate of
the error bounds around the estimated value.
.sp
@@ -1703,10 +1727,9 @@ It should be noted that this is not the only means of protection against using
unreliable estimates. At all times, \fBchronyd\fP keeps track of both the estimated
gain or loss rate, and the error bound on the estimate. When a new estimate is
generated following another measurement from one of the sources, a weighted
-combination algorithm is used to update the master estimate. So if \fBchronyd\fP
-has an existing highly\-reliable master estimate and a new estimate is generated
-which has large error bounds, the existing master estimate will dominate in the
-new master estimate.
+combination algorithm is used to update the existing estimate. If it has
+significantly smaller error bounds than the new estimate, the existing estimate
+will dominate in the new combined value.
.RE
.sp
\fBmaxslewrate\fP \fIrate\-in\-ppm\fP
@@ -2097,7 +2120,7 @@ synchronised to it. If that server stops responding, the server with the second
smallest reference ID will take over when its local reference mode activates
(root distance reaches the threshold configured by the \fBdistance\fP option).
.sp
-The \fBorphan\fP mode is compatible with the \fBntpd\fP\(aqs orphan mode (enabled by the
+The \fBorphan\fP mode is compatible with the \fBntpd\fP\*(Aqs orphan mode (enabled by the
\fBtos orphan\fP command).
.RE
.RE
@@ -2242,7 +2265,10 @@ save the keys to the \fIntskeys\fP file and will reload the keys from the file w
the \fBrekey\fP command is issued in \fBchronyc\fP. The file can
be periodically copied from another server running \fBchronyd\fP (which does
not have \fBntsrotate\fP set to 0) in order to have one or more servers dedicated
-to NTS\-KE. The NTS\-KE servers need to be configured with the
+to NTS\-KE. The file includes the subsequent key to which the NTS\-KE server will
+switch on the next rotation, i.e. the process copying and reloading the file
+does not need to be timed precisely (it can be delayed by up to one rotation
+interval). The NTS\-KE servers need to be configured with the
\fBntsntpserver\fP directive to point the clients to the right NTP
server.
.sp
@@ -2549,7 +2575,7 @@ used by the \fBhwclock\fP program on Linux. \fBchronyd\fP parses the file to fin
if the RTC keeps local time or UTC. It overrides the \fBrtconutc\fP
directive.
.sp
-The compiled\-in default value is \(aq\fI@DEFAULT_HWCLOCK_FILE@\fP\(aq.
+The compiled\-in default value is \*(Aq\fI@DEFAULT_HWCLOCK_FILE@\fP\*(Aq.
.sp
An example of the directive is:
.sp
@@ -3412,6 +3438,63 @@ delay, root dispersion).
. sp -1
. IP " 5." 4.2
.\}
+Current effective selection options of the source. which can be different
+from the configured options due to the authentication selection mode
+(configured by the \fBauthselectmode\fP directive). [\-\-\-\-\-]
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+. sp -1
+. IP \(bu 2.3
+.\}
+\fIN\fP indicates the \fBnoselect\fP option.
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+. sp -1
+. IP \(bu 2.3
+.\}
+\fIP\fP indicates the \fBprefer\fP option.
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+. sp -1
+. IP \(bu 2.3
+.\}
+\fIT\fP indicates the \fBtrust\fP option.
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+. sp -1
+. IP \(bu 2.3
+.\}
+\fIR\fP indicates the \fBrequire\fP option.
+.RE
+.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04' 6.\h'+01'\c
+.\}
+.el \{\
+. sp -1
+. IP " 6." 4.2
+.\}
Reachability register printed as an octal number. The register has 8 bits and
is updated on every received or missed packet from the source. A value of 377
indicates that a valid reply was received for all from the last eight
@@ -3420,11 +3503,11 @@ transmissions. [377]
.sp
.RS 4
.ie n \{\
-\h'-04' 6.\h'+01'\c
+\h'-04' 7.\h'+01'\c
.\}
.el \{\
. sp -1
-. IP " 6." 4.2
+. IP " 7." 4.2
.\}
Current score against the source in the \fI*\fP state. The scoring system avoids
frequent reselection when multiple sources have a similar root distance. A
@@ -3435,22 +3518,22 @@ and the scores will be reset to 1. [1.00]
.sp
.RS 4
.ie n \{\
-\h'-04' 7.\h'+01'\c
+\h'-04' 8.\h'+01'\c
.\}
.el \{\
. sp -1
-. IP " 7." 4.2
+. IP " 8." 4.2
.\}
Interval since the last measurement of the source in seconds. [4.228e+01]
.RE
.sp
.RS 4
.ie n \{\
-\h'-04' 8.\h'+01'\c
+\h'-04' 9.\h'+01'\c
.\}
.el \{\
. sp -1
-. IP " 8." 4.2
+. IP " 9." 4.2
.\}
Lower endpoint of the interval which was expected to contain the true offset
of the local clock determined by the root distance of the source. [\-1.575e\-04]
@@ -3458,11 +3541,11 @@ of the local clock determined by the root distance of the source. [\-1.575e\-04]
.sp
.RS 4
.ie n \{\
-\h'-04' 9.\h'+01'\c
+\h'-04' 10.\h'+01'\c
.\}
.el \{\
. sp -1
-. IP " 9." 4.2
+. IP " 10." 4.2
.\}
Upper endpoint of the interval which was expected to contain the true offset
of the local clock determined by the root distance of the source. [1.239e\-04]
@@ -4164,10 +4247,13 @@ physical clock created by writing to \fI/sys/class/ptp/ptpX/n_vclocks\fP. This
feature is available on Linux 5.14 and newer.
.sp
If the kernel supports software timestamping, it will be enabled for all
-interfaces. The source of timestamps (i.e. hardware, kernel, or daemon) is
-indicated in the \fImeasurements.log\fP file if enabled by the \fBlog
-measurements\fP directive, and the \fBntpdata\fP report in
-\fBchronyc\fP.
+interfaces automatically.
+.sp
+The source of timestamps (i.e. hardware, kernel, or daemon) is indicated on the
+client side in the \fImeasurements.log\fP file (if enabled by the \fBlog\fP
+directive) and the \fBntpdata\fP report. On the server
+side, the number of served timestamps from each source is provided in the
+\fBserverstats\fP report.
.sp
This directive can be used multiple times to enable HW timestamping on multiple
interfaces. If the specified interface is \fI*\fP, \fBchronyd\fP will try to enable HW
@@ -4178,10 +4264,18 @@ The \fBhwtimestamp\fP directive has the following options:
\fBminpoll\fP \fIpoll\fP
.RS 4
This option specifies the minimum interval between readings of the NIC clock.
-It\(cqs defined as a power of two. It should correspond to the minimum polling
+It\(cqs defined as a power of 2. It should correspond to the minimum polling
interval of all NTP sources and the minimum expected polling interval of NTP
-clients. The default value is 0 (1 second) and the minimum value is \-6 (1/64th
-of a second).
+clients. The default value is 0 (1 second), the minimum value is \-6 (1/64th
+of a second), and the maximum value is 20 (about 12 days).
+.RE
+.sp
+\fBmaxpoll\fP \fIpoll\fP
+.RS 4
+This option specifies the maximum interval between readings of the NIC clock,
+as a power of 2. The default value is \fBminpoll\fP + 1, i.e. 1 (2 seconds) with
+the default \fBminpoll\fP of 0. The minimum and maximum values are the same as with
+the \fBminpoll\fP option.
.RE
.sp
\fBminsamples\fP \fIsamples\fP
@@ -4279,6 +4373,29 @@ hwtimestamp *
.if n .RE
.RE
.sp
+\fBhwtstimeout\fP \fItimeout\fP
+.RS 4
+If hardware timestamping is used with a close NTP server, or the NIC or its
+driver is slow in providing the transmit timestamp of NTP requests, a response
+from the server can be received before the transmit timestamp of the request.
+To avoid calculating the offset with a less accurate transmit timestamp,
+\fBchronyd\fP can save the response for later processing and wait for the hardware
+transmit timestamp. There is no guarantee that the timestamp will be provided
+(NICs typically have a limited rate of transmit timestamping). This directive
+configures how long should \fBchronyd\fP wait for the timestamp after receiving a
+valid response from the server. If a second valid response is received from the
+server while waiting for the timestamp, they will be both processed
+immediately.
+.sp
+The default value is 0.001 seconds, which should be sufficient with most
+hardware. If you frequently see kernel transmit timestamps in the
+\fImeasurements.log\fP file or \fBntpdata\fP report, and it is
+not a server handling a high rate of requests in the interleaved mode on the
+same interface (which would compete with timestamping of the server\(cqs own
+requests), increasing the timeout to 0.01 or possibly even longer might help.
+Note that the maximum timeout is limited by the NTP polling interval.
+.RE
+.sp
\fBkeyfile\fP \fIfile\fP
.RS 4
This directive is used to specify the location of the file containing symmetric
@@ -4625,7 +4742,7 @@ and the script \fI/etc/ppp/ip\-down\fP would include:
.fi
.if n .RE
.sp
-\fBchronyd\fP\(aqs polling of the servers would now only occur whilst the machine is
+\fBchronyd\fP\*(Aqs polling of the servers would now only occur whilst the machine is
actually connected to the Internet.
.SS "Isolated networks"
.sp
@@ -4647,7 +4764,7 @@ in the form of the \fBmanual\fP directive and the
If the server is rebooted, \fBchronyd\fP can re\-read the drift rate from the drift
file. However, the server has no accurate estimate of the current time. To get
around this, the system can be configured so that the server can initially set
-itself to a \(oqmajority\-vote\(cq of selected clients\(aq times; this allows the
+itself to a \(oqmajority\-vote\(cq of selected clients\*(Aq times; this allows the
clients to \(oqflywheel\(cq the server while it is rebooting.
.sp
The \fBsmoothtime\fP directive is useful when the clocks of the
@@ -4760,7 +4877,7 @@ system clock to the best estimate of what its time would have been now, had it
been left running continuously. The measurement histories for the servers are
then reloaded.
.sp
-The next time the computer goes online, the previous sessions\(aq measurements can
+The next time the computer goes online, the previous sessions\*(Aq measurements can
contribute to the line\-fitting process, which gives a much better estimate of
the computer\(cqs gain or loss rate.
.sp
@@ -4835,7 +4952,7 @@ and the relevant part of the \fI/etc/ppp/ip\-down\fP script is:
.sp
\fBchronyd\fP is started during the boot sequence with the \fB\-r\fP and \fB\-s\fP options.
It might need to be started before any software that depends on the system clock
-not jumping or moving backwards, depending on the directives in \fBchronyd\fP\(aqs
+not jumping or moving backwards, depending on the directives in \fBchronyd\fP\*(Aqs
configuration file.
.sp
For the system shutdown, \fBchronyd\fP should receive a SIGTERM several seconds
diff --git a/doc/chronyc.adoc b/doc/chronyc.adoc
index 45d8548..2a37e5c 100644
--- a/doc/chronyc.adoc
+++ b/doc/chronyc.adoc
@@ -89,6 +89,10 @@ format. Reverse DNS lookups will be disabled, time will be printed as number of
seconds since the epoch, and values in seconds will not be converted to other
units.
+*-e*::
+With this option each *chronyc* response will end with a line containing a
+single dot.
+
*-d*::
This option enables printing of debugging messages if *chronyc* was compiled
with debugging support.
@@ -500,8 +504,8 @@ This column displays the configured selection options of the source.
This column displays the current effective selection options of the source,
which can be different from the configured options due to the authentication
selection mode (configured by the
-<<chrony.conf.adoc#authselmode,*authselmode*>> directive). The symbols are the
-same as in the *COpts* column.
+<<chrony.conf.adoc#authselectmode,*authselectmode*>> directive). The symbols
+are the same as in the *COpts* column.
*Last*:::
This column displays how long ago was the last measurement of the source made
when the selection was performed.
@@ -522,6 +526,23 @@ This column displays the current leap status of the source.
* _-_ indicates that a leap second will be deleted at the end of the month.
* _?_ indicates the unknown status (i.e. no valid measurement was made).
+[[selectopts]]*selectopts* _address|refid_ [_+|-option_]...::
+The *selectopts* command modifies the configured selection options of an NTP
+source specified by IP address (or the _ID#XXXXXXXXXX_ identifier used for
+unknown addresses), or a reference clock specified by reference ID as a string.
++
+The selection options can be added with the *+* symbol or removed with the *-*
+symbol. The *selectdata* command can be used to verify the configuration. The
+modified options will be applied in the next source selection, e.g. when a new
+measurement is made, or the *reselect* command is executed.
++
+An example of using this command is shown below.
++
+----
+selectopts 1.2.3.4 -noselect +prefer
+selectopts GPS +trust
+----
+
[[reselect]]*reselect*::
To avoid excessive switching between sources, *chronyd* can stay synchronised
to a source even when it is not currently the best one among the available
@@ -610,6 +631,7 @@ be reported:
* 13: AES128
* 14: AES256
* 15: AEAD-AES-SIV-CMAC-256
+* 30: AEAD-AES-128-GCM-SIV
*KLen*:::
This column shows the length of the key in bits.
*Last*:::
@@ -1136,6 +1158,12 @@ Authenticated NTP packets : 189
Interleaved NTP packets : 43
NTP timestamps held : 44
NTP timestamp span : 120
+NTP daemon RX timestamps : 0
+NTP daemon TX timestamps : 1537
+NTP kernel RX timestamps : 1590
+NTP kernel TX timestamps : 43
+NTP hardware RX timestamps : 0
+NTP hardware TX timestamps : 0
----
+
The fields have the following meaning:
@@ -1170,10 +1198,24 @@ The number of pairs of receive and transmit timestamps that the server is
currently holding in memory for clients using the interleaved mode.
*NTP timestamp span*:::
The interval (in seconds) covered by the currently held NTP timestamps.
-{blank}::
-+
-Note that the numbers reported by this overflow to zero after 4294967295
-(32-bit values).
+*NTP daemon RX timestamps*:::
+The number of NTP responses which included a receive timestamp captured by the
+daemon.
+*NTP daemon TX timestamps*:::
+The number of NTP responses which included a transmit timestamp captured by the
+daemon.
+*NTP kernel RX timestamps*:::
+The number of NTP responses which included a receive timestamp captured by the
+kernel.
+*NTP kernel TX timestamps*:::
+The number of NTP responses (in the interleaved mode) which included a transmit
+timestamp captured by the kernel.
+*NTP hardware RX timestamps*:::
+The number of NTP responses which included a receive timestamp captured by the
+NIC.
+*NTP hardware TX timestamps*:::
+The number of NTP responses (in the interleaved mode) which included a transmit
+timestamp captured by the NIC.
[[allow]]*allow* [*all*] [_subnet_]::
The effect of the allow command is identical to the
@@ -1212,8 +1254,8 @@ deny all
*local* *off*::
The *local* command allows *chronyd* to be told that it is to appear as a
reference source, even if it is not itself properly synchronised to an external
-source. (This can be used on isolated networks, to allow one computer to be a
-master time server with the other computers slaving to it.)
+source. This can be used on isolated networks, to allow a computer to be the
+primary time server for other computers.
+
The first form enables the local reference mode on the host. The syntax is
identical to the <<chrony.conf.adoc#local,*local*>> directive in the
diff --git a/doc/chronyc.man.in b/doc/chronyc.man.in
index 36b5c81..2d7c39c 100644
--- a/doc/chronyc.man.in
+++ b/doc/chronyc.man.in
@@ -1,13 +1,13 @@
'\" t
.\" Title: chronyc
.\" Author: [see the "AUTHOR(S)" section]
-.\" Generator: Asciidoctor 2.0.15
-.\" Date: 2022-08-29
+.\" Generator: Asciidoctor 2.0.18
+.\" Date: 2023-05-10
.\" Manual: User manual
.\" Source: chrony @CHRONY_VERSION@
.\" Language: English
.\"
-.TH "CHRONYC" "1" "2022-08-29" "chrony @CHRONY_VERSION@" "User manual"
+.TH "CHRONYC" "1" "2023-05-10" "chrony @CHRONY_VERSION@" "User manual"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
@@ -35,12 +35,12 @@ chronyc \- command\-line interface for chrony daemon
.SH "DESCRIPTION"
.sp
\fBchronyc\fP is a command\-line interface program which can be used to monitor
-\fBchronyd\fP\(aqs performance and to change various operating parameters whilst it is
+\fBchronyd\fP\*(Aqs performance and to change various operating parameters whilst it is
running.
.sp
If no commands are specified on the command line, \fBchronyc\fP will expect input
from the user. The prompt \fIchronyc>\fP will be displayed when it is being run
-from a terminal. If \fBchronyc\fP\(aqs input or output are redirected from or to a file,
+from a terminal. If \fBchronyc\fP\*(Aqs input or output are redirected from or to a file,
the prompt will not be shown.
.sp
There are two ways \fBchronyc\fP can access \fBchronyd\fP. One is the Internet
@@ -56,7 +56,7 @@ Only the following monitoring commands, which do not affect the behaviour of
\fBrtcdata\fP, \fBsmoothing\fP, \fBsourcename\fP, \fBsources\fP, \fBsourcestats\fP, \fBtracking\fP,
\fBwaitsync\fP. The
set of hosts from which \fBchronyd\fP will accept these commands can be configured
-with the \fBcmdallow\fP directive in the \fBchronyd\fP\(aqs
+with the \fBcmdallow\fP directive in the \fBchronyd\fP\*(Aqs
configuration file or the \fBcmdallow\fP command in \fBchronyc\fP. By
default, the commands are accepted only from localhost (127.0.0.1 or ::1).
.sp
@@ -65,7 +65,7 @@ over the network, \fBchronyd\fP will respond with a \(oqNot authorised\(cq error
if it is from localhost.
.sp
Having full access to \fBchronyd\fP via \fBchronyc\fP is more or less equivalent to
-being able to modify the \fBchronyd\fP\(aqs configuration file and restart it.
+being able to modify the \fBchronyd\fP\*(Aqs configuration file and restart it.
.SH "OPTIONS"
.sp
\fB\-4\fP
@@ -100,6 +100,12 @@ seconds since the epoch, and values in seconds will not be converted to other
units.
.RE
.sp
+\fB\-e\fP
+.RS 4
+With this option each \fBchronyc\fP response will end with a line containing a
+single dot.
+.RE
+.sp
\fB\-d\fP
.RS 4
This option enables printing of debugging messages if \fBchronyc\fP was compiled
@@ -933,8 +939,8 @@ This column displays the configured selection options of the source.
This column displays the current effective selection options of the source,
which can be different from the configured options due to the authentication
selection mode (configured by the
-\fBauthselmode\fP directive). The symbols are the
-same as in the \fBCOpts\fP column.
+\fBauthselectmode\fP directive). The symbols
+are the same as in the \fBCOpts\fP column.
.RE
.sp
\fBLast\fP
@@ -1009,6 +1015,29 @@ This column displays the current leap status of the source.
.RE
.RE
.sp
+\fBselectopts\fP \fIaddress|refid\fP [\fI+|\-option\fP]...
+.RS 4
+The \fBselectopts\fP command modifies the configured selection options of an NTP
+source specified by IP address (or the \fIID#XXXXXXXXXX\fP identifier used for
+unknown addresses), or a reference clock specified by reference ID as a string.
+.sp
+The selection options can be added with the \fB+\fP symbol or removed with the \fB\-\fP
+symbol. The \fBselectdata\fP command can be used to verify the configuration. The
+modified options will be applied in the next source selection, e.g. when a new
+measurement is made, or the \fBreselect\fP command is executed.
+.sp
+An example of using this command is shown below.
+.sp
+.if n .RS 4
+.nf
+.fam C
+selectopts 1.2.3.4 \-noselect +prefer
+selectopts GPS +trust
+.fam
+.fi
+.if n .RE
+.RE
+.sp
\fBreselect\fP
.RS 4
To avoid excessive switching between sources, \fBchronyd\fP can stay synchronised
@@ -1271,6 +1300,17 @@ be reported:
.\}
15: AEAD\-AES\-SIV\-CMAC\-256
.RE
+.sp
+.RS 4
+.ie n \{\
+\h'-04'\(bu\h'+03'\c
+.\}
+.el \{\
+. sp -1
+. IP \(bu 2.3
+.\}
+30: AEAD\-AES\-128\-GCM\-SIV
+.RE
.RE
.sp
\fBKLen\fP
@@ -1547,7 +1587,7 @@ obtained.
.sp
\fImask\fP
.RS 4
-This is an IP address with which the IP address of each of \fBchronyd\fP\(aqs
+This is an IP address with which the IP address of each of \fBchronyd\fP\*(Aqs
sources is to be masked.
.RE
.sp
@@ -1937,7 +1977,7 @@ accheck 2001:db8::1
.sp
This command can be used to examine the effect of a series of \fBallow\fP, \fBallow
all\fP, \fBdeny\fP, and \fBdeny all\fP commands specified either via \fBchronyc\fP, or in
-\fBchronyd\fP\(aqs configuration file.
+\fBchronyd\fP\*(Aqs configuration file.
.RE
.sp
\fBclients\fP [\fB\-p\fP \fIpackets\fP] [\fB\-k\fP] [\fB\-r\fP]
@@ -2108,6 +2148,12 @@ Authenticated NTP packets : 189
Interleaved NTP packets : 43
NTP timestamps held : 44
NTP timestamp span : 120
+NTP daemon RX timestamps : 0
+NTP daemon TX timestamps : 1537
+NTP kernel RX timestamps : 1590
+NTP kernel TX timestamps : 43
+NTP hardware RX timestamps : 0
+NTP hardware TX timestamps : 0
.fam
.fi
.if n .RE
@@ -2176,13 +2222,42 @@ currently holding in memory for clients using the interleaved mode.
.RS 4
The interval (in seconds) covered by the currently held NTP timestamps.
.RE
+.sp
+\fBNTP daemon RX timestamps\fP
+.RS 4
+The number of NTP responses which included a receive timestamp captured by the
+daemon.
.RE
.sp
-
+\fBNTP daemon TX timestamps\fP
+.RS 4
+The number of NTP responses which included a transmit timestamp captured by the
+daemon.
+.RE
+.sp
+\fBNTP kernel RX timestamps\fP
+.RS 4
+The number of NTP responses which included a receive timestamp captured by the
+kernel.
+.RE
+.sp
+\fBNTP kernel TX timestamps\fP
+.RS 4
+The number of NTP responses (in the interleaved mode) which included a transmit
+timestamp captured by the kernel.
+.RE
+.sp
+\fBNTP hardware RX timestamps\fP
.RS 4
+The number of NTP responses which included a receive timestamp captured by the
+NIC.
+.RE
.sp
-Note that the numbers reported by this overflow to zero after 4294967295
-(32\-bit values).
+\fBNTP hardware TX timestamps\fP
+.RS 4
+The number of NTP responses (in the interleaved mode) which included a transmit
+timestamp captured by the NIC.
+.RE
.RE
.sp
\fBallow\fP [\fBall\fP] [\fIsubnet\fP]
@@ -2233,8 +2308,8 @@ deny all
.RS 4
The \fBlocal\fP command allows \fBchronyd\fP to be told that it is to appear as a
reference source, even if it is not itself properly synchronised to an external
-source. (This can be used on isolated networks, to allow one computer to be a
-master time server with the other computers slaving to it.)
+source. This can be used on isolated networks, to allow a computer to be the
+primary time server for other computers.
.sp
The first form enables the local reference mode on the host. The syntax is
identical to the \fBlocal\fP directive in the
@@ -2398,7 +2473,7 @@ This is the estimate of how many seconds fast the RTC when it thought
the time was at the reference time (above). If this value is large, you
might (or might not) want to use the \fBtrimrtc\fP command to bring the
RTC into line with the system clock. (Note, a large error will not affect
-\fBchronyd\fP\(aqs operation, unless it becomes so big as to start causing rounding
+\fBchronyd\fP\*(Aqs operation, unless it becomes so big as to start causing rounding
errors.)
.RE
.sp
@@ -2500,7 +2575,7 @@ or SIGTERM signals) or when the \fBtrimrtc\fP command is issued.
.sp
\fBcyclelogs\fP
.RS 4
-The \fBcyclelogs\fP command causes all of \fBchronyd\fP\(aqs open log files to be closed
+The \fBcyclelogs\fP command causes all of \fBchronyd\fP\*(Aqs open log files to be closed
and re\-opened. This allows them to be renamed so that they can be periodically
purged. An example of how to do this is shown below.
.sp
diff --git a/doc/chronyd.adoc b/doc/chronyd.adoc
index 7ba991d..38c1526 100644
--- a/doc/chronyd.adoc
+++ b/doc/chronyd.adoc
@@ -88,8 +88,10 @@ will not detach from the terminal.
*-Q*::
This option is similar to the *-q* option, except it only prints the offset
-without making any corrections of the clock and it allows *chronyd* to be
-started without root privileges.
+without making any corrections of the clock and disables server ports to allow
+*chronyd* to be started without root privileges, assuming the configuration
+does not have any directives which would require them (e.g. *refclock*,
+*hwtimestamp*, *rtcfile*, etc).
*-r*::
This option will try to reload and then delete files containing sample
diff --git a/doc/chronyd.man.in b/doc/chronyd.man.in
index 090d810..b8d6906 100644
--- a/doc/chronyd.man.in
+++ b/doc/chronyd.man.in
@@ -1,13 +1,13 @@
'\" t
.\" Title: chronyd
.\" Author: [see the "AUTHOR(S)" section]
-.\" Generator: Asciidoctor 2.0.15
-.\" Date: 2022-08-29
+.\" Generator: Asciidoctor 2.0.18
+.\" Date: 2023-05-10
.\" Manual: System Administration
.\" Source: chrony @CHRONY_VERSION@
.\" Language: English
.\"
-.TH "CHRONYD" "8" "2022-08-29" "chrony @CHRONY_VERSION@" "System Administration"
+.TH "CHRONYD" "8" "2023-05-10" "chrony @CHRONY_VERSION@" "System Administration"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
@@ -109,8 +109,10 @@ will not detach from the terminal.
\fB\-Q\fP
.RS 4
This option is similar to the \fB\-q\fP option, except it only prints the offset
-without making any corrections of the clock and it allows \fBchronyd\fP to be
-started without root privileges.
+without making any corrections of the clock and disables server ports to allow
+\fBchronyd\fP to be started without root privileges, assuming the configuration
+does not have any directives which would require them (e.g. \fBrefclock\fP,
+\fBhwtimestamp\fP, \fBrtcfile\fP, etc).
.RE
.sp
\fB\-r\fP
@@ -122,7 +124,7 @@ files are expected to be in the directory specified by the
directive in the configuration file. This option is useful if you want to stop
and restart \fBchronyd\fP briefly for any reason, e.g. to install a new version.
However, it should be used only on systems where the kernel can maintain clock
-compensation whilst not under \fBchronyd\fP\(aqs control (i.e. Linux, FreeBSD, NetBSD,
+compensation whilst not under \fBchronyd\fP\*(Aqs control (i.e. Linux, FreeBSD, NetBSD,
illumos, and macOS 10.13 or later).
.RE
.sp
diff --git a/doc/faq.adoc b/doc/faq.adoc
index 1b299d2..d6e0692 100644
--- a/doc/faq.adoc
+++ b/doc/faq.adoc
@@ -232,7 +232,7 @@ authenticated servers should be configured as trusted and required to not allow
the unauthenticated servers to override the authenticated servers in the source
selection. Since `chrony` version 4.0, the selection options are enabled in
such a case automatically. This behaviour can be disabled or modified by the
-`authselmode` directive.
+`authselectmode` directive.
An example of a client configuration limiting the impact of the attacks could
be
@@ -499,39 +499,41 @@ it is connected to a GPIO pin, or another serial port, the PPS device needs to
be specified on the command line as an additional data source. On Linux, the
`ldattach` utility can be used to create a PPS device for a serial device.
-The message-based time source provided by `gpsd` is specified as a `SHM 0`
-refclock, or other even number if `gpsd` is configured with multiple receivers.
-
-The PPS-based time source is specified as a `SHM 1` refclock (or other odd
-number), or `SOCK /var/run/chrony.DEV.sock` where `DEV` is the name of the
+The PPS-based time source provided by `gpsd` is available as a `SHM 1`
+refclock, or other odd number if `gpsd` is configured with multiple receivers,
+and also as `SOCK /var/run/chrony.DEV.sock` where `DEV` is the name of the
serial device (e.g. ttyS0).
-With `chronyd` and `gpsd` both supporting PPS, and `gpsd` providing two
-different refclocks for PPS, there are three different recommended
-configurations:
+The message-based time source is available as a `SHM 0` refclock (or other even
+number) and since `gpsd` version 3.25 also as
+`SOCK /var/run/chrony.clk.DEV.sock` where `DEV` is the name of the serial
+device.
+
+The SOCK refclocks should be preferred over SHM for better security
+(the shared memory segment needs to be created by `chronyd` or `gpsd` with an
+expected owner and permissions before an untrusted application or user has a
+chance to create its own in order to feed `chronyd` with false measurements).
+`gpsd` needs to be started after `chronyd` in order to connect to the socket.
+
+With `chronyd` and `gpsd` both supporting PPS, there are two different
+recommended configurations:
----
# First option
refclock SOCK /var/run/chrony.ttyS0.sock refid GPS
# Second option
-refclock SHM 1 refid GPS
-
-# Third option
refclock PPS /dev/pps0 lock NMEA refid GPS
-refclock SHM 0 offset 0.5 delay 0.1 refid NMEA noselect
+refclock SOCK /var/run/chrony.clk.ttyS0.sock offset 0.5 delay 0.1 refid NMEA noselect
----
-Each option has some advantages:
+They both have some advantages:
-* `SOCK` does not use polling (i.e. it can get samples earlier than `SHM`),
- but it requires `gpsd` to be started after `chronyd` in order to connect to
- its socket
-* `SOCK` and `SHM 1` can be more accurate than `PPS` if `gpsd` corrects for the
+* `SOCK` can be more accurate than `PPS` if `gpsd` corrects for the
sawtooth error provided by the receiver in serial data
* `PPS` can be used with higher PPS rates (specified by the `rate` option),
but it requires a second refclock or another time source to pair pulses
- with seconds, and the `SHM 0` offset needs to be specified
+ with seconds, and the `SOCK` offset needs to be specified
<<using-pps-refclock,correctly>> to compensate for the message delay, while
`gpsd` can apply HW-specific information
@@ -539,6 +541,12 @@ If the PPS signal is not available, or cannot be used for some reason, the only
option is the message-based timing
----
+refclock SOCK /var/run/chrony.clk.ttyS0.sock offset 0.5 delay 0.1 refid GPS
+----
+
+or the SHM equivalent if using `gpsd` version before 3.25
+
+----
refclock SHM 0 offset 0.5 delay 0.1 refid GPS
----
@@ -605,8 +613,9 @@ following questions.
Check the `Reach` value printed by the ``chronyc``'s `sources` command. If it
is zero, it means `chronyd` did not get any valid responses from the NTP server
you are trying to use. If there is a firewall between you and the server, the
-packets might be blocked. Try using a tool like `wireshark` or `tcpdump` to see
-if you are getting any responses from the server.
+requests sent to the UDP port 123 of the server or responses sent back from
+the port might be blocked. Try using a tool like `wireshark` or `tcpdump` to
+see if you are getting any responses from the server.
When `chronyd` is receiving responses from the servers, the output of the
`sources` command issued few minutes after `chronyd` start might look like
diff --git a/examples/chrony.keys.example b/examples/chrony.keys.example
index 65b6be2..05e720c 100644
--- a/examples/chrony.keys.example
+++ b/examples/chrony.keys.example
@@ -11,3 +11,5 @@
#1 MD5 AVeryLongAndRandomPassword
#2 MD5 HEX:12114855C7931009B4049EF3EFC48A139C3F989F
#3 SHA1 HEX:B2159C05D6A219673A3B7E896B6DE07F6A440995
+#4 AES128 HEX:2DA837C4B6573748CA692B8C828E4891
+#5 AES256 HEX:2666B8099BFF2D5BA20876121788ED24D2BE59111B8FFB562F0F56AE6EC7246E
diff --git a/examples/chrony.nm-dispatcher.onoffline b/examples/chrony.nm-dispatcher.onoffline
index 01e6fdb..18f5c3f 100644
--- a/examples/chrony.nm-dispatcher.onoffline
+++ b/examples/chrony.nm-dispatcher.onoffline
@@ -12,8 +12,10 @@ if [ $# -ge 2 ]; then
case "$2" in
up|down|connectivity-change)
;;
- dhcp6-change)
- # No other action is reported for routable IPv6
+ dhcp4-change|dhcp6-change)
+ # Actions "up" and "connectivity-change" in some cases do not
+ # guarantee that the interface has a route (e.g. a bond).
+ # dhcp(x)-change handles at least cases that use DHCP.
;;
*)
exit 0;;
diff --git a/examples/chronyd-restricted.service b/examples/chronyd-restricted.service
new file mode 100644
index 0000000..5099833
--- /dev/null
+++ b/examples/chronyd-restricted.service
@@ -0,0 +1,59 @@
+# This is a more restricted version of the chronyd service intended for
+# minimal NTP/NTS client configurations. The daemon is started without root
+# privileges and is allowed to write only to its own runtime, state, and log
+# directories. It cannot bind to privileged ports in order to operate as an
+# NTP server, or provide monitoring access over IPv4/IPv6. It cannot use
+# reference clocks, HW timestamping, RTC tracking, and other features.
+[Unit]
+Description=NTP client (restricted)
+Documentation=man:chronyd(8) man:chrony.conf(5)
+After=chronyd.service ntpdate.service sntp.service ntpd.service
+Conflicts=chronyd.service ntpd.service systemd-timesyncd.service
+ConditionCapability=CAP_SYS_TIME
+
+[Service]
+Type=forking
+PIDFile=/run/chrony/chronyd.pid
+EnvironmentFile=-/etc/sysconfig/chronyd
+ExecStart=/usr/sbin/chronyd -U $OPTIONS
+
+User=chrony
+LogsDirectory=chrony
+LogsDirectoryMode=0750
+RuntimeDirectory=chrony
+RuntimeDirectoryMode=0750
+RuntimeDirectoryPreserve=restart
+StateDirectory=chrony
+StateDirectoryMode=0750
+
+AmbientCapabilities=CAP_SYS_TIME
+CapabilityBoundingSet=CAP_SYS_TIME
+DevicePolicy=closed
+LockPersonality=yes
+MemoryDenyWriteExecute=yes
+NoNewPrivileges=yes
+PrivateDevices=yes
+PrivateTmp=yes
+# This breaks adjtimex()
+#PrivateUsers=yes
+ProcSubset=pid
+ProtectControlGroups=yes
+ProtectHome=yes
+ProtectHostname=yes
+ProtectKernelLogs=yes
+ProtectKernelModules=yes
+ProtectKernelTunables=yes
+ProtectProc=invisible
+ProtectSystem=strict
+RemoveIPC=yes
+RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
+RestrictNamespaces=yes
+RestrictRealtime=yes
+RestrictSUIDSGID=yes
+SystemCallArchitectures=native
+SystemCallFilter=~@cpu-emulation @debug @module @mount @obsolete @raw-io
+SystemCallFilter=~@reboot @resources @swap
+UMask=0077
+
+[Install]
+WantedBy=multi-user.target
diff --git a/getdate.c b/getdate.c
index 5c38119..65d1b99 100644
--- a/getdate.c
+++ b/getdate.c
@@ -2010,9 +2010,9 @@ yyreturnlab:
the same signature as the function definition does. */
#include "getdate.h"
-extern struct tm *gmtime ();
-extern struct tm *localtime ();
-extern time_t mktime ();
+extern struct tm *gmtime (const time_t *timep);
+extern struct tm *localtime (const time_t *timep);
+extern time_t mktime (struct tm *tm);
/* Month and day table. */
static TABLE const MonthDayTable[] = {
@@ -2203,16 +2203,13 @@ static TABLE const MilitaryTable[] = {
/* ARGSUSED */
static int
-yyerror (s)
- char *s ATTRIBUTE_UNUSED;
+yyerror (char *s ATTRIBUTE_UNUSED)
{
return 0;
}
static int
-ToHour (Hours, Meridian)
- int Hours;
- MERIDIAN Meridian;
+ToHour (int Hours, MERIDIAN Meridian)
{
switch (Meridian)
{
@@ -2239,8 +2236,7 @@ ToHour (Hours, Meridian)
}
static int
-ToYear (Year)
- int Year;
+ToYear (int Year)
{
if (Year < 0)
Year = -Year;
@@ -2256,8 +2252,7 @@ ToYear (Year)
}
static int
-LookupWord (buff)
- char *buff;
+LookupWord (char *buff)
{
register char *p;
register char *q;
diff --git a/getdate.y b/getdate.y
index f87875c..47d368d 100644
--- a/getdate.y
+++ b/getdate.y
@@ -448,9 +448,9 @@ o_merid : /* NULL */
the same signature as the function definition does. */
#include "getdate.h"
-extern struct tm *gmtime ();
-extern struct tm *localtime ();
-extern time_t mktime ();
+extern struct tm *gmtime (const time_t *timep);
+extern struct tm *localtime (const time_t *timep);
+extern time_t mktime (struct tm *tm);
/* Month and day table. */
static TABLE const MonthDayTable[] = {
@@ -641,16 +641,13 @@ static TABLE const MilitaryTable[] = {
/* ARGSUSED */
static int
-yyerror (s)
- char *s ATTRIBUTE_UNUSED;
+yyerror (char *s ATTRIBUTE_UNUSED)
{
return 0;
}
static int
-ToHour (Hours, Meridian)
- int Hours;
- MERIDIAN Meridian;
+ToHour (int Hours, MERIDIAN Meridian)
{
switch (Meridian)
{
@@ -677,8 +674,7 @@ ToHour (Hours, Meridian)
}
static int
-ToYear (Year)
- int Year;
+ToYear (int Year)
{
if (Year < 0)
Year = -Year;
@@ -694,8 +690,7 @@ ToYear (Year)
}
static int
-LookupWord (buff)
- char *buff;
+LookupWord (char *buff)
{
register char *p;
register char *q;
diff --git a/keys.c b/keys.c
index cc1131c..9225e6c 100644
--- a/keys.c
+++ b/keys.c
@@ -182,6 +182,9 @@ KEY_Reload(void)
if (!key_file)
return;
+ if (!UTI_CheckFilePermissions(key_file, 0771))
+ ;
+
in = UTI_OpenFile(NULL, key_file, NULL, 'r', 0);
if (!in) {
LOG(LOGS_WARN, "Could not open keyfile %s", key_file);
@@ -255,6 +258,8 @@ KEY_Reload(void)
more careful! */
qsort(ARR_GetElements(keys), ARR_GetSize(keys), sizeof (Key), compare_keys_by_id);
+ LOG(LOGS_INFO, "Loaded %u symmetric keys", ARR_GetSize(keys));
+
/* Check for duplicates */
for (i = 1; i < ARR_GetSize(keys); i++) {
if (get_key(i - 1)->id == get_key(i)->id)
diff --git a/logging.c b/logging.c
index d858d2a..22c326c 100644
--- a/logging.c
+++ b/logging.c
@@ -39,6 +39,9 @@
/* This is used by DEBUG_LOG macro */
LOG_Severity log_min_severity = LOGS_INFO;
+/* Current logging contexts */
+static LOG_Context log_contexts;
+
/* ================================================== */
/* Flag indicating we have initialised */
static int initialised = 0;
@@ -72,6 +75,8 @@ void
LOG_Initialise(void)
{
debug_prefix = Strdup("");
+ log_contexts = 0;
+
initialised = 1;
LOG_OpenFileLog(NULL);
}
@@ -238,6 +243,30 @@ LOG_GetMinSeverity(void)
/* ================================================== */
void
+LOG_SetContext(LOG_Context context)
+{
+ log_contexts |= context;
+}
+
+/* ================================================== */
+
+void
+LOG_UnsetContext(LOG_Context context)
+{
+ log_contexts &= ~context;
+}
+
+/* ================================================== */
+
+LOG_Severity
+LOG_GetContextSeverity(LOG_Context contexts)
+{
+ return log_contexts & contexts ? LOGS_INFO : LOGS_DEBUG;
+}
+
+/* ================================================== */
+
+void
LOG_SetDebugPrefix(const char *prefix)
{
Free(debug_prefix);
diff --git a/logging.h b/logging.h
index 31bd462..ff2e30e 100644
--- a/logging.h
+++ b/logging.h
@@ -100,6 +100,20 @@ extern void LOG_SetMinSeverity(LOG_Severity severity);
/* Get the minimum severity */
extern LOG_Severity LOG_GetMinSeverity(void);
+/* Flags for info messages that should be logged only in specific contexts */
+typedef enum {
+ LOGC_Command = 1,
+ LOGC_SourceFile = 2,
+} LOG_Context;
+
+/* Modify current contexts */
+extern void LOG_SetContext(LOG_Context context);
+extern void LOG_UnsetContext(LOG_Context context);
+
+/* Get severity depending on the current active contexts: INFO if they contain
+ at least one of the specified contexts, DEBUG otherwise */
+extern LOG_Severity LOG_GetContextSeverity(LOG_Context contexts);
+
/* Set a prefix for debug messages */
extern void LOG_SetDebugPrefix(const char *prefix);
diff --git a/main.c b/main.c
index c40b5e4..31e3c8f 100644
--- a/main.c
+++ b/main.c
@@ -637,9 +637,13 @@ int main
}
/* Drop root privileges if the specified user has a non-zero UID */
- if (!geteuid() && (pw->pw_uid || pw->pw_gid))
+ if (!geteuid() && (pw->pw_uid || pw->pw_gid)) {
SYS_DropRoot(pw->pw_uid, pw->pw_gid, SYS_MAIN_PROCESS);
+ /* Warn if missing read access or having write access to keys */
+ CNF_CheckReadOnlyAccess();
+ }
+
if (!geteuid())
LOG(LOGS_WARN, "Running with root privileges");
diff --git a/md5.c b/md5.c
index f997a6e..b8eeda3 100644
--- a/md5.c
+++ b/md5.c
@@ -117,8 +117,7 @@ inline UINT4 ROTATE_LEFT(UINT4 x, int n)
/* The routine MD5Init initializes the message-digest context
mdContext. All fields are set to zero.
*/
-void MD5Init (mdContext)
-MD5_CTX *mdContext;
+void MD5Init (MD5_CTX *mdContext)
{
mdContext->i[0] = mdContext->i[1] = (UINT4)0;
@@ -134,10 +133,7 @@ MD5_CTX *mdContext;
account for the presence of each of the characters inBuf[0..inLen-1]
in the message whose digest is being computed.
*/
-void MD5Update (mdContext, inBuf, inLen)
-MD5_CTX *mdContext;
-unsigned const char *inBuf;
-unsigned int inLen;
+void MD5Update (MD5_CTX *mdContext, unsigned const char *inBuf, unsigned int inLen)
{
UINT4 in[16];
int mdi;
@@ -173,8 +169,7 @@ unsigned int inLen;
ends with the desired message digest in mdContext->digest[0...15].
*/
-void MD5Final (mdContext)
-MD5_CTX *mdContext;
+void MD5Final (MD5_CTX *mdContext)
{
UINT4 in[16];
int mdi;
@@ -214,9 +209,7 @@ MD5_CTX *mdContext;
/* Basic MD5 step. Transforms buf based on in.
*/
-static void Transform (buf, in)
-UINT4 *buf;
-UINT4 *in;
+static void Transform (UINT4 *buf, UINT4 *in)
{
UINT4 a = buf[0], b = buf[1], c = buf[2], d = buf[3];
diff --git a/ntp.h b/ntp.h
index 52b2ab5..906adc3 100644
--- a/ntp.h
+++ b/ntp.h
@@ -179,4 +179,11 @@ typedef struct {
double root_dispersion;
} NTP_Sample;
+/* Possible sources of timestamps */
+typedef enum {
+ NTP_TS_DAEMON = 0,
+ NTP_TS_KERNEL,
+ NTP_TS_HARDWARE
+} NTP_Timestamp_Source;
+
#endif /* GOT_NTP_H */
diff --git a/ntp_core.c b/ntp_core.c
index 63c5a3a..4e2289a 100644
--- a/ntp_core.c
+++ b/ntp_core.c
@@ -64,6 +64,17 @@ typedef enum {
MD_BURST_WAS_ONLINE, /* Burst sampling, return to online afterwards */
} OperatingMode;
+/* Structure holding a response and other data waiting to be processed when
+ a late HW transmit timestamp of the request is available, or a timeout is
+ reached */
+struct SavedResponse {
+ NTP_Local_Address local_addr;
+ NTP_Local_Timestamp rx_ts;
+ NTP_Packet message;
+ NTP_PacketInfo info;
+ SCH_TimeoutID timeout_id;
+};
+
/* ================================================== */
/* Structure used for holding a single peer/server's
protocol machine */
@@ -204,6 +215,12 @@ struct NCR_Instance_Record {
SPF_Instance filter;
int filter_count;
+ /* Flag indicating HW transmit timestamps are expected */
+ int had_hw_tx_timestamp;
+
+ /* Response waiting for a HW transmit timestamp of the request */
+ struct SavedResponse *saved_response;
+
int burst_good_samples_to_go;
int burst_total_samples_to_go;
@@ -324,10 +341,15 @@ static const char tss_chars[3] = {'D', 'K', 'H'};
/* Forward prototypes */
static void transmit_timeout(void *arg);
-static double get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx);
+static double get_transmit_delay(NCR_Instance inst, int on_tx);
static double get_separation(int poll);
static int parse_packet(NTP_Packet *packet, int length, NTP_PacketInfo *info);
static void process_sample(NCR_Instance inst, NTP_Sample *sample);
+static int has_saved_response(NCR_Instance inst);
+static void process_saved_response(NCR_Instance inst);
+static int process_response(NCR_Instance inst, int saved, NTP_Local_Address *local_addr,
+ NTP_Local_Timestamp *rx_ts, NTP_Packet *message,
+ NTP_PacketInfo *info);
static void set_connectivity(NCR_Instance inst, SRC_Connectivity connectivity);
/* ================================================== */
@@ -490,8 +512,7 @@ restart_timeout(NCR_Instance inst, double delay)
static void
start_initial_timeout(NCR_Instance inst)
{
- double delay, last_tx;
- struct timespec now;
+ double delay;
if (!inst->tx_timeout_id) {
/* This will be the first transmission after mode change */
@@ -504,11 +525,7 @@ start_initial_timeout(NCR_Instance inst)
the interval between packets at least as long as the current polling
interval */
if (!UTI_IsZeroTimespec(&inst->local_tx.ts)) {
- SCH_GetLastEventTime(&now, NULL, NULL);
- last_tx = UTI_DiffTimespecsToDouble(&now, &inst->local_tx.ts);
- if (last_tx < 0.0)
- last_tx = 0.0;
- delay = get_transmit_delay(inst, 0, 0.0) - last_tx;
+ delay = get_transmit_delay(inst, 0);
} else {
delay = 0.0;
}
@@ -531,6 +548,11 @@ close_client_socket(NCR_Instance inst)
SCH_RemoveTimeout(inst->rx_timeout_id);
inst->rx_timeout_id = 0;
+
+ if (has_saved_response(inst)) {
+ SCH_RemoveTimeout(inst->saved_response->timeout_id);
+ inst->saved_response->timeout_id = 0;
+ }
}
/* ================================================== */
@@ -673,6 +695,8 @@ NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
else
result->filter = NULL;
+ result->saved_response = NULL;
+
result->rx_timeout_id = 0;
result->tx_timeout_id = 0;
result->tx_suspended = 1;
@@ -709,6 +733,9 @@ NCR_DestroyInstance(NCR_Instance instance)
if (instance->filter)
SPF_DestroyInstance(instance->filter);
+ if (instance->saved_response)
+ Free(instance->saved_response);
+
NAU_DestroyInstance(instance->auth);
/* This will destroy the source instance inside the
@@ -767,6 +794,8 @@ NCR_ResetInstance(NCR_Instance instance)
if (instance->filter)
SPF_DropSamples(instance->filter);
instance->filter_count = 0;
+
+ instance->had_hw_tx_timestamp = 0;
}
/* ================================================== */
@@ -779,7 +808,7 @@ NCR_ResetPoll(NCR_Instance instance)
/* The timer was set with a longer poll interval, restart it */
if (instance->tx_timeout_id)
- restart_timeout(instance, get_transmit_delay(instance, 0, 0.0));
+ restart_timeout(instance, get_transmit_delay(instance, 0));
}
}
@@ -900,10 +929,19 @@ get_transmit_poll(NCR_Instance inst)
/* ================================================== */
static double
-get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx)
+get_transmit_delay(NCR_Instance inst, int on_tx)
{
int poll_to_use, stratum_diff;
- double delay_time;
+ double delay_time, last_tx;
+ struct timespec now;
+
+ /* Calculate the interval since last transmission if known */
+ if (!on_tx && !UTI_IsZeroTimespec(&inst->local_tx.ts)) {
+ SCH_GetLastEventTime(&now, NULL, NULL);
+ last_tx = UTI_DiffTimespecsToDouble(&now, &inst->local_tx.ts);
+ } else {
+ last_tx = 0;
+ }
/* If we're in burst mode, queue for immediate dispatch.
@@ -943,12 +981,6 @@ get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx)
last_tx / delay_time > PEER_SAMPLING_ADJ - 0.5))
delay_time *= PEER_SAMPLING_ADJ;
- /* Substract the already spend time */
- if (last_tx > 0.0)
- delay_time -= last_tx;
- if (delay_time < 0.0)
- delay_time = 0.0;
-
break;
default:
assert(0);
@@ -966,6 +998,12 @@ get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx)
break;
}
+ /* Subtract elapsed time */
+ if (last_tx > 0.0)
+ delay_time -= last_tx;
+ if (delay_time < 0.0)
+ delay_time = 0.0;
+
return delay_time;
}
@@ -1278,6 +1316,15 @@ transmit_timeout(void *arg)
inst->tx_timeout_id = 0;
+ if (has_saved_response(inst)) {
+ process_saved_response(inst);
+
+ /* Wait for the new transmission timeout (if the response was still
+ valid and it did not cause switch to offline) */
+ if (inst->tx_timeout_id != 0)
+ return;
+ }
+
switch (inst->opmode) {
case MD_BURST_WAS_ONLINE:
/* With online burst switch to online before last packet */
@@ -1309,11 +1356,10 @@ transmit_timeout(void *arg)
/* Prepare authentication */
if (!NAU_PrepareRequestAuth(inst->auth)) {
- if (inst->burst_total_samples_to_go > 0)
- inst->burst_total_samples_to_go--;
- adjust_poll(inst, 0.25);
SRC_UpdateReachability(inst->source, 0);
- restart_timeout(inst, get_transmit_delay(inst, 1, 0.0));
+ restart_timeout(inst, get_transmit_delay(inst, 1));
+ /* Count missing samples for the sample filter */
+ process_sample(inst, NULL);
return;
}
@@ -1416,7 +1462,7 @@ transmit_timeout(void *arg)
}
/* Restart timer for this message */
- restart_timeout(inst, get_transmit_delay(inst, 1, 0.0));
+ restart_timeout(inst, get_transmit_delay(inst, 1));
/* If a client packet was just sent, schedule a timeout to close the socket
at the time when all server replies would fail the delay test, so the
@@ -1729,7 +1775,69 @@ process_sample(NCR_Instance inst, NTP_Sample *sample)
/* ================================================== */
static int
-process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
+has_saved_response(NCR_Instance inst)
+{
+ return inst->saved_response && inst->saved_response->timeout_id > 0;
+}
+
+/* ================================================== */
+
+static void
+process_saved_response(NCR_Instance inst)
+{
+ SCH_RemoveTimeout(inst->saved_response->timeout_id);
+ inst->saved_response->timeout_id = 0;
+
+ DEBUG_LOG("Processing saved response from %s", UTI_IPToString(&inst->remote_addr.ip_addr));
+ process_response(inst, 1, &inst->saved_response->local_addr, &inst->saved_response->rx_ts,
+ &inst->saved_response->message, &inst->saved_response->info);
+}
+
+/* ================================================== */
+
+static void
+saved_response_timeout(void *arg)
+{
+ NCR_Instance inst = arg;
+
+ inst->saved_response->timeout_id = 0;
+ process_saved_response(inst);
+}
+
+/* ================================================== */
+
+static int
+save_response(NCR_Instance inst, NTP_Local_Address *local_addr,
+ NTP_Local_Timestamp *rx_ts, NTP_Packet *message, NTP_PacketInfo *info)
+{
+ double timeout = CNF_GetHwTsTimeout();
+
+ if (timeout <= 0.0)
+ return 0;
+
+ /* If another message is already saved, process both immediately */
+ if (has_saved_response(inst)) {
+ process_saved_response(inst);
+ return 0;
+ }
+
+ if (!inst->saved_response)
+ inst->saved_response = MallocNew(struct SavedResponse);
+ inst->saved_response->local_addr = *local_addr;
+ inst->saved_response->rx_ts = *rx_ts;
+ inst->saved_response->message = *message;
+ inst->saved_response->info = *info;
+ inst->saved_response->timeout_id = SCH_AddTimeoutByDelay(timeout, saved_response_timeout,
+ inst);
+ DEBUG_LOG("Saved valid response for later processing");
+
+ return 1;
+}
+
+/* ================================================== */
+
+static int
+process_response(NCR_Instance inst, int saved, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *rx_ts, NTP_Packet *message, NTP_PacketInfo *info)
{
NTP_Sample sample;
@@ -1823,8 +1931,10 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
/* Test 4 would check for denied access. It would always pass as this
function is called only for known sources. */
- /* Test 5 checks for authentication failure */
- test5 = NAU_CheckResponseAuth(inst->auth, message, info);
+ /* Test 5 checks for authentication failure. If it is a saved message,
+ which had to pass all these tests before, avoid authenticating it for
+ the second time (that is not allowed in the NTS code). */
+ test5 = saved || NAU_CheckResponseAuth(inst->auth, message, info);
/* Test 6 checks for unsynchronised server */
test6 = pkt_leap != LEAP_Unsynchronised &&
@@ -1840,6 +1950,20 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
valid_packet = test1 && test2 && test3 && test5;
synced_packet = valid_packet && test6 && test7;
+ /* If the server is very close and/or the NIC hardware/driver is slow, it
+ is possible that a response from the server is received before the HW
+ transmit timestamp of the request. To avoid getting a less accurate
+ offset or failing one of the later tests, save the response and wait for
+ the transmit timestamp or timeout. Allow this only for the first valid
+ response to the request, when at least one good response has already been
+ accepted to avoid incorrectly confirming a tentative source. */
+ if (valid_packet && synced_packet && !saved && !inst->valid_rx &&
+ inst->had_hw_tx_timestamp && inst->local_tx.source != NTP_TS_HARDWARE &&
+ inst->report.total_good_count > 0) {
+ if (save_response(inst, local_addr, rx_ts, message, info))
+ return 1;
+ }
+
/* Check for Kiss-o'-Death codes */
kod_rate = 0;
if (test1 && test2 && test5 && pkt_leap == LEAP_Unsynchronised &&
@@ -2170,8 +2294,7 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
/* And now, requeue the timer */
if (inst->opmode != MD_OFFLINE) {
- delay_time = get_transmit_delay(inst, 0,
- UTI_DiffTimespecsToDouble(&inst->local_rx.ts, &inst->local_tx.ts));
+ delay_time = get_transmit_delay(inst, 0);
if (kod_rate) {
LOG(LOGS_WARN, "Received KoD RATE from %s",
@@ -2186,7 +2309,8 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
}
/* Get rid of old timeout and start a new one */
- assert(inst->tx_timeout_id);
+ if (!saved)
+ assert(inst->tx_timeout_id);
restart_timeout(inst, delay_time);
}
@@ -2331,8 +2455,8 @@ NCR_ProcessRxKnown(NCR_Instance inst, NTP_Local_Address *local_addr,
client mode operation.
This copes with the case for an isolated network where one
- machine is set by eye and is used as the master, with the
- other machines pointed at it. If the master goes down, we
+ machine is set by eye and is used as the primary server, with
+ the other machines pointed at it. If the server goes down, we
want to be able to reset its time at startup by relying on
one of the secondaries to flywheel it. The behaviour coded here
is required in the secondaries to make this possible. */
@@ -2375,7 +2499,7 @@ NCR_ProcessRxKnown(NCR_Instance inst, NTP_Local_Address *local_addr,
return 0;
}
- return process_response(inst, local_addr, rx_ts, message, &info);
+ return process_response(inst, 0, local_addr, rx_ts, message, &info);
} else if (proc_as_unknown) {
NCR_ProcessRxUnknown(&inst->remote_addr, local_addr, rx_ts, message, length);
/* It's not a reply to our request, don't return success */
@@ -2456,8 +2580,6 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
/* Don't respond unless a non-zero KoD was returned */
if (kod == 0)
return;
- } else if (info.auth.mode != NTP_AUTH_NONE && info.auth.mode != NTP_AUTH_MSSNTP) {
- CLG_LogAuthNtpRequest();
}
local_ntp_rx = NULL;
@@ -2478,13 +2600,18 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
ntp_rx = message->originate_ts;
local_ntp_rx = &ntp_rx;
UTI_ZeroTimespec(&local_tx.ts);
- interleaved = CLG_GetNtpTxTimestamp(&ntp_rx, &local_tx.ts);
+ local_tx.source = NTP_TS_DAEMON;
+ interleaved = CLG_GetNtpTxTimestamp(&ntp_rx, &local_tx.ts, &local_tx.source);
tx_ts = &local_tx;
if (interleaved)
CLG_DisableNtpTimestamps(&ntp_rx);
}
+ CLG_UpdateNtpStats(kod != 0 && info.auth.mode != NTP_AUTH_NONE &&
+ info.auth.mode != NTP_AUTH_MSSNTP,
+ rx_ts->source, interleaved ? tx_ts->source : NTP_TS_DAEMON);
+
/* Suggest the client to increase its polling interval if it indicates
the interval is shorter than the rate limiting interval */
poll = CLG_GetNtpMinPoll();
@@ -2501,7 +2628,7 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
return;
if (local_ntp_rx)
- CLG_SaveNtpTimestamps(local_ntp_rx, tx_ts ? &tx_ts->ts : NULL);
+ CLG_SaveNtpTimestamps(local_ntp_rx, &tx_ts->ts, tx_ts->source);
}
/* ================================================== */
@@ -2555,6 +2682,13 @@ NCR_ProcessTxKnown(NCR_Instance inst, NTP_Local_Address *local_addr,
update_tx_timestamp(&inst->local_tx, tx_ts, &inst->local_ntp_rx, &inst->local_ntp_tx,
message);
+
+ if (tx_ts->source == NTP_TS_HARDWARE) {
+ inst->had_hw_tx_timestamp = 1;
+
+ if (has_saved_response(inst))
+ process_saved_response(inst);
+ }
}
/* ================================================== */
@@ -2579,7 +2713,7 @@ NCR_ProcessTxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
local_ntp_rx = &message->receive_ts;
new_tx = *tx_ts;
- if (!CLG_GetNtpTxTimestamp(local_ntp_rx, &old_tx.ts))
+ if (!CLG_GetNtpTxTimestamp(local_ntp_rx, &old_tx.ts, &old_tx.source))
return;
/* Undo a clock adjustment between the RX and TX timestamps to minimise error
@@ -2588,7 +2722,7 @@ NCR_ProcessTxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
update_tx_timestamp(&old_tx, &new_tx, local_ntp_rx, NULL, message);
- CLG_UpdateNtpTxTimestamp(local_ntp_rx, &new_tx.ts);
+ CLG_UpdateNtpTxTimestamp(local_ntp_rx, &new_tx.ts, new_tx.source);
}
/* ================================================== */
@@ -2611,6 +2745,10 @@ NCR_SlewTimes(NCR_Instance inst, struct timespec *when, double dfreq, double dof
if (inst->filter)
SPF_SlewSamples(inst->filter, when, dfreq, doffset);
+
+ if (has_saved_response(inst))
+ UTI_AdjustTimespec(&inst->saved_response->rx_ts.ts, when, &inst->saved_response->rx_ts.ts,
+ &delta, dfreq, doffset);
}
/* ================================================== */
@@ -2856,6 +2994,10 @@ NCR_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all)
if (status != ADF_SUCCESS)
return 0;
+ LOG(LOG_GetContextSeverity(LOGC_Command), "%s%s %s access from %s",
+ allow ? "Allowed" : "Denied", all ? " all" : "", "NTP",
+ UTI_IPSubnetToString(ip_addr, subnet_bits));
+
/* Keep server sockets open only when an address allowed */
if (allow) {
NTP_Remote_Address remote_addr;
diff --git a/ntp_core.h b/ntp_core.h
index 33f2bb8..a3c5546 100644
--- a/ntp_core.h
+++ b/ntp_core.h
@@ -38,12 +38,6 @@ typedef enum {
NTP_SERVER, NTP_PEER
} NTP_Source_Type;
-typedef enum {
- NTP_TS_DAEMON = 0,
- NTP_TS_KERNEL,
- NTP_TS_HARDWARE
-} NTP_Timestamp_Source;
-
typedef struct {
struct timespec ts;
double err;
diff --git a/ntp_io.c b/ntp_io.c
index 86e3f26..deb8e25 100644
--- a/ntp_io.c
+++ b/ntp_io.c
@@ -126,8 +126,14 @@ open_socket(int family, int local_port, int client_only, IPSockAddr *remote_addr
dscp = CNF_GetNtpDscp();
if (dscp > 0 && dscp < 64) {
#ifdef IP_TOS
- if (!SCK_SetIntOption(sock_fd, IPPROTO_IP, IP_TOS, dscp << 2))
- ;
+ if (family == IPADDR_INET4)
+ if (!SCK_SetIntOption(sock_fd, IPPROTO_IP, IP_TOS, dscp << 2))
+ ;
+#endif
+#if defined(FEAT_IPV6) && defined(IPV6_TCLASS)
+ if (family == IPADDR_INET6)
+ if (!SCK_SetIntOption(sock_fd, IPPROTO_IPV6, IPV6_TCLASS, dscp << 2))
+ ;
#endif
}
@@ -163,9 +169,6 @@ 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);
SCK_CloseSocket(sock_fd);
}
@@ -461,11 +464,6 @@ read_from_socket(int sock_fd, int event, void *anything)
SCK_Message *messages;
int i, received, flags = 0;
-#ifdef HAVE_LINUX_TIMESTAMPING
- if (NIO_Linux_ProcessEvent(sock_fd, event))
- return;
-#endif
-
if (event == SCH_FILE_EXCEPTION) {
#ifdef HAVE_LINUX_TIMESTAMPING
flags |= SCK_FLAG_MSG_ERRQUEUE;
@@ -522,6 +520,8 @@ NIO_UnwrapMessage(SCK_Message *message, int sock_fd)
static int
wrap_message(SCK_Message *message, int sock_fd)
{
+ static uint16_t sequence_id = 0;
+
assert(PTP_NTP_PREFIX_LENGTH == 48);
if (!is_ptp_socket(sock_fd))
@@ -542,6 +542,7 @@ wrap_message(SCK_Message *message, int sock_fd)
ptp_message->header.length = htons(PTP_NTP_PREFIX_LENGTH + message->length);
ptp_message->header.domain = PTP_DOMAIN_NTP;
ptp_message->header.flags = htons(PTP_FLAG_UNICAST);
+ ptp_message->header.sequence_id = htons(sequence_id++);
ptp_message->tlv_header.type = htons(PTP_TLV_NTP);
ptp_message->tlv_header.length = htons(message->length);
memcpy((char *)ptp_message + PTP_NTP_PREFIX_LENGTH, message->data, message->length);
diff --git a/ntp_io_linux.c b/ntp_io_linux.c
index 004a4d4..1fc509d 100644
--- a/ntp_io_linux.c
+++ b/ntp_io_linux.c
@@ -39,6 +39,7 @@
#include "hwclock.h"
#include "local.h"
#include "logging.h"
+#include "memory.h"
#include "ntp_core.h"
#include "ntp_io.h"
#include "ntp_io_linux.h"
@@ -63,13 +64,16 @@ struct Interface {
double tx_comp;
double rx_comp;
HCL_Instance clock;
+ int maxpoll;
+ SCH_TimeoutID poll_timeout_id;
};
/* Number of PHC readings per HW clock sample */
#define PHC_READINGS 25
-/* Minimum interval between PHC readings */
+/* Minimum and maximum interval between PHC readings */
#define MIN_PHC_POLL -6
+#define MAX_PHC_POLL 20
/* Maximum acceptable offset between SW/HW and daemon timestamp */
#define MAX_TS_DELAY 1.0
@@ -84,19 +88,6 @@ 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
@@ -107,13 +98,17 @@ static int dummy_rxts_socket;
/* ================================================== */
+static void poll_phc(struct Interface *iface, struct timespec *now);
+
+/* ================================================== */
+
static int
add_interface(CNF_HwTsInterface *conf_iface)
{
+ int sock_fd, if_index, minpoll, phc_fd, req_hwts_flags, rx_filter;
struct ethtool_ts_info ts_info;
struct hwtstamp_config ts_config;
struct ifreq req;
- int sock_fd, if_index, phc_fd, req_hwts_flags, rx_filter;
unsigned int i;
struct Interface *iface;
@@ -245,9 +240,15 @@ add_interface(CNF_HwTsInterface *conf_iface)
iface->tx_comp = conf_iface->tx_comp;
iface->rx_comp = conf_iface->rx_comp;
+ minpoll = CLAMP(MIN_PHC_POLL, conf_iface->minpoll, MAX_PHC_POLL);
iface->clock = HCL_CreateInstance(conf_iface->min_samples, conf_iface->max_samples,
- UTI_Log2ToDouble(MAX(conf_iface->minpoll, MIN_PHC_POLL)),
- conf_iface->precision);
+ UTI_Log2ToDouble(minpoll), conf_iface->precision);
+
+ iface->maxpoll = CLAMP(minpoll, conf_iface->maxpoll, MAX_PHC_POLL);
+
+ /* Do not schedule the first poll timeout here! The argument (interface) can
+ move until all interfaces are added. Wait for the first HW timestamp. */
+ iface->poll_timeout_id = 0;
LOG(LOGS_INFO, "Enabled HW timestamping %son %s",
ts_config.rx_filter == HWTSTAMP_FILTER_NONE ? "(TX only) " : "", iface->name);
@@ -412,8 +413,6 @@ 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;
}
@@ -430,6 +429,7 @@ NIO_Linux_Finalise(void)
for (i = 0; i < ARR_GetSize(interfaces); i++) {
iface = ARR_GetElement(interfaces, i);
+ SCH_RemoveTimeout(iface->poll_timeout_id);
HCL_DestroyInstance(iface->clock);
close(iface->phc_fd);
}
@@ -472,88 +472,76 @@ NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events)
/* ================================================== */
-static void
-resume_socket(int sock_fd)
+static struct Interface *
+get_interface(int if_index)
{
- 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);
+ struct Interface *iface;
+ unsigned int i;
- DEBUG_LOG("Resumed RX processing %s timeout fd=%d",
- resume_timeout_id ? "before" : "on", sock_fd);
+ for (i = 0; i < ARR_GetSize(interfaces); i++) {
+ iface = ARR_GetElement(interfaces, i);
+ if (iface->if_index != if_index)
+ continue;
- if (resume_timeout_id) {
- SCH_RemoveTimeout(resume_timeout_id);
- resume_timeout_id = 0;
+ return iface;
}
-}
-
-/* ================================================== */
-static void
-resume_timeout(void *arg)
-{
- resume_timeout_id = 0;
- resume_socket(suspended_socket);
+ return NULL;
}
/* ================================================== */
static void
-suspend_socket(int sock_fd)
+poll_timeout(void *arg)
{
- resume_socket(suspended_socket);
+ struct Interface *iface = arg;
+ struct timespec now;
- suspended_socket = sock_fd;
+ iface->poll_timeout_id = 0;
- 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);
+ SCH_GetLastEventTime(&now, NULL, NULL);
+ poll_phc(iface, &now);
}
/* ================================================== */
-int
-NIO_Linux_ProcessEvent(int sock_fd, int event)
+static void
+poll_phc(struct Interface *iface, struct timespec *now)
{
- if (sock_fd != monitored_socket)
- return 0;
+ struct timespec sample_phc_ts, sample_sys_ts, sample_local_ts;
+ struct timespec phc_readings[PHC_READINGS][3];
+ double phc_err, local_err, interval;
+ int n_readings;
- if (event == SCH_FILE_INPUT) {
- suspend_socket(monitored_socket);
- monitored_socket = INVALID_SOCK_FD;
+ if (!HCL_NeedsNewSample(iface->clock, now))
+ return;
- /* Don't process the message yet */
- return 1;
- }
+ DEBUG_LOG("Polling PHC on %s%s",
+ iface->name, iface->poll_timeout_id != 0 ? " before timeout" : "");
- return 0;
-}
+ n_readings = SYS_Linux_GetPHCReadings(iface->phc_fd, iface->phc_nocrossts,
+ &iface->phc_mode, PHC_READINGS, phc_readings);
-/* ================================================== */
+ /* Add timeout for the next poll in case no HW timestamp will be captured
+ between the minpoll and maxpoll. Separate reading of different PHCs to
+ avoid long intervals between handling I/O events. */
+ SCH_RemoveTimeout(iface->poll_timeout_id);
+ interval = UTI_Log2ToDouble(iface->maxpoll);
+ iface->poll_timeout_id = SCH_AddTimeoutInClass(interval, interval /
+ ARR_GetSize(interfaces) / 4, 0.1,
+ SCH_PhcPollClass, poll_timeout, iface);
-static struct Interface *
-get_interface(int if_index)
-{
- struct Interface *iface;
- unsigned int i;
+ if (n_readings <= 0)
+ return;
- for (i = 0; i < ARR_GetSize(interfaces); i++) {
- iface = ARR_GetElement(interfaces, i);
- if (iface->if_index != if_index)
- continue;
+ if (!HCL_ProcessReadings(iface->clock, n_readings, phc_readings,
+ &sample_phc_ts, &sample_sys_ts, &phc_err))
+ return;
- return iface;
- }
+ LCL_CookTime(&sample_sys_ts, &sample_local_ts, &local_err);
+ HCL_AccumulateSample(iface->clock, &sample_phc_ts, &sample_local_ts, phc_err + local_err);
- return NULL;
+ update_interface_speed(iface);
}
/* ================================================== */
@@ -563,24 +551,10 @@ process_hw_timestamp(struct Interface *iface, struct timespec *hw_ts,
NTP_Local_Timestamp *local_ts, int rx_ntp_length, int family,
int l2_length)
{
- struct timespec sample_phc_ts, sample_sys_ts, sample_local_ts, ts;
- struct timespec phc_readings[PHC_READINGS][3];
- double rx_correction, ts_delay, phc_err, local_err;
- int n_readings;
+ double rx_correction, ts_delay, local_err;
+ struct timespec ts;
- if (HCL_NeedsNewSample(iface->clock, &local_ts->ts)) {
- n_readings = SYS_Linux_GetPHCReadings(iface->phc_fd, iface->phc_nocrossts,
- &iface->phc_mode, PHC_READINGS, phc_readings);
- if (n_readings > 0 &&
- HCL_ProcessReadings(iface->clock, n_readings, phc_readings,
- &sample_phc_ts, &sample_sys_ts, &phc_err)) {
- LCL_CookTime(&sample_sys_ts, &sample_local_ts, &local_err);
- HCL_AccumulateSample(iface->clock, &sample_phc_ts, &sample_local_ts,
- phc_err + local_err);
-
- update_interface_speed(iface);
- }
- }
+ poll_phc(iface, &local_ts->ts);
/* We need to transpose RX timestamps as hardware timestamps are normally
preamble timestamps and RX timestamps in NTP are supposed to be trailer
@@ -758,11 +732,6 @@ NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
} 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(&message->timestamp.kernel) &&
@@ -825,23 +794,9 @@ NIO_Linux_RequestTxTimestamp(SCK_Message *message, int sock_fd)
if (!ts_flags)
return;
- /* 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;
message->timestamp.tx_flags = ts_tx_flags;
}
-
-/* ================================================== */
-
-void
-NIO_Linux_NotifySocketClosing(int sock_fd)
-{
- resume_socket(sock_fd);
-}
diff --git a/ntp_io_linux.h b/ntp_io_linux.h
index 4d3af13..089d4a1 100644
--- a/ntp_io_linux.h
+++ b/ntp_io_linux.h
@@ -35,13 +35,9 @@ 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(SCK_Message *message, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *local_ts, int event);
extern void NIO_Linux_RequestTxTimestamp(SCK_Message *message, int sock_fd);
-extern void NIO_Linux_NotifySocketClosing(int sock_fd);
-
#endif
diff --git a/ntp_sources.c b/ntp_sources.c
index d46c211..fa562a1 100644
--- a/ntp_sources.c
+++ b/ntp_sources.c
@@ -317,6 +317,31 @@ rehash_records(void)
/* ================================================== */
+static void
+log_source(SourceRecord *record, int addition, int once_per_pool)
+{
+ int pool, log_addr;
+ char *ip_str;
+
+ if (once_per_pool && record->pool_id != INVALID_POOL) {
+ if (get_pool(record->pool_id)->sources > 1)
+ return;
+ pool = 1;
+ log_addr = 0;
+ } else {
+ ip_str = UTI_IPToString(&record->remote_addr->ip_addr);
+ pool = 0;
+ log_addr = strcmp(record->name, ip_str) != 0;
+ }
+
+ LOG(LOG_GetContextSeverity(LOGC_Command | LOGC_SourceFile), "%s %s %s%s%s%s",
+ addition ? "Added" : "Removed", pool ? "pool" : "source",
+ log_addr ? ip_str : record->name,
+ log_addr ? " (" : "", log_addr ? record->name : "", log_addr ? ")" : "");
+}
+
+/* ================================================== */
+
/* Procedure to add a new source */
static NSR_Status
add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type,
@@ -371,6 +396,8 @@ add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type,
if (auto_start_sources && UTI_IsIPReal(&remote_addr->ip_addr))
NCR_StartInstance(record->data);
+ log_source(record, 1, 1);
+
/* The new instance is allowed to change its address immediately */
handle_saved_address_update();
@@ -884,6 +911,7 @@ NSR_RemoveSource(IPAddr *address)
if (find_slot(address, &slot) == 0)
return NSR_NoSuchSource;
+ log_source(get_record(slot), 0, 0);
clean_source_record(get_record(slot));
/* Rehash the table to make sure there are no broken probe sequences.
@@ -906,6 +934,7 @@ NSR_RemoveSourcesById(uint32_t conf_id)
record = get_record(i);
if (!record->remote_addr || record->conf_id != conf_id)
continue;
+ log_source(record, 0, 1);
clean_source_record(record);
}
diff --git a/nts_ke_client.c b/nts_ke_client.c
index c22b0eb..3891f71 100644
--- a/nts_ke_client.c
+++ b/nts_ke_client.c
@@ -102,16 +102,22 @@ static int
prepare_request(NKC_Instance inst)
{
NKSN_Instance session = inst->session;
- uint16_t datum;
+ uint16_t data[2];
+ int length;
NKSN_BeginMessage(session);
- datum = htons(NKE_NEXT_PROTOCOL_NTPV4);
- if (!NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, &datum, sizeof (datum)))
+ data[0] = htons(NKE_NEXT_PROTOCOL_NTPV4);
+ if (!NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, data, sizeof (data[0])))
return 0;
- datum = htons(AEAD_AES_SIV_CMAC_256);
- if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, &datum, sizeof (datum)))
+ length = 0;
+ if (SIV_GetKeyLength(AEAD_AES_128_GCM_SIV) > 0)
+ data[length++] = htons(AEAD_AES_128_GCM_SIV);
+ if (SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256) > 0)
+ data[length++] = htons(AEAD_AES_SIV_CMAC_256);
+ if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, data,
+ length * sizeof (data[0])))
return 0;
if (!NKSN_EndMessage(session))
@@ -159,12 +165,14 @@ process_response(NKC_Instance inst)
next_protocol = NKE_NEXT_PROTOCOL_NTPV4;
break;
case NKE_RECORD_AEAD_ALGORITHM:
- if (length != 2 || ntohs(data[0]) != AEAD_AES_SIV_CMAC_256) {
+ if (length != 2 || (ntohs(data[0]) != AEAD_AES_SIV_CMAC_256 &&
+ ntohs(data[0]) != AEAD_AES_128_GCM_SIV) ||
+ SIV_GetKeyLength(ntohs(data[0])) <= 0) {
DEBUG_LOG("Unexpected NTS-KE AEAD algorithm");
error = 1;
break;
}
- aead_algorithm = AEAD_AES_SIV_CMAC_256;
+ aead_algorithm = ntohs(data[0]);
inst->context.algorithm = aead_algorithm;
break;
case NKE_RECORD_ERROR:
@@ -236,7 +244,7 @@ process_response(NKC_Instance inst)
if (error || inst->num_cookies == 0 ||
next_protocol != NKE_NEXT_PROTOCOL_NTPV4 ||
- aead_algorithm != AEAD_AES_SIV_CMAC_256)
+ aead_algorithm < 0)
return 0;
return 1;
@@ -370,6 +378,13 @@ NKC_Start(NKC_Instance inst)
return 0;
}
+ /* Don't try to connect if missing the algorithm which all servers
+ are required to support */
+ if (SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256) <= 0) {
+ LOG(LOGS_ERR, "Missing AES-SIV-CMAC-256");
+ return 0;
+ }
+
/* Follow the bindacqaddress and bindacqdevice settings */
CNF_GetBindAcquisitionAddress(inst->address.ip_addr.family, &local_addr.ip_addr);
local_addr.port = 0;
diff --git a/nts_ke_server.c b/nts_ke_server.c
index ece1b4c..6c60a5b 100644
--- a/nts_ke_server.c
+++ b/nts_ke_server.c
@@ -47,31 +47,33 @@
#define SERVER_TIMEOUT 2.0
-#define SERVER_COOKIE_SIV AEAD_AES_SIV_CMAC_256
-#define SERVER_COOKIE_NONCE_LENGTH 16
+#define MAX_COOKIE_NONCE_LENGTH 16
#define KEY_ID_INDEX_BITS 2
#define MAX_SERVER_KEYS (1U << KEY_ID_INDEX_BITS)
#define FUTURE_KEYS 1
#define DUMP_FILENAME "ntskeys"
-#define DUMP_IDENTIFIER "NKS0\n"
+#define DUMP_IDENTIFIER "NKS1\n"
+#define OLD_DUMP_IDENTIFIER "NKS0\n"
#define INVALID_SOCK_FD (-7)
typedef struct {
uint32_t key_id;
- unsigned char nonce[SERVER_COOKIE_NONCE_LENGTH];
} ServerCookieHeader;
typedef struct {
uint32_t id;
unsigned char key[SIV_MAX_KEY_LENGTH];
+ SIV_Algorithm siv_algorithm;
SIV_Instance siv;
+ int nonce_length;
} ServerKey;
typedef struct {
uint32_t key_id;
+ uint32_t siv_algorithm;
unsigned char key[SIV_MAX_KEY_LENGTH];
IPAddr client_addr;
uint16_t client_port;
@@ -149,11 +151,29 @@ handle_client(int sock_fd, IPSockAddr *addr)
/* ================================================== */
static void
+update_key_siv(ServerKey *key, SIV_Algorithm algorithm)
+{
+ if (!key->siv || key->siv_algorithm != algorithm) {
+ if (key->siv)
+ SIV_DestroyInstance(key->siv);
+ key->siv_algorithm = algorithm;
+ key->siv = SIV_CreateInstance(algorithm);
+ key->nonce_length = MIN(SIV_GetMaxNonceLength(key->siv), MAX_COOKIE_NONCE_LENGTH);
+ }
+
+ if (!key->siv || !SIV_SetKey(key->siv, key->key, SIV_GetKeyLength(key->siv_algorithm)))
+ LOG_FATAL("Could not set SIV key");
+}
+
+/* ================================================== */
+
+static void
handle_helper_request(int fd, int event, void *arg)
{
SCK_Message *message;
HelperRequest *req;
IPSockAddr client_addr;
+ ServerKey *key;
int sock_fd;
/* Receive the helper request with the NTS-KE session socket.
@@ -181,16 +201,14 @@ handle_helper_request(int fd, int event, void *arg)
req = message->data;
/* Extract the current server key and client address from the request */
- server_keys[current_server_key].id = ntohl(req->key_id);
- assert(sizeof (server_keys[current_server_key].key) == sizeof (req->key));
- memcpy(server_keys[current_server_key].key, req->key,
- sizeof (server_keys[current_server_key].key));
+ key = &server_keys[current_server_key];
+ key->id = ntohl(req->key_id);
+ assert(sizeof (key->key) == sizeof (req->key));
+ memcpy(key->key, req->key, sizeof (key->key));
UTI_IPNetworkToHost(&req->client_addr, &client_addr.ip_addr);
client_addr.port = ntohs(req->client_port);
- if (!SIV_SetKey(server_keys[current_server_key].siv, server_keys[current_server_key].key,
- SIV_GetKeyLength(SERVER_COOKIE_SIV)))
- LOG_FATAL("Could not set SIV key");
+ update_key_siv(key, ntohl(req->siv_algorithm));
if (!handle_client(sock_fd, &client_addr)) {
SCK_CloseSocket(sock_fd);
@@ -240,6 +258,7 @@ accept_connection(int listening_fd, int event, void *arg)
/* Include the current server key and client address in the request */
req.key_id = htonl(server_keys[current_server_key].id);
+ req.siv_algorithm = htonl(server_keys[current_server_key].siv_algorithm);
assert(sizeof (req.key) == sizeof (server_keys[current_server_key].key));
memcpy(req.key, server_keys[current_server_key].key, sizeof (req.key));
UTI_IPHostToNetwork(&addr.ip_addr, &req.client_addr);
@@ -427,8 +446,9 @@ process_request(NKSN_Instance session)
for (i = 0; i < MIN(length, sizeof (data)) / 2; i++) {
aead_algorithm_values++;
- if (ntohs(data[i]) == AEAD_AES_SIV_CMAC_256)
- aead_algorithm = AEAD_AES_SIV_CMAC_256;
+ /* Use the first supported algorithm */
+ if (aead_algorithm < 0 && SIV_GetKeyLength(ntohs(data[i])) > 0)
+ aead_algorithm = ntohs(data[i]);;
}
break;
case NKE_RECORD_ERROR:
@@ -470,28 +490,37 @@ handle_message(void *arg)
static void
generate_key(int index)
{
+ SIV_Algorithm algorithm;
+ ServerKey *key;
int key_length;
if (index < 0 || index >= MAX_SERVER_KEYS)
assert(0);
- key_length = SIV_GetKeyLength(SERVER_COOKIE_SIV);
- if (key_length > sizeof (server_keys[index].key))
- assert(0);
+ /* Prefer AES-128-GCM-SIV if available. Note that if older keys loaded
+ from ntsdumpdir use a different algorithm, responding to NTP requests
+ with cookies encrypted with those keys will not work if the new algorithm
+ produces longer cookies (i.e. response would be longer than request).
+ Switching from AES-SIV-CMAC-256 to AES-128-GCM-SIV is ok. */
+ algorithm = SIV_GetKeyLength(AEAD_AES_128_GCM_SIV) > 0 ?
+ AEAD_AES_128_GCM_SIV : AEAD_AES_SIV_CMAC_256;
- UTI_GetRandomBytesUrandom(server_keys[index].key, key_length);
+ key = &server_keys[index];
- if (!server_keys[index].siv ||
- !SIV_SetKey(server_keys[index].siv, server_keys[index].key, key_length))
- LOG_FATAL("Could not set SIV key");
+ key_length = SIV_GetKeyLength(algorithm);
+ if (key_length > sizeof (key->key))
+ assert(0);
- UTI_GetRandomBytes(&server_keys[index].id, sizeof (server_keys[index].id));
+ UTI_GetRandomBytesUrandom(key->key, key_length);
+ UTI_GetRandomBytes(&key->id, sizeof (key->id));
/* Encode the index in the lowest bits of the ID */
- server_keys[index].id &= -1U << KEY_ID_INDEX_BITS;
- server_keys[index].id |= index;
+ key->id &= -1U << KEY_ID_INDEX_BITS;
+ key->id |= index;
+
+ update_key_siv(key, algorithm);
- DEBUG_LOG("Generated server key %"PRIX32, server_keys[index].id);
+ DEBUG_LOG("Generated key %08"PRIX32" (%d)", key->id, (int)key->siv_algorithm);
last_server_key_ts = SCH_GetLastEventMonoTime();
}
@@ -519,18 +548,19 @@ save_keys(void)
if (!f)
return;
- key_length = SIV_GetKeyLength(SERVER_COOKIE_SIV);
last_key_age = SCH_GetLastEventMonoTime() - last_server_key_ts;
- if (fprintf(f, "%s%d %.1f\n", DUMP_IDENTIFIER, SERVER_COOKIE_SIV, last_key_age) < 0)
+ if (fprintf(f, "%s%.1f\n", DUMP_IDENTIFIER, last_key_age) < 0)
goto error;
for (i = 0; i < MAX_SERVER_KEYS; i++) {
index = (current_server_key + i + 1 + FUTURE_KEYS) % MAX_SERVER_KEYS;
+ key_length = SIV_GetKeyLength(server_keys[index].siv_algorithm);
if (key_length > sizeof (server_keys[index].key) ||
!UTI_BytesToHex(server_keys[index].key, key_length, buf, sizeof (buf)) ||
- fprintf(f, "%08"PRIX32" %s\n", server_keys[index].id, buf) < 0)
+ fprintf(f, "%08"PRIX32" %s %d\n", server_keys[index].id, buf,
+ (int)server_keys[index].siv_algorithm) < 0)
goto error;
}
@@ -545,7 +575,7 @@ save_keys(void)
return;
error:
- DEBUG_LOG("Could not %s server keys", "save");
+ LOG(LOGS_ERR, "Could not %s %s", "save", "server NTS keys");
fclose(f);
if (!UTI_RemoveFile(dump_dir, DUMP_FILENAME, NULL))
@@ -554,17 +584,16 @@ error:
/* ================================================== */
-#define MAX_WORDS 2
+#define MAX_WORDS 3
static int
load_keys(void)
{
+ int i, index, key_length, algorithm = 0, old_ver;
char *dump_dir, line[1024], *words[MAX_WORDS];
- unsigned char key[SIV_MAX_KEY_LENGTH];
- int i, index, key_length, algorithm;
+ ServerKey new_keys[MAX_SERVER_KEYS];
double key_age;
FILE *f;
- uint32_t id;
dump_dir = CNF_GetNtsDumpDir();
if (!dump_dir)
@@ -574,43 +603,57 @@ load_keys(void)
if (!f)
return 0;
- if (!fgets(line, sizeof (line), f) || strcmp(line, DUMP_IDENTIFIER) != 0 ||
- !fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 2 ||
- sscanf(words[0], "%d", &algorithm) != 1 || algorithm != SERVER_COOKIE_SIV ||
- sscanf(words[1], "%lf", &key_age) != 1)
+ if (!fgets(line, sizeof (line), f) ||
+ (strcmp(line, DUMP_IDENTIFIER) != 0 && strcmp(line, OLD_DUMP_IDENTIFIER) != 0))
goto error;
- key_length = SIV_GetKeyLength(SERVER_COOKIE_SIV);
- last_server_key_ts = SCH_GetLastEventMonoTime() - MAX(key_age, 0.0);
+ old_ver = strcmp(line, DUMP_IDENTIFIER) != 0;
+
+ if (!fgets(line, sizeof (line), f) ||
+ UTI_SplitString(line, words, MAX_WORDS) != (old_ver ? 2 : 1) ||
+ (old_ver && sscanf(words[0], "%d", &algorithm) != 1) ||
+ sscanf(words[old_ver ? 1 : 0], "%lf", &key_age) != 1)
+ goto error;
for (i = 0; i < MAX_SERVER_KEYS && fgets(line, sizeof (line), f); i++) {
- if (UTI_SplitString(line, words, MAX_WORDS) != 2 ||
- sscanf(words[0], "%"PRIX32, &id) != 1)
+ if (UTI_SplitString(line, words, MAX_WORDS) != (old_ver ? 2 : 3) ||
+ sscanf(words[0], "%"PRIX32, &new_keys[i].id) != 1 ||
+ (!old_ver && sscanf(words[2], "%d", &algorithm) != 1))
goto error;
- if (UTI_HexToBytes(words[1], key, sizeof (key)) != key_length)
- goto error;
+ new_keys[i].siv_algorithm = algorithm;
+ key_length = SIV_GetKeyLength(algorithm);
- index = id % MAX_SERVER_KEYS;
+ if ((i > 0 && (new_keys[i].id - new_keys[i - 1].id) % MAX_SERVER_KEYS != 1) ||
+ key_length <= 0 ||
+ UTI_HexToBytes(words[1], new_keys[i].key, sizeof (new_keys[i].key)) != key_length)
+ goto error;
+ }
- server_keys[index].id = id;
- assert(sizeof (server_keys[index].key) == sizeof (key));
- memcpy(server_keys[index].key, key, key_length);
+ if (i < MAX_SERVER_KEYS)
+ goto error;
- if (!SIV_SetKey(server_keys[index].siv, server_keys[index].key, key_length))
- LOG_FATAL("Could not set SIV key");
+ for (i = 0; i < MAX_SERVER_KEYS; i++) {
+ index = new_keys[i].id % MAX_SERVER_KEYS;
+ server_keys[index].id = new_keys[i].id;
+ memcpy(server_keys[index].key, new_keys[i].key, sizeof (server_keys[index].key));
- DEBUG_LOG("Loaded key %"PRIX32, id);
+ update_key_siv(&server_keys[index], new_keys[i].siv_algorithm);
- current_server_key = (index + MAX_SERVER_KEYS - FUTURE_KEYS) % MAX_SERVER_KEYS;
+ DEBUG_LOG("Loaded key %08"PRIX32" (%d)",
+ server_keys[index].id, (int)server_keys[index].siv_algorithm);
}
+ current_server_key = (index + MAX_SERVER_KEYS - FUTURE_KEYS) % MAX_SERVER_KEYS;
+ last_server_key_ts = SCH_GetLastEventMonoTime() - MAX(key_age, 0.0);
+
fclose(f);
+ LOG(LOGS_ERR, "Loaded %s", "server NTS keys");
return 1;
error:
- DEBUG_LOG("Could not %s server keys", "load");
+ LOG(LOGS_ERR, "Could not %s %s", "load", "server NTS keys");
fclose(f);
return 0;
@@ -759,7 +802,7 @@ NKS_Initialise(void)
/* Generate random keys, even if they will be replaced by reloaded keys,
or unused (in the helper) */
for (i = 0; i < MAX_SERVER_KEYS; i++) {
- server_keys[i].siv = SIV_CreateInstance(SERVER_COOKIE_SIV);
+ server_keys[i].siv = NULL;
generate_key(i);
}
@@ -779,6 +822,11 @@ NKS_Initialise(void)
key_delay = key_rotation_interval - (SCH_GetLastEventMonoTime() - last_server_key_ts);
SCH_AddTimeoutByDelay(MAX(key_delay, 0.0), key_timeout, NULL);
}
+
+ /* Warn if keys are not saved, which can cause a flood of requests
+ after server restart */
+ if (!CNF_GetNtsDumpDir())
+ LOG(LOGS_WARN, "No ntsdumpdir to save server keys");
}
initialised = 1;
@@ -852,7 +900,7 @@ NKS_ReloadKeys(void)
int
NKS_GenerateCookie(NKE_Context *context, NKE_Cookie *cookie)
{
- unsigned char plaintext[2 * NKE_MAX_KEY_LENGTH], *ciphertext;
+ unsigned char *nonce, plaintext[2 * NKE_MAX_KEY_LENGTH], *ciphertext;
int plaintext_length, tag_length;
ServerCookieHeader *header;
ServerKey *key;
@@ -862,14 +910,12 @@ NKS_GenerateCookie(NKE_Context *context, NKE_Cookie *cookie)
return 0;
}
- /* The algorithm is hardcoded for now */
- if (context->algorithm != AEAD_AES_SIV_CMAC_256) {
- DEBUG_LOG("Unexpected SIV algorithm");
- return 0;
- }
+ /* The AEAD ID is not encoded in the cookie. It is implied from the key
+ length (as long as only algorithms with different key lengths are
+ supported). */
if (context->c2s.length < 0 || context->c2s.length > NKE_MAX_KEY_LENGTH ||
- context->s2c.length < 0 || context->s2c.length > NKE_MAX_KEY_LENGTH) {
+ context->s2c.length != context->c2s.length) {
DEBUG_LOG("Invalid key length");
return 0;
}
@@ -879,7 +925,11 @@ NKS_GenerateCookie(NKE_Context *context, NKE_Cookie *cookie)
header = (ServerCookieHeader *)cookie->cookie;
header->key_id = htonl(key->id);
- UTI_GetRandomBytes(header->nonce, sizeof (header->nonce));
+
+ nonce = cookie->cookie + sizeof (*header);
+ if (key->nonce_length > sizeof (cookie->cookie) - sizeof (*header))
+ assert(0);
+ UTI_GetRandomBytes(nonce, key->nonce_length);
plaintext_length = context->c2s.length + context->s2c.length;
assert(plaintext_length <= sizeof (plaintext));
@@ -887,11 +937,11 @@ NKS_GenerateCookie(NKE_Context *context, NKE_Cookie *cookie)
memcpy(plaintext + context->c2s.length, context->s2c.key, context->s2c.length);
tag_length = SIV_GetTagLength(key->siv);
- cookie->length = sizeof (*header) + plaintext_length + tag_length;
+ cookie->length = sizeof (*header) + key->nonce_length + plaintext_length + tag_length;
assert(cookie->length <= sizeof (cookie->cookie));
- ciphertext = cookie->cookie + sizeof (*header);
+ ciphertext = cookie->cookie + sizeof (*header) + key->nonce_length;
- if (!SIV_Encrypt(key->siv, header->nonce, sizeof (header->nonce),
+ if (!SIV_Encrypt(key->siv, nonce, key->nonce_length,
"", 0,
plaintext, plaintext_length,
ciphertext, plaintext_length + tag_length)) {
@@ -907,7 +957,7 @@ NKS_GenerateCookie(NKE_Context *context, NKE_Cookie *cookie)
int
NKS_DecodeCookie(NKE_Cookie *cookie, NKE_Context *context)
{
- unsigned char plaintext[2 * NKE_MAX_KEY_LENGTH], *ciphertext;
+ unsigned char *nonce, plaintext[2 * NKE_MAX_KEY_LENGTH], *ciphertext;
int ciphertext_length, plaintext_length, tag_length;
ServerCookieHeader *header;
ServerKey *key;
@@ -924,8 +974,6 @@ NKS_DecodeCookie(NKE_Cookie *cookie, NKE_Context *context)
}
header = (ServerCookieHeader *)cookie->cookie;
- ciphertext = cookie->cookie + sizeof (*header);
- ciphertext_length = cookie->length - sizeof (*header);
key_id = ntohl(header->key_id);
key = &server_keys[key_id % MAX_SERVER_KEYS];
@@ -935,18 +983,23 @@ NKS_DecodeCookie(NKE_Cookie *cookie, NKE_Context *context)
}
tag_length = SIV_GetTagLength(key->siv);
- if (tag_length >= ciphertext_length) {
+
+ if (cookie->length <= (int)sizeof (*header) + key->nonce_length + tag_length) {
DEBUG_LOG("Invalid cookie length");
return 0;
}
+ nonce = cookie->cookie + sizeof (*header);
+ ciphertext = cookie->cookie + sizeof (*header) + key->nonce_length;
+ ciphertext_length = cookie->length - sizeof (*header) - key->nonce_length;
plaintext_length = ciphertext_length - tag_length;
+
if (plaintext_length > sizeof (plaintext) || plaintext_length % 2 != 0) {
DEBUG_LOG("Invalid cookie length");
return 0;
}
- if (!SIV_Decrypt(key->siv, header->nonce, sizeof (header->nonce),
+ if (!SIV_Decrypt(key->siv, nonce, key->nonce_length,
"", 0,
ciphertext, ciphertext_length,
plaintext, plaintext_length)) {
@@ -954,7 +1007,19 @@ NKS_DecodeCookie(NKE_Cookie *cookie, NKE_Context *context)
return 0;
}
- context->algorithm = AEAD_AES_SIV_CMAC_256;
+ /* Select a supported algorithm corresponding to the key length, avoiding
+ potentially slow SIV_GetKeyLength() */
+ switch (plaintext_length / 2) {
+ case 16:
+ context->algorithm = AEAD_AES_128_GCM_SIV;
+ break;
+ case 32:
+ context->algorithm = AEAD_AES_SIV_CMAC_256;
+ break;
+ default:
+ DEBUG_LOG("Unknown key length");
+ return 0;
+ }
context->c2s.length = plaintext_length / 2;
context->s2c.length = plaintext_length / 2;
diff --git a/nts_ke_session.c b/nts_ke_session.c
index dfcd18a..2ae1e91 100644
--- a/nts_ke_session.c
+++ b/nts_ke_session.c
@@ -667,6 +667,8 @@ create_credentials(const char **certs, const char **keys, int n_certs_keys,
assert(0);
for (i = 0; i < n_certs_keys; i++) {
+ if (!UTI_CheckFilePermissions(keys[i], 0771))
+ ;
r = gnutls_certificate_set_x509_key_file(credentials, certs[i], keys[i],
GNUTLS_X509_FMT_PEM);
if (r < 0)
diff --git a/nts_ntp_auth.c b/nts_ntp_auth.c
index ac0763e..b92c406 100644
--- a/nts_ntp_auth.c
+++ b/nts_ntp_auth.c
@@ -61,23 +61,25 @@ get_padded_length(int length)
int
NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
- const unsigned char *nonce, int nonce_length,
+ const unsigned char *nonce, int max_nonce_length,
const unsigned char *plaintext, int plaintext_length,
int min_ef_length)
{
- int auth_length, ciphertext_length, assoc_length;
+ int auth_length, ciphertext_length, assoc_length, nonce_length, max_siv_nonce_length;
int nonce_padding, ciphertext_padding, additional_padding;
unsigned char *ciphertext, *body;
struct AuthHeader *header;
assert(sizeof (*header) == 4);
- if (nonce_length <= 0 || plaintext_length < 0) {
+ if (max_nonce_length <= 0 || plaintext_length < 0) {
DEBUG_LOG("Invalid nonce/plaintext length");
return 0;
}
assoc_length = info->length;
+ max_siv_nonce_length = SIV_GetMaxNonceLength(siv);
+ nonce_length = MIN(max_nonce_length, max_siv_nonce_length);
ciphertext_length = SIV_GetTagLength(siv) + plaintext_length;
nonce_padding = get_padding_length(nonce_length);
ciphertext_padding = get_padding_length(ciphertext_length);
@@ -86,8 +88,8 @@ NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
auth_length = sizeof (*header) + nonce_length + nonce_padding +
ciphertext_length + ciphertext_padding;
additional_padding = MAX(min_ef_length - auth_length - 4, 0);
- additional_padding = MAX(NTS_MIN_UNPADDED_NONCE_LENGTH - nonce_length - nonce_padding,
- additional_padding);
+ additional_padding = MAX(MIN(NTS_MIN_UNPADDED_NONCE_LENGTH, max_siv_nonce_length) -
+ nonce_length - nonce_padding, additional_padding);
auth_length += additional_padding;
if (!NEF_AddBlankField(packet, info, NTP_EF_NTS_AUTH_AND_EEF, auth_length,
@@ -113,6 +115,7 @@ NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
plaintext, plaintext_length, ciphertext, ciphertext_length)) {
DEBUG_LOG("SIV encrypt failed");
info->length = assoc_length;
+ info->ext_fields--;
return 0;
}
@@ -127,7 +130,7 @@ int
NNA_DecryptAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv, int ef_start,
unsigned char *plaintext, int buffer_length, int *plaintext_length)
{
- unsigned int siv_tag_length, nonce_length, ciphertext_length;
+ int siv_tag_length, max_siv_nonce_length, nonce_length, ciphertext_length;
unsigned char *nonce, *ciphertext;
int ef_type, ef_body_length;
void *ef_body;
@@ -155,6 +158,7 @@ NNA_DecryptAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv, in
nonce = (unsigned char *)(header + 1);
ciphertext = nonce + get_padded_length(nonce_length);
+ max_siv_nonce_length = SIV_GetMaxNonceLength(siv);
siv_tag_length = SIV_GetTagLength(siv);
if (nonce_length < 1 ||
@@ -164,8 +168,8 @@ NNA_DecryptAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv, in
return 0;
}
- if (ef_body_length < sizeof (*header) +
- NTS_MIN_UNPADDED_NONCE_LENGTH + get_padded_length(ciphertext_length)) {
+ if (sizeof (*header) + MIN(NTS_MIN_UNPADDED_NONCE_LENGTH, max_siv_nonce_length) +
+ get_padded_length(ciphertext_length) > ef_body_length) {
DEBUG_LOG("Missing padding");
return 0;
}
diff --git a/nts_ntp_auth.h b/nts_ntp_auth.h
index 856beb3..19d04bf 100644
--- a/nts_ntp_auth.h
+++ b/nts_ntp_auth.h
@@ -32,7 +32,7 @@
#include "siv.h"
extern int NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
- const unsigned char *nonce, int nonce_length,
+ const unsigned char *nonce, int max_nonce_length,
const unsigned char *plaintext, int plaintext_length,
int min_ef_length);
diff --git a/nts_ntp_client.c b/nts_ntp_client.c
index 34412a6..10cf071 100644
--- a/nts_ntp_client.c
+++ b/nts_ntp_client.c
@@ -46,6 +46,9 @@
/* Maximum length of all cookies to avoid IP fragmentation */
#define MAX_TOTAL_COOKIE_LENGTH (8 * 108)
+/* Retry interval for NTS-KE start (which doesn't generate network traffic) */
+#define RETRY_INTERVAL_KE_START 2.0
+
/* Magic string of files containing keys and cookies */
#define DUMP_IDENTIFIER "NNC0\n"
@@ -203,10 +206,15 @@ set_ntp_address(NNC_Instance inst, NTP_Remote_Address *negotiated_address)
/* ================================================== */
static void
-update_next_nke_attempt(NNC_Instance inst, double now)
+update_next_nke_attempt(NNC_Instance inst, int failed_start, double now)
{
int factor, interval;
+ if (failed_start) {
+ inst->next_nke_attempt = now + RETRY_INTERVAL_KE_START;
+ return;
+ }
+
if (!inst->nke)
return;
@@ -221,8 +229,8 @@ static int
get_cookies(NNC_Instance inst)
{
NTP_Remote_Address ntp_address;
+ int got_data, failed_start = 0;
double now;
- int got_data;
assert(inst->num_cookies == 0);
@@ -239,13 +247,12 @@ get_cookies(NNC_Instance inst)
inst->nke = NKC_CreateInstance(&inst->nts_address, inst->name, inst->cert_set);
inst->nke_attempts++;
- update_next_nke_attempt(inst, now);
if (!NKC_Start(inst->nke))
- return 0;
+ failed_start = 1;
}
- update_next_nke_attempt(inst, now);
+ update_next_nke_attempt(inst, failed_start, now);
/* Wait until the session stops */
if (NKC_IsActive(inst->nke))
diff --git a/nts_ntp_server.c b/nts_ntp_server.c
index 9226c89..7db3998 100644
--- a/nts_ntp_server.c
+++ b/nts_ntp_server.c
@@ -41,13 +41,15 @@
#include "siv.h"
#include "util.h"
-#define SERVER_SIV AEAD_AES_SIV_CMAC_256
+#define MAX_SERVER_SIVS 2
struct NtsServer {
- SIV_Instance siv;
+ SIV_Instance sivs[MAX_SERVER_SIVS];
+ SIV_Algorithm siv_algorithms[MAX_SERVER_SIVS];
unsigned char nonce[NTS_MIN_UNPADDED_NONCE_LENGTH];
NKE_Cookie cookies[NTS_MAX_COOKIES];
int num_cookies;
+ int siv_index;
NTP_int64 req_tx;
};
@@ -60,6 +62,7 @@ void
NNS_Initialise(void)
{
const char **certs, **keys;
+ int i;
/* Create an NTS-NTP server instance only if NTS-KE server is enabled */
if (CNF_GetNtsServerCertAndKeyFiles(&certs, &keys) <= 0) {
@@ -68,9 +71,17 @@ NNS_Initialise(void)
}
server = Malloc(sizeof (struct NtsServer));
- server->siv = SIV_CreateInstance(SERVER_SIV);
- if (!server->siv)
- LOG_FATAL("Could not initialise SIV cipher");
+
+ server->siv_algorithms[0] = AEAD_AES_SIV_CMAC_256;
+ server->siv_algorithms[1] = AEAD_AES_128_GCM_SIV;
+ assert(MAX_SERVER_SIVS == 2);
+
+ for (i = 0; i < 2; i++)
+ server->sivs[i] = SIV_CreateInstance(server->siv_algorithms[i]);
+
+ /* AES-SIV-CMAC-256 is required on servers */
+ if (!server->sivs[0])
+ LOG_FATAL("Missing AES-SIV-CMAC-256");
}
/* ================================================== */
@@ -78,10 +89,15 @@ NNS_Initialise(void)
void
NNS_Finalise(void)
{
+ int i;
+
if (!server)
return;
- SIV_DestroyInstance(server->siv);
+ for (i = 0; i < MAX_SERVER_SIVS; i++) {
+ if (server->sivs[i])
+ SIV_DestroyInstance(server->sivs[i]);
+ }
Free(server);
server = NULL;
}
@@ -96,6 +112,7 @@ NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
unsigned char plaintext[NTP_MAX_EXTENSIONS_LENGTH];
NKE_Context context;
NKE_Cookie cookie;
+ SIV_Instance siv;
void *ef_body;
*kod = 0;
@@ -104,6 +121,7 @@ NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
return 0;
server->num_cookies = 0;
+ server->siv_index = -1;
server->req_tx = packet->transmit_ts;
if (info->ext_fields == 0 || info->mode != MODE_CLIENT)
@@ -163,17 +181,22 @@ NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
return 0;
}
- if (context.algorithm != SERVER_SIV) {
+ /* Find the SIV instance needed for authentication */
+ for (i = 0; i < MAX_SERVER_SIVS && context.algorithm != server->siv_algorithms[i]; i++)
+ ;
+ if (i == MAX_SERVER_SIVS || !server->sivs[i]) {
DEBUG_LOG("Unexpected SIV");
return 0;
}
+ server->siv_index = i;
+ siv = server->sivs[i];
- if (!SIV_SetKey(server->siv, context.c2s.key, context.c2s.length)) {
+ if (!SIV_SetKey(siv, context.c2s.key, context.c2s.length)) {
DEBUG_LOG("Could not set C2S key");
return 0;
}
- if (!NNA_DecryptAuthEF(packet, info, server->siv, auth_start,
+ if (!NNA_DecryptAuthEF(packet, info, siv, auth_start,
plaintext, sizeof (plaintext), &plaintext_length)) {
*kod = NTP_KOD_NTS_NAK;
return 0;
@@ -199,7 +222,7 @@ NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
}
}
- if (!SIV_SetKey(server->siv, context.s2c.key, context.s2c.length)) {
+ if (!SIV_SetKey(siv, context.s2c.key, context.s2c.length)) {
DEBUG_LOG("Could not set S2C key");
return 0;
}
@@ -271,9 +294,12 @@ NNS_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *req_info,
server->num_cookies = 0;
+ if (server->siv_index < 0)
+ return 0;
+
/* Generate an authenticator field which will make the length
of the response equal to the length of the request */
- if (!NNA_GenerateAuthEF(response, res_info, server->siv,
+ if (!NNA_GenerateAuthEF(response, res_info, server->sivs[server->siv_index],
server->nonce, sizeof (server->nonce),
plaintext, plaintext_length,
req_info->length - res_info->length))
diff --git a/pktlength.c b/pktlength.c
index 642e477..d7ed272 100644
--- a/pktlength.c
+++ b/pktlength.c
@@ -129,6 +129,7 @@ static const struct request_length request_lengths[] = {
REQ_LENGTH_ENTRY(select_data, select_data), /* SELECT_DATA */
REQ_LENGTH_ENTRY(null, null), /* RELOAD_SOURCES */
REQ_LENGTH_ENTRY(doffset, null), /* DOFFSET2 */
+ REQ_LENGTH_ENTRY(modify_select_opts, null), /* MODIFY_SELECTOPTS */
};
static const uint16_t reply_lengths[] = {
@@ -156,7 +157,8 @@ static const uint16_t reply_lengths[] = {
RPY_LENGTH_ENTRY(client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX3 */
0, /* SERVER_STATS2 - not supported */
RPY_LENGTH_ENTRY(select_data), /* SELECT_DATA */
- RPY_LENGTH_ENTRY(server_stats), /* SERVER_STATS3 */
+ 0, /* SERVER_STATS3 - not supported */
+ RPY_LENGTH_ENTRY(server_stats), /* SERVER_STATS4 */
};
/* ================================================== */
diff --git a/ptp.h b/ptp.h
index 7a93590..8034a2c 100644
--- a/ptp.h
+++ b/ptp.h
@@ -44,7 +44,12 @@ typedef struct {
uint8_t domain;
uint8_t min_sdoid;
uint16_t flags;
- uint8_t rest[26];
+ uint8_t correction[8];
+ uint8_t msg_specific[4];
+ uint8_t port_id[10];
+ uint16_t sequence_id;
+ uint8_t control;
+ int8_t interval;
} PTP_Header;
typedef struct {
diff --git a/refclock_phc.c b/refclock_phc.c
index e0e206e..3b68c84 100644
--- a/refclock_phc.c
+++ b/refclock_phc.c
@@ -33,6 +33,9 @@
#include "sysincl.h"
+#include <sys/sysmacros.h>
+
+#include "array.h"
#include "refclock.h"
#include "hwclock.h"
#include "local.h"
@@ -44,14 +47,19 @@
struct phc_instance {
int fd;
+ int dev_index;
int mode;
int nocrossts;
int extpps;
int pin;
int channel;
+ struct timespec last_extts;
HCL_Instance clock;
};
+/* Array of RCL_Instance with enabled extpps */
+static ARR_Instance extts_phcs = NULL;
+
static void read_ext_pulse(int sockfd, int event, void *anything);
static int phc_initialise(RCL_Instance instance)
@@ -59,6 +67,7 @@ static int phc_initialise(RCL_Instance instance)
const char *options[] = {"nocrossts", "extpps", "pin", "channel", "clear", NULL};
struct phc_instance *phc;
int phc_fd, rising_edge;
+ struct stat st;
char *path, *s;
RCL_CheckDriverOptions(instance, options);
@@ -71,10 +80,13 @@ static int phc_initialise(RCL_Instance instance)
phc = MallocNew(struct phc_instance);
phc->fd = phc_fd;
+ if (fstat(phc_fd, &st) < 0 || !S_ISCHR(st.st_mode))
+ LOG_FATAL("Could not get PHC index");
+ phc->dev_index = minor(st.st_rdev);
phc->mode = 0;
phc->nocrossts = RCL_GetDriverOption(instance, "nocrossts") ? 1 : 0;
phc->extpps = RCL_GetDriverOption(instance, "extpps") ? 1 : 0;
-
+ UTI_ZeroTimespec(&phc->last_extts);
phc->clock = HCL_CreateInstance(0, 16, UTI_Log2ToDouble(RCL_GetDriverPoll(instance)),
RCL_GetPrecision(instance));
@@ -90,6 +102,10 @@ static int phc_initialise(RCL_Instance instance)
LOG_FATAL("Could not enable external PHC timestamping");
SCH_AddFileHandler(phc->fd, SCH_FILE_INPUT, read_ext_pulse, instance);
+
+ if (!extts_phcs)
+ extts_phcs = ARR_CreateInstance(sizeof (RCL_Instance));
+ ARR_AppendElement(extts_phcs, &instance);
} else {
phc->pin = phc->channel = 0;
}
@@ -101,12 +117,22 @@ static int phc_initialise(RCL_Instance instance)
static void phc_finalise(RCL_Instance instance)
{
struct phc_instance *phc;
+ unsigned int i;
phc = (struct phc_instance *)RCL_GetDriverData(instance);
if (phc->extpps) {
SCH_RemoveFileHandler(phc->fd);
SYS_Linux_SetPHCExtTimestamping(phc->fd, phc->pin, phc->channel, 0, 0, 0);
+
+ for (i = 0; i < ARR_GetSize(extts_phcs); i++) {
+ if ((*(RCL_Instance *)ARR_GetElement(extts_phcs, i)) == instance)
+ ARR_RemoveElement(extts_phcs, i--);
+ }
+ if (ARR_GetSize(extts_phcs) == 0) {
+ ARR_DestroyInstance(extts_phcs);
+ extts_phcs = NULL;
+ }
}
HCL_DestroyInstance(phc->clock);
@@ -114,30 +140,52 @@ static void phc_finalise(RCL_Instance instance)
Free(phc);
}
-static void read_ext_pulse(int fd, int event, void *anything)
+static void process_ext_pulse(RCL_Instance instance, struct timespec *phc_ts)
{
- RCL_Instance instance;
struct phc_instance *phc;
- struct timespec phc_ts, local_ts;
+ struct timespec local_ts;
double local_err;
- int channel;
- instance = anything;
phc = RCL_GetDriverData(instance);
- if (!SYS_Linux_ReadPHCExtTimestamp(phc->fd, &phc_ts, &channel))
- return;
-
- if (channel != phc->channel) {
- DEBUG_LOG("Unexpected extts channel %d\n", channel);
+ if (UTI_CompareTimespecs(&phc->last_extts, phc_ts) == 0) {
+ DEBUG_LOG("Ignoring duplicated PHC timestamp");
return;
}
+ phc->last_extts = *phc_ts;
- if (!HCL_CookTime(phc->clock, &phc_ts, &local_ts, &local_err))
+ if (!HCL_CookTime(phc->clock, phc_ts, &local_ts, &local_err))
return;
RCL_AddCookedPulse(instance, &local_ts, 1.0e-9 * local_ts.tv_nsec, local_err,
- UTI_DiffTimespecsToDouble(&phc_ts, &local_ts));
+ UTI_DiffTimespecsToDouble(phc_ts, &local_ts));
+}
+
+static void read_ext_pulse(int fd, int event, void *anything)
+{
+ RCL_Instance instance;
+ struct phc_instance *phc1, *phc2;
+ struct timespec phc_ts;
+ unsigned int i;
+ int channel;
+
+ if (!SYS_Linux_ReadPHCExtTimestamp(fd, &phc_ts, &channel))
+ return;
+
+ instance = anything;
+ phc1 = RCL_GetDriverData(instance);
+
+ /* The Linux kernel (as of 6.2) has one shared queue of timestamps for all
+ descriptors of the same PHC. Search for all refclocks that expect
+ the timestamp. */
+
+ for (i = 0; i < ARR_GetSize(extts_phcs); i++) {
+ instance = *(RCL_Instance *)ARR_GetElement(extts_phcs, i);
+ phc2 = RCL_GetDriverData(instance);
+ if (!phc2->extpps || phc2->dev_index != phc1->dev_index || phc2->channel != channel)
+ continue;
+ process_ext_pulse(instance, &phc_ts);
+ }
}
#define PHC_READINGS 25
diff --git a/refclock_sock.c b/refclock_sock.c
index f0d91c5..2da57ef 100644
--- a/refclock_sock.c
+++ b/refclock_sock.c
@@ -58,8 +58,29 @@ struct sock_sample {
int magic;
};
+/* On 32-bit glibc-based systems enable conversion between timevals using
+ 32-bit and 64-bit time_t to support SOCK clients compiled with different
+ time_t size than chrony */
+#ifdef __GLIBC_PREREQ
+#if __GLIBC_PREREQ(2, 34) && __TIMESIZE == 32
+#define CONVERT_TIMEVAL 1
+#if defined(_TIME_BITS) && _TIME_BITS == 64
+typedef int32_t alt_time_t;
+typedef int32_t alt_suseconds_t;
+#else
+typedef int64_t alt_time_t;
+typedef int64_t alt_suseconds_t;
+#endif
+struct alt_timeval {
+ alt_time_t tv_sec;
+ alt_suseconds_t tv_usec;
+};
+#endif
+#endif
+
static void read_sample(int sockfd, int event, void *anything)
{
+ char buf[sizeof (struct sock_sample) + 16];
struct timespec sys_ts, ref_ts;
struct sock_sample sample;
RCL_Instance instance;
@@ -67,14 +88,33 @@ static void read_sample(int sockfd, int event, void *anything)
instance = (RCL_Instance)anything;
- s = recv(sockfd, &sample, sizeof (sample), 0);
+ s = recv(sockfd, buf, sizeof (buf), 0);
if (s < 0) {
DEBUG_LOG("Could not read SOCK sample : %s", strerror(errno));
return;
}
- if (s != sizeof (sample)) {
+ if (s == sizeof (sample)) {
+ memcpy(&sample, buf, sizeof (sample));
+#ifdef CONVERT_TIMEVAL
+ } else if (s == sizeof (sample) - sizeof (struct timeval) + sizeof (struct alt_timeval)) {
+ struct alt_timeval atv;
+ memcpy(&atv, buf, sizeof (atv));
+#ifndef HAVE_LONG_TIME_T
+ if (atv.tv_sec > INT32_MAX || atv.tv_sec < INT32_MIN ||
+ atv.tv_usec > INT32_MAX || atv.tv_usec < INT32_MIN) {
+ DEBUG_LOG("Could not convert 64-bit timeval");
+ return;
+ }
+#endif
+ sample.tv.tv_sec = atv.tv_sec;
+ sample.tv.tv_usec = atv.tv_usec;
+ DEBUG_LOG("Converted %d-bit timeval", 8 * (int)sizeof (alt_time_t));
+ memcpy((char *)&sample + sizeof (struct timeval), buf + sizeof (struct alt_timeval),
+ sizeof (sample) - sizeof (struct timeval));
+#endif
+ } else {
DEBUG_LOG("Unexpected length of SOCK sample : %d != %ld",
s, (long)sizeof (sample));
return;
diff --git a/reference.c b/reference.c
index e6cc547..97dfbe9 100644
--- a/reference.c
+++ b/reference.c
@@ -1329,6 +1329,7 @@ void
REF_ModifyMaxupdateskew(double new_max_update_skew)
{
max_update_skew = new_max_update_skew * 1.0e-6;
+ LOG(LOGS_INFO, "New maxupdateskew %f ppm", new_max_update_skew);
}
/* ================================================== */
@@ -1338,6 +1339,7 @@ REF_ModifyMakestep(int limit, double threshold)
{
make_step_limit = limit;
make_step_threshold = threshold;
+ LOG(LOGS_INFO, "New makestep %f %d", threshold, limit);
}
/* ================================================== */
@@ -1349,6 +1351,7 @@ REF_EnableLocal(int stratum, double distance, int orphan)
local_stratum = CLAMP(1, stratum, NTP_MAX_STRATUM - 1);
local_distance = distance;
local_orphan = !!orphan;
+ LOG(LOGS_INFO, "%s local reference mode", "Enabled");
}
/* ================================================== */
@@ -1357,6 +1360,7 @@ void
REF_DisableLocal(void)
{
enable_local_stratum = 0;
+ LOG(LOGS_INFO, "%s local reference mode", "Disabled");
}
/* ================================================== */
diff --git a/reports.h b/reports.h
index 6674dd9..0150fc6 100644
--- a/reports.h
+++ b/reports.h
@@ -109,17 +109,23 @@ typedef struct {
} RPT_ClientAccessByIndex_Report;
typedef struct {
- uint32_t ntp_hits;
- uint32_t nke_hits;
- uint32_t cmd_hits;
- uint32_t ntp_drops;
- uint32_t nke_drops;
- uint32_t cmd_drops;
- uint32_t log_drops;
- uint32_t ntp_auth_hits;
- uint32_t ntp_interleaved_hits;
- uint32_t ntp_timestamps;
- uint32_t ntp_span_seconds;
+ uint64_t ntp_hits;
+ uint64_t nke_hits;
+ uint64_t cmd_hits;
+ uint64_t ntp_drops;
+ uint64_t nke_drops;
+ uint64_t cmd_drops;
+ uint64_t log_drops;
+ uint64_t ntp_auth_hits;
+ uint64_t ntp_interleaved_hits;
+ uint64_t ntp_timestamps;
+ uint64_t ntp_span_seconds;
+ uint64_t ntp_daemon_rx_timestamps;
+ uint64_t ntp_daemon_tx_timestamps;
+ uint64_t ntp_kernel_rx_timestamps;
+ uint64_t ntp_kernel_tx_timestamps;
+ uint64_t ntp_hw_rx_timestamps;
+ uint64_t ntp_hw_tx_timestamps;
} RPT_ServerStatsReport;
typedef struct {
diff --git a/sched.c b/sched.c
index 1f5ebe4..1505dc3 100644
--- a/sched.c
+++ b/sched.c
@@ -104,7 +104,10 @@ static unsigned long n_timer_queue_entries;
static SCH_TimeoutID next_tqe_id;
/* Pointer to head of free list */
-static TimerQueueEntry *tqe_free_list = NULL;
+static TimerQueueEntry *tqe_free_list;
+
+/* Array of all allocated tqe blocks to be freed in finalisation */
+static ARR_Instance tqe_blocks;
/* Timestamp when was last timeout dispatched for each class */
static struct timespec last_class_dispatch[SCH_NumberOfClasses];
@@ -133,6 +136,8 @@ SCH_Initialise(void)
n_timer_queue_entries = 0;
next_tqe_id = 0;
+ tqe_free_list = NULL;
+ tqe_blocks = ARR_CreateInstance(sizeof (TimerQueueEntry *));
timer_queue.next = &timer_queue;
timer_queue.prev = &timer_queue;
@@ -154,8 +159,14 @@ SCH_Initialise(void)
void
SCH_Finalise(void) {
+ unsigned int i;
+
ARR_DestroyInstance(file_handlers);
+ for (i = 0; i < ARR_GetSize(tqe_blocks); i++)
+ Free(*(TimerQueueEntry **)ARR_GetElement(tqe_blocks, i));
+ ARR_DestroyInstance(tqe_blocks);
+
LCL_RemoveParameterChangeHandler(handle_slew, NULL);
initialised = 0;
@@ -281,6 +292,7 @@ allocate_tqe(void)
}
new_block[0].next = NULL;
tqe_free_list = &(new_block[TQE_ALLOC_QUANTUM - 1]);
+ ARR_AppendElement(tqe_blocks, &new_block);
}
result = tqe_free_list;
diff --git a/sched.h b/sched.h
index f1f4eb9..90d757f 100644
--- a/sched.h
+++ b/sched.h
@@ -37,6 +37,7 @@ typedef enum {
SCH_NtpClientClass,
SCH_NtpPeerClass,
SCH_NtpBroadcastClass,
+ SCH_PhcPollClass,
SCH_NumberOfClasses /* needs to be last */
} SCH_TimeoutClass;
diff --git a/siv.h b/siv.h
index e303d34..868edbd 100644
--- a/siv.h
+++ b/siv.h
@@ -53,6 +53,10 @@ extern int SIV_GetKeyLength(SIV_Algorithm algorithm);
extern int SIV_SetKey(SIV_Instance instance, const unsigned char *key, int length);
+extern int SIV_GetMinNonceLength(SIV_Instance instance);
+
+extern int SIV_GetMaxNonceLength(SIV_Instance instance);
+
extern int SIV_GetTagLength(SIV_Instance instance);
extern int SIV_Encrypt(SIV_Instance instance,
diff --git a/siv_gnutls.c b/siv_gnutls.c
index aba2bab..95387f0 100644
--- a/siv_gnutls.c
+++ b/siv_gnutls.c
@@ -196,6 +196,22 @@ SIV_SetKey(SIV_Instance instance, const unsigned char *key, int length)
/* ================================================== */
int
+SIV_GetMinNonceLength(SIV_Instance instance)
+{
+ return 1;
+}
+
+/* ================================================== */
+
+int
+SIV_GetMaxNonceLength(SIV_Instance instance)
+{
+ return INT_MAX;
+}
+
+/* ================================================== */
+
+int
SIV_GetTagLength(SIV_Instance instance)
{
int len;
diff --git a/siv_nettle.c b/siv_nettle.c
index d8f8b23..800beb7 100644
--- a/siv_nettle.c
+++ b/siv_nettle.c
@@ -34,12 +34,25 @@
#include "siv_nettle_int.c"
#endif
+#ifdef HAVE_NETTLE_SIV_GCM
+#include <nettle/siv-gcm.h>
+#endif
+
#include "memory.h"
#include "siv.h"
struct SIV_Instance_Record {
- struct siv_cmac_aes128_ctx siv;
+ SIV_Algorithm algorithm;
int key_set;
+ int min_nonce_length;
+ int max_nonce_length;
+ int tag_length;
+ union {
+ struct siv_cmac_aes128_ctx cmac_aes128;
+#ifdef HAVE_NETTLE_SIV_GCM
+ struct aes128_ctx aes128;
+#endif
+ } ctx;
};
/* ================================================== */
@@ -49,12 +62,30 @@ SIV_CreateInstance(SIV_Algorithm algorithm)
{
SIV_Instance instance;
- if (algorithm != AEAD_AES_SIV_CMAC_256)
+ if (SIV_GetKeyLength(algorithm) <= 0)
return NULL;
instance = MallocNew(struct SIV_Instance_Record);
+ instance->algorithm = algorithm;
instance->key_set = 0;
+ switch (algorithm) {
+ case AEAD_AES_SIV_CMAC_256:
+ instance->min_nonce_length = SIV_MIN_NONCE_SIZE;
+ instance->max_nonce_length = INT_MAX;
+ instance->tag_length = SIV_DIGEST_SIZE;
+ break;
+#ifdef HAVE_NETTLE_SIV_GCM
+ case AEAD_AES_128_GCM_SIV:
+ instance->min_nonce_length = SIV_GCM_NONCE_SIZE;
+ instance->max_nonce_length = SIV_GCM_NONCE_SIZE;
+ instance->tag_length = SIV_GCM_DIGEST_SIZE;
+ break;
+#endif
+ default:
+ assert(0);
+ }
+
return instance;
}
@@ -71,11 +102,18 @@ SIV_DestroyInstance(SIV_Instance instance)
int
SIV_GetKeyLength(SIV_Algorithm algorithm)
{
- assert(32 <= SIV_MAX_KEY_LENGTH);
-
- if (algorithm == AEAD_AES_SIV_CMAC_256)
- return 32;
- return 0;
+ assert(2 * AES128_KEY_SIZE <= SIV_MAX_KEY_LENGTH);
+
+ switch (algorithm) {
+ case AEAD_AES_SIV_CMAC_256:
+ return 2 * AES128_KEY_SIZE;
+#ifdef HAVE_NETTLE_SIV_GCM
+ case AEAD_AES_128_GCM_SIV:
+ return AES128_KEY_SIZE;
+#endif
+ default:
+ return 0;
+ }
}
/* ================================================== */
@@ -83,10 +121,21 @@ SIV_GetKeyLength(SIV_Algorithm algorithm)
int
SIV_SetKey(SIV_Instance instance, const unsigned char *key, int length)
{
- if (length != 32)
+ if (length != SIV_GetKeyLength(instance->algorithm))
return 0;
- siv_cmac_aes128_set_key(&instance->siv, key);
+ switch (instance->algorithm) {
+ case AEAD_AES_SIV_CMAC_256:
+ siv_cmac_aes128_set_key(&instance->ctx.cmac_aes128, key);
+ break;
+#ifdef HAVE_NETTLE_SIV_GCM
+ case AEAD_AES_128_GCM_SIV:
+ aes128_set_encrypt_key(&instance->ctx.aes128, key);
+ break;
+#endif
+ default:
+ assert(0);
+ }
instance->key_set = 1;
@@ -96,11 +145,27 @@ SIV_SetKey(SIV_Instance instance, const unsigned char *key, int length)
/* ================================================== */
int
-SIV_GetTagLength(SIV_Instance instance)
+SIV_GetMinNonceLength(SIV_Instance instance)
{
- assert(SIV_DIGEST_SIZE <= SIV_MAX_TAG_LENGTH);
+ return instance->min_nonce_length;
+}
- return SIV_DIGEST_SIZE;
+/* ================================================== */
+
+int
+SIV_GetMaxNonceLength(SIV_Instance instance)
+{
+ return instance->max_nonce_length;
+}
+
+/* ================================================== */
+
+int
+SIV_GetTagLength(SIV_Instance instance)
+{
+ if (instance->tag_length < 1 || instance->tag_length > SIV_MAX_TAG_LENGTH)
+ assert(0);
+ return instance->tag_length;
}
/* ================================================== */
@@ -115,16 +180,31 @@ SIV_Encrypt(SIV_Instance instance,
if (!instance->key_set)
return 0;
- if (nonce_length < SIV_MIN_NONCE_SIZE || assoc_length < 0 ||
+ if (nonce_length < instance->min_nonce_length ||
+ nonce_length > instance->max_nonce_length || assoc_length < 0 ||
plaintext_length < 0 || plaintext_length > ciphertext_length ||
- plaintext_length + SIV_DIGEST_SIZE != ciphertext_length)
+ plaintext_length + SIV_GetTagLength(instance) != ciphertext_length)
return 0;
assert(assoc && plaintext);
- siv_cmac_aes128_encrypt_message(&instance->siv, nonce_length, nonce,
- assoc_length, assoc,
- ciphertext_length, ciphertext, plaintext);
+ switch (instance->algorithm) {
+ case AEAD_AES_SIV_CMAC_256:
+ siv_cmac_aes128_encrypt_message(&instance->ctx.cmac_aes128,
+ nonce_length, nonce, assoc_length, assoc,
+ ciphertext_length, ciphertext, plaintext);
+ break;
+#ifdef HAVE_NETTLE_SIV_GCM
+ case AEAD_AES_128_GCM_SIV:
+ siv_gcm_aes128_encrypt_message(&instance->ctx.aes128,
+ nonce_length, nonce, assoc_length, assoc,
+ ciphertext_length, ciphertext, plaintext);
+ break;
+#endif
+ default:
+ assert(0);
+ }
+
return 1;
}
@@ -140,17 +220,32 @@ SIV_Decrypt(SIV_Instance instance,
if (!instance->key_set)
return 0;
- if (nonce_length < SIV_MIN_NONCE_SIZE || assoc_length < 0 ||
+ if (nonce_length < instance->min_nonce_length ||
+ nonce_length > instance->max_nonce_length || assoc_length < 0 ||
plaintext_length < 0 || plaintext_length > ciphertext_length ||
- plaintext_length + SIV_DIGEST_SIZE != ciphertext_length)
+ plaintext_length + SIV_GetTagLength(instance) != ciphertext_length)
return 0;
assert(assoc && plaintext);
- if (!siv_cmac_aes128_decrypt_message(&instance->siv, nonce_length, nonce,
- assoc_length, assoc,
- plaintext_length, plaintext, ciphertext))
- return 0;
+ switch (instance->algorithm) {
+ case AEAD_AES_SIV_CMAC_256:
+ if (!siv_cmac_aes128_decrypt_message(&instance->ctx.cmac_aes128,
+ nonce_length, nonce, assoc_length, assoc,
+ plaintext_length, plaintext, ciphertext))
+ return 0;
+ break;
+#ifdef HAVE_NETTLE_SIV_GCM
+ case AEAD_AES_128_GCM_SIV:
+ if (!siv_gcm_aes128_decrypt_message(&instance->ctx.aes128,
+ nonce_length, nonce, assoc_length, assoc,
+ plaintext_length, plaintext, ciphertext))
+ return 0;
+ break;
+#endif
+ default:
+ assert(0);
+ }
return 1;
}
diff --git a/smooth.c b/smooth.c
index 4c350e9..ee7094e 100644
--- a/smooth.c
+++ b/smooth.c
@@ -302,7 +302,7 @@ SMT_Activate(struct timespec *now)
if (!enabled || !locked)
return;
- LOG(LOGS_INFO, "Time smoothing activated%s", leap_only_mode ?
+ LOG(LOGS_INFO, "Activated %s%s", "time smoothing", leap_only_mode ?
" (leap seconds only)" : "");
locked = 0;
last_update = *now;
@@ -322,6 +322,8 @@ SMT_Reset(struct timespec *now)
for (i = 0; i < NUM_STAGES; i++)
stages[i].wander = stages[i].length = 0.0;
+
+ LOG(LOGS_INFO, "Reset %s", "time smoothing");
}
void
diff --git a/sources.c b/sources.c
index 2aee6f2..730ac55 100644
--- a/sources.c
+++ b/sources.c
@@ -140,6 +140,10 @@ struct SRC_Instance_Record {
/* Flag indicating the source has a leap second vote */
int leap_vote;
+
+ /* Flag indicating the source was already reported as
+ a falseticker since the last selection change */
+ int reported_falseticker;
};
/* ================================================== */
@@ -165,6 +169,8 @@ static int max_n_sources; /* Capacity of the table */
static int selected_source_index; /* Which source index is currently
selected (set to INVALID_SOURCE
if no current valid reference) */
+static int reported_no_majority; /* Flag to avoid repeated log message
+ about no majority */
/* Score needed to replace the currently selected source */
#define SCORE_LIMIT 10.0
@@ -296,6 +302,9 @@ void SRC_DestroyInstance(SRC_Instance instance)
int dead_index, i;
assert(initialised);
+ if (instance->index < 0 || instance->index >= n_sources ||
+ instance != sources[instance->index])
+ assert(0);
SST_DeleteInstance(instance->stats);
dead_index = instance->index;
@@ -329,6 +338,7 @@ SRC_ResetInstance(SRC_Instance instance)
instance->stratum = 0;
instance->leap = LEAP_Unsynchronised;
instance->leap_vote = 0;
+ instance->reported_falseticker = 0;
memset(&instance->sel_info, 0, sizeof (instance->sel_info));
@@ -580,17 +590,17 @@ update_sel_options(void)
/* ================================================== */
static void
-log_selection_message(const char *format, const char *arg)
+log_selection_message(LOG_Severity severity, const char *format, const char *arg)
{
if (REF_GetMode() != REF_ModeNormal)
return;
- LOG(LOGS_INFO, format, arg);
+ LOG(severity, format, arg);
}
/* ================================================== */
static void
-log_selection_source(const char *format, SRC_Instance inst)
+log_selection_source(LOG_Severity severity, const char *format, SRC_Instance inst)
{
char buf[320], *name, *ntp_name;
@@ -602,7 +612,7 @@ log_selection_source(const char *format, SRC_Instance inst)
else
snprintf(buf, sizeof (buf), "%s", name);
- log_selection_message(format, buf);
+ log_selection_message(severity, format, buf);
}
/* ================================================== */
@@ -807,7 +817,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
if (n_sources == 0) {
/* In this case, we clearly cannot synchronise to anything */
if (selected_source_index != INVALID_SOURCE) {
- log_selection_message("Can't synchronise: no sources", NULL);
+ log_selection_message(LOGS_INFO, "Can't synchronise: no sources", NULL);
selected_source_index = INVALID_SOURCE;
}
return;
@@ -1000,7 +1010,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
if (n_endpoints == 0) {
/* No sources provided valid endpoints */
if (selected_source_index != INVALID_SOURCE) {
- log_selection_message("Can't synchronise: no selectable sources", NULL);
+ log_selection_message(LOGS_INFO, "Can't synchronise: no selectable sources", NULL);
selected_source_index = INVALID_SOURCE;
}
return;
@@ -1080,8 +1090,12 @@ SRC_SelectSource(SRC_Instance updated_inst)
(best_trust_depth > 0 && best_trust_depth <= n_sel_trust_sources / 2)) {
/* Could not even get half the reachable (trusted) sources to agree */
+ if (!reported_no_majority) {
+ log_selection_message(LOGS_WARN, "Can't synchronise: no majority", NULL);
+ reported_no_majority = 1;
+ }
+
if (selected_source_index != INVALID_SOURCE) {
- log_selection_message("Can't synchronise: no majority", NULL);
REF_SetUnsynchronised();
selected_source_index = INVALID_SOURCE;
}
@@ -1127,12 +1141,16 @@ SRC_SelectSource(SRC_Instance updated_inst)
sel_req_source = 0;
} else {
mark_source(sources[i], SRC_FALSETICKER);
+ if (!sources[i]->reported_falseticker) {
+ log_selection_source(LOGS_WARN, "Detected falseticker %s", sources[i]);
+ sources[i]->reported_falseticker = 1;
+ }
}
}
if (!n_sel_sources || sel_req_source || n_sel_sources < CNF_GetMinSources()) {
if (selected_source_index != INVALID_SOURCE) {
- log_selection_message("Can't synchronise: %s selectable sources",
+ log_selection_message(LOGS_INFO, "Can't synchronise: %s selectable sources",
!n_sel_sources ? "no" :
sel_req_source ? "no required source in" : "not enough");
selected_source_index = INVALID_SOURCE;
@@ -1249,13 +1267,16 @@ SRC_SelectSource(SRC_Instance updated_inst)
}
selected_source_index = max_score_index;
- log_selection_source("Selected source %s", sources[selected_source_index]);
+ log_selection_source(LOGS_INFO, "Selected source %s", sources[selected_source_index]);
/* New source has been selected, reset all scores */
for (i = 0; i < n_sources; i++) {
sources[i]->sel_score = 1.0;
sources[i]->distant = 0;
+ sources[i]->reported_falseticker = 0;
}
+
+ reported_no_majority = 0;
}
mark_source(sources[selected_source_index], SRC_SELECTED);
@@ -1546,6 +1567,8 @@ SRC_ResetSources(void)
for (i = 0; i < n_sources; i++)
SRC_ResetInstance(sources[i]);
+
+ LOG(LOGS_INFO, "Reset all sources");
}
/* ================================================== */
@@ -1593,6 +1616,46 @@ SRC_ActiveSources(void)
/* ================================================== */
+static SRC_Instance
+find_source(IPAddr *ip, uint32_t ref_id)
+{
+ int i;
+
+ for (i = 0; i < n_sources; i++) {
+ if ((ip->family != IPADDR_UNSPEC && sources[i]->type == SRC_NTP &&
+ UTI_CompareIPs(ip, sources[i]->ip_addr, NULL) == 0) ||
+ (ip->family == IPADDR_UNSPEC && sources[i]->type == SRC_REFCLOCK &&
+ ref_id == sources[i]->ref_id))
+ return sources[i];
+ }
+
+ return NULL;
+}
+
+/* ================================================== */
+
+int
+SRC_ModifySelectOptions(IPAddr *ip, uint32_t ref_id, int options, int mask)
+{
+ SRC_Instance inst;
+
+ inst = find_source(ip, ref_id);
+ if (!inst)
+ return 0;
+
+ if ((inst->conf_sel_options & mask) == options)
+ return 1;
+
+ inst->conf_sel_options = (inst->conf_sel_options & ~mask) | options;
+ LOG(LOGS_INFO, "Source %s selection options modified", source_to_string(inst));
+
+ update_sel_options();
+
+ return 1;
+}
+
+/* ================================================== */
+
int
SRC_ReportSource(int index, RPT_SourceReport *report, struct timespec *now)
{
diff --git a/sources.h b/sources.h
index 6c799bb..da3a533 100644
--- a/sources.h
+++ b/sources.h
@@ -131,6 +131,10 @@ extern int SRC_IsReachable(SRC_Instance inst);
extern int SRC_ReadNumberOfSources(void);
extern int SRC_ActiveSources(void);
+/* Modify selection options of an NTP source specified by address, or
+ refclock specified by its reference ID */
+extern int SRC_ModifySelectOptions(IPAddr *ip, uint32_t ref_id, int options, int mask);
+
extern int SRC_ReportSource(int index, RPT_SourceReport *report, struct timespec *now);
extern int SRC_ReportSourcestats(int index, RPT_SourcestatsReport *report, struct timespec *now);
extern int SRC_GetSelectReport(int index, RPT_SelectReport *report);
diff --git a/sourcestats.c b/sourcestats.c
index eb4705e..ce326e9 100644
--- a/sourcestats.c
+++ b/sourcestats.c
@@ -80,7 +80,7 @@ static LOG_FileID logfileid;
struct SST_Stats_Record {
- /* Reference ID and IP address of source, used for logging to statistics log */
+ /* Reference ID and IP address (NULL if not an NTP source) */
uint32_t refid;
IPAddr *ip_addr;
@@ -964,9 +964,10 @@ SST_DoSourceReport(SST_Stats inst, RPT_SourceReport *report, struct timespec *no
report->latest_meas = inst->offsets[i];
report->latest_meas_err = 0.5*inst->root_delays[j] + inst->root_dispersions[j];
- /* Align the sample time to reduce the leak of the receive timestamp */
+ /* Align the sample time to reduce the leak of the NTP receive timestamp */
last_sample_time = inst->sample_times[i];
- last_sample_time.tv_nsec = 0;
+ if (inst->ip_addr)
+ last_sample_time.tv_nsec = 0;
report->latest_meas_ago = UTI_DiffTimespecsToDouble(now, &last_sample_time);
} else {
report->latest_meas_ago = (uint32_t)-1;
diff --git a/sys_linux.c b/sys_linux.c
index f2baab1..2a5eb8f 100644
--- a/sys_linux.c
+++ b/sys_linux.c
@@ -35,6 +35,7 @@
#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
#include <linux/ptp_clock.h>
+#include <poll.h>
#endif
#ifdef FEAT_SCFILTER
@@ -633,6 +634,9 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context)
{ SOL_IP, IP_PKTINFO }, { SOL_IP, IP_FREEBIND }, { SOL_IP, IP_TOS },
#ifdef FEAT_IPV6
{ SOL_IPV6, IPV6_V6ONLY }, { SOL_IPV6, IPV6_RECVPKTINFO },
+#ifdef IPV6_TCLASS
+ { SOL_IPV6, IPV6_TCLASS },
+#endif
#endif
#ifdef SO_BINDTODEVICE
{ SOL_SOCKET, SO_BINDTODEVICE },
@@ -991,6 +995,16 @@ int
SYS_Linux_ReadPHCExtTimestamp(int fd, struct timespec *phc_ts, int *channel)
{
struct ptp_extts_event extts_event;
+ struct pollfd pfd;
+
+ /* Make sure the read will not block in case we have multiple
+ descriptors of the same PHC (O_NONBLOCK does not work) */
+ pfd.fd = fd;
+ pfd.events = POLLIN;
+ if (poll(&pfd, 1, 0) != 1 || pfd.revents != POLLIN) {
+ DEBUG_LOG("Missing PHC extts event");
+ return 0;
+ }
if (read(fd, &extts_event, sizeof (extts_event)) != sizeof (extts_event)) {
DEBUG_LOG("Could not read PHC extts event");
diff --git a/test/compilation/003-sanitizers b/test/compilation/003-sanitizers
index 8040efe..fad7831 100755
--- a/test/compilation/003-sanitizers
+++ b/test/compilation/003-sanitizers
@@ -44,7 +44,7 @@ do
export CC
for san_options in "" "-fsanitize=address" "-fsanitize=memory"; do
- export CFLAGS="-O2 -g -fsanitize=undefined -fsanitize=float-divide-by-zero -fno-sanitize-recover=undefined,float-divide-by-zero $san_options $arch_opts"
+ export CFLAGS="-O2 -g -fsanitize=undefined -fsanitize=float-divide-by-zero -fsanitize=float-cast-overflow -fno-sanitize-recover=all $san_options $arch_opts"
# clang msan doesn't work on i686 and otherwise requires patches
echo $CFLAGS | grep -q 'sanitize=memory' && continue
diff --git a/test/simulation/106-refclock b/test/simulation/106-refclock
index f09f170..ba7c482 100755
--- a/test/simulation/106-refclock
+++ b/test/simulation/106-refclock
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
. ./test.common
-test_start "SHM refclock"
+test_start "reference clocks"
check_config_h 'FEAT_REFCLOCK 1' || test_skip
check_config_h 'FEAT_PHC 1' || test_skip
diff --git a/test/simulation/110-chronyc b/test/simulation/110-chronyc
index b78f0d8..33d204b 100755
--- a/test/simulation/110-chronyc
+++ b/test/simulation/110-chronyc
@@ -91,6 +91,18 @@ check_chronyd_exit || test_fail
check_chronyc_output "^C0A87B01,192\.168\.123\.1,2,12623049..\..........,-?0\.0000.....,-?0\.000......,0\.000......,(99|100)\....,-?[0-9]\....,[0-9]\....,0\.000......,0\.000......,[0-9]+\..,Normal$" \
|| test_fail
+chronyc_options="-c -e"
+chronyc_conf="sources"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+
+check_chronyc_output "^#,.,SHM0.*
+\^,.,192\.168\.123\.1.*
+\^,.,192\.168\.123\.2.*
+\.$" \
+ || test_fail
+
chronyc_options=""
server_strata=0
chronyc_start=0.5
@@ -164,6 +176,9 @@ for chronyc_conf in \
"reselectdist 1e-3" \
"reset sources" \
"selectdata" \
+ "selectopts 1.2.3.4 -noselect +trust +require +prefer" \
+ "selectopts ID#0000000001 +prefer" \
+ "selectopts PPS0 +prefer" \
"settime 16:30" \
"settime 16:30:05" \
"settime Nov 21, 2015 16:30:05" \
@@ -245,7 +260,13 @@ NTS-KE connections dropped : 0
Authenticated NTP packets : 0
Interleaved NTP packets : 0
NTP timestamps held : 0
-NTP timestamp span : 0$" || test_fail
+NTP timestamp span : 0
+NTP daemon RX timestamps : 0
+NTP daemon TX timestamps : 1
+NTP kernel RX timestamps : 1
+NTP kernel TX timestamps : 0
+NTP hardware RX timestamps : 0
+NTP hardware TX timestamps : 0$" || test_fail
chronyc_conf="
deny all
@@ -327,6 +348,8 @@ maxupdateskew 192.168.123.1 10.0
minpoll 192.168.123.1 3
minstratum 192.168.123.1 1
polltarget 192.168.123.1 10
+selectopts 192.168.123.1 +trust +prefer -require
+selectdata
delete 192.168.123.1"
run_test || test_fail
@@ -345,6 +368,10 @@ check_chronyc_output "^200 OK
200 OK
200 OK
200 OK
+200 OK
+S Name/IP Address Auth COpts EOpts Last Score Interval Leap
+=======================================================================
+M node1\.net1\.clk N \-PT\-\- \-PT\-\- 0 1\.0 \+0ns \+0ns \?
200 OK$" || test_fail
chronyc_conf="
diff --git a/test/simulation/133-hwtimestamp b/test/simulation/133-hwtimestamp
index d3cce6d..f02a010 100755
--- a/test/simulation/133-hwtimestamp
+++ b/test/simulation/133-hwtimestamp
@@ -47,9 +47,10 @@ for client_conf in \
check_log_messages "Received error.*message.*tss=KH" 195 200 || test_fail
check_log_messages "Updated RX timestamp.*tss=1" 1 1 || test_fail
check_log_messages "Updated RX timestamp.*tss=2" 195 200 || test_fail
+ check_log_messages "Polling PHC" 195 220 || test_fail
if echo "$client_conf" | grep -q nocrossts; then
check_log_messages "update_tx_timestamp.*Updated" 180 200 || test_fail
- check_log_messages "update_tx_timestamp.*Unacceptable" 0 10 || test_fail
+ check_log_messages "update_tx_timestamp.*Unacceptable" 0 13 || test_fail
else
check_log_messages "update_tx_timestamp.*Updated" 50 140 || test_fail
check_log_messages "update_tx_timestamp.*Unacceptable" 50 140 || test_fail
@@ -57,4 +58,32 @@ for client_conf in \
fi
done
+server_conf+="
+server 192.168.123.2 minpoll 1 maxpoll 1 noselect"
+
+for maxpoll in -1 0 1; do
+ client_conf="hwtimestamp eth0 minpoll -1 maxpoll $maxpoll nocrossts"
+ run_test || test_fail
+ check_chronyd_exit || test_fail
+ check_source_selection || test_fail
+ check_sync || test_fail
+
+ if check_config_h 'FEAT_DEBUG 1'; then
+ case $maxpoll in
+ -1)
+ check_log_messages "Polling PHC on eth0$" 360 380 || test_fail
+ check_log_messages "Polling PHC.*before" 3 25 || test_fail
+ ;;
+ 0)
+ check_log_messages "Polling PHC on eth0$" 8 45 || test_fail
+ check_log_messages "Polling PHC.*before" 150 190 || test_fail
+ ;;
+ 1)
+ check_log_messages "Polling PHC on eth0$" 1 1 || test_fail
+ check_log_messages "Polling PHC.*before" 194 199 || test_fail
+ ;;
+ esac
+ fi
+done
+
test_pass
diff --git a/test/simulation/145-rtc b/test/simulation/145-rtc
new file mode 100755
index 0000000..22b62d9
--- /dev/null
+++ b/test/simulation/145-rtc
@@ -0,0 +1,75 @@
+#!/usr/bin/env bash
+
+. ./test.common
+test_start "RTC tracking"
+
+check_config_h 'FEAT_CMDMON 1' || test_skip
+check_config_h 'FEAT_RTC 1' || test_skip
+
+export CLKNETSIM_START_DATE=$(date -d 'Jan 1 00:00:00 UTC 2010' +'%s')
+export CLKNETSIM_RTC_OFFSET=-10.0
+
+time_offset=$(awk "BEGIN {print -($freq_offset * $limit)}")
+wander=0.0
+chronyc_start=9900
+chronyc_conf="rtcdata"
+client_chronyd_options="-x"
+
+client_conf="
+hwclockfile /dev/null
+driftfile tmp/drift
+rtcfile tmp/rtc
+rtconutc"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_chronyc_output "^RTC ref time \(UTC\) : Fri Jan 01 02:4[34]:.. 2010
+Number of samples : [0-9]+
+Number of runs : [0-9]+
+Sample span period : [ 0-9]+
+RTC is fast by : -9\.01.... seconds
+RTC gains time at : 99\.9[98]. ppm$" \
+|| test_fail
+
+export CLKNETSIM_START_DATE=$(date -d 'Jan 5 00:00:00 UTC 2010' +'%s')
+export CLKNETSIM_RTC_OFFSET=$(awk "BEGIN {print -(10.0 - 4 * 86400 * $freq_offset)}")
+touch -d 'Jan 1 00:00:00 UTC 2010' tmp/drift
+
+time_offset=10
+min_sync_time=2
+max_sync_time=12
+time_max_limit=1e-2
+freq_max_limit=1e-1
+time_rms_limit=1e-3
+freq_rms_limit=1e-3
+client_chronyd_options="-s"
+client_conf+="
+rtcautotrim 1"
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_source_selection || test_fail
+check_sync || test_fail
+check_chronyc_output "^RTC ref time \(UTC\) : Tue Jan 05 02:4[34]:.. 2010
+Number of samples : [0-9]+
+Number of runs : [0-9]+
+Sample span period : [ 0-9]+
+RTC is fast by : 0\.1..... seconds
+RTC gains time at : [- ]0\.0.. ppm$" \
+|| test_fail
+
+export CLKNETSIM_START_DATE=$(date -d 'Jan 10 00:00:00 UTC 2010' +'%s')
+export CLKNETSIM_RTC_OFFSET=-10.0
+touch -d 'Jan 10 00:00:00 UTC 2010' tmp/drift
+
+time_offset=-10000
+min_sync_time=1
+max_sync_time=1
+
+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/146-offline b/test/simulation/146-offline
new file mode 100755
index 0000000..f110a12
--- /dev/null
+++ b/test/simulation/146-offline
@@ -0,0 +1,73 @@
+#!/usr/bin/env bash
+
+. ./test.common
+
+test_start "online/offline switching"
+
+check_config_h 'FEAT_CMDMON 1' || test_skip
+
+servers=2
+limit=$[10 * 1800]
+client_server_conf="
+server 192.168.123.1 offline iburst
+server 192.168.123.2 polltarget 64"
+chronyc_conf="timeout 4000000
+activity
+offline
+activity
+onoffline 192.168.123.1
+online 192.168.123.2
+activity
+offline
+activity
+"
+chronyc_start=1
+base_delay="(+ 1e-4 (* 1800 (equal 0.1 from 4)))"
+jitter=1e-6
+
+time_max_limit=2e-2
+freq_max_limit=1e-3
+time_rms_limit=2e-2
+freq_rms_limit=1e-5
+min_sync_time=120
+max_sync_time=140
+
+run_test || test_fail
+check_chronyd_exit || test_fail
+check_packet_interval || test_fail
+check_sync || test_fail
+
+check_file_messages " 3 1 .* 123 " 30 90 log.packets || test_fail
+check_file_messages " 3 2 .* 123 " 130 150 log.packets || test_fail
+
+check_chronyc_output "^200 OK
+1 sources online
+1 sources offline
+0 sources doing burst \(return to online\)
+0 sources doing burst \(return to offline\)
+0 sources with unknown address
+200 OK
+200 OK
+0 sources online
+2 sources offline
+0 sources doing burst \(return to online\)
+0 sources doing burst \(return to offline\)
+0 sources with unknown address
+200 OK
+200 OK
+200 OK
+2 sources online
+0 sources offline
+0 sources doing burst \(return to online\)
+0 sources doing burst \(return to offline\)
+0 sources with unknown address
+200 OK
+200 OK
+0 sources online
+2 sources offline
+0 sources doing burst \(return to online\)
+0 sources doing burst \(return to offline\)
+0 sources with unknown address" \
+ || test_fail
+
+test_pass
diff --git a/test/simulation/test.common b/test/simulation/test.common
index 70bbde1..3f6e80b 100644
--- a/test/simulation/test.common
+++ b/test/simulation/test.common
@@ -78,6 +78,7 @@ default_client_min_mean_out_interval=0.0
default_client_max_min_out_interval=inf
default_cmdmon_unix=1
+default_pcap_dumps=0
default_dns=0
# Initialize test settings from their defaults
@@ -469,6 +470,9 @@ run_test() {
for i in $(seq 1 $n); do
test_message 2 0 "starting node $node:"
+
+ [ $pcap_dumps -ne 0 ] && export CLKNETSIM_PCAP_DUMP=tmp/pcap.$node
+
if [ $stratum -eq 1 ]; then
step=$server_step
start=$server_start
@@ -509,6 +513,8 @@ run_test() {
for i in $(seq 1 $[$nodes - $node + 1]); do
test_message 2 0 "starting node $node:"
+ [ $pcap_dumps -ne 0 ] && export CLKNETSIM_PCAP_DUMP=tmp/pcap.$node
+
options=$([ $dns -eq 0 ] && printf "%s" "-n")
if [ $cmdmon_unix -ne 0 ]; then
options+=" -h /clknetsim/unix/$[$node - $clients]:1"
diff --git a/test/system/007-cmdmon b/test/system/007-cmdmon
index 04d14c2..f9541d3 100755
--- a/test/system/007-cmdmon
+++ b/test/system/007-cmdmon
@@ -42,6 +42,7 @@ for command in \
"reselect" \
"reselectdist 1e-3" \
"reset sources" \
+ "selectopts $server -noselect +trust +prefer +require" \
"smoothtime reset" \
"smoothtime activate" \
; do
@@ -101,7 +102,7 @@ Total good RX : [0-9]+$" || test_fail
run_chronyc "selectdata" || test_fail
check_chronyc_output "^S Name/IP Address Auth COpts EOpts Last Score Interval Leap
=======================================================================
-s 127\.0\.0\.1 N ----- ----- 0 1\.0 \+0ns \+0ns \?$" || test_fail
+s 127\.0\.0\.1 N -PTR- -PTR- 0 1\.0 \+0ns \+0ns \?$" || test_fail
run_chronyc "serverstats" || test_fail
check_chronyc_output "^NTP packets received : [0-9]+
@@ -114,7 +115,13 @@ NTS-KE connections dropped : 0
Authenticated NTP packets : 0
Interleaved NTP packets : 0
NTP timestamps held : 0
-NTP timestamp span : 0$"|| test_fail
+NTP timestamp span : 0
+NTP daemon RX timestamps : 0
+NTP daemon TX timestamps : [0-9]+
+NTP kernel RX timestamps : [0-9]+
+NTP kernel TX timestamps : 0
+NTP hardware RX timestamps : 0
+NTP hardware TX timestamps : 0$"|| test_fail
run_chronyc "manual on" || test_fail
check_chronyc_output "^200 OK$" || test_fail
diff --git a/test/unit/array.c b/test/unit/array.c
new file mode 100644
index 0000000..65ad1d8
--- /dev/null
+++ b/test/unit/array.c
@@ -0,0 +1,97 @@
+/*
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar 2023
+ *
+ * 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 <array.c>
+#include <util.h>
+#include "test.h"
+
+void
+test_unit(void)
+{
+ unsigned int i, j, k, k2, l, es, s;
+ unsigned char *el1, el2[20];
+ ARR_Instance a;
+
+ for (i = 0; i < 1000; i++) {
+ es = random() % sizeof (el2) + 1;
+
+ a = ARR_CreateInstance(es);
+
+ TEST_CHECK(ARR_GetSize(a) == 0);
+
+ for (j = 0; j < 100; j++) {
+ s = ARR_GetSize(a);
+
+ switch (random() % 6) {
+ case 0:
+ el1 = ARR_GetNewElement(a);
+ TEST_CHECK(ARR_GetSize(a) == s + 1);
+ memset(el1, s % 256, es);
+ TEST_CHECK(ARR_GetElement(a, s) == el1);
+ break;
+ case 1:
+ for (k = 0; k < s; k++) {
+ el1 = ARR_GetElement(a, k);
+ for (l = 0; l < es; l++)
+ TEST_CHECK(el1[l] == k % 256);
+ }
+ break;
+ case 2:
+ if (s == 0)
+ break;
+ TEST_CHECK(ARR_GetElements(a) == ARR_GetElement(a, 0));
+ break;
+ case 3:
+ memset(el2, s % 256, es);
+ ARR_AppendElement(a, el2);
+ TEST_CHECK(ARR_GetSize(a) == s + 1);
+ break;
+ case 4:
+ if (s == 0)
+ break;
+ k2 = random() % s;
+ ARR_RemoveElement(a, k2);
+ TEST_CHECK(ARR_GetSize(a) == s - 1);
+ for (k = k2; k < s - 1; k++) {
+ el1 = ARR_GetElement(a, k);
+ for (l = 0; l < es; l++) {
+ TEST_CHECK(el1[l] == (k + 1) % 256);
+ el1[l] = k % 256;
+ }
+ }
+ break;
+ case 5:
+ k2 = random() % 1000;
+ ARR_SetSize(a, k2);
+ TEST_CHECK(ARR_GetSize(a) == k2);
+ for (k = s; k < k2; k++) {
+ el1 = ARR_GetElement(a, k);
+ for (l = 0; l < es; l++)
+ el1[l] = k % 256;
+ }
+ break;
+ default:
+ assert(0);
+ }
+ }
+
+ ARR_DestroyInstance(a);
+ }
+}
diff --git a/test/unit/clientlog.c b/test/unit/clientlog.c
index f59e130..e5bf1f4 100644
--- a/test/unit/clientlog.c
+++ b/test/unit/clientlog.c
@@ -36,6 +36,7 @@ test_unit(void)
{
uint64_t ts64, prev_first_ts64, prev_last_ts64, max_step;
uint32_t index2, prev_first, prev_size;
+ NTP_Timestamp_Source ts_src, ts_src2;
struct timespec ts, ts2;
int i, j, k, index, shift;
CLG_Service s;
@@ -95,7 +96,7 @@ test_unit(void)
TEST_CHECK(!ntp_ts_map.timestamps);
UTI_ZeroNtp64(&ntp_ts);
- CLG_SaveNtpTimestamps(&ntp_ts, NULL);
+ CLG_SaveNtpTimestamps(&ntp_ts, NULL, 0);
TEST_CHECK(ntp_ts_map.timestamps);
TEST_CHECK(ntp_ts_map.first == 0);
TEST_CHECK(ntp_ts_map.size == 0);
@@ -132,8 +133,10 @@ test_unit(void)
UTI_ZeroTimespec(&ts);
}
+ ts_src = random() % (MAX_NTP_TS + 1);
CLG_SaveNtpTimestamps(&ntp_ts,
- UTI_IsZeroTimespec(&ts) ? (random() % 2 ? &ts : NULL) : &ts);
+ UTI_IsZeroTimespec(&ts) ? (random() % 2 ? &ts : NULL) : &ts,
+ ts_src);
if (j < ntp_ts_map.max_size) {
TEST_CHECK(ntp_ts_map.size == j + 1);
@@ -144,14 +147,15 @@ test_unit(void)
}
TEST_CHECK(ntp_ts_map.cached_index == ntp_ts_map.size - 1);
TEST_CHECK(get_ntp_tss(ntp_ts_map.size - 1)->slew_epoch == ntp_ts_map.slew_epoch);
- TEST_CHECK(CLG_GetNtpTxTimestamp(&ntp_ts, &ts2));
+ TEST_CHECK(CLG_GetNtpTxTimestamp(&ntp_ts, &ts2, &ts_src2));
TEST_CHECK(UTI_CompareTimespecs(&ts, &ts2) == 0);
+ TEST_CHECK(UTI_IsZeroTimespec(&ts) || ts_src == ts_src2);
for (k = random() % 4; k > 0; k--) {
index2 = random() % ntp_ts_map.size;
int64_to_ntp64(get_ntp_tss(index2)->rx_ts, &ntp_ts);
if (random() % 2)
- TEST_CHECK(CLG_GetNtpTxTimestamp(&ntp_ts, &ts));
+ TEST_CHECK(CLG_GetNtpTxTimestamp(&ntp_ts, &ts, &ts_src2));
UTI_Ntp64ToTimespec(&ntp_ts, &ts);
UTI_AddDoubleToTimespec(&ts, TST_GetRandomDouble(-1.999, 1.999), &ts);
@@ -165,10 +169,12 @@ test_unit(void)
1.0e-9);
}
- CLG_UpdateNtpTxTimestamp(&ntp_ts, &ts);
+ ts_src = random() % (MAX_NTP_TS + 1);
+ CLG_UpdateNtpTxTimestamp(&ntp_ts, &ts, ts_src);
- TEST_CHECK(CLG_GetNtpTxTimestamp(&ntp_ts, &ts2));
+ TEST_CHECK(CLG_GetNtpTxTimestamp(&ntp_ts, &ts2, &ts_src2));
TEST_CHECK(UTI_CompareTimespecs(&ts, &ts2) == 0);
+ TEST_CHECK(ts_src == ts_src2);
if (random() % 2) {
uint16_t prev_epoch = ntp_ts_map.slew_epoch;
@@ -181,20 +187,20 @@ test_unit(void)
index = random() % (ntp_ts_map.size - 1);
if (get_ntp_tss(index)->rx_ts + 1 != get_ntp_tss(index + 1)->rx_ts) {
int64_to_ntp64(get_ntp_tss(index)->rx_ts + 1, &ntp_ts);
- TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts));
+ TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts, &ts_src2));
int64_to_ntp64(get_ntp_tss(index + 1)->rx_ts - 1, &ntp_ts);
- TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts));
- CLG_UpdateNtpTxTimestamp(&ntp_ts, &ts);
+ TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts, &ts_src2));
+ CLG_UpdateNtpTxTimestamp(&ntp_ts, &ts, ts_src);
CLG_UndoNtpTxTimestampSlew(&ntp_ts, &ts);
}
}
if (random() % 2) {
int64_to_ntp64(get_ntp_tss(0)->rx_ts - 1, &ntp_ts);
- TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts));
+ TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts, &ts_src2));
int64_to_ntp64(get_ntp_tss(ntp_ts_map.size - 1)->rx_ts + 1, &ntp_ts);
- TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts));
- CLG_UpdateNtpTxTimestamp(&ntp_ts, &ts);
+ TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts, &ts_src2));
+ CLG_UpdateNtpTxTimestamp(&ntp_ts, &ts, ts_src);
CLG_UndoNtpTxTimestampSlew(&ntp_ts, &ts);
}
}
@@ -207,7 +213,7 @@ test_unit(void)
while (ntp_ts_map.size < ntp_ts_map.max_size) {
ts64 += get_random64() >> (shift + 8);
int64_to_ntp64(ts64, &ntp_ts);
- CLG_SaveNtpTimestamps(&ntp_ts, NULL);
+ CLG_SaveNtpTimestamps(&ntp_ts, NULL, 0);
if (ntp_ts_map.cached_index + NTPTS_INSERT_LIMIT < ntp_ts_map.size)
ts64 = get_ntp_tss(ntp_ts_map.size - 1)->rx_ts;
}
@@ -228,7 +234,7 @@ test_unit(void)
prev_size = ntp_ts_map.size;
prev_first_ts64 = get_ntp_tss(0)->rx_ts;
prev_last_ts64 = get_ntp_tss(prev_size - 1)->rx_ts;
- CLG_SaveNtpTimestamps(&ntp_ts, NULL);
+ CLG_SaveNtpTimestamps(&ntp_ts, NULL, 0);
TEST_CHECK(find_ntp_rx_ts(ts64, &index2));
@@ -259,13 +265,13 @@ test_unit(void)
if (random() % 10 == 0) {
CLG_DisableNtpTimestamps(&ntp_ts);
- TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts));
+ TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts, &ts_src2));
}
for (k = random() % 10; k > 0; k--) {
ts64 = get_random64() >> shift;
int64_to_ntp64(ts64, &ntp_ts);
- CLG_GetNtpTxTimestamp(&ntp_ts, &ts);
+ CLG_GetNtpTxTimestamp(&ntp_ts, &ts, &ts_src2);
}
}
@@ -274,8 +280,8 @@ test_unit(void)
LCL_ChangeUnknownStep, NULL);
TEST_CHECK(ntp_ts_map.size == 0);
TEST_CHECK(ntp_ts_map.cached_rx_ts == 0ULL);
- TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts));
- CLG_UpdateNtpTxTimestamp(&ntp_ts, &ts);
+ TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts, &ts_src2));
+ CLG_UpdateNtpTxTimestamp(&ntp_ts, &ts, ts_src);
}
}
diff --git a/test/unit/ntp_core.c b/test/unit/ntp_core.c
index 875731e..83c832c 100644
--- a/test/unit/ntp_core.c
+++ b/test/unit/ntp_core.c
@@ -80,7 +80,7 @@ get_random_key_id(void)
}
static void
-send_request(NCR_Instance inst)
+send_request(NCR_Instance inst, int late_hwts)
{
NTP_Local_Address local_addr;
NTP_Local_Timestamp local_ts;
@@ -104,6 +104,16 @@ send_request(NCR_Instance inst)
NCR_ProcessTxKnown(inst, &local_addr, &local_ts, &req_buffer, req_length);
}
+
+ if (late_hwts) {
+ inst->had_hw_tx_timestamp = 1;
+ inst->report.total_good_count++;
+ } else {
+ if (random() % 2)
+ inst->had_hw_tx_timestamp = 0;
+ else
+ inst->report.total_good_count = 0;
+ }
}
static void
@@ -267,7 +277,8 @@ send_response(int interleaved, int authenticated, int allow_update, int valid_ts
}
static void
-proc_response(NCR_Instance inst, int good, int valid, int updated_sync, int updated_init)
+proc_response(NCR_Instance inst, int good, int valid, int updated_sync,
+ int updated_init, int save)
{
NTP_Local_Address local_addr;
NTP_Local_Timestamp local_ts;
@@ -292,6 +303,19 @@ proc_response(NCR_Instance inst, int good, int valid, int updated_sync, int upda
ret = NCR_ProcessRxKnown(inst, &local_addr, &local_ts, res, res_length);
+ if (save) {
+ TEST_CHECK(ret);
+ TEST_CHECK(inst->saved_response);
+ TEST_CHECK(inst->saved_response->timeout_id != 0);
+ TEST_CHECK(has_saved_response(inst));
+ if (random() % 2)
+ saved_response_timeout(inst);
+ else
+ transmit_timeout(inst);
+ TEST_CHECK(inst->saved_response->timeout_id == 0);
+ TEST_CHECK(!has_saved_response(inst));
+ }
+
if (good > 0)
TEST_CHECK(ret);
else if (!good)
@@ -327,7 +351,7 @@ process_replay(NCR_Instance inst, NTP_Packet *packet_queue,
do {
res_buffer = packet_queue[random() % queue_length];
} while (!UTI_CompareNtp64(&res_buffer.transmit_ts, &inst->remote_ntp_tx));
- proc_response(inst, 0, 0, 0, updated_init);
+ proc_response(inst, 0, 0, 0, updated_init, 0);
advance_time(1e-6);
}
@@ -392,7 +416,7 @@ test_unit(void)
"local",
"keyfile ntp_core.keys"
};
- int i, j, k, interleaved, authenticated, valid, updated, has_updated;
+ int i, j, k, interleaved, authenticated, valid, updated, has_updated, late_hwts;
CPS_NTP_Source source;
NTP_Remote_Address remote_addr;
NCR_Instance inst1, inst2;
@@ -439,6 +463,8 @@ test_unit(void)
for (j = 0; j < 50; j++) {
DEBUG_LOG("client/peer test iteration %d/%d", i, j);
+ late_hwts = random() % 2;
+ authenticated = random() % 2;
interleaved = random() % 2 && (inst1->mode != MODE_CLIENT ||
inst1->tx_count < MAX_CLIENT_INTERLEAVED_TX);
authenticated = random() % 2;
@@ -454,35 +480,35 @@ test_unit(void)
(int)source.params.authkey, source.params.version,
interleaved, authenticated, valid, updated, has_updated);
- send_request(inst1);
+ send_request(inst1, late_hwts);
send_response(interleaved, authenticated, 1, 0, 1);
DEBUG_LOG("response 1");
- proc_response(inst1, 0, 0, 0, updated);
+ proc_response(inst1, 0, 0, 0, updated, 0);
if (source.params.authkey) {
send_response(interleaved, authenticated, 1, 1, 0);
DEBUG_LOG("response 2");
- proc_response(inst1, 0, 0, 0, 0);
+ proc_response(inst1, 0, 0, 0, 0, 0);
}
send_response(interleaved, authenticated, 1, 1, 1);
DEBUG_LOG("response 3");
- proc_response(inst1, -1, valid, valid, updated);
+ proc_response(inst1, -1, valid, valid, updated, valid && late_hwts);
DEBUG_LOG("response 4");
- proc_response(inst1, 0, 0, 0, 0);
+ proc_response(inst1, 0, 0, 0, 0, 0);
advance_time(-1.0);
send_response(interleaved, authenticated, 1, 1, 1);
DEBUG_LOG("response 5");
- proc_response(inst1, 0, 0, 0, updated && valid);
+ proc_response(inst1, 0, 0, 0, updated && valid, 0);
advance_time(1.0);
send_response(interleaved, authenticated, 1, 1, 1);
DEBUG_LOG("response 6");
- proc_response(inst1, 0, 0, valid && updated, updated);
+ proc_response(inst1, 0, 0, valid && updated, updated, 0);
}
NCR_DestroyInstance(inst1);
@@ -494,12 +520,12 @@ test_unit(void)
for (j = 0; j < 20; j++) {
DEBUG_LOG("server test iteration %d/%d", i, j);
- send_request(inst1);
+ send_request(inst1, 0);
process_request(&remote_addr);
proc_response(inst1,
!source.params.interleaved || source.params.version != 4 ||
inst1->mode == MODE_ACTIVE || j != 2,
- 1, 1, 0);
+ 1, 1, 0, 0);
advance_time(1 << inst1->local_poll);
}
@@ -515,7 +541,9 @@ test_unit(void)
for (j = 0; j < 20; j++) {
DEBUG_LOG("peer replay test iteration %d/%d", i, j);
- send_request(inst1);
+ late_hwts = random() % 2;
+
+ send_request(inst1, late_hwts);
res_buffer = req_buffer;
assert(!res_length || res_length == req_length);
res_length = req_length;
@@ -523,7 +551,7 @@ test_unit(void)
TEST_CHECK(inst1->valid_timestamps == (j > 0));
DEBUG_LOG("response 1->2");
- proc_response(inst2, j > source.params.interleaved, j > 0, j > 0, 1);
+ proc_response(inst2, j > source.params.interleaved, j > 0, j > 0, 1, 0);
packet_queue[(j * 2) % PACKET_QUEUE_LENGTH] = res_buffer;
@@ -536,14 +564,14 @@ test_unit(void)
advance_time(1 << (source.params.minpoll - 1));
- send_request(inst2);
+ send_request(inst2, 0);
res_buffer = req_buffer;
assert(res_length == req_length);
TEST_CHECK(inst2->valid_timestamps == (j > 0));
DEBUG_LOG("response 2->1");
- proc_response(inst1, 1, 1, 1, 1);
+ proc_response(inst1, 1, 1, 1, 1, late_hwts);
packet_queue[(j * 2 + 1) % PACKET_QUEUE_LENGTH] = res_buffer;
diff --git a/test/unit/nts_ke_client.c b/test/unit/nts_ke_client.c
index 72690bf..3100d1d 100644
--- a/test/unit/nts_ke_client.c
+++ b/test/unit/nts_ke_client.c
@@ -64,9 +64,12 @@ prepare_response(NKSN_Instance session, int valid)
if (index != 5) {
if (index == 6)
- data[0] = htons(AEAD_AES_SIV_CMAC_256 + random() % 10 + 1);
+ do {
+ data[0] = htons(random() % 100);
+ } while (SIV_GetKeyLength(ntohs(data[0])) > 0);
else
- data[0] = htons(AEAD_AES_SIV_CMAC_256);
+ data[0] = htons(random() % 2 && SIV_GetKeyLength(AEAD_AES_128_GCM_SIV) > 0 ?
+ AEAD_AES_128_GCM_SIV : AEAD_AES_SIV_CMAC_256);
if (index == 7)
length = 3 + random() % 10;
TEST_CHECK(NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, data, length));
diff --git a/test/unit/nts_ke_server.c b/test/unit/nts_ke_server.c
index f4f03a1..01156c1 100644
--- a/test/unit/nts_ke_server.c
+++ b/test/unit/nts_ke_server.c
@@ -75,7 +75,8 @@ prepare_request(NKSN_Instance session, int valid)
TEST_CHECK(NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, data, length));
if (index != 4) {
- data[0] = htons(AEAD_AES_SIV_CMAC_256);
+ data[0] = htons(random() % 2 && SIV_GetKeyLength(AEAD_AES_128_GCM_SIV) > 0 ?
+ AEAD_AES_128_GCM_SIV : AEAD_AES_SIV_CMAC_256);
if (index == 5)
length = 0;
else if (index == 6)
diff --git a/test/unit/nts_ntp_auth.c b/test/unit/nts_ntp_auth.c
index 307b93b..c3a7432 100644
--- a/test/unit/nts_ntp_auth.c
+++ b/test/unit/nts_ntp_auth.c
@@ -34,74 +34,97 @@ test_unit(void)
unsigned char key[SIV_MAX_KEY_LENGTH], nonce[256], plaintext[256], plaintext2[256];
NTP_PacketInfo info;
NTP_Packet packet;
+ SIV_Algorithm algo;
SIV_Instance siv;
int i, j, r, packet_length, nonce_length, key_length;
int plaintext_length, plaintext2_length, min_ef_length;
- siv = SIV_CreateInstance(AEAD_AES_SIV_CMAC_256);
- TEST_CHECK(siv);
-
- for (i = 0; i < 10000; i++) {
- key_length = SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256);
- for (j = 0; j < key_length; j++)
- key[j] = random() % 256;
- TEST_CHECK(SIV_SetKey(siv, key, key_length));
-
- nonce_length = random() % sizeof (nonce) + 1;
- for (j = 0; j < nonce_length; j++)
- nonce[j] = random() % 256;
-
- plaintext_length = random() % (sizeof (plaintext) + 1);
- for (j = 0; j < plaintext_length; j++)
- plaintext[j] = random() % 256;
-
- packet_length = NTP_HEADER_LENGTH + random() % 100 * 4;
- min_ef_length = random() % (sizeof (packet) - packet_length);
-
- memset(&packet, 0, sizeof (packet));
- packet.lvm = NTP_LVM(0, 4, 0);
- memset(&info, 0, sizeof (info));
- info.version = 4;
- info.length = packet_length;
-
- DEBUG_LOG("packet_length=%d nonce_length=%d plaintext_length=%d min_ef_length=%d",
- packet_length, nonce_length, plaintext_length, min_ef_length);
-
- r = NNA_GenerateAuthEF(&packet, &info, siv, nonce, nonce_length, plaintext,
- -1, 0);
- TEST_CHECK(!r);
- r = NNA_GenerateAuthEF(&packet, &info, siv, nonce, 0, plaintext,
- plaintext_length, 0);
- TEST_CHECK(!r);
- r = NNA_GenerateAuthEF(&packet, &info, siv, nonce, nonce_length, plaintext,
- plaintext_length, sizeof (packet) - info.length + 1);
- TEST_CHECK(!r);
-
- r = NNA_GenerateAuthEF(&packet, &info, siv, nonce, nonce_length, plaintext,
- plaintext_length, min_ef_length);
- TEST_CHECK(r);
- TEST_CHECK(info.length - packet_length >= min_ef_length);
-
- r = NNA_DecryptAuthEF(&packet, &info, siv, packet_length, plaintext2,
- -1, &plaintext2_length);
- TEST_CHECK(!r);
-
- r = NNA_DecryptAuthEF(&packet, &info, siv, packet_length, plaintext2,
- sizeof (plaintext2), &plaintext2_length);
- TEST_CHECK(r);
- TEST_CHECK(plaintext_length == plaintext2_length);
- TEST_CHECK(memcmp(plaintext, plaintext2, plaintext_length) == 0);
-
- j = random() % (packet_length + plaintext_length +
- nonce_length + SIV_GetTagLength(siv) + 8) / 4 * 4;
- ((unsigned char *)&packet)[j]++;
- r = NNA_DecryptAuthEF(&packet, &info, siv, packet_length, plaintext2,
- sizeof (plaintext2), &plaintext2_length);
- TEST_CHECK(!r);
- ((unsigned char *)&packet)[j]--;
+ for (algo = 1; algo < 100; algo++) {
+ siv = SIV_CreateInstance(algo);
+ if (!siv) {
+ TEST_CHECK(algo != AEAD_AES_SIV_CMAC_256);
+ continue;
+ }
+
+ DEBUG_LOG("algo=%d", (int)algo);
+
+ for (i = 0; i < 10000; i++) {
+ key_length = SIV_GetKeyLength(algo);
+ for (j = 0; j < key_length; j++)
+ key[j] = random() % 256;
+ TEST_CHECK(SIV_SetKey(siv, key, key_length));
+
+ assert(sizeof (nonce) >= SIV_GetMinNonceLength(siv));
+ nonce_length = SIV_GetMinNonceLength(siv) +
+ random() % (MIN(sizeof (nonce), SIV_GetMaxNonceLength(siv)) -
+ SIV_GetMinNonceLength(siv) + 1);
+ for (j = 0; j < nonce_length; j++)
+ nonce[j] = random() % 256;
+
+ plaintext_length = random() % (sizeof (plaintext) + 1);
+ for (j = 0; j < plaintext_length; j++)
+ plaintext[j] = random() % 256;
+
+ packet_length = NTP_HEADER_LENGTH + random() % 100 * 4;
+ min_ef_length = random() % (sizeof (packet) - packet_length);
+
+ memset(&packet, 0, sizeof (packet));
+ packet.lvm = NTP_LVM(0, 4, 0);
+ memset(&info, 0, sizeof (info));
+ info.version = 4;
+ info.length = packet_length;
+
+ DEBUG_LOG("packet_length=%d nonce_length=%d plaintext_length=%d min_ef_length=%d",
+ packet_length, nonce_length, plaintext_length, min_ef_length);
+
+ r = NNA_GenerateAuthEF(&packet, &info, siv, nonce, nonce_length, plaintext,
+ -1, 0);
+ TEST_CHECK(!r);
+ r = NNA_GenerateAuthEF(&packet, &info, siv, nonce, 0, plaintext,
+ plaintext_length, 0);
+ TEST_CHECK(!r);
+ if (SIV_GetMinNonceLength(siv) > 1) {
+ r = NNA_GenerateAuthEF(&packet, &info, siv, nonce, SIV_GetMinNonceLength(siv) - 1,
+ plaintext, plaintext_length, 0);
+ TEST_CHECK(!r);
+ TEST_CHECK(info.ext_fields == 0);
+ }
+ if (SIV_GetMaxNonceLength(siv) <= sizeof (nonce)) {
+ r = NNA_GenerateAuthEF(&packet, &info, siv, nonce, SIV_GetMaxNonceLength(siv) - 1,
+ plaintext, plaintext_length, 0);
+ TEST_CHECK(!r);
+ TEST_CHECK(info.ext_fields == 0);
+ }
+ r = NNA_GenerateAuthEF(&packet, &info, siv, nonce, nonce_length, plaintext,
+ plaintext_length, sizeof (packet) - info.length + 1);
+ TEST_CHECK(!r);
+
+ r = NNA_GenerateAuthEF(&packet, &info, siv, nonce, nonce_length, plaintext,
+ plaintext_length, min_ef_length);
+ TEST_CHECK(r);
+ TEST_CHECK(info.length - packet_length >= min_ef_length);
+
+ r = NNA_DecryptAuthEF(&packet, &info, siv, packet_length, plaintext2,
+ -1, &plaintext2_length);
+ TEST_CHECK(!r);
+
+ r = NNA_DecryptAuthEF(&packet, &info, siv, packet_length, plaintext2,
+ sizeof (plaintext2), &plaintext2_length);
+ TEST_CHECK(r);
+ TEST_CHECK(plaintext_length == plaintext2_length);
+ TEST_CHECK(memcmp(plaintext, plaintext2, plaintext_length) == 0);
+
+ j = random() % (packet_length + plaintext_length +
+ nonce_length + SIV_GetTagLength(siv) + 8) / 4 * 4;
+ ((unsigned char *)&packet)[j]++;
+ r = NNA_DecryptAuthEF(&packet, &info, siv, packet_length, plaintext2,
+ sizeof (plaintext2), &plaintext2_length);
+ TEST_CHECK(!r);
+ ((unsigned char *)&packet)[j]--;
+ }
+
+ SIV_DestroyInstance(siv);
}
-
- SIV_DestroyInstance(siv);
}
#else
void
diff --git a/test/unit/nts_ntp_client.c b/test/unit/nts_ntp_client.c
index c14fd41..7953413 100644
--- a/test/unit/nts_ntp_client.c
+++ b/test/unit/nts_ntp_client.c
@@ -50,7 +50,10 @@ get_nts_data(NKC_Instance inst, NKE_Context *context,
if (random() % 2)
return 0;
- context->algorithm = AEAD_AES_SIV_CMAC_256;
+ do {
+ context->algorithm = AEAD_AES_SIV_CMAC_256 + random() %
+ (AEAD_AES_256_GCM_SIV - AEAD_AES_SIV_CMAC_256 + 10);
+ } while (SIV_GetKeyLength(context->algorithm) <= 0);
context->c2s.length = SIV_GetKeyLength(context->algorithm);
UTI_GetRandomBytes(context->c2s.key, context->c2s.length);
@@ -75,9 +78,9 @@ static int
get_request(NNC_Instance inst)
{
unsigned char nonce[NTS_MIN_UNPADDED_NONCE_LENGTH], uniq_id[NTS_MIN_UNIQ_ID_LENGTH];
+ int nonce_length, expected_length, req_cookies;
NTP_PacketInfo info;
NTP_Packet packet;
- int expected_length, req_cookies;
memset(&packet, 0, sizeof (packet));
memset(&info, 0, sizeof (info));
@@ -100,6 +103,17 @@ get_request(NNC_Instance inst)
TEST_CHECK(inst->num_cookies > 0);
TEST_CHECK(inst->siv);
+ switch (inst->context.algorithm) {
+ case AEAD_AES_SIV_CMAC_256:
+ nonce_length = 16;
+ break;
+ case AEAD_AES_128_GCM_SIV:
+ nonce_length = 12;
+ break;
+ default:
+ assert(0);
+ }
+
memcpy(nonce, inst->nonce, sizeof (nonce));
memcpy(uniq_id, inst->uniq_id, sizeof (uniq_id));
TEST_CHECK(NNC_PrepareForAuth(inst));
@@ -111,9 +125,10 @@ get_request(NNC_Instance inst)
(inst->cookies[inst->cookie_index].length + 4));
expected_length = info.length + 4 + sizeof (inst->uniq_id) +
req_cookies * (4 + inst->cookies[inst->cookie_index].length) +
- 4 + 4 + sizeof (inst->nonce) + SIV_GetTagLength(inst->siv);
- DEBUG_LOG("length=%d cookie_length=%d expected_length=%d",
- info.length, inst->cookies[inst->cookie_index].length, expected_length);
+ 4 + 4 + nonce_length + SIV_GetTagLength(inst->siv);
+ DEBUG_LOG("algo=%d length=%d cookie_length=%d expected_length=%d",
+ (int)inst->context.algorithm, info.length,
+ inst->cookies[inst->cookie_index].length, expected_length);
if (info.length % 4 == 0 && info.length >= NTP_HEADER_LENGTH &&
inst->cookies[inst->cookie_index].length % 4 == 0 &&
@@ -162,7 +177,10 @@ prepare_response(NNC_Instance inst, NTP_Packet *packet, NTP_PacketInfo *info, in
return;
}
- nonce_length = random() % (sizeof (nonce)) + 1;
+ nonce_length = SIV_GetMinNonceLength(inst->siv) + random() %
+ (MIN(SIV_GetMaxNonceLength(inst->siv), sizeof (nonce)) -
+ SIV_GetMinNonceLength(inst->siv) + 1);
+ assert(nonce_length >= 1 && nonce_length <= sizeof (nonce));
do {
cookie_length = random() % (sizeof (cookie) + 1);
diff --git a/test/unit/nts_ntp_server.c b/test/unit/nts_ntp_server.c
index 40938d6..2777942 100644
--- a/test/unit/nts_ntp_server.c
+++ b/test/unit/nts_ntp_server.c
@@ -37,10 +37,13 @@ prepare_request(NTP_Packet *packet, NTP_PacketInfo *info, int valid, int nak)
NKE_Cookie cookie;
int i, index, cookie_start, auth_start;
- context.algorithm = SERVER_SIV;
+ context.algorithm = random() % 2 && SIV_GetKeyLength(AEAD_AES_128_GCM_SIV) > 0 ?
+ AEAD_AES_128_GCM_SIV : AEAD_AES_SIV_CMAC_256;
context.c2s.length = SIV_GetKeyLength(context.algorithm);
+ assert(context.c2s.length <= sizeof (context.c2s.key));
UTI_GetRandomBytes(&context.c2s.key, context.c2s.length);
context.s2c.length = SIV_GetKeyLength(context.algorithm);
+ assert(context.s2c.length <= sizeof (context.s2c.key));
UTI_GetRandomBytes(&context.s2c.key, context.s2c.length);
TEST_CHECK(NKS_GenerateCookie(&context, &cookie));
@@ -80,6 +83,7 @@ prepare_request(NTP_Packet *packet, NTP_PacketInfo *info, int valid, int nak)
if (index != 2) {
siv = SIV_CreateInstance(context.algorithm);
+ TEST_CHECK(siv);
TEST_CHECK(SIV_SetKey(siv, context.c2s.key, context.c2s.length));
TEST_CHECK(NNA_GenerateAuthEF(packet, info, siv, nonce, sizeof (nonce),
(const unsigned char *)"", 0, 0));
diff --git a/test/unit/siv.c b/test/unit/siv.c
index 76846c8..54f435d 100644
--- a/test/unit/siv.c
+++ b/test/unit/siv.c
@@ -125,15 +125,94 @@ test_unit(void)
"\x8d\x49\x2f\x14\x62\xa4\x7c\x2a\x57\x38\x87\xce\xc6\x72\xd3\x5c"
"\xa1", 97
},
+ { AEAD_AES_128_GCM_SIV,
+ "\x01\x23\x45\x67\x89\xab\xcd\xef\xf0\x12\x34\x56\x78\x9a\xbc\xde", 16,
+ "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b", 12,
+ "", 0,
+ "", 0,
+ "\xba\x05\x1c\x40\xeb\x7e\x5f\xa2\x3f\x6c\xe5\xbe\xfe\x5b\x04\xad", 16
+ },
+ { AEAD_AES_128_GCM_SIV,
+ "\x01\x23\x45\x67\x89\xab\xcd\xef\xf0\x12\x34\x56\x78\x9a\xbc\xde", 16,
+ "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b", 12,
+ "\x4c\x9d\x4f\xca\xed\x8a\xe2\xba\xad\x3f\x3e\xa6\xe9\x3c\x8c\x8b", 16,
+ "", 0,
+ "\x8f\x47\xfe\x1f\x26\x4e\xe2\x99\x5f\x35\x3d\x26\x74\x14\xd4\x3b", 16
+ },
+ { AEAD_AES_128_GCM_SIV,
+ "\x01\x23\x45\x67\x89\xab\xcd\xef\xf0\x12\x34\x56\x78\x9a\xbc\xde", 16,
+ "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b", 12,
+ "", 0,
+ "\xba\x05\x1c\x40\xeb\x7e\x5f\xa2\x3f\x6c\xe5\xbe\xfe\x5b\x04\xad", 16,
+ "\xa1\xc6\x1b\xf7\x32\x39\x93\x0e\x10\xf8\xa6\x21\x6c\x6e\x26\x83"
+ "\x5c\xa9\xb0\xdd\x91\x0f\x81\xa6\xf0\x3b\x45\xda\xa6\x9a\x2b\x24", 32,
+ },
+ { AEAD_AES_128_GCM_SIV,
+ "\x01\x23\x45\x67\x89\xab\xcd\xef\xf0\x12\x34\x56\x78\x9a\xbc\xde", 16,
+ "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b", 12,
+ "\x4c\x9d\x4f\xca\xed\x8a\xe2\xba\xad\x3f\x3e\xa6\xe9\x3c\x8c", 15,
+ "\xba\x99\x79\x31\x23\x7e\x3c\x53\x58\x7e\xd4\x93\x02\xab\xe4", 15,
+ "\x7a\x23\xa7\x35\x8d\x34\x5b\xf6\x0d\xa7\x6d\x3b\x58\x8c\x4c\x65"
+ "\xd9\x85\x4e\x17\xb7\x52\x48\xf7\x91\xb4\xdd\xd6\x8b\xec\x02", 31
+ },
+ { AEAD_AES_128_GCM_SIV,
+ "\x01\x23\x45\x67\x89\xab\xcd\xef\xf0\x12\x34\x56\x78\x9a\xbc\xde", 16,
+ "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b", 12,
+ "\x4c\x9d\x4f\xca\xed\x8a\xe2\xba\xad\x3f\x3e\xa6\xe9\x3c\x8c\x8b", 16,
+ "\xba\x99\x79\x31\x23\x7e\x3c\x53\x58\x7e\xd4\x93\x02\xab\xe4\xa7", 16,
+ "\xa3\x10\xae\x5f\x26\xd9\x90\xfa\xab\x30\x29\x80\x7f\x93\x62\x23"
+ "\x83\x8f\xc9\x57\x90\xbb\x05\x87\x02\x11\x57\xd6\x13\x9b\x82\x4d", 32
+ },
+ { AEAD_AES_128_GCM_SIV,
+ "\x01\x23\x45\x67\x89\xab\xcd\xef\xf0\x12\x34\x56\x78\x9a\xbc\xde", 16,
+ "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b", 12,
+ "\x4c\x9d\x4f\xca\xed\x8a\xe2\xba\xad\x3f\x3e\xa6\xe9\x3c\x8c\x8b"
+ "\xa0", 17,
+ "\xba\x99\x79\x31\x23\x7e\x3c\x53\x58\x7e\xd4\x93\x02\xab\xe4\xa7"
+ "\x08", 17,
+ "\x4c\x48\x67\x48\xce\x8b\x14\x7b\x70\xac\x71\xe8\x7b\x4e\x4a\x6a"
+ "\xb4\x3d\xb5\x8e\x58\x81\xfc\x3e\x97\xcd\xdf\xef\x67\x1e\xf4\x4f"
+ "\x0d", 33
+ },
+ { AEAD_AES_128_GCM_SIV,
+ "\x01\x23\x45\x67\x89\xab\xcd\xef\xf0\x12\x34\x56\x78\x9a\xbc\xde", 16,
+ "\xb0\x5a\x1b\xc7\x56\xe7\xb6\x2c\xb4\x85\xe5\x56", 12,
+ "\xe5\x8b\xd2\x6a\x30\xc5\xc5\x61\xcc\xbd\x7c\x27\xbf\xfe\xf9\x06"
+ "\x00\x5b\xd7\xfc\x11\x0b\xcf\x16\x61\xef\xac\x05\xa7\xaf\xec\x27"
+ "\x41\xc8\x5e\x9e\x0d\xf9\x2f\xaf\x20\x79\x17\xe5\x17\x91\x2a\x27"
+ "\x34\x1c\xbc\xaf\xeb\xef\x7f\x52\xe7\x1e\x4c\x2a\xca\xbd\x2b\xbe"
+ "\x34\xd6\xfb\x69\xd3\x3e\x49\x59\x60\xb4\x26\xc9\xb8\xce\xba", 79,
+ "\x6c\xe7\xcf\x7e\xab\x7b\xa0\xe1\xa7\x22\xcb\x88\xde\x5e\x42\xd2"
+ "\xec\x79\xe0\xa2\xcf\x5f\x0f\x6f\x6b\x89\x57\xcd\xae\x17\xd4\xc2"
+ "\xf3\x1b\xa2\xa8\x13\x78\x23\x2f\x83\xa8\xd4\x0c\xc0\xd2\xf3\x99"
+ "\xae\x81\xa1\xca\x5b\x5f\x45\xa6\x6f\x0c\x8a\xf3\xd4\x67\x40\x81"
+ "\x26\xe2\x01\x86\xe8\x5a\xd5\xf8\x58\x80\x9f\x56\xaa\x76\x96\xbf"
+ "\x31", 81,
+ "\xf6\xa0\x1a\xf3\x4f\xe9\x36\xde\x5c\xbd\xb6\x0a\x26\x9d\x60\x1d"
+ "\xe6\xc9\x6d\xb8\xf2\x5f\xcd\xce\x26\xf4\x0d\x86\xec\xdd\x84\x25"
+ "\xaf\xec\x72\x10\x2d\x74\x2d\xde\x95\x84\xac\xce\xbf\x8a\x52\x9f"
+ "\x10\x6f\xc2\xa8\x1f\xed\x47\xff\xeb\x28\x57\x54\xb3\x45\x45\x56"
+ "\xbb\xcf\x7d\x9b\x99\x68\xbd\x36\x75\xe3\xf7\x8c\x09\x25\x01\xbe"
+ "\xe1\xe2\x3d\x19\x4f\x15\x64\x12\x6e\xea\x67\x6c\x42\x2f\xc1\x91"
+ "\xff", 97
+ },
{ 0, "", 0 }
};
unsigned char plaintext[sizeof (((struct siv_test *)NULL)->plaintext)];
unsigned char ciphertext[sizeof (((struct siv_test *)NULL)->ciphertext)];
SIV_Instance siv;
- int i, j, r;
+ int i, j, r, fixed_nonce_length;
- TEST_CHECK(SIV_CreateInstance(0) == NULL);
+ for (i = 0; i < AEAD_AES_256_GCM_SIV + 10; i++) {
+ switch (i) {
+ case AEAD_AES_SIV_CMAC_256:
+ case AEAD_AES_128_GCM_SIV:
+ continue;
+ }
+ TEST_CHECK(SIV_GetKeyLength(i) == 0);
+ TEST_CHECK(SIV_CreateInstance(i) == NULL);
+ }
for (i = 0; tests[i].algorithm != 0; i++) {
DEBUG_LOG("testing %d (%d)", (int)tests[i].algorithm, i);
@@ -145,9 +224,32 @@ test_unit(void)
assert(tests[i].ciphertext_length <= sizeof (tests[i].ciphertext));
siv = SIV_CreateInstance(tests[i].algorithm);
- TEST_CHECK(siv != NULL);
+
+ switch (tests[i].algorithm) {
+ case AEAD_AES_SIV_CMAC_256:
+ TEST_CHECK(siv != NULL);
+ fixed_nonce_length = 0;
+ break;
+ case AEAD_AES_128_GCM_SIV:
+ fixed_nonce_length = 1;
+ break;
+ default:
+ assert(0);
+ }
+
+ if (!siv) {
+ DEBUG_LOG("missing %d support", (int)tests[i].algorithm);
+ TEST_CHECK(SIV_GetKeyLength(tests[i].algorithm) == 0);
+ continue;
+ }
TEST_CHECK(SIV_GetKeyLength(tests[i].algorithm) == tests[i].key_length);
+ TEST_CHECK(SIV_GetMinNonceLength(siv) >= 1);
+ TEST_CHECK(SIV_GetMinNonceLength(siv) <= 12);
+ TEST_CHECK(SIV_GetMaxNonceLength(siv) >= 12);
+ TEST_CHECK(SIV_GetMinNonceLength(siv) <= SIV_GetMaxNonceLength(siv));
+ if (fixed_nonce_length)
+ TEST_CHECK(SIV_GetMinNonceLength(siv) == SIV_GetMaxNonceLength(siv));
r = SIV_Encrypt(siv, tests[i].nonce, tests[i].nonce_length,
tests[i].assoc, tests[i].assoc_length,
@@ -188,7 +290,7 @@ test_unit(void)
tests[i].assoc, tests[i].assoc_length,
tests[i].plaintext, tests[i].plaintext_length,
ciphertext, tests[i].ciphertext_length);
- if (j > 0) {
+ if (j > 0 && (j == tests[i].nonce_length || !fixed_nonce_length)) {
TEST_CHECK(r);
TEST_CHECK(memcmp(ciphertext, tests[i].ciphertext, tests[i].ciphertext_length) != 0);
} else {
diff --git a/test/unit/test.c b/test/unit/test.c
index 94619c1..1ebb11a 100644
--- a/test/unit/test.c
+++ b/test/unit/test.c
@@ -80,6 +80,7 @@ main(int argc, char **argv)
test_unit();
+ UTI_ResetGetRandomFunctions();
LOG_Finalise();
printf("PASS\n");
diff --git a/test/unit/util.c b/test/unit/util.c
index 112676d..fa294c4 100644
--- a/test/unit/util.c
+++ b/test/unit/util.c
@@ -40,6 +40,7 @@ test_unit(void)
double x, y, nan, inf;
IPAddr ip, ip2, ip3;
IPSockAddr ip_saddr;
+ Integer64 integer64;
Timespec tspec;
Float f;
int i, j, c;
@@ -470,6 +471,15 @@ test_unit(void)
s = UTI_IPSockAddrToString(&ip_saddr);
TEST_CHECK(strcmp(s, "1.2.3.4:12345") == 0);
+ ip = ip_saddr.ip_addr;
+ s = UTI_IPSubnetToString(&ip, 10);
+ TEST_CHECK(strcmp(s, "1.2.3.4/10") == 0);
+ s = UTI_IPSubnetToString(&ip, 32);
+ TEST_CHECK(strcmp(s, "1.2.3.4") == 0);
+ ip.family = IPADDR_UNSPEC;
+ s = UTI_IPSubnetToString(&ip, 0);
+ TEST_CHECK(strcmp(s, "any address") == 0);
+
s = UTI_TimeToLogForm(2000000000);
TEST_CHECK(strcmp(s, "2033-05-18 03:33:20") == 0);
@@ -556,6 +566,10 @@ test_unit(void)
UTI_TimespecNetworkToHost(&tspec, &ts2);
TEST_CHECK(!UTI_CompareTimespecs(&ts, &ts2));
+ integer64 = UTI_Integer64HostToNetwork(0x1234567890ABCDEFULL);
+ TEST_CHECK(memcmp(&integer64, "\x12\x34\x56\x78\x90\xab\xcd\xef", 8) == 0);
+ TEST_CHECK(UTI_Integer64NetworkToHost(integer64) == 0x1234567890ABCDEFULL);
+
TEST_CHECK(UTI_CmacNameToAlgorithm("AES128") == CMC_AES128);
TEST_CHECK(UTI_CmacNameToAlgorithm("AES256") == CMC_AES256);
TEST_CHECK(UTI_CmacNameToAlgorithm("NOSUCHCMAC") == CMC_INVALID);
@@ -741,4 +755,6 @@ test_unit(void)
TEST_CHECK(words[1] == buf + 3);
TEST_CHECK(strcmp(words[0], "a") == 0);
TEST_CHECK(strcmp(words[1], "b") == 0);
+
+ HSH_Finalise();
}
diff --git a/util.c b/util.c
index c4c8934..6b3320b 100644
--- a/util.c
+++ b/util.c
@@ -552,6 +552,26 @@ UTI_IPSockAddrToString(const IPSockAddr *sa)
/* ================================================== */
char *
+UTI_IPSubnetToString(IPAddr *subnet, int bits)
+{
+ char *result;
+
+ result = NEXT_BUFFER;
+
+ if (subnet->family == IPADDR_UNSPEC)
+ snprintf(result, BUFFER_LENGTH, "%s", "any address");
+ else if ((subnet->family == IPADDR_INET4 && bits == 32) ||
+ (subnet->family == IPADDR_INET6 && bits == 128))
+ snprintf(result, BUFFER_LENGTH, "%s", UTI_IPToString(subnet));
+ else
+ snprintf(result, BUFFER_LENGTH, "%s/%d", UTI_IPToString(subnet), bits);
+
+ return result;
+}
+
+/* ================================================== */
+
+char *
UTI_TimeToLogForm(time_t t)
{
struct tm *stm;
@@ -889,6 +909,25 @@ UTI_TimespecHostToNetwork(const struct timespec *src, Timespec *dest)
/* ================================================== */
+uint64_t
+UTI_Integer64NetworkToHost(Integer64 i)
+{
+ return (uint64_t)ntohl(i.high) << 32 | ntohl(i.low);
+}
+
+/* ================================================== */
+
+Integer64
+UTI_Integer64HostToNetwork(uint64_t i)
+{
+ Integer64 r;
+ r.high = htonl(i >> 32);
+ r.low = htonl(i);
+ return r;
+}
+
+/* ================================================== */
+
#define FLOAT_EXP_BITS 7
#define FLOAT_EXP_MIN (-(1 << (FLOAT_EXP_BITS - 1)))
#define FLOAT_EXP_MAX (-FLOAT_EXP_MIN - 1)
@@ -1228,6 +1267,40 @@ UTI_CheckDirPermissions(const char *path, mode_t perm, uid_t uid, gid_t gid)
/* ================================================== */
+int
+UTI_CheckFilePermissions(const char *path, mode_t perm)
+{
+ mode_t extra_perm;
+ struct stat buf;
+
+ if (stat(path, &buf) < 0 || !S_ISREG(buf.st_mode)) {
+ /* Not considered an error */
+ return 1;
+ }
+
+ extra_perm = (buf.st_mode & 0777) & ~perm;
+ if (extra_perm != 0) {
+ LOG(LOGS_WARN, "%s permissions on %s", extra_perm & 0006 ?
+ (extra_perm & 0004 ? "World-readable" : "World-writable") : "Wrong", path);
+ return 0;
+ }
+
+ return 1;
+}
+
+/* ================================================== */
+
+void
+UTI_CheckReadOnlyAccess(const char *path)
+{
+ if (access(path, R_OK) != 0 && errno != ENOENT)
+ LOG(LOGS_WARN, "Missing read access to %s : %s", path, strerror(errno));
+ if (access(path, W_OK) == 0)
+ LOG(LOGS_WARN, "Having write access to %s", path);
+}
+
+/* ================================================== */
+
static int
join_path(const char *basedir, const char *name, const char *suffix,
char *buffer, size_t length, LOG_Severity severity)
diff --git a/util.h b/util.h
index a57ef9b..d38abf0 100644
--- a/util.h
+++ b/util.h
@@ -120,6 +120,8 @@ extern int UTI_CompareIPs(const IPAddr *a, const IPAddr *b, const IPAddr *mask);
extern char *UTI_IPSockAddrToString(const IPSockAddr *sa);
+extern char *UTI_IPSubnetToString(IPAddr *subnet, int bits);
+
extern char *UTI_TimeToLogForm(time_t t);
/* Adjust time following a frequency/offset change */
@@ -170,6 +172,9 @@ extern double UTI_Log2ToDouble(int l);
extern void UTI_TimespecNetworkToHost(const Timespec *src, struct timespec *dest);
extern void UTI_TimespecHostToNetwork(const struct timespec *src, Timespec *dest);
+uint64_t UTI_Integer64NetworkToHost(Integer64 i);
+Integer64 UTI_Integer64HostToNetwork(uint64_t i);
+
extern double UTI_FloatNetworkToHost(Float x);
extern Float UTI_FloatHostToNetwork(double x);
@@ -194,6 +199,14 @@ extern int UTI_CreateDirAndParents(const char *path, mode_t mode, uid_t uid, gid
permissions and its uid/gid must match the specified values. */
extern int UTI_CheckDirPermissions(const char *path, mode_t perm, uid_t uid, gid_t gid);
+/* Check and log a warning message if a file has more permissions than
+ specified. It does not return error if it is not an accessible file. */
+extern int UTI_CheckFilePermissions(const char *path, mode_t perm);
+
+/* Log a warning message if not having read access or having write access
+ to a file/directory */
+extern void UTI_CheckReadOnlyAccess(const char *path);
+
/* Open a file. The full path of the file is constructed from the basedir
(may be NULL), '/' (if basedir is not NULL), name, and suffix (may be NULL).
Created files have specified permissions (umasked). Returns NULL on error.
diff --git a/version.txt b/version.txt
index 69df05f..a20eaa3 100644
--- a/version.txt
+++ b/version.txt
@@ -1 +1 @@
-4.3
+4.4-pre1