Compare commits

...

75 Commits

Author SHA1 Message Date
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
52 changed files with 1374 additions and 307 deletions

23
NEWS
View File

@@ -1,3 +1,26 @@
New in version 3.3
==================
Enhancements
------------
* Add burst option to server/pool directive
* Add stratum and tai options to refclock directive
* Add support for Nettle crypto library
* Add workaround for missing kernel receive timestamps on Linux
* Wait for late hardware transmit timestamps
* Improve source selection with unreachable sources
* Improve protection against replay attacks on symmetric mode
* Allow PHC refclock to use socket in /var/run/chrony
* Add shutdown command to stop chronyd
* Simplify format of response to manual list command
* Improve handling of unknown responses in chronyc
Bug fixes
---------
* Respond to NTPv1 client requests with zero mode
* Fix -x option to not require CAP_SYS_TIME under non-root user
* Fix chronyc to not get stuck in infinite loop after clock step
New in version 3.2 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 The file INSTALL gives instructions. On supported systems the
compilation process should be automatic. compilation process should be automatic. You will need a C compiler,
e.g. gcc or clang.
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.
What documentation is there? What documentation is there?
============================ ============================
A manual is supplied in Texinfo format (chrony.texi) and The distribution includes manual pages and a document containing
ready-formatted plain text (chrony.txt) in the distribution. 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 through the URL
https://chrony.tuxfamily.org/ 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 'sources' and 'sourcestats' output from chronyc
Improvements to documentation Improvements to documentation
Investigation of required dosynctodr behaviour for various Solaris Investigation of required dosynctodr behaviour for various Solaris
versions. versions
Stephan I. Boettcher <stephan@nevis1.columbia.edu> Stephan I. Boettcher <stephan@nevis1.columbia.edu>
Entries in contrib directory Entries in contrib directory
@@ -140,27 +136,27 @@ Bryan Christianson <bryan@whatroute.net>
Entries in contrib directory Entries in contrib directory
Juliusz Chroboczek <jch@pps.jussieu.fr> 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> Paul Elliott <pelliott@io.com>
DNSchrony (in contrib directory), a tool for handling NTP servers Entries in contrib directory
with variable IP addresses.
Mike Fleetwood <mike@rockover.demon.co.uk> Mike Fleetwood <mike@rockover.demon.co.uk>
Fixes for compiler warnings Fixes for compiler warnings
Alexander Gretencord <arutha@gmx.de> Alexander Gretencord <arutha@gmx.de>
Changes to installation directory system to make it easier for Changes to installation directory system to make it easier for
package builders. package builders
Andrew Griffiths <agriffit@redhat.com> Andrew Griffiths <agriffit@redhat.com>
Patch to add support for seccomp filter Patch to add support for seccomp filter
Walter Haidinger <walter.haidinger@gmx.at> Walter Haidinger <walter.haidinger@gmx.at>
Providing me with login access to a Linux installation where v1.12 Access to a Linux installation where v1.12 wouldn't compile
wouldn't compile, so I could develop the fixes for v1.13. Also, for Disc space for an independent backup of the sources
providing the disc space so I can keep an independent backup of the
sources.
Juergen Hannken-Illjes <hannken@eis.cs.tu-bs.de> Juergen Hannken-Illjes <hannken@eis.cs.tu-bs.de>
Port to NetBSD Port to NetBSD
@@ -170,7 +166,7 @@ John Hasler <john@dhh.gt.org>
Changes to support 64 bit machines (i.e. those where Changes to support 64 bit machines (i.e. those where
sizeof(unsigned long) > 4) sizeof(unsigned long) > 4)
Bug fix to initstepslew directive 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 Memory locking and real-time scheduler support
Fix fault where chronyd enters an endless loop 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 Patch to support Linux with HZ!=100
Kalle Olavi Niemitalo <tosi@stekt.oulu.fi> Kalle Olavi Niemitalo <tosi@stekt.oulu.fi>
acquisitionport support Patch to add acquisitionport directive
Frank Otto <sandwichmacher@web.de> Frank Otto <sandwichmacher@web.de>
Handling arbitrary HZ values Handling arbitrary HZ values
@@ -206,12 +202,18 @@ Frank Otto <sandwichmacher@web.de>
Denny Page <dennypage@me.com> Denny Page <dennypage@me.com>
Advice on support for hardware timestamping 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> Gautier PHILIPPON <gautier.philippon@ensimag.grenoble-inp.fr>
Patch to add refresh command to chronyc Patch to add refresh command to chronyc
Andreas Piesk <apiesk@virbus.de> Andreas Piesk <apiesk@virbus.de>
Patch to make chronyc use the readline library if available 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> Timo Teras <timo.teras@iki.fi>
Patch to reply correctly on multihomed hosts Patch to reply correctly on multihomed hosts
@@ -228,8 +230,7 @@ Ralf Wildenhues <Ralf.Wildenhues@gmx.de>
Many robustness and security improvements Many robustness and security improvements
Ulrich Windl <ulrich.windl@rz.uni-regensburg.de> for the Ulrich Windl <ulrich.windl@rz.uni-regensburg.de> for the
Providing me with information about the Linux 2.2 kernel Information about the Linux 2.2 kernel functionality compared to 2.0
functionality compared to 2.0.
Doug Woodward <dougw@whistler.com> Doug Woodward <dougw@whistler.com>
Advice on configuring for Solaris 2.8 on x86 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_PEER2 59
#define REQ_ADD_SERVER3 60 #define REQ_ADD_SERVER3 60
#define REQ_ADD_PEER3 61 #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 */ /* Structure used to exchange timespecs independent of time_t size */
typedef struct { typedef struct {
@@ -252,6 +253,7 @@ typedef struct {
#define REQ_ADDSRC_TRUST 0x20 #define REQ_ADDSRC_TRUST 0x20
#define REQ_ADDSRC_REQUIRE 0x40 #define REQ_ADDSRC_REQUIRE 0x40
#define REQ_ADDSRC_INTERLEAVED 0x80 #define REQ_ADDSRC_INTERLEAVED 0x80
#define REQ_ADDSRC_BURST 0x100
typedef struct { typedef struct {
IPAddr ip_addr; IPAddr ip_addr;
@@ -367,9 +369,9 @@ typedef struct {
domain socket. domain socket.
Version 6 (no authentication) : changed format of client accesses by index Version 6 (no authentication) : changed format of client accesses by index
(using new request/reply types) and manual timestamp, new fields and flags (using new request/reply types) and manual timestamp, added new fields and
in NTP source request and report, new commands: ntpdata, refresh, flags to NTP source request and report, made length of manual list constant,
serverstats added new commands: ntpdata, refresh, serverstats, shutdown
*/ */
#define PROTO_VERSION_NUMBER 6 #define PROTO_VERSION_NUMBER 6
@@ -468,7 +470,8 @@ typedef struct {
#define RPY_CLIENT_ACCESSES_BY_INDEX2 15 #define RPY_CLIENT_ACCESSES_BY_INDEX2 15
#define RPY_NTP_DATA 16 #define RPY_NTP_DATA 16
#define RPY_MANUAL_TIMESTAMP2 17 #define RPY_MANUAL_TIMESTAMP2 17
#define N_REPLY_TYPES 18 #define RPY_MANUAL_LIST2 18
#define N_REPLY_TYPES 19
/* Status codes */ /* Status codes */
#define STT_SUCCESS 0 #define STT_SUCCESS 0

View File

@@ -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.auto_offline ? REQ_ADDSRC_AUTOOFFLINE : 0) |
(data.params.iburst ? REQ_ADDSRC_IBURST : 0) | (data.params.iburst ? REQ_ADDSRC_IBURST : 0) |
(data.params.interleaved ? REQ_ADDSRC_INTERLEAVED : 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_PREFER ? REQ_ADDSRC_PREFER : 0) |
(data.params.sel_options & SRC_SELECT_NOSELECT ? REQ_ADDSRC_NOSELECT : 0) | (data.params.sel_options & SRC_SELECT_NOSELECT ? REQ_ADDSRC_NOSELECT : 0) |
(data.params.sel_options & SRC_SELECT_TRUST ? REQ_ADDSRC_TRUST : 0) | (data.params.sel_options & SRC_SELECT_TRUST ? REQ_ADDSRC_TRUST : 0) |
@@ -1245,6 +1246,7 @@ give_help(void)
"cyclelogs\0Close and re-open log files\0" "cyclelogs\0Close and re-open log files\0"
"dump\0Dump all measurements to save files\0" "dump\0Dump all measurements to save files\0"
"rekey\0Re-read keys from key file\0" "rekey\0Re-read keys from key file\0"
"shutdown\0Stop daemon\0"
"\0\0" "\0\0"
"Client commands:\0\0" "Client commands:\0\0"
"dns -n|+n\0Disable/enable resolving IP addresses to hostnames\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", "maxdelay", "maxdelaydevratio", "maxdelayratio", "maxpoll",
"maxupdateskew", "minpoll", "minstratum", "ntpdata", "offline", "online", "maxupdateskew", "minpoll", "minstratum", "ntpdata", "offline", "online",
"polltarget", "quit", "refresh", "rekey", "reselect", "reselectdist", "polltarget", "quit", "refresh", "rekey", "reselect", "reselectdist",
"retries", "rtcdata", "serverstats", "settime", "smoothing", "smoothtime", "retries", "rtcdata", "serverstats", "settime", "shutdown", "smoothing",
"sources", "sources -v", "sourcestats", "sourcestats -v", "timeout", "smoothtime", "sources", "sources -v", "sourcestats", "sourcestats -v",
"tracking", "trimrtc", "waitsync", "writertc", "timeout", "tracking", "trimrtc", "waitsync", "writertc",
NULL NULL
}; };
static int list_index, len; static int list_index, len;
@@ -1324,18 +1326,16 @@ static int proto_version = PROTO_VERSION_NUMBER;
static int static int
submit_request(CMD_Request *request, CMD_Reply *reply) submit_request(CMD_Request *request, CMD_Reply *reply)
{ {
int bad_length, bad_sequence, bad_header;
int select_status; int select_status;
int recv_status; int recv_status;
int read_length; int read_length;
int expected_length;
int command_length; int command_length;
int padding_length; int padding_length;
struct timespec ts_now, ts_start; struct timespec ts_now, ts_start;
struct timeval tv; struct timeval tv;
int n_attempts, new_attempt; int n_attempts, new_attempt;
double timeout; double timeout;
fd_set rdfd, wrfd, exfd; fd_set rdfd;
request->pkt_type = PKT_TYPE_CMD_REQUEST; request->pkt_type = PKT_TYPE_CMD_REQUEST;
request->res1 = 0; request->res1 = 0;
@@ -1347,15 +1347,15 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
new_attempt = 1; new_attempt = 1;
do { do {
if (gettimeofday(&tv, NULL))
return 0;
if (new_attempt) { if (new_attempt) {
new_attempt = 0; new_attempt = 0;
if (n_attempts > max_retries) if (n_attempts > max_retries)
return 0; return 0;
if (gettimeofday(&tv, NULL))
return 0;
UTI_TimevalToTimespec(&tv, &ts_start); UTI_TimevalToTimespec(&tv, &ts_start);
UTI_GetRandomBytes(&request->sequence, sizeof (request->sequence)); 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); DEBUG_LOG("Sent %d bytes", command_length);
} }
if (gettimeofday(&tv, NULL))
return 0;
UTI_TimevalToTimespec(&tv, &ts_now); UTI_TimevalToTimespec(&tv, &ts_now);
/* Check if the clock wasn't stepped back */ /* 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)) - timeout = initial_timeout / 1000.0 * (1U << (n_attempts - 1)) -
UTI_DiffTimespecsToDouble(&ts_now, &ts_start); UTI_DiffTimespecsToDouble(&ts_now, &ts_start);
UTI_DoubleToTimeval(timeout, &tv);
DEBUG_LOG("Timeout %f seconds", timeout); DEBUG_LOG("Timeout %f seconds", timeout);
FD_ZERO(&rdfd); /* Avoid calling select() with an invalid timeout */
FD_ZERO(&wrfd); if (timeout <= 0.0) {
FD_ZERO(&exfd); new_attempt = 1;
continue;
}
UTI_DoubleToTimeval(timeout, &tv);
FD_ZERO(&rdfd);
FD_SET(sock_fd, &rdfd); FD_SET(sock_fd, &rdfd);
if (quit) if (quit)
return 0; 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) { if (select_status < 0) {
DEBUG_LOG("select failed : %s", strerror(errno)); DEBUG_LOG("select failed : %s", strerror(errno));
return 0;
} else if (select_status == 0) { } else if (select_status == 0) {
/* Timeout must have elapsed, try a resend? */ /* Timeout must have elapsed, try a resend? */
new_attempt = 1; new_attempt = 1;
@@ -1425,34 +1427,18 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
DEBUG_LOG("Received %d bytes", recv_status); DEBUG_LOG("Received %d bytes", recv_status);
read_length = 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 || /* Check if the header is valid */
expected_length < offsetof(CMD_Reply, data)); if (read_length < offsetof(CMD_Reply, data) ||
(reply->version != proto_version &&
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 && !(reply->version >= PROTO_VERSION_MISMATCH_COMPAT_CLIENT &&
ntohs(reply->status) == STT_BADPKTVERSION)) || ntohs(reply->status) == STT_BADPKTVERSION)) ||
(reply->pkt_type != PKT_TYPE_CMD_REPLY) || reply->pkt_type != PKT_TYPE_CMD_REPLY ||
(reply->res1 != 0) || reply->res1 != 0 ||
(reply->res2 != 0) || reply->res2 != 0 ||
(reply->command != request->command)); reply->command != request->command ||
reply->sequence != request->sequence) {
if (bad_header) { DEBUG_LOG("Invalid reply");
continue; continue;
} }
@@ -1471,6 +1457,15 @@ submit_request(CMD_Request *request, CMD_Reply *reply)
#error unknown compatibility with PROTO_VERSION - 1 #error unknown compatibility with PROTO_VERSION - 1
#endif #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 */ /* Good packet received, print out results */
DEBUG_LOG("Reply cmd=%d reply=%d stat=%d", DEBUG_LOG("Reply cmd=%d reply=%d stat=%d",
ntohs(reply->command), ntohs(reply->reply), ntohs(reply->status)); 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; return 0;
} }
/* Make sure an unknown response was not requested */
assert(PKL_ReplyLength(reply));
return 1; return 1;
} }
@@ -2540,7 +2538,7 @@ process_cmd_manual_list(const char *line)
struct timespec when; struct timespec when;
request.command = htons(REQ_MANUAL_LIST); 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; return 0;
n_samples = ntohl(reply.data.manual_list.n_samples); 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"); 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]; sample = &reply.data.manual_list.samples[i];
UTI_TimespecNetworkToHost(&sample->when, &when); 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 static int
process_cmd_waitsync(char *line) process_cmd_waitsync(char *line)
{ {
@@ -3004,6 +3010,8 @@ process_line(char *line)
} else if (!strcmp(command, "settime")) { } else if (!strcmp(command, "settime")) {
do_normal_submit = 0; do_normal_submit = 0;
ret = process_cmd_settime(line); ret = process_cmd_settime(line);
} else if (!strcmp(command, "shutdown")) {
process_cmd_shutdown(&tx_message, line);
} else if (!strcmp(command, "smoothing")) { } else if (!strcmp(command, "smoothing")) {
do_normal_submit = 0; do_normal_submit = 0;
ret = process_cmd_smoothing(line); ret = process_cmd_smoothing(line);

View File

@@ -138,6 +138,7 @@ static const char permissions[] = {
PERMIT_AUTH, /* ADD_PEER2 */ PERMIT_AUTH, /* ADD_PEER2 */
PERMIT_AUTH, /* ADD_SERVER3 */ PERMIT_AUTH, /* ADD_SERVER3 */
PERMIT_AUTH, /* ADD_PEER3 */ PERMIT_AUTH, /* ADD_PEER3 */
PERMIT_AUTH, /* SHUTDOWN */
}; };
/* ================================================== */ /* ================================================== */
@@ -278,7 +279,6 @@ do_size_checks(void)
for (i = 1; i < N_REPLY_TYPES; i++) { for (i = 1; i < N_REPLY_TYPES; i++) {
reply.reply = htons(i); reply.reply = htons(i);
reply.status = STT_SUCCESS; reply.status = STT_SUCCESS;
reply.data.manual_list.n_samples = htonl(MAX_MANUAL_LIST_SAMPLES);
reply_length = PKL_ReplyLength(&reply); reply_length = PKL_ReplyLength(&reply);
if ((reply_length && reply_length < offsetof(CMD_Reply, data)) || if ((reply_length && reply_length < offsetof(CMD_Reply, data)) ||
reply_length > sizeof (CMD_Reply)) 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.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.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.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 = 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_PREFER ? SRC_SELECT_PREFER : 0) |
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_NOSELECT ? SRC_SELECT_NOSELECT : 0) | (ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_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->reply = htons(RPY_CLIENT_ACCESSES_BY_INDEX2);
tx_message->data.client_accesses_by_index.n_indices = htonl(n_indices); 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++) { for (i = req_first_index, j = 0; i < (uint32_t)n_indices && j < req_n_clients; i++) {
if (!CLG_GetClientAccessReportByIndex(i, &report, &now)) if (!CLG_GetClientAccessReportByIndex(i, &report, &now))
continue; continue;
@@ -1103,10 +1101,11 @@ handle_manual_list(CMD_Request *rx_message, CMD_Reply *tx_message)
RPY_ManualListSample *sample; RPY_ManualListSample *sample;
RPT_ManualSamplesReport report[MAX_MANUAL_LIST_SAMPLES]; 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); MNL_ReportSamples(report, MAX_MANUAL_LIST_SAMPLES, &n_samples);
tx_message->data.manual_list.n_samples = htonl(n_samples); tx_message->data.manual_list.n_samples = htonl(n_samples);
for (i=0; i<n_samples; i++) { for (i=0; i<n_samples; i++) {
sample = &tx_message->data.manual_list.samples[i]; sample = &tx_message->data.manual_list.samples[i];
UTI_TimespecHostToNetwork(&report[i].when, &sample->when); 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)); 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 */ /* 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); expected_length = PKL_CommandLength(&rx_message);
rx_command = ntohs(rx_message.command); rx_command = ntohs(rx_message.command);
memset(&tx_message, 0, sizeof (tx_message));
tx_message.version = PROTO_VERSION_NUMBER; tx_message.version = PROTO_VERSION_NUMBER;
tx_message.pkt_type = PKT_TYPE_CMD_REPLY; tx_message.pkt_type = PKT_TYPE_CMD_REPLY;
tx_message.res1 = 0;
tx_message.res2 = 0;
tx_message.command = rx_message.command; tx_message.command = rx_message.command;
tx_message.reply = htons(RPY_NULL); tx_message.reply = htons(RPY_NULL);
tx_message.status = htons(STT_SUCCESS); 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.sequence = rx_message.sequence;
tx_message.pad4 = 0;
tx_message.pad5 = 0;
if (rx_message.version != PROTO_VERSION_NUMBER) { if (rx_message.version != PROTO_VERSION_NUMBER) {
DEBUG_LOG("Command packet has invalid version (%d != %d)", 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); handle_ntp_data(&rx_message, &tx_message);
break; break;
case REQ_SHUTDOWN:
handle_shutdown(&rx_message, &tx_message);
break;
default: default:
DEBUG_LOG("Unhandled command %d", rx_command); DEBUG_LOG("Unhandled command %d", rx_command);
tx_message.status = htons(STT_FAILED); 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.online = 1;
src->params.auto_offline = 0; src->params.auto_offline = 0;
src->params.presend_minpoll = SRC_DEFAULT_PRESEND_MINPOLL; src->params.presend_minpoll = SRC_DEFAULT_PRESEND_MINPOLL;
src->params.burst = 0;
src->params.iburst = 0; src->params.iburst = 0;
src->params.min_stratum = SRC_DEFAULT_MINSTRATUM; src->params.min_stratum = SRC_DEFAULT_MINSTRATUM;
src->params.poll_target = SRC_DEFAULT_POLLTARGET; src->params.poll_target = SRC_DEFAULT_POLLTARGET;
@@ -84,6 +85,8 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
if (!strcasecmp(cmd, "auto_offline")) { if (!strcasecmp(cmd, "auto_offline")) {
src->params.auto_offline = 1; src->params.auto_offline = 1;
} else if (!strcasecmp(cmd, "burst")) {
src->params.burst = 1;
} else if (!strcasecmp(cmd, "iburst")) { } else if (!strcasecmp(cmd, "iburst")) {
src->params.iburst = 1; src->params.iburst = 1;
} else if (!strcasecmp(cmd, "offline")) { } else if (!strcasecmp(cmd, "offline")) {

19
conf.c
View File

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

27
configure vendored
View File

@@ -85,6 +85,7 @@ For better control, use the options below.
--with-readline-library=DIR Specify where readline lib directory is --with-readline-library=DIR Specify where readline lib directory is
--with-ncurses-library=DIR Specify where ncurses lib directory is --with-ncurses-library=DIR Specify where ncurses lib directory is
--disable-sechash Disable support for hashes other than MD5 --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-nss Don't use NSS even if it is available
--without-tomcrypt Don't use libtomcrypt even if it is available --without-tomcrypt Don't use libtomcrypt even if it is available
--disable-cmdmon Disable command and monitoring support --disable-cmdmon Disable command and monitoring support
@@ -198,6 +199,7 @@ feat_readline=1
try_readline=1 try_readline=1
try_editline=1 try_editline=1
feat_sechash=1 feat_sechash=1
try_nettle=1
try_nss=1 try_nss=1
try_tomcrypt=1 try_tomcrypt=1
feat_rtc=1 feat_rtc=1
@@ -360,6 +362,9 @@ do
--disable-sechash ) --disable-sechash )
feat_sechash=0 feat_sechash=0
;; ;;
--without-nettle )
try_nettle=0
;;
--without-nss ) --without-nss )
try_nss=0 try_nss=0
;; ;;
@@ -549,8 +554,12 @@ then
if [ "x$ntp_era_split" != "x" ]; then if [ "x$ntp_era_split" != "x" ]; then
split_seconds=$ntp_era_split split_seconds=$ntp_era_split
split_days=0 split_days=0
else
if [ "x$SOURCE_DATE_EPOCH" != "x" ]; then
split_seconds=$SOURCE_DATE_EPOCH
else else
split_seconds=`date '+%s'` split_seconds=`date '+%s'`
fi
if [ "x$split_seconds" = "x" ]; then if [ "x$split_seconds" = "x" ]; then
echo "error: could not get current time, --with-ntp-era option is needed" echo "error: could not get current time, --with-ntp-era option is needed"
exit 1 exit 1
@@ -696,6 +705,7 @@ then
struct scm_ts_pktinfo pktinfo; struct scm_ts_pktinfo pktinfo;
pktinfo.if_index = pktinfo.pkt_length = 0; pktinfo.if_index = pktinfo.pkt_length = 0;
return pktinfo.if_index + pktinfo.pkt_length + HWTSTAMP_FILTER_NTP_ALL + return pktinfo.if_index + pktinfo.pkt_length + HWTSTAMP_FILTER_NTP_ALL +
SCM_TIMESTAMPING_PKTINFO +
SOF_TIMESTAMPING_OPT_PKTINFO + SOF_TIMESTAMPING_OPT_TX_SWHW;'; then SOF_TIMESTAMPING_OPT_PKTINFO + SOF_TIMESTAMPING_OPT_TX_SWHW;'; then
add_def HAVE_LINUX_TIMESTAMPING_RXFILTER_NTP 1 add_def HAVE_LINUX_TIMESTAMPING_RXFILTER_NTP 1
add_def HAVE_LINUX_TIMESTAMPING_OPT_PKTINFO 1 add_def HAVE_LINUX_TIMESTAMPING_OPT_PKTINFO 1
@@ -852,7 +862,22 @@ fi
HASH_OBJ="hash_intmd5.o" HASH_OBJ="hash_intmd5.o"
HASH_LINK="" 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_cflags="`pkg_config --cflags nss`"
test_link="`pkg_config --libs-only-L nss` -lfreebl3" test_link="`pkg_config --libs-only-L nss` -lfreebl3"
if test_code 'NSS' 'nss.h hasht.h nsslowhash.h' \ if test_code 'NSS' 'nss.h hasht.h nsslowhash.h' \

View File

@@ -66,33 +66,40 @@ server, or its IP address. The *server* directive supports the following
options: options:
+ +
*minpoll* _poll_::: *minpoll* _poll_:::
Although *chronyd* will trim the rate at which it samples the server during This option specifies the minimum interval between requests sent to the server
normal operation, the user might want to constrain the minimum polling interval. as a power of 2 in seconds. For example, *minpoll 5* would mean that the
This is always defined as a power of 2, so *minpoll 5* would mean that the polling interval should not drop below 32 seconds. The default is 6 (64
polling interval cannot drop below 32 seconds. The default is 6 (64 seconds), seconds), the minimum is -4 (1/16th of a second), and the maximum is 24 (6
the minimum is -4 (1/16th of a second), and the maximum is 24 (6 months). Note months). Note that intervals shorter than 6 (64 seconds) should generally not
that intervals shorter than 6 (64 seconds) should generally not be used with be used with public servers on the Internet, because it might be considered
public servers on the Internet, because it might be considered abuse. abuse.
*maxpoll* _poll_::: *maxpoll* _poll_:::
In a similar way, the user might want to constrain the maximum polling interval. This option specifies the maximum interval between requests sent to the server
Again this is specified as a power of 2, *maxpoll 9* indicates that the polling as a power of 2 in seconds. For example, *maxpoll 9* indicates that the polling
interval must stay at or below 512 seconds. The default is 10 (1024 seconds), interval should stay at or below 9 (512 seconds). The default is 10 (1024
the minimum is 0 (1 second), and the maximum is 24 (6 months). seconds), the minimum is 0 (1 second), and the maximum is 24 (6 months).
*iburst*::: *iburst*:::
If this option is set, the interval between the first four polls will be 2 With this option, the interval between the first four requests sent to the
seconds instead of _minpoll_. This is useful to quickly get the first update of server will be 2 seconds instead of the interval specified by the *minpoll*
the clock after *chronyd* is started. option, which allows *chronyd* to make the first update of the clock shortly
*key* _id_::: after start.
*burst*:::
With this option, *chronyd* will shorten the interval between up to four
requests to 2 seconds when it cannot get a good measurement from the server.
The number of requests in the burst is limited by the current polling interval
to keep the average interval at or above the minimum interval, i.e. the current
interval needs to be at least two times longer than the minimum interval in
order to allow a burst with two requests.
*key* _ID_:::
The NTP protocol supports the inclusion of checksums in the packets, to prevent The NTP protocol supports the inclusion of checksums in the packets, to prevent
computers having their system time upset by rogue packets being sent to them. 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 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 cryptographic hash function set in the key file, which is specified by the
<<keyfile,*keyfile*>> directive. <<keyfile,*keyfile*>> directive.
+ +
If the key option is present, *chronyd* will attempt to use authenticated The *key* option specifies which key (with an ID in the range 1 through 2^32-1)
packets when communicating with this server. The key number used will be the should *chronyd* use to authenticate requests sent to the server and verify its
single argument to the key option (an unsigned integer in the range 1 through responses. The server must have the same key for this number configured,
2^32-1). The server must have the same password for this key number configured,
otherwise no relationship between the computers will be possible. otherwise no relationship between the computers will be possible.
*maxdelay* _delay_::: *maxdelay* _delay_:::
*chronyd* uses the network round-trip delay to the server to determine how *chronyd* uses the network round-trip delay to the server to determine how
@@ -113,7 +120,7 @@ option. For example, *maxdelay 0.3* would indicate that measurements with a
round-trip delay of 0.3 seconds or more should be ignored. The default value is 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. 3 seconds and the maximum value is 1000 seconds.
*maxdelayratio* _ratio_::: *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 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 buffered. If a measurement has a round trip delay that is greater than the
maxdelayratio times the minimum delay, it will be rejected. maxdelayratio times the minimum delay, it will be rejected.
@@ -123,14 +130,14 @@ minimum delay amongst the previous measurements to the standard deviation of
the previous measurements that is greater than the specified ratio, it will be the previous measurements that is greater than the specified ratio, it will be
rejected. The default is 10.0. rejected. The default is 10.0.
*mindelay* _delay_::: *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 the minimum amongst the previous measurements. This can be useful in networks
with static configuration to improve the stability of corrections for with static configuration to improve the stability of corrections for
asymmetric jitter, weighting of the measurements, and the *maxdelayratio* and asymmetric jitter, weighting of the measurements, and the *maxdelayratio* and
*maxdelaydevratio* tests. The value should be set accurately in order to have a *maxdelaydevratio* tests. The value should be set accurately in order to have a
positive effect on the synchronisation. positive effect on the synchronisation.
*asymmetry* _ratio_::: *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. 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 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 packets sent to the source is more variable than the delay of packets sent from
@@ -154,14 +161,15 @@ option can be specified. *chronyd* will not try to poll the server until it is
enabled to do so (by using the <<chronyc.adoc#online,*online*>> command in enabled to do so (by using the <<chronyc.adoc#online,*online*>> command in
*chronyc*). *chronyc*).
*auto_offline*::: *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 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* 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 when disconnecting the network link, if it is safe to assume that the requests
<<chronyc.adoc#online,*online*>> command when the link has been established, to and responses will not be dropped in the network, e.g. in a trusted local
enable measurements to start.) 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*:::
Prefer this source over sources without prefer option. Prefer this source over sources without the *prefer* option.
*noselect*::: *noselect*:::
Never select this source. This is particularly useful for monitoring. Never select this source. This is particularly useful for monitoring.
*trust*::: *trust*:::
@@ -500,7 +508,7 @@ This option specifies the width of the pulses (in seconds). It is used to
filter PPS samples when the driver provides samples for both rising and falling 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 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 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*::: *pps*:::
This options forces *chronyd* to treat any refclock (e.g. SHM or PHC) as a 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 refclock. This can be useful when the refclock provides time with a variable
@@ -516,6 +524,9 @@ is included in the maximum assumed error which is used in the source selection
algorithm. Increasing the delay is useful to avoid having no majority in the 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 source selection or to make it prefer other sources. The default is 1e-9 (1
nanosecond). 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_::: *precision* _precision_:::
This option sets the precision of the reference clock (in seconds). The default This option sets the precision of the reference clock (in seconds). The default
value is the estimated precision of the system clock. value is the estimated precision of the system clock.
@@ -546,6 +557,12 @@ but not very precise, reference clock to be safely combined with
unauthenticated NTP sources in order to improve the accuracy of the clock. They 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 can be selected and used for synchronisation only if they agree with the
trusted and required source. 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_::: *minsamples* _samples_:::
Set the minimum number of samples kept for this source. This overrides the Set the minimum number of samples kept for this source. This overrides the
<<minsamples,*minsamples*>> directive. <<minsamples,*minsamples*>> directive.
@@ -862,6 +879,11 @@ It is also useful when the system clock is required to have correct TAI-UTC
offset. Note that the offset is set only when leap seconds are handled by the offset. Note that the offset is set only when leap seconds are handled by the
kernel, i.e. <<leapsecmode,*leapsecmode*>> is set to *system*. 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: An example of the directive is:
+ +
---- ----
@@ -1985,12 +2007,18 @@ format of the file is shown below:
+ +
Each line consists of an ID, name of an authentication hash function (optional), 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 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* 2^32-1. The default hash function is *MD5*, which is always supported.
was compiled, other supported functions might be *SHA1*, *SHA256*, *SHA384*, +
*SHA512*, *RMD128*, *RMD160*, *RMD256*, *RMD320*, *TIGER*, and *WHIRLPOOL*. The If *chronyd* was built with enabled support for hashing using a crypto library
password can be specified as a string of characters not containing white space (nettle, nss, or libtomcrypt), the following functions are available: *MD5*,
with an optional *ASCII:* prefix, or as a hexadecimal number with the *HEX:* *SHA1*, *SHA256*, *SHA384*, *SHA512*. Depending on which library and version is
prefix. The maximum length of the line is 2047 characters. *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 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 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 The *rekey* command causes *chronyd* to re-read the key file specified in the
configuration file by the <<chrony.conf.adoc#keyfile,*keyfile*>> directive. 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 === Client commands
[[dns]]*dns* _option_:: [[dns]]*dns* _option_::

View File

@@ -135,7 +135,7 @@ range of privileged system calls on behalf of the parent.
*-F* _level_:: *-F* _level_::
This option configures a system call filter when *chronyd* is compiled with This option configures a system call filter when *chronyd* is compiled with
support for the Linux secure computing (seccomp) facility. In level 1 the 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). 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 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. This mode is only supported on Linux.
*-x*:: *-x*::
This option disables the control of the system clock. *chronyd* will not make This option disables the control of the system clock. *chronyd* will not try to
any adjustments of the clock, but it will still track its offset and frequency make any adjustments of the clock. It will assume the clock is free running and
relative to the estimated true time, and be able to operate as an NTP server. still track its offset and frequency relative to the estimated true time. This
This allows *chronyd* to run without the capability to adjust or set the system option allows *chronyd* to run without the capability to adjust or set the
clock (e.g. in some containers). 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*:: *-v*::
With this option *chronyd* will print version number to the terminal and exit. With this option *chronyd* will print version number to the terminal and exit.

View File

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

View File

@@ -97,6 +97,12 @@ driftfile /var/lib/chrony/drift
! pidfile /var/run/chronyd.pid ! 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 ### INITIAL CLOCK CORRECTION
# This option is useful to quickly correct the clock on start if it's # This option is useful to quickly correct the clock on start if it's

View File

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

@@ -62,6 +62,12 @@ static const struct hash hashes[] = {
#ifdef LTC_SHA512 #ifdef LTC_SHA512
{ "SHA512", "sha512", &sha512_desc }, { "SHA512", "sha512", &sha512_desc },
#endif #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 #ifdef LTC_TIGER
{ "TIGER", "tiger", &tiger_desc }, { "TIGER", "tiger", &tiger_desc },
#endif #endif

View File

@@ -79,11 +79,11 @@ LOG_Initialise(void)
void void
LOG_Finalise(void) LOG_Finalise(void)
{ {
if (system_log) { if (system_log)
closelog(); closelog();
} else {
if (file_log)
fclose(file_log); fclose(file_log);
}
LOG_CycleLogFiles(); LOG_CycleLogFiles();
@@ -116,7 +116,7 @@ static void log_message(int fatal, LOG_Severity severity, const char *message)
assert(0); assert(0);
} }
syslog(priority, fatal ? "Fatal error : %s" : "%s", message); syslog(priority, fatal ? "Fatal error : %s" : "%s", message);
} else { } else if (file_log) {
fprintf(file_log, fatal ? "Fatal error : %s\n" : "%s\n", message); fprintf(file_log, fatal ? "Fatal error : %s\n" : "%s\n", message);
} }
} }
@@ -134,7 +134,7 @@ void LOG_Message(LOG_Severity severity,
time_t t; time_t t;
struct tm stm; struct tm stm;
if (!system_log) { if (!system_log && file_log) {
/* Don't clutter up syslog with timestamps and internal debugging info */ /* Don't clutter up syslog with timestamps and internal debugging info */
time(&t); time(&t);
stm = *gmtime(&t); stm = *gmtime(&t);
@@ -160,17 +160,15 @@ void LOG_Message(LOG_Severity severity,
case LOGS_FATAL: case LOGS_FATAL:
log_message(1, severity, buf); log_message(1, severity, buf);
/* With syslog, send the message also to the grandparent /* Send the message also to the foreground process if it is
process or write it to stderr if not detached */ still running, or stderr if it is still open */
if (system_log) {
if (parent_fd > 0) { if (parent_fd > 0) {
if (write(parent_fd, buf, strlen(buf) + 1) < 0) if (write(parent_fd, buf, strlen(buf) + 1) < 0)
; /* Not much we can do here */ ; /* Not much we can do here */
} else if (parent_fd == 0) { } else if (system_log && parent_fd == 0) {
system_log = 0; system_log = 0;
log_message(1, severity, buf); log_message(1, severity, buf);
} }
}
break; break;
default: default:
assert(0); assert(0);
@@ -220,6 +218,8 @@ void
LOG_SetParentFd(int fd) LOG_SetParentFd(int fd)
{ {
parent_fd = 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 */ /* Log messages to syslog instead of stderr */
extern void LOG_OpenSystemLog(void); 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); extern void LOG_SetParentFd(int fd);
/* Close the pipe to the foreground process so it can exit */ /* 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 static void
go_daemon(void) go_daemon(void)
{ {
@@ -352,6 +354,13 @@ go_daemon(void)
} }
LOG_SetParentFd(pipefd[1]); 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 our pidfile to prevent other chronyds running */
write_pidfile(); 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(); PRV_Initialise();
LCL_Initialise(); LCL_Initialise();
SCH_Initialise(); SCH_Initialise();
@@ -551,16 +570,6 @@ int main
SYS_LockMemory(); 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 */ /* Drop root privileges if the specified user has a non-zero UID */
if (!geteuid() && (pw->pw_uid || pw->pw_gid)) if (!geteuid() && (pw->pw_uid || pw->pw_gid))
SYS_DropRoot(pw->pw_uid, pw->pw_gid); SYS_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); max_addrs = MIN(max_addrs, DNS_MAX_ADDRESSES);
memset(&hints, 0, sizeof (hints)); memset(&hints, 0, sizeof (hints));
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_family = AF_UNSPEC;
}
hints.ai_socktype = SOCK_STREAM; hints.ai_socktype = SOCK_STREAM;
result = getaddrinfo(name, NULL, &hints, &res); result = getaddrinfo(name, NULL, &hints, &res);

View File

@@ -88,6 +88,7 @@ struct NCR_Instance_Record {
SCH_TimeoutID tx_timeout_id; /* Timeout ID for next transmission */ SCH_TimeoutID tx_timeout_id; /* Timeout ID for next transmission */
int tx_suspended; /* Boolean indicating we can't transmit yet */ 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 int auto_offline; /* If 1, automatically go offline if server/peer
isn't responding */ isn't responding */
@@ -149,14 +150,11 @@ struct NCR_Instance_Record {
be used for synchronisation */ be used for synchronisation */
int valid_timestamps; int valid_timestamps;
/* Flag indicating the timestamps below were updated since last request */ /* Receive and transmit timestamps from the last valid response */
int updated_timestamps;
/* Receive and transmit timestamps from the last received packet */
NTP_int64 remote_ntp_rx; NTP_int64 remote_ntp_rx;
NTP_int64 remote_ntp_tx; 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 source. We have to be prepared to tinker with this if the local
clock has its frequency adjusted before we repond. The value we clock has its frequency adjusted before we repond. The value we
store here is what our own local time was when the same arrived. 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; int prev_local_poll;
unsigned int prev_tx_count; 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 /* The instance record in the main source management module. This
performs the statistical analysis on the samples we generate */ performs the statistical analysis on the samples we generate */
@@ -230,6 +237,10 @@ static ARR_Instance broadcasts;
#define IBURST_GOOD_SAMPLES 4 #define IBURST_GOOD_SAMPLES 4
#define IBURST_TOTAL_SAMPLES SOURCE_REACH_BITS #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 */ /* Time to wait after sending packet to 'warm up' link */
#define WARM_UP_DELAY 2.0 #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_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->max_delay_dev_ratio = CLAMP(0.0, params->max_delay_dev_ratio, MAX_MAXDELAYDEVRATIO);
result->offset_correction = params->offset; result->offset_correction = params->offset;
result->auto_burst = params->burst;
result->auto_offline = params->auto_offline; result->auto_offline = params->auto_offline;
result->poll_target = params->poll_target; result->poll_target = params->poll_target;
@@ -652,7 +664,6 @@ NCR_ResetInstance(NCR_Instance instance)
instance->valid_rx = 0; instance->valid_rx = 0;
instance->valid_timestamps = 0; instance->valid_timestamps = 0;
instance->updated_timestamps = 0;
UTI_ZeroNtp64(&instance->remote_ntp_rx); UTI_ZeroNtp64(&instance->remote_ntp_rx);
UTI_ZeroNtp64(&instance->remote_ntp_tx); UTI_ZeroNtp64(&instance->remote_ntp_tx);
UTI_ZeroNtp64(&instance->local_ntp_rx); UTI_ZeroNtp64(&instance->local_ntp_rx);
@@ -662,6 +673,10 @@ NCR_ResetInstance(NCR_Instance instance)
zero_local_timestamp(&instance->prev_local_tx); zero_local_timestamp(&instance->prev_local_tx);
instance->prev_local_poll = 0; instance->prev_local_poll = 0;
instance->prev_tx_count = 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_rx, /* The RX time of the received packet */
NTP_Local_Timestamp *local_tx, /* The TX time of the previous packet NTP_Local_Timestamp *local_tx, /* The TX time of the previous packet
RESULT : TX time of this packet */ RESULT : TX time of this packet */
NTP_int64 *local_ntp_rx, /* RESULT : receive timestamp from this packet */ NTP_int64 *local_ntp_rx, /* The receive timestamp from the previous packet
NTP_int64 *local_ntp_tx, /* RESULT : transmit timestamp from this 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_Remote_Address *where_to, /* Where to address the reponse to */
NTP_Local_Address *from /* From what address to send it */ 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; version = NTP_VERSION;
} }
/* Allow interleaved mode only if there was a prior transmission */ /* Check if the packet can be formed in the interleaved mode */
if (interleaved && (!local_tx || UTI_IsZeroTimespec(&local_tx->ts))) if (interleaved && (!remote_ntp_rx || !local_tx || UTI_IsZeroTimespec(&local_tx->ts)))
interleaved = 0; interleaved = 0;
smooth_time = 0; smooth_time = 0;
@@ -989,6 +1006,7 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
/* Originate - this comes from the last packet the source sent us */ /* Originate - this comes from the last packet the source sent us */
message.originate_ts = interleaved ? *remote_ntp_rx : *remote_ntp_tx; message.originate_ts = interleaved ? *remote_ntp_rx : *remote_ntp_tx;
do {
/* Prepare random bits which will be added to the receive timestamp */ /* Prepare random bits which will be added to the receive timestamp */
UTI_GetNtp64Fuzz(&ts_fuzz, precision); UTI_GetNtp64Fuzz(&ts_fuzz, precision);
@@ -997,6 +1015,12 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
the source like we have been running on our latest estimate of the source like we have been running on our latest estimate of
frequency all along */ frequency all along */
UTI_TimespecToNtp64(&local_receive, &message.receive_ts, &ts_fuzz); 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 { } else {
UTI_ZeroNtp64(&message.originate_ts); UTI_ZeroNtp64(&message.originate_ts);
UTI_ZeroNtp64(&message.receive_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); &message.transmit_ts, &ts_fuzz);
} }
/* Avoid sending messages with non-zero transmit timestamp equal to the /* Do not send a packet with a non-zero transmit timestamp which is
receive timestamp to allow reliable detection of the interleaved mode */ equal to any of the following timestamps:
} while (!UTI_CompareNtp64(&message.transmit_ts, &message.receive_ts) && - receive (to allow reliable detection of the interleaved mode)
!UTI_IsZeroNtp64(&message.transmit_ts)); - 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); 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; NCR_Instance inst = (NCR_Instance) arg;
NTP_Local_Address local_addr; NTP_Local_Address local_addr;
int interleaved, sent; int interleaved, initial, sent;
inst->tx_timeout_id = 0; inst->tx_timeout_id = 0;
@@ -1093,10 +1123,20 @@ transmit_timeout(void *arg)
/* With online burst switch to online before last packet */ /* With online burst switch to online before last packet */
if (inst->burst_total_samples_to_go <= 1) if (inst->burst_total_samples_to_go <= 1)
inst->opmode = MD_ONLINE; inst->opmode = MD_ONLINE;
break;
case MD_BURST_WAS_OFFLINE: case MD_BURST_WAS_OFFLINE:
if (inst->burst_total_samples_to_go <= 0) if (inst->burst_total_samples_to_go <= 0)
take_offline(inst); take_offline(inst);
break; 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: default:
break; break;
} }
@@ -1137,6 +1177,19 @@ transmit_timeout(void *arg)
(inst->mode == MODE_ACTIVE && (inst->mode == MODE_ACTIVE &&
inst->prev_tx_count == 1 && inst->tx_count == 0)); 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 /* 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 sending an NTP exchange to ensure both ends' ARP caches are
primed or whether we need to send two packets first to ensure a primed or whether we need to send two packets first to ensure a
@@ -1148,18 +1201,16 @@ transmit_timeout(void *arg)
inst->presend_done--; inst->presend_done--;
} }
sent = transmit_packet(inst->mode, interleaved, inst->local_poll, /* Send the request (which may also be a response in the symmetric mode) */
inst->version, sent = transmit_packet(inst->mode, interleaved, inst->local_poll, inst->version,
inst->auth_mode, inst->auth_key_id, inst->auth_mode, inst->auth_key_id,
&inst->remote_ntp_rx, &inst->remote_ntp_tx, initial ? NULL : &inst->remote_ntp_rx,
&inst->local_rx, &inst->local_tx, initial ? &inst->init_remote_ntp_tx : &inst->remote_ntp_tx,
&inst->local_ntp_rx, &inst->local_ntp_tx, initial ? &inst->init_local_rx : &inst->local_rx,
&inst->remote_addr, &inst->local_tx, &inst->local_ntp_rx, &inst->local_ntp_tx,
&local_addr); &inst->remote_addr, &local_addr);
++inst->tx_count; ++inst->tx_count;
inst->valid_rx = 0;
inst->updated_timestamps = 0;
if (sent) if (sent)
inst->report.total_tx_count++; 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; NTP_Local_Timestamp local_receive, local_transmit;
double remote_interval, local_interval, response_time; double remote_interval, local_interval, response_time;
double delay_time, precision; 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. */ The test values are 1 when passed and 0 when failed. */
/* Test 1 checks for duplicate packet */ /* 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 /* 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. */ 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 The authentication test (test5) is required to prevent DoS attacks using
unauthenticated packets on authenticated symmetric associations. */ unauthenticated packets on authenticated symmetric associations. */
if ((inst->mode == MODE_CLIENT && valid_packet && !inst->valid_rx) || if ((inst->mode == MODE_CLIENT && valid_packet && !inst->valid_rx) ||
(inst->mode == MODE_ACTIVE && (valid_packet || !inst->valid_rx) && (inst->mode == MODE_ACTIVE && valid_packet &&
test5 && !UTI_IsZeroNtp64(&message->transmit_ts) && (!inst->valid_rx ||
(!inst->updated_timestamps || (valid_packet && !inst->valid_rx) ||
UTI_CompareNtp64(&inst->remote_ntp_tx, &message->transmit_ts) < 0))) { UTI_CompareNtp64(&inst->remote_ntp_tx, &message->transmit_ts) < 0))) {
inst->remote_ntp_rx = message->receive_ts; inst->remote_ntp_rx = message->receive_ts;
inst->remote_ntp_tx = message->transmit_ts; inst->remote_ntp_tx = message->transmit_ts;
inst->local_rx = *rx_ts; inst->local_rx = *rx_ts;
inst->valid_timestamps = synced_packet; 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 */ /* Don't use the same set of timestamps for the next sample */
if (interleaved_packet) if (interleaved_packet)
inst->prev_local_tx = inst->local_tx; inst->prev_local_tx = inst->local_tx;
else else
zero_local_timestamp(&inst->prev_local_tx); 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 /* 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", DEBUG_LOG("remote_interval=%.9f local_interval=%.9f response_time=%.9f txs=%c rxs=%c",
remote_interval, local_interval, response_time, remote_interval, local_interval, response_time,
tss_chars[local_transmit.source], tss_chars[local_receive.source]); 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, test1, test2, test3, test5, test6, test7, testA, testB, testC, testD,
kod_rate, interleaved_packet, inst->presend_done, valid_packet, good_packet, 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) { if (valid_packet) {
inst->remote_poll = message->poll; 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_Mode pkt_mode, my_mode;
NTP_int64 *local_ntp_rx, *local_ntp_tx; NTP_int64 *local_ntp_rx, *local_ntp_tx;
NTP_Local_Timestamp local_tx, *tx_ts; 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; AuthenticationMode auth_mode;
uint32_t key_id; 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_mode = NTP_LVM_TO_MODE(message->lvm);
pkt_version = NTP_LVM_TO_VERSION(message->lvm);
switch (pkt_mode) { switch (pkt_mode) {
case MODE_ACTIVE: case MODE_ACTIVE:
@@ -2019,6 +2087,15 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
/* Reply with server packet */ /* Reply with server packet */
my_mode = MODE_SERVER; my_mode = MODE_SERVER;
break; 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: default:
/* Discard */ /* Discard */
DEBUG_LOG("NTP packet discarded pkt_mode=%d", pkt_mode); 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) { if (log_index >= 0) {
CLG_GetNtpTimestamps(log_index, &local_ntp_rx, &local_ntp_tx); CLG_GetNtpTimestamps(log_index, &local_ntp_rx, &local_ntp_tx);
interleaved = !UTI_IsZeroNtp64(local_ntp_rx) && 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) { if (interleaved) {
UTI_Ntp64ToTimespec(local_ntp_tx, &local_tx.ts); 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); poll = MAX(poll, message->poll);
/* Send a reply */ /* 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, auth_mode, key_id, &message->receive_ts, &message->transmit_ts,
rx_ts, tx_ts, local_ntp_rx, NULL, remote_addr, local_addr); 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)) if (!UTI_IsZeroTimespec(&inst->prev_local_tx.ts))
UTI_AdjustTimespec(&inst->prev_local_tx.ts, when, &inst->prev_local_tx.ts, &delta, dfreq, UTI_AdjustTimespec(&inst->prev_local_tx.ts, when, &inst->prev_local_tx.ts, &delta, dfreq,
doffset); 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) if (sock_fd == INVALID_SOCK_FD)
return; return;
#ifdef HAVE_LINUX_TIMESTAMPING
NIO_Linux_NotifySocketClosing(sock_fd);
#endif
SCH_RemoveFileHandler(sock_fd); SCH_RemoveFileHandler(sock_fd);
close(sock_fd); close(sock_fd);
} }
@@ -685,6 +688,11 @@ read_from_socket(int sock_fd, int event, void *anything)
unsigned int i, n; unsigned int i, n;
int status, flags = 0; int status, flags = 0;
#ifdef HAVE_LINUX_TIMESTAMPING
if (NIO_Linux_ProcessEvent(sock_fd, event))
return;
#endif
hdr = ARR_GetElements(recv_headers); hdr = ARR_GetElements(recv_headers);
n = ARR_GetSize(recv_headers); n = ARR_GetSize(recv_headers);
assert(n >= 1); assert(n >= 1);

View File

@@ -94,6 +94,27 @@ static int ts_tx_flags;
/* Flag indicating the socket options can't be changed in control messages */ /* Flag indicating the socket options can't be changed in control messages */
static int permanent_ts_options; 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 static int
@@ -252,7 +273,7 @@ update_interface_speed(struct Interface *iface)
{ {
struct ethtool_cmd cmd; struct ethtool_cmd cmd;
struct ifreq req; struct ifreq req;
int sock_fd; int sock_fd, link_speed;
sock_fd = socket(AF_INET, SOCK_DGRAM, 0); sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (sock_fd < 0) if (sock_fd < 0)
@@ -273,7 +294,12 @@ update_interface_speed(struct Interface *iface)
close(sock_fd); 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 void
NIO_Linux_Initialise(void) NIO_Linux_Initialise(void)
{ {
@@ -350,6 +399,10 @@ NIO_Linux_Initialise(void)
/* Kernels before 4.7 ignore timestamping flags set in control messages */ /* Kernels before 4.7 ignore timestamping flags set in control messages */
permanent_ts_options = !SYS_Linux_CheckKernelVersion(4, 7); 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; struct Interface *iface;
unsigned int i; unsigned int i;
if (dummy_rxts_socket != INVALID_SOCK_FD)
close(dummy_rxts_socket);
for (i = 0; i < ARR_GetSize(interfaces); i++) { for (i = 0; i < ARR_GetSize(interfaces); i++) {
iface = ARR_GetElement(interfaces, i); iface = ARR_GetElement(interfaces, i);
HCL_DestroyInstance(iface->clock); 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 * static struct Interface *
get_interface(int if_index) get_interface(int if_index)
{ {
@@ -614,6 +737,11 @@ NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *loc
} else { } else {
DEBUG_LOG("HW clock not found for interface %d", ts_if_index); 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]) && 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 */ /* Return the message if it's not received from the error queue */
if (!is_tx) if (!is_tx)
return 0; return 0;
@@ -682,6 +818,15 @@ NIO_Linux_RequestTxTimestamp(struct msghdr *msg, int cmsglen, int sock_fd)
{ {
struct cmsghdr *cmsg; 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 */ /* Check if TX timestamping is disabled on this socket */
if (permanent_ts_options || !NIO_IsServerSocket(sock_fd)) if (permanent_ts_options || !NIO_IsServerSocket(sock_fd))
return cmsglen; return cmsglen;
@@ -701,3 +846,11 @@ NIO_Linux_RequestTxTimestamp(struct msghdr *msg, int cmsglen, int sock_fd)
return cmsglen; 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. 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_Initialise(void);
extern void NIO_Linux_Finalise(void); extern void NIO_Linux_Finalise(void);
extern int NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events); 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, 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); NTP_Local_Timestamp *local_ts, struct msghdr *hdr, int length);
extern int NIO_Linux_RequestTxTimestamp(struct msghdr *msg, int cmsglen, int sock_fd); 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; return;
/* Disable output and wait for a response */ /* 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) { 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 */ /* Move the head and enable output for the next packet */
queue_head = NEXT_QUEUE_INDEX(queue_head); queue_head = NEXT_QUEUE_INDEX(queue_head);
if (!IS_QUEUE_EMPTY()) 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 */ /* Enable output if there was no pending request */
if (IS_QUEUE_EMPTY()) 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); queue_tail = NEXT_QUEUE_INDEX(queue_tail);

View File

@@ -118,6 +118,7 @@ static const struct request_length request_lengths[] = {
{ 0, 0 }, /* ADD_PEER2 */ { 0, 0 }, /* ADD_PEER2 */
REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_SERVER3 */ REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_SERVER3 */
REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_PEER3 */ REQ_LENGTH_ENTRY(ntp_source, null), /* ADD_PEER3 */
REQ_LENGTH_ENTRY(null, null), /* SHUTDOWN */
}; };
static const uint16_t reply_lengths[] = { static const uint16_t reply_lengths[] = {
@@ -132,13 +133,14 @@ static const uint16_t reply_lengths[] = {
0, /* SUBNETS_ACCESSED - not supported */ 0, /* SUBNETS_ACCESSED - not supported */
0, /* CLIENT_ACCESSES - not supported */ 0, /* CLIENT_ACCESSES - not supported */
0, /* CLIENT_ACCESSES_BY_INDEX - 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(activity), /* ACTIVITY */
RPY_LENGTH_ENTRY(smoothing), /* SMOOTHING */ RPY_LENGTH_ENTRY(smoothing), /* SMOOTHING */
RPY_LENGTH_ENTRY(server_stats), /* SERVER_STATS */ RPY_LENGTH_ENTRY(server_stats), /* SERVER_STATS */
RPY_LENGTH_ENTRY(client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX2 */ RPY_LENGTH_ENTRY(client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX2 */
RPY_LENGTH_ENTRY(ntp_data), /* NTP_DATA */ RPY_LENGTH_ENTRY(ntp_data), /* NTP_DATA */
RPY_LENGTH_ENTRY(manual_timestamp), /* MANUAL_TIMESTAMP2 */ 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) if (type < 1 || type >= N_REPLY_TYPES)
return 0; 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]; return reply_lengths[type];
} }

View File

@@ -79,6 +79,8 @@ struct RCL_Instance_Record {
int pps_rate; int pps_rate;
int pps_active; int pps_active;
int max_lock_age; int max_lock_age;
int stratum;
int tai;
struct MedianFilter filter; struct MedianFilter filter;
uint32_t ref_id; uint32_t ref_id;
uint32_t lock_ref; uint32_t lock_ref;
@@ -181,13 +183,13 @@ RCL_AddRefclock(RefclockParameters *params)
inst->driver = &RCL_PHC_driver; inst->driver = &RCL_PHC_driver;
} else { } else {
LOG_FATAL("unknown refclock driver %s", params->driver_name); 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); 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->data = NULL;
inst->driver_parameter = params->driver_parameter; inst->driver_parameter = params->driver_parameter;
@@ -200,6 +202,8 @@ RCL_AddRefclock(RefclockParameters *params)
inst->pps_rate = params->pps_rate; inst->pps_rate = params->pps_rate;
inst->pps_active = 0; inst->pps_active = 0;
inst->max_lock_age = params->max_lock_age; inst->max_lock_age = params->max_lock_age;
inst->stratum = params->stratum;
inst->tai = params->tai;
inst->lock_ref = params->lock_ref_id; inst->lock_ref = params->lock_ref_id;
inst->offset = params->offset; inst->offset = params->offset;
inst->delay = params->delay; inst->delay = params->delay;
@@ -251,11 +255,8 @@ RCL_AddRefclock(RefclockParameters *params)
} }
} }
if (inst->driver->init) if (inst->driver->init && !inst->driver->init(inst))
if (!inst->driver->init(inst)) {
LOG_FATAL("refclock %s initialisation failed", params->driver_name); LOG_FATAL("refclock %s initialisation failed", params->driver_name);
return 0;
}
filter_init(&inst->filter, params->filter_length, params->max_dispersion); filter_init(&inst->filter, params->filter_length, params->max_dispersion);
@@ -356,6 +357,28 @@ RCL_GetDriverOption(RCL_Instance instance, char *name)
return NULL; 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 int
RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset, int leap) 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; 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); filter_add_sample(&instance->filter, &cooked_time, offset - correction + instance->offset, dispersion);
instance->pps_active = 0; instance->pps_active = 0;
@@ -635,7 +663,7 @@ poll_timeout(void *arg)
/* Handle special case when PPS is used with local stratum */ /* Handle special case when PPS is used with local stratum */
stratum = pps_stratum(inst, &sample_time); stratum = pps_stratum(inst, &sample_time);
else else
stratum = 0; stratum = inst->stratum;
SRC_UpdateReachability(inst->source, 1); SRC_UpdateReachability(inst->source, 1);
SRC_AccumulateSample(inst->source, &sample_time, offset, SRC_AccumulateSample(inst->source, &sample_time, offset,

View File

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

View File

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

View File

@@ -184,6 +184,9 @@ extern void REF_DisableLocal(void);
and is better to discard any measurements */ and is better to discard any measurements */
extern int REF_IsLeapSecondClose(void); 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); extern void REF_GetTrackingReport(RPT_TrackingReport *rep);
#endif /* GOT_REFERENCE_H */ #endif /* GOT_REFERENCE_H */

View File

@@ -219,13 +219,16 @@ SCH_RemoveFileHandler(int fd)
/* ================================================== */ /* ================================================== */
void void
SCH_SetFileHandlerEvents(int fd, int events) SCH_SetFileHandlerEvent(int fd, int event, int enable)
{ {
FileHandlerEntry *ptr; FileHandlerEntry *ptr;
assert(events);
ptr = ARR_GetElement(file_handlers, fd); 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 */ /* 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_AddFileHandler(int fd, int events, SCH_FileHandler handler, SCH_ArbitraryArgument arg);
extern void SCH_RemoveFileHandler(int fd); 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 */ /* 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); 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; 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 */ /* Require the root distance to be below the allowed maximum */
if (si->root_distance > max_distance) { if (si->root_distance > max_distance) {
sources[i]->status = SRC_BAD_DISTANCE; sources[i]->status = SRC_BAD_DISTANCE;

View File

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

View File

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

2
sys.c
View File

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

View File

@@ -380,6 +380,18 @@ test_step_offset(void)
return 1; 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 */ /* Initialisation code for this module */
@@ -388,6 +400,8 @@ SYS_Linux_Initialise(void)
{ {
get_version_specific_details(); get_version_specific_details();
report_time_adjust_blockers();
reset_adjtime_offset(); reset_adjtime_offset();
if (have_setoffset && !test_step_offset()) { if (have_setoffset && !test_step_offset()) {
@@ -415,9 +429,9 @@ SYS_Linux_Finalise(void)
#ifdef FEAT_PRIVDROP #ifdef FEAT_PRIVDROP
void 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; cap_t cap;
if (prctl(PR_SET_KEEPCAPS, 1)) { if (prctl(PR_SET_KEEPCAPS, 1)) {
@@ -426,9 +440,12 @@ SYS_Linux_DropRoot(uid_t uid, gid_t gid)
UTI_DropRoot(uid, gid); UTI_DropRoot(uid, gid);
/* Keep CAP_NET_BIND_SERVICE only if NTP port can be opened */ /* Keep CAP_NET_BIND_SERVICE only if a server NTP port can be opened
cap_text = CNF_GetNTPPort() ? and keep CAP_SYS_TIME only if the clock control is enabled */
"cap_net_bind_service,cap_sys_time=ep" : "cap_sys_time=ep"; 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) { if ((cap = cap_from_text(cap_text)) == NULL) {
LOG_FATAL("cap_from_text() failed"); LOG_FATAL("cap_from_text() failed");

View File

@@ -31,7 +31,7 @@ extern void SYS_Linux_Initialise(void);
extern void SYS_Linux_Finalise(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); extern void SYS_Linux_EnableSystemCallFilter(int level);

View File

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

View File

@@ -8,11 +8,20 @@ limit=1000
refclock_jitter=$jitter refclock_jitter=$jitter
min_sync_time=45 min_sync_time=45
max_sync_time=70 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 run_test || test_fail
check_chronyd_exit || test_fail check_chronyd_exit || test_fail
check_source_selection || test_fail check_source_selection || test_fail
check_sync || 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 test_pass

View File

@@ -4,14 +4,30 @@
test_start "chronyc" 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 sources
sourcestats" sourcestats
manual list
smoothing
waitsync
rtcdata"
run_test || test_fail run_test || test_fail
check_chronyd_exit || 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 Stratum : 2
Ref time \(UTC\) : Fri Jan 01 00:1.:.. 2010 Ref time \(UTC\) : Fri Jan 01 00:1.:.. 2010
System time : 0\.0000..... seconds (slow|fast) of NTP time System time : 0\.0000..... seconds (slow|fast) of NTP time
@@ -24,14 +40,27 @@ Root delay : 0\.000...... seconds
Root dispersion : 0\.000...... seconds Root dispersion : 0\.000...... seconds
Update interval : [0-9]+\.. seconds Update interval : [0-9]+\.. seconds
Leap status : Normal Leap status : Normal
210 Number of sources = 1 210 Number of sources = 2
MS Name/IP address Stratum Poll Reach LastRx Last sample 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 \^\* 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 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_fail
test_pass test_pass

View File

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

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

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

View File

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

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

View File

@@ -36,6 +36,7 @@ static int req_length, res_length;
#define NIO_CloseServerSocket(fd) assert(fd == 100) #define NIO_CloseServerSocket(fd) assert(fd == 100)
#define NIO_OpenClientSocket(addr) ((addr)->ip_addr.family != IPADDR_UNSPEC ? 101 : 0) #define NIO_OpenClientSocket(addr) ((addr)->ip_addr.family != IPADDR_UNSPEC ? 101 : 0)
#define NIO_CloseClientSocket(fd) assert(fd == 101) #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 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_AddTimeoutByDelay(delay, handler, arg) (1 ? 102 : (handler(arg), 1))
#define SCH_AddTimeoutInClass(delay, separation, randomness, class, handler, arg) \ #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 SCH_RemoveTimeout(id) assert(!id || id == 102)
#define LCL_ReadRawTime(ts) (*ts = current_time) #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_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_UpdateReachability(inst, reach)
#define SRC_ResetReachability(inst) #define SRC_ResetReachability(inst)
@@ -55,8 +57,6 @@ add_timeout_in_class(double min_delay, double separation, double randomness,
#include <ntp_core.c> #include <ntp_core.c>
static NCR_Instance inst;
static void static void
advance_time(double x) advance_time(double x)
{ {
@@ -64,7 +64,7 @@ advance_time(double x)
} }
static void static void
send_request(void) send_request(NCR_Instance inst)
{ {
NTP_Local_Address local_addr; NTP_Local_Address local_addr;
NTP_Local_Timestamp local_ts; NTP_Local_Timestamp local_ts;
@@ -74,19 +74,48 @@ send_request(void)
transmit_timeout(inst); transmit_timeout(inst);
TEST_CHECK(!inst->valid_rx); TEST_CHECK(!inst->valid_rx);
TEST_CHECK(!inst->updated_timestamps);
TEST_CHECK(prev_tx_count + 1 == inst->report.total_tx_count); 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.ip_addr.family = IPADDR_UNSPEC;
local_addr.if_index = INVALID_IF_INDEX; local_addr.if_index = INVALID_IF_INDEX;
local_addr.sock_fd = 101; local_addr.sock_fd = 101;
local_ts.ts = current_time; local_ts.ts = current_time;
local_ts.err = 0.0; 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); 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 = 100;
local_ts.ts = current_time;
local_ts.err = 0.0;
local_ts.source = NTP_TS_KERNEL;
res_length = 0;
NCR_ProcessRxUnknown(remote_addr, &local_addr, &local_ts,
&req_buffer.ntp_pkt, req_length);
res_length = req_length;
res_buffer = req_buffer;
advance_time(1e-5);
if (random() % 2) {
local_ts.ts = current_time;
NCR_ProcessTxUnknown(remote_addr, &local_addr, &local_ts,
&res_buffer.ntp_pkt, res_length);
}
} }
static void static void
@@ -165,30 +194,36 @@ send_response(int interleaved, int authenticated, int allow_update, int valid_ts
} }
static void 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_Address local_addr;
NTP_Local_Timestamp local_ts; NTP_Local_Timestamp local_ts;
NTP_Packet *res; NTP_Packet *res;
uint32_t prev_rx_count, prev_valid_count; uint32_t prev_rx_count, prev_valid_count;
struct timespec prev_rx_ts; struct timespec prev_rx_ts, prev_init_rx_ts;
int prev_open_socket; int prev_open_socket, ret;
res = &res_buffer.ntp_pkt; res = &res_buffer.ntp_pkt;
local_addr.ip_addr.family = IPADDR_UNSPEC; local_addr.ip_addr.family = IPADDR_UNSPEC;
local_addr.if_index = INVALID_IF_INDEX; 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.ts = current_time;
local_ts.err = 0.0; 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_rx_count = inst->report.total_rx_count;
prev_valid_count = inst->report.total_valid_count; prev_valid_count = inst->report.total_valid_count;
prev_rx_ts = inst->local_rx.ts; 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; 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) if (prev_open_socket)
TEST_CHECK(prev_rx_count + 1 == inst->report.total_rx_count); TEST_CHECK(prev_rx_count + 1 == inst->report.total_rx_count);
@@ -200,23 +235,51 @@ process_response(int valid, int updated)
else else
TEST_CHECK(prev_valid_count == inst->report.total_valid_count); 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)); TEST_CHECK(UTI_CompareTimespecs(&inst->local_rx.ts, &prev_rx_ts));
else else
TEST_CHECK(!UTI_CompareTimespecs(&inst->local_rx.ts, &prev_rx_ts)); 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 void
test_unit(void) test_unit(void)
{ {
char source_line[] = "127.0.0.1"; char source_line[] = "127.0.0.1 maxdelaydevratio 1e6";
char conf[][100] = { char conf[][100] = {
"allow",
"port 0", "port 0",
"local",
"keyfile ntp_core.keys" "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; CPS_NTP_Source source;
NTP_Remote_Address remote_addr; NTP_Remote_Address remote_addr;
NCR_Instance inst1, inst2;
NTP_Receive_Buffer packet_queue[PACKET_QUEUE_LENGTH];
CNF_Initialise(0, 0); CNF_Initialise(0, 0);
for (i = 0; i < sizeof conf / sizeof conf[0]; i++) for (i = 0; i < sizeof conf / sizeof conf[0]; i++)
@@ -231,12 +294,16 @@ test_unit(void)
REF_Initialise(); REF_Initialise();
KEY_Initialise(); KEY_Initialise();
for (i = 0; i < 1000; i++) { CNF_SetupAccessRestrictions();
CPS_ParseNTPSourceAdd(source_line, &source); CPS_ParseNTPSourceAdd(source_line, &source);
for (i = 0; i < 1000; i++) {
if (random() % 2) if (random() % 2)
source.params.interleaved = 1; source.params.interleaved = 1;
if (random() % 2) if (random() % 2)
source.params.authkey = 1; source.params.authkey = 1;
source.params.version = random() % 4 + 1;
UTI_ZeroTimespec(&current_time); UTI_ZeroTimespec(&current_time);
advance_time(TST_GetRandomDouble(1.0, 1e9)); advance_time(TST_GetRandomDouble(1.0, 1e9));
@@ -244,48 +311,125 @@ test_unit(void)
TST_GetRandomAddress(&remote_addr.ip_addr, IPADDR_UNSPEC, -1); TST_GetRandomAddress(&remote_addr.ip_addr, IPADDR_UNSPEC, -1);
remote_addr.port = 123; remote_addr.port = 123;
inst = NCR_GetInstance(&remote_addr, random() % 2 ? NTP_SERVER : NTP_PEER, &source.params); inst1 = NCR_GetInstance(&remote_addr, random() % 2 ? NTP_SERVER : NTP_PEER, &source.params);
NCR_StartInstance(inst); NCR_StartInstance(inst1);
has_updated = 0; has_updated = 0;
for (j = 0; j < 50; j++) { 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 || interleaved = random() % 2 && (inst1->mode != MODE_CLIENT ||
inst->tx_count < MAX_CLIENT_INTERLEAVED_TX); inst1->tx_count < MAX_CLIENT_INTERLEAVED_TX);
authenticated = random() % 2; authenticated = random() % 2;
valid = (!interleaved || (source.params.interleaved && has_updated)) && valid = (!interleaved || (source.params.interleaved && has_updated)) &&
(!source.params.authkey || authenticated); (!source.params.authkey || authenticated);
updated = (valid || inst->mode == MODE_ACTIVE) && updated = (valid || inst1->mode == MODE_ACTIVE) &&
(!source.params.authkey || authenticated); (!source.params.authkey || authenticated);
has_updated = has_updated || updated; has_updated = has_updated || updated;
if (inst1->mode == MODE_CLIENT)
updated = 0;
send_request(); send_request(inst1);
send_response(interleaved, authenticated, 1, 0, 1); 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) { if (source.params.authkey) {
send_response(interleaved, authenticated, 1, 1, 0); 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); send_response(interleaved, authenticated, 1, 1, 1);
process_response(valid, updated); DEBUG_LOG("response 3");
process_response(0, 0); process_response(inst1, -1, valid, valid, updated);
DEBUG_LOG("response 4");
process_response(inst1, 0, 0, 0, 0);
advance_time(-1.0); advance_time(-1.0);
send_response(interleaved, authenticated, 1, 1, 1); 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); advance_time(1.0);
send_response(interleaved, authenticated, 1, 1, 1); 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(); KEY_Finalise();

View File

@@ -4,6 +4,7 @@
void test_unit(void) { void test_unit(void) {
NTP_int64 ntp_ts, ntp_fuzz; NTP_int64 ntp_ts, ntp_fuzz;
struct timespec ts, ts2; struct timespec ts, ts2;
struct timeval tv;
struct sockaddr_un sun; struct sockaddr_un sun;
double x, y; double x, y;
Float f; Float f;
@@ -40,6 +41,32 @@ void test_unit(void) {
TEST_CHECK(UTI_DoubleToNtp32(1000000) == htonl(0xffffffff)); TEST_CHECK(UTI_DoubleToNtp32(1000000) == htonl(0xffffffff));
TEST_CHECK(UTI_DoubleToNtp32(-1.0) == htonl(0)); 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.hi = htonl(JAN_1970);
ntp_ts.lo = 0xffffffff; ntp_ts.lo = 0xffffffff;
UTI_Ntp64ToTimespec(&ntp_ts, &ts); 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_ts, &ntp_fuzz) < 0);
TEST_CHECK(UTI_CompareNtp64(&ntp_fuzz, &ntp_ts) > 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_sec = 1;
ts.tv_nsec = 2; ts.tv_nsec = 2;
ts2.tv_sec = 1; ts2.tv_sec = 1;

41
util.c
View File

@@ -119,13 +119,11 @@ UTI_TimevalToDouble(struct timeval *tv)
void void
UTI_DoubleToTimeval(double a, struct timeval *b) UTI_DoubleToTimeval(double a, struct timeval *b)
{ {
long int_part;
double frac_part; double frac_part;
int_part = (long)(a);
frac_part = 1.0e6 * (a - (double)(int_part)); b->tv_sec = a;
frac_part = frac_part > 0 ? frac_part + 0.5 : frac_part - 0.5; frac_part = 1.0e6 * (a - b->tv_sec);
b->tv_sec = int_part; b->tv_usec = frac_part > 0 ? frac_part + 0.5 : frac_part - 0.5;
b->tv_usec = (long)frac_part;
UTI_NormaliseTimeval(b); UTI_NormaliseTimeval(b);
} }
@@ -368,16 +366,14 @@ UTI_IPToRefid(IPAddr *ip)
case IPADDR_INET4: case IPADDR_INET4:
return ip->addr.in4; return ip->addr.in4;
case IPADDR_INET6: case IPADDR_INET6:
if (MD5_hash < 0) { if (MD5_hash < 0)
MD5_hash = HSH_GetHashId("MD5"); MD5_hash = HSH_GetHashId("MD5");
assert(MD5_hash >= 0);
}
if (HSH_Hash(MD5_hash, (unsigned const char *)ip->addr.in6, sizeof if (MD5_hash < 0 ||
(ip->addr.in6), NULL, 0, buf, 16) != 16) { HSH_Hash(MD5_hash, (const unsigned char *)ip->addr.in6, sizeof (ip->addr.in6),
assert(0); NULL, 0, buf, sizeof (buf)) != sizeof (buf))
return 0; LOG_FATAL("Could not get MD5");
};
return (uint32_t)buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]; return (uint32_t)buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
} }
return 0; 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 */ /* Seconds part of NTP timestamp correponding to the origin of the time_t format */
#define JAN_1970 0x83aa7e80UL #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. */ b, and 1 if a is after b. */
extern int UTI_CompareNtp64(NTP_int64 *a, NTP_int64 *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 */ /* Convert a timespec into an NTP timestamp */
extern void UTI_TimespecToNtp64(struct timespec *src, NTP_int64 *dest, NTP_int64 *fuzz); extern void UTI_TimespecToNtp64(struct timespec *src, NTP_int64 *dest, NTP_int64 *fuzz);