Compare commits

..

89 Commits

Author SHA1 Message Date
Miroslav Lichvar
366345790d doc: update NEWS 2018-04-04 09:18:44 +02:00
Miroslav Lichvar
f881c153bf client: update copyright years 2018-04-04 09:18:44 +02:00
Miroslav Lichvar
19f3ab2225 ntp: fix handling of socket errors with error queue
In the next Linux version the recvmmsg() system call will be probably
fixed to not return socket errors (e.g. due to ICMP) when reading from
the error queue.

The NTP I/O code assumed this was the correct behavior. When the system
call is fixed, a socket error on a client socket will cause chronyd to
enter a busy loop consuming the CPU until the receive timeout is reached
(8 seconds by default).

Use getsockopt(SO_ERROR) to clear the socket error when reading from the
error queue failed.
2018-04-04 09:18:44 +02:00
Miroslav Lichvar
fd1e80802f privops: allow binding to acquisition port
Fix the privileged helper process to allow binding of client sockets to
a non-zero acquisition port which is not equal to the server port.
2018-04-04 09:18:34 +02:00
Miroslav Lichvar
4b7cb161a8 doc: improve FAQ 2018-03-28 16:57:48 +02:00
Miroslav Lichvar
7848794222 doc: improve description of fallbackdrift directive 2018-03-28 16:57:48 +02:00
Miroslav Lichvar
94822d5156 doc: improve description of key option in chrony.conf man page 2018-03-28 16:57:48 +02:00
Miroslav Lichvar
e3f840aae9 doc: update installation document 2018-03-28 16:57:48 +02:00
Miroslav Lichvar
5aae563277 update copyright years 2018-03-28 16:57:48 +02:00
Miroslav Lichvar
02de782fa3 ntp: fix compiler warnings
Warnings about using uninitialized variables were seen with gcc-7.3.1
and -O3 in CFLAGS.
2018-03-28 16:57:48 +02:00
Miroslav Lichvar
3f6df33feb configure: include SECHASH in chronyc features
chronyc is linked with the crypto library and the keygen command checks
if the specified hash function is known.
2018-03-28 16:57:48 +02:00
Miroslav Lichvar
a94f5fe007 test: extend 126-burst 2018-03-28 16:57:48 +02:00
Miroslav Lichvar
63f0234748 test: fix hash unit test for NSS
Some hash functions in the freebl3 library ignore the length of the
output buffer and always return the length of the digest.
2018-03-28 16:57:48 +02:00
Miroslav Lichvar
47921c7c0c hash: initialize return value before calling NSS hash functions
Some hash functions in the freebl3 library don't support truncated
digests and either return immediately with no update of the output
length, or ignore the length of the output buffer and always write whole
digest.

Initialize the return value to zero to get correct result with the
former.

This is triggered only in the hash unit test. chronyd always provides a
sufficient buffer for the digest.
2018-03-28 16:57:48 +02:00
Miroslav Lichvar
42a85f685e doc: update NEWS 2018-03-15 09:00:47 +01:00
Miroslav Lichvar
feca2399e4 hash: add support for older nettle versions
Use nettle_hashes[] instead of nettle_get_hashes(), which is available
only in nettle >= 3.4. nettle_hashes[] is a symbol available in older
versions and may be renamed in future. In nettle >= 3.4 it is a macro
using nettle_get_hashes() for compatibility.
2018-03-15 09:00:09 +01:00
Miroslav Lichvar
d34e611ec8 doc: update README 2018-03-14 16:14:56 +01:00
Christian Ehrhardt
02098ed830 sys_linux: report if CAP_SYS_TIME is not present
Instead of having adjtimex just fail with a permission issue
improve the error messaging by warning for the lack of
CAP_SYS_TIME on SYS_Linux_Initialise.

Message will look like (instead of only the latter message):
 CAP_SYS_TIME not present
 adjtimex(0x8001) failed : Operation not permitted

Signed-off-by: Christian Ehrhardt <christian.ehrhardt@canonical.com>
2018-03-14 16:14:56 +01:00
Miroslav Lichvar
aa4228bf1b doc: improve description of -x in chronyd man page 2018-03-14 14:59:41 +01:00
Miroslav Lichvar
b296441708 ntp: fix adjustment of init_local_rx timestamp
This fixes commit f0f18a02a7.
2018-03-14 14:59:41 +01:00
Miroslav Lichvar
b827475378 ntp: add debug message to update of link speed 2018-03-14 14:59:41 +01:00
Miroslav Lichvar
78a6698ae1 test: update compilation test to disable nettle 2018-03-13 10:46:57 +01:00
Miroslav Lichvar
e7b6feb34b doc: update supported hash functions in chrony.conf man apge 2018-03-13 10:46:57 +01:00
Miroslav Lichvar
84be834385 hash: add support for nettle 2018-03-13 10:46:57 +01:00
Miroslav Lichvar
e83d808dfd hash: add support for SHA-3 with libtomcrypt 2018-03-13 10:44:24 +01:00
Miroslav Lichvar
35a68d5b59 test: add hash unit test 2018-03-13 10:44:24 +01:00
Miroslav Lichvar
3c593137b0 doc: fix typo in chrony.conf man page 2018-03-12 12:42:05 +01:00
Miroslav Lichvar
deaf0ffed3 ntp: add missing breaks in switch statement
Fortunately, they didn't change the behavior of the code.
2018-03-12 12:42:05 +01:00
Miroslav Lichvar
af145e871e test: use random version in ntp_core unit test 2018-03-07 13:17:55 +01:00
Miroslav Lichvar
fbca570d0b ntp: respond to NTPv1 client requests with zero mode
NTPv1 packets have a reserved field instead of the mode field and the
actual mode is determined from the port numbers. It seems there is still
a large number of clients sending NTPv1 requests with a zero value in
the field (per RFC 1059).

Follow ntpd and respond to the requests with server mode packets.
2018-03-07 11:36:11 +01:00
Miroslav Lichvar
448ef779c2 client: improve handling of unknown responses
Rework the code to not ignore valid packets with unknown or obsolete
responses and return immediately with "bad reply from daemon" instead of
timing out with "cannot talk to daemon".
2018-03-06 13:47:25 +01:00
Miroslav Lichvar
499a69e611 cmdmon: initialize all bytes of reply buffer
Instead of zeroing individual fields, zero all bytes of the buffer
before the reply is formed.

This may have a small impact on performance, but it simplifies the code
and minimizes the risk of leaking uninitialized memory.
2018-03-06 13:47:25 +01:00
Miroslav Lichvar
58c2915878 cmdmon: update protocol changelog 2018-03-06 13:47:25 +01:00
Miroslav Lichvar
eda4b111d3 cmdmon: make length of manual list constant
Make the length of responses containing manual samples constant to
simplify the protocol. It was the only type of response that had a
variable length.

This reverts commit 2343e7a89c.
2018-03-06 13:47:25 +01:00
Miroslav Lichvar
c6dd749687 ntp: check RX and TX timestamp in interleaved client requests
Clients sending packets in the interleaved mode are supposed to use
a different receive and transmit timestamp in order to reliably detect
the mode of the response. If an interleaved request with the receive
timestamp equal to the transmit timestamp is detected, respond in the
basic mode.
2018-03-06 13:47:25 +01:00
Miroslav Lichvar
d2a96f5fbc doc: update README 2018-03-02 14:46:08 +01:00
Miroslav Lichvar
499f513d40 cmdmon: add shutdown command
The command is functionally equivalent to sending the process the
SIGTERM signal.
2018-03-02 13:04:14 +01:00
Miroslav Lichvar
8b1f68b1b4 ntp: delay enabling permanent kernel RX timestamping on Linux
Wait until a kernel RX timestamp is actually missing before opening the
dummy socket in order to avoid a small performance impact in case the
servers are so slow/distant that the kernel can constantly win the race.
2018-03-02 13:03:26 +01:00
Miroslav Lichvar
8e4c776900 test: add 126-burst test 2018-02-28 10:09:47 +01:00
Miroslav Lichvar
d0eb9427c2 ntp: add burst option
When the burst option is specified in the server/pool directive and the
current poll is longer than the minimum poll, initiate on each poll a
burst with 1 good sample and 2 or 4 total samples according to the
difference between the current and minimum poll.
2018-02-28 10:09:47 +01:00
Miroslav Lichvar
7d100b89fc doc: improve description of server options 2018-02-28 10:08:58 +01:00
Miroslav Lichvar
a4bd7f1800 test: make 119-smoothtime more reliable 2018-02-26 17:24:08 +01:00
Miroslav Lichvar
5308e0a25f sources: include maxclockerror in source selection
In the source selection algorithm, include extra dispersion due to
maxclockerror in the root distance of sources that don't have new
samples (the last sample is older than span of all samples) to not
prefer unreachable sources with a short distance and small skew over
reachable sources for too long, and also to decrease their chances of
becoming falsetickers.
2018-02-26 13:42:04 +01:00
Miroslav Lichvar
da862158bf main: open /dev/null as stdin/out/err in daemonization
chronyd doesn't normally write anything to stdout or stderr when running
as a daemon, but it is a good practice to replace them with descriptors
of /dev/null to prevent accidental writes to other files or sockets that
would otherwise take their place.
2018-02-26 13:42:04 +01:00
Miroslav Lichvar
7b98443a13 logging: don't write fatal messages to invalid descriptor
If opening the log file specified with the -l option failed (after
closing all descriptors), the error message is written to an invalid
descriptor as no log file or syslog is opened yet. Fix the code to track
when the output is usable.
2018-02-26 13:42:04 +01:00
Miroslav Lichvar
4da9f74d24 util: replace assert for missing MD5 with fatal log message
Apparently, on some systems the MD5 function is missing with the NSS
support (freebl3). Instead of failing an assertion, exit with a log
message.
2018-02-26 13:42:04 +01:00
Miroslav Lichvar
e41042e258 test: update util unit test 2018-02-26 13:42:04 +01:00
Miroslav Lichvar
5581466c63 test: improve and extend ntp_core unit test 2018-02-26 13:42:04 +01:00
Miroslav Lichvar
e79a6c2116 sourcestats: limit minimum value of std_dev 2018-02-26 13:42:04 +01:00
Miroslav Lichvar
666ece122e ntp: compare receive timestamp when checking for duplicate
Compare both receive and transmit timestamps in the NTP test number 1.

This prevents a client from dropping a valid response in the interleaved
mode if it follows a response in the basic mode and the server did not
have a kernel/hardware transmit timestamp, and the random bits of the
two timestamps happen to be the same (chance of 1 in 2^(32-precision)).
2018-02-26 13:42:04 +01:00
Miroslav Lichvar
2c7ab98370 ntp: don't send packets with RX/TX timestamp equal to another timestamp
Before sending a new packet, check if the receive/transmit timestamp
is not equal to the origin timestamp or the previous receive/transmit
timestamp in order to prevent the packet from being its own valid
response (in the symmetric mode) and invalidate responses to the
previous packet.

This improves protection against replay attacks in the symmetric mode.
2018-02-26 13:42:04 +01:00
Miroslav Lichvar
f0f18a02a7 ntp: separate timestamps for restarting symmetric protocol
Save the local receive and remote transmit timestamp needed for
(re)starting the symmetric protocol when no valid reply was received
separately from the timestamps that are used for synchronization of the
local clock.

This extends the interval in which the local NTP state is (partially)
protected against replay attacks in order to complete a measurement
in the interleaved symmetric mode from [last valid RX, next TX] to
[last TX, next TX], i.e. it should be the same as in the basic mode.
2018-02-26 13:42:04 +01:00
Miroslav Lichvar
c5d8af0285 main: create directories before refclock initialization
This allows the SOCK refclock to open sockets in the /var/run/chrony
directory.
2018-02-16 11:09:54 +01:00
Miroslav Lichvar
0ce15a8472 main: improve error message for failed getpwnam() 2018-02-16 11:09:54 +01:00
Miroslav Lichvar
da60629201 configure: improve check for timestamping options
The socket.h header provided by musl doesn't seem to include the kernel
headers and is missing SCM_TIMESTAMPING_PKTINFO, which causes the
Linux-specific code in chrony to fail to build.
2018-02-16 11:09:54 +01:00
Miroslav Lichvar
2343e7a89c pktlength: handle truncated MANUAL_LIST reply
Before reading the n_samples field of the MANUAL_LIST reply, check if it
is actually contained in the received message. This does not change the
outcome of the client's length check as the returned length was always
larger than the length of the truncated reply and it was dropped anyway,
but it prevents the client from reading uninitialized memory.
2018-02-16 11:09:54 +01:00
Miroslav Lichvar
45f27f4f5e sourcestats: reset instance before loading dump file
Don't rely on the caller to reset the instance and always reset it
before loading data to make sure it can't get to an unexpected state.
2018-02-16 11:09:54 +01:00
Miroslav Lichvar
0bc112f8b4 doc: improve description of refclock tai option
Emphasize that tzdata must be kept up to date in order for the
correction to work as expected.
2018-02-16 11:09:54 +01:00
Miroslav Lichvar
bfc2fa645c test: add 125-packetloss test 2018-02-16 11:09:54 +01:00
Miroslav Lichvar
11111804fd test: extend 106-refclock 2018-02-16 11:09:54 +01:00
Miroslav Lichvar
87ec67247e test: extend 110-chronyc 2018-02-16 11:09:54 +01:00
Miroslav Lichvar
0df8328ceb ntp: keep kernel RX timestamping permanently enabled on Linux
The Linux kernel has a counter for sockets using kernel RX timestamping
and timestamps (all) received packets only when it is not zero. However,
this counter is updated asynchronously from setsockopt(). If there are
currently no other sockets using the timestamping, it is possible that a
fast server response is received before the kernel timestamping is
actually enabled after setting the socket option and sending a request.

Open a dummy socket on start to make sure there is always at least one
timestamping socket to avoid the race condition.
2018-02-16 11:09:54 +01:00
Miroslav Lichvar
b563048ee2 examples: ignore non-up/down events in nm-dispatcher script 2018-02-16 11:09:54 +01:00
Miroslav Lichvar
e8096330be sys_linux: don't keep CAP_SYS_TIME with -x option
When dropping the root privileges, don't try to keep the CAP_SYS_TIME
capability if the -x option was enabled. This allows chronyd to be
started without the capability (e.g. in containers) and also drop the
root privileges.
2018-02-05 14:05:19 +01:00
Miroslav Lichvar
b1647dbcb7 ntp: wait for late HW TX timestamps
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, 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.
2018-02-02 11:36:38 +01:00
Miroslav Lichvar
4ddadd5622 ntp: don't request TX timestamp when SW/HW timestamping is disabled 2018-02-01 17:27:45 +01:00
Miroslav Lichvar
3e854006c7 ntp: add missing header guard 2018-01-31 17:23:40 +01:00
Miroslav Lichvar
2c4c235147 sched: allow enabling/disabling individual file handler events 2018-01-30 15:56:51 +01:00
Miroslav Lichvar
6863e43269 client: avoid reading clock after sending request
If chronyc sent a request which caused chronyd to step the clock (e.g.
makestep, settime) and the second reading of the clock before calling
select() to wait for a response happened after the clock was stepped, a
new request could be sent immediately and chronyd would process the same
command twice. If the second request failed (e.g. a settime request too
close to the first request), chronyc would report an error.

Change the submit_request() function to read the clock only once per
select() to wait for the first response even when the clock was stepped.
2017-12-12 11:37:36 +01:00
Miroslav Lichvar
de8708f331 client: remove unused file descriptor sets 2017-12-05 12:13:40 +01:00
Miroslav Lichvar
d0b2486036 client: don't call select() with invalid timeout
If the system clock was stepped forward after chronyc sent a request and
before it read the clock in order to calculate the receive timeout,
select() could be called with a negative timeout, which resulted in an
infinite loop waiting for select() to succeed.

Fix the submit_request() function to not call select() with a negative
timeout. Also, return immediately on any error of select().
2017-12-05 12:13:37 +01:00
Miroslav Lichvar
5384a93645 test: extend util unit test 2017-12-05 10:14:19 +01:00
Miroslav Lichvar
4bbc768652 util: avoid casting to long in UTI_DoubleToTimeval() 2017-12-05 09:44:59 +01:00
Vincent Blut
fead915b45 doc: fix typo in chronyd man page 2017-12-04 11:16:20 +01:00
Miroslav Lichvar
5422e49026 doc: improve leapsectz description 2017-10-12 14:07:12 +02:00
Miroslav Lichvar
77a1f27a1d test: add 124-tai test 2017-10-11 17:49:30 +02:00
Miroslav Lichvar
b45d864f73 test: check for maxchange message in check_chronyd_exit() 2017-10-11 17:49:21 +02:00
Miroslav Lichvar
f35c81c871 refclock: improve TAI-UTC conversion
Instead of using the TAI-UTC offset which corresponds to the current
system time, get the offset for the reference time. This allows the
clock to be accurately stepped from a time with different TAI-UTC
offset.
2017-10-11 17:45:21 +02:00
Miroslav Lichvar
a349b2803c refclock: remove unnecessary return statements 2017-10-11 17:28:34 +02:00
Chris Perl
f5d1b8fb74 refclock: add tai option
This option is for indicating to chronyd that the reference clock is
kept in TAI and that chrony should attempt to convert from TAI to UTC by
using the timezone configured by the "leapsectz" directive.
2017-10-11 17:28:34 +02:00
Chris Perl
a0fe71eef1 reference: add function to get TAI-UTC offset 2017-10-11 17:28:34 +02:00
Andreas Steinmetz
154b39cf7a refclock: add stratum option 2017-10-09 10:39:20 +02:00
Bernhard M. Wiedemann
6f54210db2 configure: allow to override build date
in order to make builds reproducible.
See https://reproducible-builds.org/ for why this is good
and https://reproducible-builds.org/specs/source-date-epoch/
for the definition of this variable.
2017-10-05 18:18:07 +02:00
Miroslav Lichvar
f6539449c5 nameserv: set hints for getaddrinfo() according to -4/-6 option
Avoid sending unnecessary DNS requests when the -4/-6 option is
specified.
2017-10-04 11:20:10 +02:00
Miroslav Lichvar
b8d546a0d1 examples: add leapsectz to configuration examples 2017-09-15 08:32:09 +02:00
Miroslav Lichvar
04e6474b75 reference: check for gmtime() error
Although gmtime() is expected to convert any time of the system clock at
least in the next few NTP eras, a correct code should always check the
returned value and this shouldn't be a fatal error in handling of leap
seconds.
2017-09-15 08:32:08 +02:00
Vincent Blut
eb51c500e8 doc: fix typo in chrony.conf man page 2017-09-11 11:21:13 +02:00
Miroslav Lichvar
6f8fba9a3f conf: check if GLOB_NOMAGIC is defined
This option is not supported by musl and possibly other libc
implementations.
2017-09-01 11:32:16 +02:00
Miroslav Lichvar
750afc30f2 test: fix keys unit test 2017-09-01 11:28:55 +02:00
58 changed files with 1507 additions and 359 deletions

25
NEWS
View File

@@ -1,3 +1,28 @@
New in version 3.3
==================
Enhancements
------------
* Add burst option to server/pool directive
* Add stratum and tai options to refclock directive
* Add support for Nettle crypto library
* Add workaround for missing kernel receive timestamps on Linux
* Wait for late hardware transmit timestamps
* Improve source selection with unreachable sources
* Improve protection against replay attacks on symmetric mode
* Allow PHC refclock to use socket in /var/run/chrony
* Add shutdown command to stop chronyd
* Simplify format of response to manual list command
* Improve handling of unknown responses in chronyc
Bug fixes
---------
* Respond to NTPv1 client requests with zero mode
* Fix -x option to not require CAP_SYS_TIME under non-root user
* Fix acquisitionport directive to work with privilege separation
* Fix handling of socket errors on Linux to avoid high CPU usage
* Fix chronyc to not get stuck in infinite loop after clock step
New in version 3.2
==================

45
README
View File

@@ -37,20 +37,16 @@ How do I set it up?
===================
The file INSTALL gives instructions. On supported systems the
compilation process should be automatic.
You will need an ANSI C compiler -- gcc is recommended.
The manual (in texinfo and text formats) describes how to set the
software up for the less straightforward cases.
compilation process should be automatic. You will need a C compiler,
e.g. gcc or clang.
What documentation is there?
============================
A manual is supplied in Texinfo format (chrony.texi) and
ready-formatted plain text (chrony.txt) in the distribution.
The distribution includes manual pages and a document containing
Frequently Asked Questions (FAQ).
There is also information available on the chrony web pages, accessible
The documentation is also available on the chrony web pages, accessible
through the URL
https://chrony.tuxfamily.org/
@@ -126,7 +122,7 @@ Andrew Bishop <amb@gedanken.demon.co.uk>
Improvements to 'sources' and 'sourcestats' output from chronyc
Improvements to documentation
Investigation of required dosynctodr behaviour for various Solaris
versions.
versions
Stephan I. Boettcher <stephan@nevis1.columbia.edu>
Entries in contrib directory
@@ -140,27 +136,27 @@ Bryan Christianson <bryan@whatroute.net>
Entries in contrib directory
Juliusz Chroboczek <jch@pps.jussieu.fr>
Fix install rule in Makefile if chronyd file is in use.
Patch to fix install rule in Makefile if chronyd file is in use
Christian Ehrhardt <christian.ehrhardt@canonical.com>
Patch to generate a warning message when CAP_SYS_TIME is missing
Paul Elliott <pelliott@io.com>
DNSchrony (in contrib directory), a tool for handling NTP servers
with variable IP addresses.
Entries in contrib directory
Mike Fleetwood <mike@rockover.demon.co.uk>
Fixes for compiler warnings
Alexander Gretencord <arutha@gmx.de>
Changes to installation directory system to make it easier for
package builders.
package builders
Andrew Griffiths <agriffit@redhat.com>
Patch to add support for seccomp filter
Walter Haidinger <walter.haidinger@gmx.at>
Providing me with login access to a Linux installation where v1.12
wouldn't compile, so I could develop the fixes for v1.13. Also, for
providing the disc space so I can keep an independent backup of the
sources.
Access to a Linux installation where v1.12 wouldn't compile
Disc space for an independent backup of the sources
Juergen Hannken-Illjes <hannken@eis.cs.tu-bs.de>
Port to NetBSD
@@ -170,7 +166,7 @@ John Hasler <john@dhh.gt.org>
Changes to support 64 bit machines (i.e. those where
sizeof(unsigned long) > 4)
Bug fix to initstepslew directive
Fix to remove potential buffer overrun errors.
Fix to remove potential buffer overrun errors
Memory locking and real-time scheduler support
Fix fault where chronyd enters an endless loop
@@ -198,7 +194,7 @@ Victor Moroz <vim@prv.adlum.ru>
Patch to support Linux with HZ!=100
Kalle Olavi Niemitalo <tosi@stekt.oulu.fi>
acquisitionport support
Patch to add acquisitionport directive
Frank Otto <sandwichmacher@web.de>
Handling arbitrary HZ values
@@ -206,12 +202,18 @@ Frank Otto <sandwichmacher@web.de>
Denny Page <dennypage@me.com>
Advice on support for hardware timestamping
Chris Perl <cperl@janestreet.com>
Patches to improve support for refclocks keeping time in TAI
Gautier PHILIPPON <gautier.philippon@ensimag.grenoble-inp.fr>
Patch to add refresh command to chronyc
Andreas Piesk <apiesk@virbus.de>
Patch to make chronyc use the readline library if available
Andreas Steinmetz <ast@domdv.de>
Patch to make stratum of refclocks configurable
Timo Teras <timo.teras@iki.fi>
Patch to reply correctly on multihomed hosts
@@ -228,8 +230,7 @@ Ralf Wildenhues <Ralf.Wildenhues@gmx.de>
Many robustness and security improvements
Ulrich Windl <ulrich.windl@rz.uni-regensburg.de> for the
Providing me with information about the Linux 2.2 kernel
functionality compared to 2.0.
Information about the Linux 2.2 kernel functionality compared to 2.0
Doug Woodward <dougw@whistler.com>
Advice on configuring for Solaris 2.8 on x86

13
candm.h
View File

@@ -99,7 +99,8 @@
#define REQ_ADD_PEER2 59
#define REQ_ADD_SERVER3 60
#define REQ_ADD_PEER3 61
#define N_REQUEST_TYPES 62
#define REQ_SHUTDOWN 62
#define N_REQUEST_TYPES 63
/* Structure used to exchange timespecs independent of time_t size */
typedef struct {
@@ -252,6 +253,7 @@ typedef struct {
#define REQ_ADDSRC_TRUST 0x20
#define REQ_ADDSRC_REQUIRE 0x40
#define REQ_ADDSRC_INTERLEAVED 0x80
#define REQ_ADDSRC_BURST 0x100
typedef struct {
IPAddr ip_addr;
@@ -367,9 +369,9 @@ typedef struct {
domain socket.
Version 6 (no authentication) : changed format of client accesses by index
(using new request/reply types) and manual timestamp, new fields and flags
in NTP source request and report, new commands: ntpdata, refresh,
serverstats
(using new request/reply types) and manual timestamp, added new fields and
flags to NTP source request and report, made length of manual list constant,
added new commands: ntpdata, refresh, serverstats, shutdown
*/
#define PROTO_VERSION_NUMBER 6
@@ -468,7 +470,8 @@ typedef struct {
#define RPY_CLIENT_ACCESSES_BY_INDEX2 15
#define RPY_NTP_DATA 16
#define RPY_MANUAL_TIMESTAMP2 17
#define N_REPLY_TYPES 18
#define RPY_MANUAL_LIST2 18
#define N_REPLY_TYPES 19
/* Status codes */
#define STT_SUCCESS 0

104
client.c
View File

@@ -4,7 +4,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Lonnie Abelbeck 2016
* Copyright (C) Miroslav Lichvar 2009-2017
* Copyright (C) Miroslav Lichvar 2009-2018
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -1109,6 +1109,7 @@ process_cmd_add_server_or_peer(CMD_Request *msg, char *line)
(data.params.auto_offline ? REQ_ADDSRC_AUTOOFFLINE : 0) |
(data.params.iburst ? REQ_ADDSRC_IBURST : 0) |
(data.params.interleaved ? REQ_ADDSRC_INTERLEAVED : 0) |
(data.params.burst ? REQ_ADDSRC_BURST : 0) |
(data.params.sel_options & SRC_SELECT_PREFER ? REQ_ADDSRC_PREFER : 0) |
(data.params.sel_options & SRC_SELECT_NOSELECT ? REQ_ADDSRC_NOSELECT : 0) |
(data.params.sel_options & SRC_SELECT_TRUST ? REQ_ADDSRC_TRUST : 0) |
@@ -1245,6 +1246,7 @@ give_help(void)
"cyclelogs\0Close and re-open log files\0"
"dump\0Dump all measurements to save files\0"
"rekey\0Re-read keys from key file\0"
"shutdown\0Stop daemon\0"
"\0\0"
"Client commands:\0\0"
"dns -n|+n\0Disable/enable resolving IP addresses to hostnames\0"
@@ -1279,9 +1281,9 @@ command_name_generator(const char *text, int state)
"maxdelay", "maxdelaydevratio", "maxdelayratio", "maxpoll",
"maxupdateskew", "minpoll", "minstratum", "ntpdata", "offline", "online",
"polltarget", "quit", "refresh", "rekey", "reselect", "reselectdist",
"retries", "rtcdata", "serverstats", "settime", "smoothing", "smoothtime",
"sources", "sources -v", "sourcestats", "sourcestats -v", "timeout",
"tracking", "trimrtc", "waitsync", "writertc",
"retries", "rtcdata", "serverstats", "settime", "shutdown", "smoothing",
"smoothtime", "sources", "sources -v", "sourcestats", "sourcestats -v",
"timeout", "tracking", "trimrtc", "waitsync", "writertc",
NULL
};
static int list_index, len;
@@ -1324,18 +1326,16 @@ static int proto_version = PROTO_VERSION_NUMBER;
static int
submit_request(CMD_Request *request, CMD_Reply *reply)
{
int bad_length, bad_sequence, bad_header;
int select_status;
int recv_status;
int read_length;
int expected_length;
int command_length;
int padding_length;
struct timespec ts_now, ts_start;
struct timeval tv;
int n_attempts, new_attempt;
double timeout;
fd_set rdfd, wrfd, exfd;
fd_set rdfd;
request->pkt_type = PKT_TYPE_CMD_REQUEST;
request->res1 = 0;
@@ -1347,15 +1347,15 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
new_attempt = 1;
do {
if (gettimeofday(&tv, NULL))
return 0;
if (new_attempt) {
new_attempt = 0;
if (n_attempts > max_retries)
return 0;
if (gettimeofday(&tv, NULL))
return 0;
UTI_TimevalToTimespec(&tv, &ts_start);
UTI_GetRandomBytes(&request->sequence, sizeof (request->sequence));
@@ -1383,9 +1383,6 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
DEBUG_LOG("Sent %d bytes", command_length);
}
if (gettimeofday(&tv, NULL))
return 0;
UTI_TimevalToTimespec(&tv, &ts_now);
/* Check if the clock wasn't stepped back */
@@ -1394,22 +1391,27 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
timeout = initial_timeout / 1000.0 * (1U << (n_attempts - 1)) -
UTI_DiffTimespecsToDouble(&ts_now, &ts_start);
UTI_DoubleToTimeval(timeout, &tv);
DEBUG_LOG("Timeout %f seconds", timeout);
FD_ZERO(&rdfd);
FD_ZERO(&wrfd);
FD_ZERO(&exfd);
/* Avoid calling select() with an invalid timeout */
if (timeout <= 0.0) {
new_attempt = 1;
continue;
}
UTI_DoubleToTimeval(timeout, &tv);
FD_ZERO(&rdfd);
FD_SET(sock_fd, &rdfd);
if (quit)
return 0;
select_status = select(sock_fd + 1, &rdfd, &wrfd, &exfd, &tv);
select_status = select(sock_fd + 1, &rdfd, NULL, NULL, &tv);
if (select_status < 0) {
DEBUG_LOG("select failed : %s", strerror(errno));
return 0;
} else if (select_status == 0) {
/* Timeout must have elapsed, try a resend? */
new_attempt = 1;
@@ -1425,34 +1427,18 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
DEBUG_LOG("Received %d bytes", recv_status);
read_length = recv_status;
if (read_length >= offsetof(CMD_Reply, data)) {
expected_length = PKL_ReplyLength(reply);
} else {
expected_length = 0;
}
bad_length = (read_length < expected_length ||
expected_length < offsetof(CMD_Reply, data));
if (!bad_length) {
bad_sequence = reply->sequence != request->sequence;
} else {
bad_sequence = 0;
}
if (bad_length || bad_sequence) {
continue;
}
bad_header = ((reply->version != proto_version &&
!(reply->version >= PROTO_VERSION_MISMATCH_COMPAT_CLIENT &&
ntohs(reply->status) == STT_BADPKTVERSION)) ||
(reply->pkt_type != PKT_TYPE_CMD_REPLY) ||
(reply->res1 != 0) ||
(reply->res2 != 0) ||
(reply->command != request->command));
if (bad_header) {
/* Check if the header is valid */
if (read_length < offsetof(CMD_Reply, data) ||
(reply->version != proto_version &&
!(reply->version >= PROTO_VERSION_MISMATCH_COMPAT_CLIENT &&
ntohs(reply->status) == STT_BADPKTVERSION)) ||
reply->pkt_type != PKT_TYPE_CMD_REPLY ||
reply->res1 != 0 ||
reply->res2 != 0 ||
reply->command != request->command ||
reply->sequence != request->sequence) {
DEBUG_LOG("Invalid reply");
continue;
}
@@ -1471,6 +1457,15 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
#error unknown compatibility with PROTO_VERSION - 1
#endif
/* Check that the packet contains all data it is supposed to have.
Unknown responses will always pass this test as their expected
length is zero. */
if (read_length < PKL_ReplyLength(reply)) {
DEBUG_LOG("Reply too short");
new_attempt = 1;
continue;
}
/* Good packet received, print out results */
DEBUG_LOG("Reply cmd=%d reply=%d stat=%d",
ntohs(reply->command), ntohs(reply->reply), ntohs(reply->status));
@@ -1577,6 +1572,9 @@ request_reply(CMD_Request *request, CMD_Reply *reply, int requested_reply, int v
return 0;
}
/* Make sure an unknown response was not requested */
assert(PKL_ReplyLength(reply));
return 1;
}
@@ -2540,7 +2538,7 @@ process_cmd_manual_list(const char *line)
struct timespec when;
request.command = htons(REQ_MANUAL_LIST);
if (!request_reply(&request, &reply, RPY_MANUAL_LIST, 0))
if (!request_reply(&request, &reply, RPY_MANUAL_LIST2, 0))
return 0;
n_samples = ntohl(reply.data.manual_list.n_samples);
@@ -2548,7 +2546,7 @@ process_cmd_manual_list(const char *line)
print_header("# Date Time(UTC) Slewed Original Residual");
for (i = 0; i < n_samples; i++) {
for (i = 0; i < n_samples && i < MAX_MANUAL_LIST_SAMPLES; i++) {
sample = &reply.data.manual_list.samples[i];
UTI_TimespecNetworkToHost(&sample->when, &when);
@@ -2709,6 +2707,14 @@ process_cmd_refresh(CMD_Request *msg, char *line)
/* ================================================== */
static void
process_cmd_shutdown(CMD_Request *msg, char *line)
{
msg->command = htons(REQ_SHUTDOWN);
}
/* ================================================== */
static int
process_cmd_waitsync(char *line)
{
@@ -3004,6 +3010,8 @@ process_line(char *line)
} else if (!strcmp(command, "settime")) {
do_normal_submit = 0;
ret = process_cmd_settime(line);
} else if (!strcmp(command, "shutdown")) {
process_cmd_shutdown(&tx_message, line);
} else if (!strcmp(command, "smoothing")) {
do_normal_submit = 0;
ret = process_cmd_smoothing(line);
@@ -3098,7 +3106,7 @@ static void
display_gpl(void)
{
printf("chrony version %s\n"
"Copyright (C) 1997-2003, 2007, 2009-2017 Richard P. Curnow and others\n"
"Copyright (C) 1997-2003, 2007, 2009-2018 Richard P. Curnow and others\n"
"chrony comes with ABSOLUTELY NO WARRANTY. This is free software, and\n"
"you are welcome to redistribute it under certain conditions. See the\n"
"GNU General Public License version 2 for details.\n\n",

View File

@@ -138,6 +138,7 @@ static const char permissions[] = {
PERMIT_AUTH, /* ADD_PEER2 */
PERMIT_AUTH, /* ADD_SERVER3 */
PERMIT_AUTH, /* ADD_PEER3 */
PERMIT_AUTH, /* SHUTDOWN */
};
/* ================================================== */
@@ -278,7 +279,6 @@ do_size_checks(void)
for (i = 1; i < N_REPLY_TYPES; i++) {
reply.reply = htons(i);
reply.status = STT_SUCCESS;
reply.data.manual_list.n_samples = htonl(MAX_MANUAL_LIST_SAMPLES);
reply_length = PKL_ReplyLength(&reply);
if ((reply_length && reply_length < offsetof(CMD_Reply, data)) ||
reply_length > sizeof (CMD_Reply))
@@ -801,6 +801,7 @@ handle_add_source(NTP_Source_Type type, CMD_Request *rx_message, CMD_Reply *tx_m
params.auto_offline = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_AUTOOFFLINE ? 1 : 0;
params.iburst = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_IBURST ? 1 : 0;
params.interleaved = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_INTERLEAVED ? 1 : 0;
params.burst = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_BURST ? 1 : 0;
params.sel_options =
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_PREFER ? SRC_SELECT_PREFER : 0) |
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_NOSELECT ? SRC_SELECT_NOSELECT : 0) |
@@ -1068,9 +1069,6 @@ handle_client_accesses_by_index(CMD_Request *rx_message, CMD_Reply *tx_message)
tx_message->reply = htons(RPY_CLIENT_ACCESSES_BY_INDEX2);
tx_message->data.client_accesses_by_index.n_indices = htonl(n_indices);
memset(tx_message->data.client_accesses_by_index.clients, 0,
sizeof (tx_message->data.client_accesses_by_index.clients));
for (i = req_first_index, j = 0; i < (uint32_t)n_indices && j < req_n_clients; i++) {
if (!CLG_GetClientAccessReportByIndex(i, &report, &now))
continue;
@@ -1103,10 +1101,11 @@ handle_manual_list(CMD_Request *rx_message, CMD_Reply *tx_message)
RPY_ManualListSample *sample;
RPT_ManualSamplesReport report[MAX_MANUAL_LIST_SAMPLES];
tx_message->reply = htons(RPY_MANUAL_LIST);
tx_message->reply = htons(RPY_MANUAL_LIST2);
MNL_ReportSamples(report, MAX_MANUAL_LIST_SAMPLES, &n_samples);
tx_message->data.manual_list.n_samples = htonl(n_samples);
for (i=0; i<n_samples; i++) {
sample = &tx_message->data.manual_list.samples[i];
UTI_TimespecHostToNetwork(&report[i].when, &sample->when);
@@ -1238,6 +1237,15 @@ handle_ntp_data(CMD_Request *rx_message, CMD_Reply *tx_message)
memset(tx_message->data.ntp_data.reserved, 0xff, sizeof (tx_message->data.ntp_data.reserved));
}
/* ================================================== */
static void
handle_shutdown(CMD_Request *rx_message, CMD_Reply *tx_message)
{
LOG(LOGS_INFO, "Received shutdown command");
SCH_QuitProgram();
}
/* ================================================== */
/* Read a packet and process it */
@@ -1329,19 +1337,14 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
expected_length = PKL_CommandLength(&rx_message);
rx_command = ntohs(rx_message.command);
memset(&tx_message, 0, sizeof (tx_message));
tx_message.version = PROTO_VERSION_NUMBER;
tx_message.pkt_type = PKT_TYPE_CMD_REPLY;
tx_message.res1 = 0;
tx_message.res2 = 0;
tx_message.command = rx_message.command;
tx_message.reply = htons(RPY_NULL);
tx_message.status = htons(STT_SUCCESS);
tx_message.pad1 = 0;
tx_message.pad2 = 0;
tx_message.pad3 = 0;
tx_message.sequence = rx_message.sequence;
tx_message.pad4 = 0;
tx_message.pad5 = 0;
if (rx_message.version != PROTO_VERSION_NUMBER) {
DEBUG_LOG("Command packet has invalid version (%d != %d)",
@@ -1629,6 +1632,10 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
handle_ntp_data(&rx_message, &tx_message);
break;
case REQ_SHUTDOWN:
handle_shutdown(&rx_message, &tx_message);
break;
default:
DEBUG_LOG("Unhandled command %d", rx_command);
tx_message.status = htons(STT_FAILED);

View File

@@ -51,6 +51,7 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
src->params.online = 1;
src->params.auto_offline = 0;
src->params.presend_minpoll = SRC_DEFAULT_PRESEND_MINPOLL;
src->params.burst = 0;
src->params.iburst = 0;
src->params.min_stratum = SRC_DEFAULT_MINSTRATUM;
src->params.poll_target = SRC_DEFAULT_POLLTARGET;
@@ -84,6 +85,8 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
if (!strcasecmp(cmd, "auto_offline")) {
src->params.auto_offline = 1;
} else if (!strcasecmp(cmd, "burst")) {
src->params.burst = 1;
} else if (!strcasecmp(cmd, "iburst")) {
src->params.iburst = 1;
} else if (!strcasecmp(cmd, "offline")) {

19
conf.c
View File

@@ -681,7 +681,7 @@ static void
parse_refclock(char *line)
{
int n, poll, dpoll, filter_length, pps_rate, min_samples, max_samples, sel_options;
int max_lock_age, pps_forced;
int max_lock_age, pps_forced, stratum, tai;
uint32_t ref_id, lock_ref_id;
double offset, delay, precision, max_dispersion, pulse_width;
char *p, *cmd, *name, *param;
@@ -704,6 +704,8 @@ parse_refclock(char *line)
ref_id = 0;
max_lock_age = 2;
lock_ref_id = 0;
stratum = 0;
tai = 0;
if (!*line) {
command_parse_error();
@@ -774,6 +776,13 @@ parse_refclock(char *line)
} else if (!strcasecmp(cmd, "maxdispersion")) {
if (sscanf(line, "%lf%n", &max_dispersion, &n) != 1)
break;
} else if (!strcasecmp(cmd, "stratum")) {
if (sscanf(line, "%d%n", &stratum, &n) != 1 ||
stratum >= NTP_MAX_STRATUM || stratum < 0)
break;
} else if (!strcasecmp(cmd, "tai")) {
n = 0;
tai = 1;
} else if (!strcasecmp(cmd, "width")) {
if (sscanf(line, "%lf%n", &pulse_width, &n) != 1)
break;
@@ -811,6 +820,8 @@ parse_refclock(char *line)
refclock->min_samples = min_samples;
refclock->max_samples = max_samples;
refclock->sel_options = sel_options;
refclock->stratum = stratum;
refclock->tai = tai;
refclock->offset = offset;
refclock->delay = delay;
refclock->precision = precision;
@@ -1335,7 +1346,11 @@ parse_include(char *line)
check_number_of_args(line, 1);
if ((r = glob(line, GLOB_ERR | GLOB_NOMAGIC, NULL, &gl)) != 0) {
if ((r = glob(line,
#ifdef GLOB_NOMAGIC
GLOB_NOMAGIC |
#endif
GLOB_ERR, NULL, &gl)) != 0) {
if (r != GLOB_NOMATCH)
LOG_FATAL("Could not search for files matching %s", line);

35
configure vendored
View File

@@ -5,7 +5,7 @@
#
# Copyright (C) Richard P. Curnow 1997-2003
# Copyright (C) Bryan Christianson 2016
# Copyright (C) Miroslav Lichvar 2009, 2012-2016
# Copyright (C) Miroslav Lichvar 2009, 2012-2018
#
# =======================================================================
@@ -85,6 +85,7 @@ For better control, use the options below.
--with-readline-library=DIR Specify where readline lib directory is
--with-ncurses-library=DIR Specify where ncurses lib directory is
--disable-sechash Disable support for hashes other than MD5
--without-nettle Don't use nettle even if it is available
--without-nss Don't use NSS even if it is available
--without-tomcrypt Don't use libtomcrypt even if it is available
--disable-cmdmon Disable command and monitoring support
@@ -198,6 +199,7 @@ feat_readline=1
try_readline=1
try_editline=1
feat_sechash=1
try_nettle=1
try_nss=1
try_tomcrypt=1
feat_rtc=1
@@ -360,6 +362,9 @@ do
--disable-sechash )
feat_sechash=0
;;
--without-nettle )
try_nettle=0
;;
--without-nss )
try_nss=0
;;
@@ -550,7 +555,11 @@ then
split_seconds=$ntp_era_split
split_days=0
else
split_seconds=`date '+%s'`
if [ "x$SOURCE_DATE_EPOCH" != "x" ]; then
split_seconds=$SOURCE_DATE_EPOCH
else
split_seconds=`date '+%s'`
fi
if [ "x$split_seconds" = "x" ]; then
echo "error: could not get current time, --with-ntp-era option is needed"
exit 1
@@ -696,6 +705,7 @@ then
struct scm_ts_pktinfo pktinfo;
pktinfo.if_index = pktinfo.pkt_length = 0;
return pktinfo.if_index + pktinfo.pkt_length + HWTSTAMP_FILTER_NTP_ALL +
SCM_TIMESTAMPING_PKTINFO +
SOF_TIMESTAMPING_OPT_PKTINFO + SOF_TIMESTAMPING_OPT_TX_SWHW;'; then
add_def HAVE_LINUX_TIMESTAMPING_RXFILTER_NTP 1
add_def HAVE_LINUX_TIMESTAMPING_OPT_PKTINFO 1
@@ -852,7 +862,22 @@ fi
HASH_OBJ="hash_intmd5.o"
HASH_LINK=""
if [ $feat_sechash = "1" ] && [ $try_nss = "1" ]; then
if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_nettle = "1" ]; then
test_cflags="`pkg_config --cflags nettle`"
test_link="`pkg_config --libs nettle`"
if test_code 'nettle' 'nettle/nettle-meta.h nettle/sha2.h' \
"$test_cflags" "$test_link" \
'return nettle_hashes[0]->context_size;'
then
HASH_OBJ="hash_nettle.o"
HASH_LINK="$test_link"
LIBS="$LIBS $HASH_LINK"
MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
add_def FEAT_SECHASH
fi
fi
if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_nss = "1" ]; then
test_cflags="`pkg_config --cflags nss`"
test_link="`pkg_config --libs-only-L nss` -lfreebl3"
if test_code 'NSS' 'nss.h hasht.h nsslowhash.h' \
@@ -942,9 +967,9 @@ add_def DEFAULT_USER "\"$default_user\""
add_def DEFAULT_COMMAND_SOCKET "\"$CHRONYRUNDIR/chronyd.sock\""
add_def MAIL_PROGRAM "\"$mail_program\""
common_features="`get_features IPV6 DEBUG`"
common_features="`get_features SECHASH IPV6 DEBUG`"
chronyc_features="`get_features READLINE`"
chronyd_features="`get_features CMDMON NTP REFCLOCK RTC PRIVDROP SCFILTER SECHASH SIGND ASYNCDNS`"
chronyd_features="`get_features CMDMON NTP REFCLOCK RTC PRIVDROP SCFILTER SIGND ASYNCDNS`"
add_def CHRONYC_FEATURES "\"$chronyc_features $common_features\""
add_def CHRONYD_FEATURES "\"$chronyd_features $common_features\""
echo "Features : $chronyd_features $chronyc_features $common_features"

View File

@@ -66,34 +66,44 @@ server, or its IP address. The *server* directive supports the following
options:
+
*minpoll* _poll_:::
Although *chronyd* will trim the rate at which it samples the server during
normal operation, the user might want to constrain the minimum polling interval.
This is always defined as a power of 2, so *minpoll 5* would mean that the
polling interval cannot drop below 32 seconds. The default is 6 (64 seconds),
the minimum is -4 (1/16th of a second), and the maximum is 24 (6 months). Note
that intervals shorter than 6 (64 seconds) should generally not be used with
public servers on the Internet, because it might be considered abuse.
This option specifies the minimum interval between requests sent to the server
as a power of 2 in seconds. For example, *minpoll 5* would mean that the
polling interval should not drop below 32 seconds. The default is 6 (64
seconds), the minimum is -4 (1/16th of a second), and the maximum is 24 (6
months). Note that intervals shorter than 6 (64 seconds) should generally not
be used with public servers on the Internet, because it might be considered
abuse.
*maxpoll* _poll_:::
In a similar way, the user might want to constrain the maximum polling interval.
Again this is specified as a power of 2, *maxpoll 9* indicates that the polling
interval must stay at or below 512 seconds. The default is 10 (1024 seconds),
the minimum is 0 (1 second), and the maximum is 24 (6 months).
This option specifies the maximum interval between requests sent to the server
as a power of 2 in seconds. For example, *maxpoll 9* indicates that the polling
interval should stay at or below 9 (512 seconds). The default is 10 (1024
seconds), the minimum is 0 (1 second), and the maximum is 24 (6 months).
*iburst*:::
If this option is set, the interval between the first four polls will be 2
seconds instead of _minpoll_. This is useful to quickly get the first update of
the clock after *chronyd* is started.
*key* _id_:::
The NTP protocol supports the inclusion of checksums in the packets, to prevent
With this option, the interval between the first four requests sent to the
server will be 2 seconds instead of the interval specified by the *minpoll*
option, which allows *chronyd* to make the first update of the clock shortly
after start.
*burst*:::
With this option, *chronyd* will shorten the interval between up to four
requests to 2 seconds when it cannot get a good measurement from the server.
The number of requests in the burst is limited by the current polling interval
to keep the average interval at or above the minimum interval, i.e. the current
interval needs to be at least two times longer than the minimum interval in
order to allow a burst with two requests.
*key* _ID_:::
The NTP protocol supports a message authentication code (MAC) to prevent
computers having their system time upset by rogue packets being sent to them.
The checksums are generated as a function of a password, using the
cryptographic hash function set in the key file, which is specified by the
<<keyfile,*keyfile*>> directive.
The MAC is generated as a function of a password specified in the key file,
which is specified by the <<keyfile,*keyfile*>> directive.
+
If the key option is present, *chronyd* will attempt to use authenticated
packets when communicating with this server. The key number used will be the
single argument to the key option (an unsigned integer in the range 1 through
2^32-1). The server must have the same password for this key number configured,
The *key* option specifies which key (with an ID in the range 1 through 2^32-1)
should *chronyd* use to authenticate requests sent to the server and verify its
responses. The server must have the same key for this number configured,
otherwise no relationship between the computers will be possible.
+
If the server is running *ntpd* and the output size of the hash function used
by the key is longer than 160 bits (e.g. SHA256), the *version* option needs to
be set to 4 for compatibility.
*maxdelay* _delay_:::
*chronyd* uses the network round-trip delay to the server to determine how
accurate a particular measurement is likely to be. Long round-trip delays
@@ -113,7 +123,7 @@ option. For example, *maxdelay 0.3* would indicate that measurements with a
round-trip delay of 0.3 seconds or more should be ignored. The default value is
3 seconds and the maximum value is 1000 seconds.
*maxdelayratio* _ratio_:::
This option is similar to the maxdelay option above. *chronyd* keeps a record
This option is similar to the *maxdelay* option above. *chronyd* keeps a record
of the minimum round-trip delay amongst the previous measurements that it has
buffered. If a measurement has a round trip delay that is greater than the
maxdelayratio times the minimum delay, it will be rejected.
@@ -123,14 +133,14 @@ minimum delay amongst the previous measurements to the standard deviation of
the previous measurements that is greater than the specified ratio, it will be
rejected. The default is 10.0.
*mindelay* _delay_:::
This options specifies a fixed minimum round-trip delay to be used instead of
This option specifies a fixed minimum round-trip delay to be used instead of
the minimum amongst the previous measurements. This can be useful in networks
with static configuration to improve the stability of corrections for
asymmetric jitter, weighting of the measurements, and the *maxdelayratio* and
*maxdelaydevratio* tests. The value should be set accurately in order to have a
positive effect on the synchronisation.
*asymmetry* _ratio_:::
This options specifies the asymmetry of the network jitter on the path to the
This option specifies the asymmetry of the network jitter on the path to the
source, which is used to correct the measured offset according to the delay.
The asymmetry can be between -0.5 and +0.5. A negative value means the delay of
packets sent to the source is more variable than the delay of packets sent from
@@ -154,14 +164,15 @@ option can be specified. *chronyd* will not try to poll the server until it is
enabled to do so (by using the <<chronyc.adoc#online,*online*>> command in
*chronyc*).
*auto_offline*:::
If this option is set, the server will be assumed to have gone offline when 2
With this option, the server will be assumed to have gone offline when two
requests have been sent to it without receiving a response. This option avoids
the need to run the <<chronyc.adoc#offline,*offline*>> command from *chronyc*
when disconnecting the network link. (It will still be necessary to use the
<<chronyc.adoc#online,*online*>> command when the link has been established, to
enable measurements to start.)
when disconnecting the network link, if it is safe to assume that the requests
and responses will not be dropped in the network, e.g. in a trusted local
network. (It will still be necessary to use the <<chronyc.adoc#online,*online*>>
command when the link has been established, to enable measurements to start.)
*prefer*:::
Prefer this source over sources without prefer option.
Prefer this source over sources without the *prefer* option.
*noselect*:::
Never select this source. This is particularly useful for monitoring.
*trust*:::
@@ -500,7 +511,7 @@ This option specifies the width of the pulses (in seconds). It is used to
filter PPS samples when the driver provides samples for both rising and falling
edges. Note that it reduces the maximum allowed error of the time source which
completes the PPS samples. If the duty cycle is configurable, 50% should be
prefered in order to maximise the allowed error.
preferred in order to maximise the allowed error.
*pps*:::
This options forces *chronyd* to treat any refclock (e.g. SHM or PHC) as a PPS
refclock. This can be useful when the refclock provides time with a variable
@@ -516,6 +527,9 @@ is included in the maximum assumed error which is used in the source selection
algorithm. Increasing the delay is useful to avoid having no majority in the
source selection or to make it prefer other sources. The default is 1e-9 (1
nanosecond).
*stratum* _stratum_:::
This option sets the NTP stratum of the refclock. This can be useful when the
refclock provides time with a stratum other than 0. The default is 0.
*precision* _precision_:::
This option sets the precision of the reference clock (in seconds). The default
value is the estimated precision of the system clock.
@@ -546,6 +560,12 @@ but not very precise, reference clock to be safely combined with
unauthenticated NTP sources in order to improve the accuracy of the clock. They
can be selected and used for synchronisation only if they agree with the
trusted and required source.
*tai*:::
This option indicates that the reference clock keeps time in TAI instead of UTC
and that *chronyd* should correct its offset by the current TAI-UTC offset. The
<<leapsectz,*leapsectz*>> directive must be used with this option and the
database must be kept up to date in order for this correction to work as
expected. This option does not make sense with PPS refclocks.
*minsamples* _samples_:::
Set the minimum number of samples kept for this source. This overrides the
<<minsamples,*minsamples*>> directive.
@@ -751,9 +771,10 @@ driftfile @CHRONYVARDIR@/drift
[[fallbackdrift]]*fallbackdrift* _min-interval_ _max-interval_::
Fallback drifts are long-term averages of the system clock drift calculated
over exponentially increasing intervals. They are used when the clock is no
longer synchronised to avoid quickly drifting away from true time if there was
a short-term deviation in the drift before the synchronisation was lost.
over exponentially increasing intervals. They are used to avoid quickly
drifting away from true time when the clock was not updated for a longer period
of time and there was a short-term deviation in the drift before the updates
stopped.
+
The directive specifies the minimum and maximum interval since the last clock
update to switch between fallback drifts. They are defined as a power of 2 (in
@@ -765,8 +786,10 @@ fallbackdrift 16 19
+
In this example, the minimum interval is 16 (18 hours) and the maximum interval is
19 (6 days). The system clock frequency will be set to the first fallback 18
hours after last clock update, to the second after 36 hours, etc. This might be
a good setting to cover daily and weekly temperature fluctuations.
hours after last clock update, to the second after 36 hours, and so on. This
might be a good setting to cover frequency changes due to daily and weekly
temperature fluctuations. When the frequency is set to a fallback, the state of
the clock will change to '`Not synchronised`'.
+
By default (or if the specified maximum or minimum is 0), no fallbacks are used
and the clock frequency changes only with new measurements from NTP sources,
@@ -862,6 +885,11 @@ It is also useful when the system clock is required to have correct TAI-UTC
offset. Note that the offset is set only when leap seconds are handled by the
kernel, i.e. <<leapsecmode,*leapsecmode*>> is set to *system*.
+
The specified timezone is not used as an exclusive source of information about
leap seconds. If a majority of time sources announce on the last day of June or
December that a leap second should be inserted or deleted, it will be accepted
even if it is not included in the timezone.
+
An example of the directive is:
+
----
@@ -1985,12 +2013,18 @@ format of the file is shown below:
+
Each line consists of an ID, name of an authentication hash function (optional),
and a password. The ID can be any unsigned integer in the range 1 through
2^32-1. The default hash function is *MD5*. Depending on how *chronyd*
was compiled, other supported functions might be *SHA1*, *SHA256*, *SHA384*,
*SHA512*, *RMD128*, *RMD160*, *RMD256*, *RMD320*, *TIGER*, and *WHIRLPOOL*. The
password can be specified as a string of characters not containing white space
with an optional *ASCII:* prefix, or as a hexadecimal number with the *HEX:*
prefix. The maximum length of the line is 2047 characters.
2^32-1. The default hash function is *MD5*, which is always supported.
+
If *chronyd* was built with enabled support for hashing using a crypto library
(nettle, nss, or libtomcrypt), the following functions are available: *MD5*,
*SHA1*, *SHA256*, *SHA384*, *SHA512*. Depending on which library and version is
*chronyd* using, some or all of the following functions may also be available:
*SHA3-224*, *SHA3-256*, *SHA3-384*, *SHA3-512*, *RMD128*, *RMD160*, *RMD256*,
*RMD320*, *TIGER*, *WHIRLPOOL*.
+
The password can be specified as a string of characters not containing white
space with an optional *ASCII:* prefix, or as a hexadecimal number with the
*HEX:* prefix. The maximum length of the line is 2047 characters.
+
The password is used with the hash function to generate and verify a message
authentication code (MAC) in NTP packets. It is recommended to use SHA1, or

View File

@@ -1128,6 +1128,10 @@ running.
The *rekey* command causes *chronyd* to re-read the key file specified in the
configuration file by the <<chrony.conf.adoc#keyfile,*keyfile*>> directive.
[[rekey]]*shutdown*::
The *shutdown* command causes *chronyd* to exit. This is equivalent to sending
the process the SIGTERM signal.
=== Client commands
[[dns]]*dns* _option_::

View File

@@ -135,7 +135,7 @@ range of privileged system calls on behalf of the parent.
*-F* _level_::
This option configures a system call filter when *chronyd* is compiled with
support for the Linux secure computing (seccomp) facility. In level 1 the
process is killed when a forbidden system call is made, in level -1 the SYSSIG
process is killed when a forbidden system call is made, in level -1 the SIGSYS
signal is thrown instead and in level 0 the filter is disabled (default 0).
+
It's recommended to enable the filter only when it's known to work on the
@@ -157,11 +157,13 @@ This option will lock *chronyd* into RAM so that it will never be paged out.
This mode is only supported on Linux.
*-x*::
This option disables the control of the system clock. *chronyd* will not make
any adjustments of the clock, but it will still track its offset and frequency
relative to the estimated true time, and be able to operate as an NTP server.
This allows *chronyd* to run without the capability to adjust or set the system
clock (e.g. in some containers).
This option disables the control of the system clock. *chronyd* will not try to
make any adjustments of the clock. It will assume the clock is free running and
still track its offset and frequency relative to the estimated true time. This
option allows *chronyd* to run without the capability to adjust or set the
system clock (e.g. in some containers) in order to operate as an NTP server. It
is not recommended to run *chronyd* (with or without *-x*) when another process
is controlling the system clock.
*-v*::
With this option *chronyd* will print version number to the terminal and exit.

View File

@@ -393,16 +393,31 @@ things
Some other program running on the system may be using the device.
=== What if my computer does not have an RTC or backup battery?
In this case you can still use the `-s` option to set the system clock to the
last modification time of the drift file, which should correspond to the system
time when `chronyd` was previously stopped. The initial system time will be
increasing across reboots and applications started after `chronyd` will not
observe backward steps.
== NTP-specific issues
=== Can `chronyd` be driven from broadcast NTP servers?
=== Can `chronyd` be driven from broadcast/multicast NTP servers?
No, the broadcast client mode is not supported and there is currently no plan
to implement it. The broadcast and multicast modes are inherently less
accurate and less secure (even with authentication) than the ordinary
server/client mode and they are not as useful as they used to be. Even with
very modest hardware a single NTP server can serve time to hundreds of
thousands of clients using the ordinary mode.
No, the broadcast/multicast client mode is not supported and there is currently
no plan to implement it. While the mode may be useful to simplify configuration
of clients in large networks, it is inherently less accurate and less secure
(even with authentication) than the ordinary client/server mode.
When configuring a large number of clients in a network, it is recommended to
use the `pool` directive with a DNS name which resolves to addresses of
multiple NTP servers. The clients will automatically replace the servers when
they become unreachable, or otherwise unsuitable for synchronisation, with new
servers from the pool.
Even with very modest hardware, an NTP server can serve time to hundreds of
thousands of clients using the ordinary client/server mode.
=== Can `chronyd` transmit broadcast NTP packets?

View File

@@ -29,8 +29,8 @@ After unpacking the source code, change directory into it, and type
----
This is a shell script that automatically determines the system type. There is
a single optional parameter, `--prefix` which indicates the directory tree
where the software should be installed. For example,
an optional parameter `--prefix`, which indicates the directory tree where the
software should be installed. For example,
----
./configure --prefix=/opt/free
@@ -40,11 +40,11 @@ will install the `chronyd` daemon into `/opt/free/sbin` and the `chronyc`
control program into `/opt/free/bin`. The default value for the prefix is
`/usr/local`.
The configure script assumes you want to use gcc as your compiler. If you want
to use a different compiler, you can configure this way:
The `configure` script assumes you want to use `gcc` as your compiler. If you
want to use a different compiler, you can configure this way:
----
CC=cc CFLAGS=-O ./configure --prefix=/opt/free
CC=cc ./configure --prefix=/opt/free
----
for Bourne-family shells, or
@@ -63,11 +63,26 @@ shown. Otherwise, `Makefile` will be generated.
On Linux, if development files for the libcap library are available, `chronyd`
will be built with support for dropping root privileges. On other systems no
extra library is needed. The default user which `chronyd` should run as can be
specified with the `--with-user` option of the configure script.
specified with the `--with-user` option of the `configure` script.
If development files for the POSIX threads library are available, `chronyd`
will be built with support for asynchronous resolving of hostnames specified in
the `server`, `peer`, and `pool` directives. This allows `chronyd` operating as
a server to respond to client requests when resolving a hostname. If you don't
want to enable the support, specify the `--disable-asyncdns` flag to
`configure`.
If development files for the https://www.lysator.liu.se/~nisse/nettle/[Nettle],
https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS[NSS], or
http://www.libtom.net/LibTomCrypt/[libtomcrypt] library are available,
`chronyd` will be built with support for other cryptographic hash functions
than MD5, which can be used for NTP authentication with a symmetric key. If you
don't want to enable the support, specify the `--disable-sechash` flag to
`configure`.
If development files for the editline or readline library are available,
`chronyc` will be built with line editing support. If you don't want this,
specify the `--disable-readline` flag to configure.
specify the `--disable-readline` flag to `configure`.
If a `timepps.h` header is available (e.g. from the
http://linuxpps.org[LinuxPPS project]), `chronyd` will be built with PPS API
@@ -75,6 +90,9 @@ reference clock driver. If the header is installed in a location that isn't
normally searched by the compiler, you can add it to the searched locations by
setting the `CPPFLAGS` variable to `-I/path/to/timepps`.
The `--help` option can be specified to `configure` to print all options
supported by the script.
Now type
----
@@ -122,6 +140,16 @@ unprivileged user for `chronyd` and specify it with the `-u` command-line
option or the `user` directive in the configuration file, or set the default
user with the `--with-user` configure option before building.
== Support for system call filtering
`chronyd` can be built with support for the Linux secure computing (seccomp)
facility. This requires development files for the
https://github.com/seccomp/libseccomp[libseccomp] library and the
`--enable-scfilter` option specified to `configure`. The `-F` option of
`chronyd` will enable a system call filter, which should significantly reduce
the kernel attack surface and possibly prevent kernel exploits from `chronyd`
if it is compromised.
== Support for line editing libraries
`chronyc` can be built with support for line editing, this allows you to use
@@ -132,12 +160,12 @@ Please note that readline since version 6.0 is licensed under GPLv3+ which is
incompatible with chrony's license GPLv2. You should use editline instead if
you don't want to use older readline versions.
The configure script will automatically enable the line editing support if one
of the supported libraries is available. If they are both available, the
The `configure` script will automatically enable the line editing support if
one of the supported libraries is available. If they are both available, the
editline library will be used.
If you don't want to use it (in which case chronyc will use a minimal command
line interface), invoke configure like this:
If you don't want to use it (in which case `chronyc` will use a minimal command
line interface), invoke `configure` like this:
----
./configure --disable-readline other-options...
@@ -161,12 +189,12 @@ normally searched by the compiler and linker, you need to use extra options:
== Extra options for package builders
The configure and make procedures have some extra options that may be useful if
you are building a distribution package for chrony.
The `configure` and `make` procedures have some extra options that may be
useful if you are building a distribution package for `chrony`.
The `--mandir=DIR` option to configure specifies an install directory for the
man pages. This overrides the `man` subdirectory of the argument to the
--prefix option.
The `--mandir=DIR` option to `configure` specifies an installation directory
for the man pages. This overrides the `man` subdirectory of the argument to the
`--prefix` option.
----
./configure --prefix=/usr --mandir=/usr/share/man
@@ -174,8 +202,8 @@ man pages. This overrides the `man` subdirectory of the argument to the
to set both options together.
The final option is the `DESTDIR` option to the make command. For example, you
could use the commands
The final option is the `DESTDIR` option to the `make` command. For example,
you could use the commands
----
./configure --prefix=/usr --mandir=/usr/share/man

View File

@@ -28,6 +28,9 @@ rtcsync
# Specify file containing keys for NTP authentication.
#keyfile /etc/chrony.keys
# Get TAI-UTC offset and leap seconds from the system tz database.
#leapsectz right/UTC
# Specify directory for log files.
logdir /var/log/chrony

View File

@@ -97,6 +97,12 @@ driftfile /var/lib/chrony/drift
! pidfile /var/run/chronyd.pid
# If the system timezone database is kept up to date and includes the
# right/UTC timezone, chronyd can use it to determine the current
# TAI-UTC offset and when will the next leap second occur.
! leapsectz right/UTC
#######################################################################
### INITIAL CLOCK CORRECTION
# This option is useful to quickly correct the clock on start if it's

View File

@@ -4,6 +4,8 @@
export LC_ALL=C
[ "$2" != "up" ] && [ "$2" != "down" ] && exit 0
# Check if there is a default route
if /sbin/ip route list 2> /dev/null | grep -q '^default'; then

120
hash_nettle.c Normal file
View File

@@ -0,0 +1,120 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2018
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
Routines implementing crypto hashing using the nettle library.
*/
#include "config.h"
#include "sysincl.h"
#include <nettle/nettle-meta.h>
#include "hash.h"
#include "memory.h"
struct hash {
const char *name;
const char *int_name;
const struct nettle_hash *nettle_hash;
void *context;
};
static struct hash hashes[] = {
{ "MD5", "md5", NULL, NULL },
{ "RMD160", "ripemd160", NULL, NULL },
{ "SHA1", "sha1", NULL, NULL },
{ "SHA256", "sha256", NULL, NULL },
{ "SHA384", "sha384", NULL, NULL },
{ "SHA512", "sha512", NULL, NULL },
{ "SHA3-224", "sha3_224", NULL, NULL },
{ "SHA3-256", "sha3_256", NULL, NULL },
{ "SHA3-384", "sha3_384", NULL, NULL },
{ "SHA3-512", "sha3_512", NULL, NULL },
{ NULL, NULL, NULL, NULL }
};
int
HSH_GetHashId(const char *name)
{
int id, nid;
for (id = 0; hashes[id].name; id++) {
if (!strcmp(name, hashes[id].name))
break;
}
if (!hashes[id].name)
return -1;
if (hashes[id].context)
return id;
for (nid = 0; nettle_hashes[nid]; nid++) {
if (!strcmp(hashes[id].int_name, nettle_hashes[nid]->name))
break;
}
if (!nettle_hashes[nid] || !nettle_hashes[nid]->context_size || !nettle_hashes[nid]->init)
return -1;
hashes[id].nettle_hash = nettle_hashes[nid];
hashes[id].context = Malloc(hashes[id].nettle_hash->context_size);
return id;
}
unsigned int
HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len,
const unsigned char *in2, unsigned int in2_len,
unsigned char *out, unsigned int out_len)
{
const struct nettle_hash *hash;
void *context;
hash = hashes[id].nettle_hash;
context = hashes[id].context;
if (out_len > hash->digest_size)
out_len = hash->digest_size;
hash->init(context);
hash->update(context, in1_len, in1);
if (in2)
hash->update(context, in2_len, in2);
hash->digest(context, out_len, out);
return out_len;
}
void
HSH_Finalise(void)
{
int i;
for (i = 0; hashes[i].name; i++) {
if (hashes[i].context)
Free(hashes[i].context);
}
}

View File

@@ -78,7 +78,7 @@ HSH_Hash(int id, const unsigned char *in1, unsigned int in1_len,
const unsigned char *in2, unsigned int in2_len,
unsigned char *out, unsigned int out_len)
{
unsigned int ret;
unsigned int ret = 0;
NSSLOWHASH_Begin(hashes[id].context);
NSSLOWHASH_Update(hashes[id].context, in1, in1_len);

View File

@@ -62,6 +62,12 @@ static const struct hash hashes[] = {
#ifdef LTC_SHA512
{ "SHA512", "sha512", &sha512_desc },
#endif
#ifdef LTC_SHA3
{ "SHA3-224", "sha3-224", &sha3_224_desc },
{ "SHA3-256", "sha3-256", &sha3_256_desc },
{ "SHA3-384", "sha3-384", &sha3_384_desc },
{ "SHA3-512", "sha3-512", &sha3_512_desc },
#endif
#ifdef LTC_TIGER
{ "TIGER", "tiger", &tiger_desc },
#endif

View File

@@ -79,11 +79,11 @@ LOG_Initialise(void)
void
LOG_Finalise(void)
{
if (system_log) {
if (system_log)
closelog();
} else {
if (file_log)
fclose(file_log);
}
LOG_CycleLogFiles();
@@ -116,7 +116,7 @@ static void log_message(int fatal, LOG_Severity severity, const char *message)
assert(0);
}
syslog(priority, fatal ? "Fatal error : %s" : "%s", message);
} else {
} else if (file_log) {
fprintf(file_log, fatal ? "Fatal error : %s\n" : "%s\n", message);
}
}
@@ -134,7 +134,7 @@ void LOG_Message(LOG_Severity severity,
time_t t;
struct tm stm;
if (!system_log) {
if (!system_log && file_log) {
/* Don't clutter up syslog with timestamps and internal debugging info */
time(&t);
stm = *gmtime(&t);
@@ -160,16 +160,14 @@ void LOG_Message(LOG_Severity severity,
case LOGS_FATAL:
log_message(1, severity, buf);
/* With syslog, send the message also to the grandparent
process or write it to stderr if not detached */
if (system_log) {
if (parent_fd > 0) {
if (write(parent_fd, buf, strlen(buf) + 1) < 0)
; /* Not much we can do here */
} else if (parent_fd == 0) {
system_log = 0;
log_message(1, severity, buf);
}
/* Send the message also to the foreground process if it is
still running, or stderr if it is still open */
if (parent_fd > 0) {
if (write(parent_fd, buf, strlen(buf) + 1) < 0)
; /* Not much we can do here */
} else if (system_log && parent_fd == 0) {
system_log = 0;
log_message(1, severity, buf);
}
break;
default:
@@ -220,6 +218,8 @@ void
LOG_SetParentFd(int fd)
{
parent_fd = fd;
if (file_log == stderr)
file_log = NULL;
}
/* ================================================== */

View File

@@ -105,7 +105,7 @@ extern void LOG_OpenFileLog(const char *log_file);
/* Log messages to syslog instead of stderr */
extern void LOG_OpenSystemLog(void);
/* Send fatal message also to the foreground process */
/* Stop using stderr and send fatal message to the foreground process */
extern void LOG_SetParentFd(int fd);
/* Close the pipe to the foreground process so it can exit */

29
main.c
View File

@@ -292,6 +292,8 @@ write_pidfile(void)
/* ================================================== */
#define DEV_NULL "/dev/null"
static void
go_daemon(void)
{
@@ -352,6 +354,13 @@ go_daemon(void)
}
LOG_SetParentFd(pipefd[1]);
/* Open /dev/null as new stdin/out/err */
errno = 0;
if (open(DEV_NULL, O_RDONLY) != STDIN_FILENO ||
open(DEV_NULL, O_WRONLY) != STDOUT_FILENO ||
open(DEV_NULL, O_RDWR) != STDERR_FILENO)
LOG_FATAL("Could not open %s : %s", DEV_NULL, strerror(errno));
}
}
}
@@ -524,6 +533,16 @@ int main
/* Write our pidfile to prevent other chronyds running */
write_pidfile();
if (!user)
user = CNF_GetUser();
pw = getpwnam(user);
if (!pw)
LOG_FATAL("Could not get user/group ID of %s", user);
/* Create directories for sockets, log files, and dump files */
CNF_CreateDirs(pw->pw_uid, pw->pw_gid);
PRV_Initialise();
LCL_Initialise();
SCH_Initialise();
@@ -551,16 +570,6 @@ int main
SYS_LockMemory();
}
if (!user) {
user = CNF_GetUser();
}
if ((pw = getpwnam(user)) == NULL)
LOG_FATAL("Could not get %s uid/gid", user);
/* Create all directories before dropping root */
CNF_CreateDirs(pw->pw_uid, pw->pw_gid);
/* Drop root privileges if the specified user has a non-zero UID */
if (!geteuid() && (pw->pw_uid || pw->pw_gid))
SYS_DropRoot(pw->pw_uid, pw->pw_gid);

View File

@@ -53,7 +53,19 @@ DNS_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs)
max_addrs = MIN(max_addrs, DNS_MAX_ADDRESSES);
memset(&hints, 0, sizeof (hints));
hints.ai_family = AF_UNSPEC;
switch (address_family) {
case IPADDR_INET4:
hints.ai_family = AF_INET;
break;
#ifdef FEAT_IPV6
case IPADDR_INET6:
hints.ai_family = AF_INET6;
break;
#endif
default:
hints.ai_family = AF_UNSPEC;
}
hints.ai_socktype = SOCK_STREAM;
result = getaddrinfo(name, NULL, &hints, &res);

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2017
* Copyright (C) Miroslav Lichvar 2009-2018
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -88,6 +88,7 @@ struct NCR_Instance_Record {
SCH_TimeoutID tx_timeout_id; /* Timeout ID for next transmission */
int tx_suspended; /* Boolean indicating we can't transmit yet */
int auto_burst; /* If 1, initiate a burst on each poll */
int auto_offline; /* If 1, automatically go offline if server/peer
isn't responding */
@@ -149,14 +150,11 @@ struct NCR_Instance_Record {
be used for synchronisation */
int valid_timestamps;
/* Flag indicating the timestamps below were updated since last request */
int updated_timestamps;
/* Receive and transmit timestamps from the last received packet */
/* Receive and transmit timestamps from the last valid response */
NTP_int64 remote_ntp_rx;
NTP_int64 remote_ntp_tx;
/* Local timestamp when the last packet was received from the
/* Local timestamp when the last valid response was received from the
source. We have to be prepared to tinker with this if the local
clock has its frequency adjusted before we repond. The value we
store here is what our own local time was when the same arrived.
@@ -183,6 +181,15 @@ struct NCR_Instance_Record {
int prev_local_poll;
unsigned int prev_tx_count;
/* Flag indicating the two timestamps below were updated since the
last transmission */
int updated_init_timestamps;
/* Timestamps used for (re)starting the symmetric protocol, when we
need to respond to a packet which is not a valid response */
NTP_int64 init_remote_ntp_tx;
NTP_Local_Timestamp init_local_rx;
/* The instance record in the main source management module. This
performs the statistical analysis on the samples we generate */
@@ -230,6 +237,10 @@ static ARR_Instance broadcasts;
#define IBURST_GOOD_SAMPLES 4
#define IBURST_TOTAL_SAMPLES SOURCE_REACH_BITS
/* Number of samples in automatic burst */
#define BURST_GOOD_SAMPLES 1
#define MAX_BURST_TOTAL_SAMPLES 4
/* Time to wait after sending packet to 'warm up' link */
#define WARM_UP_DELAY 2.0
@@ -551,6 +562,7 @@ NCR_GetInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourcePar
result->max_delay_ratio = CLAMP(0.0, params->max_delay_ratio, MAX_MAXDELAYRATIO);
result->max_delay_dev_ratio = CLAMP(0.0, params->max_delay_dev_ratio, MAX_MAXDELAYDEVRATIO);
result->offset_correction = params->offset;
result->auto_burst = params->burst;
result->auto_offline = params->auto_offline;
result->poll_target = params->poll_target;
@@ -652,7 +664,6 @@ NCR_ResetInstance(NCR_Instance instance)
instance->valid_rx = 0;
instance->valid_timestamps = 0;
instance->updated_timestamps = 0;
UTI_ZeroNtp64(&instance->remote_ntp_rx);
UTI_ZeroNtp64(&instance->remote_ntp_tx);
UTI_ZeroNtp64(&instance->local_ntp_rx);
@@ -662,6 +673,10 @@ NCR_ResetInstance(NCR_Instance instance)
zero_local_timestamp(&instance->prev_local_tx);
instance->prev_local_poll = 0;
instance->prev_tx_count = 0;
instance->updated_init_timestamps = 0;
UTI_ZeroNtp64(&instance->init_remote_ntp_tx);
zero_local_timestamp(&instance->init_local_rx);
}
/* ================================================== */
@@ -890,8 +905,10 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
NTP_Local_Timestamp *local_rx, /* The RX time of the received packet */
NTP_Local_Timestamp *local_tx, /* The TX time of the previous packet
RESULT : TX time of this packet */
NTP_int64 *local_ntp_rx, /* RESULT : receive timestamp from this packet */
NTP_int64 *local_ntp_tx, /* RESULT : transmit timestamp from this packet */
NTP_int64 *local_ntp_rx, /* The receive timestamp from the previous packet
RESULT : receive timestamp from this packet */
NTP_int64 *local_ntp_tx, /* The transmit timestamp from the previous packet
RESULT : transmit timestamp from this packet */
NTP_Remote_Address *where_to, /* Where to address the reponse to */
NTP_Local_Address *from /* From what address to send it */
)
@@ -914,8 +931,8 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
version = NTP_VERSION;
}
/* Allow interleaved mode only if there was a prior transmission */
if (interleaved && (!local_tx || UTI_IsZeroTimespec(&local_tx->ts)))
/* Check if the packet can be formed in the interleaved mode */
if (interleaved && (!remote_ntp_rx || !local_tx || UTI_IsZeroTimespec(&local_tx->ts)))
interleaved = 0;
smooth_time = 0;
@@ -989,14 +1006,21 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
/* Originate - this comes from the last packet the source sent us */
message.originate_ts = interleaved ? *remote_ntp_rx : *remote_ntp_tx;
/* Prepare random bits which will be added to the receive timestamp */
UTI_GetNtp64Fuzz(&ts_fuzz, precision);
do {
/* Prepare random bits which will be added to the receive timestamp */
UTI_GetNtp64Fuzz(&ts_fuzz, precision);
/* Receive - this is when we received the last packet from the source.
This timestamp will have been adjusted so that it will now look to
the source like we have been running on our latest estimate of
frequency all along */
UTI_TimespecToNtp64(&local_receive, &message.receive_ts, &ts_fuzz);
/* Receive - this is when we received the last packet from the source.
This timestamp will have been adjusted so that it will now look to
the source like we have been running on our latest estimate of
frequency all along */
UTI_TimespecToNtp64(&local_receive, &message.receive_ts, &ts_fuzz);
/* Do not send a packet with a non-zero receive timestamp equal to the
originate timestamp or previous receive timestamp */
} while (!UTI_IsZeroNtp64(&message.receive_ts) &&
UTI_IsEqualAnyNtp64(&message.receive_ts, &message.originate_ts,
local_ntp_rx, NULL));
} else {
UTI_ZeroNtp64(&message.originate_ts);
UTI_ZeroNtp64(&message.receive_ts);
@@ -1055,10 +1079,16 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
&message.transmit_ts, &ts_fuzz);
}
/* Avoid sending messages with non-zero transmit timestamp equal to the
receive timestamp to allow reliable detection of the interleaved mode */
} while (!UTI_CompareNtp64(&message.transmit_ts, &message.receive_ts) &&
!UTI_IsZeroNtp64(&message.transmit_ts));
/* Do not send a packet with a non-zero transmit timestamp which is
equal to any of the following timestamps:
- receive (to allow reliable detection of the interleaved mode)
- originate (to prevent the packet from being its own valid response
in the symmetric mode)
- previous transmit (to invalidate responses to the previous packet)
(the precision must be at least -30 to prevent an infinite loop!) */
} while (!UTI_IsZeroNtp64(&message.transmit_ts) &&
UTI_IsEqualAnyNtp64(&message.transmit_ts, &message.receive_ts,
&message.originate_ts, local_ntp_tx));
ret = NIO_SendPacket(&message, where_to, from, length, local_tx != NULL);
@@ -1084,7 +1114,7 @@ transmit_timeout(void *arg)
{
NCR_Instance inst = (NCR_Instance) arg;
NTP_Local_Address local_addr;
int interleaved, sent;
int interleaved, initial, sent;
inst->tx_timeout_id = 0;
@@ -1093,10 +1123,20 @@ transmit_timeout(void *arg)
/* With online burst switch to online before last packet */
if (inst->burst_total_samples_to_go <= 1)
inst->opmode = MD_ONLINE;
break;
case MD_BURST_WAS_OFFLINE:
if (inst->burst_total_samples_to_go <= 0)
take_offline(inst);
break;
case MD_ONLINE:
/* Start a new burst if the burst option is enabled and the average
polling interval including the burst will not fall below the
minimum polling interval */
if (inst->auto_burst && inst->local_poll > inst->minpoll && inst->local_poll > 1)
NCR_InitiateSampleBurst(inst, BURST_GOOD_SAMPLES,
MIN(1 << (inst->local_poll - inst->minpoll),
MAX_BURST_TOTAL_SAMPLES));
break;
default:
break;
}
@@ -1137,6 +1177,19 @@ transmit_timeout(void *arg)
(inst->mode == MODE_ACTIVE &&
inst->prev_tx_count == 1 && inst->tx_count == 0));
/* In symmetric mode, if no valid response was received since the previous
transmission, respond to the last received packet even if it failed some
specific NTP tests. This is necessary for starting and restarting the
protocol, e.g. when a packet was lost. */
initial = inst->mode == MODE_ACTIVE && !inst->valid_rx &&
!UTI_IsZeroNtp64(&inst->init_remote_ntp_tx);
/* Prepare for the response */
inst->valid_rx = 0;
inst->updated_init_timestamps = 0;
if (initial)
inst->valid_timestamps = 0;
/* Check whether we need to 'warm up' the link to the other end by
sending an NTP exchange to ensure both ends' ARP caches are
primed or whether we need to send two packets first to ensure a
@@ -1148,18 +1201,16 @@ transmit_timeout(void *arg)
inst->presend_done--;
}
sent = transmit_packet(inst->mode, interleaved, inst->local_poll,
inst->version,
/* Send the request (which may also be a response in the symmetric mode) */
sent = transmit_packet(inst->mode, interleaved, inst->local_poll, inst->version,
inst->auth_mode, inst->auth_key_id,
&inst->remote_ntp_rx, &inst->remote_ntp_tx,
&inst->local_rx, &inst->local_tx,
&inst->local_ntp_rx, &inst->local_ntp_tx,
&inst->remote_addr,
&local_addr);
initial ? NULL : &inst->remote_ntp_rx,
initial ? &inst->init_remote_ntp_tx : &inst->remote_ntp_tx,
initial ? &inst->init_local_rx : &inst->local_rx,
&inst->local_tx, &inst->local_ntp_rx, &inst->local_ntp_tx,
&inst->remote_addr, &local_addr);
++inst->tx_count;
inst->valid_rx = 0;
inst->updated_timestamps = 0;
if (sent)
inst->report.total_tx_count++;
@@ -1442,6 +1493,7 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
NTP_Local_Timestamp local_receive, local_transmit;
double remote_interval, local_interval, response_time;
double delay_time, precision;
int updated_timestamps;
/* ==================== */
@@ -1459,7 +1511,8 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
The test values are 1 when passed and 0 when failed. */
/* Test 1 checks for duplicate packet */
test1 = !!UTI_CompareNtp64(&message->transmit_ts, &inst->remote_ntp_tx);
test1 = UTI_CompareNtp64(&message->receive_ts, &inst->remote_ntp_rx) ||
UTI_CompareNtp64(&message->transmit_ts, &inst->remote_ntp_tx);
/* Test 2 checks for bogus packet in the basic and interleaved modes. This
ensures the source is responding to the latest packet we sent to it. */
@@ -1644,21 +1697,34 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
The authentication test (test5) is required to prevent DoS attacks using
unauthenticated packets on authenticated symmetric associations. */
if ((inst->mode == MODE_CLIENT && valid_packet && !inst->valid_rx) ||
(inst->mode == MODE_ACTIVE && (valid_packet || !inst->valid_rx) &&
test5 && !UTI_IsZeroNtp64(&message->transmit_ts) &&
(!inst->updated_timestamps || (valid_packet && !inst->valid_rx) ||
(inst->mode == MODE_ACTIVE && valid_packet &&
(!inst->valid_rx ||
UTI_CompareNtp64(&inst->remote_ntp_tx, &message->transmit_ts) < 0))) {
inst->remote_ntp_rx = message->receive_ts;
inst->remote_ntp_tx = message->transmit_ts;
inst->local_rx = *rx_ts;
inst->valid_timestamps = synced_packet;
inst->updated_timestamps = 1;
UTI_ZeroNtp64(&inst->init_remote_ntp_tx);
zero_local_timestamp(&inst->init_local_rx);
inst->updated_init_timestamps = 0;
updated_timestamps = 2;
/* Don't use the same set of timestamps for the next sample */
if (interleaved_packet)
inst->prev_local_tx = inst->local_tx;
else
zero_local_timestamp(&inst->prev_local_tx);
} else if (inst->mode == MODE_ACTIVE &&
test1 && !UTI_IsZeroNtp64(&message->transmit_ts) && test5 &&
(!inst->updated_init_timestamps ||
UTI_CompareNtp64(&inst->init_remote_ntp_tx, &message->transmit_ts) < 0)) {
inst->init_remote_ntp_tx = message->transmit_ts;
inst->init_local_rx = *rx_ts;
inst->updated_init_timestamps = 1;
updated_timestamps = 1;
} else {
updated_timestamps = 0;
}
/* Accept at most one response per request. The NTP specification recommends
@@ -1694,10 +1760,11 @@ receive_packet(NCR_Instance inst, NTP_Local_Address *local_addr,
DEBUG_LOG("remote_interval=%.9f local_interval=%.9f response_time=%.9f txs=%c rxs=%c",
remote_interval, local_interval, response_time,
tss_chars[local_transmit.source], tss_chars[local_receive.source]);
DEBUG_LOG("test123=%d%d%d test567=%d%d%d testABCD=%d%d%d%d kod_rate=%d interleaved=%d presend=%d valid=%d good=%d updated=%d",
DEBUG_LOG("test123=%d%d%d test567=%d%d%d testABCD=%d%d%d%d kod_rate=%d interleaved=%d"
" presend=%d valid=%d good=%d updated=%d",
test1, test2, test3, test5, test6, test7, testA, testB, testC, testD,
kod_rate, interleaved_packet, inst->presend_done, valid_packet, good_packet,
!UTI_CompareTimespecs(&inst->local_rx.ts, &rx_ts->ts));
updated_timestamps);
if (valid_packet) {
inst->remote_poll = message->poll;
@@ -1988,7 +2055,7 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
NTP_Mode pkt_mode, my_mode;
NTP_int64 *local_ntp_rx, *local_ntp_tx;
NTP_Local_Timestamp local_tx, *tx_ts;
int valid_auth, log_index, interleaved, poll;
int pkt_version, valid_auth, log_index, interleaved, poll;
AuthenticationMode auth_mode;
uint32_t key_id;
@@ -2009,6 +2076,7 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
}
pkt_mode = NTP_LVM_TO_MODE(message->lvm);
pkt_version = NTP_LVM_TO_VERSION(message->lvm);
switch (pkt_mode) {
case MODE_ACTIVE:
@@ -2019,6 +2087,15 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
/* Reply with server packet */
my_mode = MODE_SERVER;
break;
case MODE_UNDEFINED:
/* Check if it is an NTPv1 client request (NTPv1 packets have a reserved
field instead of the mode field and the actual mode is determined from
the port numbers). Don't ever respond with a mode 0 packet! */
if (pkt_version == 1 && remote_addr->port != NTP_PORT) {
my_mode = MODE_SERVER;
break;
}
/* Fall through */
default:
/* Discard */
DEBUG_LOG("NTP packet discarded pkt_mode=%d", pkt_mode);
@@ -2064,7 +2141,8 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
if (log_index >= 0) {
CLG_GetNtpTimestamps(log_index, &local_ntp_rx, &local_ntp_tx);
interleaved = !UTI_IsZeroNtp64(local_ntp_rx) &&
!UTI_CompareNtp64(&message->originate_ts, local_ntp_rx);
!UTI_CompareNtp64(&message->originate_ts, local_ntp_rx) &&
UTI_CompareNtp64(&message->receive_ts, &message->transmit_ts);
if (interleaved) {
UTI_Ntp64ToTimespec(local_ntp_tx, &local_tx.ts);
@@ -2081,7 +2159,7 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
poll = MAX(poll, message->poll);
/* Send a reply */
transmit_packet(my_mode, interleaved, poll, NTP_LVM_TO_VERSION(message->lvm),
transmit_packet(my_mode, interleaved, poll, pkt_version,
auth_mode, key_id, &message->receive_ts, &message->transmit_ts,
rx_ts, tx_ts, local_ntp_rx, NULL, remote_addr, local_addr);
@@ -2189,6 +2267,9 @@ NCR_SlewTimes(NCR_Instance inst, struct timespec *when, double dfreq, double dof
if (!UTI_IsZeroTimespec(&inst->prev_local_tx.ts))
UTI_AdjustTimespec(&inst->prev_local_tx.ts, when, &inst->prev_local_tx.ts, &delta, dfreq,
doffset);
if (!UTI_IsZeroTimespec(&inst->init_local_rx.ts))
UTI_AdjustTimespec(&inst->init_local_rx.ts, when, &inst->init_local_rx.ts, &delta, dfreq,
doffset);
}
/* ================================================== */

View File

@@ -318,6 +318,9 @@ close_socket(int sock_fd)
if (sock_fd == INVALID_SOCK_FD)
return;
#ifdef HAVE_LINUX_TIMESTAMPING
NIO_Linux_NotifySocketClosing(sock_fd);
#endif
SCH_RemoveFileHandler(sock_fd);
close(sock_fd);
}
@@ -685,6 +688,11 @@ read_from_socket(int sock_fd, int event, void *anything)
unsigned int i, n;
int status, flags = 0;
#ifdef HAVE_LINUX_TIMESTAMPING
if (NIO_Linux_ProcessEvent(sock_fd, event))
return;
#endif
hdr = ARR_GetElements(recv_headers);
n = ARR_GetSize(recv_headers);
assert(n >= 1);
@@ -709,6 +717,20 @@ read_from_socket(int sock_fd, int event, void *anything)
#endif
if (status < 0) {
#ifdef HAVE_LINUX_TIMESTAMPING
/* If reading from the error queue failed, the exception should be
for a socket error. Clear the error to avoid a busy loop. */
if (flags & MSG_ERRQUEUE) {
int error = 0;
socklen_t len = sizeof (error);
if (getsockopt(sock_fd, SOL_SOCKET, SO_ERROR, &error, &len))
DEBUG_LOG("Could not get SO_ERROR");
if (error)
errno = error;
}
#endif
DEBUG_LOG("Could not receive from fd %d : %s", sock_fd,
strerror(errno));
return;

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2016-2017
* Copyright (C) Miroslav Lichvar 2016-2018
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -94,6 +94,27 @@ static int ts_tx_flags;
/* Flag indicating the socket options can't be changed in control messages */
static int permanent_ts_options;
/* When sending client requests to a close and fast server, it is possible that
a response will be received before the HW transmit timestamp of the request
itself. To avoid processing of the response without the HW timestamp, we
monitor events returned by select() and suspend reading of packets from the
receive queue for up to 200 microseconds. As the requests are normally
separated by at least 200 milliseconds, it is sufficient to monitor and
suspend one socket at a time. */
static int monitored_socket;
static int suspended_socket;
static SCH_TimeoutID resume_timeout_id;
#define RESUME_TIMEOUT 200.0e-6
/* Unbound socket keeping the kernel RX timestamping permanently enabled
in order to avoid a race condition between receiving a server response
and the kernel actually starting to timestamp received packets after
enabling the timestamping and sending a request */
static int dummy_rxts_socket;
#define INVALID_SOCK_FD -3
/* ================================================== */
static int
@@ -252,7 +273,7 @@ update_interface_speed(struct Interface *iface)
{
struct ethtool_cmd cmd;
struct ifreq req;
int sock_fd;
int sock_fd, link_speed;
sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (sock_fd < 0)
@@ -273,7 +294,12 @@ update_interface_speed(struct Interface *iface)
close(sock_fd);
iface->link_speed = ethtool_cmd_speed(&cmd);
link_speed = ethtool_cmd_speed(&cmd);
if (iface->link_speed != link_speed) {
iface->link_speed = link_speed;
DEBUG_LOG("Updated speed of %s to %d Mb/s", iface->name, link_speed);
}
}
/* ================================================== */
@@ -301,6 +327,29 @@ check_timestamping_option(int option)
/* ================================================== */
static int
open_dummy_socket(void)
{
int sock_fd, events = 0;
if ((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0
#ifdef FEAT_IPV6
&& (sock_fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0
#endif
)
return INVALID_SOCK_FD;
if (!NIO_Linux_SetTimestampSocketOptions(sock_fd, 1, &events)) {
close(sock_fd);
return INVALID_SOCK_FD;
}
UTI_FdSetCloexec(sock_fd);
return sock_fd;
}
/* ================================================== */
void
NIO_Linux_Initialise(void)
{
@@ -350,6 +399,10 @@ NIO_Linux_Initialise(void)
/* Kernels before 4.7 ignore timestamping flags set in control messages */
permanent_ts_options = !SYS_Linux_CheckKernelVersion(4, 7);
monitored_socket = INVALID_SOCK_FD;
suspended_socket = INVALID_SOCK_FD;
dummy_rxts_socket = INVALID_SOCK_FD;
}
/* ================================================== */
@@ -360,6 +413,9 @@ NIO_Linux_Finalise(void)
struct Interface *iface;
unsigned int i;
if (dummy_rxts_socket != INVALID_SOCK_FD)
close(dummy_rxts_socket);
for (i = 0; i < ARR_GetSize(interfaces); i++) {
iface = ARR_GetElement(interfaces, i);
HCL_DestroyInstance(iface->clock);
@@ -406,6 +462,73 @@ NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events)
/* ================================================== */
static void
resume_socket(int sock_fd)
{
if (monitored_socket == sock_fd)
monitored_socket = INVALID_SOCK_FD;
if (sock_fd == INVALID_SOCK_FD || sock_fd != suspended_socket)
return;
suspended_socket = INVALID_SOCK_FD;
SCH_SetFileHandlerEvent(sock_fd, SCH_FILE_INPUT, 1);
DEBUG_LOG("Resumed RX processing %s timeout fd=%d",
resume_timeout_id ? "before" : "on", sock_fd);
if (resume_timeout_id) {
SCH_RemoveTimeout(resume_timeout_id);
resume_timeout_id = 0;
}
}
/* ================================================== */
static void
resume_timeout(void *arg)
{
resume_timeout_id = 0;
resume_socket(suspended_socket);
}
/* ================================================== */
static void
suspend_socket(int sock_fd)
{
resume_socket(suspended_socket);
suspended_socket = sock_fd;
SCH_SetFileHandlerEvent(suspended_socket, SCH_FILE_INPUT, 0);
resume_timeout_id = SCH_AddTimeoutByDelay(RESUME_TIMEOUT, resume_timeout, NULL);
DEBUG_LOG("Suspended RX processing fd=%d", sock_fd);
}
/* ================================================== */
int
NIO_Linux_ProcessEvent(int sock_fd, int event)
{
if (sock_fd != monitored_socket)
return 0;
if (event == SCH_FILE_INPUT) {
suspend_socket(monitored_socket);
monitored_socket = INVALID_SOCK_FD;
/* Don't process the message yet */
return 1;
}
return 0;
}
/* ================================================== */
static struct Interface *
get_interface(int if_index)
{
@@ -614,6 +737,11 @@ NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *loc
} else {
DEBUG_LOG("HW clock not found for interface %d", ts_if_index);
}
/* If a HW transmit timestamp was received, resume processing
of non-error messages on this socket */
if (is_tx)
resume_socket(local_addr->sock_fd);
}
if (local_ts->source == NTP_TS_DAEMON && !UTI_IsZeroTimespec(&ts3.ts[0]) &&
@@ -638,6 +766,14 @@ NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *loc
}
}
/* If the kernel is slow with enabling RX timestamping, open a dummy
socket to keep the kernel RX timestamping permanently enabled */
if (!is_tx && local_ts->source == NTP_TS_DAEMON && ts_flags) {
DEBUG_LOG("Missing kernel RX timestamp");
if (dummy_rxts_socket == INVALID_SOCK_FD)
dummy_rxts_socket = open_dummy_socket();
}
/* Return the message if it's not received from the error queue */
if (!is_tx)
return 0;
@@ -682,6 +818,15 @@ NIO_Linux_RequestTxTimestamp(struct msghdr *msg, int cmsglen, int sock_fd)
{
struct cmsghdr *cmsg;
if (!ts_flags)
return cmsglen;
/* If a HW transmit timestamp is requested on a client socket, monitor
events on the socket in order to avoid processing of a fast response
without the HW timestamp of the request */
if (ts_tx_flags & SOF_TIMESTAMPING_TX_HARDWARE && !NIO_IsServerSocket(sock_fd))
monitored_socket = sock_fd;
/* Check if TX timestamping is disabled on this socket */
if (permanent_ts_options || !NIO_IsServerSocket(sock_fd))
return cmsglen;
@@ -701,3 +846,11 @@ NIO_Linux_RequestTxTimestamp(struct msghdr *msg, int cmsglen, int sock_fd)
return cmsglen;
}
/* ================================================== */
void
NIO_Linux_NotifySocketClosing(int sock_fd)
{
resume_socket(sock_fd);
}

View File

@@ -24,13 +24,22 @@
This is the header file for the Linux-specific NTP socket I/O bits.
*/
#ifndef GOT_NTP_IO_LINUX_H
#define GOT_NTP_IO_LINUX_H
extern void NIO_Linux_Initialise(void);
extern void NIO_Linux_Finalise(void);
extern int NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events);
extern int NIO_Linux_ProcessEvent(int sock_fd, int event);
extern int NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *local_ts, struct msghdr *hdr, int length);
extern int NIO_Linux_RequestTxTimestamp(struct msghdr *msg, int cmsglen, int sock_fd);
extern void NIO_Linux_NotifySocketClosing(int sock_fd);
#endif

View File

@@ -235,7 +235,7 @@ read_write_socket(int sock_fd, int event, void *anything)
return;
/* Disable output and wait for a response */
SCH_SetFileHandlerEvents(sock_fd, SCH_FILE_INPUT);
SCH_SetFileHandlerEvent(sock_fd, SCH_FILE_OUTPUT, 0);
}
if (event == SCH_FILE_INPUT) {
@@ -283,7 +283,7 @@ read_write_socket(int sock_fd, int event, void *anything)
/* Move the head and enable output for the next packet */
queue_head = NEXT_QUEUE_INDEX(queue_head);
if (!IS_QUEUE_EMPTY())
SCH_SetFileHandlerEvents(sock_fd, SCH_FILE_INPUT | SCH_FILE_OUTPUT);
SCH_SetFileHandlerEvent(sock_fd, SCH_FILE_OUTPUT, 1);
}
}
@@ -369,7 +369,7 @@ NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_Remote_Address *r
/* Enable output if there was no pending request */
if (IS_QUEUE_EMPTY())
SCH_SetFileHandlerEvents(sock_fd, SCH_FILE_INPUT | SCH_FILE_OUTPUT);
SCH_SetFileHandlerEvent(sock_fd, SCH_FILE_OUTPUT, 1);
queue_tail = NEXT_QUEUE_INDEX(queue_tail);

View File

@@ -207,11 +207,12 @@ find_slot(NTP_Remote_Address *remote_addr, int *slot, int *found)
size = ARR_GetSize(records);
*slot = 0;
*found = 0;
if (remote_addr->ip_addr.family != IPADDR_INET4 &&
remote_addr->ip_addr.family != IPADDR_INET6) {
*found = *slot = 0;
remote_addr->ip_addr.family != IPADDR_INET6)
return;
}
hash = UTI_IPToHash(&remote_addr->ip_addr);
port = remote_addr->port;
@@ -230,8 +231,6 @@ find_slot(NTP_Remote_Address *remote_addr, int *slot, int *found)
return;
}
}
*found = 0;
}
/* ================================================== */

View File

@@ -118,6 +118,7 @@ static const struct request_length request_lengths[] = {
{ 0, 0 }, /* ADD_PEER2 */
REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_SERVER3 */
REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_PEER3 */
REQ_LENGTH_ENTRY(null, null), /* SHUTDOWN */
};
static const uint16_t reply_lengths[] = {
@@ -132,13 +133,14 @@ static const uint16_t reply_lengths[] = {
0, /* SUBNETS_ACCESSED - not supported */
0, /* CLIENT_ACCESSES - not supported */
0, /* CLIENT_ACCESSES_BY_INDEX - not supported */
0, /* MANUAL_LIST - variable length */
0, /* MANUAL_LIST - not supported */
RPY_LENGTH_ENTRY(activity), /* ACTIVITY */
RPY_LENGTH_ENTRY(smoothing), /* SMOOTHING */
RPY_LENGTH_ENTRY(server_stats), /* SERVER_STATS */
RPY_LENGTH_ENTRY(client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX2 */
RPY_LENGTH_ENTRY(ntp_data), /* NTP_DATA */
RPY_LENGTH_ENTRY(manual_timestamp), /* MANUAL_TIMESTAMP2 */
RPY_LENGTH_ENTRY(manual_list), /* MANUAL_LIST2 */
};
/* ================================================== */
@@ -195,21 +197,6 @@ PKL_ReplyLength(CMD_Reply *r)
if (type < 1 || type >= N_REPLY_TYPES)
return 0;
/* Length of MANUAL_LIST depends on number of samples stored in it */
if (type == RPY_MANUAL_LIST) {
uint32_t ns;
if (r->status != htons(STT_SUCCESS))
return offsetof(CMD_Reply, data);
ns = ntohl(r->data.manual_list.n_samples);
if (ns > MAX_MANUAL_LIST_SAMPLES)
return 0;
return offsetof(CMD_Reply, data.manual_list.samples) +
ns * sizeof (RPY_ManualListSample);
}
return reply_lengths[type];
}

View File

@@ -268,7 +268,7 @@ do_bind_socket(ReqBindSocket *req, PrvResponse *res)
sock_fd = req->sock;
UTI_SockaddrToIPAndPort(sa, &ip, &port);
if (port && port != CNF_GetNTPPort()) {
if (port && port != CNF_GetNTPPort() && port != CNF_GetAcquisitionPort()) {
close(sock_fd);
res_fatal(res, "Invalid port %d", port);
return;
@@ -579,7 +579,8 @@ PRV_BindSocket(int sock, struct sockaddr *address, socklen_t address_len)
unsigned short port;
UTI_SockaddrToIPAndPort(address, &ip, &port);
assert(!port || port == CNF_GetNTPPort());
if (port && port != CNF_GetNTPPort() && port != CNF_GetAcquisitionPort())
assert(0);
if (!have_helper())
return bind(sock, address, address_len);

View File

@@ -79,6 +79,8 @@ struct RCL_Instance_Record {
int pps_rate;
int pps_active;
int max_lock_age;
int stratum;
int tai;
struct MedianFilter filter;
uint32_t ref_id;
uint32_t lock_ref;
@@ -181,13 +183,13 @@ RCL_AddRefclock(RefclockParameters *params)
inst->driver = &RCL_PHC_driver;
} else {
LOG_FATAL("unknown refclock driver %s", params->driver_name);
return 0;
}
if (!inst->driver->init && !inst->driver->poll) {
if (!inst->driver->init && !inst->driver->poll)
LOG_FATAL("refclock driver %s is not compiled in", params->driver_name);
return 0;
}
if (params->tai && !CNF_GetLeapSecTimezone())
LOG_FATAL("refclock tai option requires leapsectz");
inst->data = NULL;
inst->driver_parameter = params->driver_parameter;
@@ -200,6 +202,8 @@ RCL_AddRefclock(RefclockParameters *params)
inst->pps_rate = params->pps_rate;
inst->pps_active = 0;
inst->max_lock_age = params->max_lock_age;
inst->stratum = params->stratum;
inst->tai = params->tai;
inst->lock_ref = params->lock_ref_id;
inst->offset = params->offset;
inst->delay = params->delay;
@@ -251,11 +255,8 @@ RCL_AddRefclock(RefclockParameters *params)
}
}
if (inst->driver->init)
if (!inst->driver->init(inst)) {
LOG_FATAL("refclock %s initialisation failed", params->driver_name);
return 0;
}
if (inst->driver->init && !inst->driver->init(inst))
LOG_FATAL("refclock %s initialisation failed", params->driver_name);
filter_init(&inst->filter, params->filter_length, params->max_dispersion);
@@ -356,6 +357,28 @@ RCL_GetDriverOption(RCL_Instance instance, char *name)
return NULL;
}
static int
convert_tai_offset(struct timespec *sample_time, double *offset)
{
struct timespec tai_ts, utc_ts;
int tai_offset;
/* Get approximate TAI-UTC offset for the reference time in TAI */
UTI_AddDoubleToTimespec(sample_time, *offset, &tai_ts);
tai_offset = REF_GetTaiOffset(&tai_ts);
/* Get TAI-UTC offset for the reference time in UTC +/- 1 second */
UTI_AddDoubleToTimespec(&tai_ts, -tai_offset, &utc_ts);
tai_offset = REF_GetTaiOffset(&utc_ts);
if (!tai_offset)
return 0;
*offset -= tai_offset;
return 1;
}
int
RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset, int leap)
{
@@ -385,6 +408,11 @@ RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset
return 0;
}
if (instance->tai && !convert_tai_offset(sample_time, &offset)) {
DEBUG_LOG("refclock sample ignored unknown TAI offset");
return 0;
}
filter_add_sample(&instance->filter, &cooked_time, offset - correction + instance->offset, dispersion);
instance->pps_active = 0;
@@ -635,7 +663,7 @@ poll_timeout(void *arg)
/* Handle special case when PPS is used with local stratum */
stratum = pps_stratum(inst, &sample_time);
else
stratum = 0;
stratum = inst->stratum;
SRC_UpdateReachability(inst->source, 1);
SRC_AccumulateSample(inst->source, &sample_time, offset,

View File

@@ -43,6 +43,8 @@ typedef struct {
int max_samples;
int sel_options;
int max_lock_age;
int stratum;
int tai;
uint32_t ref_id;
uint32_t lock_ref_id;
double offset;

View File

@@ -609,7 +609,14 @@ is_offset_ok(double offset)
/* ================================================== */
static int
is_leap_second_day(struct tm *stm) {
is_leap_second_day(time_t when)
{
struct tm *stm;
stm = gmtime(&when);
if (!stm)
return 0;
/* Allow leap second only on the last day of June and December */
return (stm->tm_mon == 5 && stm->tm_mday == 30) ||
(stm->tm_mon == 11 && stm->tm_mday == 31);
@@ -624,7 +631,7 @@ get_tz_leap(time_t when, int *tai_offset)
static NTP_Leap tz_leap;
static int tz_tai_offset;
struct tm stm;
struct tm stm, *tm;
time_t t;
char *tz_env, tz_orig[128];
@@ -639,7 +646,11 @@ get_tz_leap(time_t when, int *tai_offset)
tz_leap = LEAP_Normal;
tz_tai_offset = 0;
stm = *gmtime(&when);
tm = gmtime(&when);
if (!tm)
return tz_leap;
stm = *tm;
/* Temporarily switch to the timezone containing leap seconds */
tz_env = getenv("TZ");
@@ -784,7 +795,7 @@ update_leap_status(NTP_Leap leap, time_t now, int reset)
if (leap == LEAP_InsertSecond || leap == LEAP_DeleteSecond) {
/* Check that leap second is allowed today */
if (is_leap_second_day(gmtime(&now))) {
if (is_leap_second_day(now)) {
if (leap == LEAP_InsertSecond) {
leap_sec = 1;
} else {
@@ -1345,6 +1356,18 @@ int REF_IsLeapSecondClose(void)
/* ================================================== */
int
REF_GetTaiOffset(struct timespec *ts)
{
int tai_offset;
get_tz_leap(ts->tv_sec, &tai_offset);
return tai_offset;
}
/* ================================================== */
void
REF_GetTrackingReport(RPT_TrackingReport *rep)
{

View File

@@ -184,6 +184,9 @@ extern void REF_DisableLocal(void);
and is better to discard any measurements */
extern int REF_IsLeapSecondClose(void);
/* Return TAI-UTC offset corresponding to a time in UTC if available */
extern int REF_GetTaiOffset(struct timespec *ts);
extern void REF_GetTrackingReport(RPT_TrackingReport *rep);
#endif /* GOT_REFERENCE_H */

View File

@@ -219,13 +219,16 @@ SCH_RemoveFileHandler(int fd)
/* ================================================== */
void
SCH_SetFileHandlerEvents(int fd, int events)
SCH_SetFileHandlerEvent(int fd, int event, int enable)
{
FileHandlerEntry *ptr;
assert(events);
ptr = ARR_GetElement(file_handlers, fd);
ptr->events = events;
if (enable)
ptr->events |= event;
else
ptr->events &= ~event;
}
/* ================================================== */

View File

@@ -60,7 +60,7 @@ extern void SCH_Finalise(void);
/* Register a handler for when select goes true on a file descriptor */
extern void SCH_AddFileHandler(int fd, int events, SCH_FileHandler handler, SCH_ArbitraryArgument arg);
extern void SCH_RemoveFileHandler(int fd);
extern void SCH_SetFileHandlerEvents(int fd, int events);
extern void SCH_SetFileHandlerEvent(int fd, int event, int enable);
/* Get the time stamp taken after a file descriptor became ready or a timeout expired */
extern void SCH_GetLastEventTime(struct timespec *cooked, double *err, struct timespec *raw);

View File

@@ -665,6 +665,16 @@ SRC_SelectSource(SRC_Instance updated_inst)
continue;
}
/* Include extra dispersion in the root distance of sources that don't
have new samples (the last sample is older than span of all samples) */
if (first_sample_ago < 2.0 * si->last_sample_ago) {
double extra_disp = LCL_GetMaxClockError() *
(2.0 * si->last_sample_ago - first_sample_ago);
si->root_distance += extra_disp;
si->lo_limit -= extra_disp;
si->hi_limit += extra_disp;
}
/* Require the root distance to be below the allowed maximum */
if (si->root_distance > max_distance) {
sources[i]->status = SRC_BAD_DISTANCE;

View File

@@ -51,6 +51,9 @@
#define MIN_SKEW 1.0e-12
#define MAX_SKEW 1.0e+02
/* The minimum standard deviation */
#define MIN_STDDEV 1.0e-9
/* The asymmetry of network jitter when all jitter is in one direction */
#define MAX_ASYMMETRY 0.5
@@ -571,7 +574,7 @@ SST_DoNewRegression(SST_Stats inst)
inst->estimated_offset = est_intercept;
inst->offset_time = inst->sample_times[inst->last_sample];
inst->estimated_offset_sd = est_intercept_sd;
inst->std_dev = sqrt(est_var);
inst->std_dev = MAX(MIN_STDDEV, sqrt(est_var));
inst->nruns = nruns;
inst->skew = CLAMP(MIN_SKEW, inst->skew, MAX_SKEW);
@@ -884,7 +887,7 @@ SST_LoadFromFile(SST_Stats inst, FILE *in)
char line[1024];
double weight;
assert(!inst->n_samples);
SST_ResetInstance(inst);
if (fgets(line, sizeof(line), in) &&
sscanf(line, "%d", &inst->n_samples) == 1 &&
@@ -933,7 +936,6 @@ SST_LoadFromFile(SST_Stats inst, FILE *in)
return 1;
inst->last_sample = inst->n_samples - 1;
inst->runs_samples = 0;
find_min_delay_sample(inst);
SST_DoNewRegression(inst);

View File

@@ -35,6 +35,7 @@ typedef struct {
int online;
int auto_offline;
int presend_minpoll;
int burst;
int iburst;
int min_stratum;
int poll_target;

2
sys.c
View File

@@ -97,7 +97,7 @@ SYS_Finalise(void)
void SYS_DropRoot(uid_t uid, gid_t gid)
{
#if defined(LINUX) && defined (FEAT_PRIVDROP)
SYS_Linux_DropRoot(uid, gid);
SYS_Linux_DropRoot(uid, gid, !null_driver);
#elif defined(SOLARIS) && defined(FEAT_PRIVDROP)
SYS_Solaris_DropRoot(uid, gid);
#elif (defined(NETBSD) || defined(FREEBSD)) && defined(FEAT_PRIVDROP)

View File

@@ -380,6 +380,18 @@ test_step_offset(void)
return 1;
}
/* ================================================== */
static void
report_time_adjust_blockers(void)
{
#ifdef FEAT_PRIVDROP
if (CAP_IS_SUPPORTED(CAP_SYS_TIME) && cap_get_bound(CAP_SYS_TIME))
return;
LOG(LOGS_WARN, "CAP_SYS_TIME not present");
#endif
}
/* ================================================== */
/* Initialisation code for this module */
@@ -388,6 +400,8 @@ SYS_Linux_Initialise(void)
{
get_version_specific_details();
report_time_adjust_blockers();
reset_adjtime_offset();
if (have_setoffset && !test_step_offset()) {
@@ -415,9 +429,9 @@ SYS_Linux_Finalise(void)
#ifdef FEAT_PRIVDROP
void
SYS_Linux_DropRoot(uid_t uid, gid_t gid)
SYS_Linux_DropRoot(uid_t uid, gid_t gid, int clock_control)
{
const char *cap_text;
char cap_text[256];
cap_t cap;
if (prctl(PR_SET_KEEPCAPS, 1)) {
@@ -426,9 +440,12 @@ SYS_Linux_DropRoot(uid_t uid, gid_t gid)
UTI_DropRoot(uid, gid);
/* Keep CAP_NET_BIND_SERVICE only if NTP port can be opened */
cap_text = CNF_GetNTPPort() ?
"cap_net_bind_service,cap_sys_time=ep" : "cap_sys_time=ep";
/* Keep CAP_NET_BIND_SERVICE only if a server NTP port can be opened
and keep CAP_SYS_TIME only if the clock control is enabled */
if (snprintf(cap_text, sizeof (cap_text), "%s %s",
CNF_GetNTPPort() ? "cap_net_bind_service=ep" : "",
clock_control ? "cap_sys_time=ep" : "") >= sizeof (cap_text))
assert(0);
if ((cap = cap_from_text(cap_text)) == NULL) {
LOG_FATAL("cap_from_text() failed");
@@ -480,7 +497,7 @@ SYS_Linux_EnableSystemCallFilter(int level)
SCMP_SYS(lseek), SCMP_SYS(rename), SCMP_SYS(stat), SCMP_SYS(stat64),
SCMP_SYS(statfs), SCMP_SYS(statfs64), SCMP_SYS(unlink),
/* Socket */
SCMP_SYS(bind), SCMP_SYS(connect), SCMP_SYS(getsockname),
SCMP_SYS(bind), SCMP_SYS(connect), SCMP_SYS(getsockname), SCMP_SYS(getsockopt),
SCMP_SYS(recvfrom), SCMP_SYS(recvmmsg), SCMP_SYS(recvmsg),
SCMP_SYS(sendmmsg), SCMP_SYS(sendmsg), SCMP_SYS(sendto),
/* TODO: check socketcall arguments */

View File

@@ -31,7 +31,7 @@ extern void SYS_Linux_Initialise(void);
extern void SYS_Linux_Finalise(void);
extern void SYS_Linux_DropRoot(uid_t uid, gid_t gid);
extern void SYS_Linux_DropRoot(uid_t uid, gid_t gid, int clock_control);
extern void SYS_Linux_EnableSystemCallFilter(int level);

View File

@@ -6,8 +6,9 @@ for opts in \
"--host-system=Linux" \
"--host-system=NetBSD" \
"--host-system=FreeBSD" \
"--without-nss" \
"--without-tomcrypt --without-nss"
"--without-nettle" \
"--without-nettle --without-nss" \
"--without-nettle --without-nss --without-tomcrypt"
do
./configure $opts
scan-build make "$@" || exit 1

View File

@@ -8,11 +8,20 @@ limit=1000
refclock_jitter=$jitter
min_sync_time=45
max_sync_time=70
client_conf="refclock SHM 0"
chronyc_start=70
client_conf="refclock SHM 0 stratum 3 delay 1e-3 refid GPS"
chronyc_conf="tracking"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
check_chronyc_output "^Reference ID.*47505300 \(GPS\)
Stratum.*: 4
.*
Root delay : 0.001000000 seconds
.*
Update interval : 16\.. seconds
.*$" || test_fail
test_pass

View File

@@ -4,14 +4,30 @@
test_start "chronyc"
chronyc_conf="tracking
refclock_jitter=$jitter
client_conf="
refclock SHM 0 noselect
smoothtime 400 0.001 leaponly"
chronyc_conf="activity
tracking
sources
sourcestats"
sourcestats
manual list
smoothing
waitsync
rtcdata"
run_test || test_fail
check_chronyd_exit || test_fail
check_chronyc_output "^Reference ID : C0A87B01 \(192\.168\.123\.1\)
check_chronyc_output "^200 OK
1 sources online
0 sources offline
0 sources doing burst \(return to online\)
0 sources doing burst \(return to offline\)
0 sources with unknown address
Reference ID : C0A87B01 \(192\.168\.123\.1\)
Stratum : 2
Ref time \(UTC\) : Fri Jan 01 00:1.:.. 2010
System time : 0\.0000..... seconds (slow|fast) of NTP time
@@ -24,14 +40,27 @@ Root delay : 0\.000...... seconds
Root dispersion : 0\.000...... seconds
Update interval : [0-9]+\.. seconds
Leap status : Normal
210 Number of sources = 1
210 Number of sources = 2
MS Name/IP address Stratum Poll Reach LastRx Last sample
===============================================================================
#\? SHM0 0 4 377 [0-9]+ [0-9 +-]+[un]s\[[0-9 +-]+[un]s\] \+/-[ 0-9]+[un]s
\^\* 192\.168\.123\.1 1 [67] 377 [0-9]+ [0-9 +-]+[un]s\[[0-9 +-]+[un]s\] \+/-[ 0-9]+[un]s
210 Number of sources = 1
210 Number of sources = 2
Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev
==============================================================================
192\.168\.123\.1 [0-9 ]+ [0-9 ]+ [0-9 ]+ [ +-][01]\.... [0-9 ]+\.... [0-9 +-]+[un]s [0-9 ]+[un]s$" \
SHM0 [0-9 ]+ [0-9 ]+ [0-9 ]+ [ +-][01]\.... [0-9 ]+\.... [0-9 +-]+[un]s [0-9 ]+[un]s
192\.168\.123\.1 [0-9 ]+ [0-9 ]+ [0-9 ]+ [ +-][01]\.... [0-9 ]+\.... [0-9 +-]+[un]s [0-9 ]+[un]s
210 n_samples = 0
# Date Time\(UTC\) Slewed Original Residual
=======================================================
Active : Yes \(leap second only\)
Offset : \+0\.000000000 seconds
Frequency : \+0\.000000 ppm
Wander : \+0\.000000 ppm per second
Last update : [0-9]+\.. seconds ago
Remaining time : 0\.0 seconds
try: 1, refid: C0A87B01, correction: 0\.000......, skew: .\....
513 RTC driver not running$" \
|| test_fail
test_pass

View File

@@ -5,8 +5,9 @@ test_start "smoothtime option"
server_strata=2
server_conf="smoothtime 400 0.001"
min_sync_time=250
max_sync_time=1000
server_server_options="minpoll 8"
min_sync_time=600
max_sync_time=800
run_test || test_fail
check_chronyd_exit || test_fail
@@ -22,6 +23,7 @@ server_conf="refclock SHM 0 dpoll 4 poll 6
smoothtime 2000 1
maxjitter 10.0"
time_offset=-10
server_server_options=""
client_server_options="minpoll 6 maxpoll 6"
client_conf="corrtimeratio 100"
min_sync_time=8000

42
test/simulation/124-tai Executable file
View File

@@ -0,0 +1,42 @@
#!/bin/bash
. ./test.common
test_start "tai option"
export CLKNETSIM_START_DATE=$(TZ=UTC date -d 'Dec 31 2008 23:50:00' +'%s')
leap=$[10 * 60]
limit=$[20 * 60]
min_sync_time=2
max_sync_time=15
refclock_jitter=1e-6
servers=0
refclock_offset="(+ -34 (equal 0.1 (max (sum 1.0) $leap) $leap))"
client_conf="
refclock SHM 0 dpoll 0 poll 0 tai
leapsectz right/UTC
leapsecmode ignore
maxchange 1e-3 1 0"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
export CLKNETSIM_START_DATE=$(TZ=UTC date -d 'Jan 01 2009 00:10:00' +'%s')
time_offset=-1000
refclock_offset="(+ -34)"
client_conf="
refclock SHM 0 dpoll 0 poll 0 tai
leapsectz right/UTC
makestep 1 1
maxchange 1e-3 1 0"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
test_pass

29
test/simulation/125-packetloss Executable file
View File

@@ -0,0 +1,29 @@
#!/bin/bash
. ./test.common
test_start "packet loss"
# Drop 33% of packets by default and 100% on the 3->1 path
base_delay=$(cat <<-EOF | tr -d '\n'
(+ 1e-4
(* -1 (equal 0.33 (uniform) 1.0))
(* -1 (equal 0.1 from 3) (equal 0.1 to 1)))
EOF
)
clients=2
peers=2
jitter=1e-5
limit=20000
max_sync_time=10000
for options in "maxpoll 8" "maxpoll 8 xleave"; do
client_server_options=$options
client_peer_options=$options
run_test || test_fail
check_chronyd_exit || test_fail
check_sync || test_fail
done
test_pass

45
test/simulation/126-burst Executable file
View File

@@ -0,0 +1,45 @@
#!/bin/bash
. ./test.common
test_start "burst option"
# Pass every fourth packet on the 2->1 path
base_delay=$(cat <<-EOF | tr -d '\n'
(+ 1e-4
(* -1
(equal 0.1 from 2)
(equal 0.1 to 1)
(equal 0.1 (min (% (sum 1) 4) 1) 1)))
EOF
)
client_server_options="burst polltarget 1"
min_sync_time=700
max_sync_time=730
client_max_min_out_interval=2.2
client_min_mean_out_interval=150.0
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
# Add a significant delay to 70% of packets on the 2->1 path after 6th packet
base_delay=$(cat <<-EOF | tr -d '\n'
(+ 1e-4
(* 0.15
(equal 0.1 from 2)
(equal 0.1 to 1)
(equal 0.1 (min (sum 1) 7) 7)
(equal 0.7 (uniform) 0.0)))
EOF
)
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
test_pass

View File

@@ -71,6 +71,9 @@ default_freq_rms_limit=1e-5
default_min_sync_time=120
default_max_sync_time=210
default_client_min_mean_out_interval=0.0
default_client_max_min_out_interval=inf
# Initialize test settings from their defaults
for defopt in $(declare | grep '^default_'); do
defoptname=${defopt%%=*}
@@ -251,6 +254,7 @@ check_chronyd_exit() {
test_message 3 0 "node $i:"
tail -n 1 tmp/log.$i | grep -q 'chronyd exiting' && \
! grep -q 'Adjustment.*exceeds.*exiting' tmp/log.$i && \
test_ok || test_bad
[ $? -eq 0 ] || ret=1
done
@@ -302,10 +306,12 @@ check_packet_interval() {
([ $i -gt $servers ] || \
check_stat $mean_in_interval 0.0 $mean_out_interval 10*$jitter) && \
([ $i -le $[$servers * $server_strata] ] || \
check_stat $mean_out_interval 0.0 $mean_in_interval 10*$jitter) && \
check_stat $mean_out_interval $client_min_mean_out_interval \
$mean_in_interval 10*$jitter) && \
([ $i -le $[$servers * $server_strata] ] || \
check_stat $min_out_interval \
$([ $servers -gt 1 ] && echo 0.18 || echo 1.8) inf) && \
$([ $servers -gt 1 ] && echo 0.18 || echo 1.8) \
$client_max_min_out_interval) && \
test_ok || test_bad
[ $? -eq 0 ] || ret=1

123
test/unit/hash.c Normal file
View File

@@ -0,0 +1,123 @@
/*
**********************************************************************
* Copyright (C) Miroslav Lichvar 2018
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
*/
#include <config.h>
#include <sysincl.h>
#include <hash.h>
#include <logging.h>
#include "test.h"
struct hash_test {
const char *name;
const unsigned char out[MAX_HASH_LENGTH];
unsigned int length;
};
void
test_unit(void)
{
unsigned char data1[] = "abcdefghijklmnopqrstuvwxyz";
unsigned char data2[] = "12345678910";
unsigned char out[MAX_HASH_LENGTH];
struct hash_test tests[] = {
{ "MD5", "\xfc\x24\x97\x1b\x52\x66\xdc\x46\xef\xe0\xe8\x08\x46\x89\xb6\x88", 16 },
{ "SHA1", "\xd8\x85\xb3\x86\xce\xea\x93\xeb\x92\xcd\x7b\x94\xb9\x8d\xc2\x8e"
"\x3e\x31\x13\xdd", 20},
{ "SHA256", "\x0e\x35\x14\xe7\x15\x7a\x1d\xdd\xea\x11\x78\xd3\x41\xf6\xb9\x3e"
"\xa0\x42\x96\x73\x3c\x54\x74\x0b\xfa\x6b\x9e\x29\x59\xad\x69\xd3", 32 },
{ "SHA384", "\x2c\xeb\xbd\x4d\x95\xed\xad\x03\xed\x80\xc4\xf3\xa6\x10\x21\xde"
"\x40\x69\x54\xed\x42\x70\xb8\x95\xb0\x6f\x01\x1d\x04\xdf\x57\xbc"
"\x1d\xb5\x85\xbf\x4f\x03\x88\xd5\x83\x93\xbc\x81\x90\xb0\xa9\x9b", 48 },
{ "SHA512", "\x20\xba\xec\xcb\x68\x98\x33\x5b\x70\x26\x63\x13\xe2\xf7\x0e\x67"
"\x08\xf3\x77\x4f\xbd\xeb\xc4\xa8\xc5\x94\xe2\x39\x40\x7e\xed\x0b"
"\x69\x0e\x18\xa5\xa2\x03\x73\xe7\x1d\x20\x7d\x3f\xc8\x70\x2d\x64"
"\x9e\x89\x6d\x20\x6a\x4a\x5a\x46\xe7\x4f\x2c\xf9\x0f\x0a\x54\xdc", 64 },
{ "SHA3-224", "\x3b\xa2\x22\x28\xdd\x26\x18\xec\x3b\xb9\x25\x39\x5e\xbd\x94\x25"
"\xd4\x20\x8a\x76\x76\xc0\x3c\x5d\x9e\x0a\x06\x46", 28},
{ "SHA3-256", "\x26\xd1\x19\xb2\xc1\x64\xc8\xb8\x10\xd8\xa8\x1c\xb6\xa4\x0d\x29"
"\x09\xc9\x8e\x2e\x2d\xde\x7a\x74\x8c\x43\x70\xb8\xaa\x0f\x09\x17", 32 },
{ "SHA3-384", "\x6a\x64\xb9\x89\x08\x29\xd0\xa7\x4b\x84\xba\xa6\x65\xf5\xe7\x54"
"\xe2\x18\x12\xc3\x63\x34\xc6\xba\x26\xf5\x6e\x99\xe2\x54\xcc\x9d"
"\x01\x10\x9d\xee\x35\x38\x04\x83\xe5\x71\x70\xd8\xc8\x99\x96\xd8", 48 },
{ "SHA3-512", "\xa8\xe3\x2b\x65\x1f\x87\x90\x73\x19\xc8\xa0\x3f\xe3\x85\x60\x3c"
"\x39\xfc\xcb\xc1\x29\xe1\x23\x7d\x8b\x56\x54\xe3\x08\x9d\xf9\x74"
"\x78\x69\x2e\x3c\x7e\x51\x1e\x9d\xab\x09\xbe\xe7\x6b\x1a\xa1\x22"
"\x93\xb1\x2b\x82\x9d\x1e\xcf\xa8\x99\xc5\xec\x7b\x1d\x89\x07\x2b", 64 },
{ "RMD128", "\x6f\xd7\x1f\x37\x47\x0f\xbd\x42\x57\xc8\xbb\xee\xba\x65\xf9\x35", 16 },
{ "RMD160", "\x7a\x88\xec\xc7\x09\xc5\x65\x34\x11\x24\xe3\xf9\xf7\xa5\xbf\xc6"
"\x01\xe2\xc9\x32", 20},
{ "RMD256", "\x59\xdf\xd4\xcb\xc9\xbe\x7c\x27\x08\xa7\x23\xf7\xb3\x0c\xf0\x0d"
"\xa0\xcf\x5b\x18\x16\x51\x56\x6d\xda\x7b\x87\x24\x9d\x83\x35\xe1", 32 },
{ "RMD320", "\x68\x98\x10\xf4\xb6\x79\xb6\x15\xf1\x48\x2d\x73\xd0\x23\x84\x01"
"\xbf\xaa\x67\xcf\x1e\x35\x5c\xbf\xe9\xb8\xaf\xe1\xee\x0d\xf0\x6b"
"\xe2\x3a\x9a\x3a\xa7\x56\xad\x70", 40},
{ "TIGER", "\x1c\xcd\x68\x74\xca\xd6\xd5\x17\xba\x3e\x82\xaf\xbd\x70\xdc\x66"
"\x99\xaa\xae\x16\x72\x59\xd1\x64", 24},
{ "WHIRLPOOL", "\xe3\xcd\xe6\xbf\xe1\x8c\xe4\x4d\xc8\xb4\xa5\x7c\x36\x8d\xc8\x8a"
"\x8b\xad\x52\x24\xc0\x4e\x99\x5b\x7e\x86\x94\x2d\x10\x56\x12\xa3"
"\x29\x2a\x65\x0f\x9e\x07\xbc\x15\x21\x14\xe6\x07\xfc\xe6\xb9\x2f"
"\x13\xe2\x57\xe9\x0a\xb0\xd2\xf4\xa3\x20\x36\x9c\x88\x92\x8e\xc9", 64 },
{ "", "", 0 }
};
unsigned int length;
int i, j, hash_id;
for (i = 0; tests[i].name[0] != '\0'; i++) {
hash_id = HSH_GetHashId(tests[i].name);
if (hash_id < 0) {
TEST_CHECK(strcmp(tests[i].name, "MD5"));
#ifdef FEAT_SECHASH
TEST_CHECK(strcmp(tests[i].name, "SHA1"));
TEST_CHECK(strcmp(tests[i].name, "SHA256"));
TEST_CHECK(strcmp(tests[i].name, "SHA384"));
TEST_CHECK(strcmp(tests[i].name, "SHA512"));
#endif
continue;
}
DEBUG_LOG("testing %s", tests[i].name);
for (j = 0; j <= sizeof (out); j++) {
TEST_CHECK(HSH_GetHashId(tests[i].name) == hash_id);
TEST_CHECK(HSH_GetHashId("nosuchhash") < 0);
memset(out, 0, sizeof (out));
length = HSH_Hash(hash_id, data1, sizeof (data1) - 1, data2, sizeof (data2) - 1,
out, j);
if (j >= tests[i].length)
TEST_CHECK(length == tests[i].length);
else
TEST_CHECK(length == 0 || length == j || length == tests[i].length);
TEST_CHECK(!memcmp(out, tests[i].out, length));
}
for (j = 0; j < 10000; j++) {
length = HSH_Hash(hash_id, data1, random() % sizeof (data1),
random() % 2 ? data2 : NULL, random() % sizeof (data2),
out, sizeof (out));
TEST_CHECK(length == tests[i].length);
}
}
HSH_Finalise();
}

View File

@@ -1,6 +1,6 @@
/*
**********************************************************************
* Copyright (C) Miroslav Lichvar 2016
* Copyright (C) Miroslav Lichvar 2016-2017
*
* 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

View File

@@ -134,8 +134,8 @@ test_unit(void)
UTI_GetRandomBytes(&key, sizeof (key));
if (KEY_KeyKnown(key))
continue;
TEST_CHECK(!KEY_GenerateAuth(j, data, data_len, auth, sizeof (auth)));
TEST_CHECK(!KEY_CheckAuth(j, data, data_len, auth, auth_len, auth_len));
TEST_CHECK(!KEY_GenerateAuth(key, data, data_len, auth, sizeof (auth)));
TEST_CHECK(!KEY_CheckAuth(key, data, data_len, auth, auth_len, auth_len));
}
}

View File

@@ -1,6 +1,6 @@
/*
**********************************************************************
* Copyright (C) Miroslav Lichvar 2017
* Copyright (C) Miroslav Lichvar 2017-2018
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -36,6 +36,7 @@ static int req_length, res_length;
#define NIO_CloseServerSocket(fd) assert(fd == 100)
#define NIO_OpenClientSocket(addr) ((addr)->ip_addr.family != IPADDR_UNSPEC ? 101 : 0)
#define NIO_CloseClientSocket(fd) assert(fd == 101)
#define NIO_IsServerSocket(fd) (fd == 100)
#define NIO_SendPacket(msg, to, from, len, process_tx) (memcpy(&req_buffer, msg, len), req_length = len, 1)
#define SCH_AddTimeoutByDelay(delay, handler, arg) (1 ? 102 : (handler(arg), 1))
#define SCH_AddTimeoutInClass(delay, separation, randomness, class, handler, arg) \
@@ -43,6 +44,7 @@ static int req_length, res_length;
#define SCH_RemoveTimeout(id) assert(!id || id == 102)
#define LCL_ReadRawTime(ts) (*ts = current_time)
#define LCL_ReadCookedTime(ts, err) do {double *p = err; *ts = current_time; if (p) *p = 0.0;} while (0)
#define LCL_GetSysPrecisionAsLog() (random() % 10 - 30)
#define SRC_UpdateReachability(inst, reach)
#define SRC_ResetReachability(inst)
@@ -55,8 +57,6 @@ add_timeout_in_class(double min_delay, double separation, double randomness,
#include <ntp_core.c>
static NCR_Instance inst;
static void
advance_time(double x)
{
@@ -64,7 +64,7 @@ advance_time(double x)
}
static void
send_request(void)
send_request(NCR_Instance inst)
{
NTP_Local_Address local_addr;
NTP_Local_Timestamp local_ts;
@@ -74,19 +74,48 @@ send_request(void)
transmit_timeout(inst);
TEST_CHECK(!inst->valid_rx);
TEST_CHECK(!inst->updated_timestamps);
TEST_CHECK(prev_tx_count + 1 == inst->report.total_tx_count);
advance_time(1e-4);
advance_time(1e-5);
if (random() % 2) {
local_addr.ip_addr.family = IPADDR_UNSPEC;
local_addr.if_index = INVALID_IF_INDEX;
local_addr.sock_fd = 101;
local_ts.ts = current_time;
local_ts.err = 0.0;
local_ts.source = NTP_TS_KERNEL;
NCR_ProcessTxKnown(inst, &local_addr, &local_ts, &req_buffer.ntp_pkt, req_length);
}
}
static void
process_request(NTP_Remote_Address *remote_addr)
{
NTP_Local_Address local_addr;
NTP_Local_Timestamp local_ts;
local_addr.ip_addr.family = IPADDR_UNSPEC;
local_addr.if_index = INVALID_IF_INDEX;
local_addr.sock_fd = 101;
local_addr.sock_fd = 100;
local_ts.ts = current_time;
local_ts.err = 0.0;
local_ts.source = NTP_TS_DAEMON;
local_ts.source = NTP_TS_KERNEL;
NCR_ProcessTxKnown(inst, &local_addr, &local_ts, &req_buffer.ntp_pkt, req_length);
res_length = 0;
NCR_ProcessRxUnknown(remote_addr, &local_addr, &local_ts,
&req_buffer.ntp_pkt, req_length);
res_length = req_length;
res_buffer = req_buffer;
advance_time(1e-5);
if (random() % 2) {
local_ts.ts = current_time;
NCR_ProcessTxUnknown(remote_addr, &local_addr, &local_ts,
&res_buffer.ntp_pkt, res_length);
}
}
static void
@@ -165,30 +194,36 @@ send_response(int interleaved, int authenticated, int allow_update, int valid_ts
}
static void
process_response(int valid, int updated)
process_response(NCR_Instance inst, int good, int valid, int updated_sync, int updated_init)
{
NTP_Local_Address local_addr;
NTP_Local_Timestamp local_ts;
NTP_Packet *res;
uint32_t prev_rx_count, prev_valid_count;
struct timespec prev_rx_ts;
int prev_open_socket;
struct timespec prev_rx_ts, prev_init_rx_ts;
int prev_open_socket, ret;
res = &res_buffer.ntp_pkt;
local_addr.ip_addr.family = IPADDR_UNSPEC;
local_addr.if_index = INVALID_IF_INDEX;
local_addr.sock_fd = NTP_LVM_TO_MODE(res->lvm) == MODE_ACTIVE ? 100 : 101;
local_addr.sock_fd = NTP_LVM_TO_MODE(res->lvm) != MODE_SERVER ? 100 : 101;
local_ts.ts = current_time;
local_ts.err = 0.0;
local_ts.source = NTP_TS_DAEMON;
local_ts.source = NTP_TS_KERNEL;
prev_rx_count = inst->report.total_rx_count;
prev_valid_count = inst->report.total_valid_count;
prev_rx_ts = inst->local_rx.ts;
prev_init_rx_ts = inst->init_local_rx.ts;
prev_open_socket = inst->local_addr.sock_fd != INVALID_SOCK_FD;
NCR_ProcessRxKnown(inst, &local_addr, &local_ts, res, res_length);
ret = NCR_ProcessRxKnown(inst, &local_addr, &local_ts, res, res_length);
if (good > 0)
TEST_CHECK(ret);
else if (!good)
TEST_CHECK(!ret);
if (prev_open_socket)
TEST_CHECK(prev_rx_count + 1 == inst->report.total_rx_count);
@@ -200,23 +235,51 @@ process_response(int valid, int updated)
else
TEST_CHECK(prev_valid_count == inst->report.total_valid_count);
if (updated)
if (updated_sync)
TEST_CHECK(UTI_CompareTimespecs(&inst->local_rx.ts, &prev_rx_ts));
else
TEST_CHECK(!UTI_CompareTimespecs(&inst->local_rx.ts, &prev_rx_ts));
if (updated_init > 0)
TEST_CHECK(UTI_CompareTimespecs(&inst->init_local_rx.ts, &prev_init_rx_ts));
else if (!updated_init)
TEST_CHECK(!UTI_CompareTimespecs(&inst->init_local_rx.ts, &prev_init_rx_ts));
if (valid) {
TEST_CHECK(UTI_IsZeroTimespec(&inst->init_local_rx.ts));
TEST_CHECK(UTI_IsZeroNtp64(&inst->init_remote_ntp_tx));
}
}
static void
process_replay(NCR_Instance inst, NTP_Receive_Buffer *packet_queue,
int queue_length, int updated_init)
{
do {
res_buffer = packet_queue[random() % queue_length];
} while (!UTI_CompareNtp64(&res_buffer.ntp_pkt.transmit_ts,
&inst->remote_ntp_tx));
process_response(inst, 0, 0, 0, updated_init);
advance_time(1e-6);
}
#define PACKET_QUEUE_LENGTH 10
void
test_unit(void)
{
char source_line[] = "127.0.0.1";
char source_line[] = "127.0.0.1 maxdelaydevratio 1e6";
char conf[][100] = {
"allow",
"port 0",
"local",
"keyfile ntp_core.keys"
};
int i, j, interleaved, authenticated, valid, updated, has_updated;
int i, j, k, interleaved, authenticated, valid, updated, has_updated;
CPS_NTP_Source source;
NTP_Remote_Address remote_addr;
NCR_Instance inst1, inst2;
NTP_Receive_Buffer packet_queue[PACKET_QUEUE_LENGTH];
CNF_Initialise(0, 0);
for (i = 0; i < sizeof conf / sizeof conf[0]; i++)
@@ -231,12 +294,16 @@ test_unit(void)
REF_Initialise();
KEY_Initialise();
CNF_SetupAccessRestrictions();
CPS_ParseNTPSourceAdd(source_line, &source);
for (i = 0; i < 1000; i++) {
CPS_ParseNTPSourceAdd(source_line, &source);
if (random() % 2)
source.params.interleaved = 1;
if (random() % 2)
source.params.authkey = 1;
source.params.version = random() % 4 + 1;
UTI_ZeroTimespec(&current_time);
advance_time(TST_GetRandomDouble(1.0, 1e9));
@@ -244,48 +311,125 @@ test_unit(void)
TST_GetRandomAddress(&remote_addr.ip_addr, IPADDR_UNSPEC, -1);
remote_addr.port = 123;
inst = NCR_GetInstance(&remote_addr, random() % 2 ? NTP_SERVER : NTP_PEER, &source.params);
NCR_StartInstance(inst);
inst1 = NCR_GetInstance(&remote_addr, random() % 2 ? NTP_SERVER : NTP_PEER, &source.params);
NCR_StartInstance(inst1);
has_updated = 0;
for (j = 0; j < 50; j++) {
DEBUG_LOG("iteration %d, %d", i, j);
DEBUG_LOG("client/peer test iteration %d/%d", i, j);
interleaved = random() % 2 && (inst->mode != MODE_CLIENT ||
inst->tx_count < MAX_CLIENT_INTERLEAVED_TX);
interleaved = random() % 2 && (inst1->mode != MODE_CLIENT ||
inst1->tx_count < MAX_CLIENT_INTERLEAVED_TX);
authenticated = random() % 2;
valid = (!interleaved || (source.params.interleaved && has_updated)) &&
(!source.params.authkey || authenticated);
updated = (valid || inst->mode == MODE_ACTIVE) &&
updated = (valid || inst1->mode == MODE_ACTIVE) &&
(!source.params.authkey || authenticated);
has_updated = has_updated || updated;
if (inst1->mode == MODE_CLIENT)
updated = 0;
send_request();
send_request(inst1);
send_response(interleaved, authenticated, 1, 0, 1);
process_response(0, inst->mode == MODE_CLIENT ? 0 : updated);
DEBUG_LOG("response 1");
process_response(inst1, 0, 0, 0, updated);
if (source.params.authkey) {
send_response(interleaved, authenticated, 1, 1, 0);
process_response(0, 0);
DEBUG_LOG("response 2");
process_response(inst1, 0, 0, 0, 0);
}
send_response(interleaved, authenticated, 1, 1, 1);
process_response(valid, updated);
process_response(0, 0);
DEBUG_LOG("response 3");
process_response(inst1, -1, valid, valid, updated);
DEBUG_LOG("response 4");
process_response(inst1, 0, 0, 0, 0);
advance_time(-1.0);
send_response(interleaved, authenticated, 1, 1, 1);
process_response(0, 0);
DEBUG_LOG("response 5");
process_response(inst1, 0, 0, 0, updated && valid);
advance_time(1.0);
send_response(interleaved, authenticated, 1, 1, 1);
process_response(0, inst->mode == MODE_CLIENT ? 0 : updated);
DEBUG_LOG("response 6");
process_response(inst1, 0, 0, valid && updated, updated);
}
NCR_DestroyInstance(inst);
NCR_DestroyInstance(inst1);
inst1 = NCR_GetInstance(&remote_addr, random() % 2 ? NTP_SERVER : NTP_PEER, &source.params);
NCR_StartInstance(inst1);
for (j = 0; j < 20; j++) {
DEBUG_LOG("server test iteration %d/%d", i, j);
send_request(inst1);
process_request(&remote_addr);
process_response(inst1, 1, 1, 1, 0);
advance_time(1 << inst1->local_poll);
}
NCR_DestroyInstance(inst1);
inst1 = NCR_GetInstance(&remote_addr, NTP_PEER, &source.params);
NCR_StartInstance(inst1);
inst2 = NCR_GetInstance(&remote_addr, NTP_PEER, &source.params);
NCR_StartInstance(inst2);
res_length = req_length = 0;
for (j = 0; j < 20; j++) {
DEBUG_LOG("peer replay test iteration %d/%d", i, j);
send_request(inst1);
res_buffer = req_buffer;
assert(!res_length || res_length == req_length);
res_length = req_length;
TEST_CHECK(inst1->valid_timestamps == (j > 0));
DEBUG_LOG("response 1->2");
process_response(inst2, j > source.params.interleaved, j > 0, j > 0, 1);
packet_queue[(j * 2) % PACKET_QUEUE_LENGTH] = res_buffer;
for (k = 0; k < j % 4 + 1; k++) {
DEBUG_LOG("replay ?->1 %d", k);
process_replay(inst1, packet_queue, MIN(j * 2 + 1, PACKET_QUEUE_LENGTH), k ? -1 : 1);
DEBUG_LOG("replay ?->2 %d", k);
process_replay(inst2, packet_queue, MIN(j * 2 + 1, PACKET_QUEUE_LENGTH), -1);
}
advance_time(1 << (source.params.minpoll - 1));
send_request(inst2);
res_buffer = req_buffer;
assert(res_length == req_length);
TEST_CHECK(inst2->valid_timestamps == (j > 0));
DEBUG_LOG("response 2->1");
process_response(inst1, 1, 1, 1, 1);
packet_queue[(j * 2 + 1) % PACKET_QUEUE_LENGTH] = res_buffer;
for (k = 0; k < j % 4 + 1; k++) {
DEBUG_LOG("replay ?->1 %d", k);
process_replay(inst1, packet_queue, MIN(j * 2 + 2, PACKET_QUEUE_LENGTH), k ? -1 : 1);
DEBUG_LOG("replay ?->2 %d", k);
process_replay(inst2, packet_queue, MIN(j * 2 + 2, PACKET_QUEUE_LENGTH), -1);
}
advance_time(1 << (source.params.minpoll - 1));
}
NCR_DestroyInstance(inst1);
NCR_DestroyInstance(inst2);
}
KEY_Finalise();

View File

@@ -4,6 +4,7 @@
void test_unit(void) {
NTP_int64 ntp_ts, ntp_fuzz;
struct timespec ts, ts2;
struct timeval tv;
struct sockaddr_un sun;
double x, y;
Float f;
@@ -40,6 +41,32 @@ void test_unit(void) {
TEST_CHECK(UTI_DoubleToNtp32(1000000) == htonl(0xffffffff));
TEST_CHECK(UTI_DoubleToNtp32(-1.0) == htonl(0));
UTI_DoubleToTimeval(0.4e-6, &tv);
TEST_CHECK(tv.tv_sec == 0);
TEST_CHECK(tv.tv_usec == 0);
UTI_DoubleToTimeval(-0.4e-6, &tv);
TEST_CHECK(tv.tv_sec == 0);
TEST_CHECK(tv.tv_usec == 0);
UTI_DoubleToTimeval(0.5e-6, &tv);
TEST_CHECK(tv.tv_sec == 0);
TEST_CHECK(tv.tv_usec == 1);
UTI_DoubleToTimeval(-0.5e-6, &tv);
TEST_CHECK(tv.tv_sec == -1);
TEST_CHECK(tv.tv_usec == 999999);
UTI_DoubleToTimespec(0.9e-9, &ts);
TEST_CHECK(ts.tv_sec == 0);
TEST_CHECK(ts.tv_nsec == 0);
UTI_DoubleToTimespec(1.0e-9, &ts);
TEST_CHECK(ts.tv_sec == 0);
TEST_CHECK(ts.tv_nsec == 1);
UTI_DoubleToTimespec(-0.9e-9, &ts);
TEST_CHECK(ts.tv_sec == 0);
TEST_CHECK(ts.tv_nsec == 0);
UTI_DoubleToTimespec(-1.0e-9, &ts);
TEST_CHECK(ts.tv_sec == -1);
TEST_CHECK(ts.tv_nsec == 999999999);
ntp_ts.hi = htonl(JAN_1970);
ntp_ts.lo = 0xffffffff;
UTI_Ntp64ToTimespec(&ntp_ts, &ts);
@@ -109,6 +136,11 @@ void test_unit(void) {
TEST_CHECK(UTI_CompareNtp64(&ntp_ts, &ntp_fuzz) < 0);
TEST_CHECK(UTI_CompareNtp64(&ntp_fuzz, &ntp_ts) > 0);
TEST_CHECK(UTI_IsEqualAnyNtp64(&ntp_ts, &ntp_ts, NULL, NULL));
TEST_CHECK(UTI_IsEqualAnyNtp64(&ntp_ts, NULL, &ntp_ts, NULL));
TEST_CHECK(UTI_IsEqualAnyNtp64(&ntp_ts, NULL, NULL, &ntp_ts));
TEST_CHECK(!UTI_IsEqualAnyNtp64(&ntp_ts, &ntp_fuzz, &ntp_fuzz, &ntp_fuzz));
ts.tv_sec = 1;
ts.tv_nsec = 2;
ts2.tv_sec = 1;

41
util.c
View File

@@ -119,13 +119,11 @@ UTI_TimevalToDouble(struct timeval *tv)
void
UTI_DoubleToTimeval(double a, struct timeval *b)
{
long int_part;
double frac_part;
int_part = (long)(a);
frac_part = 1.0e6 * (a - (double)(int_part));
frac_part = frac_part > 0 ? frac_part + 0.5 : frac_part - 0.5;
b->tv_sec = int_part;
b->tv_usec = (long)frac_part;
b->tv_sec = a;
frac_part = 1.0e6 * (a - b->tv_sec);
b->tv_usec = frac_part > 0 ? frac_part + 0.5 : frac_part - 0.5;
UTI_NormaliseTimeval(b);
}
@@ -368,16 +366,14 @@ UTI_IPToRefid(IPAddr *ip)
case IPADDR_INET4:
return ip->addr.in4;
case IPADDR_INET6:
if (MD5_hash < 0) {
if (MD5_hash < 0)
MD5_hash = HSH_GetHashId("MD5");
assert(MD5_hash >= 0);
}
if (HSH_Hash(MD5_hash, (unsigned const char *)ip->addr.in6, sizeof
(ip->addr.in6), NULL, 0, buf, 16) != 16) {
assert(0);
return 0;
};
if (MD5_hash < 0 ||
HSH_Hash(MD5_hash, (const unsigned char *)ip->addr.in6, sizeof (ip->addr.in6),
NULL, 0, buf, sizeof (buf)) != sizeof (buf))
LOG_FATAL("Could not get MD5");
return (uint32_t)buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
}
return 0;
@@ -728,6 +724,23 @@ UTI_CompareNtp64(NTP_int64 *a, NTP_int64 *b)
/* ================================================== */
int
UTI_IsEqualAnyNtp64(NTP_int64 *a, NTP_int64 *b1, NTP_int64 *b2, NTP_int64 *b3)
{
if (b1 && a->lo == b1->lo && a->hi == b1->hi)
return 1;
if (b2 && a->lo == b2->lo && a->hi == b2->hi)
return 1;
if (b3 && a->lo == b3->lo && a->hi == b3->hi)
return 1;
return 0;
}
/* ================================================== */
/* Seconds part of NTP timestamp correponding to the origin of the time_t format */
#define JAN_1970 0x83aa7e80UL

4
util.h
View File

@@ -136,6 +136,10 @@ extern int UTI_IsZeroNtp64(NTP_int64 *ts);
b, and 1 if a is after b. */
extern int UTI_CompareNtp64(NTP_int64 *a, NTP_int64 *b);
/* Compare an NTP timestamp with up to three other timestamps. Returns 0
if a is not equal to any of b1, b2, and b3, 1 otherwise. */
extern int UTI_IsEqualAnyNtp64(NTP_int64 *a, NTP_int64 *b1, NTP_int64 *b2, NTP_int64 *b3);
/* Convert a timespec into an NTP timestamp */
extern void UTI_TimespecToNtp64(struct timespec *src, NTP_int64 *dest, NTP_int64 *fuzz);