Compare commits

...

268 Commits

Author SHA1 Message Date
Miroslav Lichvar
f323c814af doc: update NEWS 2022-08-11 10:32:58 +02:00
Miroslav Lichvar
19b47dcbc9 doc: mention maxdelayquant in FAQ 2022-08-10 15:32:54 +02:00
Miroslav Lichvar
5edeadcbd9 test: extend 106-refclock test 2022-08-09 16:53:12 +02:00
Miroslav Lichvar
d91ae2094f configure: disable arc4random on Linux
In glibc 2.36 was added the arc4random family of functions. However,
unlike on other supported systems, it is not a user-space PRNG
implementation. It just wraps the getrandom() system call with no
buffering, which causes a performance loss on NTP servers due to
the function being called twice for each response to add randomness
to the RX and TX timestamp below the clock precision.

Don't check for arc4random on Linux to keep using the buffered
getrandom().
2022-08-03 15:23:38 +02:00
Miroslav Lichvar
30a5845098 configure: avoid -Wnonnull warnings
Replace NULL in test code of functions which have (at least in glibc) or
could have arguments marked as nonnull to avoid the -Wnonnull warnings,
which breaks the detection with the -Werror option.
2022-08-03 14:53:35 +02:00
Miroslav Lichvar
0f367efac5 doc: suggest self-signed certificates for NTS in FAQ 2022-08-03 13:58:28 +02:00
Miroslav Lichvar
24c011d4a6 test: catch definite leaks with valgrind 2022-08-02 15:09:47 +02:00
Miroslav Lichvar
0c2cdd2fb1 test: fix ntp_core unit test to disable source selection
If the randomly generated timestamps are close to the current time, the
source can be selected for synchronization, which causes a crash when
logging the source name due to uninitialized ntp_sources.

Specify the source with the noselect option to prevent selection.
2022-08-02 14:44:05 +02:00
Miroslav Lichvar
cd1a666e1b test: fix sources unit test to call SRC_ReportSource() correctly
Call the function with current time instead of latest sample of the
first source to avoid undefined conversion of negative double to long
int.

Fixes: 07600cbd71 ("test: extend sources unit test")
2022-08-02 14:44:05 +02:00
Miroslav Lichvar
070b4f69d0 ntp: add maxdelayquant option
Add a new test for maximum delay using a long-term estimate of a
p-quantile of the peer delay. If enabled, it replaces the
maxdelaydevratio test. It's main advantage is that it is not sensitive
to outliers corrupting the minimum delay.

As it can take a large number of samples for the estimate to reach the
expected value and adapt to a new value after a network change, the
option is recommended only for local networks with very short polling
intervals.
2022-07-21 16:05:48 +02:00
Miroslav Lichvar
851c823b42 doc: improve description of maxdelay* options 2022-07-21 15:33:35 +02:00
Miroslav Lichvar
df80274644 quantiles: add function to get minimum k 2022-07-21 15:33:35 +02:00
Miroslav Lichvar
bb2d68ddf9 test: extend 101-poll and 127-filter tests 2022-07-21 15:33:35 +02:00
Miroslav Lichvar
685d8f725b ntp: change minimum allowed poll to -7
Change the minimum poll allowed in configuration from -6 to -7. This
matches some PTP profiles using 128 sync messages per second.
2022-07-21 15:33:35 +02:00
Miroslav Lichvar
4234732b08 ntp: rework filter option to count missing samples
Instead of waiting for the sample filter to accumulate the specified
number of samples and then deciding if the result is acceptable, count
missing samples and get the result after the specified number of polls.

This should work better when samples are dropped at a high rate. The
source and clock update interval will be stable as long as at least
one sample can be collected.
2022-07-21 15:33:08 +02:00
Miroslav Lichvar
a16094adfb samplefilt: add debug message for selected samples 2022-07-21 14:17:22 +02:00
Miroslav Lichvar
a4349b13df samplefilt: add function to get maximum number of samples 2022-07-21 14:17:22 +02:00
Miroslav Lichvar
3556dadea1 ntp: enable sub-second poll sooner with filter option
When the minimum round-trip time is checked to enable a sub-second
polling interval, consider also the last sample in the filter to avoid
waiting for the first sample to be accumulated in sourcestats.
2022-07-21 14:17:22 +02:00
Miroslav Lichvar
220e6d1907 ntp: fix initial poll to follow non-LAN minimum
If a sub-second polling interval is configured, initialize the local
poll to 0 to avoid a shorter interval between the first and second
request in case no response to the first request is received (in time).
2022-07-21 14:17:22 +02:00
Miroslav Lichvar
a738037705 client: check for stdout errors
Return with an error code from chronyc if the command is expected to
print some data and fflush() or ferror() indicates an error. This should
make it easier for scripts to detect missing data when redirected to a
file.
2022-07-21 14:17:11 +02:00
Yury Vostrikov
7daf34675a refclock: remove unused struct MedianFilter
Filtering was moved to a separate source file in commit
c498c21fad ("refclock: split off median filter). It looks like
MedianFilter struct somehow survived the split. Remove it to reduce
confusion.
2022-07-11 10:20:44 +02:00
Miroslav Lichvar
de598c2310 main: add log message for timeout reached with -t option
This should make it more clear why chronyd exits if -q/-Q does not
finish before the timeout is reached.
2022-06-30 14:46:18 +02:00
Miroslav Lichvar
91cc4dbb12 doc: improve description of test A in measurements log 2022-06-30 10:20:49 +02:00
Miroslav Lichvar
0ae6f2485b ntp: don't use first response in interleaved mode
With the first interleaved response coming after a basic response the
client is forced to select the four timestamps covering most of the last
polling interval, which makes measured delay very sensitive to the
frequency offset between server and client. To avoid corrupting the
minimum delay held in sourcestats (which can cause testC failures),
reject the first interleaved response in the client/server mode as
failing the test A.

This does not change anything for the symmetric mode, where both sets of
the four timestamps generally cover a significant part of the polling
interval.
2022-06-30 10:18:48 +02:00
Miroslav Lichvar
52ec694d2b test: fix server interleaved mode in ntp_core unit test 2022-06-28 15:43:25 +02:00
Miroslav Lichvar
e2e07af8a4 doc: improve and add more questions to FAQ 2022-06-23 14:29:21 +02:00
Miroslav Lichvar
2ed88c31c7 sys_generic: damp slew oscillation due to delayed stop
If the computer is overloaded so much that chronyd cannot stop a slew
within one second of the scheduled end and the actual duration is more
than doubled (2 seconds with the minimum duration of 1 second), the
overshoot will be larger than the intended correction. If these
conditions persist, the oscillation will grow up to the maximum offset
allowed by maxslewrate and the delay in stopping.

Monitor the excess duration as an exponentially decaying maximum value
and don't allow any slews shorter than 5 times the value to damp the
oscillation. Ignore delays longer than 100 seconds, assuming they have a
different cause (e.g. the system was suspended and resumed) and are
already handled in the scheduler by triggering cancellation of the
ongoing slew.

This should also make it safer to shorten the minimum duration if
needed.

Reported-by: Daniel Franke <dff@amazon.com>
2022-06-15 17:42:49 +02:00
Miroslav Lichvar
af8e4a5115 sys_generic: rename slew constants 2022-06-14 16:02:06 +02:00
Miroslav Lichvar
f503a9a490 test: improve 133-hwtimestamp test 2022-06-09 16:01:22 +02:00
Miroslav Lichvar
9c64fbb9c4 hwclock: improve filtering of readings
Estimate the 1st and 2nd 10-quantile of the reading delay and accept
only readings between them unless the error of the offset predicted from
previous samples is larger than the minimum reading error. With the 25
PHC readings per ioctl it should combine about 2-3 readings.

This should improve hwclock tracking and synchronization stability when
a PHC reading delay occasionally falls below the normal expected
minimum, or all readings in the batch are delayed significantly (e.g.
due to high PCIe load).
2022-06-09 16:01:22 +02:00
Miroslav Lichvar
b428f901c7 quantiles: add support for quantile estimation
Add estimation of quantiles using the Frugal-2U streaming algorithm
(https://arxiv.org/pdf/1407.1121v1.pdf). It does not need to save
previous samples and adapts to changes in the distribution.

Allow multiple estimates of the same quantile and select the median for
better stability.
2022-06-09 16:01:19 +02:00
Miroslav Lichvar
09b7f77f9a hwclock: refactor processing of PHC readings
Move processing of PHC readings from sys_linux to hwclock, where
statistics can be collected and filtering improved.

In the PHC refclock driver accumulate the samples even if not in the
external timestamping mode to update the context which will be needed
for improved filtering.
2022-06-09 12:04:20 +02:00
Miroslav Lichvar
c23c0b8484 ntp: convert HW timestamp even if PHC reading fails
Reading of PHC can fail occasionally on some hardware. If that happens,
don't abort the conversion of the timestamp that triggered the reading.
2022-06-08 15:30:05 +02:00
Miroslav Lichvar
d530055917 sys_linux: increase number of PHC readings
Increase the number of requested readings from 10 to 25 - the maximum
accepted by the PTP_SYS_OFFSET* ioctls. This should improve stability of
HW clock tracking and PHC refclock.
2022-06-08 14:29:35 +02:00
Miroslav Lichvar
f41d09e19f doc: improve hwtimestamp description
Latest versions of ethtool print only the shorter lower-case names of
capabilities and filters. Explain that chronyd doesn't synchronize the
PHC and refer to the new vclock feature of the kernel, which should be
used by applications that need a synchronized PHC (e.g. ptp4l and
phc2sys) in order to not interfere with chronyd.
2022-05-19 10:50:10 +02:00
Miroslav Lichvar
46030d9d3e sources: add selection log
Add an option to enable selection log, capturing some data from the
selectdata report.
2022-05-19 08:23:05 +02:00
Miroslav Lichvar
02ccd3a3c7 sourcestats: don't load samples from future
When loading a dumped file, make sure there are no sample times in
future relative to the current system time (e.g. after reboot with
missing RTC).
2022-05-18 16:38:41 +02:00
Miroslav Lichvar
9cc609c4b0 local: cancel remaining correction after external step
Instead of the generic clock driver silently zeroing the remaining
offset after detecting an external step, cancel it properly with the
slew handlers in order to correct timestamps that are not reset in
handling of the unknown step (e.g. the NTP local TX).
2022-05-16 16:28:36 +02:00
Miroslav Lichvar
a0a496dcb4 refclock: set minimum maxlockage in local mode
Use 3 as the minimum maxlockage in the local mode to avoid disruptions
due to losing the lock when a single sample is missed, e.g. when the PPS
driver polling interval is slightly longer than the pulse interval and a
pulse is skipped.
2022-05-11 14:28:58 +02:00
Miroslav Lichvar
8d08486edf refclock: restart local mode after losing lock
A refclock in the local mode is locked to itself. When the maxlockage
check failed after missing some samples, it failed permanently and the
refclock was not able to accumulate any new samples.

When the check fails, drop all samples and reset the source to start
from scratch.

Reported-by: Dan Drown <dan-ntp@drown.org>
2022-05-11 14:27:18 +02:00
Miroslav Lichvar
a3b376cf0a refclock: fix invalid warning in local mode
A refclock in the local mode is locked to itself by design.

Reported-by: Dan Drown <dan-ntp@drown.org>
2022-05-11 14:22:41 +02:00
Miroslav Lichvar
e66f1df89d samplefilt: drop last sample in SPF_DropSamples()
When SPF_DropSamples() is called, don't keep the last sample to be
retrieved by SPF_GetLastSample(). It should be kept only after
filtering.
2022-05-11 14:21:09 +02:00
Miroslav Lichvar
35220aac9d siv: set key directly with gnutls
A new function is provided by the latest gnutls (should be in 3.7.5) to
set the key of an AEAD cipher. If available, use it to avoid destroying
and creating a new SIV instance with each key change.

This improves the server NTS-NTP performance if using gnutls for SIV.
2022-05-11 12:22:33 +02:00
Miroslav Lichvar
5b04f3ca90 doc: improve description of chronyc -h option 2022-05-05 14:34:15 +02:00
Miroslav Lichvar
beb1c36136 doc: improve maxchange description 2022-05-05 14:34:08 +02:00
Miroslav Lichvar
da3495c472 nts: don't exit if initialization of priority cache fails
Initialization of the gnutls priority cache can fail depending on the
system crypto policy (e.g. disabled TLS1.3). Log an error mentioning
TLS, but continue to run without the server/client credentials.
2022-05-05 10:27:48 +02:00
Miroslav Lichvar
356771c0c3 client: rework command catenation
Use snprintf() instead of strcat() and don't try to parse commands
longer than 2048 characters to make it consistent with the chrony.conf
parser, avoid memory allocation, and not rely on the system ARG_MAX to
keep the length sane.
2022-05-04 14:17:32 +02:00
Miroslav Lichvar
fca8966ada examples: replace grep command in NM dispatcher script
Some grep implementations detect binary data and return success without
matching whole line. This might be an issue for the DHCPv6 NTP FQDN
check. The GNU grep in the C locale seems to check only for the NUL
character, which cannot be passed in an environment variable, but other
implementations might behave differently and there doesn't seem to be a
portable way to force matching the whole line.

Instead of the grep command, check for invalid characters by comparing
the length of the input passed through "tr -d -c".
2022-03-23 15:36:17 +01:00
Miroslav Lichvar
25f80a1a9d doc: include gnutls in libraries providing SECHASH feature 2022-03-16 14:54:12 +01:00
Miroslav Lichvar
1219f99935 ntp: keep original source IP address
When an added source is specified by IP address, save the original
string instead of formatting a new string from the parsed address, which
can be different (e.g. compressed vs expanded IPv6 address).

This fixes the chronyc sourcename command and -N option to print the IP
address exactly as it was specified in the configuration file or chronyc
add command.
2022-03-10 09:54:31 +01:00
Miroslav Lichvar
33a1fe7a9c ntp: split out conf_id allocation 2022-03-10 09:53:29 +01:00
Miroslav Lichvar
eed0a0de56 test: update 007-cmdmon system test for recent changes
The new unsynchronised source state is now reported in selectdata before
the first measurement.

Fixes: c29f8888c767 ("sources: handle unsynchronized sources in selection")
2022-03-07 16:00:20 +01:00
Miroslav Lichvar
07600cbd71 test: extend sources unit test 2022-03-02 12:17:02 +01:00
Miroslav Lichvar
f2e341b5ed sources: improve debug messages
Print source status as char and print the name instead of index in
combining.
2022-03-02 12:11:24 +01:00
Miroslav Lichvar
55717c1ccd refclock: trim offset in local mode
With the local option, trim offset larger than 1 second to not lose
precision after large steps of the clock.
2022-02-24 11:40:26 +01:00
Miroslav Lichvar
d5e645eb38 samplefilt: add function to correct accumulated offsets
Analogously to SST_CorrectOffset(), add SPF_CorrectOffset() to correct
the offsets accumulated in the filter.
2022-02-24 11:40:01 +01:00
Miroslav Lichvar
3196630fb9 sys_linux: don't require configurable pin for external PPS
Some PHCs that have a PPS input don't have configurable pins (their
function is hardcoded). Accept a negative pin index to skip the pin
configuration before requesting external timestamping.
2022-02-23 14:43:39 +01:00
Miroslav Lichvar
663dde1ad7 refclock: improve precision with large offset
If a SHM or PHC refclock has a very large offset compensated by the
offset option, or ignored with the pps or local option, there is a
persistent loss of precision in the calculation of the sample offset
using the double format.

Rework the code to delay the calculation of the accumulated offset to
include the specificed compensation and remaining correction of the
system clock, where the calculation can be split to improve the
precision. In the pps mode ignore integer seconds competely.

The precision of the SOCK refclock is now limited to 1 nanosecond due to
the extra double->timespec->double conversion.
2022-02-23 14:43:39 +01:00
Miroslav Lichvar
62757cda49 refclock: add local option
Add "local" option to specify that the reference clock is an
unsynchronized clock which is more stable than the system clock (e.g.
TCXO, OCXO, or atomic clock) and it should be used as a local standard
to stabilize the system clock.

Handle the local refclock as a PPS refclock locked to itself which gives
the unsynchronized status to be ignored in the source selection. Wait
for the refclock to get at least minsamples samples and adjust the clock
directly to follow changes in the refclock's sourcestats frequency and
offset.

There should be at most one refclock specified with this option.
2022-02-23 14:43:39 +01:00
Miroslav Lichvar
af6ae9186b reference: allow clock adjustments without updating reference
Add support for accumulating frequency and time offset without changing
the reference parameters and calling the local parameter change
handlers.

This will allow an unsynchronized source to operate below other sources
in order to stabilize the clock.
2022-02-23 14:43:39 +01:00
Miroslav Lichvar
4c29f8888c sources: handle unsynchronized sources in selection
Allow sources to accumulate samples with the leap status set to not
synchronized. Define a new state for them to be ignored in the
selection. This is intended for sources that are never synchronized and
will be used only for stabilization.
2022-02-23 14:42:40 +01:00
Miroslav Lichvar
d06ae4a60e sourcestats: add function to get minsamples 2022-02-10 16:38:50 +01:00
Miroslav Lichvar
f9af2f9733 sourcestats: clamp minsamples and maxsamples in initialization
Don't leave the variables set to values outside their effective range.
This has no functional impact, but makes it clear what is the precedence
of the two settings.
2022-02-10 16:31:46 +01:00
Miroslav Lichvar
43ae0131cd sourcestats: use constant for required number of samples 2022-02-10 15:16:08 +01:00
Michael Hudson-Doyle
8bb8f15a7d sys_linux: allow rseq in seccomp filter
Libc 2.35 will use rseq syscalls [1][2] by default and thereby
break chrony in seccomp isolation.

[1]: https://www.efficios.com/blog/2019/02/08/linux-restartable-sequences/
[2]: https://sourceware.org/pipermail/libc-alpha/2022-February/136040.html

Tested-by: Christian Ehrhardt <christian.ehrhardt@canonical.com>
Reviewed-by: Christian Ehrhardt <christian.ehrhardt@canonical.com>
Signed-off-by: Michael Hudson-Doyle <michael.hudson@canonical.com>
Signed-off-by: Christian Ehrhardt <christian.ehrhardt@canonical.com>
2022-02-09 10:46:09 +01:00
Miroslav Lichvar
e55f174bd3 examples: handle more actions in NM dispatcher script
Run the chronyc onoffline command also when the connectivity-change
and dhcp6-change actions are reported by the NetworkManager dispatcher.

The latter should not be necessary, but there currently doesn't seem to
be any action for IPv6 becoming routable after duplicate address
detection, so at least in networks using DHCPv6, IPv6 NTP servers should
not be stuck in the offline state from a previously reported action.
2022-02-07 17:04:19 +01:00
Miroslav Lichvar
5bd13c8d59 examples: support DHCPv6 NTP servers in NM dispatcher script
Latest NetworkManager code provides NTP servers from the DHCPv6 NTP
option (RFC 5908) in the DHCP6_DHCP6_NTP_SERVERS variable to dispatcher
scripts.

Check for invalid characters (which can come from the FQDN suboption)
and include the servers in the interface-specific sources file.
2022-02-07 16:59:10 +01:00
Miroslav Lichvar
759580aa6f client: fix waitsync command to reconnect to server
If chronyc waitsync was started before chronyd, it would try all
addresses (Unix socket, IPv4, IPv6) and get stuck with no address, not
getting any response later when chronyd was running.

Reset the address index in open_io() when returning with failure to
allow the next call to start with the first address again.

Reported-by: Jan Mikkelsen <janm@transactionware.com>
2022-01-26 16:11:01 +01:00
Vincent Blut
b61cbed689 test: ensure awk commands in 008-ntpera return an integer
Some awk interpreters (e.g. mawk) print long integers in exponential
notation skewing the test result.
2022-01-13 09:40:12 +01:00
Miroslav Lichvar
2ac2247756 doc: update NEWS 2021-12-16 13:17:42 +01:00
Miroslav Lichvar
55f48b14b7 update copyright years 2021-12-16 13:17:42 +01:00
Miroslav Lichvar
3dfac33858 ntp: set local address on PTP socket on FreeBSD
Fix the FreeBSD-specific code checking for a bound IPv4 socket to
include the new PTP port. This should fix a multihomed server to respond
to NTP-over-PTP requests from the address which received the request.

Fixes: be3158c4e5 ("ntp: add support for NTP over PTP")
2021-12-16 13:17:42 +01:00
Miroslav Lichvar
d5f2401421 cmdmon: fix transmit_reply() to not read uninitialized data
In the FreeBSD-specific code checking for a bound IPv4 socket, make
sure it is not a Unix domain address to avoid reading uninitialized
IP-specific fields.

This fixes an error reported by valgrind.
2021-12-16 11:49:15 +01:00
Miroslav Lichvar
fb0570cc73 socket: zero sockaddr_un to initialize sa_len
Zero the whole sockaddr struct before calling bind() and connect() to
initialize the FreeBSD-specific sa_len field.

This fixes errors reported by valgrind.
2021-12-16 10:48:31 +01:00
Miroslav Lichvar
43936ba0d1 clientlog: remove unnecessary operation in timestamp conversion 2021-12-14 10:47:26 +01:00
Miroslav Lichvar
f2ba20f293 ntp: avoid unnecessary source lookups
Avoid searching the hash table of sources when a packet in the client
mode is received. It cannot be a response from our source. Analogously,
avoid source lookups for transmitted packets in the server mode. This
doesn't change anything for packets in symmetric modes, which can be
requests and responses at the same time.

This slightly improves the maximum packet rate handled as a server.
2021-12-14 10:47:10 +01:00
Miroslav Lichvar
fcd384523b ntp: fix typo in comment 2021-12-14 10:34:19 +01:00
Miroslav Lichvar
48bce351bf doc: describe use case for leapsecmode ignore option 2021-12-09 17:13:09 +01:00
Miroslav Lichvar
25f93875d9 doc: switch Solaris support to illumos
For a long time, the Solaris support in chrony wasn't tested on a real
Solaris system, but on illumos/OpenIndiana, which was forked from
OpenSolaris when it was discontinued in 2010.

While Solaris and illumos might have not diverged enough to make a
difference for chrony, replace Solaris in the documentation with illumos
to make it clear which system is actually supported by the chrony
project.
2021-12-09 17:03:56 +01:00
Miroslav Lichvar
ebc610fcb3 sys_solaris: disable kernel dosynctodr
The dosynctodr kernel variable needs to be set to 0 to block automatic
synchronization of the system clock to the hardware clock. chronyd used
to disable dosynctodr on Solaris versions before 2.6, but it seems it is
now needed even on current versions as the clock driver sets frequency
only without calling adjtime() or setting the ntp_adjtime() PLL offset.

This issue was reproduced and fix tested on current OpenIndiana.

Fixes: 8feb37df2b ("sys_solaris: use timex driver")
2021-12-07 12:18:56 +01:00
Miroslav Lichvar
264957a443 doc: update NEWS 2021-12-02 11:19:40 +01:00
Miroslav Lichvar
af611b5842 ntp: limit total monotonic offset correction
In addition to the 16s limit in per-response change in the monotonic
offset, don't allow the total accumulated offset injected in sourcestats
to be larger than 16 seconds.
2021-12-02 11:01:01 +01:00
Miroslav Lichvar
1c1ca1d12f test: update and improve 003-sanitizers test 2021-12-01 10:13:14 +01:00
Miroslav Lichvar
c506b9aac8 test: allow another inaccuracy in util unit test
A 1ns error in UTI_AdjustTimespec() was observed with an i686 build.
2021-12-01 09:26:41 +01:00
Miroslav Lichvar
2eefa61f10 test: fix 008-ntpera test for arbitrary NTP era split 2021-12-01 09:24:13 +01:00
Miroslav Lichvar
89a5e21e4d reference: check for unset leap_when in is_leap_close()
Check that the leap_when variable is set before testing a timestamp for
being close to a leap second. This allows the first measurement to be
accepted if starting at the Unix epoch (e.g. in a test).
2021-12-01 09:22:26 +01:00
Miroslav Lichvar
6a79771898 ntp: check for zero timestamp in initial TX timeout
Calculate the delay since the previous transmission only if the
TX timestamp is actually set. This removes an unnecessary delay when
starting at the Unix epoch in 1970 (e.g. in a test).
2021-12-01 09:22:26 +01:00
Miroslav Lichvar
53353529cf rtc: don't drop first sample after initial trim
It seems there is no longer an issue with the first sample after the
initial trim and it can be accumulated. It might have been a workaround
for an unrelated bug which was fixed since then.

This fixes the number of samples reported in rtcdata briefly jumping to
65535 and also brings back the expectation that n_samples is never
negative.
2021-12-01 09:22:26 +01:00
Miroslav Lichvar
22bfdf204f rtc: drop rtc_trim array
It always contained zero values and had no effect on anything.
2021-12-01 09:22:26 +01:00
Miroslav Lichvar
fc28e9ae56 rtc: remove unnecessary variable initializations 2021-12-01 09:22:26 +01:00
Miroslav Lichvar
17e6258694 doc: update FAQ 2021-11-24 15:07:22 +01:00
Miroslav Lichvar
d7a444593f ntp: improve check for PTP socket
Check for INVALID_SOCK_FD in case the PTP port is enabled, but opening
one of the PTP sockets failed.
2021-11-24 15:07:04 +01:00
Miroslav Lichvar
701b9415a5 test: update 110-chronyc test 2021-11-24 15:06:36 +01:00
Miroslav Lichvar
d5894c0738 main: add assertions for timespec signedness
Some of the code (e.g. util and clientlog) may work with negative
values. Require that time_t and the tv_nsec types are signed. This seems
to be the case on all supported systems, but it it is not required by
POSIX.
2021-11-24 11:17:24 +01:00
Miroslav Lichvar
a0a9560258 util: reset GetRandom functions in helpers after fork
Close /dev/urandom and drop cached getrandom() data after forking helper
processes to avoid them getting the same sequence of random numbers
(e.g. two NTS-KE helpers generating cookies with identical nonces).
arc4random() is assumed to be able to detect forks and reseed
automatically.

This is not strictly necessary with the current code, which does not use
the GetRandom functions before the NTS-KE helper processes are forked,
but that could change in future.

Also, call the reset function before exit to close /dev/urandom in order
to avoid valgrind reporting the file object as "still reachable".
2021-11-24 11:17:24 +01:00
Miroslav Lichvar
09067e06d3 ntp: fix exp1 EF search in process_response()
Don't ignore the magic field when searching for the exp1 extension
field in a received response. If there were two exp1 fields in the
packet, and only one of them had the expected magic value, it should
pick the right one.

Fixes: 2319f72b29 ("ntp: add client support for experimental extension field")
2021-11-24 11:17:24 +01:00
Miroslav Lichvar
dbbdd5af06 ntp: make default NTP version with xleave to be always 4
If the xleave option is enabled, ignore the key option and the hash
length. Always use version 4 as the default to get interleaved responses
from new chrony servers.
2021-11-22 17:03:40 +01:00
Miroslav Lichvar
7f984cf7fa ntp: limit interleaved responses to NTPv4
The interleaved modes are being specified for NTPv4 only. As a server,
detect interleaved requests only in NTPv4 packets.

Clients and peers can still send interleaved requests in lower-version
packets if configured with the version option.
2021-11-22 17:02:49 +01:00
Miroslav Lichvar
8df49b799f ntp: suppress monotonic timestamp if smoothing is enabled
Frequency transfer and time smoothing are conflicting features. Set the
monotonic timestamp in the experimental extension field to zero
(invalid) if time smoothing is activated.
2021-11-22 15:52:01 +01:00
Miroslav Lichvar
e7c2f71cea ntp: add special value to experimental root delay/disp
The maximum value of the new 32-bit fields is slightly less than 16,
which can cause the NTP test #7 to pass for a server which has a zero
root delay but maximum root dispersion.

Interpret the maximum value as the maximum value of the original 32-bit
fields (~65536.0 seconds) for better compatibility with NTPv4.
2021-11-22 15:21:29 +01:00
Miroslav Lichvar
219085b8f6 test: add 144-exp1 test 2021-11-16 10:36:26 +01:00
Miroslav Lichvar
2319f72b29 ntp: add client support for experimental extension field
Add "extfield F323" option to include the new extension field in
requests. If the server responds with this field, use the root
delay/dispersion and monotonic timestamp. Accumulate changes in the
offset between the monotonic and real-time receive timestamps and use
it for the correction of previous offsets in sourcestats. In the
interleaved mode, cancel out the latest change in the offset in
timestamps of the previous request and response, which were captured
before the change actually happened.
2021-11-16 10:34:32 +01:00
Miroslav Lichvar
72f7d09f58 sourcestats: add function to correct accumulated offsets
This will be needed to follow server time corrections in order to
better estimate frequency.
2021-11-16 10:23:20 +01:00
Miroslav Lichvar
0bf39c0ab9 ntp: add server support for experimental extension field
Maintain a server monotonic timescale needed for the experimental
extension field. It follows the best estimate of frequency without
time corrections. Implement it as an offset relative to the NTP time,
starting at zero, using a slew handler to cancel time corrections of the
NTP clock. The 32-bit epoch ID is set to a random value on start and
every step of the system clock.
2021-11-16 10:23:20 +01:00
Miroslav Lichvar
2e126ed2b5 util: add functions for converting new root delay/dispersion 2021-11-16 10:23:20 +01:00
Miroslav Lichvar
a652ce7d0e util: add function to subtract NTP timestamps
This will be needed to work with monotonic timestamps, which don't have
a stable epoch and cannot be converted to timespec.
2021-11-16 10:23:20 +01:00
Miroslav Lichvar
a97ca73704 ntp: add pre-NTPv5 experimental extension field
Add an experimental extension field for some features that were proposed
for NTPv5. Higher-resolution root delay and dispersion (using 28-bit
fraction) are added. A monotonic receive timestamp will allow a
frequency transfer between the server and client. The client will be
able to separate the server's time corrections from frequency
corrections by tracking the offset between the real-time and monotonic
receive timestamps.

The field has a type of 0xF323 from the new experimental range proposed
by the NTP working group. Include a magic 32-bit value in the field to
avoid interoperability issues if a different implementation choses the
same type for its own experimental field. The value will be changed on
incompatible changes to avoid issues between two different chrony
versions.
2021-11-16 10:23:15 +01:00
Miroslav Lichvar
125d7a5c32 ntp: prepare for non-authentication extension fields
Add a new variable to the packet info structure with flags for extension
fields included in received packets and add a new parameter to
transmit_packet() to add the fields to transmitted packets.
2021-11-16 10:21:39 +01:00
Miroslav Lichvar
36356ef033 ntp: move initial packet parsing from ntp_auth to ntp_core
Since commit fdfcabd79b ("ntp: drop support for long NTPv4 MACs"), the
parser doesn't need to check validify of MACs in NTPv4 packets to
distinguish them from extension fields. Move the parser to ntp_core to
avoid having a separate iteration looking for non-authentication
extension fields.
2021-11-16 10:00:31 +01:00
Miroslav Lichvar
a2d1569455 socket: increase message buffer length
Add extra space to the socket message buffer to be able to receive
maximum-length NTP-over-PTP SW/HW-timestamped messages from the Linux
error queue (which are looped back as layer-2 frames).
2021-10-27 16:22:12 +02:00
Miroslav Lichvar
952c3b2528 ntp: use previous root delay/disp in interleaved mode
When calculating the root delay and dispersion of a sample measured in
the interleaved mode, use the root delay and dispersion values from
the previous response (to which the TX timestamp corresponds). If the TX
timestamp is combined with the RX timestamp of the latest response (e.g.
in the symmetric mode), use the maximum of the previous and latest root
delay/dispersion.
2021-10-27 16:22:12 +02:00
Miroslav Lichvar
d92d24ad7f test: extend 122-xleave test 2021-10-27 16:22:12 +02:00
Miroslav Lichvar
bc33e1cda1 clientlog: undo clock adjustments in updated TX timestamps
When the server clock was updated between saving of the RX timestamp and
updating the TX timestamp, a client using interleaved mode with the four
timestamps which minimize error in measured delay (e.g. chrony) had the
server clock adjustment included in the measured delay, which could
disrupt the sample filtering and weighting.

Add a handler to track the slew epoch and remember the last offset. Undo
the adjustment in TX timestamps which have their RX timestamp in the
previous epoch to fix the delay observed by the clients.

If an unknown clock step is detected, drop all timestamps.
2021-10-27 16:22:12 +02:00
Miroslav Lichvar
189bf9c536 ntp: don't save timestamps if transmit_packet() failed
Don't save server RX and TX timestamp to clientlog if the transmission
or authentication failed (e.g. packet is handled in ntp_signd). They
will not be needed.
2021-10-27 16:22:08 +02:00
Miroslav Lichvar
c5dde9b66a ntp: initialize saved TX timestamp
Zero the initial TX timestamp which is saved for the interleaved
mode in case there is no previous timestamp saved in clientlog and
transmit_packet() does not generate a new one (e.g. due to failure in
authentication).

Fixes: 5f4cbaab7e ("ntp: optimize detection of clients using interleaved mode")
2021-10-27 12:31:36 +02:00
Miroslav Lichvar
1fb60f8db8 cmdmon: add interleaved stats to serverstats
Report the number of received interleaved requests and current timestamp
count with their span.

Expand the serverstats description in chronyc man page.
2021-10-21 17:04:51 +02:00
Miroslav Lichvar
2f05287e15 test: improve clientlog unit test
Test also timestamp maps with smaller maximum sizes.
2021-10-19 15:15:46 +02:00
Miroslav Lichvar
61226cda8c ntp: don't capture TX timestamps if clientlog is disabled
When responding to a request, don't waste time with TX timestamping
if the timestamp will not be saved (i.e. clientlog is disabled).

Fixes: 5f4cbaab7e ("ntp: optimize detection of clients using interleaved mode")
2021-10-19 15:15:46 +02:00
Miroslav Lichvar
26b51d841e doc: improve clientloglimit description 2021-10-14 17:17:10 +02:00
Miroslav Lichvar
5f4cbaab7e ntp: optimize detection of clients using interleaved mode
Use the lowest bit of the server RX and TX timestamp as a flag
indicating RX timestamp. This allows the server to detect potential
interleaved requests without having to save all its RX timestamps. It
significantly reduces the amount of memory needed to support clients
using the interleaved mode if most of the server's clients are using the
basic mode (e.g. a public server).

Capture the TX timestamp on the first response to the request which has
the flag set to not further delay the first interleaved response.

False positives are possible with broken clients which set the origin
timestamp to something else than zero or the server RX or TX timestamp.
This causes an unnecessary RX timestamp to be saved and TX timestamp
captured and saved.
2021-10-14 17:17:05 +02:00
Miroslav Lichvar
7a80647fb4 ntp: move authentication calls in transmit_packet()
Move the calls resetting and generating authentication data out of the
loop checking for unique TX timestamp. This allows the timestamps to be
manipulated after the check.
2021-10-14 16:42:23 +02:00
Miroslav Lichvar
14b8df3702 clientlog: separate NTP timestamps from IP addresses
Instead of keeping one pair of RX and TX timestamp for each address, add
a separate RX->TX map using an ordered circular buffer. Save the RX
timestamps as 64-bit integers and search them with a combined linear
interpolation and binary algorithm.

This enables the server to support multiple interleaved clients sharing
the same IP address (e.g. NAT) and it will allow other improvements to
be implemented later. A drawback is that a single broken client sending
interleaved requests at a high rate (without spoofing the source
address) can now prevent clients on other addresses from getting
interleaved responses.

The total number of saved timestamps does not change. It's still
determined by the clientloglimit directive. A new option may be added
later if needed. The whole buffer is allocated at once, but only on
first use to not waste memory on client-only configurations.
2021-10-14 16:42:20 +02:00
Miroslav Lichvar
5cb469b204 clientlog: fix debug message for maximum number of records 2021-10-14 15:03:26 +02:00
Miroslav Lichvar
29d7d3176d sys_linux: fix seccomp filter for BINDTODEVICE option
The BINDTODEVICE socket option is the first option in the seccomp filter
setting a string instead of int. Remove the length check from the
setsockopt rules to allow a device name longer than 3 characters.

This was reported in Debian bug #995207.

Fixes: b9f5ce83b0 ("sys_linux: allow BINDTODEVICE option in seccomp filter")
2021-10-06 10:09:35 +02:00
Miroslav Lichvar
76a905d652 examples: improve chronyd service
Allow writing logfiles (enabled by logdir or -l option) to /var/log and
don't require /var/spool to exist.
2021-10-04 10:54:40 +02:00
Miroslav Lichvar
83f96efdfd examples: harden systemd services
Add various settings to the example chronyd and chrony-wait services to
decrease the exposure reported by the "systemd-analyze security"
command. The original exposure was high as the analyzer does not check
the actual process (e.g. that it dropped the root privileges or that it
has its own seccomp filter).

Limit read-write access to /run, /var/lib/chrony, and /var/spool.
Access to /run (instead of /run/chrony) is needed for the refclock
socket expected by gpsd.

The mailonchange directive is most likely to break as it executes
/usr/sbin/sendmail, which can do unexpected operations depending on the
implementation. It should work with a setuid/setgid binary, but it is
not expected to write outside of /var/spool and the private /tmp.
2021-09-29 15:56:55 +02:00
Miroslav Lichvar
127826a399 ntp: check software timestamps on Linux
Apparently some routers with hardware NAT acceleration have a bug
causing the kernel timestamps to be corrupted and break NTP. Similarly
to the sanity check applied to hardware timestamps, require the
kernel/driver timestamps to be within one second of the daemon timestamp
to be accepted.
2021-09-23 15:51:35 +02:00
Miroslav Lichvar
7ee5f4888e ntp: print stratum 1 refid in ASCII in debug message 2021-09-23 15:51:35 +02:00
Miroslav Lichvar
9ed1d1afc2 doc: show arguments of ratelimit options 2021-09-23 15:51:35 +02:00
Miroslav Lichvar
d0d9a3fa43 use round() for rounding
Replace casting of values incremented by +0.5/-0.5 with round().
2021-09-23 15:51:35 +02:00
Miroslav Lichvar
9600993c28 test: fix incorrect use of RAND_MAX
On some systems (e.g. Solaris/OpenIndiana) rand() and random() have
different ranges. RAND_MAX is the maximum value returned by rand(),
but random() should always have a range of 0 through 2^31-1.

This fixes multiple failures in different tests.
2021-09-23 15:51:35 +02:00
Miroslav Lichvar
5e6f8458ff client: replace allow/deny parser
Use the new cmdparse function for parsing the (cmd)allow/deny commands
and refactor the code a bit to reduce the number of functions needed for
all the (cmd)allow/deny(all) combinations.
2021-09-23 15:50:05 +02:00
Miroslav Lichvar
f5fe5452f6 conf: rework allow/deny parser
Refactor the (cmd)allow/deny parser and make it more strict in what
input it accepts. Check the scanned numbers and require whole input to
be processed.

Move the parser to cmdparse to make it available to the client.
2021-09-23 15:16:33 +02:00
Miroslav Lichvar
3ac6a0c26c cmdmon: move comment to make its scope clearer 2021-09-23 14:48:59 +02:00
Miroslav Lichvar
c2872d1e12 test: extend 110-chronyc test 2021-09-23 14:48:59 +02:00
Miroslav Lichvar
e47e7e3661 test: fix chronyc test with disabled IPv6 support 2021-09-23 14:48:59 +02:00
Miroslav Lichvar
d8f14ec59b test: add 143-manual test 2021-09-23 14:48:59 +02:00
Miroslav Lichvar
274a51bc38 test: enable chronyc to use Unix domain socket 2021-09-23 14:48:59 +02:00
Miroslav Lichvar
92700e194c test: fix 002-scanbuild test 2021-09-23 14:48:59 +02:00
Miroslav Lichvar
87df268723 test: update compilation tests 2021-09-02 16:10:17 +02:00
Miroslav Lichvar
17a9caf5c8 cmac: add gnutls support
Similarly to hashing, add support for AES-CMAC in gnutls to avoid
linking directly with nettle.
2021-09-02 16:10:09 +02:00
Miroslav Lichvar
36441fabde hash: allow non-security MD5 use in FIPS mode
gnutls running in the FIPS140-2 mode does not allow MD5 to be
initialized, which breaks chronyd using MD5 to calculate reference ID
of IPv6 addresses. Specify a new hash algorithm for non-security MD5 use
and temporarily switch to the lax mode when initializing the hash
function.
2021-09-02 15:17:08 +02:00
Miroslav Lichvar
f363998517 hash: add gnutls support
Add support for crypto hash functions in gnutls (internally using
nettle). This can be useful to avoid directly linking with nettle to
avoid ABI breaks.
2021-09-02 15:17:08 +02:00
Miroslav Lichvar
6fc30baba8 configure: fix SIV detection in gnutls
gnutls_aead_cipher_init() is declared in gnutls/crypto.h. If the
compiler handles implicit declarations as errors, the SIV support was
not detected. Fix the check to use the correct header.
2021-09-02 15:17:00 +02:00
Miroslav Lichvar
70a0f18d52 siv: deinit gnutls on unsupported SIV 2021-09-02 13:35:35 +02:00
Miroslav Lichvar
0ad5f5ea89 privops: allow binding to PTP port
Fixes: be3158c4e5 ("ntp: add support for NTP over PTP")
2021-09-02 13:35:33 +02:00
Miroslav Lichvar
d676f39b84 doc: improve ptpport example 2021-09-01 16:56:54 +02:00
Miroslav Lichvar
31690261f5 doc: remove obsolete comment in maxslewrate description 2021-08-19 14:51:42 +02:00
Miroslav Lichvar
93326488a3 doc: shorten lock_all description 2021-08-19 14:51:42 +02:00
Miroslav Lichvar
d5ca98eaaa test: add 142-ptpport test 2021-08-19 14:51:42 +02:00
Miroslav Lichvar
be3158c4e5 ntp: add support for NTP over PTP
Allow NTP messages to be exchanged as a payload of PTP messages to
enable full hardware timestamping on NICs that can timestamp PTP packets
only. Implemented is the protocol described in this draft (version 00):

https://datatracker.ietf.org/doc/draft-mlichvar-ntp-over-ptp/

This is an experimental feature. It can be changed or removed in future.
The used PTP domain is 123 and the NTP TLV type is 0x2023 from the "do
not propagate" experimental range.

The ptpport directive enables NTP-over-PTP as a server and as a client
for all sources that have the port option set to the PTP port. The port
should be the PTP event port (319) to trigger timestamping in the
hardware.

The implementation is contained to ntp_io. It is transparent to
ntp_core.
2021-08-19 14:51:38 +02:00
Miroslav Lichvar
2f1d5d9255 ntp: add PTP rxfilter
Setting rxfilter to ptp enables timestamping of PTPv2 packets (UDP or
all transports). It will be needed for NTP-over-PTP support.
2021-08-18 16:56:09 +02:00
Miroslav Lichvar
b2c2132e4b ntp: provide remote port to NIO_OpenServerSocket()
This will allow selection of different protocols based on the remote
port. Zero means the default (NTP).
2021-08-16 14:53:46 +02:00
Stefan R. Filipek
aab6d1b153 doc: fix chronyd platform support for -P and -m
A while back, support for memory locking and real-time scheduling was
added to more platforms. The chronyd documentation wasn't updated at
that time (chronyd.conf was). This patch fixes that.
2021-08-09 12:08:12 +02:00
Miroslav Lichvar
bbbd80bf03 sys_linux: allow clone3 and pread64 in seccomp filter
These seem to be needed with the latest glibc.
2021-08-09 11:48:21 +02:00
Miroslav Lichvar
f27d719a4e rtc: avoid printing and scanning time_t
With the latest glibc it's now possible to define _TIME_BITS=64 to get
64-bit time_t on 32-bit Linux systems. This breaks the %ld printf/scanf
modifier used with the RTC drift timestamp. Process it as a double.
2021-08-05 14:41:28 +02:00
Miroslav Lichvar
789817cd91 doc: improve ntsserverkey/cert description
The files are read after dropping root privileges. They need to be
readable by the chrony user. The error message "Could not set
credentials : Error while reading file." does not make this requirement
very obvious.
2021-07-07 16:45:46 +02:00
Miroslav Lichvar
885e7774fd doc: update NEWS 2021-05-12 13:06:15 +02:00
Miroslav Lichvar
883b7eed8a update copyright years 2021-05-12 13:06:15 +02:00
Miroslav Lichvar
4049ed8766 test: make 007-cmdmon test more reliable
Reorder the local off command with respect to offline and online to
prevent the client from getting an unsynchronized response.
2021-05-12 13:06:15 +02:00
Miroslav Lichvar
f9f6803b8a test: allow inaccurate math in util unit test
Don't require timespec/timeval-double conversion tests to produce
correctly rounded results to handle x86 and other archs with wider
intermediate results.
2021-05-10 18:15:45 +02:00
Miroslav Lichvar
385f7ebfd9 test: disable privdrop in nts test
They are unrelated features. Not setting privdrop avoids a skip due to
the nobody user not having access to the test directory.
2021-05-10 16:04:34 +02:00
Miroslav Lichvar
f9cbc4803d sys_linux: check if execveat is defined
The syscall is missing on older systems.
2021-05-06 15:43:04 +02:00
Miroslav Lichvar
97973b1833 sys_linux: add second scfilter level
Add level "2" to enable a filter which blocks only specific system calls
like fork and exec* instead of blocking everything unknown. It should
be reliable with respect to changes in libraries, but it provides only a
very limited protection.
2021-05-06 13:37:21 +02:00
Miroslav Lichvar
9cdfc15e31 sys_linux: allow getuid32 in seccomp filter
This was triggered on x86 in an NTS test.
2021-05-06 13:11:10 +02:00
Miroslav Lichvar
fc99317291 sourcestats: check samples loaded from dump files
When loading a dump file with the -r option, check also sanity of the
sample time, offset, peer/root delay/dispersion, and the sample order to
better handle corrupted files.
2021-05-06 13:10:51 +02:00
Miroslav Lichvar
bb9ba3e4bd source: don't print duplicated address in selection message
Don't print the original IP address in parentheses in the "Selected
source ..." message if it is identical to the current address. That is
expected to be the usual case for sources specified by IP address.
2021-05-05 12:41:23 +02:00
Miroslav Lichvar
649f54a1e6 conf: log error when source cannot be added
Log an error message when adding of a source fails, e.g. due to the new
limit on number of sources, or when the same address is specified
multiple times.
2021-05-05 12:41:23 +02:00
Miroslav Lichvar
4070d7ffa6 nts: close file after loading cookies
Don't forget to close the file with cookies in ntsdumpdir if
successfully loaded.

Fixes: 2fa83b541c ("nts: save and load cookies on client")
2021-05-05 12:41:23 +02:00
Miroslav Lichvar
0493abb68a nts: ignore long non-critical records
In the NTS-KE client don't reject the response if it has non-critical
records that are too long for the processing buffer. This is not
expected to happen with the current specification, but it might be
needed with future extensions.

Fixes: 7925ed39b8 ("nts: fix handling of long server negotiation record")
2021-05-05 12:41:22 +02:00
Miroslav Lichvar
8c1e16711d test: fix date use in 010-nts system test
Avoid using nonportable -d option of date.
2021-04-29 15:03:37 +02:00
Miroslav Lichvar
1d03908646 test: remove logs before chronyd start in system tests 2021-04-29 13:26:01 +02:00
Miroslav Lichvar
49d718c025 test: extend configuration in system tests 2021-04-29 13:23:34 +02:00
Miroslav Lichvar
c536b2561b test: rework seccomp testing
Instead of a single test with enabled seccomp, rerun all other
non-destructive and destructive tests for each seccomp level.
2021-04-29 13:23:34 +02:00
Miroslav Lichvar
b9f5ce83b0 sys_linux: allow BINDTODEVICE option in seccomp filter
Fixes: 4ef944b734 ("socket: add support for binding sockets to device")
2021-04-29 12:37:26 +02:00
Miroslav Lichvar
8baab00ae0 doc: warn about -F and mailonchange in chronyd man page 2021-04-29 09:44:32 +02:00
Miroslav Lichvar
d01cb5af46 nts: avoid assumption about cookie record
The cookie record is currently assumed to be the longest record that
needs to be accepted by the client, but that does not have to be always
the case. Define the processing buffer using the maximum body record
constant instead and add an assertion to make sure it's not smaller than
the maximum accepted cookie length.
2021-04-29 09:44:32 +02:00
Miroslav Lichvar
7925ed39b8 nts: fix handling of long server negotiation record
Recent change in handling of the NTPv4 server negotiation record (commit
754097944b) increased the length of the instance name buffer to make
room for the trailing dot. This allowed a record with body truncated in
the processing buffer to be accepted and caused an over-read of 1 byte
in the memcpy() call saving the name to the instance buffer.

Modify the client to accept only records that fit in the processing
buffer.

Fixes: 754097944b ("nts: handle negotiated server as FQDN")
2021-04-29 09:44:18 +02:00
Miroslav Lichvar
9d869d8709 doc: update NEWS 2021-04-22 10:44:50 +02:00
Miroslav Lichvar
4f94e22b4b doc: update README 2021-04-22 10:20:31 +02:00
Miroslav Lichvar
d9b720471d ntp: fix address in error message 2021-04-22 10:20:31 +02:00
Miroslav Lichvar
039b388c82 nameserv: avoid sockaddr_in6 with disabled IPv6 support
Fixes: 10c760a80c ("nameserv: require getaddrinfo() and getnameinfo()")
2021-04-22 10:20:31 +02:00
Miroslav Lichvar
3f6528da77 test: extend 129-reload test 2021-04-22 10:20:31 +02:00
Miroslav Lichvar
4f43c060a3 sources: fix loading of refclock dump files
Allow zero stratum in loaded dump files.

Fixes: f8610d69f0 ("sources: improve handling of dump files and their format")
2021-04-22 10:20:31 +02:00
Miroslav Lichvar
3e55fe6919 sources: don't print NULL string to dump file
For reference clocks, which don't have a name, print "." instead of
NULL.

Fixes: f8610d69f0 ("sources: improve handling of dump files and their format")
2021-04-22 10:20:31 +02:00
Miroslav Lichvar
754097944b nts: handle negotiated server as FQDN
The NTS RFC requires the recipient of the Server Negotiation NTS-KE
record to handle the name as a fully qualified domain name. Add a
trailing dot if not present to force the name to be resolved as one.
2021-04-22 10:20:31 +02:00
Miroslav Lichvar
dd6a25edf2 test: extend 106-refclock test 2021-04-22 10:20:31 +02:00
Miroslav Lichvar
e697833976 doc: improve description of allow directive
Prefer CIDR notation, clarify use of hostnames and order of allow/deny
directives, refer to the accheck command.
2021-04-22 10:20:31 +02:00
Bryan Christianson
40d80624f6 sys_timex: remove workaround for broken ntp_adjtime on macOS
Early beta releases of macOS Big Sur had a signed/unsigned error in
Apple's implementation of ntp_adjtime. Apple have since fixed this error
and the workaround is no longer required.
2021-04-20 15:30:47 +02:00
Miroslav Lichvar
9a716cc284 doc: improve FAQ 2021-04-15 15:17:13 +02:00
Miroslav Lichvar
13a78ecd2f conf: require sourcedir files to be terminated by newline
When reading a *.sources file require that each line is termined by the
newline character to avoid processing an unfinished line, e.g. due to an
unexpected call of the reload command when the file is being written in
place.
2021-04-15 15:17:13 +02:00
Miroslav Lichvar
a9f0c681cb test: make system tests more reliable 2021-04-15 15:17:13 +02:00
Miroslav Lichvar
862aa285a2 test: update and extend 110-chronyc test 2021-04-15 15:17:13 +02:00
Miroslav Lichvar
84d2811800 ntp: add copy option
When separate client and server instances of chronyd are running on one
computer (e.g. for security or performance reasons) and are synchronized
to each other, the server instance provides a reference ID based on the
local address used for synchronization of its NTP clock, which breaks
detection of synchronization loops for its own clients.

Add a "copy" option to specify that the server and client are closely
related, no loop can form between them, and the client should assume the
reference ID and stratum of the server to fix detection of loops between
the server and clients of the client.
2021-04-15 15:17:13 +02:00
Miroslav Lichvar
635a9d3f5a ntp: clamp remote stratum
Don't set the remote stratum (used for polling adjustments) to values
larger than 16.
2021-04-15 15:17:13 +02:00
Miroslav Lichvar
81f7f6ddf0 ntp: don't update source status with unsynchronized data
Don't update the leap and stratum used in source selection if they
indicate an unsynchronized source.

Fixes: 2582be8754 ("sources: separate update of leap status")
2021-04-15 15:16:39 +02:00
Uwe Kleine-König
aa22c515ce refclock: drop return after LOG_FATAL
The LOG_FATAL macro expands to (emitting the message and then) exit(1).
So a return after LOG_FATAL isn't reached. Drop all those to simplify
the code a bit.
2021-04-12 09:24:07 +02:00
Miroslav Lichvar
2ca2c85365 ntp: fix loop test for special reference modes
It is not sufficient to check for disabled server sockets as they are
not open only after the special reference modes end (e.g. initstepslew).

Fixes: 004986310d ("ntp: skip loop test if no server socket is open")
2021-04-07 17:14:22 +02:00
Foster Snowhill
966e6fd939 sys_linux: allow setsockopt(SOL_IP, IP_TOS) in seccomp
This system call is required by the DSCP marking feature introduced in commit
6a5665ca58 ("conf: add dscp directive").

Before this change, enabling seccomp filtering (chronyd -F 1) and specifying a
custom DSCP value in the configuration (for example "dscp 46") caused the
process to be killed by seccomp due to IP_TOS not being allowed by the filter.

Tested before and after the change on Ubuntu 21.04, kernel 5.11.0-13-generic.
IP_TOS is available since Linux 1.0, so I didn't add any ifdefs for it.

Signed-off-by: Foster Snowhill <forst@forstwoof.ru>
2021-04-07 17:14:22 +02:00
Miroslav Lichvar
4f0dd72cf0 doc: improve chrony.conf man page 2021-04-07 17:14:22 +02:00
Miroslav Lichvar
69aa2eff99 doc: improve FAQ
Add new questions, fix typos and version-specific information.
2021-04-07 17:14:09 +02:00
Miroslav Lichvar
3e1ec36ca5 test: extend 103-initstepslew test 2021-04-07 16:55:38 +02:00
Miroslav Lichvar
224ab8ddb1 test: enable valgrind in more tests 2021-03-24 17:50:33 +01:00
Miroslav Lichvar
307c2ec70f test: extend 106-refclock test 2021-03-18 17:41:36 +01:00
Miroslav Lichvar
5381fb4ee9 refclock: increase PPS lock limit
Increase the maximum acceptable offset of the PPS lock reference from
20% to 40% of the PPS interval to not require the refclock offset to be
specified in configuration so accurately, or enable operation with a
highly unstable reference clock.
2021-03-18 17:41:28 +01:00
Miroslav Lichvar
3812ec2aa2 declare variables set from signal handlers as volatile
Make sure variables set from signal handlers are not cached in
registers.
2021-03-18 17:38:18 +01:00
Kamil Dudka
4e7690ebec configure: use well-known file name conftest.c
... for configuration checks.  Compiler wrappers check for this name
in order to skip any instrumentation of the build that is intended
for regular source files only.
2021-03-15 10:42:48 +01:00
Miroslav Lichvar
cf3d976a68 test: extend ntp_sources unit test 2021-03-11 11:47:48 +01:00
Miroslav Lichvar
26fc28c056 test: drop logging suspension
Instead of selectively suspending logging by redirecting messages to
/dev/null, increase the default minimum log severity to FATAL. In the
debug mode, all messages are printed.
2021-03-11 11:47:31 +01:00
Miroslav Lichvar
d2117ab697 cmdmon: return error if doffset command fails 2021-03-04 17:26:00 +01:00
Miroslav Lichvar
52b29f673f cmdmon: convert doffset request to float 2021-03-04 17:26:00 +01:00
Miroslav Lichvar
e86b60a9d7 local: return status from offset accumulation
Change the functions accumulating offset to return success or failure.
2021-03-04 17:26:00 +01:00
Miroslav Lichvar
53501b743f client: report invalid values in doffset and dfreq commands 2021-03-04 17:26:00 +01:00
Miroslav Lichvar
c61ddb70da test: extend util unit test 2021-03-04 17:26:00 +01:00
Miroslav Lichvar
9339766bfe test: use env shebang in all bash scripts
This allows the scripts to be executed on systems that don't have bash
in /bin. This fixes "make check".
2021-03-04 12:36:36 +01:00
Miroslav Lichvar
f60410016a test: extend 007-cmdmon system test 2021-03-04 12:36:36 +01:00
Miroslav Lichvar
7a02371698 util: require inet_pton()
Always use inet_pton() for converting IP addresses. It should be
available on all currently supported systems.
2021-03-04 12:36:36 +01:00
Miroslav Lichvar
579d8c9907 nameserv: avoid unnecessary getaddrinfo() calls
Check if the name passed to DNS_Name2IPAddress() is an IP address
before calling getaddrinfo(), which can be much slower and work
differently on different systems.
2021-03-04 12:36:36 +01:00
Miroslav Lichvar
10c760a80c nameserv: require getaddrinfo() and getnameinfo()
Remove support for the long-deprecated gethostbyname() and
gethostbyaddr() functions.
2021-03-04 12:36:36 +01:00
Miroslav Lichvar
2d39a12f51 cmdmon: fix responding to IPv4 addresses on FreeBSD
On FreeBSD, the source address cannot be specified when sending a
message on a socket bound to a non-any IPv4 address, e.g. in default
configuration 127.0.0.1. In this case, make the address unspecified.

This is similar to commit 6af39d63aa ("ntp: don't use IP_SENDSRCADDR
on bound socket").

Fixes: f06c1cfa97 ("cmdmon: respond from same address")
2021-03-04 12:36:36 +01:00
Miroslav Lichvar
517b1ae29a main: suppress info messages with -p option
Log (to stderr) only warnings and higher when printing the
configuration to suppress the "chronyd starting" message.
2021-03-04 12:36:23 +01:00
Miroslav Lichvar
b7347d931b sys_linux: check if statx syscall is defined
statx seems to be missing in older kernel and libseccomp headers, still
used on some supported systems.
2021-03-03 10:04:07 +01:00
Miroslav Lichvar
4f878ba144 main: warn if running with root privileges
Log a warning message if the main process has not dropped the root
privileges, i.e. when the compiled-in user or user specified by the user
directive or -u option is root.
2021-02-25 17:06:14 +01:00
Miroslav Lichvar
8acdb5d1e2 refclock: warn if lock refid is invalid
Log a warning message if the specified lock refid doesn't match any
existing refclock or it matches the refclock which has the lock option
itself.
2021-02-25 17:06:13 +01:00
Miroslav Lichvar
62f2d5736d refclock: warn if maxlockage is too small
Log a warning message if the interval covered by the maxlockage at the
PPS rate of a refclock is shorter than driver poll of the locked
refclock.

Reported-by: Matt Corallo <ntp-lists@mattcorallo.com>
2021-02-25 17:06:10 +01:00
Miroslav Lichvar
dc22df93f5 ntp: restart resolving on online command
If the online command is received when the resolver is running, start
it again as soon as it finishes instead of waiting for the timer.

This should reduce the time needed to get all sources resolved on boot
if chronyd is started before the network is online and the chronyc
online command is issued before the first round of resolving can finish,
e.g. due to an unreachable DNS server in resolv.conf.
2021-02-25 17:02:58 +01:00
Miroslav Lichvar
d898bd246b test: extend 139-nts test 2021-02-18 17:44:04 +01:00
Miroslav Lichvar
ebf0ff2c0d cmdmon: set certset for new sources
Add the new certset option to the cmdmon protocol.
2021-02-18 17:44:04 +01:00
Miroslav Lichvar
cc77b0e9fd conf: add certset option to NTP sources
Allow the set of trusted certificates to be selected for each NTP
source individually.
2021-02-18 17:44:04 +01:00
Miroslav Lichvar
a8bc25e543 conf: add set selection to ntstrustedcerts
Add an optional set-ID argument to the ntstrustedcerts directive to
enable multiple sets of trusted certificates to be specified.
2021-02-18 17:44:04 +01:00
Miroslav Lichvar
6615bb1b78 nts: add support for multiple sets of trusted certificates
Modify the session, NTS-KE, and NTS-NTP code to support multiple sets of
trusted certificates and identify the sets by a 32-bit ID.
2021-02-18 17:44:04 +01:00
Miroslav Lichvar
f650b8c515 configure: check for O_NOFOLLOW flag
If the O_NOFOLLOW flag used by open() is not defined, try it with
_GNU_SOURCE. This is needed with glibc-2.11 and earlier.

Reported-by: Marius Rohde <marius.rohde@meinberg.de>
2021-02-16 13:59:41 +01:00
Christian Ehrhardt
ae2e0318d1 sys_linux: allow statx and fstatat64 in seccomp filter
With glibc 2.33 on armhf statx and fstatat64 are triggered.
Allow this call to un-break chrony on such platforms.

Without this e.g. test 005-scfilter fails and with ltrace -rTS reports:
a)
  0.001684 SYS_397(11, 0xf75def08, 6144, 2047 <no return ...>
  0.759239 +++ killed by SIGSYS +++
b)
  0.003749 SYS_327(-100, 0xffdbcc3c, 0xffdbcb50, 0)
  0.000821 --- SIGSYS (Bad system call) ---

Current armhf syscalls from:
https://github.com/torvalds/linux/blob/v5.10/arch/arm/tools/syscall.tbl

Signed-off-by: Christian Ehrhardt <christian.ehrhardt@canonical.com>
2021-02-12 11:01:22 +01:00
Miroslav Lichvar
26ce610155 nts: allow ntstrustedcerts to specify directory
If the specified path is a directory, load all certificates in the
directory.
2021-02-11 16:13:39 +01:00
Miroslav Lichvar
316d47e3b4 nts: allow multiple files with trusted certificates
Allow the ntstrustedcerts directive to be specified multiple times.
2021-02-11 16:13:39 +01:00
Miroslav Lichvar
90557cf1ba nts: allow multiple server keys and certificates
Allow the ntsservercert and ntsserverkey directives to be specified
multiple times to enable the NTS-KE server to operate under multiple
names.
2021-02-11 16:13:39 +01:00
Miroslav Lichvar
80e627c86b nts: define type for credentials
Add a NKSN_Credentials type to avoid referring to it as void *.
2021-02-11 16:13:39 +01:00
Miroslav Lichvar
0e4995e10b nts: split creating server and client credentials 2021-02-11 16:13:39 +01:00
Miroslav Lichvar
a598983f9b client: fix sourcename command to accept ID addresses
Fix the command to print the name corresponding to an unresolved
address.
2021-02-11 16:13:39 +01:00
Miroslav Lichvar
27641876c5 ntp: simplify NSR_Finalise() 2021-02-11 16:13:39 +01:00
Miroslav Lichvar
4d139eeca6 ntp: limit number of sources
Don't rely on assertions and running out of memory to terminate if
an extremely large number of sources is added. Set the maximum number
to 65536 to have a practical limit where chronyd still has a chance to
appear functional with some operations having a quadratic time
complexity.
2021-02-11 16:13:39 +01:00
Miroslav Lichvar
3f2806c19c nts: reset NTP address/port if removed in NTS-KE
When an NTS-KE server stops providing the NTP address or port, change
them to the original values to avoid the client getting stuck
with a non-responding address/port.
2021-02-11 15:24:12 +01:00
Miroslav Lichvar
e297df78e4 nts: load cookies early
Instead of waiting for the first request, try to load the cookies as
soon as the instance is created, or the NTS address is changed.

This enables loading of dump files for servers that are negotiated in
NTS-KE.
2021-02-11 09:52:57 +01:00
Miroslav Lichvar
c1d56ede3f nts: rework update of NTP server address
In the NTS-NTP client instance, maintain a local copy of the NTP address
instead of using a pointer to the NCR's address, which may change at
unexpected times.

Also, change the NNC_CreateInstance() to accept only the NTP port to
make it clear the initial NTP address is the same as the NTS-KE address
and to make it consistent with NNC_ChangeAddress(), which accepts only
one address.
2021-02-11 09:52:57 +01:00
Miroslav Lichvar
2e52aca3bf ntp: avoid recursive update of address
Allow NSR_UpdateSourceNtpAddress() to be (indirectly) called from
NCR_CreateInstance() and NCR_ChangeRemoteAddress(). In these cases, save
the addresses and make the update later when the function calls return.
2021-02-11 09:52:57 +01:00
Miroslav Lichvar
b0fc5832f4 ntp: require port match in address update
In NSR_UpdateSourceNtpAddress() and other updates of the address require
that the old port matches the current source's port.
2021-02-11 09:52:57 +01:00
Miroslav Lichvar
cf6af112e1 test: extend 129-reload test 2021-02-04 17:48:51 +01:00
Miroslav Lichvar
fa3052e776 sources: set reference after loading dump files
After loading the dump files with the -r option, immediately perform a
source selection with forced setting of the reference. This shortens the
interval when a restarted server doesn't respond with synchronized time.
It no longer needs to wait for the first measurement from the best
source (which had to pass all the filters).
2021-02-04 17:48:47 +01:00
Miroslav Lichvar
f8610d69f0 sources: improve handling of dump files and their format
Check for write errors when saving dump files. Don't save files with no
samples. Add more sanity checks for loaded data.

Extend the file format to include an identifier, the reachability
register, leap status, name, and authentication flag. Avoid loading
unauthenticated data after switching authentication on. Change format
and order of some fields to simplify parsing. Drop fields that were kept
only for compatibility.

The dump files now contain all information needed to perform the source
selection and update the reference.

There is no support kept for the old file format. Loading of old dump
files will fail after upgrading to new version.
2021-02-04 17:44:27 +01:00
Miroslav Lichvar
1a8dcce84f sources: update stratum with leap status
Remove stratum from the NTP sample and update it together with the leap
status. This enables a faster update when samples are dropped by the NTP
filters.
2021-02-04 17:43:47 +01:00
Miroslav Lichvar
f74eb67567 sourcestats: move stratum to sources
The stratum value is not needed in sourcestats. Keep it in the source
itself.
2021-02-04 17:43:29 +01:00
Miroslav Lichvar
144fcdde34 main: fix typo in comment 2021-02-03 17:51:47 +01:00
Miroslav Lichvar
3cef7f975c main: cancel clock correction before dumping sources
On exit, cancel the remaining clock correction before measurements are
saved to dumpdir to fix them for the state in which chronyd will start
again.
2021-02-03 11:06:00 +01:00
Baruch Siach
a2372b0c3a sys_linux: fix build with older kernel headers
The renameat2 system call was introduced in kernel version 3.15. Fix
build against older headers.
2021-01-28 15:32:03 +01:00
Miroslav Lichvar
362d7c517d test: improve NTS tests 2021-01-14 18:17:48 +01:00
Miroslav Lichvar
62389b7e50 nts: support servers specified by IP address
Certificates can include IP addresses as alternative names to enable
clients to verify such certificates without knowing the hostname.

Accept an IP address as a name in the NTS-NTP client and modify the
session code to not set the SNI in this case.
2021-01-14 18:17:48 +01:00
Miroslav Lichvar
eb9e6701fd ntp: allow replacement of sources specified by IP address
For sources specified by an IP address, keep the original address as the
source's name and pass it to the NCR instance. Allow the sources to go
through the replacement process if their address has changed.

This will be useful with NTS-KE negotiation.

The IP-based source names are now provided via cmdmon. This means
chronyc -n and -N can show two different addresses for a source.
2021-01-14 18:17:48 +01:00
Miroslav Lichvar
b585954b21 ntp: fix NULL pointer 2021-01-14 18:17:48 +01:00
Miroslav Lichvar
82ddc6a883 test: support ss as netstat replacement
netstat is considered obsolete on Linux. It is replaced by ss from
iproute. Support both tools for the test port selection.
2021-01-14 18:17:48 +01:00
Miroslav Lichvar
624b76e86e test: fix port selection to disable grep output 2021-01-14 18:17:48 +01:00
Miroslav Lichvar
4dd0aece02 test: make 120-selectoptions more reliable
Remove packet interval checks with long delays as the tests are much
more likely to end when the client is waiting for a response. Increase
the base delay to make selection with two sources more reliable.

Reported-by: Christian Ehrhardt <christian.ehrhardt@canonical.com>
2021-01-14 18:17:48 +01:00
Miroslav Lichvar
e85fb0c25e socket: add debug message for unexpected control message 2021-01-14 18:17:48 +01:00
Miroslav Lichvar
fc8783a933 socket: check length of received control messages
Make sure each processed control messages has the expected length.
Beside improved safety, this should prevent potential issues with broken
timestamps on systems that support both 64-bit and 32-bit time_t.
2021-01-14 18:17:48 +01:00
Miroslav Lichvar
e7897eb9cc sched: stop dispatching timeouts on exit
Check in the dispatch loop whether the need_to_exit flag was set.
2021-01-14 18:17:48 +01:00
Miroslav Lichvar
59e8b79034 sched: improve infinite loop detection
The "infinite loop in scheduling" fatal error was observed on a system
running out of memory. Presumably, the execution of the process slowed
down due to memory thrashing so much that the dispatching loop wasn't
able to break with a single server polled at a 16-second interval.

To allow recovery in such a case, require for the error more than
20 handled timeouts and a rate higher than 100 per second.

Reported-by: Jamie Gruener <jamie.gruener@biospatial.io>
2021-01-14 18:17:39 +01:00
Michael Witten
fb7475bf59 rtc: log error message when driver initialisation fails 2020-12-15 10:49:07 +01:00
Michael Witten
cd98516cae doc: diagnose problem with RTC interrupts on Linux
This commit updates the FAQ with a new entry.

chronyd's Linux RTC driver (rtc_linux.c) requires the following ioctl
requests to be functional:

  RTC_UIE_ON
  RTC_UIE_OFF

However, a Linux system's RTC driver does not necessarily implement them,
as noted in these previous commits:

  d66b2f2b24
  rtc: handle RTCs that don't support interrupts
  Tue Dec 10 17:45:28 2019 +0100

  bff3f51d13
  rtc: extend check for RTCs that don't support interrupts
  Thu Dec 12 12:50:19 2019 +0100

Fortunately, the Linux kernel can be built with software emulation of
these hardware requests, by enabling the following config variable:

  CONFIG_RTC_INTF_DEV_UIE_EMUL
    Provides an emulation for RTC_UIE if the underlying rtc chip
    driver does not expose RTC_UIE ioctls. Those requests generate
    once-per-second update interrupts, used for synchronization.

    The emulation code will read the time from the hardware
    clock several times per second, please enable this option
    only if you know that you really need it.

This commit records these facts for the benefit of the user.
2020-12-15 10:41:53 +01:00
Miroslav Lichvar
e399d8dd1f doc: fix ntsntpserver reference in chrony.conf man page
Fix the name of ntsntpserver directive in ntsrotate description.

Reported-By: Phil Roberts <phil@robertskeys.net>
2020-11-26 15:09:38 +01:00
173 changed files with 7222 additions and 2024 deletions

View File

@@ -35,7 +35,7 @@ LDFLAGS = @LDFLAGS@
EXTRA_OBJS = @EXTRA_OBJS@ EXTRA_OBJS = @EXTRA_OBJS@
OBJS = array.o cmdparse.o conf.o local.o logging.o main.o memory.o \ OBJS = array.o cmdparse.o conf.o local.o logging.o main.o memory.o quantiles.o \
reference.o regress.o rtc.o samplefilt.o sched.o socket.o sources.o sourcestats.o \ reference.o regress.o rtc.o samplefilt.o sched.o socket.o sources.o sourcestats.o \
stubs.o smooth.o sys.o sys_null.o tempcomp.o util.o $(EXTRA_OBJS) stubs.o smooth.o sys.o sys_null.o tempcomp.o util.o $(EXTRA_OBJS)

69
NEWS
View File

@@ -1,3 +1,72 @@
New in version 4.3
==================
Enhancements
------------
* Add local option to refclock directive to stabilise system clock
with more stable free-running clock (e.g. TCXO, OCXO)
* Add maxdelayquant option to server/pool/peer directive to replace
maxdelaydevratio filter with long-term quantile-based filtering
* Add selection option to log directive
* Allow external PPS in PHC refclock without configurable pin
* Don't accept first interleaved response to minimise error in delay
* Don't use arc4random on Linux to avoid server performance loss
* Improve filter option to better handle missing NTP samples
* Improve stability with hardware timestamping and PHC refclock
* Update seccomp filter
Bug fixes
---------
* Fix waitsync command to reconnect when not getting response
New in version 4.2
==================
Enhancements
------------
* Add support for NTPv4 extension field improving synchronisation
stability and resolution of root delay and dispersion (experimental)
* Add support for NTP over PTP (experimental)
* Add support for AES-CMAC and hash functions in GnuTLS
* Improve server interleaved mode to be more reliable and support
multiple clients behind NAT
* Update seccomp filter
* Add statistics about interleaved mode to serverstats report
Bug fixes
---------
* Fix RTC support with 64-bit time_t on 32-bit Linux
* Fix seccomp filter to work correctly with bind*device directives
* Suppress kernel adjustments of system clock (dosynctodr) on illumos
Other changes
-------------
* Switch Solaris support to illumos
New in version 4.1
==================
Enhancements
------------
* Add support for NTS servers specified by IP address (matching
Subject Alternative Name in server certificate)
* Add source-specific configuration of trusted certificates
* Allow multiple files and directories with trusted certificates
* Allow multiple pairs of server keys and certificates
* Add copy option to server/pool directive
* Increase PPS lock limit to 40% of pulse interval
* Perform source selection immediately after loading dump files
* Reload dump files for addresses negotiated by NTS-KE server
* Update seccomp filter and add less restrictive level
* Restart ongoing name resolution on online command
Bug fixes
---------
* Fix responding to IPv4 command requests on FreeBSD
* Fix dump files to not include uncorrected offset
* Fix initstepslew to accept time from own NTP clients
* Reset NTP address and port when no longer negotiated by NTS-KE server
New in version 4.0 New in version 4.0
================== ==================

9
README
View File

@@ -28,7 +28,7 @@ What will chrony run on?
======================== ========================
The software is known to work on Linux, FreeBSD, NetBSD, macOS and The software is known to work on Linux, FreeBSD, NetBSD, macOS and
Solaris. Closely related systems may work too. Any other system will illumos. Closely related systems may work too. Any other system will
likely require a porting exercise. likely require a porting exercise.
How do I set it up? How do I set it up?
@@ -91,7 +91,7 @@ Acknowledgements
In writing the chronyd program, extensive use has been made of the NTPv3 (RFC In writing the chronyd program, extensive use has been made of the NTPv3 (RFC
1305) and NTPv4 (RFC 5905) specification. The source code of the xntpd/ntpd 1305) and NTPv4 (RFC 5905) specification. The source code of the xntpd/ntpd
implementation written by Dennis Fergusson, Lars Mathiesen, David Mills, and implementation written by Dennis Fergusson, Lars Mathiesen, David Mills, and
others, has been used to check the details of the protocol. others has been used to check the details of the protocol.
The following people have provided patches and other major contributions The following people have provided patches and other major contributions
to chrony: to chrony:
@@ -108,6 +108,7 @@ Erik Bryer <ebryer@spots.ab.ca>
Jonathan Cameron <jic23@cam.ac.uk> Jonathan Cameron <jic23@cam.ac.uk>
Bryan Christianson <bryan@whatroute.net> Bryan Christianson <bryan@whatroute.net>
Juliusz Chroboczek <jch@pps.jussieu.fr> Juliusz Chroboczek <jch@pps.jussieu.fr>
Kamil Dudka <kdudka@redhat.com>
Christian Ehrhardt <christian.ehrhardt@canonical.com> Christian Ehrhardt <christian.ehrhardt@canonical.com>
Paul Elliott <pelliott@io.com> Paul Elliott <pelliott@io.com>
Robert Fairley <rfairley@redhat.com> Robert Fairley <rfairley@redhat.com>
@@ -124,6 +125,7 @@ Jachym Holecek <jakym@volny.cz>
Håkan Johansson <f96hajo@chalmers.se> Håkan Johansson <f96hajo@chalmers.se>
Jim Knoble <jmknoble@pobox.com> Jim Knoble <jmknoble@pobox.com>
Antti Jrvinen <costello@iki.fi> Antti Jrvinen <costello@iki.fi>
Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Eric Lammerts <eric@lammerts.org> Eric Lammerts <eric@lammerts.org>
Stefan Lucke <stefan@lucke.in-berlin.de> Stefan Lucke <stefan@lucke.in-berlin.de>
Victor Lum <viclum@vanu.com> Victor Lum <viclum@vanu.com>
@@ -137,6 +139,8 @@ Denny Page <dennypage@me.com>
Chris Perl <cperl@janestreet.com> Chris Perl <cperl@janestreet.com>
Gautier PHILIPPON <gautier.philippon@ensimag.grenoble-inp.fr> Gautier PHILIPPON <gautier.philippon@ensimag.grenoble-inp.fr>
Andreas Piesk <apiesk@virbus.de> Andreas Piesk <apiesk@virbus.de>
Baruch Siach <baruch@tkos.co.il>
Foster Snowhill <forst@forstwoof.ru>
Andreas Steinmetz <ast@domdv.de> Andreas Steinmetz <ast@domdv.de>
NAKAMURA Takumi <takumi@ps.sakura.ne.jp> NAKAMURA Takumi <takumi@ps.sakura.ne.jp>
Timo Teras <timo.teras@iki.fi> Timo Teras <timo.teras@iki.fi>
@@ -148,6 +152,7 @@ Bernhard M. Wiedemann <bwiedemann@suse.de>
Joachim Wiedorn <ad_debian@joonet.de> Joachim Wiedorn <ad_debian@joonet.de>
Ralf Wildenhues <Ralf.Wildenhues@gmx.de> Ralf Wildenhues <Ralf.Wildenhues@gmx.de>
Ulrich Windl <ulrich.windl@rz.uni-regensburg.de> Ulrich Windl <ulrich.windl@rz.uni-regensburg.de>
Michael Witten <mfwitten@gmail.com>
Doug Woodward <dougw@whistler.com> Doug Woodward <dougw@whistler.com>
Thomas Zajic <zlatko@zlatko.fdns.net> Thomas Zajic <zlatko@zlatko.fdns.net>

23
candm.h
View File

@@ -108,7 +108,8 @@
#define REQ_CLIENT_ACCESSES_BY_INDEX3 68 #define REQ_CLIENT_ACCESSES_BY_INDEX3 68
#define REQ_SELECT_DATA 69 #define REQ_SELECT_DATA 69
#define REQ_RELOAD_SOURCES 70 #define REQ_RELOAD_SOURCES 70
#define N_REQUEST_TYPES 71 #define REQ_DOFFSET2 71
#define N_REQUEST_TYPES 72
/* Structure used to exchange timespecs independent of time_t size */ /* Structure used to exchange timespecs independent of time_t size */
typedef struct { typedef struct {
@@ -268,6 +269,8 @@ typedef struct {
#define REQ_ADDSRC_INTERLEAVED 0x80 #define REQ_ADDSRC_INTERLEAVED 0x80
#define REQ_ADDSRC_BURST 0x100 #define REQ_ADDSRC_BURST 0x100
#define REQ_ADDSRC_NTS 0x200 #define REQ_ADDSRC_NTS 0x200
#define REQ_ADDSRC_COPY 0x400
#define REQ_ADDSRC_EF_EXP1 0x800
typedef struct { typedef struct {
uint32_t type; uint32_t type;
@@ -292,7 +295,9 @@ typedef struct {
Float offset; Float offset;
uint32_t flags; uint32_t flags;
int32_t filter_length; int32_t filter_length;
uint32_t reserved[3]; uint32_t cert_set;
Float max_delay_quant;
uint32_t reserved[1];
int32_t EOR; int32_t EOR;
} REQ_NTP_Source; } REQ_NTP_Source;
@@ -307,8 +312,7 @@ typedef struct {
} REQ_Dfreq; } REQ_Dfreq;
typedef struct { typedef struct {
int32_t sec; Float doffset;
int32_t usec;
int32_t EOR; int32_t EOR;
} REQ_Doffset; } REQ_Doffset;
@@ -403,7 +407,7 @@ 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, added new fields and (two times), delta offset, and manual timestamp, added new fields and
flags to NTP source request and report, made length of manual list constant, flags to NTP source request and report, made length of manual list constant,
added new commands: authdata, ntpdata, onoffline, refresh, reset, added new commands: authdata, ntpdata, onoffline, refresh, reset,
selectdata, serverstats, shutdown, sourcename selectdata, serverstats, shutdown, sourcename
@@ -514,7 +518,8 @@ typedef struct {
#define RPY_CLIENT_ACCESSES_BY_INDEX3 21 #define RPY_CLIENT_ACCESSES_BY_INDEX3 21
#define RPY_SERVER_STATS2 22 #define RPY_SERVER_STATS2 22
#define RPY_SELECT_DATA 23 #define RPY_SELECT_DATA 23
#define N_REPLY_TYPES 24 #define RPY_SERVER_STATS3 24
#define N_REPLY_TYPES 25
/* Status codes */ /* Status codes */
#define STT_SUCCESS 0 #define STT_SUCCESS 0
@@ -527,8 +532,7 @@ typedef struct {
#define STT_BADSUBNET 7 #define STT_BADSUBNET 7
#define STT_ACCESSALLOWED 8 #define STT_ACCESSALLOWED 8
#define STT_ACCESSDENIED 9 #define STT_ACCESSDENIED 9
/* Deprecated */ #define STT_NOHOSTACCESS 10 /* Deprecated */
#define STT_NOHOSTACCESS 10
#define STT_SOURCEALREADYKNOWN 11 #define STT_SOURCEALREADYKNOWN 11
#define STT_TOOMANYSOURCES 12 #define STT_TOOMANYSOURCES 12
#define STT_NORTC 13 #define STT_NORTC 13
@@ -658,6 +662,9 @@ typedef struct {
uint32_t cmd_drops; uint32_t cmd_drops;
uint32_t log_drops; uint32_t log_drops;
uint32_t ntp_auth_hits; uint32_t ntp_auth_hits;
uint32_t ntp_interleaved_hits;
uint32_t ntp_timestamps;
uint32_t ntp_span_seconds;
int32_t EOR; int32_t EOR;
} RPY_ServerStats; } RPY_ServerStats;

323
client.c
View File

@@ -4,7 +4,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Lonnie Abelbeck 2016, 2018 * Copyright (C) Lonnie Abelbeck 2016, 2018
* Copyright (C) Miroslav Lichvar 2009-2020 * Copyright (C) Miroslav Lichvar 2009-2021
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of version 2 of the GNU General Public License as
@@ -61,7 +61,7 @@ static ARR_Instance server_addresses;
static int sock_fd = -1; static int sock_fd = -1;
static int quit = 0; static volatile int quit = 0;
static int on_terminal = 0; static int on_terminal = 0;
@@ -283,6 +283,9 @@ open_io(void)
close_io(); close_io();
} }
/* Start from the first address if called again */
address_index = 0;
return 0; return 0;
} }
@@ -779,182 +782,25 @@ process_cmd_manual(CMD_Request *msg, const char *line)
/* ================================================== */ /* ================================================== */
static int static int
parse_allow_deny(CMD_Request *msg, char *line) process_cmd_allowdeny(CMD_Request *msg, char *line, int cmd, int allcmd)
{ {
unsigned long a, b, c, d; int all, subnet_bits;
int n, specified_subnet_bits;
IPAddr ip; IPAddr ip;
char *p;
p = line; if (!CPS_ParseAllowDeny(line, &all, &ip, &subnet_bits)) {
if (!*p) { LOG(LOGS_ERR, "Could not read address");
/* blank line - applies to all addresses */ return 0;
ip.family = IPADDR_UNSPEC;
UTI_IPHostToNetwork(&ip, &msg->data.allow_deny.ip);
msg->data.allow_deny.subnet_bits = htonl(0);
} else {
char *slashpos;
slashpos = strchr(p, '/');
if (slashpos) *slashpos = 0;
n = 0;
if (!UTI_StringToIP(p, &ip) &&
(n = sscanf(p, "%lu.%lu.%lu.%lu", &a, &b, &c, &d)) <= 0) {
/* Try to parse as the name of a machine */
if (slashpos || DNS_Name2IPAddress(p, &ip, 1) != DNS_Success) {
LOG(LOGS_ERR, "Could not read address");
return 0;
} else {
UTI_IPHostToNetwork(&ip, &msg->data.allow_deny.ip);
if (ip.family == IPADDR_INET6)
msg->data.allow_deny.subnet_bits = htonl(128);
else
msg->data.allow_deny.subnet_bits = htonl(32);
}
} else {
if (n == 0) {
if (ip.family == IPADDR_INET6)
msg->data.allow_deny.subnet_bits = htonl(128);
else
msg->data.allow_deny.subnet_bits = htonl(32);
} else {
ip.family = IPADDR_INET4;
a &= 0xff;
b &= 0xff;
c &= 0xff;
d &= 0xff;
switch (n) {
case 1:
ip.addr.in4 = htonl((a<<24));
msg->data.allow_deny.subnet_bits = htonl(8);
break;
case 2:
ip.addr.in4 = htonl((a<<24) | (b<<16));
msg->data.allow_deny.subnet_bits = htonl(16);
break;
case 3:
ip.addr.in4 = htonl((a<<24) | (b<<16) | (c<<8));
msg->data.allow_deny.subnet_bits = htonl(24);
break;
case 4:
ip.addr.in4 = htonl((a<<24) | (b<<16) | (c<<8) | d);
msg->data.allow_deny.subnet_bits = htonl(32);
break;
default:
assert(0);
}
}
UTI_IPHostToNetwork(&ip, &msg->data.allow_deny.ip);
if (slashpos) {
n = sscanf(slashpos+1, "%d", &specified_subnet_bits);
if (n == 1) {
msg->data.allow_deny.subnet_bits = htonl(specified_subnet_bits);
} else {
LOG(LOGS_WARN, "Warning: badly formatted subnet size, using %d",
(int)ntohl(msg->data.allow_deny.subnet_bits));
}
}
}
} }
msg->command = htons(all ? allcmd : cmd);
UTI_IPHostToNetwork(&ip, &msg->data.allow_deny.ip);
msg->data.allow_deny.subnet_bits = htonl(subnet_bits);
return 1; return 1;
} }
/* ================================================== */ /* ================================================== */
static int
process_cmd_allow(CMD_Request *msg, char *line)
{
int status;
msg->command = htons(REQ_ALLOW);
status = parse_allow_deny(msg, line);
return status;
}
/* ================================================== */
static int
process_cmd_allowall(CMD_Request *msg, char *line)
{
int status;
msg->command = htons(REQ_ALLOWALL);
status = parse_allow_deny(msg, line);
return status;
}
/* ================================================== */
static int
process_cmd_deny(CMD_Request *msg, char *line)
{
int status;
msg->command = htons(REQ_DENY);
status = parse_allow_deny(msg, line);
return status;
}
/* ================================================== */
static int
process_cmd_denyall(CMD_Request *msg, char *line)
{
int status;
msg->command = htons(REQ_DENYALL);
status = parse_allow_deny(msg, line);
return status;
}
/* ================================================== */
static int
process_cmd_cmdallow(CMD_Request *msg, char *line)
{
int status;
msg->command = htons(REQ_CMDALLOW);
status = parse_allow_deny(msg, line);
return status;
}
/* ================================================== */
static int
process_cmd_cmdallowall(CMD_Request *msg, char *line)
{
int status;
msg->command = htons(REQ_CMDALLOWALL);
status = parse_allow_deny(msg, line);
return status;
}
/* ================================================== */
static int
process_cmd_cmddeny(CMD_Request *msg, char *line)
{
int status;
msg->command = htons(REQ_CMDDENY);
status = parse_allow_deny(msg, line);
return status;
}
/* ================================================== */
static int
process_cmd_cmddenyall(CMD_Request *msg, char *line)
{
int status;
msg->command = htons(REQ_CMDDENYALL);
status = parse_allow_deny(msg, line);
return status;
}
/* ================================================== */
static int static int
process_cmd_accheck(CMD_Request *msg, char *line) process_cmd_accheck(CMD_Request *msg, char *line)
{ {
@@ -987,35 +833,38 @@ process_cmd_cmdaccheck(CMD_Request *msg, char *line)
/* ================================================== */ /* ================================================== */
static void static int
process_cmd_dfreq(CMD_Request *msg, char *line) process_cmd_dfreq(CMD_Request *msg, char *line)
{ {
double dfreq; double dfreq;
msg->command = htons(REQ_DFREQ); msg->command = htons(REQ_DFREQ);
if (sscanf(line, "%lf", &dfreq) == 1) {
msg->data.dfreq.dfreq = UTI_FloatHostToNetwork(dfreq); if (sscanf(line, "%lf", &dfreq) != 1) {
} else { LOG(LOGS_ERR, "Invalid value");
msg->data.dfreq.dfreq = UTI_FloatHostToNetwork(0.0); return 0;
} }
msg->data.dfreq.dfreq = UTI_FloatHostToNetwork(dfreq);
return 1;
} }
/* ================================================== */ /* ================================================== */
static void static int
process_cmd_doffset(CMD_Request *msg, char *line) process_cmd_doffset(CMD_Request *msg, char *line)
{ {
struct timeval tv;
double doffset; double doffset;
msg->command = htons(REQ_DOFFSET); msg->command = htons(REQ_DOFFSET2);
if (sscanf(line, "%lf", &doffset) == 1) {
UTI_DoubleToTimeval(doffset, &tv); if (sscanf(line, "%lf", &doffset) != 1) {
msg->data.doffset.sec = htonl(tv.tv_sec); LOG(LOGS_ERR, "Invalid value");
msg->data.doffset.usec = htonl(tv.tv_usec); return 0;
} else {
msg->data.doffset.sec = htonl(0);
msg->data.doffset.usec = htonl(0);
} }
msg->data.doffset.doffset = UTI_FloatHostToNetwork(doffset);
return 1;
} }
/* ================================================== */ /* ================================================== */
@@ -1095,11 +944,16 @@ process_cmd_add_source(CMD_Request *msg, char *line)
(data.params.interleaved ? REQ_ADDSRC_INTERLEAVED : 0) | (data.params.interleaved ? REQ_ADDSRC_INTERLEAVED : 0) |
(data.params.burst ? REQ_ADDSRC_BURST : 0) | (data.params.burst ? REQ_ADDSRC_BURST : 0) |
(data.params.nts ? REQ_ADDSRC_NTS : 0) | (data.params.nts ? REQ_ADDSRC_NTS : 0) |
(data.params.copy ? REQ_ADDSRC_COPY : 0) |
(data.params.ext_fields & NTP_EF_FLAG_EXP1 ? REQ_ADDSRC_EF_EXP1 : 0) |
(data.params.sel_options & SRC_SELECT_PREFER ? REQ_ADDSRC_PREFER : 0) | (data.params.sel_options & SRC_SELECT_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) |
(data.params.sel_options & SRC_SELECT_REQUIRE ? REQ_ADDSRC_REQUIRE : 0)); (data.params.sel_options & SRC_SELECT_REQUIRE ? REQ_ADDSRC_REQUIRE : 0));
msg->data.ntp_source.filter_length = htonl(data.params.filter_length); msg->data.ntp_source.filter_length = htonl(data.params.filter_length);
msg->data.ntp_source.cert_set = htonl(data.params.cert_set);
msg->data.ntp_source.max_delay_quant =
UTI_FloatHostToNetwork(data.params.max_delay_quant);
memset(msg->data.ntp_source.reserved, 0, sizeof (msg->data.ntp_source.reserved)); memset(msg->data.ntp_source.reserved, 0, sizeof (msg->data.ntp_source.reserved));
result = 1; result = 1;
@@ -2086,7 +1940,7 @@ process_cmd_sourcename(char *line)
IPAddr ip_addr; IPAddr ip_addr;
char name[256]; char name[256];
if (!UTI_StringToIP(line, &ip_addr)) { if (!parse_source_address(line, &ip_addr)) {
LOG(LOGS_ERR, "Could not read address"); LOG(LOGS_ERR, "Could not read address");
return 0; return 0;
} }
@@ -2547,12 +2401,12 @@ process_cmd_selectdata(char *line)
n_sources = ntohl(reply.data.n_sources.n_sources); n_sources = ntohl(reply.data.n_sources.n_sources);
if (verbose) { if (verbose) {
printf( " .-- State: N - noselect, M - missing samples, d/D - large distance,\n"); printf( " . State: N - noselect, s - unsynchronised, M - missing samples,\n");
printf( " / ~ - jittery, w/W - waits for others, T - not trusted,\n"); printf( " / d/D - large distance, ~ - jittery, w/W - waits for others,\n");
printf( "| x - falseticker, P - not preferred, U - waits for update,\n"); printf( "| S - stale, O - orphan, T - not trusted, P - not preferred,\n");
printf( "| S - stale, O - orphan, + - combined, * - best.\n"); printf( "| U - waits for update,, x - falseticker, + - combined, * - best.\n");
printf( "| Effective options ------. (N - noselect, P - prefer\n"); printf( "| Effective options ---------. (N - noselect, P - prefer\n");
printf( "| Configured options -. \\ T - trust, R - require)\n"); printf( "| Configured options ----. \\ T - trust, R - require)\n");
printf( "| Auth. enabled (Y/N) -. \\ \\ Offset interval --.\n"); printf( "| Auth. enabled (Y/N) -. \\ \\ Offset interval --.\n");
printf( "| | | | |\n"); printf( "| | | | |\n");
} }
@@ -2611,7 +2465,7 @@ process_cmd_serverstats(char *line)
CMD_Reply reply; CMD_Reply reply;
request.command = htons(REQ_SERVER_STATS); request.command = htons(REQ_SERVER_STATS);
if (!request_reply(&request, &reply, RPY_SERVER_STATS2, 0)) if (!request_reply(&request, &reply, RPY_SERVER_STATS3, 0))
return 0; return 0;
print_report("NTP packets received : %U\n" print_report("NTP packets received : %U\n"
@@ -2621,7 +2475,10 @@ process_cmd_serverstats(char *line)
"Client log records dropped : %U\n" "Client log records dropped : %U\n"
"NTS-KE connections accepted: %U\n" "NTS-KE connections accepted: %U\n"
"NTS-KE connections dropped : %U\n" "NTS-KE connections dropped : %U\n"
"Authenticated NTP packets : %U\n", "Authenticated NTP packets : %U\n"
"Interleaved NTP packets : %U\n"
"NTP timestamps held : %U\n"
"NTP timestamp span : %U\n",
(unsigned long)ntohl(reply.data.server_stats.ntp_hits), (unsigned long)ntohl(reply.data.server_stats.ntp_hits),
(unsigned long)ntohl(reply.data.server_stats.ntp_drops), (unsigned long)ntohl(reply.data.server_stats.ntp_drops),
(unsigned long)ntohl(reply.data.server_stats.cmd_hits), (unsigned long)ntohl(reply.data.server_stats.cmd_hits),
@@ -2630,6 +2487,9 @@ process_cmd_serverstats(char *line)
(unsigned long)ntohl(reply.data.server_stats.nke_hits), (unsigned long)ntohl(reply.data.server_stats.nke_hits),
(unsigned long)ntohl(reply.data.server_stats.nke_drops), (unsigned long)ntohl(reply.data.server_stats.nke_drops),
(unsigned long)ntohl(reply.data.server_stats.ntp_auth_hits), (unsigned long)ntohl(reply.data.server_stats.ntp_auth_hits),
(unsigned long)ntohl(reply.data.server_stats.ntp_interleaved_hits),
(unsigned long)ntohl(reply.data.server_stats.ntp_timestamps),
(unsigned long)ntohl(reply.data.server_stats.ntp_span_seconds),
REPORT_END); REPORT_END);
return 1; return 1;
@@ -3227,11 +3087,7 @@ process_line(char *line)
} else if (!strcmp(command, "add")) { } else if (!strcmp(command, "add")) {
do_normal_submit = process_cmd_add_source(&tx_message, line); do_normal_submit = process_cmd_add_source(&tx_message, line);
} else if (!strcmp(command, "allow")) { } else if (!strcmp(command, "allow")) {
if (!strncmp(line, "all", 3)) { do_normal_submit = process_cmd_allowdeny(&tx_message, line, REQ_ALLOW, REQ_ALLOWALL);
do_normal_submit = process_cmd_allowall(&tx_message, CPS_SplitWord(line));
} else {
do_normal_submit = process_cmd_allow(&tx_message, line);
}
} else if (!strcmp(command, "authdata")) { } else if (!strcmp(command, "authdata")) {
do_normal_submit = 0; do_normal_submit = 0;
ret = process_cmd_authdata(line); ret = process_cmd_authdata(line);
@@ -3243,35 +3099,22 @@ process_line(char *line)
} else if (!strcmp(command, "cmdaccheck")) { } else if (!strcmp(command, "cmdaccheck")) {
do_normal_submit = process_cmd_cmdaccheck(&tx_message, line); do_normal_submit = process_cmd_cmdaccheck(&tx_message, line);
} else if (!strcmp(command, "cmdallow")) { } else if (!strcmp(command, "cmdallow")) {
if (!strncmp(line, "all", 3)) { do_normal_submit = process_cmd_allowdeny(&tx_message, line, REQ_CMDALLOW, REQ_CMDALLOWALL);
do_normal_submit = process_cmd_cmdallowall(&tx_message, CPS_SplitWord(line));
} else {
do_normal_submit = process_cmd_cmdallow(&tx_message, line);
}
} else if (!strcmp(command, "cmddeny")) { } else if (!strcmp(command, "cmddeny")) {
if (!strncmp(line, "all", 3)) { do_normal_submit = process_cmd_allowdeny(&tx_message, line, REQ_CMDDENY, REQ_CMDDENYALL);
line = CPS_SplitWord(line);
do_normal_submit = process_cmd_cmddenyall(&tx_message, line);
} else {
do_normal_submit = process_cmd_cmddeny(&tx_message, line);
}
} else if (!strcmp(command, "cyclelogs")) { } else if (!strcmp(command, "cyclelogs")) {
process_cmd_cyclelogs(&tx_message, line); process_cmd_cyclelogs(&tx_message, line);
} else if (!strcmp(command, "delete")) { } else if (!strcmp(command, "delete")) {
do_normal_submit = process_cmd_delete(&tx_message, line); do_normal_submit = process_cmd_delete(&tx_message, line);
} else if (!strcmp(command, "deny")) { } else if (!strcmp(command, "deny")) {
if (!strncmp(line, "all", 3)) { do_normal_submit = process_cmd_allowdeny(&tx_message, line, REQ_DENY, REQ_DENYALL);
do_normal_submit = process_cmd_denyall(&tx_message, CPS_SplitWord(line));
} else {
do_normal_submit = process_cmd_deny(&tx_message, line);
}
} else if (!strcmp(command, "dfreq")) { } else if (!strcmp(command, "dfreq")) {
process_cmd_dfreq(&tx_message, line); do_normal_submit = process_cmd_dfreq(&tx_message, line);
} else if (!strcmp(command, "dns")) { } else if (!strcmp(command, "dns")) {
ret = process_cmd_dns(line); ret = process_cmd_dns(line);
do_normal_submit = 0; do_normal_submit = 0;
} else if (!strcmp(command, "doffset")) { } else if (!strcmp(command, "doffset")) {
process_cmd_doffset(&tx_message, line); do_normal_submit = process_cmd_doffset(&tx_message, line);
} else if (!strcmp(command, "dump")) { } else if (!strcmp(command, "dump")) {
process_cmd_dump(&tx_message, line); process_cmd_dump(&tx_message, line);
} else if (!strcmp(command, "exit")) { } else if (!strcmp(command, "exit")) {
@@ -3397,44 +3240,46 @@ process_line(char *line)
if (do_normal_submit) { if (do_normal_submit) {
ret = request_reply(&tx_message, &rx_message, RPY_NULL, 1); ret = request_reply(&tx_message, &rx_message, RPY_NULL, 1);
} }
fflush(stderr); fflush(stderr);
fflush(stdout);
if (fflush(stdout) != 0 || ferror(stdout) != 0) {
LOG(LOGS_ERR, "Could not write to stdout");
/* Return error for commands that print data */
if (!do_normal_submit)
return 0;
}
return ret; return ret;
} }
/* ================================================== */ /* ================================================== */
#define MAX_LINE_LENGTH 2048
static int static int
process_args(int argc, char **argv, int multi) process_args(int argc, char **argv, int multi)
{ {
int total_length, i, ret = 0; char line[MAX_LINE_LENGTH];
char *line; int i, l, ret = 0;
total_length = 0; for (i = l = 0; i < argc; i++) {
for(i=0; i<argc; i++) { l += snprintf(line + l, sizeof (line) - l, "%s ", argv[i]);
total_length += strlen(argv[i]) + 1; if (l >= sizeof (line)) {
} LOG(LOGS_ERR, "Command too long");
return 0;
line = (char *) Malloc((2 + total_length) * sizeof(char));
for (i = 0; i < argc; i++) {
line[0] = '\0';
if (multi) {
strcat(line, argv[i]);
} else {
for (; i < argc; i++) {
strcat(line, argv[i]);
if (i + 1 < argc)
strcat(line, " ");
}
} }
if (!multi && i + 1 < argc)
continue;
ret = process_line(line); ret = process_line(line);
if (!ret || quit) if (!ret || quit)
break; break;
}
Free(line); l = 0;
}
return ret; return ret;
} }
@@ -3453,7 +3298,7 @@ static void
display_gpl(void) display_gpl(void)
{ {
printf("chrony version %s\n" printf("chrony version %s\n"
"Copyright (C) 1997-2003, 2007, 2009-2020 Richard P. Curnow and others\n" "Copyright (C) 1997-2003, 2007, 2009-2021 Richard P. Curnow and others\n"
"chrony comes with ABSOLUTELY NO WARRANTY. This is free software, and\n" "chrony comes with ABSOLUTELY NO WARRANTY. This is free software, and\n"
"you are welcome to redistribute it under certain conditions. See the\n" "you are welcome to redistribute it under certain conditions. See the\n"
"GNU General Public License version 2 for details.\n\n", "GNU General Public License version 2 for details.\n\n",

View File

@@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009, 2015-2017 * Copyright (C) Miroslav Lichvar 2009, 2015-2017, 2021
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of version 2 of the GNU General Public License as
@@ -38,6 +38,7 @@
#include "array.h" #include "array.h"
#include "clientlog.h" #include "clientlog.h"
#include "conf.h" #include "conf.h"
#include "local.h"
#include "memory.h" #include "memory.h"
#include "ntp.h" #include "ntp.h"
#include "reports.h" #include "reports.h"
@@ -55,8 +56,6 @@ typedef struct {
int8_t rate[MAX_SERVICES]; int8_t rate[MAX_SERVICES];
int8_t ntp_timeout_rate; int8_t ntp_timeout_rate;
uint8_t drop_flags; uint8_t drop_flags;
NTP_int64 ntp_rx_ts;
NTP_int64 ntp_tx_ts;
} Record; } Record;
/* Hash table of records, there is a fixed number of records per slot */ /* Hash table of records, there is a fixed number of records per slot */
@@ -124,10 +123,43 @@ static int limit_interval[MAX_SERVICES];
/* Flag indicating whether facility is turned on or not */ /* Flag indicating whether facility is turned on or not */
static int active; static int active;
/* RX and TX timestamp saved for clients using interleaved mode */
typedef struct {
uint64_t rx_ts;
uint16_t flags;
uint16_t slew_epoch;
int32_t tx_ts_offset;
} NtpTimestamps;
/* Flags for NTP timestamps */
#define NTPTS_DISABLED 1
#define NTPTS_VALID_TX 2
/* RX->TX map using a circular buffer with ordered timestamps */
typedef struct {
ARR_Instance timestamps;
uint32_t first;
uint32_t size;
uint32_t max_size;
uint32_t cached_index;
uint64_t cached_rx_ts;
uint16_t slew_epoch;
double slew_offset;
} NtpTimestampMap;
static NtpTimestampMap ntp_ts_map;
/* Maximum interval of NTP timestamps in future after a backward step */
#define NTPTS_FUTURE_LIMIT (1LL << 32) /* 1 second */
/* Maximum number of timestamps moved in the array to insert a new timestamp */
#define NTPTS_INSERT_LIMIT 64
/* Global statistics */ /* Global statistics */
static uint32_t total_hits[MAX_SERVICES]; static uint32_t total_hits[MAX_SERVICES];
static uint32_t total_drops[MAX_SERVICES]; static uint32_t total_drops[MAX_SERVICES];
static uint32_t total_ntp_auth_hits; static uint32_t total_ntp_auth_hits;
static uint32_t total_ntp_interleaved_hits;
static uint32_t total_record_drops; static uint32_t total_record_drops;
#define NSEC_PER_SEC 1000000000U #define NSEC_PER_SEC 1000000000U
@@ -135,6 +167,8 @@ static uint32_t total_record_drops;
/* ================================================== */ /* ================================================== */
static int expand_hashtable(void); static int expand_hashtable(void);
static void handle_slew(struct timespec *raw, struct timespec *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything);
/* ================================================== */ /* ================================================== */
@@ -229,8 +263,6 @@ get_record(IPAddr *ip)
record->rate[i] = INVALID_RATE; record->rate[i] = INVALID_RATE;
record->ntp_timeout_rate = INVALID_RATE; record->ntp_timeout_rate = INVALID_RATE;
record->drop_flags = 0; record->drop_flags = 0;
UTI_ZeroNtp64(&record->ntp_rx_ts);
UTI_ZeroNtp64(&record->ntp_tx_ts);
return record; return record;
} }
@@ -316,7 +348,7 @@ set_bucket_params(int interval, int burst, uint16_t *max_tokens,
void void
CLG_Initialise(void) CLG_Initialise(void)
{ {
int i, interval, burst, lrate; int i, interval, burst, lrate, slots2;
for (i = 0; i < MAX_SERVICES; i++) { for (i = 0; i < MAX_SERVICES; i++) {
max_tokens[i] = 0; max_tokens[i] = 0;
@@ -359,9 +391,13 @@ CLG_Initialise(void)
/* Calculate the maximum number of slots that can be allocated in the /* Calculate the maximum number of slots that can be allocated in the
configured memory limit. Take into account expanding of the hash configured memory limit. Take into account expanding of the hash
table where two copies exist at the same time. */ table where two copies exist at the same time. */
max_slots = CNF_GetClientLogLimit() / (sizeof (Record) * SLOT_SIZE * 3 / 2); max_slots = CNF_GetClientLogLimit() /
((sizeof (Record) + sizeof (NtpTimestamps)) * SLOT_SIZE * 3 / 2);
max_slots = CLAMP(MIN_SLOTS, max_slots, MAX_SLOTS); max_slots = CLAMP(MIN_SLOTS, max_slots, MAX_SLOTS);
DEBUG_LOG("Max records %u", 1U << ((int)round(log(max_slots) / log(2)) + SLOT_BITS)); for (slots2 = 0; 1U << (slots2 + 1) <= max_slots; slots2++)
;
DEBUG_LOG("Max records %u", 1U << (slots2 + SLOT_BITS));
slots = 0; slots = 0;
records = NULL; records = NULL;
@@ -370,6 +406,17 @@ CLG_Initialise(void)
UTI_GetRandomBytes(&ts_offset, sizeof (ts_offset)); UTI_GetRandomBytes(&ts_offset, sizeof (ts_offset));
ts_offset %= NSEC_PER_SEC / (1U << TS_FRAC); ts_offset %= NSEC_PER_SEC / (1U << TS_FRAC);
ntp_ts_map.timestamps = NULL;
ntp_ts_map.first = 0;
ntp_ts_map.size = 0;
ntp_ts_map.max_size = 1U << (slots2 + SLOT_BITS);
ntp_ts_map.cached_index = 0;
ntp_ts_map.cached_rx_ts = 0ULL;
ntp_ts_map.slew_epoch = 0;
ntp_ts_map.slew_offset = 0.0;
LCL_AddParameterChangeHandler(handle_slew, NULL);
} }
/* ================================================== */ /* ================================================== */
@@ -381,6 +428,10 @@ CLG_Finalise(void)
return; return;
ARR_DestroyInstance(records); ARR_DestroyInstance(records);
if (ntp_ts_map.timestamps)
ARR_DestroyInstance(ntp_ts_map.timestamps);
LCL_RemoveParameterChangeHandler(handle_slew, NULL);
} }
/* ================================================== */ /* ================================================== */
@@ -595,22 +646,334 @@ CLG_LogAuthNtpRequest(void)
/* ================================================== */ /* ================================================== */
void CLG_GetNtpTimestamps(int index, NTP_int64 **rx_ts, NTP_int64 **tx_ts) int
CLG_GetNtpMinPoll(void)
{ {
Record *record; return limit_interval[CLG_NTP];
}
record = ARR_GetElement(records, index); /* ================================================== */
*rx_ts = &record->ntp_rx_ts; static NtpTimestamps *
*tx_ts = &record->ntp_tx_ts; get_ntp_tss(uint32_t index)
{
return ARR_GetElement(ntp_ts_map.timestamps,
(ntp_ts_map.first + index) & (ntp_ts_map.max_size - 1));
}
/* ================================================== */
static int
find_ntp_rx_ts(uint64_t rx_ts, uint32_t *index)
{
uint64_t rx_x, rx_lo, rx_hi, step;
uint32_t i, x, lo, hi;
if (ntp_ts_map.cached_rx_ts == rx_ts && rx_ts != 0ULL) {
*index = ntp_ts_map.cached_index;
return 1;
}
if (ntp_ts_map.size == 0) {
*index = 0;
return 0;
}
lo = 0;
hi = ntp_ts_map.size - 1;
rx_lo = get_ntp_tss(lo)->rx_ts;
rx_hi = get_ntp_tss(hi)->rx_ts;
/* Check for ts < lo before ts > hi to trim timestamps from "future" later
if both conditions are true to not break the order of the endpoints.
Compare timestamps by their difference to allow adjacent NTP eras. */
if ((int64_t)(rx_ts - rx_lo) < 0) {
*index = 0;
return 0;
} else if ((int64_t)(rx_ts - rx_hi) > 0) {
*index = ntp_ts_map.size;
return 0;
}
/* Perform a combined linear interpolation and binary search */
for (i = 0; ; i++) {
if (rx_ts == rx_hi) {
*index = ntp_ts_map.cached_index = hi;
ntp_ts_map.cached_rx_ts = rx_ts;
return 1;
} else if (rx_ts == rx_lo) {
*index = ntp_ts_map.cached_index = lo;
ntp_ts_map.cached_rx_ts = rx_ts;
return 1;
} else if (lo + 1 == hi) {
*index = hi;
return 0;
}
if (hi - lo > 3 && i % 2 == 0) {
step = (rx_hi - rx_lo) / (hi - lo);
if (step == 0)
step = 1;
x = lo + (rx_ts - rx_lo) / step;
} else {
x = lo + (hi - lo) / 2;
}
if (x <= lo)
x = lo + 1;
else if (x >= hi)
x = hi - 1;
rx_x = get_ntp_tss(x)->rx_ts;
if ((int64_t)(rx_x - rx_ts) <= 0) {
lo = x;
rx_lo = rx_x;
} else {
hi = x;
rx_hi = rx_x;
}
}
}
/* ================================================== */
static uint64_t
ntp64_to_int64(NTP_int64 *ts)
{
return (uint64_t)ntohl(ts->hi) << 32 | ntohl(ts->lo);
}
/* ================================================== */
static void
int64_to_ntp64(uint64_t ts, NTP_int64 *ntp_ts)
{
ntp_ts->hi = htonl(ts >> 32);
ntp_ts->lo = htonl(ts);
}
/* ================================================== */
static uint32_t
push_ntp_tss(uint32_t index)
{
if (ntp_ts_map.size < ntp_ts_map.max_size) {
ntp_ts_map.size++;
} else {
ntp_ts_map.first = (ntp_ts_map.first + 1) % (ntp_ts_map.max_size);
if (index > 0)
index--;
}
return index;
}
/* ================================================== */
static void
set_ntp_tx_offset(NtpTimestamps *tss, NTP_int64 *rx_ts, struct timespec *tx_ts)
{
struct timespec ts;
if (!tx_ts) {
tss->flags &= ~NTPTS_VALID_TX;
return;
}
UTI_Ntp64ToTimespec(rx_ts, &ts);
UTI_DiffTimespecs(&ts, tx_ts, &ts);
if (ts.tv_sec < -2 || ts.tv_sec > 1) {
tss->flags &= ~NTPTS_VALID_TX;
return;
}
tss->tx_ts_offset = (int32_t)ts.tv_nsec + (int32_t)ts.tv_sec * (int32_t)NSEC_PER_SEC;
tss->flags |= NTPTS_VALID_TX;
}
/* ================================================== */
static void
get_ntp_tx(NtpTimestamps *tss, struct timespec *tx_ts)
{
int32_t offset = tss->tx_ts_offset;
NTP_int64 ntp_ts;
if (tss->flags & NTPTS_VALID_TX) {
int64_to_ntp64(tss->rx_ts, &ntp_ts);
UTI_Ntp64ToTimespec(&ntp_ts, tx_ts);
if (offset >= (int32_t)NSEC_PER_SEC) {
offset -= NSEC_PER_SEC;
tx_ts->tv_sec++;
}
tx_ts->tv_nsec += offset;
UTI_NormaliseTimespec(tx_ts);
} else {
UTI_ZeroTimespec(tx_ts);
}
}
/* ================================================== */
void
CLG_SaveNtpTimestamps(NTP_int64 *rx_ts, struct timespec *tx_ts)
{
NtpTimestamps *tss;
uint32_t i, index;
uint64_t rx;
if (!active)
return;
/* Allocate the array on first use */
if (!ntp_ts_map.timestamps) {
ntp_ts_map.timestamps = ARR_CreateInstance(sizeof (NtpTimestamps));
ARR_SetSize(ntp_ts_map.timestamps, ntp_ts_map.max_size);
}
rx = ntp64_to_int64(rx_ts);
if (rx == 0ULL)
return;
/* Disable the RX timestamp if it already exists to avoid responding
with a wrong TX timestamp */
if (find_ntp_rx_ts(rx, &index)) {
get_ntp_tss(index)->flags |= NTPTS_DISABLED;
return;
}
assert(index <= ntp_ts_map.size);
if (index == ntp_ts_map.size) {
/* Increase the size or drop the oldest timestamp to make room for
the new timestamp */
index = push_ntp_tss(index);
} else {
/* Trim timestamps in distant future after backward step */
while (index < ntp_ts_map.size &&
get_ntp_tss(ntp_ts_map.size - 1)->rx_ts - rx > NTPTS_FUTURE_LIMIT)
ntp_ts_map.size--;
/* Insert the timestamp if it is close to the latest timestamp.
Otherwise, replace the closest older or the oldest timestamp. */
if (index + NTPTS_INSERT_LIMIT >= ntp_ts_map.size) {
index = push_ntp_tss(index);
for (i = ntp_ts_map.size - 1; i > index; i--)
*get_ntp_tss(i) = *get_ntp_tss(i - 1);
} else {
if (index > 0)
index--;
}
}
ntp_ts_map.cached_index = index;
ntp_ts_map.cached_rx_ts = rx;
tss = get_ntp_tss(index);
tss->rx_ts = rx;
tss->flags = 0;
tss->slew_epoch = ntp_ts_map.slew_epoch;
set_ntp_tx_offset(tss, rx_ts, tx_ts);
DEBUG_LOG("Saved RX+TX index=%"PRIu32" first=%"PRIu32" size=%"PRIu32,
index, ntp_ts_map.first, ntp_ts_map.size);
}
/* ================================================== */
static void
handle_slew(struct timespec *raw, struct timespec *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything)
{
/* Drop all timestamps on unknown step */
if (change_type == LCL_ChangeUnknownStep) {
ntp_ts_map.size = 0;
ntp_ts_map.cached_rx_ts = 0ULL;
}
ntp_ts_map.slew_epoch++;
ntp_ts_map.slew_offset = doffset;
}
/* ================================================== */
void
CLG_UndoNtpTxTimestampSlew(NTP_int64 *rx_ts, struct timespec *tx_ts)
{
uint32_t index;
if (!ntp_ts_map.timestamps)
return;
if (!find_ntp_rx_ts(ntp64_to_int64(rx_ts), &index))
return;
/* If the RX timestamp was captured before the last correction of the clock,
remove the adjustment from the TX timestamp */
if ((uint16_t)(get_ntp_tss(index)->slew_epoch + 1U) == ntp_ts_map.slew_epoch)
UTI_AddDoubleToTimespec(tx_ts, ntp_ts_map.slew_offset, tx_ts);
}
/* ================================================== */
void
CLG_UpdateNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts)
{
uint32_t index;
if (!ntp_ts_map.timestamps)
return;
if (!find_ntp_rx_ts(ntp64_to_int64(rx_ts), &index))
return;
set_ntp_tx_offset(get_ntp_tss(index), rx_ts, tx_ts);
} }
/* ================================================== */ /* ================================================== */
int int
CLG_GetNtpMinPoll(void) CLG_GetNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts)
{ {
return limit_interval[CLG_NTP]; NtpTimestamps *tss;
uint32_t index;
if (!ntp_ts_map.timestamps)
return 0;
if (!find_ntp_rx_ts(ntp64_to_int64(rx_ts), &index))
return 0;
tss = get_ntp_tss(index);
if (tss->flags & NTPTS_DISABLED)
return 0;
get_ntp_tx(tss, tx_ts);
return 1;
}
/* ================================================== */
void
CLG_DisableNtpTimestamps(NTP_int64 *rx_ts)
{
uint32_t index;
if (!ntp_ts_map.timestamps)
return;
if (find_ntp_rx_ts(ntp64_to_int64(rx_ts), &index))
get_ntp_tss(index)->flags |= NTPTS_DISABLED;
/* This assumes the function is called only to prevent multiple
interleaved responses to the same timestamp */
total_ntp_interleaved_hits++;
} }
/* ================================================== */ /* ================================================== */
@@ -717,4 +1080,9 @@ CLG_GetServerStatsReport(RPT_ServerStatsReport *report)
report->cmd_drops = total_drops[CLG_CMDMON]; report->cmd_drops = total_drops[CLG_CMDMON];
report->log_drops = total_record_drops; report->log_drops = total_record_drops;
report->ntp_auth_hits = total_ntp_auth_hits; report->ntp_auth_hits = total_ntp_auth_hits;
report->ntp_interleaved_hits = total_ntp_interleaved_hits;
report->ntp_timestamps = ntp_ts_map.size;
report->ntp_span_seconds = ntp_ts_map.size > 1 ?
(get_ntp_tss(ntp_ts_map.size - 1)->rx_ts -
get_ntp_tss(0)->rx_ts) >> 32 : 0;
} }

View File

@@ -43,9 +43,15 @@ extern int CLG_GetClientIndex(IPAddr *client);
extern int CLG_LogServiceAccess(CLG_Service service, IPAddr *client, struct timespec *now); extern int CLG_LogServiceAccess(CLG_Service service, IPAddr *client, struct timespec *now);
extern int CLG_LimitServiceRate(CLG_Service service, int index); extern int CLG_LimitServiceRate(CLG_Service service, int index);
extern void CLG_LogAuthNtpRequest(void); extern void CLG_LogAuthNtpRequest(void);
extern void CLG_GetNtpTimestamps(int index, NTP_int64 **rx_ts, NTP_int64 **tx_ts);
extern int CLG_GetNtpMinPoll(void); extern int CLG_GetNtpMinPoll(void);
/* Functions to save and retrieve timestamps for server interleaved mode */
extern void CLG_SaveNtpTimestamps(NTP_int64 *rx_ts, struct timespec *tx_ts);
extern void CLG_UndoNtpTxTimestampSlew(NTP_int64 *rx_ts, struct timespec *tx_ts);
extern void CLG_UpdateNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts);
extern int CLG_GetNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts);
extern void CLG_DisableNtpTimestamps(NTP_int64 *rx_ts);
/* And some reporting functions, for use by chronyc. */ /* And some reporting functions, for use by chronyc. */
extern int CLG_GetNumberOfIndices(void); extern int CLG_GetNumberOfIndices(void);

189
cmac_gnutls.c Normal file
View File

@@ -0,0 +1,189 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2021
*
* 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.
*
**********************************************************************
=======================================================================
CMAC using the GnuTLS library
*/
#include "config.h"
#include "sysincl.h"
#include <gnutls/crypto.h>
#include "cmac.h"
#include "hash.h"
#include "logging.h"
#include "memory.h"
struct CMC_Instance_Record {
gnutls_mac_algorithm_t algorithm;
gnutls_hmac_hd_t mac;
};
/* ================================================== */
static int instance_counter = 0;
static int gnutls_initialised = 0;
/* ================================================== */
static void
init_gnutls(void)
{
int r;
if (gnutls_initialised)
return;
r = gnutls_global_init();
if (r < 0)
LOG_FATAL("Could not initialise %s : %s", "gnutls", gnutls_strerror(r));
DEBUG_LOG("Initialised");
gnutls_initialised = 1;
}
/* ================================================== */
static void
deinit_gnutls(void)
{
assert(gnutls_initialised);
gnutls_global_deinit();
gnutls_initialised = 0;
DEBUG_LOG("Deinitialised");
}
/* ================================================== */
static gnutls_mac_algorithm_t
get_mac_algorithm(CMC_Algorithm algorithm)
{
switch (algorithm) {
case CMC_AES128:
return GNUTLS_MAC_AES_CMAC_128;
case CMC_AES256:
return GNUTLS_MAC_AES_CMAC_256;
default:
return GNUTLS_MAC_UNKNOWN;
}
}
/* ================================================== */
int
CMC_GetKeyLength(CMC_Algorithm algorithm)
{
gnutls_mac_algorithm_t malgo = get_mac_algorithm(algorithm);
int len;
if (malgo == GNUTLS_MAC_UNKNOWN)
return 0;
len = gnutls_hmac_get_key_size(malgo);
if (len < 0)
return 0;
return len;
}
/* ================================================== */
CMC_Instance
CMC_CreateInstance(CMC_Algorithm algorithm, const unsigned char *key, int length)
{
gnutls_hmac_hd_t handle;
CMC_Instance inst;
int r;
if (instance_counter == 0)
init_gnutls();
if (length <= 0 || length != CMC_GetKeyLength(algorithm))
goto error;
r = gnutls_hmac_init(&handle, get_mac_algorithm(algorithm), key, length);
if (r < 0) {
DEBUG_LOG("Could not initialise %s : %s", "mac", gnutls_strerror(r));
goto error;
}
inst = MallocNew(struct CMC_Instance_Record);
inst->algorithm = get_mac_algorithm(algorithm);
inst->mac = handle;
instance_counter++;
return inst;
error:
if (instance_counter == 0)
deinit_gnutls();
return NULL;
}
/* ================================================== */
int
CMC_Hash(CMC_Instance inst, const void *in, int in_len, unsigned char *out, int out_len)
{
unsigned char buf[MAX_HASH_LENGTH];
int hash_len;
if (in_len < 0 || out_len < 0)
return 0;
hash_len = gnutls_hmac_get_len(inst->algorithm);
if (out_len > hash_len)
out_len = hash_len;
if (hash_len > sizeof (buf))
return 0;
if (gnutls_hmac(inst->mac, in, in_len) < 0) {
/* Reset the state */
gnutls_hmac_output(inst->mac, buf);
return 0;
}
gnutls_hmac_output(inst->mac, buf);
memcpy(out, buf, out_len);
return out_len;
}
/* ================================================== */
void
CMC_DestroyInstance(CMC_Instance inst)
{
gnutls_hmac_deinit(inst->mac, NULL);
Free(inst);
instance_counter--;
if (instance_counter == 0)
deinit_gnutls();
}

View File

@@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2016, 2018-2020 * Copyright (C) Miroslav Lichvar 2009-2016, 2018-2021
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of version 2 of the GNU General Public License as
@@ -62,6 +62,9 @@ static int sock_fdu;
static int sock_fd4; static int sock_fd4;
static int sock_fd6; static int sock_fd6;
/* Flag indicating the IPv4 socket is bound to an address */
static int bound_sock_fd4;
/* Flag indicating whether this module has been initialised or not */ /* Flag indicating whether this module has been initialised or not */
static int initialised = 0; static int initialised = 0;
@@ -140,6 +143,7 @@ static const char permissions[] = {
PERMIT_AUTH, /* CLIENT_ACCESSES_BY_INDEX3 */ PERMIT_AUTH, /* CLIENT_ACCESSES_BY_INDEX3 */
PERMIT_AUTH, /* SELECT_DATA */ PERMIT_AUTH, /* SELECT_DATA */
PERMIT_AUTH, /* RELOAD_SOURCES */ PERMIT_AUTH, /* RELOAD_SOURCES */
PERMIT_AUTH, /* DOFFSET2 */
}; };
/* ================================================== */ /* ================================================== */
@@ -179,6 +183,9 @@ open_socket(int family)
return INVALID_SOCK_FD; return INVALID_SOCK_FD;
} }
if (family == IPADDR_INET4)
bound_sock_fd4 = local_addr.ip_addr.addr.in4 != INADDR_ANY;
break; break;
case IPADDR_UNSPEC: case IPADDR_UNSPEC:
local_path = CNF_GetBindCommandPath(); local_path = CNF_GetBindCommandPath();
@@ -244,6 +251,8 @@ CAM_Initialise(void)
initialised = 1; initialised = 1;
bound_sock_fd4 = 0;
sock_fdu = INVALID_SOCK_FD; sock_fdu = INVALID_SOCK_FD;
sock_fd4 = open_socket(IPADDR_INET4); sock_fd4 = open_socket(IPADDR_INET4);
sock_fd6 = open_socket(IPADDR_INET6); sock_fd6 = open_socket(IPADDR_INET6);
@@ -310,6 +319,13 @@ transmit_reply(int sock_fd, int request_length, SCK_Message *message)
!SCK_IsLinkLocalIPAddress(&message->remote_addr.ip.ip_addr)) !SCK_IsLinkLocalIPAddress(&message->remote_addr.ip.ip_addr))
message->if_index = INVALID_IF_INDEX; message->if_index = INVALID_IF_INDEX;
#if !defined(HAVE_IN_PKTINFO) && defined(IP_SENDSRCADDR)
/* On FreeBSD a local IPv4 address cannot be specified on bound socket */
if (message->addr_type == SCK_ADDR_IP && message->local_addr.ip.family == IPADDR_INET4 &&
(sock_fd != sock_fd4 || bound_sock_fd4))
message->local_addr.ip.family = IPADDR_UNSPEC;
#endif
if (!SCK_SendMessage(sock_fd, message, 0)) if (!SCK_SendMessage(sock_fd, message, 0))
return; return;
} }
@@ -735,11 +751,14 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
params.filter_length = ntohl(rx_message->data.ntp_source.filter_length); params.filter_length = ntohl(rx_message->data.ntp_source.filter_length);
params.authkey = ntohl(rx_message->data.ntp_source.authkey); params.authkey = ntohl(rx_message->data.ntp_source.authkey);
params.nts_port = ntohl(rx_message->data.ntp_source.nts_port); params.nts_port = ntohl(rx_message->data.ntp_source.nts_port);
params.cert_set = ntohl(rx_message->data.ntp_source.cert_set);
params.max_delay = UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay); params.max_delay = UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay);
params.max_delay_ratio = params.max_delay_ratio =
UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_ratio); UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_ratio);
params.max_delay_dev_ratio = params.max_delay_dev_ratio =
UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_dev_ratio); UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_dev_ratio);
params.max_delay_quant =
UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_quant);
params.min_delay = UTI_FloatNetworkToHost(rx_message->data.ntp_source.min_delay); params.min_delay = UTI_FloatNetworkToHost(rx_message->data.ntp_source.min_delay);
params.asymmetry = UTI_FloatNetworkToHost(rx_message->data.ntp_source.asymmetry); params.asymmetry = UTI_FloatNetworkToHost(rx_message->data.ntp_source.asymmetry);
params.offset = UTI_FloatNetworkToHost(rx_message->data.ntp_source.offset); params.offset = UTI_FloatNetworkToHost(rx_message->data.ntp_source.offset);
@@ -751,6 +770,9 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
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.burst = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_BURST ? 1 : 0;
params.nts = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_NTS ? 1 : 0; params.nts = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_NTS ? 1 : 0;
params.copy = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_COPY ? 1 : 0;
params.ext_fields =
ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_EF_EXP1 ? NTP_EF_FLAG_EXP1 : 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) |
@@ -841,13 +863,14 @@ handle_dfreq(CMD_Request *rx_message, CMD_Reply *tx_message)
static void static void
handle_doffset(CMD_Request *rx_message, CMD_Reply *tx_message) handle_doffset(CMD_Request *rx_message, CMD_Reply *tx_message)
{ {
long sec, usec;
double doffset; double doffset;
sec = (int32_t)ntohl(rx_message->data.doffset.sec);
usec = (int32_t)ntohl(rx_message->data.doffset.usec); doffset = UTI_FloatNetworkToHost(rx_message->data.doffset.doffset);
doffset = (double) sec + 1.0e-6 * (double) usec; if (!LCL_AccumulateOffset(doffset, 0.0)) {
LOG(LOGS_INFO, "Accumulated delta offset of %.6f seconds", doffset); tx_message->status = htons(STT_FAILED);
LCL_AccumulateOffset(doffset, 0.0); } else {
LOG(LOGS_INFO, "Accumulated delta offset of %.6f seconds", doffset);
}
} }
/* ================================================== */ /* ================================================== */
@@ -1146,7 +1169,7 @@ handle_server_stats(CMD_Request *rx_message, CMD_Reply *tx_message)
RPT_ServerStatsReport report; RPT_ServerStatsReport report;
CLG_GetServerStatsReport(&report); CLG_GetServerStatsReport(&report);
tx_message->reply = htons(RPY_SERVER_STATS2); tx_message->reply = htons(RPY_SERVER_STATS3);
tx_message->data.server_stats.ntp_hits = htonl(report.ntp_hits); tx_message->data.server_stats.ntp_hits = htonl(report.ntp_hits);
tx_message->data.server_stats.nke_hits = htonl(report.nke_hits); tx_message->data.server_stats.nke_hits = htonl(report.nke_hits);
tx_message->data.server_stats.cmd_hits = htonl(report.cmd_hits); tx_message->data.server_stats.cmd_hits = htonl(report.cmd_hits);
@@ -1155,6 +1178,9 @@ handle_server_stats(CMD_Request *rx_message, CMD_Reply *tx_message)
tx_message->data.server_stats.cmd_drops = htonl(report.cmd_drops); tx_message->data.server_stats.cmd_drops = htonl(report.cmd_drops);
tx_message->data.server_stats.log_drops = htonl(report.log_drops); tx_message->data.server_stats.log_drops = htonl(report.log_drops);
tx_message->data.server_stats.ntp_auth_hits = htonl(report.ntp_auth_hits); tx_message->data.server_stats.ntp_auth_hits = htonl(report.ntp_auth_hits);
tx_message->data.server_stats.ntp_interleaved_hits = htonl(report.ntp_interleaved_hits);
tx_message->data.server_stats.ntp_timestamps = htonl(report.ntp_timestamps);
tx_message->data.server_stats.ntp_span_seconds = htonl(report.ntp_span_seconds);
} }
/* ================================================== */ /* ================================================== */
@@ -1622,7 +1648,7 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
handle_dfreq(&rx_message, &tx_message); handle_dfreq(&rx_message, &tx_message);
break; break;
case REQ_DOFFSET: case REQ_DOFFSET2:
handle_doffset(&rx_message, &tx_message); handle_doffset(&rx_message, &tx_message);
break; break;

View File

@@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2013-2014, 2016 * Copyright (C) Miroslav Lichvar 2013-2014, 2016, 2021
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of version 2 of the GNU General Public License as
@@ -43,6 +43,7 @@ int
CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src) CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
{ {
char *hostname, *cmd; char *hostname, *cmd;
uint32_t ef_type;
int n; int n;
src->port = SRC_DEFAULT_PORT; src->port = SRC_DEFAULT_PORT;
@@ -64,10 +65,14 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
src->params.sel_options = 0; src->params.sel_options = 0;
src->params.nts = 0; src->params.nts = 0;
src->params.nts_port = SRC_DEFAULT_NTSPORT; src->params.nts_port = SRC_DEFAULT_NTSPORT;
src->params.copy = 0;
src->params.ext_fields = 0;
src->params.authkey = INACTIVE_AUTHKEY; src->params.authkey = INACTIVE_AUTHKEY;
src->params.cert_set = SRC_DEFAULT_CERTSET;
src->params.max_delay = SRC_DEFAULT_MAXDELAY; src->params.max_delay = SRC_DEFAULT_MAXDELAY;
src->params.max_delay_ratio = SRC_DEFAULT_MAXDELAYRATIO; src->params.max_delay_ratio = SRC_DEFAULT_MAXDELAYRATIO;
src->params.max_delay_dev_ratio = SRC_DEFAULT_MAXDELAYDEVRATIO; src->params.max_delay_dev_ratio = SRC_DEFAULT_MAXDELAYDEVRATIO;
src->params.max_delay_quant = 0.0;
src->params.min_delay = 0.0; src->params.min_delay = 0.0;
src->params.asymmetry = SRC_DEFAULT_ASYMMETRY; src->params.asymmetry = SRC_DEFAULT_ASYMMETRY;
src->params.offset = 0.0; src->params.offset = 0.0;
@@ -90,6 +95,8 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
src->params.auto_offline = 1; src->params.auto_offline = 1;
} else if (!strcasecmp(cmd, "burst")) { } else if (!strcasecmp(cmd, "burst")) {
src->params.burst = 1; src->params.burst = 1;
} else if (!strcasecmp(cmd, "copy")) {
src->params.copy = 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")) {
@@ -102,6 +109,9 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
src->params.sel_options |= SRC_SELECT_REQUIRE; src->params.sel_options |= SRC_SELECT_REQUIRE;
} else if (!strcasecmp(cmd, "trust")) { } else if (!strcasecmp(cmd, "trust")) {
src->params.sel_options |= SRC_SELECT_TRUST; src->params.sel_options |= SRC_SELECT_TRUST;
} else if (!strcasecmp(cmd, "certset")) {
if (sscanf(line, "%"SCNu32"%n", &src->params.cert_set, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "key")) { } else if (!strcasecmp(cmd, "key")) {
if (sscanf(line, "%"SCNu32"%n", &src->params.authkey, &n) != 1 || if (sscanf(line, "%"SCNu32"%n", &src->params.authkey, &n) != 1 ||
src->params.authkey == INACTIVE_AUTHKEY) src->params.authkey == INACTIVE_AUTHKEY)
@@ -109,6 +119,16 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
} else if (!strcasecmp(cmd, "asymmetry")) { } else if (!strcasecmp(cmd, "asymmetry")) {
if (sscanf(line, "%lf%n", &src->params.asymmetry, &n) != 1) if (sscanf(line, "%lf%n", &src->params.asymmetry, &n) != 1)
return 0; return 0;
} else if (!strcasecmp(cmd, "extfield")) {
if (sscanf(line, "%"SCNx32"%n", &ef_type, &n) != 1)
return 0;
switch (ef_type) {
case NTP_EF_EXP1:
src->params.ext_fields |= NTP_EF_FLAG_EXP1;
break;
default:
return 0;
}
} else if (!strcasecmp(cmd, "filter")) { } else if (!strcasecmp(cmd, "filter")) {
if (sscanf(line, "%d%n", &src->params.filter_length, &n) != 1) if (sscanf(line, "%d%n", &src->params.filter_length, &n) != 1)
return 0; return 0;
@@ -121,6 +141,9 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
} else if (!strcasecmp(cmd, "maxdelaydevratio")) { } else if (!strcasecmp(cmd, "maxdelaydevratio")) {
if (sscanf(line, "%lf%n", &src->params.max_delay_dev_ratio, &n) != 1) if (sscanf(line, "%lf%n", &src->params.max_delay_dev_ratio, &n) != 1)
return 0; return 0;
} else if (!strcasecmp(cmd, "maxdelayquant")) {
if (sscanf(line, "%lf%n", &src->params.max_delay_quant, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "maxpoll")) { } else if (!strcasecmp(cmd, "maxpoll")) {
if (sscanf(line, "%d%n", &src->params.maxpoll, &n) != 1) if (sscanf(line, "%d%n", &src->params.maxpoll, &n) != 1)
return 0; return 0;
@@ -174,6 +197,85 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
/* ================================================== */ /* ================================================== */
int
CPS_ParseAllowDeny(char *line, int *all, IPAddr *ip, int *subnet_bits)
{
char *p, *net, *slash;
uint32_t a, b, c;
int bits, len, n;
p = CPS_SplitWord(line);
if (strcmp(line, "all") == 0) {
*all = 1;
net = p;
p = CPS_SplitWord(p);
} else {
*all = 0;
net = line;
}
/* Make sure there are no other arguments */
if (*p)
return 0;
/* No specified address or network means all IPv4 and IPv6 addresses */
if (!*net) {
ip->family = IPADDR_UNSPEC;
*subnet_bits = 0;
return 1;
}
slash = strchr(net, '/');
if (slash) {
if (sscanf(slash + 1, "%d%n", &bits, &len) != 1 || slash[len + 1] || bits < 0)
return 0;
*slash = '\0';
} else {
bits = -1;
}
if (UTI_StringToIP(net, ip)) {
if (bits >= 0)
*subnet_bits = bits;
else
*subnet_bits = ip->family == IPADDR_INET6 ? 128 : 32;
return 1;
}
/* Check for a shortened IPv4 network notation using only 1, 2, or 3 decimal
numbers. This is different than the numbers-and-dots notation accepted
by inet_aton()! */
a = b = c = 0;
n = sscanf(net, "%"PRIu32"%n.%"PRIu32"%n.%"PRIu32"%n", &a, &len, &b, &len, &c, &len);
if (n > 0 && !net[len]) {
if (a > 255 || b > 255 || c > 255)
return 0;
ip->family = IPADDR_INET4;
ip->addr.in4 = (a << 24) | (b << 16) | (c << 8);
if (bits >= 0)
*subnet_bits = bits;
else
*subnet_bits = n * 8;
return 1;
}
/* The last possibility is a hostname */
if (bits < 0 && DNS_Name2IPAddress(net, ip, 1) == DNS_Success) {
*subnet_bits = ip->family == IPADDR_INET6 ? 128 : 32;
return 1;
}
return 0;
}
/* ================================================== */
int int
CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance) CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance)
{ {

View File

@@ -39,6 +39,9 @@ typedef struct {
/* Parse a command to add an NTP server or peer */ /* Parse a command to add an NTP server or peer */
extern int CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src); extern int CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src);
/* Parse a command to allow/deny access */
extern int CPS_ParseAllowDeny(char *line, int *all, IPAddr *ip, int *subnet_bits);
/* Parse a command to enable local reference */ /* Parse a command to enable local reference */
extern int CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance); extern int CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance);

253
conf.c
View File

@@ -76,6 +76,8 @@ static void parse_log(char *);
static void parse_mailonchange(char *); static void parse_mailonchange(char *);
static void parse_makestep(char *); static void parse_makestep(char *);
static void parse_maxchange(char *); static void parse_maxchange(char *);
static void parse_ntsserver(char *, ARR_Instance files);
static void parse_ntstrustedcerts(char *);
static void parse_ratelimit(char *line, int *enabled, int *interval, static void parse_ratelimit(char *line, int *enabled, int *interval,
int *burst, int *leak); int *burst, int *leak);
static void parse_refclock(char *); static void parse_refclock(char *);
@@ -113,6 +115,7 @@ static int cmd_port = DEFAULT_CANDM_PORT;
static int raw_measurements = 0; static int raw_measurements = 0;
static int do_log_measurements = 0; static int do_log_measurements = 0;
static int do_log_selection = 0;
static int do_log_statistics = 0; static int do_log_statistics = 0;
static int do_log_tracking = 0; static int do_log_tracking = 0;
static int do_log_rtc = 0; static int do_log_rtc = 0;
@@ -252,14 +255,15 @@ static char *user;
/* NTS server and client configuration */ /* NTS server and client configuration */
static char *nts_dump_dir = NULL; static char *nts_dump_dir = NULL;
static char *nts_ntp_server = NULL; static char *nts_ntp_server = NULL;
static char *nts_server_cert_file = NULL; static ARR_Instance nts_server_cert_files; /* array of (char *) */
static char *nts_server_key_file = NULL; static ARR_Instance nts_server_key_files; /* array of (char *) */
static int nts_server_port = NKE_PORT; static int nts_server_port = NKE_PORT;
static int nts_server_processes = 1; static int nts_server_processes = 1;
static int nts_server_connections = 100; static int nts_server_connections = 100;
static int nts_refresh = 2419200; /* 4 weeks */ static int nts_refresh = 2419200; /* 4 weeks */
static int nts_rotate = 604800; /* 1 week */ static int nts_rotate = 604800; /* 1 week */
static char *nts_trusted_cert_file = NULL; static ARR_Instance nts_trusted_certs_paths; /* array of (char *) */
static ARR_Instance nts_trusted_certs_ids; /* array of uint32_t */
/* Number of clock updates needed to enable certificate time checks */ /* Number of clock updates needed to enable certificate time checks */
static int no_cert_time_check = 0; static int no_cert_time_check = 0;
@@ -270,6 +274,9 @@ static int no_system_cert = 0;
/* Array of CNF_HwTsInterface */ /* Array of CNF_HwTsInterface */
static ARR_Instance hwts_interfaces; static ARR_Instance hwts_interfaces;
/* PTP event port (disabled by default) */
static int ptp_port = 0;
typedef struct { typedef struct {
NTP_Source_Type type; NTP_Source_Type type;
int pool; int pool;
@@ -388,6 +395,11 @@ CNF_Initialise(int r, int client_only)
ntp_restrictions = ARR_CreateInstance(sizeof (AllowDeny)); ntp_restrictions = ARR_CreateInstance(sizeof (AllowDeny));
cmd_restrictions = ARR_CreateInstance(sizeof (AllowDeny)); cmd_restrictions = ARR_CreateInstance(sizeof (AllowDeny));
nts_server_cert_files = ARR_CreateInstance(sizeof (char *));
nts_server_key_files = ARR_CreateInstance(sizeof (char *));
nts_trusted_certs_paths = ARR_CreateInstance(sizeof (char *));
nts_trusted_certs_ids = ARR_CreateInstance(sizeof (uint32_t));
rtc_device = Strdup(DEFAULT_RTC_DEVICE); rtc_device = Strdup(DEFAULT_RTC_DEVICE);
hwclock_file = Strdup(DEFAULT_HWCLOCK_FILE); hwclock_file = Strdup(DEFAULT_HWCLOCK_FILE);
user = Strdup(DEFAULT_USER); user = Strdup(DEFAULT_USER);
@@ -426,6 +438,12 @@ CNF_Finalise(void)
Free(((RefclockParameters *)ARR_GetElement(refclock_sources, i))->driver_name); Free(((RefclockParameters *)ARR_GetElement(refclock_sources, i))->driver_name);
Free(((RefclockParameters *)ARR_GetElement(refclock_sources, i))->driver_parameter); Free(((RefclockParameters *)ARR_GetElement(refclock_sources, i))->driver_parameter);
} }
for (i = 0; i < ARR_GetSize(nts_server_cert_files); i++)
Free(*(char **)ARR_GetElement(nts_server_cert_files, i));
for (i = 0; i < ARR_GetSize(nts_server_key_files); i++)
Free(*(char **)ARR_GetElement(nts_server_key_files, i));
for (i = 0; i < ARR_GetSize(nts_trusted_certs_paths); i++)
Free(*(char **)ARR_GetElement(nts_trusted_certs_paths, i));
ARR_DestroyInstance(init_sources); ARR_DestroyInstance(init_sources);
ARR_DestroyInstance(ntp_sources); ARR_DestroyInstance(ntp_sources);
@@ -437,6 +455,11 @@ CNF_Finalise(void)
ARR_DestroyInstance(ntp_restrictions); ARR_DestroyInstance(ntp_restrictions);
ARR_DestroyInstance(cmd_restrictions); ARR_DestroyInstance(cmd_restrictions);
ARR_DestroyInstance(nts_server_cert_files);
ARR_DestroyInstance(nts_server_key_files);
ARR_DestroyInstance(nts_trusted_certs_paths);
ARR_DestroyInstance(nts_trusted_certs_ids);
Free(drift_file); Free(drift_file);
Free(dumpdir); Free(dumpdir);
Free(hwclock_file); Free(hwclock_file);
@@ -457,9 +480,6 @@ CNF_Finalise(void)
Free(tempcomp_point_file); Free(tempcomp_point_file);
Free(nts_dump_dir); Free(nts_dump_dir);
Free(nts_ntp_server); Free(nts_ntp_server);
Free(nts_server_cert_file);
Free(nts_server_key_file);
Free(nts_trusted_cert_file);
} }
/* ================================================== */ /* ================================================== */
@@ -643,8 +663,6 @@ CNF_ParseLine(const char *filename, int number, char *line)
} else if (!strcasecmp(command, "ntsratelimit")) { } else if (!strcasecmp(command, "ntsratelimit")) {
parse_ratelimit(p, &nts_ratelimit_enabled, &nts_ratelimit_interval, parse_ratelimit(p, &nts_ratelimit_enabled, &nts_ratelimit_interval,
&nts_ratelimit_burst, &nts_ratelimit_leak); &nts_ratelimit_burst, &nts_ratelimit_leak);
} else if (!strcasecmp(command, "ntstrustedcerts")) {
parse_string(p, &nts_trusted_cert_file);
} else if (!strcasecmp(command, "ntscachedir") || } else if (!strcasecmp(command, "ntscachedir") ||
!strcasecmp(command, "ntsdumpdir")) { !strcasecmp(command, "ntsdumpdir")) {
parse_string(p, &nts_dump_dir); parse_string(p, &nts_dump_dir);
@@ -659,9 +677,11 @@ CNF_ParseLine(const char *filename, int number, char *line)
} else if (!strcasecmp(command, "ntsrotate")) { } else if (!strcasecmp(command, "ntsrotate")) {
parse_int(p, &nts_rotate); parse_int(p, &nts_rotate);
} else if (!strcasecmp(command, "ntsservercert")) { } else if (!strcasecmp(command, "ntsservercert")) {
parse_string(p, &nts_server_cert_file); parse_ntsserver(p, nts_server_cert_files);
} else if (!strcasecmp(command, "ntsserverkey")) { } else if (!strcasecmp(command, "ntsserverkey")) {
parse_string(p, &nts_server_key_file); parse_ntsserver(p, nts_server_key_files);
} else if (!strcasecmp(command, "ntstrustedcerts")) {
parse_ntstrustedcerts(p);
} else if (!strcasecmp(command, "peer")) { } else if (!strcasecmp(command, "peer")) {
parse_source(p, command, 1); parse_source(p, command, 1);
} else if (!strcasecmp(command, "pidfile")) { } else if (!strcasecmp(command, "pidfile")) {
@@ -670,6 +690,8 @@ CNF_ParseLine(const char *filename, int number, char *line)
parse_source(p, command, 1); parse_source(p, command, 1);
} else if (!strcasecmp(command, "port")) { } else if (!strcasecmp(command, "port")) {
parse_int(p, &ntp_port); parse_int(p, &ntp_port);
} else if (!strcasecmp(command, "ptpport")) {
parse_int(p, &ptp_port);
} else if (!strcasecmp(command, "ratelimit")) { } else if (!strcasecmp(command, "ratelimit")) {
parse_ratelimit(p, &ntp_ratelimit_enabled, &ntp_ratelimit_interval, parse_ratelimit(p, &ntp_ratelimit_enabled, &ntp_ratelimit_interval,
&ntp_ratelimit_burst, &ntp_ratelimit_leak); &ntp_ratelimit_burst, &ntp_ratelimit_leak);
@@ -840,7 +862,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, stratum, tai; int local, 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;
@@ -850,6 +872,7 @@ parse_refclock(char *line)
poll = 4; poll = 4;
dpoll = 0; dpoll = 0;
filter_length = 64; filter_length = 64;
local = 0;
pps_forced = 0; pps_forced = 0;
pps_rate = 0; pps_rate = 0;
min_samples = SRC_DEFAULT_MINSAMPLES; min_samples = SRC_DEFAULT_MINSAMPLES;
@@ -908,6 +931,9 @@ parse_refclock(char *line)
if (sscanf(line, "%d%n", &filter_length, &n) != 1) { if (sscanf(line, "%d%n", &filter_length, &n) != 1) {
break; break;
} }
} else if (!strcasecmp(cmd, "local")) {
n = 0;
local = 1;
} else if (!strcasecmp(cmd, "rate")) { } else if (!strcasecmp(cmd, "rate")) {
if (sscanf(line, "%d%n", &pps_rate, &n) != 1) if (sscanf(line, "%d%n", &pps_rate, &n) != 1)
break; break;
@@ -974,6 +1000,7 @@ parse_refclock(char *line)
refclock->driver_poll = dpoll; refclock->driver_poll = dpoll;
refclock->poll = poll; refclock->poll = poll;
refclock->filter_length = filter_length; refclock->filter_length = filter_length;
refclock->local = local;
refclock->pps_forced = pps_forced; refclock->pps_forced = pps_forced;
refclock->pps_rate = pps_rate; refclock->pps_rate = pps_rate;
refclock->min_samples = min_samples; refclock->min_samples = min_samples;
@@ -1006,6 +1033,8 @@ parse_log(char *line)
raw_measurements = 1; raw_measurements = 1;
} else if (!strcmp(log_name, "measurements")) { } else if (!strcmp(log_name, "measurements")) {
do_log_measurements = 1; do_log_measurements = 1;
} else if (!strcmp(log_name, "selection")) {
do_log_selection = 1;
} else if (!strcmp(log_name, "statistics")) { } else if (!strcmp(log_name, "statistics")) {
do_log_statistics = 1; do_log_statistics = 1;
} else if (!strcmp(log_name, "tracking")) { } else if (!strcmp(log_name, "tracking")) {
@@ -1158,103 +1187,56 @@ parse_mailonchange(char *line)
/* ================================================== */ /* ================================================== */
static void
parse_ntsserver(char *line, ARR_Instance files)
{
char *file = NULL;
parse_string(line, &file);
ARR_AppendElement(files, &file);
}
/* ================================================== */
static void
parse_ntstrustedcerts(char *line)
{
uint32_t id;
char *path;
if (get_number_of_args(line) == 2) {
path = CPS_SplitWord(line);
if (sscanf(line, "%"SCNu32, &id) != 1)
command_parse_error();
} else {
check_number_of_args(line, 1);
path = line;
id = 0;
}
path = Strdup(path);
ARR_AppendElement(nts_trusted_certs_paths, &path);
ARR_AppendElement(nts_trusted_certs_ids, &id);
}
/* ================================================== */
static void static void
parse_allow_deny(char *line, ARR_Instance restrictions, int allow) parse_allow_deny(char *line, ARR_Instance restrictions, int allow)
{ {
char *p; int all, subnet_bits;
unsigned long a, b, c, d, n; AllowDeny *node;
int all = 0; IPAddr ip;
AllowDeny *new_node = NULL;
IPAddr ip_addr;
p = line; if (!CPS_ParseAllowDeny(line, &all, &ip, &subnet_bits))
command_parse_error();
if (!strncmp(p, "all", 3)) { node = ARR_GetNewElement(restrictions);
all = 1; node->allow = allow;
p = CPS_SplitWord(line); node->all = all;
} node->ip = ip;
node->subnet_bits = subnet_bits;
if (!*p) {
/* Empty line applies to all addresses */
new_node = (AllowDeny *)ARR_GetNewElement(restrictions);
new_node->allow = allow;
new_node->all = all;
new_node->ip.family = IPADDR_UNSPEC;
new_node->subnet_bits = 0;
} else {
char *slashpos;
slashpos = strchr(p, '/');
if (slashpos) *slashpos = 0;
check_number_of_args(p, 1);
n = 0;
if (UTI_StringToIP(p, &ip_addr) ||
(n = sscanf(p, "%lu.%lu.%lu.%lu", &a, &b, &c, &d)) >= 1) {
new_node = (AllowDeny *)ARR_GetNewElement(restrictions);
new_node->allow = allow;
new_node->all = all;
if (n == 0) {
new_node->ip = ip_addr;
if (ip_addr.family == IPADDR_INET6)
new_node->subnet_bits = 128;
else
new_node->subnet_bits = 32;
} else {
new_node->ip.family = IPADDR_INET4;
a &= 0xff;
b &= 0xff;
c &= 0xff;
d &= 0xff;
switch (n) {
case 1:
new_node->ip.addr.in4 = (a<<24);
new_node->subnet_bits = 8;
break;
case 2:
new_node->ip.addr.in4 = (a<<24) | (b<<16);
new_node->subnet_bits = 16;
break;
case 3:
new_node->ip.addr.in4 = (a<<24) | (b<<16) | (c<<8);
new_node->subnet_bits = 24;
break;
case 4:
new_node->ip.addr.in4 = (a<<24) | (b<<16) | (c<<8) | d;
new_node->subnet_bits = 32;
break;
default:
assert(0);
}
}
if (slashpos) {
int specified_subnet_bits, n;
n = sscanf(slashpos+1, "%d", &specified_subnet_bits);
if (n == 1) {
new_node->subnet_bits = specified_subnet_bits;
} else {
command_parse_error();
}
}
} else {
if (!slashpos && DNS_Name2IPAddress(p, &ip_addr, 1) == DNS_Success) {
new_node = (AllowDeny *)ARR_GetNewElement(restrictions);
new_node->allow = allow;
new_node->all = all;
new_node->ip = ip_addr;
if (ip_addr.family == IPADDR_INET6)
new_node->subnet_bits = 128;
else
new_node->subnet_bits = 32;
} else {
command_parse_error();
}
}
}
} }
/* ================================================== */ /* ================================================== */
@@ -1505,6 +1487,8 @@ parse_hwtimestamp(char *line)
iface->rxfilter = CNF_HWTS_RXFILTER_NONE; iface->rxfilter = CNF_HWTS_RXFILTER_NONE;
else if (!strcasecmp(filter, "ntp")) else if (!strcasecmp(filter, "ntp"))
iface->rxfilter = CNF_HWTS_RXFILTER_NTP; iface->rxfilter = CNF_HWTS_RXFILTER_NTP;
else if (!strcasecmp(filter, "ptp"))
iface->rxfilter = CNF_HWTS_RXFILTER_PTP;
else if (!strcasecmp(filter, "all")) else if (!strcasecmp(filter, "all"))
iface->rxfilter = CNF_HWTS_RXFILTER_ALL; iface->rxfilter = CNF_HWTS_RXFILTER_ALL;
else else
@@ -1643,8 +1627,9 @@ load_source_file(const char *filename)
return; return;
while (fgets(line, sizeof (line), f)) { while (fgets(line, sizeof (line), f)) {
if (strlen(line) >= MAX_LINE_LENGTH) /* Require lines to be terminated */
continue; if (line[0] == '\0' || line[strlen(line) - 1] != '\n')
break;
CPS_NormalizeLine(line); CPS_NormalizeLine(line);
if (line[0] == '\0') if (line[0] == '\0')
@@ -1741,6 +1726,8 @@ reload_source_dirs(void)
if (s == NSR_UnresolvedName) { if (s == NSR_UnresolvedName) {
unresolved++; unresolved++;
} else if (s != NSR_Success) { } else if (s != NSR_Success) {
LOG(LOGS_ERR, "Could not add source %s", source->params.name);
/* Mark the source as not present */ /* Mark the source as not present */
source->params.name[0] = '\0'; source->params.name[0] = '\0';
} }
@@ -1812,7 +1799,8 @@ CNF_AddInitSources(void)
ntp_addr.port = cps_source.port; ntp_addr.port = cps_source.port;
cps_source.params.iburst = 1; cps_source.params.iburst = 1;
NSR_AddSource(&ntp_addr, NTP_SERVER, &cps_source.params, NULL); if (NSR_AddSource(&ntp_addr, NTP_SERVER, &cps_source.params, NULL) != NSR_Success)
LOG(LOGS_ERR, "Could not add source %s", UTI_IPToString(&ntp_addr.ip_addr));
} }
ARR_SetSize(init_sources, 0); ARR_SetSize(init_sources, 0);
@@ -1825,11 +1813,16 @@ CNF_AddSources(void)
{ {
NTP_Source *source; NTP_Source *source;
unsigned int i; unsigned int i;
NSR_Status s;
for (i = 0; i < ARR_GetSize(ntp_sources); i++) { for (i = 0; i < ARR_GetSize(ntp_sources); i++) {
source = (NTP_Source *)ARR_GetElement(ntp_sources, i); source = (NTP_Source *)ARR_GetElement(ntp_sources, i);
NSR_AddSourceByName(source->params.name, source->params.port,
source->pool, source->type, &source->params.params, NULL); s = NSR_AddSourceByName(source->params.name, source->params.port, source->pool,
source->type, &source->params.params, NULL);
if (s != NSR_Success && s != NSR_UnresolvedName)
LOG(LOGS_ERR, "Could not add source %s", source->params.name);
Free(source->params.name); Free(source->params.name);
} }
@@ -1939,6 +1932,14 @@ CNF_GetLogMeasurements(int *raw)
/* ================================================== */ /* ================================================== */
int
CNF_GetLogSelection(void)
{
return do_log_selection;
}
/* ================================================== */
int int
CNF_GetLogStatistics(void) CNF_GetLogStatistics(void)
{ {
@@ -2499,6 +2500,14 @@ CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface)
/* ================================================== */ /* ================================================== */
int
CNF_GetPtpPort(void)
{
return ptp_port;
}
/* ================================================== */
char * char *
CNF_GetNtsDumpDir(void) CNF_GetNtsDumpDir(void)
{ {
@@ -2515,18 +2524,16 @@ CNF_GetNtsNtpServer(void)
/* ================================================== */ /* ================================================== */
char * int
CNF_GetNtsServerCertFile(void) CNF_GetNtsServerCertAndKeyFiles(const char ***certs, const char ***keys)
{ {
return nts_server_cert_file; *certs = ARR_GetElements(nts_server_cert_files);
} *keys = ARR_GetElements(nts_server_key_files);
/* ================================================== */ if (ARR_GetSize(nts_server_cert_files) != ARR_GetSize(nts_server_key_files))
LOG_FATAL("Uneven number of NTS certs and keys");
char * return ARR_GetSize(nts_server_cert_files);
CNF_GetNtsServerKeyFile(void)
{
return nts_server_key_file;
} }
/* ================================================== */ /* ================================================== */
@@ -2571,10 +2578,16 @@ CNF_GetNtsRotate(void)
/* ================================================== */ /* ================================================== */
char * int
CNF_GetNtsTrustedCertFile(void) CNF_GetNtsTrustedCertsPaths(const char ***paths, uint32_t **ids)
{ {
return nts_trusted_cert_file; *paths = ARR_GetElements(nts_trusted_certs_paths);
*ids = ARR_GetElements(nts_trusted_certs_ids);
if (ARR_GetSize(nts_trusted_certs_paths) != ARR_GetSize(nts_trusted_certs_ids))
assert(0);
return ARR_GetSize(nts_trusted_certs_paths);
} }
/* ================================================== */ /* ================================================== */

9
conf.h
View File

@@ -58,6 +58,7 @@ extern char *CNF_GetLogDir(void);
extern char *CNF_GetDumpDir(void); extern char *CNF_GetDumpDir(void);
extern int CNF_GetLogBanner(void); extern int CNF_GetLogBanner(void);
extern int CNF_GetLogMeasurements(int *raw); extern int CNF_GetLogMeasurements(int *raw);
extern int CNF_GetLogSelection(void);
extern int CNF_GetLogStatistics(void); extern int CNF_GetLogStatistics(void);
extern int CNF_GetLogTracking(void); extern int CNF_GetLogTracking(void);
extern int CNF_GetLogRtc(void); extern int CNF_GetLogRtc(void);
@@ -134,6 +135,7 @@ typedef enum {
CNF_HWTS_RXFILTER_ANY, CNF_HWTS_RXFILTER_ANY,
CNF_HWTS_RXFILTER_NONE, CNF_HWTS_RXFILTER_NONE,
CNF_HWTS_RXFILTER_NTP, CNF_HWTS_RXFILTER_NTP,
CNF_HWTS_RXFILTER_PTP,
CNF_HWTS_RXFILTER_ALL, CNF_HWTS_RXFILTER_ALL,
} CNF_HwTs_RxFilter; } CNF_HwTs_RxFilter;
@@ -151,16 +153,17 @@ typedef struct {
extern int CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface); extern int CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface);
extern int CNF_GetPtpPort(void);
extern char *CNF_GetNtsDumpDir(void); extern char *CNF_GetNtsDumpDir(void);
extern char *CNF_GetNtsNtpServer(void); extern char *CNF_GetNtsNtpServer(void);
extern char *CNF_GetNtsServerCertFile(void); extern int CNF_GetNtsServerCertAndKeyFiles(const char ***certs, const char ***keys);
extern char *CNF_GetNtsServerKeyFile(void);
extern int CNF_GetNtsServerPort(void); extern int CNF_GetNtsServerPort(void);
extern int CNF_GetNtsServerProcesses(void); extern int CNF_GetNtsServerProcesses(void);
extern int CNF_GetNtsServerConnections(void); extern int CNF_GetNtsServerConnections(void);
extern int CNF_GetNtsRefresh(void); extern int CNF_GetNtsRefresh(void);
extern int CNF_GetNtsRotate(void); extern int CNF_GetNtsRotate(void);
extern char *CNF_GetNtsTrustedCertFile(void); extern int CNF_GetNtsTrustedCertsPaths(const char ***paths, uint32_t **ids);
extern int CNF_GetNoSystemCert(void); extern int CNF_GetNoSystemCert(void);
extern int CNF_GetNoCertTimeCheck(void); extern int CNF_GetNoCertTimeCheck(void);

111
configure vendored
View File

@@ -5,7 +5,7 @@
# #
# Copyright (C) Richard P. Curnow 1997-2003 # Copyright (C) Richard P. Curnow 1997-2003
# Copyright (C) Bryan Christianson 2016 # Copyright (C) Bryan Christianson 2016
# Copyright (C) Miroslav Lichvar 2009, 2012-2020 # Copyright (C) Miroslav Lichvar 2009, 2012-2021
# Copyright (C) Stefan R. Filipek 2019 # Copyright (C) Stefan R. Filipek 2019
# #
# ======================================================================= # =======================================================================
@@ -33,13 +33,13 @@ test_code () {
echo "int main(int argc, char **argv) {" echo "int main(int argc, char **argv) {"
echo "$code" echo "$code"
echo "return 0; }" echo "return 0; }"
) > docheck.c ) > conftest.c
echo "docheck.c:" >> config.log echo "conftest.c:" >> config.log
cat docheck.c >> config.log cat conftest.c >> config.log
echo $MYCC $MYCFLAGS $MYCPPFLAGS $cflags -o docheck docheck.c $ldflags \ echo $MYCC $MYCFLAGS $MYCPPFLAGS $cflags -o conftest conftest.c $ldflags \
$MYLDFLAGS >> config.log $MYLDFLAGS >> config.log
$MYCC $MYCFLAGS $MYCPPFLAGS $cflags -o docheck docheck.c $ldflags \ $MYCC $MYCFLAGS $MYCPPFLAGS $cflags -o conftest conftest.c $ldflags \
$MYLDFLAGS >> config.log 2>&1 $MYLDFLAGS >> config.log 2>&1
if [ $? -eq 0 ] if [ $? -eq 0 ]
@@ -50,7 +50,7 @@ test_code () {
echo "No" echo "No"
result=1 result=1
fi fi
rm -f docheck.c docheck rm -f conftest.c conftest
echo >> config.log echo >> config.log
return $result return $result
} }
@@ -245,6 +245,7 @@ try_lockmem=0
feat_asyncdns=1 feat_asyncdns=1
feat_forcednsretry=1 feat_forcednsretry=1
try_clock_gettime=1 try_clock_gettime=1
try_arc4random=1
try_recvmmsg=1 try_recvmmsg=1
feat_timestamping=1 feat_timestamping=1
try_timestamping=0 try_timestamping=0
@@ -421,6 +422,7 @@ case $OPERATINGSYSTEM in
try_setsched=1 try_setsched=1
try_lockmem=1 try_lockmem=1
try_phc=1 try_phc=1
try_arc4random=0
add_def LINUX add_def LINUX
echo "Configuring for " $SYSTEM echo "Configuring for " $SYSTEM
;; ;;
@@ -467,7 +469,7 @@ case $OPERATINGSYSTEM in
;; ;;
SunOS) SunOS)
EXTRA_OBJECTS="sys_generic.o sys_solaris.o sys_timex.o sys_posix.o" EXTRA_OBJECTS="sys_generic.o sys_solaris.o sys_timex.o sys_posix.o"
LIBS="$LIBS -lsocket -lnsl -lresolv" LIBS="$LIBS -lsocket -lnsl -lkvm -lelf -lresolv"
try_setsched=1 try_setsched=1
try_lockmem=1 try_lockmem=1
add_def SOLARIS add_def SOLARIS
@@ -479,7 +481,7 @@ case $OPERATINGSYSTEM in
add_def FEAT_PRIVDROP add_def FEAT_PRIVDROP
priv_ops="ADJUSTTIMEX SETTIME BINDSOCKET" priv_ops="ADJUSTTIMEX SETTIME BINDSOCKET"
fi fi
echo "Configuring for Solaris (" $SYSTEM "SunOS version" $VERSION ")" echo "Configuring for illumos (" $SYSTEM "SunOS version" $VERSION ")"
;; ;;
* ) * )
echo "error: $SYSTEM is not supported (yet?)" echo "error: $SYSTEM is not supported (yet?)"
@@ -655,14 +657,28 @@ then
fi fi
fi fi
if ! test_code 'O_NOFOLLOW flag' 'sys/types.h sys/stat.h fcntl.h' '' "$LIBS" \
'return open("/dev/null", O_NOFOLLOW);'
then
if test_code 'O_NOFOLLOW flag with _GNU_SOURCE' 'sys/types.h sys/stat.h fcntl.h' \
'-D_GNU_SOURCE' "$LIBS" \
'return open("/dev/null", O_NOFOLLOW);'
then
add_def _GNU_SOURCE
else
echo "error: open() does not support O_NOFOLLOW flag"
exit 1
fi
fi
if [ $try_clock_gettime = "1" ]; then if [ $try_clock_gettime = "1" ]; then
if test_code 'clock_gettime()' 'time.h' '' '' \ if test_code 'clock_gettime()' 'time.h' '' '' \
'clock_gettime(CLOCK_REALTIME, NULL);' 'clock_gettime(CLOCK_REALTIME, (void *)1);'
then then
add_def HAVE_CLOCK_GETTIME add_def HAVE_CLOCK_GETTIME
else else
if test_code 'clock_gettime() in -lrt' 'time.h' '' '-lrt' \ if test_code 'clock_gettime() in -lrt' 'time.h' '' '-lrt' \
'clock_gettime(CLOCK_REALTIME, NULL);' 'clock_gettime(CLOCK_REALTIME, (void *)1);'
then then
add_def HAVE_CLOCK_GETTIME add_def HAVE_CLOCK_GETTIME
EXTRA_LIBS="$EXTRA_LIBS -lrt" EXTRA_LIBS="$EXTRA_LIBS -lrt"
@@ -670,10 +686,11 @@ if [ $try_clock_gettime = "1" ]; then
fi fi
fi fi
if test_code 'getaddrinfo()' 'sys/types.h sys/socket.h netdb.h' '' "$LIBS" \ if ! test_code 'getaddrinfo()' 'sys/types.h sys/socket.h netdb.h' '' "$LIBS" \
'return getaddrinfo(0, 0, 0, 0);' 'return getaddrinfo(0, 0, 0, 0);'
then then
add_def HAVE_GETADDRINFO echo "error: getaddrinfo() not found"
exit 1
fi fi
if [ $feat_asyncdns = "1" ] && \ if [ $feat_asyncdns = "1" ] && \
@@ -687,11 +704,14 @@ then
use_pthread=1 use_pthread=1
fi fi
if test_code 'arc4random_buf()' 'stdlib.h' '' '' 'arc4random_buf(NULL, 0);'; then if [ $try_arc4random = "1" ] && \
test_code 'arc4random_buf()' 'stdlib.h' '' '' \
'arc4random_buf((void *)1, 1);'
then
add_def HAVE_ARC4RANDOM add_def HAVE_ARC4RANDOM
else else
if test_code 'getrandom()' 'stdlib.h sys/random.h' '' '' \ if test_code 'getrandom()' 'stdlib.h sys/random.h' '' '' \
'return getrandom(NULL, 256, 0);'; then 'return getrandom((void *)1, 1, 0);'; then
add_def HAVE_GETRANDOM add_def HAVE_GETRANDOM
fi fi
fi fi
@@ -885,7 +905,7 @@ if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_nettle = "1" ];
add_def FEAT_SECHASH add_def FEAT_SECHASH
if test_code 'CMAC in nettle' 'nettle/cmac.h' "$test_cflags" "$test_link" \ if test_code 'CMAC in nettle' 'nettle/cmac.h' "$test_cflags" "$test_link" \
'cmac128_update(NULL, NULL, NULL, 0, NULL);' 'cmac128_update((void *)1, (void *)2, (void *)3, 1, (void *)4);'
then then
add_def HAVE_CMAC add_def HAVE_CMAC
EXTRA_OBJECTS="$EXTRA_OBJECTS cmac_nettle.o" EXTRA_OBJECTS="$EXTRA_OBJECTS cmac_nettle.o"
@@ -910,7 +930,7 @@ fi
if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_tomcrypt = "1" ]; then if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_tomcrypt = "1" ]; then
if test_code 'tomcrypt' 'tomcrypt.h' '-I/usr/include/tomcrypt' '-ltomcrypt' \ if test_code 'tomcrypt' 'tomcrypt.h' '-I/usr/include/tomcrypt' '-ltomcrypt' \
'hash_memory_multi(find_hash("md5"), NULL, NULL, NULL, 0, NULL, 0);' 'hash_memory_multi(find_hash("md5"), (void *)1, (void *)2, (void *)3, 1, (void *)4, 1);'
then then
HASH_OBJ="hash_tomcrypt.o" HASH_OBJ="hash_tomcrypt.o"
HASH_LINK="-ltomcrypt" HASH_LINK="-ltomcrypt"
@@ -919,36 +939,69 @@ if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_tomcrypt = "1" ]
fi fi
fi fi
if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_gnutls = "1" ]; then
test_cflags="`pkg_config --cflags gnutls`"
test_link="`pkg_config --libs gnutls`"
if test_code 'gnutls' 'gnutls/crypto.h' \
"$test_cflags" "$test_link" '
return gnutls_hash((void *)1, (void *)2, 1);'
then
HASH_OBJ="hash_gnutls.o"
HASH_LINK="$test_link"
MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
add_def FEAT_SECHASH
if test_code 'CMAC in gnutls' 'gnutls/crypto.h' "$test_cflags" "$test_link" \
'return gnutls_hmac_init((void *)1, GNUTLS_MAC_AES_CMAC_128, (void *)2, 0);'
then
add_def HAVE_CMAC
EXTRA_OBJECTS="$EXTRA_OBJECTS cmac_gnutls.o"
EXTRA_CLI_OBJECTS="$EXTRA_CLI_OBJECTS cmac_gnutls.o"
fi
fi
fi
EXTRA_OBJECTS="$EXTRA_OBJECTS $HASH_OBJ" EXTRA_OBJECTS="$EXTRA_OBJECTS $HASH_OBJ"
EXTRA_CLI_OBJECTS="$EXTRA_CLI_OBJECTS $HASH_OBJ" EXTRA_CLI_OBJECTS="$EXTRA_CLI_OBJECTS $HASH_OBJ"
LIBS="$LIBS $HASH_LINK" LIBS="$LIBS $HASH_LINK"
if [ $feat_ntp = "1" ] && [ $feat_nts = "1" ] && [ $try_gnutls = "1" ]; then if [ $feat_ntp = "1" ] && [ $feat_nts = "1" ] && [ $try_gnutls = "1" ]; then
test_cflags="`pkg_config --cflags gnutls`" if [ "$HASH_OBJ" = "hash_gnutls.o" ]; then
test_link="`pkg_config --libs gnutls`" test_cflags=""
if test_code 'gnutls' 'gnutls/gnutls.h' \ test_link=""
"$test_cflags" "$test_link" ' else
return gnutls_init(NULL, 0) + GNUTLS_TLS1_3 + test_cflags="`pkg_config --cflags gnutls`"
gnutls_priority_init2(NULL, "", NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND) + test_link="`pkg_config --libs gnutls`"
gnutls_prf_rfc5705(NULL, 0, "", 0, "", 16, NULL);' fi
if test_code 'TLS1.3 in gnutls' 'gnutls/gnutls.h' \
"$test_cflags" "$test_link $LIBS" '
return gnutls_init((void *)1, 0) + GNUTLS_TLS1_3 +
gnutls_priority_init2((void *)1, "", NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND) +
gnutls_prf_rfc5705((void *)1, 0, "", 0, "", 16, (void *)2);'
then then
if test_code 'SIV in nettle' \ if test_code 'SIV in nettle' \
'nettle/siv-cmac.h' "" "$LIBS" \ 'nettle/siv-cmac.h' "" "$LIBS" \
'siv_cmac_aes128_set_key(NULL, NULL);' 'siv_cmac_aes128_set_key((void *)1, (void *)2);'
then then
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o" EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o"
add_def HAVE_SIV add_def HAVE_SIV
add_def HAVE_NETTLE_SIV_CMAC add_def HAVE_NETTLE_SIV_CMAC
else else
if test_code 'SIV in gnutls' 'gnutls/gnutls.h' \ if test_code 'SIV in gnutls' 'gnutls/crypto.h' \
"$test_cflags" "$test_link" ' "$test_cflags" "$test_link $LIBS" '
return gnutls_aead_cipher_init(NULL, GNUTLS_CIPHER_AES_128_SIV, NULL);' return gnutls_aead_cipher_init((void *)1, GNUTLS_CIPHER_AES_128_SIV, (void *)2);'
then then
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_gnutls.o" EXTRA_OBJECTS="$EXTRA_OBJECTS siv_gnutls.o"
add_def HAVE_SIV add_def HAVE_SIV
if test_code 'gnutls_aead_cipher_set_key()' 'gnutls/crypto.h' \
"$test_cflags" "$test_link $LIBS" '
return gnutls_aead_cipher_set_key((void *)1, (void *)2);'
then
add_def HAVE_GNUTLS_AEAD_CIPHER_SET_KEY
fi
else else
if test_code 'AES128 in nettle' 'nettle/aes.h' '' "$LIBS" \ if test_code 'AES128 in nettle' 'nettle/aes.h' '' "$LIBS" \
'aes128_set_encrypt_key(NULL, NULL);' 'aes128_set_encrypt_key((void *)1, (void *)2);'
then then
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o" EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o"
add_def HAVE_SIV add_def HAVE_SIV

View File

@@ -3,7 +3,7 @@
// Copyright (C) Richard P. Curnow 1997-2003 // Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Stephen Wadeley 2016 // Copyright (C) Stephen Wadeley 2016
// Copyright (C) Bryan Christianson 2017 // Copyright (C) Bryan Christianson 2017
// Copyright (C) Miroslav Lichvar 2009-2020 // Copyright (C) Miroslav Lichvar 2009-2021
// //
// This program is free software; you can redistribute it and/or modify // 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 // it under the terms of version 2 of the GNU General Public License as
@@ -72,7 +72,7 @@ server, or its IP address. It supports the following options:
This option specifies the minimum interval between requests sent to the server This option specifies the minimum interval between requests sent to the server
as a power of 2 in seconds. For example, *minpoll 5* would mean that the as a power of 2 in seconds. For example, *minpoll 5* would mean that the
polling interval should not drop below 32 seconds. The default is 6 (64 polling interval should not drop below 32 seconds. The default is 6 (64
seconds), the minimum is -6 (1/64th of a second), and the maximum is 24 (6 seconds), the minimum is -7 (1/128th of a second), and the maximum is 24 (6
months). Note that intervals shorter than 6 (64 seconds) should generally not months). Note that intervals shorter than 6 (64 seconds) should generally not
be used with public servers on the Internet, because it might be considered be used with public servers on the Internet, because it might be considered
abuse. A sub-second interval will be enabled only when the server is reachable abuse. A sub-second interval will be enabled only when the server is reachable
@@ -82,7 +82,7 @@ should be in a local network.
This option specifies the maximum interval between requests sent to the server This option specifies the maximum interval between requests sent to the server
as a power of 2 in seconds. For example, *maxpoll 9* indicates that the polling as a power of 2 in seconds. For example, *maxpoll 9* indicates that the polling
interval should stay at or below 9 (512 seconds). The default is 10 (1024 interval should stay at or below 9 (512 seconds). The default is 10 (1024
seconds), the minimum is -6 (1/64th of a second), and the maximum is 24 (6 seconds), the minimum is -7 (1/128th of a second), and the maximum is 24 (6
months). months).
*iburst*::: *iburst*:::
With this option, *chronyd* will start with a burst of 4-8 requests in order to With this option, *chronyd* will start with a burst of 4-8 requests in order to
@@ -116,6 +116,12 @@ mechanism. Unlike with the *key* option, the server and client do not need to
share a key in a key file. NTS has a Key Establishment (NTS-KE) protocol using share a key in a key file. NTS has a Key Establishment (NTS-KE) protocol using
the Transport Layer Security (TLS) protocol to get the keys and cookies the Transport Layer Security (TLS) protocol to get the keys and cookies
required by NTS for authentication of NTP packets. required by NTS for authentication of NTP packets.
*certset* _ID_:::
This option specifies which set of trusted certificates should be used to verify
the server's certificate when the *nts* option is enabled. Sets of certificates
can be specified with the <<ntstrustedcerts,*ntstrustedcerts*>> directive. The
default set is 0, which by default contains certificates of the system's
default trusted certificate authorities.
*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
accurate a particular measurement is likely to be. Long round-trip delays accurate a particular measurement is likely to be. Long round-trip delays
@@ -125,25 +131,40 @@ of the messages was delayed the measurement error is likely to be substantial.
For small variations in the round-trip delay, *chronyd* uses a weighting scheme For small variations in the round-trip delay, *chronyd* uses a weighting scheme
when processing the measurements. However, beyond a certain level of delay the when processing the measurements. However, beyond a certain level of delay the
measurements are likely to be so corrupted as to be useless. (This is measurements are likely to be so corrupted as to be useless. (This is
particularly so on dial-up or other slow links, where a long delay probably particularly so on wireless networks and other slow links, where a long delay
indicates a highly asymmetric delay caused by the response waiting behind a lot probably indicates a highly asymmetric delay caused by the response waiting
of packets related to a download of some sort). behind a lot of packets related to a download of some sort).
+ +
If the user knows that round trip delays above a certain level should cause the If the user knows that round trip delays above a certain level should cause the
measurement to be ignored, this level can be defined with the *maxdelay* measurement to be ignored, this level can be defined with the *maxdelay*
option. For example, *maxdelay 0.3* would indicate that measurements with a 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 greater than 0.3 seconds should be ignored. The default value
3 seconds and the maximum value is 1000 seconds. is 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. specified ratio times the minimum delay, it will be rejected. By default, this
test is disabled.
*maxdelaydevratio* _ratio_::: *maxdelaydevratio* _ratio_:::
If a measurement has a ratio of the increase in the round-trip delay from the If a measurement has a ratio of the increase in the round-trip delay from the
minimum delay amongst the previous measurements to the standard deviation of 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.
*maxdelayquant* _p_:::
This option disables the *maxdelaydevratio* test and specifies the maximum
acceptable delay as a quantile of the round-trip delay instead of a function of
the minimum delay amongst the buffered measurements. If a measurement has a
round-trip delay that is greater than a long-term estimate of the _p_-quantile,
it will be rejected.
+
The specified _p_ value should be between 0.05 and 0.95. For example,
*maxdelayquant 0.2* would indicate that only measurements with the lowest 20
percent of round-trip delays should be accepted. Note that it can take many
measurements for the estimated quantile to reach the expected value. This
option is intended for synchronisation in mostly static local networks with
very short polling intervals and possibly combined with the *filter* option.
By default, this test is disabled in favour of the *maxdelaydevratio* test.
*mindelay* _delay_::: *mindelay* _delay_:::
This option 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
@@ -170,11 +191,12 @@ Set the minimum number of samples kept for this source. This overrides the
*maxsamples* _samples_::: *maxsamples* _samples_:::
Set the maximum number of samples kept for this source. This overrides the Set the maximum number of samples kept for this source. This overrides the
<<maxsamples,*maxsamples*>> directive. <<maxsamples,*maxsamples*>> directive.
*filter* _samples_::: *filter* _polls_:::
This option enables a median filter to reduce noise in NTP measurements. The This option enables a median filter to reduce noise in NTP measurements. The
filter will reduce the specified number of samples to a single sample. It is filter will process samples collected in the specified number of polls
intended to be used with very short polling intervals in local networks where into a single sample. It is intended to be used with very short polling
it is acceptable to generate a lot of NTP traffic. intervals in local networks where it is acceptable to generate a lot of NTP
traffic.
*offline*::: *offline*:::
If the server will not be reachable when *chronyd* is started, the *offline* If the server will not be reachable when *chronyd* is started, the *offline*
option can be specified. *chronyd* will not try to poll the server until it is option can be specified. *chronyd* will not try to poll the server until it is
@@ -261,17 +283,46 @@ will send two extra packets instead of one.
When the synchronisation source is selected from available sources, sources When the synchronisation source is selected from available sources, sources
with lower stratum are normally slightly preferred. This option can be used to with lower stratum are normally slightly preferred. This option can be used to
increase stratum of the source to the specified minimum, so *chronyd* will increase stratum of the source to the specified minimum, so *chronyd* will
avoid selecting that source. This is useful with low stratum sources that are avoid selecting that source. This is useful with low-stratum sources that are
known to be unreliable or inaccurate and which should be used only when other known to be unreliable or inaccurate and which should be used only when other
sources are unreachable. sources are unreachable.
*version* _version_::: *version* _version_:::
This option sets the NTP version of packets sent to the server. This can be This option sets the NTP version of packets sent to the server. This can be
useful when the server runs an old NTP implementation that does not respond to useful when the server runs an old NTP implementation that does not respond to
requests using a newer version. The default version depends on whether a key is requests using a newer version. The default version depends on other options.
specified by the *key* option and which authentication hash function the key If the *extfield* or *xleave* option is used, the default version is 4. If
is using. If the output size of the hash function is longer than 160 bits, the those options are not used and the *key* option specifies a key using a hash
default version is 3 for compatibility with older *chronyd* servers. Otherwise, function with output size longer than 160 bits (e.g. SHA256), the default
version is 3 for compatibility with older *chronyd* servers. In other cases,
the default version is 4. the default version is 4.
*copy*:::
This option specifies that the server and client are closely related, their
configuration does not allow a synchronisation loop to form between them, and
the client is allowed to assume the reference ID and stratum of the server.
This is useful when multiple instances of `chronyd` are running on one computer
(e.g. for security or performance reasons), one primarily operating as a client
to synchronise the system clock and other instances started with the *-x*
option to operate as NTP servers for other computers with their NTP clocks
synchronised to the first instance.
*extfield* _type_:::
This option enables an NTPv4 extension field specified by its type as a
hexadecimal number. It will be included in requests sent to the server and
processed in received responses if the server supports it. Note that some
server implementations do not respond to requests containing an unknown
extension field (*chronyd* as a server responded to such requests since
version 2.0).
+
The following extension field can be enabled by this option:
+
_F323_::::
This is an experimental extension field for some improvements that were
proposed for the next version of the NTP protocol (NTPv5). The field contains
root delay and dispersion in higher resolution and a monotonic receive
timestamp, which enables a frequency transfer between the server and client. It
can significantly improve stability of the synchronization. Generally, it
should be expected to work only between servers and clients running the same
version of *chronyd*.
{blank}:::
[[pool]]*pool* _name_ [_option_]...:: [[pool]]*pool* _name_ [_option_]...::
The syntax of this directive is similar to that for the <<server,*server*>> The syntax of this directive is similar to that for the <<server,*server*>>
@@ -314,7 +365,7 @@ address of this host. *chronyd* does not support ephemeral associations.
This directive can be used multiple times to specify multiple peers. This directive can be used multiple times to specify multiple peers.
+ +
The following options of the *server* directive do not work in the *peer* The following options of the *server* directive do not work in the *peer*
directive: *iburst*, *burst*, *nts*, *presend*. directive: *iburst*, *burst*, *nts*, *presend*, *copy*.
+ +
When using the *xleave* option, both peers must support and have enabled the When using the *xleave* option, both peers must support and have enabled the
interleaved mode, otherwise the synchronisation will work in one direction interleaved mode, otherwise the synchronisation will work in one direction
@@ -341,19 +392,8 @@ recommended to use two separate client/server associations (specified by the
<<server,*server*>> directive on both hosts) instead. <<server,*server*>> directive on both hosts) instead.
[[initstepslew]]*initstepslew* _step-threshold_ [_hostname_]...:: [[initstepslew]]*initstepslew* _step-threshold_ [_hostname_]...::
In normal operation, *chronyd* slews the time when it needs to adjust the (This directive is deprecated in favour of the <<makestep,*makestep*>>
system clock. For example, to correct a system clock which is 1 second slow, directive.)
*chronyd* slightly increases the amount by which the system clock is advanced
on each clock interrupt, until the error is removed. Note that at no time does
time run backwards with this method.
+
On most Unix systems it is not desirable to step the system clock, because many
programs rely on time advancing monotonically forwards.
+
When the *chronyd* daemon is initially started, it is possible that the system
clock is considerably in error. Attempting to correct such an error by slewing
might not be sensible, since it might take several hours to correct the error by
this means.
+ +
The purpose of the *initstepslew* directive is to allow *chronyd* to make a The purpose of the *initstepslew* directive is to allow *chronyd* to make a
rapid measurement of the system clock error at boot time, and to correct the rapid measurement of the system clock error at boot time, and to correct the
@@ -374,29 +414,30 @@ error. *chronyd* then enters its normal operating mode.
An example of the use of the directive is: An example of the use of the directive is:
+ +
---- ----
initstepslew 30 foo.example.net bar.example.net initstepslew 30 foo.example.net bar.example.net baz.example.net
---- ----
+ +
where 2 NTP servers are used to make the measurement. The _30_ indicates that where 3 NTP servers are used to make the measurement. The _30_ indicates that
if the system's error is found to be 30 seconds or less, a slew will be used to if the system's error is found to be 30 seconds or less, a slew will be used to
correct it; if the error is above 30 seconds, a step will be used. correct it; if the error is above 30 seconds, a step will be used.
+ +
The *initstepslew* directive can also be used in an isolated LAN environment, The *initstepslew* directive can also be used in an isolated LAN environment,
where the clocks are set manually. The most stable computer is chosen as the where the clocks are set manually. The most stable computer is chosen as the
master, and the other computers are slaved to it. If each of the slaves is primary server and the other computers are its clients. If each of the clients
configured with the <<local,*local*>> directive, the master can be set up with is configured with the <<local,*local*>> directive, the server can be set up
an *initstepslew* directive which references some or all of the slaves. Then, with an *initstepslew* directive which references some or all of the clients.
if the master machine has to be rebooted, the slaves can be relied on to act Then, if the server machine has to be rebooted, the clients can be relied on to
analogously to a flywheel and preserve the time for a short period while the act analogously to a flywheel and preserve the time for a short period while
master completes its reboot. the server completes its reboot.
+ +
The *initstepslew* directive is functionally similar to a combination of the The *initstepslew* directive is functionally similar to a combination of the
<<makestep,*makestep*>> and <<server,*server*>> directives with the *iburst* <<makestep,*makestep*>> and <<server,*server*>> directives with the *iburst*
option. The main difference is that the *initstepslew* servers are used only option. The main difference is that the *initstepslew* servers are used only
before normal operation begins and that the foreground *chronyd* process waits before normal operation begins and that the foreground *chronyd* process waits
for *initstepslew* to finish before exiting. This is useful to prevent programs for *initstepslew* to finish before exiting. This prevent programs started in
started in the boot sequence after *chronyd* from reading the clock before it the boot sequence after *chronyd* from reading the clock before it has been
has been stepped. stepped. With the *makestep* directive, the
<<chronyc.adoc#waitsync,*waitsync*>> command of *chronyc* can be used instead.
[[refclock]]*refclock* _driver_ _parameter_[:__option__]... [_option_]...:: [[refclock]]*refclock* _driver_ _parameter_[:__option__]... [_option_]...::
The *refclock* directive specifies a hardware reference clock to be used as a The *refclock* directive specifies a hardware reference clock to be used as a
@@ -486,8 +527,10 @@ Note that some PTP clocks cannot be configured to timestamp only assert or
clear events, and it is necessary to use the *width* option to filter wrong clear events, and it is necessary to use the *width* option to filter wrong
PPS samples. PPS samples.
*pin*=_index_:::: *pin*=_index_::::
This option specifies the index of the pin to which is connected the PPS This option specifies the index of the pin which should be enabled for the
signal. The default value is 0. PPS timestamping. If the PHC does not have configurable pins (i.e. the channel
function is fixed), the index needs to be set to -1 to disable the pin
configuration. The default value is 0.
*channel*=_index_:::: *channel*=_index_::::
This option specifies the index of the channel for the PPS mode. The default This option specifies the index of the channel for the PPS mode. The default
value is 0. value is 0.
@@ -599,6 +642,13 @@ 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 <<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 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. expected. This option does not make sense with PPS refclocks.
*local*:::
This option specifies that the reference clock is an unsynchronised clock which
is more stable than the system clock (e.g. TCXO, OCXO, or atomic clock) and
it should be used as a local standard to stabilise the system clock. The
refclock will bypass the source selection. There should be at most one refclock
specified with this option and it should have the shortest polling interval
among all configured sources.
*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.
@@ -694,6 +744,10 @@ or the <<chronyc.adoc#dump,*dump*>> command in *chronyc* is issued.
+ +
If the directory does not exist, it will be created automatically. If the directory does not exist, it will be created automatically.
+ +
The *-r* option of *chronyd* enables loading of the dump files on start. All
dump files found in the directory will be removed after start, even if the *-r*
option is not present.
+
An example of the directive is: An example of the directive is:
+ +
---- ----
@@ -748,25 +802,49 @@ This directory is used also by the <<ntsdumpdir2,NTS server>> to save keys.
[[ntsrefresh]]*ntsrefresh* _interval_:: [[ntsrefresh]]*ntsrefresh* _interval_::
This directive specifies the maximum interval between NTS-KE handshakes (in This directive specifies the maximum interval between NTS-KE handshakes (in
seconds) in order to refresh the keys authenticating NTP packets. The default seconds) in order to refresh the keys authenticating NTP packets. The default
value is 2419200 (4 weeks). value is 2419200 (4 weeks) and the maximum value is 2^31-1 (68 years).
[[ntstrustedcerts]]*ntstrustedcerts* _file_:: [[ntstrustedcerts]]*ntstrustedcerts* [_set-ID_] _file_|_directory_::
This directive specifies a file containing certificates (in the PEM format) of This directive specifies a file or directory containing certificates (in the
trusted certificate authorities (CA) that should be used to verify certificates PEM format) of trusted certificate authorities (CA) which can be used to
of NTS servers in addition to the system's default trusted CAs (if the verify certificates of NTS servers.
*nosystemcert* directive is not present). +
The optional _set-ID_ argument is a number in the range 0 through 2^32-1, which
selects the set of certificates where certificates from the specified file
or directory are added. The default ID is 0, which is a set containing the
system's default trusted CAs (unless the *nosystemcert* directive is present).
All other sets are empty by default. A set of certificates can be selected for
verification of an NTS server by the *certset* option in the *server* or *pool*
directive.
+
This directive can be used multiple times to specify one or more sets of
trusted certificates, each containing certificates from one or more files
and/or directories.
+
It is not necessary to restart *chronyd* in order to reload the certificates if
they change (e.g. after a renewal).
+
An example is:
+
----
ntstrustedcerts /etc/pki/nts/foo.crt
ntstrustedcerts 1 /etc/pki/nts/bar.crt
ntstrustedcerts 1 /etc/pki/nts/baz.crt
ntstrustedcerts 2 /etc/pki/nts/qux.crt
----
[[nosystemcert]]*nosystemcert*:: [[nosystemcert]]*nosystemcert*::
This directive disables the system's default trusted CAs. This directive disables the system's default trusted CAs. Only certificates
specified by the *ntstrustedcerts* directive will be trusted.
[[nocerttimecheck]]*nocerttimecheck* _limit_:: [[nocerttimecheck]]*nocerttimecheck* _limit_::
This directive disables the checks of the activation and expiration times of This directive disables the checks of the activation and expiration times of
certificates for the specified number of clock updates. It allows the NTS certificates for the specified number of clock updates. It allows the NTS
authentication mechanism to be used on computers which start with wrong time authentication mechanism to be used on computers which start with wrong time
(e.g. due to not having an RTC or backup battery). Disabling the time checks (e.g. due to not having an RTC or backup battery). Disabling the time checks
has important security implications, e.g. if an NTP server was ever has important security implications and should be used only as a last resort,
compromised, its certificate could be used in an attack after the expiration preferably with a minimal number of trusted certificates. The default value is
time. The default value is 0, which means the time checks are always enabled. 0, which means the time checks are always enabled.
+ +
An example of the directive is: An example of the directive is:
+ +
@@ -870,11 +948,11 @@ source combining algorithm and only the selected source will be used to control
the system clock. the system clock.
[[maxdistance]]*maxdistance* _distance_:: [[maxdistance]]*maxdistance* _distance_::
The *maxdistance* directive sets the maximum allowed root distance of the The *maxdistance* directive sets the maximum root distance of a source to be
sources to not be rejected by the source selection algorithm. The distance acceptable for synchronisation of the clock. Sources that have a distance
includes the accumulated dispersion, which might be large when the source is no larger than the specified distance will be rejected. The distance estimates the
longer synchronised, and half of the total round-trip delay to the primary maximum error of the source. It includes the root dispersion and half of the
source. root delay (round-trip time) accumulated on the path to the primary source.
+ +
By default, the maximum root distance is 3 seconds. By default, the maximum root distance is 3 seconds.
+ +
@@ -1046,7 +1124,11 @@ clock will be off for a longer time. On Linux with the default
*ignore*::: *ignore*:::
No correction is applied to the clock for the leap second. The clock will be No correction is applied to the clock for the leap second. The clock will be
corrected later in normal operation when new measurements are made and the corrected later in normal operation when new measurements are made and the
estimated offset includes the one second error. estimated offset includes the one second error. This option is particularly
useful when multiple *chronyd* instances are running on the system, one
controlling the system clock and others started with the *-x* option, which
should rely on the first instance to correct the system clock and ignore it for
the correction of their own NTP clock running on top of the system clock.
{blank}:: {blank}::
+ +
When serving time to NTP clients that cannot be configured to correct their When serving time to NTP clients that cannot be configured to correct their
@@ -1091,10 +1173,10 @@ duration = sqrt(4 / wander)
---- ----
[[leapsectz]]*leapsectz* _timezone_:: [[leapsectz]]*leapsectz* _timezone_::
This directive specifies a timezone in the system tz database which *chronyd* This directive specifies a timezone in the system timezone database which
can use to determine when will the next leap second occur and what is the *chronyd* can use to determine when will the next leap second occur and what is
current offset between TAI and UTC. It will periodically check if 23:59:59 and the current offset between TAI and UTC. It will periodically check if 23:59:59
23:59:60 are valid times in the timezone. This typically works with the and 23:59:60 are valid times in the timezone. This normally works with the
_right/UTC_ timezone. _right/UTC_ timezone.
+ +
When a leap second is announced, the timezone needs to be updated at least 12 When a leap second is announced, the timezone needs to be updated at least 12
@@ -1131,16 +1213,17 @@ Wed Dec 31 23:59:60 UTC 2008
[[makestep]]*makestep* _threshold_ _limit_:: [[makestep]]*makestep* _threshold_ _limit_::
Normally *chronyd* will cause the system to gradually correct any time offset, Normally *chronyd* will cause the system to gradually correct any time offset,
by slowing down or speeding up the clock as required. In certain situations, by slowing down or speeding up the clock as required. In certain situations,
the system clock might be so far adrift that this slewing process would take a e.g. when *chronyd* is initially started, the system clock might be so far
very long time to correct the system clock. adrift that this slewing process would take a very long time to correct the
system clock.
+ +
This directive forces *chronyd* to step the system clock if the adjustment is This directive forces *chronyd* to step the system clock if the adjustment is
larger than a threshold value, but only if there were no more clock updates larger than a threshold value, but only if there were no more clock updates
since *chronyd* was started than a specified limit (a negative value can be since *chronyd* was started than the specified limit. A negative value disables
used to disable the limit). the limit.
+ +
This is particularly useful when using reference clocks, because the On most systems it is desirable to step the system clock only on boot, before
<<initstepslew,*initstepslew*>> directive works only with NTP sources. starting programs that rely on time advancing monotonically forwards.
+ +
An example of the use of this directive is: An example of the use of this directive is:
+ +
@@ -1152,12 +1235,16 @@ This would step the system clock if the adjustment is larger than 0.1 seconds, b
only in the first three clock updates. only in the first three clock updates.
[[maxchange]]*maxchange* _offset_ _start_ _ignore_:: [[maxchange]]*maxchange* _offset_ _start_ _ignore_::
This directive sets the maximum allowed offset corrected on a clock update. The This directive sets the maximum offset to be accepted on a clock update. The
check is performed only after the specified number of updates to allow a large offset is measured relative to the current estimate of the true time, which is
initial adjustment of the system clock. When an offset larger than the different from the system time if a previous slew did not finish.
specified maximum occurs, it will be ignored for the specified number of times +
and then *chronyd* will give up and exit (a negative value can be used to never The check is enabled after the specified number of clock updates to allow a
exit). In both cases a message is sent to syslog. large initial offset to be corrected on start. Offsets larger than the
specified maximum will be ignored for the specified number of times. Another
large offset will cause *chronyd* to give up and exit. A negative value
can be used to disable the limit to ignore all large offsets. A syslog message
will be generated when an offset is ignored or it causes the exit.
+ +
An example of the use of this directive is: An example of the use of this directive is:
+ +
@@ -1184,7 +1271,7 @@ This directive specifies the maximum assumed drift (frequency error) of the
system clock. It limits the frequency adjustment that *chronyd* is allowed to system clock. It limits the frequency adjustment that *chronyd* is allowed to
use to correct the measured drift. It is an additional limit to the maximum use to correct the measured drift. It is an additional limit to the maximum
adjustment that can be set by the system driver (100000 ppm on Linux, 500 ppm adjustment that can be set by the system driver (100000 ppm on Linux, 500 ppm
on FreeBSD, NetBSD, and macOS 10.13+, 32500 ppm on Solaris). on FreeBSD, NetBSD, and macOS 10.13+, 32500 ppm on illumos).
+ +
By default, the maximum assumed drift is 500000 ppm, i.e. the adjustment is By default, the maximum assumed drift is 500000 ppm, i.e. the adjustment is
limited by the system driver rather than this directive. limited by the system driver rather than this directive.
@@ -1202,8 +1289,8 @@ The *maxupdateskew* directive sets the threshold for determining whether an
estimate might be so unreliable that it should not be used. By default, the estimate might be so unreliable that it should not be used. By default, the
threshold is 1000 ppm. threshold is 1000 ppm.
+ +
Typical values for _skew-in-ppm_ might be 100 for a dial-up connection to Typical values for _skew-in-ppm_ might be 100 for NTP sources polled over a
servers over a phone line, and 5 or 10 for a computer on a LAN. wireless network, and 10 or smaller for sources on a local wired network.
+ +
It should be noted that this is not the only means of protection against using It should be noted that this is not the only means of protection against using
unreliable estimates. At all times, *chronyd* keeps track of both the estimated unreliable estimates. At all times, *chronyd* keeps track of both the estimated
@@ -1223,14 +1310,10 @@ all supported systems with the exception of macOS 12 or earlier).
+ +
For each system there is a maximum frequency offset of the clock that can be set For each system there is a maximum frequency offset of the clock that can be set
by the driver. On Linux it is 100000 ppm, on FreeBSD, NetBSD and macOS 10.13+ it by the driver. On Linux it is 100000 ppm, on FreeBSD, NetBSD and macOS 10.13+ it
is 5000 ppm, and on Solaris it is 32500 ppm. Also, due to a kernel limitation, is 5000 ppm, and on illumos it is 32500 ppm. Also, due to a kernel limitation,
setting *maxslewrate* on FreeBSD, NetBSD, macOS 10.13+ to a value between 500 setting *maxslewrate* on FreeBSD, NetBSD, macOS 10.13+ to a value between 500
ppm and 5000 ppm will effectively set it to 500 ppm. ppm and 5000 ppm will effectively set it to 500 ppm.
+ +
In early beta releases of macOS 13 this capability is disabled because of a
system kernel bug. When the kernel bug is fixed, chronyd will detect this and
re-enable the capability (see above limitations) with no recompilation required.
+
By default, the maximum slew rate is set to 83333.333 ppm (one twelfth). By default, the maximum slew rate is set to 83333.333 ppm (one twelfth).
[[tempcomp]] [[tempcomp]]
@@ -1322,28 +1405,21 @@ Examples of the use of the directive are as follows:
+ +
---- ----
allow 1.2.3.4 allow 1.2.3.4
allow 1.2 allow 3.4.5.0/24
allow 3.4.5 allow 3.4.5
allow 6.7.8/22
allow 6.7.8.9/22
allow 2001:db8::/32 allow 2001:db8::/32
allow 0/0 allow 0/0
allow ::/0 allow ::/0
allow allow
---- ----
+ +
The first directive allows a node with IPv4 address _1.2.3.4_ to be an NTP The first directive allows access from an IPv4 address. The second directive
client of this computer. allows access from all computers in an IPv4 subnet specified in the CIDR
The second directive allows any node with an IPv4 address of the form _1.2.x.y_ notation. The third directive specifies the same subnet using a simpler
(with _x_ and _y_ arbitrary) to be an NTP client of this computer. Likewise, notation where the prefix length is determined by the number of dots. The
the third directive allows any node with an IPv4 address of the form _3.4.5.x_ fourth directive specifies an IPv6 subnet. The fifth and sixth directives allow
to have client NTP access. The fourth and fifth forms allow access from any access from all IPv4 and IPv6 addresses respectively. The seventh directive
node with an IPv4 address of the form _6.7.8.x_, _6.7.9.x_, _6.7.10.x_ or allows access from all addresses (both IPv4 or IPv6).
_6.7.11.x_ (with _x_ arbitrary), i.e. the value 22 is the number of bits
defining the specified subnet. In the fifth form, the final byte is ignored.
The sixth form is used for IPv6 addresses. The seventh and eighth forms allow
access by any IPv4 and IPv6 node respectively. The ninth forms allows access by
any node (IPv4 or IPv6).
+ +
A second form of the directive, *allow all*, has a greater effect, depending on A second form of the directive, *allow all*, has a greater effect, depending on
the ordering of directives in the configuration file. To illustrate the effect, the ordering of directives in the configuration file. To illustrate the effect,
@@ -1351,32 +1427,43 @@ consider the two examples:
+ +
---- ----
allow 1.2.3.4 allow 1.2.3.4
deny 1.2.3 deny 1.2.3.0/24
allow 1.2 allow 1.2.0.0/16
---- ----
+ +
and and
+ +
---- ----
allow 1.2.3.4 allow 1.2.3.4
deny 1.2.3 deny 1.2.3.0/24
allow all 1.2 allow all 1.2.0.0/16
---- ----
+ +
In the first example, the effect is the same regardless of what order the three In the first example, the effect is the same regardless of what order the three
directives are given in. So the _1.2.x.y_ subnet is allowed access, except for directives are given in. So the _1.2.0.0/16_ subnet is allowed access, except
the _1.2.3.x_ subnet, which is denied access, however the host _1.2.3.4_ is for the _1.2.3.0/24_ subnet, which is denied access, however the host _1.2.3.4_
allowed access. is allowed access.
+ +
In the second example, the *allow all 1.2* directives overrides the effect of In the second example, the *allow all 1.2.0.0/16* directive overrides the
_any_ previous directive relating to a subnet within the specified subnet. effect of _any_ previous directive relating to a subnet within the specified
Within a configuration file this capability is probably rather moot; however, subnet. Within a configuration file this capability is probably rather moot;
it is of greater use for reconfiguration at run-time via *chronyc* with the however, it is of greater use for reconfiguration at run-time via *chronyc*
<<chronyc.adoc#allow,*allow all*>> command. with the <<chronyc.adoc#allow,*allow all*>> command.
+ +
The directive allows a hostname to be specified instead of an IP address, but The rules are internally represented as a tree of tables with one level per
the name must be resolvable when *chronyd* is started (i.e. *chronyd* needs four bits of the IPv4 or IPv6 address. The order of the *allow* and *deny*
to be started when the network is already up and DNS is working). directives matters if they modify the same records of one table, i.e. if one
subnet is included in the other subnet and their prefix lengths are at the same
level. For example, _1.2.3.0/28_ and _1.2.3.0/29_ are in different tables, but
_1.2.3.0/25_ and _1.2.3.0/28_ are in the same table. The configuration can be
verified for individual addresses with the <<chronyc.adoc#accheck,*accheck*>>
command in *chronyc*.
+
A hostname can be specified in the directives instead of the IP address, but
the name must be resolvable when *chronyd* is started, i.e. the network is
already up and DNS is working. If the hostname resolves to multiple addresses,
only the first address (in the order returned by the system resolver) will be
allowed or denied.
+ +
Note, if the <<initstepslew,*initstepslew*>> directive is used in the Note, if the <<initstepslew,*initstepslew*>> directive is used in the
configuration file, each of the computers listed in that directive must allow configuration file, each of the computers listed in that directive must allow
@@ -1430,14 +1517,14 @@ This directive can be used multiple times to specify multiple addresses.
The syntax is as follows: The syntax is as follows:
+ +
---- ----
broadcast 30 192.168.1.255 broadcast 32 192.168.1.255
broadcast 60 192.168.2.255 12123 broadcast 64 192.168.2.255 12123
broadcast 60 ff02::101 broadcast 64 ff02::101
---- ----
+ +
In the first example, the destination port defaults to UDP port 123 (the normal NTP In the first example, the destination port defaults to UDP port 123 (the normal NTP
port). In the second example, the destination port is specified as 12123. The port). In the second example, the destination port is specified as 12123. The
first parameter in each case (30 or 60 respectively) is the interval in seconds first parameter in each case (32 or 64 respectively) is the interval in seconds
between broadcast packets being sent. The second parameter in each case is the between broadcast packets being sent. The second parameter in each case is the
broadcast address to send the packet to. This should correspond to the broadcast address to send the packet to. This should correspond to the
broadcast address of one of the network interfaces on the computer where broadcast address of one of the network interfaces on the computer where
@@ -1460,11 +1547,12 @@ directive.
This directive specifies the maximum amount of memory that *chronyd* is allowed This directive specifies the maximum amount of memory that *chronyd* is allowed
to allocate for logging of client accesses and the state that *chronyd* as an to allocate for logging of client accesses and the state that *chronyd* as an
NTP server needs to support the interleaved mode for its clients. The default NTP server needs to support the interleaved mode for its clients. The default
limit is 524288 bytes, which is sufficient for monitoring about four thousand limit is 524288 bytes, which enables monitoring of up to 4096 IP addresses at
clients at the same time. the same time and holding NTP timestamps for up to 4096 clients using the
+ interleaved mode (depending on uniformity of their polling interval). The
In older *chrony* versions if the limit was set to 0, the memory allocation was number of addresses and timestamps is always a power of 2. The maximum
unlimited. effective value is 2147483648 (2 GB), which corresponds to 16777216 addresses
and timestamps.
+ +
An example of the use of this directive is: An example of the use of this directive is:
+ +
@@ -1566,11 +1654,28 @@ The port will be open only when a certificate and key is specified by the
[[ntsservercert]]*ntsservercert* _file_:: [[ntsservercert]]*ntsservercert* _file_::
This directive specifies a file containing a certificate in the PEM format This directive specifies a file containing a certificate in the PEM format
for *chronyd* to operate as an NTS server. for *chronyd* to operate as an NTS server. The file should also include
any intermediate certificates that the clients will need to validate the
server's certificate. The file needs to be readable by the user under which
*chronyd* is running after dropping root privileges.
+
This directive can be used multiple times to specify multiple certificates for
different names of the server.
+
The files are loaded only once. *chronyd* needs to be restarted in order to
load a renewed certificate. The <<ntsdumpdir,*ntsdumpdir*>> and
<<dumpdir,*dumpdir*>> directives with the *-r* option of *chronyd* are
recommended for a near-seamless server operation.
[[ntsserverkey]]*ntsserverkey* _file_:: [[ntsserverkey]]*ntsserverkey* _file_::
This directive specifies a file containing a private key in the PEM format This directive specifies a file containing a private key in the PEM format
for *chronyd* to operate as an NTS server. for *chronyd* to operate as an NTS server. The file needs to be readable by
the user under which *chronyd* is running after dropping root privileges. For
security reasons, it should not be readable by other users.
+
This directive can be used multiple times to specify multiple keys. The number
of keys must be the same as the number of certificates and the corresponding
files must be specified in the same order.
[[ntsprocesses]]*ntsprocesses* _processes_:: [[ntsprocesses]]*ntsprocesses* _processes_::
This directive specifies how many helper processes will *chronyd* operating This directive specifies how many helper processes will *chronyd* operating
@@ -1581,7 +1686,9 @@ process will be started and all NTS-KE requests will be handled by the main
[[maxntsconnections]]*maxntsconnections* _connections_:: [[maxntsconnections]]*maxntsconnections* _connections_::
This directive specifies the maximum number of concurrent NTS-KE connections This directive specifies the maximum number of concurrent NTS-KE connections
per process that the NTS server will accept. The default value is 100. per process that the NTS server will accept. The default value is 100. The
maximum practical value is half of the system *FD_SETSIZE* constant (usually
1024).
[[ntsdumpdir2]]*ntsdumpdir* _directory_:: [[ntsdumpdir2]]*ntsdumpdir* _directory_::
This directive specifies a directory where *chronyd* operating as an NTS server This directive specifies a directory where *chronyd* operating as an NTS server
@@ -1599,7 +1706,8 @@ ntsdumpdir @CHRONYVARDIR@
This directory is used also by the <<ntsdumpdir1,NTS client>> to save NTS cookies. This directory is used also by the <<ntsdumpdir1,NTS client>> to save NTS cookies.
[[ntsntpserver]]*ntsntpserver* _hostname_:: [[ntsntpserver]]*ntsntpserver* _hostname_::
This directive specifies the hostname or address of the NTP server(s) which is This directive specifies the hostname (as a fully qualified domain name) or
address of the NTP server(s) which is
provided in the NTS-KE response to the clients. It allows the NTS-KE server to provided in the NTS-KE response to the clients. It allows the NTS-KE server to
be separated from the NTP server. However, the servers need to share the keys, be separated from the NTP server. However, the servers need to share the keys,
i.e. external key management needs to be enabled by setting i.e. external key management needs to be enabled by setting
@@ -1613,7 +1721,7 @@ _/dev/urandom_ device. The server keeps two previous keys to give the clients
time to get new cookies encrypted by the latest key. The interval is measured time to get new cookies encrypted by the latest key. The interval is measured
as the server's operating time, i.e. the actual interval can be longer if as the server's operating time, i.e. the actual interval can be longer if
*chronyd* is not running continuously. The default interval is 604800 seconds *chronyd* is not running continuously. The default interval is 604800 seconds
(1 week). (1 week). The maximum value is 2^31-1 (68 years).
+ +
The automatic rotation of the keys can be disabled by setting *ntsrotate* to 0. The automatic rotation of the keys can be disabled by setting *ntsrotate* to 0.
In this case the keys are assumed to be managed externally. *chronyd* will not In this case the keys are assumed to be managed externally. *chronyd* will not
@@ -1622,7 +1730,7 @@ the <<chronyc.adoc#rekey,*rekey*>> command is issued in *chronyc*. The file can
be periodically copied from another server running *chronyd* (which does be periodically copied from another server running *chronyd* (which does
not have *ntsrotate* set to 0) in order to have one or more servers dedicated not have *ntsrotate* set to 0) in order to have one or more servers dedicated
to NTS-KE. The NTS-KE servers need to be configured with the to NTS-KE. The NTS-KE servers need to be configured with the
<<ntsntpname,*ntsntpname*>> directive to point the clients to the right NTP <<ntsntpserver,*ntsntpserver*>> directive to point the clients to the right NTP
server. server.
+ +
An example of the directive is: An example of the directive is:
@@ -1657,20 +1765,20 @@ directive.
The *ratelimit* directive supports a number of options (which can be defined The *ratelimit* directive supports a number of options (which can be defined
in any order): in any order):
+ +
*interval*::: *interval* _interval_:::
This option sets the minimum interval between responses. It is defined as a This option sets the minimum interval between responses. It is defined as a
power of 2 in seconds. The default value is 3 (8 seconds). The minimum value power of 2 in seconds. The default value is 3 (8 seconds). The minimum value
is -19 (524288 packets per second) and the maximum value is 12 (one packet per is -19 (524288 packets per second) and the maximum value is 12 (one packet per
4096 seconds). Note that with values below -4 the rate limiting is coarse 4096 seconds). Note that with values below -4 the rate limiting is coarse
(responses are allowed in bursts, even if the interval between them is shorter (responses are allowed in bursts, even if the interval between them is shorter
than the specified interval). than the specified interval).
*burst*::: *burst* _responses_:::
This option sets the maximum number of responses that can be sent in a burst, This option sets the maximum number of responses that can be sent in a burst,
temporarily exceeding the limit specified by the *interval* option. This is temporarily exceeding the limit specified by the *interval* option. This is
useful for clients that make rapid measurements on start (e.g. *chronyd* with useful for clients that make rapid measurements on start (e.g. *chronyd* with
the *iburst* option). The default value is 8. The minimum value is 1 and the the *iburst* option). The default value is 8. The minimum value is 1 and the
maximum value is 255. maximum value is 255.
*leak*::: *leak* _rate_:::
This option sets the rate at which responses are randomly allowed even if the This option sets the rate at which responses are randomly allowed even if the
limits specified by the *interval* and *burst* options are exceeded. This is limits specified by the *interval* and *burst* options are exceeded. This is
necessary to prevent an attacker who is sending requests with a spoofed necessary to prevent an attacker who is sending requests with a spoofed
@@ -1976,9 +2084,11 @@ from the example line above):
. Stratum of remote computer. [2] . Stratum of remote computer. [2]
. RFC 5905 tests 1 through 3 (1=pass, 0=fail) [111] . RFC 5905 tests 1 through 3 (1=pass, 0=fail) [111]
. RFC 5905 tests 5 through 7 (1=pass, 0=fail) [111] . RFC 5905 tests 5 through 7 (1=pass, 0=fail) [111]
. Tests for maximum delay, maximum delay ratio and maximum delay dev ratio, . Results of the *maxdelay*, *maxdelayratio*, and *maxdelaydevratio* (or
against defined parameters, and a test for synchronisation loop (1=pass, *maxdelayquant*) tests, and a test for synchronisation loop (1=pass,
0=fail) [1111] 0=fail). The first test from these four also checks the server precision,
response time, and whether an interleaved response is acceptable for
synchronisation. [1111]
. Local poll [10] . Local poll [10]
. Remote poll [10] . Remote poll [10]
. '`Score`' (an internal score within each polling level used to decide when to . '`Score`' (an internal score within each polling level used to decide when to
@@ -2052,6 +2162,71 @@ from the example line above):
the source is more variable than the delay of packets sent from the source the source is more variable than the delay of packets sent from the source
back. [0.00, i.e. no correction for asymmetry] back. [0.00, i.e. no correction for asymmetry]
+ +
*selection*:::
This option logs information about selection of sources for synchronisation to
a file called _selection.log_. Note that the rate of entries written to this
file grows quadratically with the number of specified sources (each measurement
triggers the selection for all sources). An example line (which actually
appears as a single line in the file) from the log file is shown below.
+
----
2022-05-01 02:01:20 203.0.113.15 * ----- 377 1.00 \
4.228e+01 -1.575e-04 1.239e-04
----
+
The columns are as follows (the quantities in square brackets are the values
from the example line above):
+
. Date [2022-05-01]
. Hour:Minute:Second. Note that the date-time pair is expressed in
UTC, not the local time zone. [02:01:20]
. IP address or reference ID of the source. [203.0.113.15]
. State of the source indicated with one of the following symbols. [*]
{blank}::::
Not considered selectable for synchronisation:
* _N_ - has the *noselect* option.
* _s_ - is not synchronised.
* _M_ - does not have enough measurements.
* _d_ - has a root distance larger than the maximum distance (configured by the
<<maxdistance,*maxdistance*>> directive).
* _~_ - has a jitter larger than the maximum jitter (configured by the
<<maxjitter,*maxjitter*>> directive).
* _w_ - waits for other sources to get out of the _M_ state.
* _S_ - has older measurements than other sources.
* _O_ - has a stratum equal or larger than the orphan stratum (configured by
the <<local,*local*>> directive).
* _T_ - does not fully agree with sources that have the *trust* option.
* _x_ - does not agree with other sources (falseticker).
{blank}::::
Considered selectable for synchronisation, but not currently used:
* _W_ - waits for other sources to be selectable (required by the
<<minsources,*minsources*>> directive, or the *require* option of
another source).
* _P_ - another selectable source is preferred due to the *prefer* option.
* _U_ - waits for a new measurement (after selecting a different best source).
* _D_ - has, or recently had, a root distance which is too large to be combined
with other sources (configured by the <<combinelimit,*combinelimit*>>
directive).
{blank}::::
Used for synchronisation of the local clock:
* _+_ - combined with the best source.
* _*_ - selected as the best source to update the reference data (e.g. root
delay, root dispersion).
. Reachability register printed as an octal number. The register has 8 bits and
is updated on every received or missed packet from the source. A value of 377
indicates that a valid reply was received for all from the last eight
transmissions. [377]
. Current score against the source in the _*_ state. The scoring system avoids
frequent reselection when multiple sources have a similar root distance. A
value larger than 1 indicates this source was better than the _*_ source in
recent selections. If the score reaches 10, the best source will be reselected
and the scores will be reset to 1. [1.00]
. Interval since the last measurement of the source in seconds. [4.228e+01]
. Lower endpoint of the interval which was expected to contain the true offset
of the local clock determined by the root distance of the source. [-1.575e-04]
. Upper endpoint of the interval which was expected to contain the true offset
of the local clock determined by the root distance of the source. [1.239e-04]
+
*tracking*::: *tracking*:::
This option logs changes to the estimate of the system's gain or loss rate, and This option logs changes to the estimate of the system's gain or loss rate, and
any slews made, to a file called _tracking.log_. An example line (which any slews made, to a file called _tracking.log_. An example line (which
@@ -2262,7 +2437,8 @@ confdir @SYSCONFDIR@/chrony.d
[[sourcedir]]*sourcedir* _directory_...:: [[sourcedir]]*sourcedir* _directory_...::
The *sourcedir* directive is identical to the *confdir* directive, except the The *sourcedir* directive is identical to the *confdir* directive, except the
configuration files have the _.sources_ suffix, they can only specify NTP configuration files have the _.sources_ suffix, they can only specify NTP
sources (i.e. use the *server*, *pool*, and *peer* directive), and can be sources (i.e. the *server*, *pool*, and *peer* directives), they are expected
to have all lines terminated by the newline character, and they can be
reloaded by the <<chronyc.adoc#reload,*reload sources*>> command in reloaded by the <<chronyc.adoc#reload,*reload sources*>> command in
*chronyc*. It is particularly useful with dynamic sources like NTP servers *chronyc*. It is particularly useful with dynamic sources like NTP servers
received from a DHCP server, which can be written to a file specific to the received from a DHCP server, which can be written to a file specific to the
@@ -2305,13 +2481,20 @@ be enabled by the *xleave* option in the <<server,*server*>> or the
+ +
This directive is supported on Linux 3.19 and newer. The NIC must support HW This directive is supported on Linux 3.19 and newer. The NIC must support HW
timestamping, which can be verified with the *ethtool -T* command. The list of timestamping, which can be verified with the *ethtool -T* command. The list of
capabilities should include _SOF_TIMESTAMPING_RAW_HARDWARE_, capabilities should include _hardware-raw-clock_, _hardware-transmit_, and
_SOF_TIMESTAMPING_TX_HARDWARE_, and _SOF_TIMESTAMPING_RX_HARDWARE_. Receive _hardware-receive_. The receive filter _all_, or _ntp_, is necessary for
filter _HWTSTAMP_FILTER_ALL_, or _HWTSTAMP_FILTER_NTP_ALL_, is necessary for timestamping of received NTP packets. Timestamping of packets received on
timestamping of received packets. Timestamping of packets received from bridged bridged and bonded interfaces is supported on Linux 4.13 and newer. If HW
and bonded interfaces is supported on Linux 4.13 and newer. When *chronyd* is timestamping does not work for received packets, *chronyd* will use kernel
running, no other process (e.g. a PTP daemon) should be working with the NIC receive timestamps instead. Transmit-only HW timestamping can still be useful
clock. to improve stability of the synchronisation.
+
*chronyd* does not synchronise the NIC clock. It assumes the clock is running
free. Multiple instances of *chronyd* can use the same interface with enabled
HW timestamping. Applications which need HW timestamping with a synchronised
clock (e.g. a PTP daemon) should use a virtual clock running on top of the
physical clock created by writing to _/sys/class/ptp/ptpX/n_vclocks_. This
feature is available on Linux 5.14 and newer.
+ +
If the kernel supports software timestamping, it will be enabled for all If the kernel supports software timestamping, it will be enabled for all
interfaces. The source of timestamps (i.e. hardware, kernel, or daemon) is interfaces. The source of timestamps (i.e. hardware, kernel, or daemon) is
@@ -2359,15 +2542,20 @@ _all_::::
Enables timestamping of all received packets. Enables timestamping of all received packets.
_ntp_:::: _ntp_::::
Enables timestamping of received NTP packets. Enables timestamping of received NTP packets.
_ptp_::::
Enables timestamping of received PTP packets.
_none_:::: _none_::::
Disables timestamping of received packets. Disables timestamping of received packets.
{blank}::: {blank}:::
The most specific filter for timestamping NTP packets which is supported by the The most specific filter for timestamping of NTP packets supported by the NIC
NIC is selected by default. Some NICs can timestamp only PTP packets, which is selected by default. Some NICs can timestamp PTP packets only. By default,
limits the selection to the _none_ filter. Forcing timestamping of all packets they will be configured with the _none_ filter and expected to provide hardware
with the _all_ filter when the NIC supports both _all_ and _ntp_ filters can be timestamps for transmitted packets only. Timestamping of PTP packets is useful
useful when packets are received from or on a non-standard UDP port (e.g. with NTP-over-PTP enabled by the <<chrony.conf.adoc#ptpport,*ptpport*>>
specified by the *port* directive). directive, or when another application is receiving PTP packets on the
interface. Forcing timestamping of all packets with the _all_ filter could be
useful if the NIC supported both the _all_ and _ntp_ filters, and it should
timestamp both NTP and PTP packets, or NTP packets on a different UDP port.
{blank}:: {blank}::
+ +
Examples of the directive are: Examples of the directive are:
@@ -2411,7 +2599,7 @@ The type is a name of a cryptographic hash function or cipher which is used to
generate and verify the MAC. The default type is *MD5*, which is always generate and verify the MAC. The default type is *MD5*, which is always
supported. supported.
If *chronyd* was built with enabled support for hashing using a crypto library If *chronyd* was built with enabled support for hashing using a crypto library
(nettle, nss, or libtomcrypt), the following functions are available: *MD5*, (Nettle, GnuTLS, NSS, or LibTomCrypt), the following functions are available: *MD5*,
*SHA1*, *SHA256*, *SHA384*, *SHA512*. Depending on which library and version is *SHA1*, *SHA256*, *SHA384*, *SHA512*. Depending on which library and version is
*chronyd* using, some of the following hash functions and ciphers may *chronyd* using, some of the following hash functions and ciphers may
also be available: also be available:
@@ -2442,12 +2630,9 @@ under which *chronyd* is normally running (to allow *chronyd* to re-read the
file when the <<chronyc.adoc#rekey,*rekey*>> command is issued by *chronyc*). file when the <<chronyc.adoc#rekey,*rekey*>> command is issued by *chronyc*).
[[lock_all]]*lock_all*:: [[lock_all]]*lock_all*::
The *lock_all* directive will lock chronyd into RAM so that it will never be The *lock_all* directive will lock the *chronyd* process into RAM so that it
paged out. This mode is supported on Linux, FreeBSD, NetBSD, and Solaris. This will never be paged out. This can result in lower and more consistent latency.
directive uses the POSIX *mlockall()* system call to prevent *chronyd* from The directive is supported on Linux, FreeBSD, NetBSD, and illumos.
ever being swapped out. This should result in lower and more consistent
latency. It should not have significant impact on performance as *chronyd's*
memory usage is modest. The *mlockall(2)* man page has more details.
[[pidfile]]*pidfile* _file_:: [[pidfile]]*pidfile* _file_::
Unless *chronyd* is started with the *-Q* option, it writes its process ID Unless *chronyd* is started with the *-Q* option, it writes its process ID
@@ -2460,8 +2645,34 @@ e.g.:
pidfile /run/chronyd.pid pidfile /run/chronyd.pid
---- ----
[[ptpport]]*ptpport* _port_::
The *ptpport* directive enables *chronyd* to send and receive NTP messages
contained in PTP event messages (NTP-over-PTP) to enable hardware timestamping
on NICs which cannot timestamp NTP packets, but can timestamp unicast PTP
packets. The port recognized by the NICs is 319 (PTP event port). The default
value is 0 (disabled).
+
The NTP-over-PTP support is experimental. The protocol and configuration can
change in future. It should be used only in local networks and expected to work
only between servers and clients running the same version of *chronyd*.
+
The PTP port will be open even if *chronyd* is not configured to operate as a
server or client. The directive does not change the default protocol of
specified NTP sources. Each NTP source that should use NTP-over-PTP needs to
be specified with the *port* option set to the PTP port. To actually enable
hardware timestamping on NICs which can timestamp PTP packets only, the
*rxfilter* option of the *hwtimestamp* directive needs to be set to _ptp_.
+
An example of client configuration is:
+
----
server foo.example.net minpoll 0 maxpoll 0 xleave port 319
hwtimestamp * rxfilter ptp
ptpport 319
----
[[sched_priority]]*sched_priority* _priority_:: [[sched_priority]]*sched_priority* _priority_::
On Linux, FreeBSD, NetBSD, and Solaris, the *sched_priority* directive will On Linux, FreeBSD, NetBSD, and illumos, the *sched_priority* directive will
select the SCHED_FIFO real-time scheduler at the specified priority (which must select the SCHED_FIFO real-time scheduler at the specified priority (which must
be between 0 and 100). On macOS, this option must have either a value of 0 (the be between 0 and 100). On macOS, this option must have either a value of 0 (the
default) to disable the thread time constraint policy or 1 for the policy to be default) to disable the thread time constraint policy or 1 for the policy to be
@@ -2487,7 +2698,7 @@ The *user* directive sets the name of the system user to which *chronyd* will
switch after start in order to drop root privileges. switch after start in order to drop root privileges.
+ +
On Linux, *chronyd* needs to be compiled with support for the *libcap* library. On Linux, *chronyd* needs to be compiled with support for the *libcap* library.
On macOS, FreeBSD, NetBSD and Solaris *chronyd* forks into two processes. On macOS, FreeBSD, NetBSD and illumos *chronyd* forks into two processes.
The child process retains root privileges, but can only perform a very limited The child process retains root privileges, but can only perform a very limited
range of privileged system calls on behalf of the parent. range of privileged system calls on behalf of the parent.
+ +
@@ -2617,22 +2828,22 @@ This section shows how to configure *chronyd* for computers that never have
network connectivity to any computer which ultimately derives its time from a network connectivity to any computer which ultimately derives its time from a
reference clock. reference clock.
In this situation, one computer is selected to be the master timeserver. The In this situation, one computer is selected to be the primary timeserver. The
other computers are either direct clients of the master, or clients of clients. other computers are either direct clients of the server, or clients of clients.
The <<local,*local*>> directive enables a local reference mode, which allows The <<local,*local*>> directive enables a local reference mode, which allows
*chronyd* to appear synchronised even when it is not. *chronyd* to appear synchronised even when it is not.
The rate value in the master's drift file needs to be set to the average rate The rate value in the server's drift file needs to be set to the average rate
at which the master gains or loses time. *chronyd* includes support for this, at which the server gains or loses time. *chronyd* includes support for this,
in the form of the <<manual,*manual*>> directive and the in the form of the <<manual,*manual*>> directive and the
<<chronyc.adoc#settime,*settime*>> command in the *chronyc* program. <<chronyc.adoc#settime,*settime*>> command in the *chronyc* program.
If the master is rebooted, *chronyd* can re-read the drift rate from the drift If the server is rebooted, *chronyd* can re-read the drift rate from the drift
file. However, the master has no accurate estimate of the current time. To get file. However, the server has no accurate estimate of the current time. To get
around this, the system can be configured so that the master can initially set around this, the system can be configured so that the server can initially set
itself to a '`majority-vote`' of selected clients' times; this allows the itself to a '`majority-vote`' of selected clients' times; this allows the
clients to '`flywheel`' the master while it is rebooting. clients to '`flywheel`' the server while it is rebooting.
The <<smoothtime,*smoothtime*>> directive is useful when the clocks of the The <<smoothtime,*smoothtime*>> directive is useful when the clocks of the
clients need to stay close together when the local time is adjusted by the clients need to stay close together when the local time is adjusted by the
@@ -2641,8 +2852,8 @@ activated by the <<chronyc.adoc#smoothtime,*smoothtime activate*>> command when
the local time is ready to be served. After that point, any adjustments will be the local time is ready to be served. After that point, any adjustments will be
smoothed out. smoothed out.
A typical configuration file for the master (called _master_) might be A typical configuration file for the server (called _ntp.local_) might be
(assuming the clients and the master are in the _192.168.165.x_ subnet): (assuming the clients and the server are in the _192.168.165.x_ subnet):
---- ----
initstepslew 1 client1 client3 client6 initstepslew 1 client1 client3 client6
@@ -2654,11 +2865,11 @@ smoothtime 400 0.01
rtcsync rtcsync
---- ----
For the clients that have to resynchronise the master when it restarts, For the clients that have to resynchronise the server when it restarts,
the configuration file might be: the configuration file might be:
---- ----
server master iburst server ntp.local iburst
driftfile @CHRONYVARDIR@/drift driftfile @CHRONYVARDIR@/drift
allow 192.168.165.0/24 allow 192.168.165.0/24
makestep 1.0 3 makestep 1.0 3
@@ -2668,22 +2879,22 @@ rtcsync
The rest of the clients would be the same, except that the *allow* directive is The rest of the clients would be the same, except that the *allow* directive is
not required. not required.
If there is no suitable computer to be designated as the master, or there is a If there is no suitable computer to be designated as the primary server, or
requirement to keep the clients synchronised even when it fails, the *orphan* there is a requirement to keep the clients synchronised even when it fails, the
option of the *local* directive enables a special mode where the master is *orphan* option of the *local* directive enables a special mode where the
selected from multiple computers automatically. They all need to use the same server is selected from multiple computers automatically. They all need to use
*local* configuration and poll one another. The server with the smallest the same *local* configuration and poll one another. The server with the
reference ID (which is based on its IP address) will take the role of the smallest reference ID (which is based on its IP address) will take the role of
master and others will be synchronised to it. When it fails, the server with the primary server and others will be synchronised to it. When it fails, the
the second smallest reference ID will take over and so on. server with the second smallest reference ID will take over and so on.
A configuration file for the first server might be (assuming there are three A configuration file for the first server might be (assuming there are three
servers called _master1_, _master2_, and _master3_): servers called _ntp1.local_, _ntp2.local_, and _ntp3.local_):
---- ----
initstepslew 1 master2 master3 initstepslew 1 ntp2.local ntp3.local
server master2 server ntp2.local
server master3 server ntp3.local
driftfile @CHRONYVARDIR@/drift driftfile @CHRONYVARDIR@/drift
local stratum 8 orphan local stratum 8 orphan
manual manual

View File

@@ -78,11 +78,10 @@ This option disables resolving of IP addresses to hostnames, e.g. to avoid slow
DNS lookups. Long addresses will not be truncated to fit into the column. DNS lookups. Long addresses will not be truncated to fit into the column.
*-N*:: *-N*::
This option enables printing of the original names of NTP sources that were This option enables printing of original hostnames or IP addresses of NTP
specified in the configuration file, or *chronyc* commands, and are internally sources that were specified in the configuration file, or *chronyc* commands.
used by *chronyd*. Without the *-n* and *-N* option, the names of NTP sources Without the *-n* and *-N* option, the printed hostnames are obtained from
are obtained from reverse DNS lookups and can be different from the original reverse DNS lookups and can be different from the specified hostnames.
names.
*-c*:: *-c*::
This option enables printing of reports in a comma-separated values (CSV) This option enables printing of reports in a comma-separated values (CSV)
@@ -100,12 +99,15 @@ With this option multiple commands can be specified. Each argument will be
interpreted as a whole command. interpreted as a whole command.
*-h* _host_:: *-h* _host_::
This option allows the user to specify which host (or comma-separated list of This option specifies the host to be contacted by *chronyc*. It can be
addresses) running the *chronyd* program is to be contacted. This allows for specified with a hostname, IP address, or path to the local Unix domain socket.
remote monitoring, without having to connect over SSH to the other host first. Multiple values can be specified as a comma-separated list to provide a
fallback.
+ +
The default is to contact *chronyd* running on the same host where The default value is _@CHRONYRUNDIR@/chronyd.sock,127.0.0.1,::1_, i.e. the host
*chronyc* is being run. where *chronyc* is being run. First, it tries to connect to the Unix domain
socket and if that fails (e.g. due to running under a non-root user), it
will try to connect to 127.0.0.1 and then ::1.
*-p* _port_:: *-p* _port_::
This option allows the user to specify the UDP port number which the target This option allows the user to specify the UDP port number which the target
@@ -449,6 +451,7 @@ states are reported.
The following states indicate the source is not considered selectable for The following states indicate the source is not considered selectable for
synchronisation: synchronisation:
* _N_ - has the *noselect* option. * _N_ - has the *noselect* option.
* _s_ - is not synchronised.
* _M_ - does not have enough measurements. * _M_ - does not have enough measurements.
* _d_ - has a root distance larger than the maximum distance (configured by the * _d_ - has a root distance larger than the maximum distance (configured by the
<<chrony.conf.adoc#maxdistance,*maxdistance*>> directive). <<chrony.conf.adoc#maxdistance,*maxdistance*>> directive).
@@ -696,7 +699,8 @@ packets sent to the source is more variable than the delay of packets sent
from the source back. from the source back.
*NTP tests*::: *NTP tests*:::
Results of RFC 5905 tests 1 through 3, 5 through 7, and tests for maximum Results of RFC 5905 tests 1 through 3, 5 through 7, and tests for maximum
delay, delay ratio, delay dev ratio, and synchronisation loop. delay, delay ratio, delay dev ratio (or delay quantile), and synchronisation
loop.
*Interleaved*::: *Interleaved*:::
This shows if the response was in the interleaved mode. This shows if the response was in the interleaved mode.
*Authenticated*::: *Authenticated*:::
@@ -1108,13 +1112,9 @@ The columns are as follows:
received/accepted. received/accepted.
[[serverstats]]*serverstats*:: [[serverstats]]*serverstats*::
The *serverstats* command displays how many valid NTP and command requests, and The *serverstats* command displays NTP and command server statistics.
NTS-KE connections, *chronyd* operating as a server received from clients, and +
how many of them were dropped due to rate limiting. It also displays how many An example of the output is shown below.
client log records were dropped due to the memory limit configured by the
<<chrony.conf.adoc#clientloglimit,*clientloglimit*>> directive and how many of
the NTP requests (from those which were not dropped) were authenticated. An
example of the output is shown below.
+ +
---- ----
NTP packets received : 1598 NTP packets received : 1598
@@ -1125,7 +1125,47 @@ Client log records dropped : 0
NTS-KE connections accepted: 3 NTS-KE connections accepted: 3
NTS-KE connections dropped : 0 NTS-KE connections dropped : 0
Authenticated NTP packets : 189 Authenticated NTP packets : 189
Interleaved NTP packets : 43
NTP timestamps held : 44
NTP timestamp span : 120
---- ----
+
The fields have the following meaning:
+
*NTP packets received*:::
The number of valid NTP requests received by the server.
*NTP packets dropped*:::
The number of NTP requests dropped by the server due to rate limiting
(configured by the <<chrony.conf.adoc#ratelimit,*ratelimit*>> directive).
*Command packets received*:::
The number of command requests received by the server.
*Command packets dropped*:::
The number of command requests dropped by the server due to rate limiting
(configured by the <<chrony.conf.adoc#cmdratelimit,*cmdratelimit*>> directive).
*Client log records dropped*:::
The number of client log records dropped by the server to limit the memory use
(configured by the <<chrony.conf.adoc#clientloglimit,*clientloglimit*>>
directive).
*NTS-KE connections accepted*:::
The number of NTS-KE connections accepted by the server.
*NTS-KE connections dropped*:::
The number of NTS-KE connections dropped by the server due to rate limiting
(configured by the <<chrony.conf.adoc#ntsratelimit,*ntsratelimit*>> directive).
*Authenticated NTP packets*:::
The number of received NTP requests that were authenticated (with a symmetric
key or NTS).
*Interleaved NTP packets*:::
The number of received NTP requests that were detected to be in the interleaved
mode.
*NTP timestamps held*:::
The number of pairs of receive and transmit timestamps that the server is
currently holding in memory for clients using the interleaved mode.
*NTP timestamp span*:::
The interval (in seconds) covered by the currently held NTP timestamps.
{blank}::
+
Note that the numbers reported by this overflow to zero after 4294967295
(32-bit values).
[[allow]]*allow* [*all*] [_subnet_]:: [[allow]]*allow* [*all*] [_subnet_]::
The effect of the allow command is identical to the The effect of the allow command is identical to the
@@ -1134,11 +1174,8 @@ The effect of the allow command is identical to the
The syntax is illustrated in the following examples: The syntax is illustrated in the following examples:
+ +
---- ----
allow foo.example.net allow 1.2.3.4
allow all 1.2 allow all 3.4.5.0/24
allow 3.4.5
allow 6.7.8/22
allow 6.7.8.9/22
allow 2001:db8:789a::/48 allow 2001:db8:789a::/48
allow 0/0 allow 0/0
allow ::/0 allow ::/0
@@ -1153,11 +1190,8 @@ The effect of the allow command is identical to the
The syntax is illustrated in the following examples: The syntax is illustrated in the following examples:
+ +
---- ----
deny foo.example.net deny 1.2.3.4
deny all 1.2 deny all 3.4.5.0/24
deny 3.4.5
deny 6.7.8/22
deny 6.7.8.9/22
deny 2001:db8:789a::/48 deny 2001:db8:789a::/48
deny 0/0 deny 0/0
deny ::/0 deny ::/0

View File

@@ -100,7 +100,7 @@ directive in the configuration file. This option is useful if you want to stop
and restart *chronyd* briefly for any reason, e.g. to install a new version. and restart *chronyd* briefly for any reason, e.g. to install a new version.
However, it should be used only on systems where the kernel can maintain clock However, it should be used only on systems where the kernel can maintain clock
compensation whilst not under *chronyd*'s control (i.e. Linux, FreeBSD, NetBSD, compensation whilst not under *chronyd*'s control (i.e. Linux, FreeBSD, NetBSD,
Solaris, and macOS 10.13 or later). illumos, and macOS 10.13 or later).
*-R*:: *-R*::
When this option is used, the <<chrony.conf.adoc#initstepslew,*initstepslew*>> When this option is used, the <<chrony.conf.adoc#initstepslew,*initstepslew*>>
@@ -141,7 +141,7 @@ after start in order to drop root privileges. It overrides the
_@DEFAULT_USER@_. _@DEFAULT_USER@_.
+ +
On Linux, *chronyd* needs to be compiled with support for the *libcap* library. On Linux, *chronyd* needs to be compiled with support for the *libcap* library.
On macOS, FreeBSD, NetBSD and Solaris *chronyd* forks into two processes. On macOS, FreeBSD, NetBSD, and illumos *chronyd* forks into two processes.
The child process retains root privileges, but can only perform a very limited The child process retains root privileges, but can only perform a very limited
range of privileged system calls on behalf of the parent. range of privileged system calls on behalf of the parent.
@@ -156,29 +156,40 @@ not recommended when the configuration is not known, or at least limited to
specific directives. specific directives.
*-F* _level_:: *-F* _level_::
This option configures a system call filter when *chronyd* is compiled with This option configures system call filters loaded by *chronyd* processes if it
support for the Linux secure computing (seccomp) facility. In level 1 the was compiled with support for the Linux secure computing (seccomp) facility.
process is killed when a forbidden system call is made, in level -1 the SIGSYS Three levels are defined: 0, 1, 2. The filters are disabled at level 0. At
signal is thrown instead and in level 0 the filter is disabled. The default levels 1 and 2, *chronyd* will be killed if it makes a system call which is
value is 0. blocked by the filters. The level can be specified as a negative number to
trigger the SIGSYS signal instead of SIGKILL, which can be useful for
debugging. The default value is 0.
+ +
It is recommended to enable the filter only when it is known to work on the At level 1, the filters allow only selected system calls that are normally
version of the system where *chrony* is installed as the filter needs to allow expected to be made by *chronyd*. Other system calls are blocked. This level is
also system calls made from libraries that *chronyd* is using (e.g. libc) and recommended only if it is known to work on the version of the system where
different versions or implementations of the libraries might make different *chrony* is installed. The filters need to allow also system calls made by
system calls. If the filter is missing some system call, *chronyd* could be libraries that *chronyd* is using (e.g. libc), but different versions or
killed even in normal operation. implementations of the libraries might make different system calls. If the
filters are missing a system call, *chronyd* could be killed even in normal
operation.
+
At level 2, the filters block only a small number of specific system calls
(e.g. fork and exec). This approach should avoid false positives, but the
protection of the system against a compromised *chronyd* process is much more
limited.
+
The filters cannot be enabled with the *mailonchange* directive.
*-P* _priority_:: *-P* _priority_::
On Linux, this option will select the SCHED_FIFO real-time scheduler at the On Linux, FreeBSD, NetBSD, and illumos this option will select the SCHED_FIFO
specified priority (which must be between 0 and 100). On macOS, this option real-time scheduler at the specified priority (which must be between 0 and
must have either a value of 0 to disable the thread time 100). On macOS, this option must have either a value of 0 to disable the thread
constraint policy or 1 for the policy to be enabled. Other systems do not time constraint policy or 1 for the policy to be enabled. Other systems do not
support this option. The default value is 0. support this option. The default value is 0.
*-m*:: *-m*::
This option will lock *chronyd* into RAM so that it will never be paged out. 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, FreeBSD, NetBSD, and illumos.
*-x*:: *-x*::
This option disables the control of the system clock. *chronyd* will not try to This option disables the control of the system clock. *chronyd* will not try to

View File

@@ -1,7 +1,7 @@
// This file is part of chrony // This file is part of chrony
// //
// Copyright (C) Richard P. Curnow 1997-2003 // Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Miroslav Lichvar 2014-2016, 2020 // Copyright (C) Miroslav Lichvar 2014-2016, 2020-2021
// //
// This program is free software; you can redistribute it and/or modify // 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 // it under the terms of version 2 of the GNU General Public License as
@@ -49,9 +49,11 @@ website.
First, the client needs to know which NTP servers it should ask for the current First, the client needs to know which NTP servers it should ask for the current
time. They are specified by the `server` or `pool` directive. The `pool` time. They are specified by the `server` or `pool` directive. The `pool`
directive can be used for names that resolve to multiple addresses. For good directive is used with names that resolve to multiple addresses of different
reliability the client should have at least three servers. The `iburst` option servers. For reliable operation, the client should have at least three servers.
speeds up the initial synchronisation.
The `iburst` option enables a burst of requests to speed up the initial
synchronisation.
To stabilise the initial synchronisation on the next start, the estimated drift To stabilise the initial synchronisation on the next start, the estimated drift
of the system clock is saved to a file specified by the `driftfile` directive. of the system clock is saved to a file specified by the `driftfile` directive.
@@ -67,7 +69,7 @@ next boot from the RTC, the `rtcsync` directive enables a mode in which the
system time is periodically copied to the RTC. It is supported on Linux and system time is periodically copied to the RTC. It is supported on Linux and
macOS. macOS.
If you want to use public NTP servers from the If you wanted to use public NTP servers from the
https://www.pool.ntp.org/[pool.ntp.org] project, the minimal _chrony.conf_ file https://www.pool.ntp.org/[pool.ntp.org] project, the minimal _chrony.conf_ file
could be: could be:
@@ -80,9 +82,16 @@ rtcsync
=== How do I make an NTP server? === How do I make an NTP server?
You need to add an `allow` directive to the _chrony.conf_ file in order to open By default, `chronyd` does not operate as an NTP server. You need to add an
the NTP port and allow `chronyd` to reply to client requests. `allow` with no `allow` directive to the _chrony.conf_ file in order for `chronyd` to open the
specified subnet allows access from all IPv4 and IPv6 addresses. server NTP port and respond to client requests.
----
allow 192.168.1.0/24
----
An `allow` directive with no specified subnet allows access from all IPv4 and
IPv6 addresses.
=== Should all computers on a LAN be clients of an external server? === Should all computers on a LAN be clients of an external server?
@@ -128,21 +137,136 @@ versions or implementations of the libraries might make different system calls.
If the filter is missing some system call, `chronyd` could be killed even in If the filter is missing some system call, `chronyd` could be killed even in
normal operation. normal operation.
=== How can I make the system clock more secure?
An NTP client synchronising the system clock to an NTP server is susceptible to
various attacks, which can break applications and network protocols relying on
accuracy of the clock (e.g. DNSSEC, Kerberos, TLS, WireGuard).
Generally, a man-in-the-middle (MITM) attacker between the client and server
can
* make fake responses, or modify real responses from the server, to create an
arbitrarily large time and frequency offset, make the server appear more
accurate, insert a leap second, etc.
* delay the requests and/or responses to create a limited time offset and
temporarily also a limited frequency offset
* drop the requests or responses to prevent updates of the clock with new
measurements
* redirect the requests to a different server
The attacks can be combined for a greater effect. The attacker can delay
packets to create a significant frequency offset first and then drop all
subsequent packets to let the clock quickly drift away from the true time.
The attacker might also be able to control the server's clock.
Some attacks cannot be prevented. Monitoring is needed for detection, e.g. the
reachability register in the `sources` report shows missing packets. The extent
to which the attacker can control the client's clock depends on its
configuration.
Enable authentication to prevent `chronyd` from accepting modified, fake, or
redirected packets. It can be enabled with a symmetric key specified by the
`key` option, or Network Time Security (NTS) by the `nts` option (supported
since `chrony` version 4.0). The server needs to support the selected
authentication mechanism. Symmetric keys have to be configured on both client
and server, and each client must have its own key (one per server).
The maximum offset that the attacker can insert in an NTP measurement by
delaying packets can be limited by the `maxdelay` option. The default value is
3 seconds. The measured delay is reported as the peer delay in the `ntpdata`
report and `measurements` log. Set the `maxdelay` option to a value larger than
the maximum value that is normally observed. Note that the delay can increase
significantly even when not under an attack, e.g. when the network is congested
or the routing has changed.
The maximum accepted change in time offset between clock updates can be limited
by the `maxchange` directive. Larger changes in the offset will be ignored or
cause `chronyd` to exit. Note that the attacker can get around this limit by
splitting the offset into multiple smaller offsets and/or creating a large
frequency offset. When this directive is used, `chronyd` will have to be
restarted after a successful attack. It will not be able to recover on its own.
It must not be restarted automatically (e.g. by the service manager).
The impact of a large accepted time offset can be reduced by disabling clock
steps, i.e. by not using the `makestep` and `initstepslew` directives. The
offset will be slowly corrected by speeding up or slowing down the clock at a
rate which can be limited by the `maxslewrate` directive. Disabling clock steps
completely is practical only if the clock cannot gain a larger error on its
own, e.g. when the computer is shut down or suspended, and the `maxslewrate`
limit is large enough to correct an expected error in an acceptable time. The
`rtcfile` directive with the `-s` option can be used to compensate for the RTC
drift.
A more practical approach is to enable `makestep` for a limited number of clock
updates (the 2nd argument of the directive) and limit the offset change in all
updates by the `maxchange` directive. The attacker will be able to make only a
limited step and only if the attack starts in a short window after booting the
computer, or when `chronyd` is restarted without the `-R` option.
The frequency offset can be limited by the `maxdrift` directive. The measured
frequency offset is reported in the drift file, `tracking` report, and
`tracking` log. Set `maxdrift` to a value larger than the maximum absolute
value that is normally observed. Note that the frequency of the clock can
change due to aging of the crystal, differences in calibration of the clock
source between reboots, migrated virtual machine, etc. A typical computer clock
has a drift smaller than 100 parts per million (ppm), but much larger drifts
are possible (e.g. in some virtual machines).
Use only trusted servers, which you expect to be well configured and managed,
using authentication for their own servers, etc. Use multiple servers, ideally
in different locations. The attacker will have to deal with a majority of the
servers in order to pass the source selection and update the clock with a large
offset. Use the `minsources` directive to increase the required number of
selectable sources to make the selection more robust.
Do not specify servers as peers. The symmetric mode is less secure than the
client/server mode. If not authenticated, it is vulnerable to off-path
denial-of-service attacks, and even when it is authenticated, it is still
susceptible to replay attacks.
Mixing of authenticated and unauthenticated servers should generally be
avoided. If mixing is necessary (e.g. for a more accurate and stable
synchronisation to a closer server which does not support authentication), the
authenticated servers should be configured as trusted and required to not allow
the unauthenticated servers to override the authenticated servers in the source
selection. Since `chrony` version 4.0, the selection options are enabled in
such a case automatically. This behaviour can be disabled or modified by the
`authselmode` directive.
An example of a client configuration limiting the impact of the attacks could
be
----
server foo.example.net iburst nts maxdelay 0.1
server bar.example.net iburst nts maxdelay 0.2
server baz.example.net iburst nts maxdelay 0.05
server qux.example.net iburst nts maxdelay 0.1
server quux.example.net iburst nts maxdelay 0.1
minsources 3
maxchange 100 0 0
makestep 0.001 1
maxdrift 100
maxslewrate 100
driftfile /var/lib/chrony/drift
ntsdumpdir /var/lib/chrony
rtcsync
----
=== How can I improve the accuracy of the system clock with NTP sources? === How can I improve the accuracy of the system clock with NTP sources?
Select NTP servers that are well synchronised, stable and close to your Select NTP servers that are well synchronised, stable and close to your
network. It is better to use more than one server, three or four is usually network. It is better to use more than one server. Three or four is usually
recommended as the minimum, so `chronyd` can detect servers that serve false recommended as the minimum, so `chronyd` can detect servers that serve false
time and combine measurements from multiple sources. time and combine measurements from multiple sources.
If you have a network card with hardware timestamping supported on Linux, it If you have a network card with hardware timestamping supported on Linux, it
can be enabled by the `hwtimestamp` directive in the _chrony.conf_ file. It can be enabled by the `hwtimestamp` directive. It should make local receive and
should make local receive and transmit timestamps of NTP packets much more transmit timestamps of NTP packets much more stable and accurate.
accurate.
There are also useful options which can be set in the `server` directive, they The `server` directive has some useful options: `minpoll`, `maxpoll`,
are `minpoll`, `maxpoll`, `polltarget`, `maxdelay`, `maxdelayratio`, `polltarget`, `maxdelay`, `maxdelayratio`, `maxdelaydevratio`, `xleave`,
`maxdelaydevratio`, and `xleave`. `filter`.
The first three options set the minimum and maximum allowed polling interval, The first three options set the minimum and maximum allowed polling interval,
and how should be the actual interval adjusted in the specified range. Their and how should be the actual interval adjusted in the specified range. Their
@@ -177,7 +301,7 @@ LAN could be
server ntp.local minpoll 2 maxpoll 4 polltarget 30 server ntp.local minpoll 2 maxpoll 4 polltarget 30
---- ----
The maxdelay options are useful to ignore measurements with an unusally large The maxdelay options are useful to ignore measurements with an unusually large
delay (e.g. due to congestion in the network) and improve the stability of the delay (e.g. due to congestion in the network) and improve the stability of the
synchronisation. The `maxdelaydevratio` option could be added to the example synchronisation. The `maxdelaydevratio` option could be added to the example
with local NTP server with local NTP server
@@ -187,8 +311,8 @@ server ntp.local minpoll 2 maxpoll 4 polltarget 30 maxdelaydevratio 2
---- ----
If your server supports the interleaved mode (e.g. it is running `chronyd`), If your server supports the interleaved mode (e.g. it is running `chronyd`),
the `xleave` option should be added to the `server` directive in order to allow the `xleave` option should be added to the `server` directive to enable the
the server to send the client more accurate transmit timestamps (kernel or server to provide the client with more accurate transmit timestamps (kernel or
preferably hardware). For example: preferably hardware). For example:
---- ----
@@ -221,6 +345,24 @@ server ntp.local minpoll -6 maxpoll -6 filter 15 xleave
hwtimestamp eth0 minpoll -6 hwtimestamp eth0 minpoll -6
---- ----
Since `chrony` version 4.3, the minimum `minpoll` is -7 and a filter using a
long-term estimate of a delay quantile can be enabled by the `maxdelayquant`
option to replace the default `maxdelaydevratio` filter, which is sensitive to
outliers corrupting the minimum delay. For example:
----
server ntp.local minpoll -7 maxpoll -7 filter 31 maxdelayquant 0.3 xleave
----
As an experimental feature added in version 4.2, `chronyd` supports an NTPv4
extension field containing an additional timestamp to enable frequency transfer
and significantly improve stability of synchronisation. It can be enabled by
the `extfield F323` option. For example:
----
server ntp.local minpoll 0 maxpoll 0 xleave extfield F323
----
=== Does `chronyd` have an ntpdate mode? === Does `chronyd` have an ntpdate mode?
Yes. With the `-q` option `chronyd` will set the system clock once and exit. Yes. With the `-q` option `chronyd` will set the system clock once and exit.
@@ -237,7 +379,7 @@ well synchronised and responding to all requests. If not synchronised or
responding, it would take about 10 seconds for `chronyd` to give up and exit responding, it would take about 10 seconds for `chronyd` to give up and exit
with a non-zero status. A faster configuration is possible. A single server can with a non-zero status. A faster configuration is possible. A single server can
be used instead of four servers, the number of measurements can be reduced with be used instead of four servers, the number of measurements can be reduced with
the `maxsamples` option to one (supported in `chrony` version 4.0), and a the `maxsamples` option to one (supported since `chrony` version 4.0), and a
timeout can be specified with the `-t` option. The following command would take timeout can be specified with the `-t` option. The following command would take
only up to about one second. only up to about one second.
@@ -284,18 +426,49 @@ and be unsuitable for monitoring.
=== Can NTP server be separated from NTP client? === Can NTP server be separated from NTP client?
Yes, it is possible to run multiple instances of `chronyd` on the same Yes, it is possible to run multiple instances of `chronyd` on a computer at the
computer. One can be configured as an NTP client, and another as a server. They same time. One can operate primarily as an NTP client to synchronise the system
need to use different pidfiles, NTP ports, command ports, and Unix domain clock and another as a server for other computers. If they use the same
command sockets. The server instance should be started with the `-x` option to filesystem, they need to be configured with different pidfiles, Unix domain
avoid touching the clock. It can be configured to serve the system time with command sockets, and any other file or directory specified in the configuration
the `local` directive, or synchronise its NTP clock to the client instance file. If they run in the same network namespace, they need to use different NTP
running on localhost using a non-standard NTP port. and command ports, or bind the ports to different addresses or interfaces.
On Linux, starting with `chrony` version 4.0, it is also possible to run The server instance should be started with the `-x` option to prevent it from
multiple server instances sharing a port to utilise multiple cores of the CPU. adjusting the system clock and interfering with the client instance. It can be
Note that the client/server interleaved mode requires that all packets from an configured as a client to synchronise its NTP clock to other servers, or the
address are handled by the same server instance. client instance running on the same computer. In the latter case, the `copy`
option (added in `chrony` version 4.1) can be used to assume the reference ID
and stratum of the client instance, which enables detection of synchronisation
loops with its own clients.
On Linux, starting with `chrony` version 4.0, it is possible to run multiple
server instances sharing a port to better utilise multiple cores of the CPU.
Note that for rate limiting and client/server interleaved mode to work well
it is necessary that all packets received from the same address are handled by
the same server instance.
An example configuration of the client instance could be
----
pool pool.ntp.org iburst
allow 127.0.0.1
port 11123
driftfile /var/lib/chrony/drift
makestep 1 3
rtcsync
----
and configuration of the first server instance could be
----
server 127.0.0.1 port 11123 minpoll 0 maxpoll 0 copy
allow
cmdport 11323
bindcmdaddress /var/run/chrony/chronyd-server1.sock
pidfile /var/run/chronyd-server1.pid
driftfile /var/lib/chrony/drift-server1
----
=== Should be a leap smear enabled on NTP server? === Should be a leap smear enabled on NTP server?
@@ -308,12 +481,71 @@ e.g. when the clients cannot be configured to handle the leap seconds as
needed, or their number is so large that configuring them all would be needed, or their number is so large that configuring them all would be
impractical. The clients should use only one leap-smearing server, or multiple impractical. The clients should use only one leap-smearing server, or multiple
identically configured leap-smearing servers. Note that some clients can get identically configured leap-smearing servers. Note that some clients can get
leap seconds from external sources (e.g. with the `leapsectz` directive in leap seconds from other sources (e.g. with the `leapsectz` directive in
`chrony`) and they will not work correctly with a leap smearing server. `chrony`) and they will not work correctly with a leap smearing server.
=== How should `chronyd` be configuration with `gpsd`?
A GPS or other GNSS receiver can be used as a reference clock with `gpsd`. It
can work as one or two separate time sources for each connected receiver. The
first time source is based on timestamping of messages sent by the receiver.
Typically, it is accurate to milliseconds. The other source is much more
accurate. It is timestamping a pulse-per-second (PPS) signal, usually connected
to a serial port (e.g. DCD pin) or GPIO pin.
If the PPS signal is connected to the serial port which is receiving messages
from the GPS/GNSS receiver, `gpsd` should detect and use it automatically. If
it is connected to a GPIO pin, or another serial port, the PPS device needs to
be specified on the command line as an additional data source. On Linux, the
`ldattach` utility can be used to create a PPS device for a serial device.
The message-based time source provided by `gpsd` is specified as a `SHM 0`
refclock, or other even number if `gpsd` is configured with multiple receivers.
The PPS-based time source is specified as a `SHM 1` refclock (or other odd
number), or `SOCK /var/run/chrony.DEV.sock` where `DEV` is the name of the
serial device (e.g. ttyS0).
With `chronyd` and `gpsd` both supporting PPS, and `gpsd` providing two
different refclocks for PPS, there are three different recommended
configurations:
----
# First option
refclock SOCK /var/run/chrony.ttyS0.sock refid GPS
# Second option
refclock SHM 1 refid GPS
# Third option
refclock PPS /dev/pps0 lock NMEA refid GPS
refclock SHM 0 offset 0.5 delay 0.1 refid NMEA noselect
----
Each option has some advantages:
* `SOCK` does not use polling (i.e. it can get samples earlier than `SHM`),
but it requires `gpsd` to be started after `chronyd` in order to connect to
its socket
* `SOCK` and `SHM 1` can be more accurate than `PPS` if `gpsd` corrects for the
sawtooth error provided by the receiver in serial data
* `PPS` can be used with higher PPS rates (specified by the `rate` option),
but it requires a second refclock or another time source to pair pulses
with seconds, and the `SHM 0` offset needs to be specified
<<using-pps-refclock,correctly>> to compensate for the message delay, while
`gpsd` can apply HW-specific information
If the PPS signal is not available, or cannot be used for some reason, the only
option is the message-based timing
----
refclock SHM 0 offset 0.5 delay 0.1 refid GPS
----
=== Does `chrony` support PTP? === Does `chrony` support PTP?
No, the Precision Time Protocol (PTP) is not supported and there are no plans No, the Precision Time Protocol (PTP) is not supported as a protocol for
synchronisation of clocks and there are no plans
to support it. It is a complex protocol, which shares some issues with the to support it. It is a complex protocol, which shares some issues with the
NTP broadcast mode. One of the main differences between NTP and PTP is that PTP NTP broadcast mode. One of the main differences between NTP and PTP is that PTP
was designed to be easily supported in hardware (e.g. network switches and was designed to be easily supported in hardware (e.g. network switches and
@@ -328,6 +560,32 @@ packets (enabled by the `hwtimestamp` directive) if the NIC can timestamp other
packets than PTP, which is usually the case at least for transmitted packets. packets than PTP, which is usually the case at least for transmitted packets.
The `ethtool -T` command can be used to verify the timestamping support. The `ethtool -T` command can be used to verify the timestamping support.
As an experimental feature added in version 4.2, `chrony` can use PTP as a
transport for NTP messages (NTP over PTP) to enable hardware timestamping on
hardware which can timestamp PTP packets only. It can be enabled by the
`ptpport` directive.
=== Why are client log records dropped before reaching `clientloglimit`?
The number of dropped client log records reported by the `serverstats` command
can be increasing before the number of clients reported by the `clients` command
reaches the maximum value corresponding to the memory limit set by the
`clientloglimit` directive.
This is due to the design of the data structure keeping the client records. It
is a hash table which can store only up to 16 colliding addresses per slot. If
a slot has more collisions and the table already has the maximum size, the
oldest record will be dropped and replaced by the new client.
Note that the size of the table is always a power of two and it can only grow.
The limit set by the `clientloglimit` directive takes into account that two
copies of the table exist when it is being resized. This means the actual
memory usage reported by `top` and other utilities can be significantly smaller
than the limit even when the maximum number of records is used.
The absolute maximum number of client records kept at the same time is
16777216.
=== What happened to the `commandkey` and `generatecommandkey` directives? === What happened to the `commandkey` and `generatecommandkey` directives?
They were removed in version 2.2. Authentication is no longer supported in the They were removed in version 2.2. Authentication is no longer supported in the
@@ -355,7 +613,6 @@ When `chronyd` is receiving responses from the servers, the output of the
this: this:
---- ----
210 Number of sources = 3
MS Name/IP address Stratum Poll Reach LastRx Last sample MS Name/IP address Stratum Poll Reach LastRx Last sample
=============================================================================== ===============================================================================
^* foo.example.net 2 6 377 34 +484us[ -157us] +/- 30ms ^* foo.example.net 2 6 377 34 +484us[ -157us] +/- 30ms
@@ -379,6 +636,19 @@ example:
0 sources with unknown address 0 sources with unknown address
---- ----
=== Is name resolution working correctly?
NTP servers specified by their hostname (instead of an IP address) have to have
their names resolved before `chronyd` can send any requests to them. If the
`activity` command prints a non-zero number of sources with unknown address,
there is an issue with the resolution. Typically, a DNS server is specified in
_/etc/resolv.conf_. Make sure it is working correctly.
Since `chrony` version 4.0, you can run `chronyc -N sources -a` command to
print all sources, even those that do not have a known address yet, with their
names as they were specified in the configuration. This can be useful to verify
that the names specified in the configuration are used as expected.
=== Is `chronyd` allowed to step the system clock? === Is `chronyd` allowed to step the system clock?
By default, `chronyd` adjusts the clock gradually by slowing it down or By default, `chronyd` adjusts the clock gradually by slowing it down or
@@ -405,6 +675,62 @@ to
makestep 1 -1 makestep 1 -1
---- ----
=== Using NTS?
The Network Time Security (NTS) mechanism uses Transport Layer Security (TLS)
to establish the keys needed for authentication of NTP packets.
Run the `authdata` command to check whether the key establishment was
successful:
----
# chronyc -N authdata
Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen
=========================================================================
foo.example.net NTS 1 15 256 33m 0 0 8 100
bar.example.net NTS 1 15 256 33m 0 0 8 100
baz.example.net NTS 1 15 256 33m 0 0 8 100
----
The KeyID, Type, and KLen columns should have non-zero values. If they are
zero, check the system log for error messages from `chronyd`. One possible
cause of failure is a firewall blocking the client's connection to the server's
TCP port 4460.
Another possible cause of failure is a certificate that is failing to verify
because the client's clock is wrong. This is a chicken-and-egg problem with NTS.
You might need to manually correct the date, or temporarily disable NTS, in
order to get NTS working. If your computer has an RTC and it is backed up by a
good battery, this operation should be needed only once, assuming the RTC will
be set periodically with the `rtcsync` directive, or compensated with the
`rtcfile` directive and the `-s` option.
If the computer does not have an RTC or battery, you can use the `-s` option
without `rtcfile` directive to restore time of the last shutdown or reboot from
the drift file. The clock will start behind the true time, but if the computer
was not shut down for too long and the server's certificate was not renewed too
close to its expiration, it should be sufficient for the time checks to
succeed.
If you run your own server, you can use a self-signed certificate covering
all dates where the client can start (e.g. years 1970-2100). The certificate
needs to be installed on the client and specified with the `ntstrustedcerts`
directive. The server can have multiple names and certificates. To avoid
trusting a certificate for too long, a new certificate can be added to the
server periodically (e.g. once per year) and the client can have the server
name and trusted certificate updated automatically (e.g. using a package
repository, or a cron script downloading the files directly from the server
over HTTPS). A client that was shut down for years will still be able to
synchronise its clock and perform the update as long as the server keeps
the old certificate.
As a last resort, you can disable the time checks by the `nocerttimecheck`
directive. This has some important security implications. To reduce the
security risk, you can use the `nosystemcert` and `ntstrustedcerts` directives
to disable the system's default trusted certificate authorities and trust only
a minimal set of selected authorities needed to validate the certificates of
used NTP servers.
=== Using a Windows NTP server? === Using a Windows NTP server?
A common issue with Windows NTP servers is that they report a very large root A common issue with Windows NTP servers is that they report a very large root
@@ -466,12 +792,14 @@ frequently, you can effectively disable the test by setting the
`maxdelaydevratio` option to a very large value (e.g. 1000000), or speed up the `maxdelaydevratio` option to a very large value (e.g. 1000000), or speed up the
recovery by increasing the clock error rate with the `maxclockerror` directive. recovery by increasing the clock error rate with the `maxclockerror` directive.
[[using-pps-refclock]]
=== Using a PPS reference clock? === Using a PPS reference clock?
A pulse-per-second (PPS) reference clock requires a non-PPS time source to A pulse-per-second (PPS) reference clock requires a non-PPS time source to
determine which second of UTC corresponds to each pulse. If it is another determine which second of UTC corresponds to each pulse. If it is another
reference clock specified with the `lock` option in the `refclock` directive, reference clock specified with the `lock` option in the `refclock` directive,
the offset between the two reference clocks must be smaller than 0.2 seconds in the offset between the two reference clocks must be smaller than 0.4 seconds
(0.2 seconds with `chrony` versions before 4.1) in
order for the PPS reference clock to work. With NMEA reference clocks it is order for the PPS reference clock to work. With NMEA reference clocks it is
common to have a larger offset. It needs to be corrected with the `offset` common to have a larger offset. It needs to be corrected with the `offset`
option. option.
@@ -490,7 +818,7 @@ foo.example.net 7 3 200 -2.991 16.141 -107us 492us
the offset of the NMEA source would need to be increased by about 0.504 the offset of the NMEA source would need to be increased by about 0.504
seconds. It does not have to be very accurate. As long as the offset of the seconds. It does not have to be very accurate. As long as the offset of the
NMEA reference clock stays below 0.2 seconds, the PPS reference clock should be NMEA reference clock stays below the limit, the PPS reference clock should be
able to determine the seconds corresponding to the pulses and allow the samples able to determine the seconds corresponding to the pulses and allow the samples
to be used for synchronisation. to be used for synchronisation.
@@ -546,6 +874,10 @@ in parentheses) on the `Reference ID` line.
Only by the source code. See _cmdmon.c_ (`chronyd` side) and _client.c_ Only by the source code. See _cmdmon.c_ (`chronyd` side) and _client.c_
(`chronyc` side). (`chronyc` side).
Note that this protocol is not compatible with the mode 6 or mode 7 protocol
supported by `ntpd`, i.e. the `ntpq` or `ntpdc` utility cannot be used to
monitor `chronyd`, and `chronyc` cannot be used to monitor `ntpd`.
== Real-time clock issues == Real-time clock issues
=== What is the real-time clock (RTC)? === What is the real-time clock (RTC)?
@@ -556,7 +888,7 @@ more than few seconds per day.
There are two approaches how `chronyd` can work with it. One is to use the There are two approaches how `chronyd` can work with it. One is to use the
`rtcsync` directive, which tells `chronyd` to enable a kernel mode which sets `rtcsync` directive, which tells `chronyd` to enable a kernel mode which sets
the RTC from the system clock every 11 minutes. `chronyd` itself won't touch the RTC from the system clock every 11 minutes. `chronyd` itself will not touch
the RTC. If the computer is not turned off for a long time, the RTC should the RTC. If the computer is not turned off for a long time, the RTC should
still be close to the true time when the system clock will be initialised from still be close to the true time when the system clock will be initialised from
it on the next boot. it on the next boot.
@@ -571,12 +903,12 @@ it is not strictly necessary if its only purpose is to set the system clock when
=== Does `hwclock` have to be disabled? === Does `hwclock` have to be disabled?
The `hwclock` program is often set-up by default in the boot and shutdown The `hwclock` program is run by default in the boot and/or shutdown
scripts with many Linux installations. With the kernel RTC synchronisation scripts in some Linux installations. With the kernel RTC synchronisation
(`rtcsync` directive), the RTC will be set also every 11 minutes as long as the (`rtcsync` directive), the RTC will be set also every 11 minutes as long as the
system clock is synchronised. If you want to use ``chronyd``'s RTC monitoring system clock is synchronised. If you want to use ``chronyd``'s RTC monitoring
(`rtcfile` directive), it is important to disable `hwclock` in the shutdown (`rtcfile` directive), it is important to disable `hwclock` in the shutdown
procedure. If you do not that, it will over-write the RTC with a new value, unknown procedure. If you do not do that, it will overwrite the RTC with a new value, unknown
to `chronyd`. At the next reboot, `chronyd` started with the `-s` option will to `chronyd`. At the next reboot, `chronyd` started with the `-s` option will
compensate this (wrong) time with its estimate of how far the RTC has drifted compensate this (wrong) time with its estimate of how far the RTC has drifted
whilst the power was off, giving a meaningless initial system time. whilst the power was off, giving a meaningless initial system time.
@@ -597,6 +929,19 @@ things
Some other program running on the system might be using the device. Some other program running on the system might be using the device.
=== When I start `chronyd`, the log says `Could not enable RTC interrupt : Invalid argument` (or it may say `disable`)
Your real-time clock hardware might not support the required ioctl requests:
* `RTC_UIE_ON`
* `RTC_UIE_OFF`
A possible solution could be to build the Linux kernel with support for software
emulation instead; try enabling the following configuration option when building
the Linux kernel:
* `CONFIG_RTC_INTF_DEV_UIE_EMUL`
=== What if my computer does not have an RTC or backup battery? === What if my computer does not have an RTC or backup battery?
In this case you can still use the `-s` option to set the system clock to the In this case you can still use the `-s` option to set the system clock to the
@@ -653,11 +998,39 @@ When two computers are synchronised to each other using the client/server or
symmetric NTP mode, there is an expectation that NTP measurements between the symmetric NTP mode, there is an expectation that NTP measurements between the
two computers made on both ends show an average offset close to zero. two computers made on both ends show an average offset close to zero.
With `chronyd` that can be expected only when the interleaved mode (`xleave` With `chronyd` that can be expected only when the interleaved mode is enabled
option) is enabled. Otherwise, `chronyd` will use different transmit timestamps by the `xleave` option. Otherwise, `chronyd` will use different transmit
(e.g. daemon timestamp vs kernel timestamp) for serving time and timestamps (e.g. daemon timestamp vs kernel timestamp) for serving time and
synchronisation of its own clock, which creates an asymmetry in the synchronisation of its own clock, which will cause the other computer to
timestamping and causes the other end to measure a significant offset. measure a significant offset.
== Operation
=== What clocks does `chronyd` use?
There are several different clocks used by `chronyd`:
* *System clock:* software clock maintained by the kernel. It is the main clock
used by applications running on the computer. It is synchronised by `chronyd`
to its NTP clock, unless started with the *-x* option.
* *NTP clock:* software clock (virtual) based on the system clock and internal
to `chronyd`. It keeps the best estimate of the true time according to the
configured time sources, which is served to NTP clients unless time smoothing
is enabled by the *smoothtime* directive. The *System time* value in the
`tracking` report is the current offset between the system and NTP clock.
* *Real-time clock (RTC):* hardware clock keeping time even when the
computer is turned off. It is used by the kernel to initialise the system
clock on boot and also by `chronyd` to compensate for its measured drift if
configured with the `rtcfile` directive and started with the `-s` option.
The clock can be kept accurate only by stepping enabled by the `rtcsync` or
`rtcautotrim` directive.
* *Reference clock:* hardware clock used as a time source. It is specified by
the `refclock` directive.
* *NIC clock (also known as PTP hardware clock):* hardware clock timestamping
packets received and transmitted by a network device specified by the
*hwtimestamp* directive. The clock is expected to be running free. It is not
synchronised by `chronyd`. Its offset is tracked relative to the NTP clock in
order to convert the hardware timestamps.
== Operating systems == Operating systems

View File

@@ -27,7 +27,7 @@ The following libraries with their development files, and programs, are needed
to enable optional features: to enable optional features:
* pkg-config: detection of development libraries * pkg-config: detection of development libraries
* Nettle, NSS, or LibTomCrypt: secure hash functions (`SECHASH`) * Nettle, GnuTLS, NSS, or LibTomCrypt: secure hash functions (`SECHASH`)
* libcap: dropping root privileges on Linux (`DROPROOT`) * libcap: dropping root privileges on Linux (`DROPROOT`)
* libseccomp: system call filter on Linux (`SCFILTER`) * libseccomp: system call filter on Linux (`SCFILTER`)
* GnuTLS and Nettle: Network Time Security (`NTS`) * GnuTLS and Nettle: Network Time Security (`NTS`)

View File

@@ -16,5 +16,32 @@ TimeoutStartSec=180
RemainAfterExit=yes RemainAfterExit=yes
StandardOutput=null StandardOutput=null
CapabilityBoundingSet=
DevicePolicy=closed
DynamicUser=yes
IPAddressAllow=localhost
IPAddressDeny=any
LockPersonality=yes
MemoryDenyWriteExecute=yes
PrivateDevices=yes
PrivateUsers=yes
ProcSubset=pid
ProtectClock=yes
ProtectControlGroups=yes
ProtectHome=yes
ProtectHostname=yes
ProtectKernelLogs=yes
ProtectKernelModules=yes
ProtectKernelTunables=yes
ProtectProc=invisible
ProtectSystem=strict
RestrictAddressFamilies=AF_INET AF_INET6
RestrictNamespaces=yes
RestrictRealtime=yes
SystemCallArchitectures=native
SystemCallFilter=@system-service
SystemCallFilter=~@privileged @resources
UMask=0777
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target

View File

@@ -1,8 +1,7 @@
#!/bin/sh #!/bin/sh
# This is a NetworkManager dispatcher script for chronyd to update # This is a NetworkManager dispatcher script for chronyd to update
# its NTP sources passed from DHCP options. Note that this script is # its NTP sources with servers from DHCP options passed by NetworkManager
# specific to NetworkManager-dispatcher due to use of the # in the DHCP4_NTP_SERVERS and DHCP6_DHCP6_NTP_SERVERS environment variables.
# DHCP4_NTP_SERVERS environment variable.
export LC_ALL=C export LC_ALL=C
@@ -10,17 +9,23 @@ interface=$1
action=$2 action=$2
chronyc=/usr/bin/chronyc chronyc=/usr/bin/chronyc
default_server_options=iburst server_options=iburst
server_dir=/var/run/chrony-dhcp server_dir=/var/run/chrony-dhcp
dhcp_server_file=$server_dir/$interface.sources dhcp_server_file=$server_dir/$interface.sources
# DHCP4_NTP_SERVERS is passed from DHCP options by NetworkManager. dhcp_ntp_servers="$DHCP4_NTP_SERVERS $DHCP6_DHCP6_NTP_SERVERS"
nm_dhcp_servers=$DHCP4_NTP_SERVERS
add_servers_from_dhcp() { add_servers_from_dhcp() {
rm -f "$dhcp_server_file" rm -f "$dhcp_server_file"
for server in $nm_dhcp_servers; do for server in $dhcp_ntp_servers; do
echo "server $server $default_server_options" >> "$dhcp_server_file" # Check for invalid characters (from the DHCPv6 NTP FQDN suboption)
len1=$(printf '%s' "$server" | wc -c)
len2=$(printf '%s' "$server" | tr -d -c 'A-Za-z0-9:.-' | wc -c)
if [ "$len1" -ne "$len2" ] || [ "$len2" -lt 1 ] || [ "$len2" -gt 255 ]; then
continue
fi
printf 'server %s %s\n' "$server" "$server_options" >> "$dhcp_server_file"
done done
$chronyc reload sources > /dev/null 2>&1 || : $chronyc reload sources > /dev/null 2>&1 || :
} }
@@ -34,10 +39,11 @@ clear_servers_from_dhcp() {
mkdir -p $server_dir mkdir -p $server_dir
if [ "$action" = "up" ] || [ "$action" = "dhcp4-change" ]; then case "$action" in
add_servers_from_dhcp up|dhcp4-change|dhcp6-change)
elif [ "$action" = "down" ]; then add_servers_from_dhcp;;
clear_servers_from_dhcp down)
fi clear_servers_from_dhcp;;
esac
exit 0 exit 0

View File

@@ -7,8 +7,18 @@ export LC_ALL=C
chronyc=/usr/bin/chronyc chronyc=/usr/bin/chronyc
# For NetworkManager consider only up/down events # For NetworkManager consider only selected events
[ $# -ge 2 ] && [ "$2" != "up" ] && [ "$2" != "down" ] && exit 0 if [ $# -ge 2 ]; then
case "$2" in
up|down|connectivity-change)
;;
dhcp6-change)
# No other action is reported for routable IPv6
;;
*)
exit 0;;
esac
fi
# Note: for networkd-dispatcher routable.d ~= on and off.d ~= off # Note: for networkd-dispatcher routable.d ~= on and off.d ~= off

View File

@@ -10,9 +10,40 @@ Type=forking
PIDFile=/run/chrony/chronyd.pid PIDFile=/run/chrony/chronyd.pid
EnvironmentFile=-/etc/sysconfig/chronyd EnvironmentFile=-/etc/sysconfig/chronyd
ExecStart=/usr/sbin/chronyd $OPTIONS ExecStart=/usr/sbin/chronyd $OPTIONS
CapabilityBoundingSet=~CAP_AUDIT_CONTROL CAP_AUDIT_READ CAP_AUDIT_WRITE
CapabilityBoundingSet=~CAP_BLOCK_SUSPEND CAP_KILL CAP_LEASE CAP_LINUX_IMMUTABLE
CapabilityBoundingSet=~CAP_MAC_ADMIN CAP_MAC_OVERRIDE CAP_MKNOD CAP_SYS_ADMIN
CapabilityBoundingSet=~CAP_SYS_BOOT CAP_SYS_CHROOT CAP_SYS_MODULE CAP_SYS_PACCT
CapabilityBoundingSet=~CAP_SYS_PTRACE CAP_SYS_RAWIO CAP_SYS_TTY_CONFIG CAP_WAKE_ALARM
DeviceAllow=char-pps rw
DeviceAllow=char-ptp rw
DeviceAllow=char-rtc rw
DevicePolicy=closed
LockPersonality=yes
MemoryDenyWriteExecute=yes
NoNewPrivileges=yes
PrivateTmp=yes PrivateTmp=yes
ProcSubset=pid
ProtectControlGroups=yes
ProtectHome=yes ProtectHome=yes
ProtectSystem=full ProtectHostname=yes
ProtectKernelLogs=yes
ProtectKernelModules=yes
ProtectKernelTunables=yes
ProtectProc=invisible
ProtectSystem=strict
ReadWritePaths=/run /var/lib/chrony -/var/log
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
RestrictNamespaces=yes
RestrictSUIDSGID=yes
SystemCallArchitectures=native
SystemCallFilter=~@cpu-emulation @debug @module @mount @obsolete @raw-io @reboot @swap
# Adjust restrictions for /usr/sbin/sendmail (mailonchange directive)
NoNewPrivileges=no
ReadWritePaths=-/var/spool
RestrictAddressFamilies=AF_NETLINK
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target

1
hash.h
View File

@@ -44,6 +44,7 @@ typedef enum {
HSH_SHA3_512 = 9, HSH_SHA3_512 = 9,
HSH_TIGER = 10, HSH_TIGER = 10,
HSH_WHIRLPOOL = 11, HSH_WHIRLPOOL = 11,
HSH_MD5_NONCRYPTO = 10000, /* For NTPv4 reference ID */
} HSH_Algorithm; } HSH_Algorithm;
extern int HSH_GetHashId(HSH_Algorithm algorithm); extern int HSH_GetHashId(HSH_Algorithm algorithm);

145
hash_gnutls.c Normal file
View File

@@ -0,0 +1,145 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2021
*
* 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.
*
**********************************************************************
=======================================================================
Crypto hashing using the GnuTLS library
*/
#include "config.h"
#include "sysincl.h"
#include <gnutls/crypto.h>
#include "hash.h"
#include "logging.h"
struct hash {
const HSH_Algorithm algorithm;
const gnutls_digest_algorithm_t type;
gnutls_hash_hd_t handle;
};
static struct hash hashes[] = {
{ HSH_MD5_NONCRYPTO, GNUTLS_DIG_MD5, NULL },
{ HSH_MD5, GNUTLS_DIG_MD5, NULL },
{ HSH_SHA1, GNUTLS_DIG_SHA1, NULL },
{ HSH_SHA256, GNUTLS_DIG_SHA256, NULL },
{ HSH_SHA384, GNUTLS_DIG_SHA384, NULL },
{ HSH_SHA512, GNUTLS_DIG_SHA512, NULL },
{ HSH_SHA3_224, GNUTLS_DIG_SHA3_224, NULL },
{ HSH_SHA3_256, GNUTLS_DIG_SHA3_256, NULL },
{ HSH_SHA3_384, GNUTLS_DIG_SHA3_384, NULL },
{ HSH_SHA3_512, GNUTLS_DIG_SHA3_512, NULL },
{ 0, 0, NULL }
};
static int gnutls_initialised = 0;
int
HSH_GetHashId(HSH_Algorithm algorithm)
{
int id, r;
if (!gnutls_initialised) {
r = gnutls_global_init();
if (r < 0)
LOG_FATAL("Could not initialise %s : %s", "gnutls", gnutls_strerror(r));
gnutls_initialised = 1;
}
for (id = 0; hashes[id].algorithm != 0; id++) {
if (hashes[id].algorithm == algorithm)
break;
}
if (hashes[id].algorithm == 0)
return -1;
if (hashes[id].handle)
return id;
if (algorithm == HSH_MD5_NONCRYPTO)
GNUTLS_FIPS140_SET_LAX_MODE();
r = gnutls_hash_init(&hashes[id].handle, hashes[id].type);
if (algorithm == HSH_MD5_NONCRYPTO)
GNUTLS_FIPS140_SET_STRICT_MODE();
if (r < 0) {
DEBUG_LOG("Could not initialise %s : %s", "hash", gnutls_strerror(r));
hashes[id].handle = NULL;
return -1;
}
return id;
}
int
HSH_Hash(int id, const void *in1, int in1_len, const void *in2, int in2_len,
unsigned char *out, int out_len)
{
unsigned char buf[MAX_HASH_LENGTH];
gnutls_hash_hd_t handle;
int hash_len;
if (in1_len < 0 || in2_len < 0 || out_len < 0)
return 0;
handle = hashes[id].handle;
hash_len = gnutls_hash_get_len(hashes[id].type);
if (out_len > hash_len)
out_len = hash_len;
if (hash_len > sizeof (buf))
return 0;
if (gnutls_hash(handle, in1, in1_len) < 0 ||
(in2 && gnutls_hash(handle, in2, in2_len) < 0)) {
/* Reset the state */
gnutls_hash_output(handle, buf);
return 0;
}
gnutls_hash_output(handle, buf);
memcpy(out, buf, out_len);
return out_len;
}
void
HSH_Finalise(void)
{
int i;
if (!gnutls_initialised)
return;
for (i = 0; hashes[i].algorithm != 0; i++) {
if (hashes[i].handle)
gnutls_hash_deinit(hashes[i].handle, NULL);
}
gnutls_global_deinit();
}

View File

@@ -39,7 +39,7 @@ int
HSH_GetHashId(HSH_Algorithm algorithm) HSH_GetHashId(HSH_Algorithm algorithm)
{ {
/* only MD5 is supported */ /* only MD5 is supported */
if (algorithm != HSH_MD5) if (algorithm != HSH_MD5 && algorithm != HSH_MD5_NONCRYPTO)
return -1; return -1;
return 0; return 0;

View File

@@ -59,6 +59,9 @@ HSH_GetHashId(HSH_Algorithm algorithm)
{ {
int id, nid; int id, nid;
if (algorithm == HSH_MD5_NONCRYPTO)
algorithm = HSH_MD5;
for (id = 0; hashes[id].algorithm != 0; id++) { for (id = 0; hashes[id].algorithm != 0; id++) {
if (hashes[id].algorithm == algorithm) if (hashes[id].algorithm == algorithm)
break; break;

View File

@@ -56,6 +56,9 @@ HSH_GetHashId(HSH_Algorithm algorithm)
{ {
int i; int i;
if (algorithm == HSH_MD5_NONCRYPTO)
algorithm = HSH_MD5;
for (i = 0; hashes[i].algorithm != 0; i++) { for (i = 0; hashes[i].algorithm != 0; i++) {
if (hashes[i].algorithm == algorithm) if (hashes[i].algorithm == algorithm)
break; break;

View File

@@ -71,6 +71,9 @@ HSH_GetHashId(HSH_Algorithm algorithm)
{ {
int i, h; int i, h;
if (algorithm == HSH_MD5_NONCRYPTO)
algorithm = HSH_MD5;
for (i = 0; hashes[i].algorithm != 0; i++) { for (i = 0; hashes[i].algorithm != 0; i++) {
if (hashes[i].algorithm == algorithm) if (hashes[i].algorithm == algorithm)
break; break;

109
hwclock.c
View File

@@ -33,6 +33,7 @@
#include "local.h" #include "local.h"
#include "logging.h" #include "logging.h"
#include "memory.h" #include "memory.h"
#include "quantiles.h"
#include "regress.h" #include "regress.h"
#include "util.h" #include "util.h"
@@ -43,6 +44,13 @@
/* Maximum acceptable frequency offset of the clock */ /* Maximum acceptable frequency offset of the clock */
#define MAX_FREQ_OFFSET (2.0 / 3.0) #define MAX_FREQ_OFFSET (2.0 / 3.0)
/* Quantiles for filtering readings by delay */
#define DELAY_QUANT_MIN_K 1
#define DELAY_QUANT_MAX_K 2
#define DELAY_QUANT_Q 10
#define DELAY_QUANT_REPEAT 7
#define DELAY_QUANT_MIN_STEP 1.0e-9
struct HCL_Instance_Record { struct HCL_Instance_Record {
/* HW and local reference timestamp */ /* HW and local reference timestamp */
struct timespec hw_ref; struct timespec hw_ref;
@@ -64,12 +72,18 @@ struct HCL_Instance_Record {
/* Minimum interval between samples */ /* Minimum interval between samples */
double min_separation; double min_separation;
/* Expected precision of readings */
double precision;
/* Flag indicating the offset and frequency values are valid */ /* Flag indicating the offset and frequency values are valid */
int valid_coefs; int valid_coefs;
/* Estimated offset and frequency of HW clock relative to local clock */ /* Estimated offset and frequency of HW clock relative to local clock */
double offset; double offset;
double frequency; double frequency;
/* Estimated quantiles of reading delay */
QNT_Instance delay_quants;
}; };
/* ================================================== */ /* ================================================== */
@@ -92,7 +106,7 @@ handle_slew(struct timespec *raw, struct timespec *cooked, double dfreq,
/* ================================================== */ /* ================================================== */
HCL_Instance HCL_Instance
HCL_CreateInstance(int min_samples, int max_samples, double min_separation) HCL_CreateInstance(int min_samples, int max_samples, double min_separation, double precision)
{ {
HCL_Instance clock; HCL_Instance clock;
@@ -110,6 +124,10 @@ HCL_CreateInstance(int min_samples, int max_samples, double min_separation)
clock->n_samples = 0; clock->n_samples = 0;
clock->valid_coefs = 0; clock->valid_coefs = 0;
clock->min_separation = min_separation; clock->min_separation = min_separation;
clock->precision = precision;
clock->delay_quants = QNT_CreateInstance(DELAY_QUANT_MIN_K, DELAY_QUANT_MAX_K,
DELAY_QUANT_Q, DELAY_QUANT_REPEAT,
DELAY_QUANT_MIN_STEP);
LCL_AddParameterChangeHandler(handle_slew, clock); LCL_AddParameterChangeHandler(handle_slew, clock);
@@ -121,6 +139,7 @@ HCL_CreateInstance(int min_samples, int max_samples, double min_separation)
void HCL_DestroyInstance(HCL_Instance clock) void HCL_DestroyInstance(HCL_Instance clock)
{ {
LCL_RemoveParameterChangeHandler(handle_slew, clock); LCL_RemoveParameterChangeHandler(handle_slew, clock);
QNT_DestroyInstance(clock->delay_quants);
Free(clock->y_data); Free(clock->y_data);
Free(clock->x_data); Free(clock->x_data);
Free(clock); Free(clock);
@@ -140,6 +159,94 @@ HCL_NeedsNewSample(HCL_Instance clock, struct timespec *now)
/* ================================================== */ /* ================================================== */
int
HCL_ProcessReadings(HCL_Instance clock, int n_readings, struct timespec tss[][3],
struct timespec *hw_ts, struct timespec *local_ts, double *err)
{
double delay, raw_delay, min_delay, low_delay, high_delay, e, pred_err;
double delay_sum, hw_sum, local_sum, local_prec, freq;
int i, min_reading, combined;
struct timespec ts1, ts2;
if (n_readings < 1)
return 0;
/* Work out the current correction multiplier needed to get cooked delays */
LCL_CookTime(&tss[0][0], &ts1, NULL);
LCL_CookTime(&tss[n_readings - 1][2], &ts2, NULL);
if (UTI_CompareTimespecs(&tss[0][0], &tss[n_readings - 1][2]) < 0)
freq = UTI_DiffTimespecsToDouble(&ts1, &ts2) /
UTI_DiffTimespecsToDouble(&tss[0][0], &tss[n_readings - 1][2]);
else
freq = 1.0;
for (i = 0; i < n_readings; i++) {
delay = freq * UTI_DiffTimespecsToDouble(&tss[i][2], &tss[i][0]);
if (delay < 0.0) {
/* Step in the middle of a reading? */
DEBUG_LOG("Bad reading delay=%e", delay);
return 0;
}
if (i == 0 || min_delay > delay) {
min_delay = delay;
min_reading = i;
}
QNT_Accumulate(clock->delay_quants, delay);
}
local_prec = LCL_GetSysPrecisionAsQuantum();
low_delay = QNT_GetQuantile(clock->delay_quants, DELAY_QUANT_MIN_K);
high_delay = QNT_GetQuantile(clock->delay_quants, DELAY_QUANT_MAX_K);
low_delay = MIN(low_delay, high_delay);
high_delay = MAX(high_delay, low_delay + local_prec);
/* Combine readings with delay in the expected interval */
for (i = combined = 0, delay_sum = hw_sum = local_sum = 0.0; i < n_readings; i++) {
raw_delay = UTI_DiffTimespecsToDouble(&tss[i][2], &tss[i][0]);
delay = freq * raw_delay;
if (delay < low_delay || delay > high_delay)
continue;
delay_sum += delay;
hw_sum += UTI_DiffTimespecsToDouble(&tss[i][1], &tss[0][1]);
local_sum += UTI_DiffTimespecsToDouble(&tss[i][0], &tss[0][0]) + raw_delay / 2.0;
combined++;
}
DEBUG_LOG("Combined %d readings lo=%e hi=%e", combined, low_delay, high_delay);
if (combined > 0) {
UTI_AddDoubleToTimespec(&tss[0][1], hw_sum / combined, hw_ts);
UTI_AddDoubleToTimespec(&tss[0][0], local_sum / combined, local_ts);
*err = MAX(delay_sum / combined / 2.0, clock->precision);
return 1;
}
/* Accept the reading with minimum delay if its interval does not contain
the current offset predicted from previous samples */
*hw_ts = tss[min_reading][1];
UTI_AddDoubleToTimespec(&tss[min_reading][0], min_delay / freq / 2.0, local_ts);
*err = MAX(min_delay / 2.0, clock->precision);
pred_err = 0.0;
LCL_CookTime(local_ts, &ts1, NULL);
if (!HCL_CookTime(clock, hw_ts, &ts2, &e) ||
((pred_err = UTI_DiffTimespecsToDouble(&ts1, &ts2)) > *err)) {
DEBUG_LOG("Accepted reading err=%e prerr=%e", *err, pred_err);
return 1;
}
return 0;
}
/* ================================================== */
void void
HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts, HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
struct timespec *local_ts, double err) struct timespec *local_ts, double err)

View File

@@ -30,7 +30,7 @@ typedef struct HCL_Instance_Record *HCL_Instance;
/* Create a new HW clock instance */ /* Create a new HW clock instance */
extern HCL_Instance HCL_CreateInstance(int min_samples, int max_samples, extern HCL_Instance HCL_CreateInstance(int min_samples, int max_samples,
double min_separation); double min_separation, double precision);
/* Destroy a HW clock instance */ /* Destroy a HW clock instance */
extern void HCL_DestroyInstance(HCL_Instance clock); extern void HCL_DestroyInstance(HCL_Instance clock);
@@ -38,6 +38,11 @@ extern void HCL_DestroyInstance(HCL_Instance clock);
/* Check if a new sample should be accumulated at this time */ /* Check if a new sample should be accumulated at this time */
extern int HCL_NeedsNewSample(HCL_Instance clock, struct timespec *now); extern int HCL_NeedsNewSample(HCL_Instance clock, struct timespec *now);
/* Process new readings of the HW clock in form of (sys, hw, sys) triplets and
produce a sample which can be accumulated */
extern int HCL_ProcessReadings(HCL_Instance clock, int n_readings, struct timespec tss[][3],
struct timespec *hw_ts, struct timespec *local_ts, double *err);
/* Accumulate a new sample */ /* Accumulate a new sample */
extern void HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts, extern void HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
struct timespec *local_ts, double err); struct timespec *local_ts, double err);

45
local.c
View File

@@ -505,7 +505,7 @@ LCL_AccumulateDeltaFrequency(double dfreq)
/* ================================================== */ /* ================================================== */
void int
LCL_AccumulateOffset(double offset, double corr_rate) LCL_AccumulateOffset(double offset, double corr_rate)
{ {
struct timespec raw, cooked; struct timespec raw, cooked;
@@ -517,12 +517,14 @@ LCL_AccumulateOffset(double offset, double corr_rate)
LCL_CookTime(&raw, &cooked, NULL); LCL_CookTime(&raw, &cooked, NULL);
if (!check_offset(&cooked, offset)) if (!check_offset(&cooked, offset))
return; return 0;
(*drv_accrue_offset)(offset, corr_rate); (*drv_accrue_offset)(offset, corr_rate);
/* Dispatch to all handlers */ /* Dispatch to all handlers */
invoke_parameter_change_handlers(&raw, &cooked, 0.0, offset, LCL_ChangeAdjust); invoke_parameter_change_handlers(&raw, &cooked, 0.0, offset, LCL_ChangeAdjust);
return 1;
} }
/* ================================================== */ /* ================================================== */
@@ -561,6 +563,8 @@ void
LCL_NotifyExternalTimeStep(struct timespec *raw, struct timespec *cooked, LCL_NotifyExternalTimeStep(struct timespec *raw, struct timespec *cooked,
double offset, double dispersion) double offset, double dispersion)
{ {
LCL_CancelOffsetCorrection();
/* Dispatch to all handlers */ /* Dispatch to all handlers */
invoke_parameter_change_handlers(raw, cooked, 0.0, offset, LCL_ChangeUnknownStep); invoke_parameter_change_handlers(raw, cooked, 0.0, offset, LCL_ChangeUnknownStep);
@@ -586,7 +590,7 @@ LCL_NotifyLeap(int leap)
/* ================================================== */ /* ================================================== */
void int
LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate) LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
{ {
struct timespec raw, cooked; struct timespec raw, cooked;
@@ -598,7 +602,7 @@ LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
LCL_CookTime(&raw, &cooked, NULL); LCL_CookTime(&raw, &cooked, NULL);
if (!check_offset(&cooked, doffset)) if (!check_offset(&cooked, doffset))
return; return 0;
old_freq_ppm = current_freq_ppm; old_freq_ppm = current_freq_ppm;
@@ -620,6 +624,26 @@ LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
/* Dispatch to all handlers */ /* Dispatch to all handlers */
invoke_parameter_change_handlers(&raw, &cooked, dfreq, doffset, LCL_ChangeAdjust); invoke_parameter_change_handlers(&raw, &cooked, dfreq, doffset, LCL_ChangeAdjust);
return 1;
}
/* ================================================== */
int
LCL_AccumulateFrequencyAndOffsetNoHandlers(double dfreq, double doffset, double corr_rate)
{
ChangeListEntry *first_handler;
int r;
first_handler = change_list.next;
change_list.next = &change_list;
r = LCL_AccumulateFrequencyAndOffset(dfreq, doffset, corr_rate);
change_list.next = first_handler;
return r;
} }
/* ================================================== */ /* ================================================== */
@@ -687,6 +711,19 @@ LCL_MakeStep(void)
/* ================================================== */ /* ================================================== */
void
LCL_CancelOffsetCorrection(void)
{
struct timespec raw;
double correction;
LCL_ReadRawTime(&raw);
LCL_GetOffsetCorrection(&raw, &correction, NULL);
LCL_AccumulateOffset(correction, 0.0);
}
/* ================================================== */
int int
LCL_CanSystemLeap(void) LCL_CanSystemLeap(void)
{ {

12
local.h
View File

@@ -149,7 +149,7 @@ extern void LCL_AccumulateDeltaFrequency(double dfreq);
forwards (i.e. it is currently slow of true time). Provided is also forwards (i.e. it is currently slow of true time). Provided is also
a suggested correction rate (correction time * offset). */ a suggested correction rate (correction time * offset). */
extern void LCL_AccumulateOffset(double offset, double corr_rate); extern int LCL_AccumulateOffset(double offset, double corr_rate);
/* Routine to apply an immediate offset by doing a sudden step if /* Routine to apply an immediate offset by doing a sudden step if
possible. (Intended for use after an initial estimate of offset has possible. (Intended for use after an initial estimate of offset has
@@ -171,7 +171,12 @@ extern void LCL_NotifyLeap(int leap);
/* Perform the combination of modifying the frequency and applying /* Perform the combination of modifying the frequency and applying
a slew, in one easy step */ a slew, in one easy step */
extern void LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate); extern int LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate);
/* Same as the routine above, except it does not call the registered
parameter change handlers */
extern int LCL_AccumulateFrequencyAndOffsetNoHandlers(double dfreq, double doffset,
double corr_rate);
/* Routine to read the system precision as a log to base 2 value. */ /* Routine to read the system precision as a log to base 2 value. */
extern int LCL_GetSysPrecisionAsLog(void); extern int LCL_GetSysPrecisionAsLog(void);
@@ -197,6 +202,9 @@ extern void LCL_Finalise(void);
to a timezone problem. */ to a timezone problem. */
extern int LCL_MakeStep(void); extern int LCL_MakeStep(void);
/* Routine to cancel the outstanding system clock correction */
extern void LCL_CancelOffsetCorrection(void);
/* Check if the system driver supports leap seconds, i.e. LCL_SetSystemLeap /* Check if the system driver supports leap seconds, i.e. LCL_SetSystemLeap
does something */ does something */
extern int LCL_CanSystemLeap(void); extern int LCL_CanSystemLeap(void);

18
main.c
View File

@@ -76,11 +76,18 @@ static REF_Mode ref_mode = REF_ModeNormal;
static void static void
do_platform_checks(void) do_platform_checks(void)
{ {
struct timespec ts;
/* Require at least 32-bit integers, two's complement representation and /* Require at least 32-bit integers, two's complement representation and
the usual implementation of conversion of unsigned integers */ the usual implementation of conversion of unsigned integers */
assert(sizeof (int) >= 4); assert(sizeof (int) >= 4);
assert(-1 == ~0); assert(-1 == ~0);
assert((int32_t)4294967295U == (int32_t)-1); assert((int32_t)4294967295U == (int32_t)-1);
/* Require time_t and tv_nsec in timespec to be signed */
ts.tv_sec = -1;
ts.tv_nsec = -1;
assert(ts.tv_sec < 0 && ts.tv_nsec < 0);
} }
/* ================================================== */ /* ================================================== */
@@ -104,6 +111,7 @@ MAI_CleanupAndExit(void)
{ {
if (!initialised) exit(exit_status); if (!initialised) exit(exit_status);
LCL_CancelOffsetCorrection();
SRC_DumpSources(); SRC_DumpSources();
/* Don't update clock when removing sources */ /* Don't update clock when removing sources */
@@ -140,6 +148,8 @@ MAI_CleanupAndExit(void)
HSH_Finalise(); HSH_Finalise();
LOG_Finalise(); LOG_Finalise();
UTI_ResetGetRandomFunctions();
exit(exit_status); exit(exit_status);
} }
@@ -156,6 +166,8 @@ signal_cleanup(int x)
static void static void
quit_timeout(void *arg) quit_timeout(void *arg)
{ {
LOG(LOGS_INFO, "Timeout reached");
/* Return with non-zero status if the clock is not synchronised */ /* Return with non-zero status if the clock is not synchronised */
exit_status = REF_GetOurStratum() >= NTP_MAX_STRATUM; exit_status = REF_GetOurStratum() >= NTP_MAX_STRATUM;
SCH_QuitProgram(); SCH_QuitProgram();
@@ -183,7 +195,7 @@ ntp_source_resolving_end(void)
NSR_AutoStartSources(); NSR_AutoStartSources();
/* Special modes can end only when sources update their reachability. /* Special modes can end only when sources update their reachability.
Give up immediatelly if there are no active sources. */ Give up immediately if there are no active sources. */
if (ref_mode != REF_ModeNormal && !SRC_ActiveSources()) { if (ref_mode != REF_ModeNormal && !SRC_ActiveSources()) {
REF_SetUnsynchronised(); REF_SetUnsynchronised();
} }
@@ -491,6 +503,7 @@ int main
user_check = 0; user_check = 0;
nofork = 1; nofork = 1;
system_log = 0; system_log = 0;
log_severity = LOGS_WARN;
break; break;
case 'P': case 'P':
sched_priority = parse_int_arg(optarg); sched_priority = parse_int_arg(optarg);
@@ -627,6 +640,9 @@ int main
if (!geteuid() && (pw->pw_uid || pw->pw_gid)) if (!geteuid() && (pw->pw_uid || pw->pw_gid))
SYS_DropRoot(pw->pw_uid, pw->pw_gid, SYS_MAIN_PROCESS); SYS_DropRoot(pw->pw_uid, pw->pw_gid, SYS_MAIN_PROCESS);
if (!geteuid())
LOG(LOGS_WARN, "Running with root privileges");
REF_Initialise(); REF_Initialise();
SST_Initialise(); SST_Initialise();
NSR_Initialise(); NSR_Initialise();

View File

@@ -50,12 +50,24 @@ DNS_SetAddressFamily(int family)
DNS_Status DNS_Status
DNS_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs) DNS_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs)
{ {
#ifdef HAVE_GETADDRINFO
struct addrinfo hints, *res, *ai; struct addrinfo hints, *res, *ai;
int i, result; int i, result;
IPAddr ip;
max_addrs = MIN(max_addrs, DNS_MAX_ADDRESSES); max_addrs = MIN(max_addrs, DNS_MAX_ADDRESSES);
for (i = 0; i < max_addrs; i++)
ip_addrs[i].family = IPADDR_UNSPEC;
/* Avoid calling getaddrinfo() if the name is an IP address */
if (UTI_StringToIP(name, &ip)) {
if (address_family != IPADDR_UNSPEC && ip.family != address_family)
return DNS_Failure;
if (max_addrs >= 1)
ip_addrs[0] = ip;
return DNS_Success;
}
memset(&hints, 0, sizeof (hints)); memset(&hints, 0, sizeof (hints));
switch (address_family) { switch (address_family) {
@@ -107,48 +119,9 @@ DNS_Name2IPAddress(const char *name, IPAddr *ip_addrs, int max_addrs)
} }
} }
for (; i < max_addrs; i++)
ip_addrs[i].family = IPADDR_UNSPEC;
freeaddrinfo(res); freeaddrinfo(res);
return !max_addrs || ip_addrs[0].family != IPADDR_UNSPEC ? DNS_Success : DNS_Failure; return !max_addrs || ip_addrs[0].family != IPADDR_UNSPEC ? DNS_Success : DNS_Failure;
#else
struct hostent *host;
int i;
if (address_family != IPADDR_UNSPEC && address_family != IPADDR_INET4)
return DNS_Failure;
max_addrs = MIN(max_addrs, DNS_MAX_ADDRESSES);
host = gethostbyname(name);
if (host == NULL) {
if (h_errno == TRY_AGAIN)
return DNS_TryAgain;
} else {
if (host->h_addrtype != AF_INET || !host->h_addr_list[0])
return DNS_Failure;
for (i = 0; host->h_addr_list[i] && i < max_addrs; i++) {
ip_addrs[i].family = IPADDR_INET4;
ip_addrs[i].addr.in4 = ntohl(*(uint32_t *)host->h_addr_list[i]);
}
for (; i < max_addrs; i++)
ip_addrs[i].family = IPADDR_UNSPEC;
return DNS_Success;
}
#ifdef FORCE_DNSRETRY
return DNS_TryAgain;
#else
return DNS_Failure;
#endif
#endif
} }
/* ================================================== */ /* ================================================== */
@@ -157,9 +130,11 @@ int
DNS_IPAddress2Name(IPAddr *ip_addr, char *name, int len) DNS_IPAddress2Name(IPAddr *ip_addr, char *name, int len)
{ {
char *result = NULL; char *result = NULL;
#ifdef FEAT_IPV6 #ifdef FEAT_IPV6
struct sockaddr_in6 in6; struct sockaddr_in6 saddr;
#else
struct sockaddr_in saddr;
#endif
IPSockAddr ip_saddr; IPSockAddr ip_saddr;
socklen_t slen; socklen_t slen;
char hbuf[NI_MAXHOST]; char hbuf[NI_MAXHOST];
@@ -167,29 +142,9 @@ DNS_IPAddress2Name(IPAddr *ip_addr, char *name, int len)
ip_saddr.ip_addr = *ip_addr; ip_saddr.ip_addr = *ip_addr;
ip_saddr.port = 0; ip_saddr.port = 0;
slen = SCK_IPSockAddrToSockaddr(&ip_saddr, (struct sockaddr *)&in6, sizeof (in6)); slen = SCK_IPSockAddrToSockaddr(&ip_saddr, (struct sockaddr *)&saddr, sizeof (saddr));
if (!getnameinfo((struct sockaddr *)&in6, slen, hbuf, sizeof (hbuf), NULL, 0, 0)) if (!getnameinfo((struct sockaddr *)&saddr, slen, hbuf, sizeof (hbuf), NULL, 0, 0))
result = hbuf; result = hbuf;
#else
struct hostent *host;
uint32_t addr;
switch (ip_addr->family) {
case IPADDR_INET4:
addr = htonl(ip_addr->addr.in4);
host = gethostbyaddr((const char *) &addr, sizeof (ip_addr), AF_INET);
break;
#ifdef FEAT_IPV6
case IPADDR_INET6:
host = gethostbyaddr((const void *) ip_addr->addr.in6, sizeof (ip_addr->addr.in6), AF_INET6);
break;
#endif
default:
host = NULL;
}
if (host)
result = host->h_name;
#endif
if (result == NULL) if (result == NULL)
result = UTI_IPToString(ip_addr); result = UTI_IPToString(ip_addr);

26
ntp.h
View File

@@ -113,6 +113,30 @@ typedef struct {
#define NTP_REFID_LOCAL 0x7F7F0101UL /* 127.127.1.1 */ #define NTP_REFID_LOCAL 0x7F7F0101UL /* 127.127.1.1 */
#define NTP_REFID_SMOOTH 0x7F7F01FFUL /* 127.127.1.255 */ #define NTP_REFID_SMOOTH 0x7F7F01FFUL /* 127.127.1.255 */
/* Non-authentication extension fields and corresponding internal flags */
#define NTP_EF_EXP1 0xF323
#define NTP_EF_FLAG_EXP1 0x1
/* Pre-NTPv5 experimental extension field */
typedef struct {
uint32_t magic;
NTP_int32 root_delay;
NTP_int32 root_dispersion;
NTP_int64 mono_receive_ts;
uint32_t mono_epoch;
} NTP_ExtFieldExp1;
#define NTP_EF_EXP1_MAGIC 0xF5BEDD9AU
/* Authentication extension fields */
#define NTP_EF_NTS_UNIQUE_IDENTIFIER 0x0104
#define NTP_EF_NTS_COOKIE 0x0204
#define NTP_EF_NTS_COOKIE_PLACEHOLDER 0x0304
#define NTP_EF_NTS_AUTH_AND_EEF 0x0404
/* Enumeration for authentication modes of NTP packets */ /* Enumeration for authentication modes of NTP packets */
typedef enum { typedef enum {
NTP_AUTH_NONE = 0, /* No authentication */ NTP_AUTH_NONE = 0, /* No authentication */
@@ -130,6 +154,7 @@ typedef struct {
NTP_Mode mode; NTP_Mode mode;
int ext_fields; int ext_fields;
int ext_field_flags;
struct { struct {
NTP_AuthMode mode; NTP_AuthMode mode;
@@ -152,7 +177,6 @@ typedef struct {
double peer_dispersion; double peer_dispersion;
double root_delay; double root_delay;
double root_dispersion; double root_dispersion;
int stratum;
} NTP_Sample; } NTP_Sample;
#endif /* GOT_NTP_H */ #endif /* GOT_NTP_H */

View File

@@ -32,7 +32,6 @@
#include "logging.h" #include "logging.h"
#include "memory.h" #include "memory.h"
#include "ntp_auth.h" #include "ntp_auth.h"
#include "ntp_ext.h"
#include "ntp_signd.h" #include "ntp_signd.h"
#include "nts_ntp.h" #include "nts_ntp.h"
#include "nts_ntp_client.h" #include "nts_ntp_client.h"
@@ -105,19 +104,6 @@ check_symmetric_auth(NTP_Packet *packet, NTP_PacketInfo *info)
/* ================================================== */ /* ================================================== */
static int
is_zero_data(unsigned char *data, int length)
{
int i;
for (i = 0; i < length; i++)
if (data[i] != 0)
return 0;
return 1;
}
/* ================================================== */
static NAU_Instance static NAU_Instance
create_instance(NTP_AuthMode mode) create_instance(NTP_AuthMode mode)
{ {
@@ -161,11 +147,12 @@ NAU_CreateSymmetricInstance(uint32_t key_id)
/* ================================================== */ /* ================================================== */
NAU_Instance NAU_Instance
NAU_CreateNtsInstance(IPSockAddr *nts_address, const char *name, const IPSockAddr *ntp_address) NAU_CreateNtsInstance(IPSockAddr *nts_address, const char *name, uint32_t cert_set,
uint16_t ntp_port)
{ {
NAU_Instance instance = create_instance(NTP_AUTH_NTS); NAU_Instance instance = create_instance(NTP_AUTH_NTS);
instance->nts = NNC_CreateInstance(nts_address, name, ntp_address); instance->nts = NNC_CreateInstance(nts_address, name, cert_set, ntp_port);
return instance; return instance;
} }
@@ -246,101 +233,6 @@ NAU_GenerateRequestAuth(NAU_Instance instance, NTP_Packet *request, NTP_PacketIn
/* ================================================== */ /* ================================================== */
int
NAU_ParsePacket(NTP_Packet *packet, NTP_PacketInfo *info)
{
int parsed, remainder, ef_length, ef_type;
unsigned char *data;
data = (void *)packet;
parsed = NTP_HEADER_LENGTH;
remainder = info->length - parsed;
info->ext_fields = 0;
/* Check if this is a plain NTP packet with no extension fields or MAC */
if (remainder <= 0)
return 1;
assert(remainder % 4 == 0);
/* In NTPv3 and older packets don't have extension fields. Anything after
the header is assumed to be a MAC. */
if (info->version <= 3) {
info->auth.mode = NTP_AUTH_SYMMETRIC;
info->auth.mac.start = parsed;
info->auth.mac.length = remainder;
info->auth.mac.key_id = ntohl(*(uint32_t *)(data + parsed));
/* Check if it is an MS-SNTP authenticator field or extended authenticator
field with zeroes as digest */
if (info->version == 3 && info->auth.mac.key_id != 0) {
if (remainder == 20 && is_zero_data(data + parsed + 4, remainder - 4))
info->auth.mode = NTP_AUTH_MSSNTP;
else if (remainder == 72 && is_zero_data(data + parsed + 8, remainder - 8))
info->auth.mode = NTP_AUTH_MSSNTP_EXT;
}
return 1;
}
/* Check for a crypto NAK */
if (remainder == 4 && ntohl(*(uint32_t *)(data + parsed)) == 0) {
info->auth.mode = NTP_AUTH_SYMMETRIC;
info->auth.mac.start = parsed;
info->auth.mac.length = remainder;
info->auth.mac.key_id = 0;
return 1;
}
/* Parse the rest of the NTPv4 packet */
while (remainder > 0) {
/* Check if the remaining data is a MAC */
if (remainder >= NTP_MIN_MAC_LENGTH && remainder <= NTP_MAX_V4_MAC_LENGTH)
break;
/* Check if this is a valid NTPv4 extension field and skip it */
if (!NEF_ParseField(packet, info->length, parsed, &ef_length, &ef_type, NULL, NULL)) {
DEBUG_LOG("Invalid format");
return 0;
}
assert(ef_length > 0 && ef_length % 4 == 0);
switch (ef_type) {
case NTP_EF_NTS_UNIQUE_IDENTIFIER:
case NTP_EF_NTS_COOKIE:
case NTP_EF_NTS_COOKIE_PLACEHOLDER:
case NTP_EF_NTS_AUTH_AND_EEF:
info->auth.mode = NTP_AUTH_NTS;
break;
default:
DEBUG_LOG("Unknown extension field type=%x", (unsigned int)ef_type);
}
info->ext_fields++;
parsed += ef_length;
remainder = info->length - parsed;
}
if (remainder == 0) {
/* No MAC */
return 1;
} else if (remainder >= NTP_MIN_MAC_LENGTH) {
info->auth.mode = NTP_AUTH_SYMMETRIC;
info->auth.mac.start = parsed;
info->auth.mac.length = remainder;
info->auth.mac.key_id = ntohl(*(uint32_t *)(data + parsed));
return 1;
}
DEBUG_LOG("Invalid format");
return 0;
}
/* ================================================== */
int int
NAU_CheckRequestAuth(NTP_Packet *request, NTP_PacketInfo *info, uint32_t *kod) NAU_CheckRequestAuth(NTP_Packet *request, NTP_PacketInfo *info, uint32_t *kod)
{ {

View File

@@ -37,7 +37,7 @@ typedef struct NAU_Instance_Record *NAU_Instance;
extern NAU_Instance NAU_CreateNoneInstance(void); extern NAU_Instance NAU_CreateNoneInstance(void);
extern NAU_Instance NAU_CreateSymmetricInstance(uint32_t key_id); extern NAU_Instance NAU_CreateSymmetricInstance(uint32_t key_id);
extern NAU_Instance NAU_CreateNtsInstance(IPSockAddr *nts_address, const char *name, extern NAU_Instance NAU_CreateNtsInstance(IPSockAddr *nts_address, const char *name,
const IPSockAddr *ntp_address); uint32_t cert_set, uint16_t ntp_port);
/* Destroy an instance */ /* Destroy an instance */
extern void NAU_DestroyInstance(NAU_Instance instance); extern void NAU_DestroyInstance(NAU_Instance instance);
@@ -55,9 +55,6 @@ extern int NAU_PrepareRequestAuth(NAU_Instance instance);
extern int NAU_GenerateRequestAuth(NAU_Instance instance, NTP_Packet *request, extern int NAU_GenerateRequestAuth(NAU_Instance instance, NTP_Packet *request,
NTP_PacketInfo *info); NTP_PacketInfo *info);
/* Parse a request or response to detect the authentication mode */
extern int NAU_ParsePacket(NTP_Packet *packet, NTP_PacketInfo *info);
/* Verify that a request is authentic. If it is not authentic and a non-zero /* Verify that a request is authentic. If it is not authentic and a non-zero
kod code is returned, a KoD response should be sent back. */ kod code is returned, a KoD response should be sent back. */
extern int NAU_CheckRequestAuth(NTP_Packet *request, NTP_PacketInfo *info, uint32_t *kod); extern int NAU_CheckRequestAuth(NTP_Packet *request, NTP_PacketInfo *info, uint32_t *kod);

File diff suppressed because it is too large Load Diff

159
ntp_io.c
View File

@@ -4,7 +4,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Timo Teras 2009 * Copyright (C) Timo Teras 2009
* Copyright (C) Miroslav Lichvar 2009, 2013-2016, 2018-2020 * Copyright (C) Miroslav Lichvar 2009, 2013-2016, 2018-2021
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of version 2 of the GNU General Public License as
@@ -30,9 +30,11 @@
#include "sysincl.h" #include "sysincl.h"
#include "memory.h"
#include "ntp_io.h" #include "ntp_io.h"
#include "ntp_core.h" #include "ntp_core.h"
#include "ntp_sources.h" #include "ntp_sources.h"
#include "ptp.h"
#include "sched.h" #include "sched.h"
#include "socket.h" #include "socket.h"
#include "local.h" #include "local.h"
@@ -70,6 +72,16 @@ static int permanent_server_sockets;
/* Flag indicating the server IPv4 socket is bound to an address */ /* Flag indicating the server IPv4 socket is bound to an address */
static int bound_server_sock_fd4; static int bound_server_sock_fd4;
/* PTP event port, or 0 if disabled */
static int ptp_port;
/* Shared server/client sockets for NTP-over-PTP */
static int ptp_sock_fd4;
static int ptp_sock_fd6;
/* Buffer for transmitted NTP-over-PTP messages */
static PTP_NtpMessage *ptp_message;
/* Flag indicating that we have been initialised */ /* Flag indicating that we have been initialised */
static int initialised=0; static int initialised=0;
@@ -221,6 +233,17 @@ NIO_Initialise(void)
client_sock_fd4 == INVALID_SOCK_FD && client_sock_fd6 == INVALID_SOCK_FD)) { client_sock_fd4 == INVALID_SOCK_FD && client_sock_fd6 == INVALID_SOCK_FD)) {
LOG_FATAL("Could not open NTP sockets"); LOG_FATAL("Could not open NTP sockets");
} }
ptp_port = CNF_GetPtpPort();
ptp_sock_fd4 = INVALID_SOCK_FD;
ptp_sock_fd6 = INVALID_SOCK_FD;
ptp_message = NULL;
if (ptp_port > 0) {
ptp_sock_fd4 = open_socket(IPADDR_INET4, ptp_port, 0, NULL);
ptp_sock_fd6 = open_socket(IPADDR_INET6, ptp_port, 0, NULL);
ptp_message = MallocNew(PTP_NtpMessage);
}
} }
/* ================================================== */ /* ================================================== */
@@ -238,6 +261,11 @@ NIO_Finalise(void)
close_socket(server_sock_fd6); close_socket(server_sock_fd6);
server_sock_fd6 = client_sock_fd6 = INVALID_SOCK_FD; server_sock_fd6 = client_sock_fd6 = INVALID_SOCK_FD;
close_socket(ptp_sock_fd4);
close_socket(ptp_sock_fd6);
ptp_sock_fd4 = ptp_sock_fd6 = INVALID_SOCK_FD;
Free(ptp_message);
#ifdef HAVE_LINUX_TIMESTAMPING #ifdef HAVE_LINUX_TIMESTAMPING
NIO_Linux_Finalise(); NIO_Linux_Finalise();
#endif #endif
@@ -250,17 +278,21 @@ NIO_Finalise(void)
int int
NIO_OpenClientSocket(NTP_Remote_Address *remote_addr) NIO_OpenClientSocket(NTP_Remote_Address *remote_addr)
{ {
if (separate_client_sockets) { switch (remote_addr->ip_addr.family) {
return open_separate_client_socket(remote_addr); case IPADDR_INET4:
} else { if (ptp_port > 0 && remote_addr->port == ptp_port)
switch (remote_addr->ip_addr.family) { return ptp_sock_fd4;
case IPADDR_INET4: if (separate_client_sockets)
return client_sock_fd4; return open_separate_client_socket(remote_addr);
case IPADDR_INET6: return client_sock_fd4;
return client_sock_fd6; case IPADDR_INET6:
default: if (ptp_port > 0 && remote_addr->port == ptp_port)
return INVALID_SOCK_FD; return ptp_sock_fd6;
} if (separate_client_sockets)
return open_separate_client_socket(remote_addr);
return client_sock_fd6;
default:
return INVALID_SOCK_FD;
} }
} }
@@ -271,6 +303,8 @@ NIO_OpenServerSocket(NTP_Remote_Address *remote_addr)
{ {
switch (remote_addr->ip_addr.family) { switch (remote_addr->ip_addr.family) {
case IPADDR_INET4: case IPADDR_INET4:
if (ptp_port > 0 && remote_addr->port == ptp_port)
return ptp_sock_fd4;
if (permanent_server_sockets) if (permanent_server_sockets)
return server_sock_fd4; return server_sock_fd4;
if (server_sock_fd4 == INVALID_SOCK_FD) if (server_sock_fd4 == INVALID_SOCK_FD)
@@ -279,6 +313,8 @@ NIO_OpenServerSocket(NTP_Remote_Address *remote_addr)
server_sock_ref4++; server_sock_ref4++;
return server_sock_fd4; return server_sock_fd4;
case IPADDR_INET6: case IPADDR_INET6:
if (ptp_port > 0 && remote_addr->port == ptp_port)
return ptp_sock_fd6;
if (permanent_server_sockets) if (permanent_server_sockets)
return server_sock_fd6; return server_sock_fd6;
if (server_sock_fd6 == INVALID_SOCK_FD) if (server_sock_fd6 == INVALID_SOCK_FD)
@@ -293,9 +329,21 @@ NIO_OpenServerSocket(NTP_Remote_Address *remote_addr)
/* ================================================== */ /* ================================================== */
static int
is_ptp_socket(int sock_fd)
{
return ptp_port > 0 && sock_fd != INVALID_SOCK_FD &&
(sock_fd == ptp_sock_fd4 || sock_fd == ptp_sock_fd6);
}
/* ================================================== */
void void
NIO_CloseClientSocket(int sock_fd) NIO_CloseClientSocket(int sock_fd)
{ {
if (is_ptp_socket(sock_fd))
return;
if (separate_client_sockets) if (separate_client_sockets)
close_socket(sock_fd); close_socket(sock_fd);
} }
@@ -305,7 +353,7 @@ NIO_CloseClientSocket(int sock_fd)
void void
NIO_CloseServerSocket(int sock_fd) NIO_CloseServerSocket(int sock_fd)
{ {
if (permanent_server_sockets || sock_fd == INVALID_SOCK_FD) if (permanent_server_sockets || sock_fd == INVALID_SOCK_FD || is_ptp_socket(sock_fd))
return; return;
if (sock_fd == server_sock_fd4) { if (sock_fd == server_sock_fd4) {
@@ -329,7 +377,7 @@ int
NIO_IsServerSocket(int sock_fd) NIO_IsServerSocket(int sock_fd)
{ {
return sock_fd != INVALID_SOCK_FD && return sock_fd != INVALID_SOCK_FD &&
(sock_fd == server_sock_fd4 || sock_fd == server_sock_fd6); (sock_fd == server_sock_fd4 || sock_fd == server_sock_fd6 || is_ptp_socket(sock_fd));
} }
/* ================================================== */ /* ================================================== */
@@ -337,7 +385,8 @@ NIO_IsServerSocket(int sock_fd)
int int
NIO_IsServerSocketOpen(void) NIO_IsServerSocketOpen(void)
{ {
return server_sock_fd4 != INVALID_SOCK_FD || server_sock_fd6 != INVALID_SOCK_FD; return server_sock_fd4 != INVALID_SOCK_FD || server_sock_fd6 != INVALID_SOCK_FD ||
ptp_sock_fd4 != INVALID_SOCK_FD || ptp_sock_fd6 != INVALID_SOCK_FD;
} }
/* ================================================== */ /* ================================================== */
@@ -392,6 +441,9 @@ process_message(SCK_Message *message, int sock_fd, int event)
DEBUG_LOG("Updated RX timestamp delay=%.9f tss=%u", DEBUG_LOG("Updated RX timestamp delay=%.9f tss=%u",
UTI_DiffTimespecsToDouble(&sched_ts, &local_ts.ts), local_ts.source); UTI_DiffTimespecsToDouble(&sched_ts, &local_ts.ts), local_ts.source);
if (!NIO_UnwrapMessage(message, sock_fd))
return;
/* Just ignore the packet if it's not of a recognized length */ /* Just ignore the packet if it's not of a recognized length */
if (message->length < NTP_HEADER_LENGTH || message->length > sizeof (NTP_Packet)) { if (message->length < NTP_HEADER_LENGTH || message->length > sizeof (NTP_Packet)) {
DEBUG_LOG("Unexpected length"); DEBUG_LOG("Unexpected length");
@@ -430,6 +482,78 @@ read_from_socket(int sock_fd, int event, void *anything)
process_message(&messages[i], sock_fd, event); process_message(&messages[i], sock_fd, event);
} }
/* ================================================== */
int
NIO_UnwrapMessage(SCK_Message *message, int sock_fd)
{
PTP_NtpMessage *msg;
if (!is_ptp_socket(sock_fd))
return 1;
if (message->length <= PTP_NTP_PREFIX_LENGTH) {
DEBUG_LOG("Unexpected length");
return 0;
}
msg = message->data;
if (msg->header.type != PTP_TYPE_DELAY_REQ || msg->header.version != PTP_VERSION ||
ntohs(msg->header.length) != message->length ||
msg->header.domain != PTP_DOMAIN_NTP ||
ntohs(msg->header.flags) != PTP_FLAG_UNICAST ||
ntohs(msg->tlv_header.type) != PTP_TLV_NTP ||
ntohs(msg->tlv_header.length) != message->length - PTP_NTP_PREFIX_LENGTH) {
DEBUG_LOG("Unexpected PTP message");
return 0;
}
message->data = (char *)message->data + PTP_NTP_PREFIX_LENGTH;
message->length -= PTP_NTP_PREFIX_LENGTH;
DEBUG_LOG("Unwrapped PTP->NTP len=%d", message->length);
return 1;
}
/* ================================================== */
static int
wrap_message(SCK_Message *message, int sock_fd)
{
assert(PTP_NTP_PREFIX_LENGTH == 48);
if (!is_ptp_socket(sock_fd))
return 1;
if (!ptp_message)
return 0;
if (message->length < NTP_HEADER_LENGTH ||
message->length + PTP_NTP_PREFIX_LENGTH > sizeof (*ptp_message)) {
DEBUG_LOG("Unexpected length");
return 0;
}
memset(ptp_message, 0, PTP_NTP_PREFIX_LENGTH);
ptp_message->header.type = PTP_TYPE_DELAY_REQ;
ptp_message->header.version = PTP_VERSION;
ptp_message->header.length = htons(PTP_NTP_PREFIX_LENGTH + message->length);
ptp_message->header.domain = PTP_DOMAIN_NTP;
ptp_message->header.flags = htons(PTP_FLAG_UNICAST);
ptp_message->tlv_header.type = htons(PTP_TLV_NTP);
ptp_message->tlv_header.length = htons(message->length);
memcpy((char *)ptp_message + PTP_NTP_PREFIX_LENGTH, message->data, message->length);
message->data = ptp_message;
message->length += PTP_NTP_PREFIX_LENGTH;
DEBUG_LOG("Wrapped NTP->PTP len=%d", message->length - PTP_NTP_PREFIX_LENGTH);
return 1;
}
/* ================================================== */ /* ================================================== */
/* Send a packet to remote address from local address */ /* Send a packet to remote address from local address */
@@ -451,6 +575,9 @@ NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
message.data = packet; message.data = packet;
message.length = length; message.length = length;
if (!wrap_message(&message, local_addr->sock_fd))
return 0;
/* Specify remote address if the socket is not connected */ /* Specify remote address if the socket is not connected */
if (NIO_IsServerSocket(local_addr->sock_fd) || !separate_client_sockets) { if (NIO_IsServerSocket(local_addr->sock_fd) || !separate_client_sockets) {
message.remote_addr.ip.ip_addr = remote_addr->ip_addr; message.remote_addr.ip.ip_addr = remote_addr->ip_addr;
@@ -467,7 +594,7 @@ NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
#if !defined(HAVE_IN_PKTINFO) && defined(IP_SENDSRCADDR) #if !defined(HAVE_IN_PKTINFO) && defined(IP_SENDSRCADDR)
/* On FreeBSD a local IPv4 address cannot be specified on bound socket */ /* On FreeBSD a local IPv4 address cannot be specified on bound socket */
if (message.local_addr.ip.family == IPADDR_INET4 && if (message.local_addr.ip.family == IPADDR_INET4 &&
(local_addr->sock_fd != server_sock_fd4 || bound_server_sock_fd4)) (bound_server_sock_fd4 || !NIO_IsServerSocket(local_addr->sock_fd)))
message.local_addr.ip.family = IPADDR_UNSPEC; message.local_addr.ip.family = IPADDR_UNSPEC;
#endif #endif

View File

@@ -31,6 +31,7 @@
#include "ntp.h" #include "ntp.h"
#include "addressing.h" #include "addressing.h"
#include "socket.h"
/* Function to initialise the module. */ /* Function to initialise the module. */
extern void NIO_Initialise(void); extern void NIO_Initialise(void);
@@ -59,6 +60,9 @@ extern int NIO_IsServerSocketOpen(void);
/* Function to check if client packets can be sent to a server */ /* Function to check if client packets can be sent to a server */
extern int NIO_IsServerConnectable(NTP_Remote_Address *remote_addr); extern int NIO_IsServerConnectable(NTP_Remote_Address *remote_addr);
/* Function to unwrap an NTP message from non-native transport (e.g. PTP) */
extern int NIO_UnwrapMessage(SCK_Message *message, int sock_fd);
/* Function to transmit a packet */ /* Function to transmit a packet */
extern int NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, extern int NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
NTP_Local_Address *local_addr, int length, int process_tx); NTP_Local_Address *local_addr, int length, int process_tx);

View File

@@ -59,8 +59,6 @@ struct Interface {
/* Start of UDP data at layer 2 for IPv4 and IPv6 */ /* Start of UDP data at layer 2 for IPv4 and IPv6 */
int l2_udp4_ntp_start; int l2_udp4_ntp_start;
int l2_udp6_ntp_start; int l2_udp6_ntp_start;
/* Precision of PHC readings */
double precision;
/* Compensation of errors in TX and RX timestamping */ /* Compensation of errors in TX and RX timestamping */
double tx_comp; double tx_comp;
double rx_comp; double rx_comp;
@@ -68,12 +66,12 @@ struct Interface {
}; };
/* Number of PHC readings per HW clock sample */ /* Number of PHC readings per HW clock sample */
#define PHC_READINGS 10 #define PHC_READINGS 25
/* Minimum interval between PHC readings */ /* Minimum interval between PHC readings */
#define MIN_PHC_POLL -6 #define MIN_PHC_POLL -6
/* Maximum acceptable offset between HW and daemon/kernel timestamp */ /* Maximum acceptable offset between SW/HW and daemon timestamp */
#define MAX_TS_DELAY 1.0 #define MAX_TS_DELAY 1.0
/* Array of Interfaces */ /* Array of Interfaces */
@@ -189,6 +187,14 @@ add_interface(CNF_HwTsInterface *conf_iface)
rx_filter = HWTSTAMP_FILTER_NTP_ALL; rx_filter = HWTSTAMP_FILTER_NTP_ALL;
break; break;
#endif #endif
case CNF_HWTS_RXFILTER_PTP:
if (ts_info.rx_filters & (1 << HWTSTAMP_FILTER_PTP_V2_L4_EVENT))
rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT;
else if (ts_info.rx_filters & (1 << HWTSTAMP_FILTER_PTP_V2_EVENT))
rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
else
rx_filter = HWTSTAMP_FILTER_NONE;
break;
default: default:
rx_filter = HWTSTAMP_FILTER_ALL; rx_filter = HWTSTAMP_FILTER_ALL;
break; break;
@@ -236,12 +242,12 @@ add_interface(CNF_HwTsInterface *conf_iface)
iface->l2_udp4_ntp_start = 42; iface->l2_udp4_ntp_start = 42;
iface->l2_udp6_ntp_start = 62; iface->l2_udp6_ntp_start = 62;
iface->precision = conf_iface->precision;
iface->tx_comp = conf_iface->tx_comp; iface->tx_comp = conf_iface->tx_comp;
iface->rx_comp = conf_iface->rx_comp; iface->rx_comp = conf_iface->rx_comp;
iface->clock = HCL_CreateInstance(conf_iface->min_samples, conf_iface->max_samples, iface->clock = HCL_CreateInstance(conf_iface->min_samples, conf_iface->max_samples,
UTI_Log2ToDouble(MAX(conf_iface->minpoll, MIN_PHC_POLL))); UTI_Log2ToDouble(MAX(conf_iface->minpoll, MIN_PHC_POLL)),
conf_iface->precision);
LOG(LOGS_INFO, "Enabled HW timestamping %son %s", LOG(LOGS_INFO, "Enabled HW timestamping %son %s",
ts_config.rx_filter == HWTSTAMP_FILTER_NONE ? "(TX only) " : "", iface->name); ts_config.rx_filter == HWTSTAMP_FILTER_NONE ? "(TX only) " : "", iface->name);
@@ -558,19 +564,22 @@ process_hw_timestamp(struct Interface *iface, struct timespec *hw_ts,
int l2_length) int l2_length)
{ {
struct timespec sample_phc_ts, sample_sys_ts, sample_local_ts, ts; struct timespec sample_phc_ts, sample_sys_ts, sample_local_ts, ts;
struct timespec phc_readings[PHC_READINGS][3];
double rx_correction, ts_delay, phc_err, local_err; double rx_correction, ts_delay, phc_err, local_err;
int n_readings;
if (HCL_NeedsNewSample(iface->clock, &local_ts->ts)) { if (HCL_NeedsNewSample(iface->clock, &local_ts->ts)) {
if (!SYS_Linux_GetPHCSample(iface->phc_fd, iface->phc_nocrossts, iface->precision, n_readings = SYS_Linux_GetPHCReadings(iface->phc_fd, iface->phc_nocrossts,
&iface->phc_mode, &sample_phc_ts, &sample_sys_ts, &iface->phc_mode, PHC_READINGS, phc_readings);
&phc_err)) if (n_readings > 0 &&
return; HCL_ProcessReadings(iface->clock, n_readings, phc_readings,
&sample_phc_ts, &sample_sys_ts, &phc_err)) {
LCL_CookTime(&sample_sys_ts, &sample_local_ts, &local_err);
HCL_AccumulateSample(iface->clock, &sample_phc_ts, &sample_local_ts,
phc_err + local_err);
LCL_CookTime(&sample_sys_ts, &sample_local_ts, &local_err); update_interface_speed(iface);
HCL_AccumulateSample(iface->clock, &sample_phc_ts, &sample_local_ts, }
phc_err + local_err);
update_interface_speed(iface);
} }
/* We need to transpose RX timestamps as hardware timestamps are normally /* We need to transpose RX timestamps as hardware timestamps are normally
@@ -611,6 +620,28 @@ process_hw_timestamp(struct Interface *iface, struct timespec *hw_ts,
local_ts->source = NTP_TS_HARDWARE; local_ts->source = NTP_TS_HARDWARE;
} }
/* ================================================== */
static void
process_sw_timestamp(struct timespec *sw_ts, NTP_Local_Timestamp *local_ts)
{
double ts_delay, local_err;
struct timespec ts;
LCL_CookTime(sw_ts, &ts, &local_err);
ts_delay = UTI_DiffTimespecsToDouble(&local_ts->ts, &ts);
if (fabs(ts_delay) > MAX_TS_DELAY) {
DEBUG_LOG("Unacceptable timestamp delay %.9f", ts_delay);
return;
}
local_ts->ts = ts;
local_ts->err = local_err;
local_ts->source = NTP_TS_KERNEL;
}
/* ================================================== */ /* ================================================== */
/* Extract UDP data from a layer 2 message. Supported is Ethernet /* Extract UDP data from a layer 2 message. Supported is Ethernet
with optional VLAN tags. */ with optional VLAN tags. */
@@ -736,8 +767,7 @@ NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
if (local_ts->source == NTP_TS_DAEMON && !UTI_IsZeroTimespec(&message->timestamp.kernel) && if (local_ts->source == NTP_TS_DAEMON && !UTI_IsZeroTimespec(&message->timestamp.kernel) &&
(!is_tx || UTI_IsZeroTimespec(&message->timestamp.hw))) { (!is_tx || UTI_IsZeroTimespec(&message->timestamp.hw))) {
LCL_CookTime(&message->timestamp.kernel, &local_ts->ts, &local_ts->err); process_sw_timestamp(&message->timestamp.kernel, local_ts);
local_ts->source = NTP_TS_KERNEL;
} }
/* If the kernel is slow with enabling RX timestamping, open a dummy /* If the kernel is slow with enabling RX timestamping, open a dummy
@@ -776,7 +806,10 @@ NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
return 1; return 1;
} }
if (message->length < NTP_HEADER_LENGTH) if (!NIO_UnwrapMessage(message, local_addr->sock_fd))
return 1;
if (message->length < NTP_HEADER_LENGTH || message->length > sizeof (NTP_Packet))
return 1; return 1;
NSR_ProcessTx(&message->remote_addr.ip, local_addr, local_ts, message->data, message->length); NSR_ProcessTx(&message->remote_addr.ip, local_addr, local_ts, message->data, message->length);

View File

@@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011-2012, 2014, 2016, 2020 * Copyright (C) Miroslav Lichvar 2011-2012, 2014, 2016, 2020-2021
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of version 2 of the GNU General Public License as
@@ -45,6 +45,9 @@
/* ================================================== */ /* ================================================== */
/* Maximum number of sources */
#define MAX_SOURCES 65536
/* Record type private to this file, used to store information about /* Record type private to this file, used to store information about
particular sources */ particular sources */
typedef struct { typedef struct {
@@ -53,7 +56,8 @@ typedef struct {
(an IPADDR_ID address means the address (an IPADDR_ID address means the address
is not resolved yet) */ is not resolved yet) */
NCR_Instance data; /* Data for the protocol engine for this source */ NCR_Instance data; /* Data for the protocol engine for this source */
char *name; /* Name of the source, may be NULL */ char *name; /* Name of the source as it was specified
(may be an IP address) */
int pool_id; /* ID of the pool from which was this source int pool_id; /* ID of the pool from which was this source
added or INVALID_POOL */ added or INVALID_POOL */
int tentative; /* Flag indicating there was no valid response int tentative; /* Flag indicating there was no valid response
@@ -72,6 +76,9 @@ static int n_sources;
/* Flag indicating new sources will be started automatically when added */ /* Flag indicating new sources will be started automatically when added */
static int auto_start_sources = 0; static int auto_start_sources = 0;
/* Flag indicating a record is currently being modified */
static int record_lock;
/* Last assigned address ID */ /* Last assigned address ID */
static uint32_t last_address_id = 0; static uint32_t last_address_id = 0;
@@ -100,6 +107,7 @@ struct UnresolvedSource {
static struct UnresolvedSource *unresolved_sources = NULL; static struct UnresolvedSource *unresolved_sources = NULL;
static int resolving_interval = 0; static int resolving_interval = 0;
static int resolving_restart = 0;
static SCH_TimeoutID resolving_id; static SCH_TimeoutID resolving_id;
static struct UnresolvedSource *resolving_source = NULL; static struct UnresolvedSource *resolving_source = NULL;
static NSR_SourceResolvingEndHandler resolving_end_handler = NULL; static NSR_SourceResolvingEndHandler resolving_end_handler = NULL;
@@ -122,11 +130,21 @@ struct SourcePool {
/* Array of SourcePool (indexed by their ID) */ /* Array of SourcePool (indexed by their ID) */
static ARR_Instance pools; static ARR_Instance pools;
/* Requested update of a source's address */
struct AddressUpdate {
NTP_Remote_Address old_address;
NTP_Remote_Address new_address;
};
/* Update saved when record_lock is true */
static struct AddressUpdate saved_address_update;
/* ================================================== */ /* ================================================== */
/* Forward prototypes */ /* Forward prototypes */
static void resolve_sources(void); static void resolve_sources(void);
static void rehash_records(void); static void rehash_records(void);
static void handle_saved_address_update(void);
static void clean_source_record(SourceRecord *record); static void clean_source_record(SourceRecord *record);
static void remove_pool_sources(int pool_id, int tentative, int unresolved); static void remove_pool_sources(int pool_id, int tentative, int unresolved);
static void remove_unresolved_source(struct UnresolvedSource *us); static void remove_unresolved_source(struct UnresolvedSource *us);
@@ -181,14 +199,7 @@ NSR_Initialise(void)
void void
NSR_Finalise(void) NSR_Finalise(void)
{ {
SourceRecord *record; NSR_RemoveAllSources();
unsigned int i;
for (i = 0; i < ARR_GetSize(records); i++) {
record = get_record(i);
if (record->remote_addr)
clean_source_record(record);
}
LCL_RemoveParameterChangeHandler(slew_sources, NULL); LCL_RemoveParameterChangeHandler(slew_sources, NULL);
@@ -275,6 +286,8 @@ rehash_records(void)
unsigned int i, old_size, new_size; unsigned int i, old_size, new_size;
int slot; int slot;
assert(!record_lock);
old_size = ARR_GetSize(records); old_size = ARR_GetSize(records);
temp_records = MallocArray(SourceRecord, old_size); temp_records = MallocArray(SourceRecord, old_size);
@@ -317,6 +330,11 @@ add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type,
/* Find empty bin & check that we don't have the address already */ /* Find empty bin & check that we don't have the address already */
if (find_slot2(remote_addr, &slot) != 0) { if (find_slot2(remote_addr, &slot) != 0) {
return NSR_AlreadyInUse; return NSR_AlreadyInUse;
} else if (!name && !UTI_IsIPReal(&remote_addr->ip_addr)) {
/* Name is required for non-real addresses */
return NSR_InvalidName;
} else if (n_sources >= MAX_SOURCES) {
return NSR_TooManySources;
} else { } else {
if (remote_addr->ip_addr.family != IPADDR_INET4 && if (remote_addr->ip_addr.family != IPADDR_INET4 &&
remote_addr->ip_addr.family != IPADDR_INET6 && remote_addr->ip_addr.family != IPADDR_INET6 &&
@@ -331,14 +349,19 @@ add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type,
assert(0); assert(0);
} }
assert(!record_lock);
record_lock = 1;
record = get_record(slot); record = get_record(slot);
record->data = NCR_CreateInstance(remote_addr, type, params, name); record->name = Strdup(name ? name : UTI_IPToString(&remote_addr->ip_addr));
record->data = NCR_CreateInstance(remote_addr, type, params, record->name);
record->remote_addr = NCR_GetRemoteAddress(record->data); record->remote_addr = NCR_GetRemoteAddress(record->data);
record->name = name ? Strdup(name) : NULL;
record->pool_id = pool_id; record->pool_id = pool_id;
record->tentative = 1; record->tentative = 1;
record->conf_id = conf_id; record->conf_id = conf_id;
record_lock = 0;
if (record->pool_id != INVALID_POOL) { if (record->pool_id != INVALID_POOL) {
get_pool(record->pool_id)->sources++; get_pool(record->pool_id)->sources++;
if (!UTI_IsIPReal(&remote_addr->ip_addr)) if (!UTI_IsIPReal(&remote_addr->ip_addr))
@@ -348,6 +371,9 @@ add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type,
if (auto_start_sources && UTI_IsIPReal(&remote_addr->ip_addr)) if (auto_start_sources && UTI_IsIPReal(&remote_addr->ip_addr))
NCR_StartInstance(record->data); NCR_StartInstance(record->data);
/* The new instance is allowed to change its address immediately */
handle_saved_address_update();
return NSR_Success; return NSR_Success;
} }
} }
@@ -365,7 +391,7 @@ change_source_address(NTP_Remote_Address *old_addr, NTP_Remote_Address *new_addr
char *name; char *name;
found = find_slot2(old_addr, &slot1); found = find_slot2(old_addr, &slot1);
if (found == 0) if (found != 2)
return NSR_NoSuchSource; return NSR_NoSuchSource;
/* Make sure there is no other source using the new address (with the same /* Make sure there is no other source using the new address (with the same
@@ -374,9 +400,16 @@ change_source_address(NTP_Remote_Address *old_addr, NTP_Remote_Address *new_addr
if (found == 2 || (found != 0 && slot1 != slot2)) if (found == 2 || (found != 0 && slot1 != slot2))
return NSR_AlreadyInUse; return NSR_AlreadyInUse;
assert(!record_lock);
record_lock = 1;
record = get_record(slot1); record = get_record(slot1);
NCR_ChangeRemoteAddress(record->data, new_addr, !replacement); NCR_ChangeRemoteAddress(record->data, new_addr, !replacement);
record->remote_addr = NCR_GetRemoteAddress(record->data);
if (record->remote_addr != NCR_GetRemoteAddress(record->data) ||
UTI_CompareIPs(&record->remote_addr->ip_addr, &new_addr->ip_addr, NULL) != 0)
assert(0);
if (!UTI_IsIPReal(&old_addr->ip_addr) && UTI_IsIPReal(&new_addr->ip_addr)) { if (!UTI_IsIPReal(&old_addr->ip_addr) && UTI_IsIPReal(&new_addr->ip_addr)) {
if (auto_start_sources) if (auto_start_sources)
NCR_StartInstance(record->data); NCR_StartInstance(record->data);
@@ -391,6 +424,8 @@ change_source_address(NTP_Remote_Address *old_addr, NTP_Remote_Address *new_addr
get_pool(record->pool_id)->confirmed_sources--; get_pool(record->pool_id)->confirmed_sources--;
} }
record_lock = 0;
name = record->name; name = record->name;
severity = UTI_IsIPReal(&old_addr->ip_addr) ? LOGS_INFO : LOGS_DEBUG; severity = UTI_IsIPReal(&old_addr->ip_addr) ? LOGS_INFO : LOGS_DEBUG;
@@ -400,10 +435,10 @@ change_source_address(NTP_Remote_Address *old_addr, NTP_Remote_Address *new_addr
LOG(severity, "Source %s %s %s (%s)", UTI_IPToString(&old_addr->ip_addr), LOG(severity, "Source %s %s %s (%s)", UTI_IPToString(&old_addr->ip_addr),
replacement ? "replaced with" : "changed to", replacement ? "replaced with" : "changed to",
UTI_IPToString(&new_addr->ip_addr), name ? name : ""); UTI_IPToString(&new_addr->ip_addr), name);
} else { } else {
LOG(severity, "Source %s (%s) changed port to %d", LOG(severity, "Source %s (%s) changed port to %d",
UTI_IPToString(&new_addr->ip_addr), name ? name : "", new_addr->port); UTI_IPToString(&new_addr->ip_addr), name, new_addr->port);
} }
return NSR_Success; return NSR_Success;
@@ -411,6 +446,24 @@ change_source_address(NTP_Remote_Address *old_addr, NTP_Remote_Address *new_addr
/* ================================================== */ /* ================================================== */
static void
handle_saved_address_update(void)
{
if (!UTI_IsIPReal(&saved_address_update.old_address.ip_addr))
return;
if (change_source_address(&saved_address_update.old_address,
&saved_address_update.new_address, 0) != NSR_Success)
/* This is expected to happen only if the old address is wrong */
LOG(LOGS_ERR, "Could not change %s to %s",
UTI_IPSockAddrToString(&saved_address_update.old_address),
UTI_IPSockAddrToString(&saved_address_update.new_address));
saved_address_update.old_address.ip_addr.family = IPADDR_UNSPEC;
}
/* ================================================== */
static int static int
replace_source_connectable(NTP_Remote_Address *old_addr, NTP_Remote_Address *new_addr) replace_source_connectable(NTP_Remote_Address *old_addr, NTP_Remote_Address *new_addr)
{ {
@@ -422,6 +475,8 @@ replace_source_connectable(NTP_Remote_Address *old_addr, NTP_Remote_Address *new
if (change_source_address(old_addr, new_addr, 1) == NSR_AlreadyInUse) if (change_source_address(old_addr, new_addr, 1) == NSR_AlreadyInUse)
return 0; return 0;
handle_saved_address_update();
return 1; return 1;
} }
@@ -524,6 +579,13 @@ name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *any
if (status == DNS_Failure || UTI_IsIPReal(&us->address.ip_addr) || is_resolved(us)) if (status == DNS_Failure || UTI_IsIPReal(&us->address.ip_addr) || is_resolved(us))
remove_unresolved_source(us); remove_unresolved_source(us);
/* If a restart was requested and this was the last source in the list,
start with the first source again (if there still is one) */
if (!next && resolving_restart) {
next = unresolved_sources;
resolving_restart = 0;
}
resolving_source = next; resolving_source = next;
if (next) { if (next) {
@@ -635,21 +697,25 @@ static int get_unused_pool_id(void)
/* ================================================== */ /* ================================================== */
static uint32_t
get_next_conf_id(uint32_t *conf_id)
{
last_conf_id++;
if (conf_id)
*conf_id = last_conf_id;
return last_conf_id;
}
/* ================================================== */
NSR_Status NSR_Status
NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type, NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
SourceParameters *params, uint32_t *conf_id) SourceParameters *params, uint32_t *conf_id)
{ {
NSR_Status s; return add_source(remote_addr, NULL, type, params, INVALID_POOL,
get_next_conf_id(conf_id));
s = add_source(remote_addr, NULL, type, params, INVALID_POOL, last_conf_id + 1);
if (s != NSR_Success)
return s;
last_conf_id++;
if (conf_id)
*conf_id = last_conf_id;
return s;
} }
/* ================================================== */ /* ================================================== */
@@ -662,12 +728,13 @@ NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
struct SourcePool *sp; struct SourcePool *sp;
NTP_Remote_Address remote_addr; NTP_Remote_Address remote_addr;
int i, new_sources, pool_id; int i, new_sources, pool_id;
uint32_t cid;
/* If the name is an IP address, don't bother with full resolving now /* If the name is an IP address, add the source with the address directly */
or later when trying to replace the source */
if (UTI_StringToIP(name, &remote_addr.ip_addr)) { if (UTI_StringToIP(name, &remote_addr.ip_addr)) {
remote_addr.port = port; remote_addr.port = port;
return NSR_AddSource(&remote_addr, type, params, conf_id); return add_source(&remote_addr, name, type, params, INVALID_POOL,
get_next_conf_id(conf_id));
} }
/* Make sure the name is at least printable and has no spaces */ /* Make sure the name is at least printable and has no spaces */
@@ -708,14 +775,12 @@ NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
append_unresolved_source(us); append_unresolved_source(us);
last_conf_id++; cid = get_next_conf_id(conf_id);
if (conf_id)
*conf_id = last_conf_id;
for (i = 0; i < new_sources; i++) { for (i = 0; i < new_sources; i++) {
if (i > 0) if (i > 0)
remote_addr.ip_addr.addr.id = ++last_address_id; remote_addr.ip_addr.addr.id = ++last_address_id;
if (add_source(&remote_addr, name, type, params, us->pool_id, last_conf_id) != NSR_Success) if (add_source(&remote_addr, name, type, params, us->pool_id, cid) != NSR_Success)
return NSR_TooManySources; return NSR_TooManySources;
} }
@@ -737,7 +802,7 @@ NSR_ResolveSources(void)
{ {
/* Try to resolve unresolved sources now */ /* Try to resolve unresolved sources now */
if (unresolved_sources) { if (unresolved_sources) {
/* Make sure no resolving is currently running */ /* Allow only one resolving to be running at a time */
if (!resolving_source) { if (!resolving_source) {
if (resolving_id != 0) { if (resolving_id != 0) {
SCH_RemoveTimeout(resolving_id); SCH_RemoveTimeout(resolving_id);
@@ -745,6 +810,9 @@ NSR_ResolveSources(void)
resolving_interval--; resolving_interval--;
} }
resolve_sources(); resolve_sources();
} else {
/* Try again as soon as the current resolving ends */
resolving_restart = 1;
} }
} else { } else {
/* No unresolved sources, we are done */ /* No unresolved sources, we are done */
@@ -796,8 +864,7 @@ clean_source_record(SourceRecord *record)
record->remote_addr = NULL; record->remote_addr = NULL;
NCR_DestroyInstance(record->data); NCR_DestroyInstance(record->data);
if (record->name) Free(record->name);
Free(record->name);
n_sources--; n_sources--;
} }
@@ -870,7 +937,8 @@ resolve_source_replacement(SourceRecord *record)
{ {
struct UnresolvedSource *us; struct UnresolvedSource *us;
DEBUG_LOG("trying to replace %s", UTI_IPToString(&record->remote_addr->ip_addr)); DEBUG_LOG("trying to replace %s (%s)",
UTI_IPToString(&record->remote_addr->ip_addr), record->name);
us = MallocNew(struct UnresolvedSource); us = MallocNew(struct UnresolvedSource);
us->name = Strdup(record->name); us->name = Strdup(record->name);
@@ -894,6 +962,7 @@ NSR_HandleBadSource(IPAddr *address)
static struct timespec last_replacement; static struct timespec last_replacement;
struct timespec now; struct timespec now;
SourceRecord *record; SourceRecord *record;
IPAddr ip_addr;
double diff; double diff;
int slot; int slot;
@@ -902,8 +971,10 @@ NSR_HandleBadSource(IPAddr *address)
record = get_record(slot); record = get_record(slot);
/* Only sources with a name can be replaced */ /* Don't try to replace a source specified by an IP address unless the
if (!record->name) address changed since the source was added (e.g. by NTS-KE) */
if (UTI_StringToIP(record->name, &ip_addr) &&
UTI_CompareIPs(&record->remote_addr->ip_addr, &ip_addr, NULL) == 0)
return; return;
/* Don't resolve names too frequently */ /* Don't resolve names too frequently */
@@ -928,7 +999,7 @@ NSR_RefreshAddresses(void)
for (i = 0; i < ARR_GetSize(records); i++) { for (i = 0; i < ARR_GetSize(records); i++) {
record = get_record(i); record = get_record(i);
if (!record->remote_addr || !record->name) if (!record->remote_addr)
continue; continue;
resolve_source_replacement(record); resolve_source_replacement(record);
@@ -940,10 +1011,28 @@ NSR_RefreshAddresses(void)
NSR_Status NSR_Status
NSR_UpdateSourceNtpAddress(NTP_Remote_Address *old_addr, NTP_Remote_Address *new_addr) NSR_UpdateSourceNtpAddress(NTP_Remote_Address *old_addr, NTP_Remote_Address *new_addr)
{ {
if (new_addr->ip_addr.family == IPADDR_UNSPEC) int slot;
if (!UTI_IsIPReal(&old_addr->ip_addr) || !UTI_IsIPReal(&new_addr->ip_addr))
return NSR_InvalidAF; return NSR_InvalidAF;
return change_source_address(old_addr, new_addr, 0); if (UTI_CompareIPs(&old_addr->ip_addr, &new_addr->ip_addr, NULL) != 0 &&
find_slot(&new_addr->ip_addr, &slot))
return NSR_AlreadyInUse;
/* If a record is being modified (e.g. by change_source_address(), or the
source is just being created), postpone the change to avoid corruption */
if (!record_lock)
return change_source_address(old_addr, new_addr, 0);
if (UTI_IsIPReal(&saved_address_update.old_address.ip_addr))
return NSR_TooManySources;
saved_address_update.old_address = *old_addr;
saved_address_update.new_address = *new_addr;
return NSR_Success;
} }
/* ================================================== */ /* ================================================== */
@@ -992,17 +1081,12 @@ NSR_GetLocalRefid(IPAddr *address)
char * char *
NSR_GetName(IPAddr *address) NSR_GetName(IPAddr *address)
{ {
SourceRecord *record;
int slot; int slot;
if (!find_slot(address, &slot)) if (!find_slot(address, &slot))
return 0; return NULL;
record = get_record(slot); return get_record(slot)->name;
if (record->name)
return record->name;
return UTI_IPToString(&record->remote_addr->ip_addr);
} }
/* ================================================== */ /* ================================================== */
@@ -1019,8 +1103,10 @@ NSR_ProcessRx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
assert(initialised); assert(initialised);
/* Must match IP address AND port number */ /* Avoid unnecessary lookup if the packet cannot be a response from our
if (find_slot2(remote_addr, &slot) == 2) { source. Otherwise, it must match both IP address and port number. */
if (NTP_LVM_TO_MODE(message->lvm) != MODE_CLIENT &&
find_slot2(remote_addr, &slot) == 2) {
record = get_record(slot); record = get_record(slot);
if (!NCR_ProcessRxKnown(record->data, local_addr, rx_ts, message, length)) if (!NCR_ProcessRxKnown(record->data, local_addr, rx_ts, message, length))
@@ -1056,8 +1142,10 @@ NSR_ProcessTx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
SourceRecord *record; SourceRecord *record;
int slot; int slot;
/* Must match IP address AND port number */ /* Avoid unnecessary lookup if the packet cannot be a request to our
if (find_slot2(remote_addr, &slot) == 2) { source. Otherwise, it must match both IP address and port number. */
if (NTP_LVM_TO_MODE(message->lvm) != MODE_SERVER &&
find_slot2(remote_addr, &slot) == 2) {
record = get_record(slot); record = get_record(slot);
NCR_ProcessTxKnown(record->data, local_addr, tx_ts, message, length); NCR_ProcessTxKnown(record->data, local_addr, tx_ts, message, length);
} else { } else {

View File

@@ -91,15 +91,16 @@ extern void NSR_HandleBadSource(IPAddr *address);
/* Procedure to resolve all names again */ /* Procedure to resolve all names again */
extern void NSR_RefreshAddresses(void); extern void NSR_RefreshAddresses(void);
/* Procedure to update the address of a source */ /* Procedure to update the address of a source. The update may be
postponed. */
extern NSR_Status NSR_UpdateSourceNtpAddress(NTP_Remote_Address *old_addr, extern NSR_Status NSR_UpdateSourceNtpAddress(NTP_Remote_Address *old_addr,
NTP_Remote_Address *new_addr); NTP_Remote_Address *new_addr);
/* Procedure to get local reference ID corresponding to a source */ /* Procedure to get local reference ID corresponding to a source */
extern uint32_t NSR_GetLocalRefid(IPAddr *address); extern uint32_t NSR_GetLocalRefid(IPAddr *address);
/* Procedure to get the name of a source. If the source doesn't have a name, /* Procedure to get the name of a source as it was specified (it may be
it returns a temporary string containing formatted address. */ an IP address) */
extern char *NSR_GetName(IPAddr *address); extern char *NSR_GetName(IPAddr *address);
/* This routine is called by ntp_io when a new packet arrives off the network */ /* This routine is called by ntp_io when a new packet arrives off the network */

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate. chronyd/chronyc - Programs for keeping computer clocks accurate.
********************************************************************** **********************************************************************
* Copyright (C) Miroslav Lichvar 2020 * Copyright (C) Miroslav Lichvar 2020-2021
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of version 2 of the GNU General Public License as
@@ -44,6 +44,7 @@
struct NKC_Instance_Record { struct NKC_Instance_Record {
char *name; char *name;
IPSockAddr address; IPSockAddr address;
NKSN_Credentials credentials;
NKSN_Instance session; NKSN_Instance session;
int destroying; int destroying;
int got_response; int got_response;
@@ -52,14 +53,14 @@ struct NKC_Instance_Record {
NKE_Context context; NKE_Context context;
NKE_Cookie cookies[NKE_MAX_COOKIES]; NKE_Cookie cookies[NKE_MAX_COOKIES];
int num_cookies; int num_cookies;
char server_name[NKE_MAX_RECORD_BODY_LENGTH + 1]; char server_name[NKE_MAX_RECORD_BODY_LENGTH + 2];
IPSockAddr ntp_address; IPSockAddr ntp_address;
}; };
/* ================================================== */ /* ================================================== */
static void *client_credentials = NULL; static NKSN_Credentials default_credentials = NULL;
static int client_credentials_refs = 0; static int default_credentials_refs = 0;
/* ================================================== */ /* ================================================== */
@@ -126,9 +127,10 @@ process_response(NKC_Instance inst)
{ {
int next_protocol = -1, aead_algorithm = -1, error = 0; int next_protocol = -1, aead_algorithm = -1, error = 0;
int i, critical, type, length; int i, critical, type, length;
uint16_t data[NKE_MAX_COOKIE_LENGTH / sizeof (uint16_t)]; uint16_t data[NKE_MAX_RECORD_BODY_LENGTH / sizeof (uint16_t)];
assert(NKE_MAX_COOKIE_LENGTH % sizeof (uint16_t) == 0); assert(NKE_MAX_COOKIE_LENGTH <= NKE_MAX_RECORD_BODY_LENGTH);
assert(sizeof (data) % sizeof (uint16_t) == 0);
assert(sizeof (uint16_t) == 2); assert(sizeof (uint16_t) == 2);
inst->num_cookies = 0; inst->num_cookies = 0;
@@ -140,6 +142,13 @@ process_response(NKC_Instance inst)
if (!NKSN_GetRecord(inst->session, &critical, &type, &length, &data, sizeof (data))) if (!NKSN_GetRecord(inst->session, &critical, &type, &length, &data, sizeof (data)))
break; break;
if (length > sizeof (data)) {
DEBUG_LOG("Record too long type=%d length=%d critical=%d", type, length, critical);
if (critical)
error = 1;
continue;
}
switch (type) { switch (type) {
case NKE_RECORD_NEXT_PROTOCOL: case NKE_RECORD_NEXT_PROTOCOL:
if (!critical || length != 2 || ntohs(data[0]) != NKE_NEXT_PROTOCOL_NTPV4) { if (!critical || length != 2 || ntohs(data[0]) != NKE_NEXT_PROTOCOL_NTPV4) {
@@ -253,6 +262,17 @@ handle_message(void *arg)
if (inst->resolving_name) if (inst->resolving_name)
return 0; return 0;
if (!UTI_StringToIP(inst->server_name, &inst->ntp_address.ip_addr)) { if (!UTI_StringToIP(inst->server_name, &inst->ntp_address.ip_addr)) {
int length = strlen(inst->server_name);
/* Add a trailing dot if not present to force the name to be
resolved as a fully qualified domain name */
if (length < 1 || length + 1 >= sizeof (inst->server_name))
return 0;
if (inst->server_name[length - 1] != '.') {
inst->server_name[length] = '.';
inst->server_name[length + 1] = '\0';
}
DNS_Name2IPAddressAsync(inst->server_name, name_resolve_handler, inst); DNS_Name2IPAddressAsync(inst->server_name, name_resolve_handler, inst);
inst->resolving_name = 1; inst->resolving_name = 1;
} }
@@ -266,9 +286,12 @@ handle_message(void *arg)
/* ================================================== */ /* ================================================== */
NKC_Instance NKC_Instance
NKC_CreateInstance(IPSockAddr *address, const char *name) NKC_CreateInstance(IPSockAddr *address, const char *name, uint32_t cert_set)
{ {
const char **trusted_certs;
uint32_t *certs_ids;
NKC_Instance inst; NKC_Instance inst;
int n_certs;
inst = MallocNew(struct NKC_Instance_Record); inst = MallocNew(struct NKC_Instance_Record);
@@ -279,10 +302,21 @@ NKC_CreateInstance(IPSockAddr *address, const char *name)
inst->destroying = 0; inst->destroying = 0;
inst->got_response = 0; inst->got_response = 0;
/* Share the credentials with other client instances */ n_certs = CNF_GetNtsTrustedCertsPaths(&trusted_certs, &certs_ids);
if (!client_credentials)
client_credentials = NKSN_CreateCertCredentials(NULL, NULL, CNF_GetNtsTrustedCertFile()); /* Share the credentials among clients using the default set of trusted
client_credentials_refs++; certificates, which likely contains most certificates */
if (cert_set == 0) {
if (!default_credentials)
default_credentials = NKSN_CreateClientCertCredentials(trusted_certs, certs_ids,
n_certs, cert_set);
inst->credentials = default_credentials;
if (default_credentials)
default_credentials_refs++;
} else {
inst->credentials = NKSN_CreateClientCertCredentials(trusted_certs, certs_ids,
n_certs, cert_set);
}
return inst; return inst;
} }
@@ -296,10 +330,16 @@ NKC_DestroyInstance(NKC_Instance inst)
Free(inst->name); Free(inst->name);
client_credentials_refs--; if (inst->credentials) {
if (client_credentials_refs <= 0 && client_credentials) { if (inst->credentials == default_credentials) {
NKSN_DestroyCertCredentials(client_credentials); default_credentials_refs--;
client_credentials = NULL; if (default_credentials_refs <= 0) {
NKSN_DestroyCertCredentials(default_credentials);
default_credentials = NULL;
}
} else {
NKSN_DestroyCertCredentials(inst->credentials);
}
} }
/* If the asynchronous resolver is running, let the handler free /* If the asynchronous resolver is running, let the handler free
@@ -325,7 +365,7 @@ NKC_Start(NKC_Instance inst)
inst->got_response = 0; inst->got_response = 0;
if (!client_credentials) { if (!inst->credentials) {
DEBUG_LOG("Missing client credentials"); DEBUG_LOG("Missing client credentials");
return 0; return 0;
} }
@@ -347,7 +387,7 @@ NKC_Start(NKC_Instance inst)
} }
/* Start an NTS-KE session */ /* Start an NTS-KE session */
if (!NKSN_StartSession(inst->session, sock_fd, label, client_credentials, CLIENT_TIMEOUT)) { if (!NKSN_StartSession(inst->session, sock_fd, label, inst->credentials, CLIENT_TIMEOUT)) {
SCK_CloseSocket(sock_fd); SCK_CloseSocket(sock_fd);
return 0; return 0;
} }

View File

@@ -33,7 +33,7 @@
typedef struct NKC_Instance_Record *NKC_Instance; typedef struct NKC_Instance_Record *NKC_Instance;
/* Create a client NTS-KE instance */ /* Create a client NTS-KE instance */
extern NKC_Instance NKC_CreateInstance(IPSockAddr *address, const char *name); extern NKC_Instance NKC_CreateInstance(IPSockAddr *address, const char *name, uint32_t cert_set);
/* Destroy an instance */ /* Destroy an instance */
extern void NKC_DestroyInstance(NKC_Instance inst); extern void NKC_DestroyInstance(NKC_Instance inst);

View File

@@ -95,7 +95,7 @@ static int initialised = 0;
/* Array of NKSN instances */ /* Array of NKSN instances */
static ARR_Instance sessions; static ARR_Instance sessions;
static void *server_credentials; static NKSN_Credentials server_credentials;
/* ================================================== */ /* ================================================== */
@@ -669,6 +669,8 @@ run_helper(uid_t uid, gid_t gid, int scfilter_level)
CNF_Finalise(); CNF_Finalise();
LOG_Finalise(); LOG_Finalise();
UTI_ResetGetRandomFunctions();
exit(0); exit(0);
} }
@@ -678,13 +680,14 @@ void
NKS_PreInitialise(uid_t uid, gid_t gid, int scfilter_level) NKS_PreInitialise(uid_t uid, gid_t gid, int scfilter_level)
{ {
int i, processes, sock_fd1, sock_fd2; int i, processes, sock_fd1, sock_fd2;
const char **certs, **keys;
char prefix[16]; char prefix[16];
pid_t pid; pid_t pid;
helper_sock_fd = INVALID_SOCK_FD; helper_sock_fd = INVALID_SOCK_FD;
is_helper = 0; is_helper = 0;
if (!CNF_GetNtsServerCertFile() || !CNF_GetNtsServerKeyFile()) if (CNF_GetNtsServerCertAndKeyFiles(&certs, &keys) <= 0)
return; return;
processes = CNF_GetNtsServerProcesses(); processes = CNF_GetNtsServerProcesses();
@@ -709,6 +712,8 @@ NKS_PreInitialise(uid_t uid, gid_t gid, int scfilter_level)
is_helper = 1; is_helper = 1;
UTI_ResetGetRandomFunctions();
snprintf(prefix, sizeof (prefix), "nks#%d:", i + 1); snprintf(prefix, sizeof (prefix), "nks#%d:", i + 1);
LOG_SetDebugPrefix(prefix); LOG_SetDebugPrefix(prefix);
LOG_CloseParentFd(); LOG_CloseParentFd();
@@ -728,21 +733,19 @@ NKS_PreInitialise(uid_t uid, gid_t gid, int scfilter_level)
void void
NKS_Initialise(void) NKS_Initialise(void)
{ {
char *cert, *key; const char **certs, **keys;
int i, n_certs_keys;
double key_delay; double key_delay;
int i;
server_sock_fd4 = INVALID_SOCK_FD; server_sock_fd4 = INVALID_SOCK_FD;
server_sock_fd6 = INVALID_SOCK_FD; server_sock_fd6 = INVALID_SOCK_FD;
cert = CNF_GetNtsServerCertFile(); n_certs_keys = CNF_GetNtsServerCertAndKeyFiles(&certs, &keys);
key = CNF_GetNtsServerKeyFile(); if (n_certs_keys <= 0)
if (!cert || !key)
return; return;
if (helper_sock_fd == INVALID_SOCK_FD) { if (helper_sock_fd == INVALID_SOCK_FD) {
server_credentials = NKSN_CreateCertCredentials(cert, key, NULL); server_credentials = NKSN_CreateServerCertCredentials(certs, keys, n_certs_keys);
if (!server_credentials) if (!server_credentials)
return; return;
} else { } else {

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate. chronyd/chronyc - Programs for keeping computer clocks accurate.
********************************************************************** **********************************************************************
* Copyright (C) Miroslav Lichvar 2020 * Copyright (C) Miroslav Lichvar 2020-2021
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of version 2 of the GNU General Public License as
@@ -225,9 +225,13 @@ create_tls_session(int server_mode, int sock_fd, const char *server_name,
} }
if (!server_mode) { if (!server_mode) {
r = gnutls_server_name_set(session, GNUTLS_NAME_DNS, server_name, strlen(server_name)); assert(server_name);
if (r < 0)
goto error; if (!UTI_IsStringIP(server_name)) {
r = gnutls_server_name_set(session, GNUTLS_NAME_DNS, server_name, strlen(server_name));
if (r < 0)
goto error;
}
flags = 0; flags = 0;
@@ -590,13 +594,13 @@ handle_step(struct timespec *raw, struct timespec *cooked, double dfreq,
static int gnutls_initialised = 0; static int gnutls_initialised = 0;
static void static int
init_gnutls(void) init_gnutls(void)
{ {
int r; int r;
if (gnutls_initialised) if (gnutls_initialised)
return; return 1;
r = gnutls_global_init(); r = gnutls_global_init();
if (r < 0) if (r < 0)
@@ -607,8 +611,12 @@ init_gnutls(void)
r = gnutls_priority_init2(&priority_cache, r = gnutls_priority_init2(&priority_cache,
"-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.2:-VERS-DTLS-ALL", "-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.2:-VERS-DTLS-ALL",
NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND); NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND);
if (r < 0) if (r < 0) {
LOG_FATAL("Could not initialise %s : %s", "priority cache", gnutls_strerror(r)); LOG(LOGS_ERR, "Could not initialise %s : %s",
"priority cache for TLS", gnutls_strerror(r));
gnutls_global_deinit();
return 0;
}
/* Use our clock instead of the system clock in certificate verification */ /* Use our clock instead of the system clock in certificate verification */
gnutls_global_set_time_function(get_time); gnutls_global_set_time_function(get_time);
@@ -617,6 +625,8 @@ init_gnutls(void)
DEBUG_LOG("Initialised"); DEBUG_LOG("Initialised");
LCL_AddParameterChangeHandler(handle_step, NULL); LCL_AddParameterChangeHandler(handle_step, NULL);
return 1;
} }
/* ================================================== */ /* ================================================== */
@@ -637,41 +647,65 @@ deinit_gnutls(void)
/* ================================================== */ /* ================================================== */
void * static NKSN_Credentials
NKSN_CreateCertCredentials(char *cert, char *key, char *trusted_certs) create_credentials(const char **certs, const char **keys, int n_certs_keys,
const char **trusted_certs, uint32_t *trusted_certs_ids,
int n_trusted_certs, uint32_t trusted_cert_set)
{ {
gnutls_certificate_credentials_t credentials = NULL; gnutls_certificate_credentials_t credentials = NULL;
int r; int i, r;
init_gnutls(); if (!init_gnutls())
return NULL;
r = gnutls_certificate_allocate_credentials(&credentials); r = gnutls_certificate_allocate_credentials(&credentials);
if (r < 0) if (r < 0)
goto error; goto error;
if (cert && key) { if (certs && keys) {
r = gnutls_certificate_set_x509_key_file(credentials, cert, key, if (trusted_certs || trusted_certs_ids)
GNUTLS_X509_FMT_PEM); assert(0);
if (r < 0)
goto error; for (i = 0; i < n_certs_keys; i++) {
r = gnutls_certificate_set_x509_key_file(credentials, certs[i], keys[i],
GNUTLS_X509_FMT_PEM);
if (r < 0)
goto error;
}
} else { } else {
if (!CNF_GetNoSystemCert()) { if (certs || keys || n_certs_keys > 0)
assert(0);
if (trusted_cert_set == 0 && !CNF_GetNoSystemCert()) {
r = gnutls_certificate_set_x509_system_trust(credentials); r = gnutls_certificate_set_x509_system_trust(credentials);
if (r < 0) if (r < 0)
goto error; goto error;
} }
if (trusted_certs) { if (trusted_certs && trusted_certs_ids) {
r = gnutls_certificate_set_x509_trust_file(credentials, trusted_certs, for (i = 0; i < n_trusted_certs; i++) {
GNUTLS_X509_FMT_PEM); struct stat buf;
if (r < 0)
goto error; if (trusted_certs_ids[i] != trusted_cert_set)
continue;
if (stat(trusted_certs[i], &buf) == 0 && S_ISDIR(buf.st_mode))
r = gnutls_certificate_set_x509_trust_dir(credentials, trusted_certs[i],
GNUTLS_X509_FMT_PEM);
else
r = gnutls_certificate_set_x509_trust_file(credentials, trusted_certs[i],
GNUTLS_X509_FMT_PEM);
if (r < 0)
goto error;
DEBUG_LOG("Added %d trusted certs from %s", r, trusted_certs[i]);
}
} }
} }
credentials_counter++; credentials_counter++;
return credentials; return (NKSN_Credentials)credentials;
error: error:
LOG(LOGS_ERR, "Could not set credentials : %s", gnutls_strerror(r)); LOG(LOGS_ERR, "Could not set credentials : %s", gnutls_strerror(r));
@@ -683,10 +717,27 @@ error:
/* ================================================== */ /* ================================================== */
void NKSN_Credentials
NKSN_DestroyCertCredentials(void *credentials) NKSN_CreateServerCertCredentials(const char **certs, const char **keys, int n_certs_keys)
{ {
gnutls_certificate_free_credentials(credentials); return create_credentials(certs, keys, n_certs_keys, NULL, NULL, 0, 0);
}
/* ================================================== */
NKSN_Credentials
NKSN_CreateClientCertCredentials(const char **certs, uint32_t *ids,
int n_certs_ids, uint32_t trusted_cert_set)
{
return create_credentials(NULL, NULL, 0, certs, ids, n_certs_ids, trusted_cert_set);
}
/* ================================================== */
void
NKSN_DestroyCertCredentials(NKSN_Credentials credentials)
{
gnutls_certificate_free_credentials((gnutls_certificate_credentials_t)credentials);
credentials_counter--; credentials_counter--;
deinit_gnutls(); deinit_gnutls();
} }
@@ -734,12 +785,13 @@ NKSN_DestroyInstance(NKSN_Instance inst)
int int
NKSN_StartSession(NKSN_Instance inst, int sock_fd, const char *label, NKSN_StartSession(NKSN_Instance inst, int sock_fd, const char *label,
void *credentials, double timeout) NKSN_Credentials credentials, double timeout)
{ {
assert(inst->state == KE_STOPPED); assert(inst->state == KE_STOPPED);
inst->tls_session = create_tls_session(inst->server, sock_fd, inst->server_name, inst->tls_session = create_tls_session(inst->server, sock_fd, inst->server_name,
credentials, priority_cache); (gnutls_certificate_credentials_t)credentials,
priority_cache);
if (!inst->tls_session) if (!inst->tls_session)
return 0; return 0;

View File

@@ -30,19 +30,25 @@
#include "nts_ke.h" #include "nts_ke.h"
#include "siv.h" #include "siv.h"
typedef struct NKSN_Credentials_Record *NKSN_Credentials;
typedef struct NKSN_Instance_Record *NKSN_Instance; typedef struct NKSN_Instance_Record *NKSN_Instance;
/* Handler for received NTS-KE messages. A zero return code stops /* Handler for received NTS-KE messages. A zero return code stops
the session. */ the session. */
typedef int (*NKSN_MessageHandler)(void *arg); typedef int (*NKSN_MessageHandler)(void *arg);
/* Get client or server credentials using certificates of trusted CAs, /* Get server or client credentials using a server certificate and key,
or a server certificate and key. The credentials may be shared between or certificates of trusted CAs. The credentials may be shared between
different clients or servers. */ different clients or servers. */
extern void *NKSN_CreateCertCredentials(char *cert, char *key, char *trusted_certs); extern NKSN_Credentials NKSN_CreateServerCertCredentials(const char **certs, const char **keys,
int n_certs_keys);
extern NKSN_Credentials NKSN_CreateClientCertCredentials(const char **certs, uint32_t *ids,
int n_certs_ids,
uint32_t trusted_cert_set);
/* Destroy the credentials */ /* Destroy the credentials */
extern void NKSN_DestroyCertCredentials(void *credentials); extern void NKSN_DestroyCertCredentials(NKSN_Credentials credentials);
/* Create an instance */ /* Create an instance */
extern NKSN_Instance NKSN_CreateInstance(int server_mode, const char *server_name, extern NKSN_Instance NKSN_CreateInstance(int server_mode, const char *server_name,
@@ -53,7 +59,7 @@ extern void NKSN_DestroyInstance(NKSN_Instance inst);
/* Start a new NTS-KE session */ /* Start a new NTS-KE session */
extern int NKSN_StartSession(NKSN_Instance inst, int sock_fd, const char *label, extern int NKSN_StartSession(NKSN_Instance inst, int sock_fd, const char *label,
void *credentials, double timeout); NKSN_Credentials credentials, double timeout);
/* Begin an NTS-KE message. A request should be made right after starting /* Begin an NTS-KE message. A request should be made right after starting
the session and response should be made in the message handler. */ the session and response should be made in the message handler. */

View File

@@ -27,11 +27,6 @@
#ifndef GOT_NTS_NTP_H #ifndef GOT_NTS_NTP_H
#define GOT_NTS_NTP_H #define GOT_NTS_NTP_H
#define NTP_EF_NTS_UNIQUE_IDENTIFIER 0x0104
#define NTP_EF_NTS_COOKIE 0x0204
#define NTP_EF_NTS_COOKIE_PLACEHOLDER 0x0304
#define NTP_EF_NTS_AUTH_AND_EEF 0x0404
#define NTP_KOD_NTS_NAK 0x4e54534e #define NTP_KOD_NTS_NAK 0x4e54534e
#define NTS_MIN_UNIQ_ID_LENGTH 32 #define NTS_MIN_UNIQ_ID_LENGTH 32

View File

@@ -50,14 +50,20 @@
#define DUMP_IDENTIFIER "NNC0\n" #define DUMP_IDENTIFIER "NNC0\n"
struct NNC_Instance_Record { struct NNC_Instance_Record {
const IPSockAddr *ntp_address; /* Address of NTS-KE server */
IPSockAddr nts_address; IPSockAddr nts_address;
/* Hostname or IP address for certificate verification */
char *name; char *name;
/* ID of trusted certificates */
uint32_t cert_set;
/* Configured NTP port */
uint16_t default_ntp_port;
/* Address of NTP server (can be negotiated in NTS-KE) */
IPSockAddr ntp_address;
NKC_Instance nke; NKC_Instance nke;
SIV_Instance siv; SIV_Instance siv;
int load_attempt;
int nke_attempts; int nke_attempts;
double next_nke_attempt; double next_nke_attempt;
double last_nke_success; double last_nke_success;
@@ -91,7 +97,6 @@ reset_instance(NNC_Instance inst)
SIV_DestroyInstance(inst->siv); SIV_DestroyInstance(inst->siv);
inst->siv = NULL; inst->siv = NULL;
inst->load_attempt = 0;
inst->nke_attempts = 0; inst->nke_attempts = 0;
inst->next_nke_attempt = 0.0; inst->next_nke_attempt = 0.0;
inst->last_nke_success = 0.0; inst->last_nke_success = 0.0;
@@ -111,20 +116,26 @@ reset_instance(NNC_Instance inst)
/* ================================================== */ /* ================================================== */
NNC_Instance NNC_Instance
NNC_CreateInstance(IPSockAddr *nts_address, const char *name, const IPSockAddr *ntp_address) NNC_CreateInstance(IPSockAddr *nts_address, const char *name, uint32_t cert_set, uint16_t ntp_port)
{ {
NNC_Instance inst; NNC_Instance inst;
inst = MallocNew(struct NNC_Instance_Record); inst = MallocNew(struct NNC_Instance_Record);
inst->ntp_address = ntp_address;
inst->nts_address = *nts_address; inst->nts_address = *nts_address;
inst->name = name ? Strdup(name) : NULL; inst->name = Strdup(name);
inst->cert_set = cert_set;
inst->default_ntp_port = ntp_port;
inst->ntp_address.ip_addr = nts_address->ip_addr;
inst->ntp_address.port = ntp_port;
inst->siv = NULL; inst->siv = NULL;
inst->nke = NULL; inst->nke = NULL;
reset_instance(inst); reset_instance(inst);
/* Try to reload saved keys and cookies */
load_cookies(inst);
return inst; return inst;
} }
@@ -165,13 +176,13 @@ set_ntp_address(NNC_Instance inst, NTP_Remote_Address *negotiated_address)
{ {
NTP_Remote_Address old_address, new_address; NTP_Remote_Address old_address, new_address;
old_address = *inst->ntp_address; old_address = inst->ntp_address;
new_address = *negotiated_address; new_address = *negotiated_address;
if (new_address.ip_addr.family == IPADDR_UNSPEC) if (new_address.ip_addr.family == IPADDR_UNSPEC)
new_address.ip_addr = old_address.ip_addr; new_address.ip_addr = inst->nts_address.ip_addr;
if (new_address.port == 0) if (new_address.port == 0)
new_address.port = old_address.port; new_address.port = inst->default_ntp_port;
if (UTI_CompareIPs(&old_address.ip_addr, &new_address.ip_addr, NULL) == 0 && if (UTI_CompareIPs(&old_address.ip_addr, &new_address.ip_addr, NULL) == 0 &&
old_address.port == new_address.port) old_address.port == new_address.port)
@@ -184,6 +195,8 @@ set_ntp_address(NNC_Instance inst, NTP_Remote_Address *negotiated_address)
return 0; return 0;
} }
inst->ntp_address = new_address;
return 1; return 1;
} }
@@ -223,13 +236,7 @@ get_cookies(NNC_Instance inst)
return 0; return 0;
} }
if (!inst->name) { inst->nke = NKC_CreateInstance(&inst->nts_address, inst->name, inst->cert_set);
LOG(LOGS_ERR, "Missing name of %s for NTS-KE",
UTI_IPToString(&inst->nts_address.ip_addr));
return 0;
}
inst->nke = NKC_CreateInstance(&inst->nts_address, inst->name);
inst->nke_attempts++; inst->nke_attempts++;
update_next_nke_attempt(inst, now); update_next_nke_attempt(inst, now);
@@ -288,12 +295,6 @@ NNC_PrepareForAuth(NNC_Instance inst)
UTI_GetRandomBytes(inst->uniq_id, sizeof (inst->uniq_id)); UTI_GetRandomBytes(inst->uniq_id, sizeof (inst->uniq_id));
UTI_GetRandomBytes(inst->nonce, sizeof (inst->nonce)); UTI_GetRandomBytes(inst->nonce, sizeof (inst->nonce));
/* Try to reload saved keys and cookies (once for the NTS-KE address) */
if (!inst->load_attempt) {
load_cookies(inst);
inst->load_attempt = 1;
}
/* Get new cookies if there are not any, or they are no longer usable */ /* Get new cookies if there are not any, or they are no longer usable */
if (!check_cookies(inst)) { if (!check_cookies(inst)) {
if (!get_cookies(inst)) if (!get_cookies(inst))
@@ -456,7 +457,7 @@ NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet,
for (parsed = NTP_HEADER_LENGTH; parsed < info->length; parsed += ef_length) { for (parsed = NTP_HEADER_LENGTH; parsed < info->length; parsed += ef_length) {
if (!NEF_ParseField(packet, info->length, parsed, if (!NEF_ParseField(packet, info->length, parsed,
&ef_length, &ef_type, &ef_body, &ef_body_length)) &ef_length, &ef_type, &ef_body, &ef_body_length))
/* This is not expected as the packet already passed NAU_ParsePacket() */ /* This is not expected as the packet already passed parsing */
return 0; return 0;
switch (ef_type) { switch (ef_type) {
@@ -524,10 +525,13 @@ NNC_ChangeAddress(NNC_Instance inst, IPAddr *address)
save_cookies(inst); save_cookies(inst);
inst->nts_address.ip_addr = *address; inst->nts_address.ip_addr = *address;
inst->ntp_address.ip_addr = *address;
reset_instance(inst); reset_instance(inst);
DEBUG_LOG("NTS reset"); DEBUG_LOG("NTS reset");
load_cookies(inst);
} }
/* ================================================== */ /* ================================================== */
@@ -541,7 +545,7 @@ save_cookies(NNC_Instance inst)
FILE *f; FILE *f;
int i; int i;
if (inst->num_cookies < 1 || !inst->name || !UTI_IsIPReal(&inst->nts_address.ip_addr)) if (inst->num_cookies < 1 || !UTI_IsIPReal(&inst->nts_address.ip_addr))
return; return;
dump_dir = CNF_GetNtsDumpDir(); dump_dir = CNF_GetNtsDumpDir();
@@ -560,7 +564,7 @@ save_cookies(NNC_Instance inst)
if (fprintf(f, "%s%s\n%.1f\n%s %d\n%u %d ", if (fprintf(f, "%s%s\n%.1f\n%s %d\n%u %d ",
DUMP_IDENTIFIER, inst->name, context_time, DUMP_IDENTIFIER, inst->name, context_time,
UTI_IPToString(&inst->ntp_address->ip_addr), inst->ntp_address->port, UTI_IPToString(&inst->ntp_address.ip_addr), inst->ntp_address.port,
inst->context_id, (int)inst->context.algorithm) < 0 || inst->context_id, (int)inst->context.algorithm) < 0 ||
!UTI_BytesToHex(inst->context.s2c.key, inst->context.s2c.length, buf, sizeof (buf)) || !UTI_BytesToHex(inst->context.s2c.key, inst->context.s2c.length, buf, sizeof (buf)) ||
fprintf(f, "%s ", buf) < 0 || fprintf(f, "%s ", buf) < 0 ||
@@ -623,7 +627,7 @@ load_cookies(NNC_Instance inst)
if (!fgets(line, sizeof (line), f) || strcmp(line, DUMP_IDENTIFIER) != 0 || if (!fgets(line, sizeof (line), f) || strcmp(line, DUMP_IDENTIFIER) != 0 ||
!fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 1 || !fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 1 ||
!inst->name || strcmp(words[0], inst->name) != 0 || strcmp(words[0], inst->name) != 0 ||
!fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 1 || !fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 1 ||
sscanf(words[0], "%lf", &context_time) != 1 || sscanf(words[0], "%lf", &context_time) != 1 ||
!fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 2 || !fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 2 ||
@@ -665,6 +669,8 @@ load_cookies(NNC_Instance inst)
inst->last_nke_success = context_time + SCH_GetLastEventMonoTime(); inst->last_nke_success = context_time + SCH_GetLastEventMonoTime();
inst->context_id = context_id; inst->context_id = context_id;
fclose(f);
DEBUG_LOG("Loaded %d cookies for %s", i, filename); DEBUG_LOG("Loaded %d cookies for %s", i, filename);
return; return;

View File

@@ -34,7 +34,7 @@
typedef struct NNC_Instance_Record *NNC_Instance; typedef struct NNC_Instance_Record *NNC_Instance;
extern NNC_Instance NNC_CreateInstance(IPSockAddr *nts_address, const char *name, extern NNC_Instance NNC_CreateInstance(IPSockAddr *nts_address, const char *name,
const IPSockAddr *ntp_address); uint32_t cert_set, uint16_t ntp_port);
extern void NNC_DestroyInstance(NNC_Instance inst); extern void NNC_DestroyInstance(NNC_Instance inst);
extern int NNC_PrepareForAuth(NNC_Instance inst); extern int NNC_PrepareForAuth(NNC_Instance inst);
extern int NNC_GenerateRequestAuth(NNC_Instance inst, NTP_Packet *packet, extern int NNC_GenerateRequestAuth(NNC_Instance inst, NTP_Packet *packet,

View File

@@ -59,8 +59,10 @@ struct NtsServer *server;
void void
NNS_Initialise(void) NNS_Initialise(void)
{ {
const char **certs, **keys;
/* Create an NTS-NTP server instance only if NTS-KE server is enabled */ /* Create an NTS-NTP server instance only if NTS-KE server is enabled */
if (!CNF_GetNtsServerCertFile() || !CNF_GetNtsServerKeyFile()) { if (CNF_GetNtsServerCertAndKeyFiles(&certs, &keys) <= 0) {
server = NULL; server = NULL;
return; return;
} }
@@ -240,7 +242,7 @@ NNS_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *req_info,
for (parsed = NTP_HEADER_LENGTH; parsed < req_info->length; parsed += ef_length) { for (parsed = NTP_HEADER_LENGTH; parsed < req_info->length; parsed += ef_length) {
if (!NEF_ParseField(request, req_info->length, parsed, if (!NEF_ParseField(request, req_info->length, parsed,
&ef_length, &ef_type, &ef_body, &ef_body_length)) &ef_length, &ef_type, &ef_body, &ef_body_length))
/* This is not expected as the packet already passed NAU_ParsePacket() */ /* This is not expected as the packet already passed parsing */
return 0; return 0;
switch (ef_type) { switch (ef_type) {

View File

@@ -87,7 +87,7 @@ static const struct request_length request_lengths[] = {
REQ_LENGTH_ENTRY(del_source, null), /* DEL_SOURCE */ REQ_LENGTH_ENTRY(del_source, null), /* DEL_SOURCE */
REQ_LENGTH_ENTRY(null, null), /* WRITERTC */ REQ_LENGTH_ENTRY(null, null), /* WRITERTC */
REQ_LENGTH_ENTRY(dfreq, null), /* DFREQ */ REQ_LENGTH_ENTRY(dfreq, null), /* DFREQ */
REQ_LENGTH_ENTRY(doffset, null), /* DOFFSET */ { 0, 0 }, /* DOFFSET - not supported */
REQ_LENGTH_ENTRY(null, tracking), /* TRACKING */ REQ_LENGTH_ENTRY(null, tracking), /* TRACKING */
REQ_LENGTH_ENTRY(sourcestats, sourcestats), /* SOURCESTATS */ REQ_LENGTH_ENTRY(sourcestats, sourcestats), /* SOURCESTATS */
REQ_LENGTH_ENTRY(null, rtc), /* RTCREPORT */ REQ_LENGTH_ENTRY(null, rtc), /* RTCREPORT */
@@ -128,6 +128,7 @@ static const struct request_length request_lengths[] = {
client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX3 */ client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX3 */
REQ_LENGTH_ENTRY(select_data, select_data), /* SELECT_DATA */ REQ_LENGTH_ENTRY(select_data, select_data), /* SELECT_DATA */
REQ_LENGTH_ENTRY(null, null), /* RELOAD_SOURCES */ REQ_LENGTH_ENTRY(null, null), /* RELOAD_SOURCES */
REQ_LENGTH_ENTRY(doffset, null), /* DOFFSET2 */
}; };
static const uint16_t reply_lengths[] = { static const uint16_t reply_lengths[] = {
@@ -153,8 +154,9 @@ static const uint16_t reply_lengths[] = {
RPY_LENGTH_ENTRY(ntp_source_name), /* NTP_SOURCE_NAME */ RPY_LENGTH_ENTRY(ntp_source_name), /* NTP_SOURCE_NAME */
RPY_LENGTH_ENTRY(auth_data), /* AUTH_DATA */ RPY_LENGTH_ENTRY(auth_data), /* AUTH_DATA */
RPY_LENGTH_ENTRY(client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX3 */ RPY_LENGTH_ENTRY(client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX3 */
RPY_LENGTH_ENTRY(server_stats), /* SERVER_STATS2 */ 0, /* SERVER_STATS2 - not supported */
RPY_LENGTH_ENTRY(select_data), /* SELECT_DATA */ RPY_LENGTH_ENTRY(select_data), /* SELECT_DATA */
RPY_LENGTH_ENTRY(server_stats), /* SERVER_STATS3 */
}; };
/* ================================================== */ /* ================================================== */

View File

@@ -255,7 +255,7 @@ do_bind_socket(ReqBindSocket *req, PrvResponse *res)
SCK_SockaddrToIPSockAddr(sa, sa_len, &ip_saddr); SCK_SockaddrToIPSockAddr(sa, sa_len, &ip_saddr);
if (ip_saddr.port != 0 && ip_saddr.port != CNF_GetNTPPort() && if (ip_saddr.port != 0 && ip_saddr.port != CNF_GetNTPPort() &&
ip_saddr.port != CNF_GetAcquisitionPort()) { ip_saddr.port != CNF_GetAcquisitionPort() && ip_saddr.port != CNF_GetPtpPort()) {
SCK_CloseSocket(sock_fd); SCK_CloseSocket(sock_fd);
res_fatal(res, "Invalid port %d", ip_saddr.port); res_fatal(res, "Invalid port %d", ip_saddr.port);
return; return;
@@ -547,7 +547,7 @@ PRV_BindSocket(int sock, struct sockaddr *address, socklen_t address_len)
SCK_SockaddrToIPSockAddr(address, address_len, &ip_saddr); SCK_SockaddrToIPSockAddr(address, address_len, &ip_saddr);
if (ip_saddr.port != 0 && ip_saddr.port != CNF_GetNTPPort() && if (ip_saddr.port != 0 && ip_saddr.port != CNF_GetNTPPort() &&
ip_saddr.port != CNF_GetAcquisitionPort()) ip_saddr.port != CNF_GetAcquisitionPort() && ip_saddr.port != CNF_GetPtpPort())
assert(0); assert(0);
if (!have_helper()) if (!have_helper())
@@ -662,6 +662,8 @@ PRV_StartHelper(void)
close(fd); close(fd);
} }
UTI_ResetGetRandomFunctions();
/* ignore signals, the process will exit on OP_QUIT request */ /* ignore signals, the process will exit on OP_QUIT request */
UTI_SetQuitSignalsHandler(SIG_IGN, 1); UTI_SetQuitSignalsHandler(SIG_IGN, 1);

64
ptp.h Normal file
View File

@@ -0,0 +1,64 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2021
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
This is the header file for the Precision Time Protocol (PTP).
*/
#ifndef GOT_PTP_H
#define GOT_PTP_H
#include "sysincl.h"
#include "ntp.h"
#define PTP_VERSION 2
#define PTP_TYPE_DELAY_REQ 1
#define PTP_DOMAIN_NTP 123
#define PTP_FLAG_UNICAST (1 << (2 + 8))
#define PTP_TLV_NTP 0x2023
typedef struct {
uint8_t type;
uint8_t version;
uint16_t length;
uint8_t domain;
uint8_t min_sdoid;
uint16_t flags;
uint8_t rest[26];
} PTP_Header;
typedef struct {
uint16_t type;
uint16_t length;
} PTP_TlvHeader;
typedef struct {
PTP_Header header;
uint8_t origin_ts[10];
PTP_TlvHeader tlv_header;
NTP_Packet ntp_msg;
} PTP_NtpMessage;
#define PTP_NTP_PREFIX_LENGTH (int)offsetof(PTP_NtpMessage, ntp_msg)
#endif

209
quantiles.c Normal file
View File

@@ -0,0 +1,209 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2022
*
* 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.
*
**********************************************************************
=======================================================================
Estimation of quantiles using the Frugal-2U streaming algorithm
(https://arxiv.org/pdf/1407.1121v1.pdf)
*/
#include "config.h"
#include "logging.h"
#include "memory.h"
#include "quantiles.h"
#include "regress.h"
#include "util.h"
/* Maximum number of repeated estimates for stabilisation */
#define MAX_REPEAT 64
struct Quantile {
double est;
double step;
int sign;
};
struct QNT_Instance_Record {
struct Quantile *quants;
int n_quants;
int repeat;
int q;
int min_k;
double min_step;
int n_set;
};
/* ================================================== */
QNT_Instance
QNT_CreateInstance(int min_k, int max_k, int q, int repeat, double min_step)
{
QNT_Instance inst;
long seed;
if (q < 2 || min_k > max_k || min_k < 1 || max_k >= q ||
repeat < 1 || repeat > MAX_REPEAT || min_step <= 0.0)
assert(0);
inst = MallocNew(struct QNT_Instance_Record);
inst->n_quants = (max_k - min_k + 1) * repeat;
inst->quants = MallocArray(struct Quantile, inst->n_quants);
inst->repeat = repeat;
inst->q = q;
inst->min_k = min_k;
inst->min_step = min_step;
QNT_Reset(inst);
/* Seed the random number generator, which will not be isolated from
other instances and other random() users */
UTI_GetRandomBytes(&seed, sizeof (seed));
srandom(seed);
return inst;
}
/* ================================================== */
void
QNT_DestroyInstance(QNT_Instance inst)
{
Free(inst->quants);
Free(inst);
}
/* ================================================== */
void
QNT_Reset(QNT_Instance inst)
{
int i;
inst->n_set = 0;
for (i = 0; i < inst->n_quants; i++) {
inst->quants[i].est = 0.0;
inst->quants[i].step = inst->min_step;
inst->quants[i].sign = 1;
}
}
/* ================================================== */
static void
insert_initial_value(QNT_Instance inst, double value)
{
int i, j, r = inst->repeat;
if (inst->n_set * r >= inst->n_quants)
assert(0);
/* Keep the initial estimates repeated and ordered */
for (i = inst->n_set; i > 0 && inst->quants[(i - 1) * r].est > value; i--) {
for (j = 0; j < r; j++)
inst->quants[i * r + j].est = inst->quants[(i - 1) * r].est;
}
for (j = 0; j < r; j++)
inst->quants[i * r + j].est = value;
inst->n_set++;
/* Duplicate the largest value in unset quantiles */
for (i = inst->n_set * r; i < inst->n_quants; i++)
inst->quants[i].est = inst->quants[i - 1].est;
}
/* ================================================== */
static void
update_estimate(struct Quantile *quantile, double value, double p, double rand,
double min_step)
{
if (value > quantile->est && rand > (1.0 - p)) {
quantile->step += quantile->sign > 0 ? min_step : -min_step;
quantile->est += quantile->step > 0.0 ? fabs(quantile->step) : min_step;
if (quantile->est > value) {
quantile->step += value - quantile->est;
quantile->est = value;
}
if (quantile->sign < 0 && quantile->step > min_step)
quantile->step = min_step;
quantile->sign = 1;
} else if (value < quantile->est && rand > p) {
quantile->step += quantile->sign < 0 ? min_step : -min_step;
quantile->est -= quantile->step > 0.0 ? fabs(quantile->step) : min_step;
if (quantile->est < value) {
quantile->step += quantile->est - value;
quantile->est = value;
}
if (quantile->sign > 0 && quantile->step > min_step)
quantile->step = min_step;
quantile->sign = -1;
}
}
/* ================================================== */
void
QNT_Accumulate(QNT_Instance inst, double value)
{
double p, rand;
int i;
/* Initialise the estimates with first received values */
if (inst->n_set * inst->repeat < inst->n_quants) {
insert_initial_value(inst, value);
return;
}
for (i = 0; i < inst->n_quants; i++) {
p = (double)(i / inst->repeat + inst->min_k) / inst->q;
rand = (double)random() / ((1U << 31) - 1);
update_estimate(&inst->quants[i], value, p, rand, inst->min_step);
}
}
/* ================================================== */
int
QNT_GetMinK(QNT_Instance inst)
{
return inst->min_k;
}
/* ================================================== */
double
QNT_GetQuantile(QNT_Instance inst, int k)
{
double estimates[MAX_REPEAT];
int i;
if (k < inst->min_k || k - inst->min_k >= inst->n_quants)
assert(0);
for (i = 0; i < inst->repeat; i++)
estimates[i] = inst->quants[(k - inst->min_k) * inst->repeat + i].est;
return RGR_FindMedian(estimates, inst->repeat);
}

41
quantiles.h Normal file
View File

@@ -0,0 +1,41 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2022
*
* 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.
*
**********************************************************************
=======================================================================
Header file for estimation of quantiles.
*/
#ifndef GOT_QUANTILES_H
#define GOT_QUANTILES_H
typedef struct QNT_Instance_Record *QNT_Instance;
extern QNT_Instance QNT_CreateInstance(int min_k, int max_k, int q, int repeat, double min_step);
extern void QNT_DestroyInstance(QNT_Instance inst);
extern void QNT_Reset(QNT_Instance inst);
extern void QNT_Accumulate(QNT_Instance inst, double value);
extern int QNT_GetMinK(QNT_Instance inst);
extern double QNT_GetQuantile(QNT_Instance inst, int k);
#endif

View File

@@ -40,6 +40,9 @@
#include "samplefilt.h" #include "samplefilt.h"
#include "sched.h" #include "sched.h"
/* Maximum offset of locked reference as a fraction of the PPS interval */
#define PPS_LOCK_LIMIT 0.4
/* list of refclock drivers */ /* list of refclock drivers */
extern RefclockDriver RCL_SHM_driver; extern RefclockDriver RCL_SHM_driver;
extern RefclockDriver RCL_SOCK_driver; extern RefclockDriver RCL_SOCK_driver;
@@ -52,21 +55,6 @@ struct FilterSample {
struct timespec sample_time; struct timespec sample_time;
}; };
struct MedianFilter {
int length;
int index;
int used;
int last;
int avg_var_n;
double avg_var;
double max_var;
struct FilterSample *samples;
int *selected;
double *x_data;
double *y_data;
double *w_data;
};
struct RCL_Instance_Record { struct RCL_Instance_Record {
RefclockDriver *driver; RefclockDriver *driver;
void *data; void *data;
@@ -76,6 +64,7 @@ struct RCL_Instance_Record {
int driver_polled; int driver_polled;
int poll; int poll;
int leap_status; int leap_status;
int local;
int pps_forced; int pps_forced;
int pps_rate; int pps_rate;
int pps_active; int pps_active;
@@ -187,6 +176,7 @@ RCL_AddRefclock(RefclockParameters *params)
inst->poll = params->poll; inst->poll = params->poll;
inst->driver_polled = 0; inst->driver_polled = 0;
inst->leap_status = LEAP_Normal; inst->leap_status = LEAP_Normal;
inst->local = params->local;
inst->pps_forced = params->pps_forced; inst->pps_forced = params->pps_forced;
inst->pps_rate = params->pps_rate; inst->pps_rate = params->pps_rate;
inst->pps_active = 0; inst->pps_active = 0;
@@ -228,6 +218,13 @@ RCL_AddRefclock(RefclockParameters *params)
inst->ref_id = (uint32_t)ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3]; inst->ref_id = (uint32_t)ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3];
} }
if (inst->local) {
inst->pps_forced = 1;
inst->lock_ref = inst->ref_id;
inst->leap_status = LEAP_Unsynchronised;
inst->max_lock_age = MAX(inst->max_lock_age, 3);
}
if (inst->driver->poll) { if (inst->driver->poll) {
int max_samples; int max_samples;
@@ -267,7 +264,7 @@ RCL_AddRefclock(RefclockParameters *params)
void void
RCL_StartRefclocks(void) RCL_StartRefclocks(void)
{ {
unsigned int i, j, n; unsigned int i, j, n, lock_index;
n = ARR_GetSize(refclocks); n = ARR_GetSize(refclocks);
@@ -277,13 +274,31 @@ RCL_StartRefclocks(void)
SRC_SetActive(inst->source); SRC_SetActive(inst->source);
inst->timeout_id = SCH_AddTimeoutByDelay(0.0, poll_timeout, (void *)inst); inst->timeout_id = SCH_AddTimeoutByDelay(0.0, poll_timeout, (void *)inst);
if (inst->lock_ref) { /* Replace lock refid with the refclock's index, or -1 if not valid */
/* Replace lock refid with index to refclocks */
for (j = 0; j < n && get_refclock(j)->ref_id != inst->lock_ref; j++) lock_index = -1;
;
inst->lock_ref = j < n ? j : -1; if (inst->lock_ref != 0) {
} else for (j = 0; j < n; j++) {
inst->lock_ref = -1; RCL_Instance inst2 = get_refclock(j);
if (inst->lock_ref != inst2->ref_id)
continue;
if (inst->driver->poll && inst2->driver->poll &&
(double)inst->max_lock_age / inst->pps_rate < UTI_Log2ToDouble(inst2->driver_poll))
LOG(LOGS_WARN, "%s maxlockage too small for %s",
UTI_RefidToString(inst->ref_id), UTI_RefidToString(inst2->ref_id));
lock_index = j;
break;
}
if (lock_index == -1 || (lock_index == i && !inst->local))
LOG(LOGS_WARN, "Invalid lock refid %s", UTI_RefidToString(inst->lock_ref));
}
inst->lock_ref = lock_index;
} }
} }
@@ -415,30 +430,28 @@ accumulate_sample(RCL_Instance instance, struct timespec *sample_time, double of
sample.peer_dispersion = dispersion; sample.peer_dispersion = dispersion;
sample.root_dispersion = dispersion; sample.root_dispersion = dispersion;
/* Handle special case when PPS is used with the local reference */
if (instance->pps_active && instance->lock_ref == -1)
sample.stratum = pps_stratum(instance, &sample.time);
else
sample.stratum = instance->stratum;
return SPF_AccumulateSample(instance->filter, &sample); return SPF_AccumulateSample(instance->filter, &sample);
} }
int int
RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset, int leap) RCL_AddSample(RCL_Instance instance, struct timespec *sample_time,
struct timespec *ref_time, int leap)
{ {
double correction, dispersion; double correction, dispersion, raw_offset, offset;
struct timespec cooked_time; struct timespec cooked_time;
if (instance->pps_forced) if (instance->pps_forced)
return RCL_AddPulse(instance, sample_time, -offset); return RCL_AddPulse(instance, sample_time,
1.0e-9 * (sample_time->tv_nsec - ref_time->tv_nsec));
raw_offset = UTI_DiffTimespecsToDouble(ref_time, sample_time);
LCL_GetOffsetCorrection(sample_time, &correction, &dispersion); LCL_GetOffsetCorrection(sample_time, &correction, &dispersion);
UTI_AddDoubleToTimespec(sample_time, correction, &cooked_time); UTI_AddDoubleToTimespec(sample_time, correction, &cooked_time);
dispersion += instance->precision; dispersion += instance->precision;
/* Make sure the timestamp and offset provided by the driver are sane */ /* Make sure the timestamp and offset provided by the driver are sane */
if (!UTI_IsTimeOffsetSane(sample_time, offset) || if (!UTI_IsTimeOffsetSane(sample_time, raw_offset) ||
!valid_sample_time(instance, &cooked_time)) !valid_sample_time(instance, &cooked_time))
return 0; return 0;
@@ -453,18 +466,24 @@ RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset
return 0; return 0;
} }
/* Calculate offset = raw_offset - correction + instance->offset
in parts to avoid loss of precision if there are large differences */
offset = ref_time->tv_sec - sample_time->tv_sec -
(time_t)correction + (time_t)instance->offset;
offset += 1.0e-9 * (ref_time->tv_nsec - sample_time->tv_nsec) -
(correction - (time_t)correction) + (instance->offset - (time_t)instance->offset);
if (instance->tai && !convert_tai_offset(sample_time, &offset)) { if (instance->tai && !convert_tai_offset(sample_time, &offset)) {
DEBUG_LOG("refclock sample ignored unknown TAI offset"); DEBUG_LOG("refclock sample ignored unknown TAI offset");
return 0; return 0;
} }
if (!accumulate_sample(instance, &cooked_time, if (!accumulate_sample(instance, &cooked_time, offset, dispersion))
offset - correction + instance->offset, dispersion))
return 0; return 0;
instance->pps_active = 0; instance->pps_active = 0;
log_sample(instance, &cooked_time, 0, 0, offset, offset - correction + instance->offset, dispersion); log_sample(instance, &cooked_time, 0, 0, raw_offset, offset, dispersion);
/* for logging purposes */ /* for logging purposes */
if (!instance->driver->poll) if (!instance->driver->poll)
@@ -543,29 +562,40 @@ RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
lock_refclock = get_refclock(instance->lock_ref); lock_refclock = get_refclock(instance->lock_ref);
if (!SPF_GetLastSample(lock_refclock->filter, &ref_sample)) { if (!SPF_GetLastSample(lock_refclock->filter, &ref_sample)) {
DEBUG_LOG("refclock pulse ignored no ref sample"); if (instance->local) {
return 0; /* Make the first sample in order to lock to itself */
ref_sample.time = *cooked_time;
ref_sample.offset = offset;
ref_sample.peer_delay = ref_sample.peer_dispersion = 0;
ref_sample.root_delay = ref_sample.root_dispersion = 0;
} else {
DEBUG_LOG("refclock pulse ignored no ref sample");
return 0;
}
} }
ref_sample.root_dispersion += SPF_GetAvgSampleDispersion(lock_refclock->filter); ref_sample.root_dispersion += SPF_GetAvgSampleDispersion(lock_refclock->filter);
sample_diff = UTI_DiffTimespecsToDouble(cooked_time, &ref_sample.time); sample_diff = UTI_DiffTimespecsToDouble(cooked_time, &ref_sample.time);
if (fabs(sample_diff) >= (double)instance->max_lock_age / rate) { if (fabs(sample_diff) >= (double)instance->max_lock_age / rate) {
DEBUG_LOG("refclock pulse ignored samplediff=%.9f", DEBUG_LOG("refclock pulse ignored samplediff=%.9f", sample_diff);
sample_diff);
/* Restart the local mode */
if (instance->local) {
LOG(LOGS_WARN, "Local refclock lost lock");
SPF_DropSamples(instance->filter);
SRC_ResetInstance(instance->source);
}
return 0; return 0;
} }
/* Align the offset to the reference sample */ /* Align the offset to the reference sample */
if ((ref_sample.offset - offset) >= 0.0) shift = round((ref_sample.offset - offset) * rate) / rate;
shift = (long)((ref_sample.offset - offset) * rate + 0.5) / (double)rate;
else
shift = (long)((ref_sample.offset - offset) * rate - 0.5) / (double)rate;
offset += shift; offset += shift;
if (fabs(ref_sample.offset - offset) + if (fabs(ref_sample.offset - offset) +
ref_sample.root_dispersion + dispersion >= 0.2 / rate) { ref_sample.root_dispersion + dispersion > PPS_LOCK_LIMIT / rate) {
DEBUG_LOG("refclock pulse ignored offdiff=%.9f refdisp=%.9f disp=%.9f", DEBUG_LOG("refclock pulse ignored offdiff=%.9f refdisp=%.9f disp=%.9f",
ref_sample.offset - offset, ref_sample.root_dispersion, dispersion); ref_sample.offset - offset, ref_sample.root_dispersion, dispersion);
return 0; return 0;
@@ -681,11 +711,57 @@ pps_stratum(RCL_Instance instance, struct timespec *ts)
return 0; return 0;
} }
static void
get_local_stats(RCL_Instance inst, struct timespec *ref, double *freq, double *offset)
{
double offset_sd, freq_sd, skew, root_delay, root_disp;
SST_Stats stats = SRC_GetSourcestats(inst->source);
if (SST_Samples(stats) < SST_GetMinSamples(stats)) {
UTI_ZeroTimespec(ref);
return;
}
SST_GetTrackingData(stats, ref, offset, &offset_sd, freq, &freq_sd,
&skew, &root_delay, &root_disp);
}
static void
follow_local(RCL_Instance inst, struct timespec *prev_ref_time, double prev_freq,
double prev_offset)
{
SST_Stats stats = SRC_GetSourcestats(inst->source);
double freq, dfreq, offset, doffset, elapsed;
struct timespec now, ref_time;
get_local_stats(inst, &ref_time, &freq, &offset);
if (UTI_IsZeroTimespec(prev_ref_time) || UTI_IsZeroTimespec(&ref_time))
return;
dfreq = (freq - prev_freq) / (1.0 - prev_freq);
elapsed = UTI_DiffTimespecsToDouble(&ref_time, prev_ref_time);
doffset = offset - elapsed * prev_freq - prev_offset;
if (!REF_AdjustReference(doffset, dfreq))
return;
LCL_ReadCookedTime(&now, NULL);
SST_SlewSamples(stats, &now, dfreq, doffset);
SPF_SlewSamples(inst->filter, &now, dfreq, doffset);
/* Keep the offset close to zero to not lose precision */
if (fabs(offset) >= 1.0) {
SST_CorrectOffset(stats, -round(offset));
SPF_CorrectOffset(inst->filter, -round(offset));
}
}
static void static void
poll_timeout(void *arg) poll_timeout(void *arg)
{ {
NTP_Sample sample; NTP_Sample sample;
int poll; int poll, stratum;
RCL_Instance inst = (RCL_Instance)arg; RCL_Instance inst = (RCL_Instance)arg;
@@ -701,11 +777,28 @@ poll_timeout(void *arg)
inst->driver_polled = 0; inst->driver_polled = 0;
if (SPF_GetFilteredSample(inst->filter, &sample)) { if (SPF_GetFilteredSample(inst->filter, &sample)) {
double local_freq, local_offset;
struct timespec local_ref_time;
/* Handle special case when PPS is used with the local reference */
if (inst->pps_active && inst->lock_ref == -1)
stratum = pps_stratum(inst, &sample.time);
else
stratum = inst->stratum;
if (inst->local) {
get_local_stats(inst, &local_ref_time, &local_freq, &local_offset);
inst->leap_status = LEAP_Unsynchronised;
}
SRC_UpdateReachability(inst->source, 1); SRC_UpdateReachability(inst->source, 1);
SRC_SetLeapStatus(inst->source, inst->leap_status); SRC_UpdateStatus(inst->source, stratum, inst->leap_status);
SRC_AccumulateSample(inst->source, &sample); SRC_AccumulateSample(inst->source, &sample);
SRC_SelectSource(inst->source); SRC_SelectSource(inst->source);
if (inst->local)
follow_local(inst, &local_ref_time, local_freq, local_offset);
log_sample(inst, &sample.time, 1, 0, 0.0, sample.offset, sample.peer_dispersion); log_sample(inst, &sample.time, 1, 0, 0.0, sample.offset, sample.peer_dispersion);
} else { } else {
SRC_UpdateReachability(inst->source, 0); SRC_UpdateReachability(inst->source, 0);

View File

@@ -37,6 +37,7 @@ typedef struct {
int driver_poll; int driver_poll;
int poll; int poll;
int filter_length; int filter_length;
int local;
int pps_forced; int pps_forced;
int pps_rate; int pps_rate;
int min_samples; int min_samples;
@@ -74,7 +75,8 @@ extern void *RCL_GetDriverData(RCL_Instance instance);
extern char *RCL_GetDriverParameter(RCL_Instance instance); extern char *RCL_GetDriverParameter(RCL_Instance instance);
extern void RCL_CheckDriverOptions(RCL_Instance instance, const char **options); extern void RCL_CheckDriverOptions(RCL_Instance instance, const char **options);
extern char *RCL_GetDriverOption(RCL_Instance instance, char *name); extern char *RCL_GetDriverOption(RCL_Instance instance, char *name);
extern int RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset, int leap); extern int RCL_AddSample(RCL_Instance instance, struct timespec *sample_time,
struct timespec *ref_time, int leap);
extern int RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second); extern int RCL_AddPulse(RCL_Instance instance, struct timespec *pulse_time, double second);
extern int RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time, extern int RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
double second, double dispersion, double raw_correction); double second, double dispersion, double raw_correction);

View File

@@ -66,10 +66,8 @@ static int phc_initialise(RCL_Instance instance)
path = RCL_GetDriverParameter(instance); path = RCL_GetDriverParameter(instance);
phc_fd = SYS_Linux_OpenPHC(path, 0); phc_fd = SYS_Linux_OpenPHC(path, 0);
if (phc_fd < 0) { if (phc_fd < 0)
LOG_FATAL("Could not open PHC"); LOG_FATAL("Could not open PHC");
return 0;
}
phc = MallocNew(struct phc_instance); phc = MallocNew(struct phc_instance);
phc->fd = phc_fd; phc->fd = phc_fd;
@@ -77,13 +75,15 @@ static int phc_initialise(RCL_Instance instance)
phc->nocrossts = RCL_GetDriverOption(instance, "nocrossts") ? 1 : 0; phc->nocrossts = RCL_GetDriverOption(instance, "nocrossts") ? 1 : 0;
phc->extpps = RCL_GetDriverOption(instance, "extpps") ? 1 : 0; phc->extpps = RCL_GetDriverOption(instance, "extpps") ? 1 : 0;
phc->clock = HCL_CreateInstance(0, 16, UTI_Log2ToDouble(RCL_GetDriverPoll(instance)),
RCL_GetPrecision(instance));
if (phc->extpps) { if (phc->extpps) {
s = RCL_GetDriverOption(instance, "pin"); s = RCL_GetDriverOption(instance, "pin");
phc->pin = s ? atoi(s) : 0; phc->pin = s ? atoi(s) : 0;
s = RCL_GetDriverOption(instance, "channel"); s = RCL_GetDriverOption(instance, "channel");
phc->channel = s ? atoi(s) : 0; phc->channel = s ? atoi(s) : 0;
rising_edge = RCL_GetDriverOption(instance, "clear") ? 0 : 1; rising_edge = RCL_GetDriverOption(instance, "clear") ? 0 : 1;
phc->clock = HCL_CreateInstance(0, 16, UTI_Log2ToDouble(RCL_GetDriverPoll(instance)));
if (!SYS_Linux_SetPHCExtTimestamping(phc->fd, phc->pin, phc->channel, if (!SYS_Linux_SetPHCExtTimestamping(phc->fd, phc->pin, phc->channel,
rising_edge, !rising_edge, 1)) rising_edge, !rising_edge, 1))
@@ -92,7 +92,6 @@ static int phc_initialise(RCL_Instance instance)
SCH_AddFileHandler(phc->fd, SCH_FILE_INPUT, read_ext_pulse, instance); SCH_AddFileHandler(phc->fd, SCH_FILE_INPUT, read_ext_pulse, instance);
} else { } else {
phc->pin = phc->channel = 0; phc->pin = phc->channel = 0;
phc->clock = NULL;
} }
RCL_SetDriverData(instance, phc); RCL_SetDriverData(instance, phc);
@@ -108,9 +107,9 @@ static void phc_finalise(RCL_Instance instance)
if (phc->extpps) { if (phc->extpps) {
SCH_RemoveFileHandler(phc->fd); SCH_RemoveFileHandler(phc->fd);
SYS_Linux_SetPHCExtTimestamping(phc->fd, phc->pin, phc->channel, 0, 0, 0); SYS_Linux_SetPHCExtTimestamping(phc->fd, phc->pin, phc->channel, 0, 0, 0);
HCL_DestroyInstance(phc->clock);
} }
HCL_DestroyInstance(phc->clock);
close(phc->fd); close(phc->fd);
Free(phc); Free(phc);
} }
@@ -141,29 +140,35 @@ static void read_ext_pulse(int fd, int event, void *anything)
UTI_DiffTimespecsToDouble(&phc_ts, &local_ts)); UTI_DiffTimespecsToDouble(&phc_ts, &local_ts));
} }
#define PHC_READINGS 25
static int phc_poll(RCL_Instance instance) static int phc_poll(RCL_Instance instance)
{ {
struct timespec phc_ts, sys_ts, local_ts, readings[PHC_READINGS][3];
struct phc_instance *phc; struct phc_instance *phc;
struct timespec phc_ts, sys_ts, local_ts; double phc_err, local_err;
double offset, phc_err, local_err; int n_readings;
phc = (struct phc_instance *)RCL_GetDriverData(instance); phc = (struct phc_instance *)RCL_GetDriverData(instance);
if (!SYS_Linux_GetPHCSample(phc->fd, phc->nocrossts, RCL_GetPrecision(instance), n_readings = SYS_Linux_GetPHCReadings(phc->fd, phc->nocrossts, &phc->mode,
&phc->mode, &phc_ts, &sys_ts, &phc_err)) PHC_READINGS, readings);
if (n_readings < 1)
return 0; return 0;
if (phc->extpps) { if (!HCL_ProcessReadings(phc->clock, n_readings, readings, &phc_ts, &sys_ts, &phc_err))
LCL_CookTime(&sys_ts, &local_ts, &local_err);
HCL_AccumulateSample(phc->clock, &phc_ts, &local_ts, phc_err + local_err);
return 0; return 0;
}
offset = UTI_DiffTimespecsToDouble(&phc_ts, &sys_ts); LCL_CookTime(&sys_ts, &local_ts, &local_err);
HCL_AccumulateSample(phc->clock, &phc_ts, &local_ts, phc_err + local_err);
DEBUG_LOG("PHC offset: %+.9f err: %.9f", offset, phc_err); if (phc->extpps)
return 0;
return RCL_AddSample(instance, &sys_ts, offset, LEAP_Normal); DEBUG_LOG("PHC offset: %+.9f err: %.9f",
UTI_DiffTimespecsToDouble(&phc_ts, &sys_ts), phc_err);
return RCL_AddSample(instance, &sys_ts, &phc_ts, LEAP_Normal);
} }
RefclockDriver RCL_PHC_driver = { RefclockDriver RCL_PHC_driver = {

View File

@@ -61,49 +61,36 @@ static int pps_initialise(RCL_Instance instance) {
edge_clear = RCL_GetDriverOption(instance, "clear") ? 1 : 0; edge_clear = RCL_GetDriverOption(instance, "clear") ? 1 : 0;
fd = open(path, O_RDWR); fd = open(path, O_RDWR);
if (fd < 0) { if (fd < 0)
LOG_FATAL("Could not open %s : %s", path, strerror(errno)); LOG_FATAL("Could not open %s : %s", path, strerror(errno));
return 0;
}
UTI_FdSetCloexec(fd); UTI_FdSetCloexec(fd);
if (time_pps_create(fd, &handle) < 0) { if (time_pps_create(fd, &handle) < 0)
LOG_FATAL("time_pps_create() failed on %s : %s", path, strerror(errno)); LOG_FATAL("time_pps_create() failed on %s : %s", path, strerror(errno));
return 0;
}
if (time_pps_getcap(handle, &mode) < 0) { if (time_pps_getcap(handle, &mode) < 0)
LOG_FATAL("time_pps_getcap() failed on %s : %s", path, strerror(errno)); LOG_FATAL("time_pps_getcap() failed on %s : %s", path, strerror(errno));
return 0;
}
if (time_pps_getparams(handle, &params) < 0) { if (time_pps_getparams(handle, &params) < 0)
LOG_FATAL("time_pps_getparams() failed on %s : %s", path, strerror(errno)); LOG_FATAL("time_pps_getparams() failed on %s : %s", path, strerror(errno));
return 0;
}
if (!edge_clear) { if (!edge_clear) {
if (!(mode & PPS_CAPTUREASSERT)) { if (!(mode & PPS_CAPTUREASSERT))
LOG_FATAL("CAPTUREASSERT not supported on %s", path); LOG_FATAL("CAPTUREASSERT not supported on %s", path);
return 0;
}
params.mode |= PPS_CAPTUREASSERT; params.mode |= PPS_CAPTUREASSERT;
params.mode &= ~PPS_CAPTURECLEAR; params.mode &= ~PPS_CAPTURECLEAR;
} else { } else {
if (!(mode & PPS_CAPTURECLEAR)) { if (!(mode & PPS_CAPTURECLEAR))
LOG_FATAL("CAPTURECLEAR not supported on %s", path); LOG_FATAL("CAPTURECLEAR not supported on %s", path);
return 0;
}
params.mode |= PPS_CAPTURECLEAR; params.mode |= PPS_CAPTURECLEAR;
params.mode &= ~PPS_CAPTUREASSERT; params.mode &= ~PPS_CAPTUREASSERT;
} }
if (time_pps_setparams(handle, &params) < 0) { if (time_pps_setparams(handle, &params) < 0)
LOG_FATAL("time_pps_setparams() failed on %s : %s", path, strerror(errno)); LOG_FATAL("time_pps_setparams() failed on %s : %s", path, strerror(errno));
return 0;
}
pps = MallocNew(struct pps_instance); pps = MallocNew(struct pps_instance);
pps->handle = handle; pps->handle = handle;

View File

@@ -95,7 +95,6 @@ static int shm_poll(RCL_Instance instance)
{ {
struct timespec receive_ts, clock_ts; struct timespec receive_ts, clock_ts;
struct shmTime t, *shm; struct shmTime t, *shm;
double offset;
shm = (struct shmTime *)RCL_GetDriverData(instance); shm = (struct shmTime *)RCL_GetDriverData(instance);
@@ -124,9 +123,8 @@ static int shm_poll(RCL_Instance instance)
UTI_NormaliseTimespec(&clock_ts); UTI_NormaliseTimespec(&clock_ts);
UTI_NormaliseTimespec(&receive_ts); UTI_NormaliseTimespec(&receive_ts);
offset = UTI_DiffTimespecsToDouble(&clock_ts, &receive_ts);
return RCL_AddSample(instance, &receive_ts, offset, t.leap); return RCL_AddSample(instance, &receive_ts, &clock_ts, t.leap);
} }
RefclockDriver RCL_SHM_driver = { RefclockDriver RCL_SHM_driver = {

View File

@@ -60,8 +60,8 @@ struct sock_sample {
static void read_sample(int sockfd, int event, void *anything) static void read_sample(int sockfd, int event, void *anything)
{ {
struct timespec sys_ts, ref_ts;
struct sock_sample sample; struct sock_sample sample;
struct timespec ts;
RCL_Instance instance; RCL_Instance instance;
int s; int s;
@@ -86,13 +86,18 @@ static void read_sample(int sockfd, int event, void *anything)
return; return;
} }
UTI_TimevalToTimespec(&sample.tv, &ts); UTI_TimevalToTimespec(&sample.tv, &sys_ts);
UTI_NormaliseTimespec(&ts); UTI_NormaliseTimespec(&sys_ts);
if (!UTI_IsTimeOffsetSane(&sys_ts, sample.offset))
return;
UTI_AddDoubleToTimespec(&sys_ts, sample.offset, &ref_ts);
if (sample.pulse) { if (sample.pulse) {
RCL_AddPulse(instance, &ts, sample.offset); RCL_AddPulse(instance, &sys_ts, sample.offset);
} else { } else {
RCL_AddSample(instance, &ts, sample.offset, sample.leap); RCL_AddSample(instance, &sys_ts, &ref_ts, sample.leap);
} }
} }

View File

@@ -150,6 +150,9 @@ static SCH_TimeoutID fb_drift_timeout_id;
static double last_ref_update; static double last_ref_update;
static double last_ref_update_interval; static double last_ref_update_interval;
static double last_ref_adjustment;
static int ref_adjustments;
/* ================================================== */ /* ================================================== */
static NTP_Leap get_tz_leap(time_t when, int *tai_offset); static NTP_Leap get_tz_leap(time_t when, int *tai_offset);
@@ -286,6 +289,8 @@ REF_Initialise(void)
UTI_ZeroTimespec(&our_ref_time); UTI_ZeroTimespec(&our_ref_time);
last_ref_update = 0.0; last_ref_update = 0.0;
last_ref_update_interval = 0.0; last_ref_update_interval = 0.0;
last_ref_adjustment = 0.0;
ref_adjustments = 0;
LCL_AddParameterChangeHandler(handle_slew, NULL); LCL_AddParameterChangeHandler(handle_slew, NULL);
@@ -960,6 +965,27 @@ fuzz_ref_time(struct timespec *ts)
/* ================================================== */ /* ================================================== */
static double
get_correction_rate(double offset_sd, double update_interval)
{
/* We want to correct the offset quickly, but we also want to keep the
frequency error caused by the correction itself low.
Define correction rate as the area of the region bounded by the graph of
offset corrected in time. Set the rate so that the time needed to correct
an offset equal to the current sourcestats stddev will be equal to the
update interval multiplied by the correction time ratio (assuming linear
adjustment). The offset and the time needed to make the correction are
inversely proportional.
This is only a suggestion and it's up to the system driver how the
adjustment will be executed. */
return correction_time_ratio * 0.5 * offset_sd * update_interval;
}
/* ================================================== */
void void
REF_SetReference(int stratum, NTP_Leap leap, int combined_sources, REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
uint32_t ref_id, IPAddr *ref_ip, struct timespec *ref_time, uint32_t ref_id, IPAddr *ref_ip, struct timespec *ref_time,
@@ -969,7 +995,7 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
{ {
double uncorrected_offset, accumulate_offset, step_offset; double uncorrected_offset, accumulate_offset, step_offset;
double residual_frequency, local_abs_frequency; double residual_frequency, local_abs_frequency;
double elapsed, mono_now, update_interval, correction_rate, orig_root_distance; double elapsed, mono_now, update_interval, orig_root_distance;
struct timespec now, raw_now; struct timespec now, raw_now;
int manual; int manual;
@@ -1024,21 +1050,6 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
last_ref_update_interval = update_interval; last_ref_update_interval = update_interval;
last_offset = offset; last_offset = offset;
/* We want to correct the offset quickly, but we also want to keep the
frequency error caused by the correction itself low.
Define correction rate as the area of the region bounded by the graph of
offset corrected in time. Set the rate so that the time needed to correct
an offset equal to the current sourcestats stddev will be equal to the
update interval multiplied by the correction time ratio (assuming linear
adjustment). The offset and the time needed to make the correction are
inversely proportional.
This is only a suggestion and it's up to the system driver how the
adjustment will be executed. */
correction_rate = correction_time_ratio * 0.5 * offset_sd * update_interval;
/* Check if the clock should be stepped */ /* Check if the clock should be stepped */
if (is_step_limit_reached(offset, uncorrected_offset)) { if (is_step_limit_reached(offset, uncorrected_offset)) {
/* Cancel the uncorrected offset and correct the total offset by step */ /* Cancel the uncorrected offset and correct the total offset by step */
@@ -1050,7 +1061,8 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
} }
/* Adjust the clock */ /* Adjust the clock */
LCL_AccumulateFrequencyAndOffset(frequency, accumulate_offset, correction_rate); LCL_AccumulateFrequencyAndOffset(frequency, accumulate_offset,
get_correction_rate(offset_sd, update_interval));
maybe_log_offset(offset, raw_now.tv_sec); maybe_log_offset(offset, raw_now.tv_sec);
@@ -1095,6 +1107,27 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
avg2_moving = 1; avg2_moving = 1;
avg2_offset = SQUARE(offset); avg2_offset = SQUARE(offset);
} }
ref_adjustments = 0;
}
/* ================================================== */
int
REF_AdjustReference(double offset, double frequency)
{
double adj_corr_rate, ref_corr_rate, mono_now;
mono_now = SCH_GetLastEventMonoTime();
ref_adjustments++;
adj_corr_rate = get_correction_rate(fabs(offset), mono_now - last_ref_adjustment);
ref_corr_rate = get_correction_rate(our_offset_sd, last_ref_update_interval) /
ref_adjustments;
last_ref_adjustment = mono_now;
return LCL_AccumulateFrequencyAndOffsetNoHandlers(frequency, offset,
MAX(adj_corr_rate, ref_corr_rate));
} }
/* ================================================== */ /* ================================================== */
@@ -1333,7 +1366,8 @@ REF_DisableLocal(void)
static int static int
is_leap_close(time_t t) is_leap_close(time_t t)
{ {
return t >= leap_when - LEAP_SECOND_CLOSE && t < leap_when + LEAP_SECOND_CLOSE; return leap_when != 0 &&
t >= leap_when - LEAP_SECOND_CLOSE && t < leap_when + LEAP_SECOND_CLOSE;
} }
/* ================================================== */ /* ================================================== */

View File

@@ -162,6 +162,10 @@ extern void REF_SetManualReference
extern void extern void
REF_SetUnsynchronised(void); REF_SetUnsynchronised(void);
/* Make a small correction of the clock without updating the reference
parameters and calling the clock change handlers */
extern int REF_AdjustReference(double offset, double frequency);
/* Announce a leap second before the full reference update */ /* Announce a leap second before the full reference update */
extern void REF_UpdateLeapStatus(NTP_Leap leap); extern void REF_UpdateLeapStatus(NTP_Leap leap);

View File

@@ -117,6 +117,9 @@ typedef struct {
uint32_t cmd_drops; uint32_t cmd_drops;
uint32_t log_drops; uint32_t log_drops;
uint32_t ntp_auth_hits; uint32_t ntp_auth_hits;
uint32_t ntp_interleaved_hits;
uint32_t ntp_timestamps;
uint32_t ntp_span_seconds;
} RPT_ServerStatsReport; } RPT_ServerStatsReport;
typedef struct { typedef struct {

2
rtc.c
View File

@@ -148,6 +148,8 @@ RTC_Initialise(int initial_set)
if (driver.init) { if (driver.init) {
if ((driver.init)()) { if ((driver.init)()) {
driver_initialised = 1; driver_initialised = 1;
} else {
LOG(LOGS_ERR, "RTC driver could not be initialised");
} }
} else { } else {
LOG(LOGS_ERR, "RTC not supported on this operating system"); LOG(LOGS_ERR, "RTC not supported on this operating system");

View File

@@ -64,7 +64,7 @@ static OperatingMode operating_mode = OM_NORMAL;
/* ================================================== */ /* ================================================== */
static int fd = -1; static int fd;
#define LOWEST_MEASUREMENT_PERIOD 15 #define LOWEST_MEASUREMENT_PERIOD 15
#define HIGHEST_MEASUREMENT_PERIOD 480 #define HIGHEST_MEASUREMENT_PERIOD 480
@@ -82,16 +82,12 @@ static int skip_interrupts;
#define MAX_SAMPLES 64 #define MAX_SAMPLES 64
/* Real time clock samples. We store the seconds count as originally /* Real time clock samples. We store the seconds count as originally
measured, together with a 'trim' that compensates these values for measured. */
any steps made to the RTC to bring it back into line
occasionally. The trim is in seconds. */
static time_t *rtc_sec = NULL; static time_t *rtc_sec = NULL;
static double *rtc_trim = NULL;
/* Reference time, against which delta times on the RTC scale are measured */ /* Reference time, against which delta times on the RTC scale are measured */
static time_t rtc_ref; static time_t rtc_ref;
/* System clock samples associated with the above samples. */ /* System clock samples associated with the above samples. */
static struct timespec *system_times = NULL; static struct timespec *system_times = NULL;
@@ -145,7 +141,7 @@ static double file_ref_offset, file_rate_ppm;
/* ================================================== */ /* ================================================== */
/* Flag to remember whether to assume the RTC is running on UTC */ /* Flag to remember whether to assume the RTC is running on UTC */
static int rtc_on_utc = 1; static int rtc_on_utc;
/* ================================================== */ /* ================================================== */
@@ -168,7 +164,6 @@ discard_samples(int new_first)
n_to_save = n_samples - new_first; n_to_save = n_samples - new_first;
memmove(rtc_sec, rtc_sec + new_first, n_to_save * sizeof(time_t)); memmove(rtc_sec, rtc_sec + new_first, n_to_save * sizeof(time_t));
memmove(rtc_trim, rtc_trim + new_first, n_to_save * sizeof(double));
memmove(system_times, system_times + new_first, n_to_save * sizeof(struct timespec)); memmove(system_times, system_times + new_first, n_to_save * sizeof(struct timespec));
n_samples = n_to_save; n_samples = n_to_save;
@@ -188,21 +183,16 @@ accumulate_sample(time_t rtc, struct timespec *sys)
} }
/* Discard all samples if the RTC was stepped back (not our trim) */ /* Discard all samples if the RTC was stepped back (not our trim) */
if (n_samples > 0 && rtc_sec[n_samples - 1] - rtc >= rtc_trim[n_samples - 1]) { if (n_samples > 0 && rtc_sec[n_samples - 1] >= rtc) {
DEBUG_LOG("RTC samples discarded"); DEBUG_LOG("RTC samples discarded");
n_samples = 0; n_samples = 0;
} }
/* Always use most recent sample as reference */ /* Always use most recent sample as reference */
/* use sample only if n_sample is not negative*/
if(n_samples >=0)
{
rtc_ref = rtc; rtc_ref = rtc;
rtc_sec[n_samples] = rtc; rtc_sec[n_samples] = rtc;
rtc_trim[n_samples] = 0.0;
system_times[n_samples] = *sys; system_times[n_samples] = *sys;
++n_samples_since_regression; ++n_samples_since_regression;
}
++n_samples; ++n_samples;
} }
@@ -227,7 +217,7 @@ run_regression(int new_sample,
if (n_samples > 0) { if (n_samples > 0) {
for (i=0; i<n_samples; i++) { for (i=0; i<n_samples; i++) {
rtc_rel[i] = rtc_trim[i] + (double)(rtc_sec[i] - rtc_ref); rtc_rel[i] = (double)(rtc_sec[i] - rtc_ref);
offsets[i] = ((double) (rtc_ref - system_times[i].tv_sec) - offsets[i] = ((double) (rtc_ref - system_times[i].tv_sec) -
(1.0e-9 * system_times[i].tv_nsec) + (1.0e-9 * system_times[i].tv_nsec) +
rtc_rel[i]); rtc_rel[i]);
@@ -434,6 +424,7 @@ setup_config(void)
static void static void
read_coefs_from_file(void) read_coefs_from_file(void)
{ {
double ref_time;
FILE *in; FILE *in;
if (!tried_to_load_coefs) { if (!tried_to_load_coefs) {
@@ -444,11 +435,12 @@ read_coefs_from_file(void)
if (coefs_file_name && if (coefs_file_name &&
(in = UTI_OpenFile(NULL, coefs_file_name, NULL, 'r', 0))) { (in = UTI_OpenFile(NULL, coefs_file_name, NULL, 'r', 0))) {
if (fscanf(in, "%d%ld%lf%lf", if (fscanf(in, "%d%lf%lf%lf",
&valid_coefs_from_file, &valid_coefs_from_file,
&file_ref_time, &ref_time,
&file_ref_offset, &file_ref_offset,
&file_rate_ppm) == 4) { &file_rate_ppm) == 4) {
file_ref_time = ref_time;
} else { } else {
LOG(LOGS_WARN, "Could not read coefficients from %s", coefs_file_name); LOG(LOGS_WARN, "Could not read coefficients from %s", coefs_file_name);
} }
@@ -472,7 +464,7 @@ write_coefs_to_file(int valid,time_t ref_time,double offset,double rate)
return RTC_ST_BADFILE; return RTC_ST_BADFILE;
/* Gain rate is written out in ppm */ /* Gain rate is written out in ppm */
fprintf(out, "%1d %ld %.6f %.3f\n", valid, ref_time, offset, 1.0e6 * rate); fprintf(out, "%1d %.0f %.6f %.3f\n", valid, (double)ref_time, offset, 1.0e6 * rate);
fclose(out); fclose(out);
/* Rename the temporary file to the correct location */ /* Rename the temporary file to the correct location */
@@ -525,7 +517,6 @@ RTC_Linux_Initialise(void)
UTI_FdSetCloexec(fd); UTI_FdSetCloexec(fd);
rtc_sec = MallocArray(time_t, MAX_SAMPLES); rtc_sec = MallocArray(time_t, MAX_SAMPLES);
rtc_trim = MallocArray(double, MAX_SAMPLES);
system_times = MallocArray(struct timespec, MAX_SAMPLES); system_times = MallocArray(struct timespec, MAX_SAMPLES);
/* Setup details depending on configuration options */ /* Setup details depending on configuration options */
@@ -578,7 +569,6 @@ RTC_Linux_Finalise(void)
LCL_RemoveParameterChangeHandler(slew_samples, NULL); LCL_RemoveParameterChangeHandler(slew_samples, NULL);
Free(rtc_sec); Free(rtc_sec);
Free(rtc_trim);
Free(system_times); Free(system_times);
} }
@@ -639,11 +629,7 @@ handle_initial_trim(void)
run_regression(1, &coefs_valid, &coef_ref_time, &coef_seconds_fast, &coef_gain_rate); run_regression(1, &coefs_valid, &coef_ref_time, &coef_seconds_fast, &coef_gain_rate);
n_samples_since_regression = 0; n_samples_since_regression = 0;
n_samples = 0;
/* Set sample number to -1 so the next sample is not used, as it will not yet be corrected for System Trim*/
n_samples = -1;
read_coefs_from_file(); read_coefs_from_file();
@@ -1028,8 +1014,7 @@ RTC_Linux_GetReport(RPT_RTC_Report *report)
report->n_samples = n_samples; report->n_samples = n_samples;
report->n_runs = n_runs; report->n_runs = n_runs;
if (n_samples > 1) { if (n_samples > 1) {
report->span_seconds = ((rtc_sec[n_samples-1] - rtc_sec[0]) + report->span_seconds = rtc_sec[n_samples - 1] - rtc_sec[0];
(long)(rtc_trim[n_samples-1] - rtc_trim[0]));
} else { } else {
report->span_seconds = 0; report->span_seconds = 0;
} }

View File

@@ -162,6 +162,14 @@ SPF_GetNumberOfSamples(SPF_Instance filter)
/* ================================================== */ /* ================================================== */
int
SPF_GetMaxSamples(SPF_Instance filter)
{
return filter->max_samples;
}
/* ================================================== */
double double
SPF_GetAvgSampleDispersion(SPF_Instance filter) SPF_GetAvgSampleDispersion(SPF_Instance filter)
{ {
@@ -170,11 +178,21 @@ SPF_GetAvgSampleDispersion(SPF_Instance filter)
/* ================================================== */ /* ================================================== */
void static void
SPF_DropSamples(SPF_Instance filter) drop_samples(SPF_Instance filter, int keep_last)
{ {
filter->index = -1; filter->index = -1;
filter->used = 0; filter->used = 0;
if (!keep_last)
filter->last = -1;
}
/* ================================================== */
void
SPF_DropSamples(SPF_Instance filter)
{
drop_samples(filter, 0);
} }
/* ================================================== */ /* ================================================== */
@@ -386,7 +404,6 @@ combine_selected_samples(SPF_Instance filter, int n, NTP_Sample *result)
result->root_dispersion = MAX(disp, mean_root_dispersion); result->root_dispersion = MAX(disp, mean_root_dispersion);
result->peer_delay = mean_peer_delay; result->peer_delay = mean_peer_delay;
result->root_delay = mean_root_delay; result->root_delay = mean_root_delay;
result->stratum = last_sample->stratum;
return 1; return 1;
} }
@@ -400,17 +417,40 @@ SPF_GetFilteredSample(SPF_Instance filter, NTP_Sample *sample)
n = select_samples(filter); n = select_samples(filter);
DEBUG_LOG("selected %d from %d samples", n, filter->used);
if (n < 1) if (n < 1)
return 0; return 0;
if (!combine_selected_samples(filter, n, sample)) if (!combine_selected_samples(filter, n, sample))
return 0; return 0;
SPF_DropSamples(filter); drop_samples(filter, 1);
return 1; return 1;
} }
/* ================================================== */
static int
get_first_last(SPF_Instance filter, int *first, int *last)
{
if (filter->last < 0)
return 0;
/* Always slew the last sample as it may be returned even if no new
samples were accumulated */
if (filter->used > 0) {
*first = 0;
*last = filter->used - 1;
} else {
*first = *last = filter->last;
}
return 1;
}
/* ================================================== */ /* ================================================== */
void void
@@ -419,18 +459,9 @@ SPF_SlewSamples(SPF_Instance filter, struct timespec *when, double dfreq, double
int i, first, last; int i, first, last;
double delta_time; double delta_time;
if (filter->last < 0) if (!get_first_last(filter, &first, &last))
return; return;
/* Always slew the last sample as it may be returned even if no new
samples were accumulated */
if (filter->used > 0) {
first = 0;
last = filter->used - 1;
} else {
first = last = filter->last;
}
for (i = first; i <= last; i++) { for (i = first; i <= last; i++) {
UTI_AdjustTimespec(&filter->samples[i].time, when, &filter->samples[i].time, UTI_AdjustTimespec(&filter->samples[i].time, when, &filter->samples[i].time,
&delta_time, dfreq, doffset); &delta_time, dfreq, doffset);
@@ -440,6 +471,20 @@ SPF_SlewSamples(SPF_Instance filter, struct timespec *when, double dfreq, double
/* ================================================== */ /* ================================================== */
void
SPF_CorrectOffset(SPF_Instance filter, double doffset)
{
int i, first, last;
if (!get_first_last(filter, &first, &last))
return;
for (i = first; i <= last; i++)
filter->samples[i].offset -= doffset;
}
/* ================================================== */
void void
SPF_AddDispersion(SPF_Instance filter, double dispersion) SPF_AddDispersion(SPF_Instance filter, double dispersion)
{ {

View File

@@ -39,11 +39,13 @@ extern void SPF_DestroyInstance(SPF_Instance filter);
extern int SPF_AccumulateSample(SPF_Instance filter, NTP_Sample *sample); extern int SPF_AccumulateSample(SPF_Instance filter, NTP_Sample *sample);
extern int SPF_GetLastSample(SPF_Instance filter, NTP_Sample *sample); extern int SPF_GetLastSample(SPF_Instance filter, NTP_Sample *sample);
extern int SPF_GetNumberOfSamples(SPF_Instance filter); extern int SPF_GetNumberOfSamples(SPF_Instance filter);
extern int SPF_GetMaxSamples(SPF_Instance filter);
extern double SPF_GetAvgSampleDispersion(SPF_Instance filter); extern double SPF_GetAvgSampleDispersion(SPF_Instance filter);
extern void SPF_DropSamples(SPF_Instance filter); extern void SPF_DropSamples(SPF_Instance filter);
extern int SPF_GetFilteredSample(SPF_Instance filter, NTP_Sample *sample); extern int SPF_GetFilteredSample(SPF_Instance filter, NTP_Sample *sample);
extern void SPF_SlewSamples(SPF_Instance filter, struct timespec *when, extern void SPF_SlewSamples(SPF_Instance filter, struct timespec *when,
double dfreq, double doffset); double dfreq, double doffset);
extern void SPF_CorrectOffset(SPF_Instance filter, double doffset);
extern void SPF_AddDispersion(SPF_Instance filter, double dispersion); extern void SPF_AddDispersion(SPF_Instance filter, double dispersion);
#endif #endif

33
sched.c
View File

@@ -111,7 +111,8 @@ static struct timespec last_class_dispatch[SCH_NumberOfClasses];
/* ================================================== */ /* ================================================== */
static int need_to_exit; /* Flag terminating the main loop, which can be set from a signal handler */
static volatile int need_to_exit;
/* ================================================== */ /* ================================================== */
@@ -498,12 +499,15 @@ SCH_RemoveTimeout(SCH_TimeoutID id)
static void static void
dispatch_timeouts(struct timespec *now) { dispatch_timeouts(struct timespec *now) {
unsigned long n_done, n_entries_on_start;
TimerQueueEntry *ptr; TimerQueueEntry *ptr;
SCH_TimeoutHandler handler; SCH_TimeoutHandler handler;
SCH_ArbitraryArgument arg; SCH_ArbitraryArgument arg;
int n_done = 0, n_entries_on_start = n_timer_queue_entries;
while (1) { n_entries_on_start = n_timer_queue_entries;
n_done = 0;
do {
LCL_ReadRawTime(now); LCL_ReadRawTime(now);
if (!(n_timer_queue_entries > 0 && if (!(n_timer_queue_entries > 0 &&
@@ -526,16 +530,21 @@ dispatch_timeouts(struct timespec *now) {
/* Increment count of timeouts handled */ /* Increment count of timeouts handled */
++n_done; ++n_done;
/* If more timeouts were handled than there were in the timer queue on /* If the number of dispatched timeouts is significantly larger than the
start and there are now, assume some code is scheduling timeouts with length of the queue on start and now, assume there is a bug causing
negative delays and abort. Make the actual limit higher in case the an infinite loop by constantly adding a timeout with a zero or negative
machine is temporarily overloaded and dispatching the handlers takes delay. Check the actual rate of timeouts to avoid false positives in
more time than was delay of a scheduled timeout. */ case the execution slowed down so much (e.g. due to memory thrashing)
if (n_done > n_timer_queue_entries * 4 && that it repeatedly takes more time to handle the timeout than is its
n_done > n_entries_on_start * 4) { delay. This is a safety mechanism intended to stop a full-speed flood
of NTP requests due to a bug in the NTP polling. */
if (n_done > 20 &&
n_done > 4 * MAX(n_timer_queue_entries, n_entries_on_start) &&
fabs(UTI_DiffTimespecsToDouble(now, &last_select_ts_raw)) / n_done < 0.01)
LOG_FATAL("Possible infinite loop in scheduling"); LOG_FATAL("Possible infinite loop in scheduling");
}
} } while (!need_to_exit);
} }
/* ================================================== */ /* ================================================== */

View File

@@ -102,8 +102,11 @@ SIV_CreateInstance(SIV_Algorithm algorithm)
init_gnutls(); init_gnutls();
/* Check if the cipher is actually supported */ /* Check if the cipher is actually supported */
if (gnutls_cipher_get_tag_size(calgo) == 0) if (gnutls_cipher_get_tag_size(calgo) == 0) {
if (instance_counter == 0)
deinit_gnutls();
return NULL; return NULL;
}
instance = MallocNew(struct SIV_Instance_Record); instance = MallocNew(struct SIV_Instance_Record);
instance->algorithm = calgo; instance->algorithm = calgo;
@@ -162,17 +165,29 @@ SIV_SetKey(SIV_Instance instance, const unsigned char *key, int length)
datum.data = (unsigned char *)key; datum.data = (unsigned char *)key;
datum.size = length; datum.size = length;
/* Initialise a new cipher with the provided key (gnutls does not seem to #ifdef HAVE_GNUTLS_AEAD_CIPHER_SET_KEY
have a function to change the key directly) */ if (instance->cipher) {
r = gnutls_aead_cipher_set_key(instance->cipher, &datum);
if (r < 0) {
DEBUG_LOG("Could not set cipher key : %s", gnutls_strerror(r));
return 0;
}
return 1;
}
#endif
/* Initialise a new cipher with the provided key */
r = gnutls_aead_cipher_init(&cipher, instance->algorithm, &datum); r = gnutls_aead_cipher_init(&cipher, instance->algorithm, &datum);
if (r < 0) { if (r < 0) {
DEBUG_LOG("Could not initialise %s : %s", "cipher", gnutls_strerror(r)); DEBUG_LOG("Could not initialise %s : %s", "cipher", gnutls_strerror(r));
return 0; return 0;
} }
/* Replace the previous cipher */ /* Destroy the previous cipher (if its key could not be changed directly) */
if (instance->cipher) if (instance->cipher)
gnutls_aead_cipher_deinit(instance->cipher); gnutls_aead_cipher_deinit(instance->cipher);
instance->cipher = cipher; instance->cipher = cipher;
return 1; return 1;

View File

@@ -40,6 +40,7 @@
#include "array.h" #include "array.h"
#include "logging.h" #include "logging.h"
#include "privops.h" #include "privops.h"
#include "ptp.h"
#include "util.h" #include "util.h"
#define INVALID_SOCK_FD (-4) #define INVALID_SOCK_FD (-4)
@@ -58,10 +59,16 @@ struct Message {
union sockaddr_all name; union sockaddr_all name;
struct iovec iov; struct iovec iov;
/* Buffer of sufficient length for all expected messages */ /* Buffer of sufficient length for all expected messages */
union { struct {
NTP_Packet ntp_msg; /* Extra space for Ethernet, IPv4/IPv6, and UDP headers in
CMD_Request cmd_request; timestamped messages received from the Linux error queue */
CMD_Reply cmd_reply; uint8_t l234_headers[64];
union {
NTP_Packet ntp_msg;
PTP_NtpMessage ptp_msg;
CMD_Request cmd_request;
CMD_Reply cmd_reply;
} msg;
} msg_buf; } msg_buf;
/* Aligned buffer for control messages */ /* Aligned buffer for control messages */
struct cmsghdr cmsg_buf[CMSG_BUF_SIZE / sizeof (struct cmsghdr)]; struct cmsghdr cmsg_buf[CMSG_BUF_SIZE / sizeof (struct cmsghdr)];
@@ -498,6 +505,8 @@ bind_unix_address(int sock_fd, const char *addr, int flags)
{ {
union sockaddr_all saddr; union sockaddr_all saddr;
memset(&saddr, 0, sizeof (saddr));
if (snprintf(saddr.un.sun_path, sizeof (saddr.un.sun_path), "%s", addr) >= if (snprintf(saddr.un.sun_path, sizeof (saddr.un.sun_path), "%s", addr) >=
sizeof (saddr.un.sun_path)) { sizeof (saddr.un.sun_path)) {
DEBUG_LOG("Unix socket path %s too long", addr); DEBUG_LOG("Unix socket path %s too long", addr);
@@ -530,6 +539,8 @@ connect_unix_address(int sock_fd, const char *addr)
{ {
union sockaddr_all saddr; union sockaddr_all saddr;
memset(&saddr, 0, sizeof (saddr));
if (snprintf(saddr.un.sun_path, sizeof (saddr.un.sun_path), "%s", addr) >= if (snprintf(saddr.un.sun_path, sizeof (saddr.un.sun_path), "%s", addr) >=
sizeof (saddr.un.sun_path)) { sizeof (saddr.un.sun_path)) {
DEBUG_LOG("Unix socket path %s too long", addr); DEBUG_LOG("Unix socket path %s too long", addr);
@@ -737,6 +748,17 @@ init_message_nonaddress(SCK_Message *message)
/* ================================================== */ /* ================================================== */
static int
match_cmsg(struct cmsghdr *cmsg, int level, int type, size_t length)
{
if (cmsg->cmsg_type == type && cmsg->cmsg_level == level &&
(length == 0 || cmsg->cmsg_len == CMSG_LEN(length)))
return 1;
return 0;
}
/* ================================================== */
static int static int
process_header(struct msghdr *msg, int msg_length, int sock_fd, int flags, process_header(struct msghdr *msg, int msg_length, int sock_fd, int flags,
SCK_Message *message) SCK_Message *message)
@@ -794,8 +816,10 @@ process_header(struct msghdr *msg, int msg_length, int sock_fd, int flags,
} }
for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
if (0) {
}
#ifdef HAVE_IN_PKTINFO #ifdef HAVE_IN_PKTINFO
if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) { else if (match_cmsg(cmsg, IPPROTO_IP, IP_PKTINFO, sizeof (struct in_pktinfo))) {
struct in_pktinfo ipi; struct in_pktinfo ipi;
if (message->addr_type != SCK_ADDR_IP) if (message->addr_type != SCK_ADDR_IP)
@@ -807,7 +831,7 @@ process_header(struct msghdr *msg, int msg_length, int sock_fd, int flags,
message->if_index = ipi.ipi_ifindex; message->if_index = ipi.ipi_ifindex;
} }
#elif defined(IP_RECVDSTADDR) #elif defined(IP_RECVDSTADDR)
if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_RECVDSTADDR) { else if (match_cmsg(cmsg, IPPROTO_IP, IP_RECVDSTADDR, sizeof (struct in_addr))) {
struct in_addr addr; struct in_addr addr;
if (message->addr_type != SCK_ADDR_IP) if (message->addr_type != SCK_ADDR_IP)
@@ -818,9 +842,8 @@ process_header(struct msghdr *msg, int msg_length, int sock_fd, int flags,
message->local_addr.ip.family = IPADDR_INET4; message->local_addr.ip.family = IPADDR_INET4;
} }
#endif #endif
#ifdef HAVE_IN6_PKTINFO #ifdef HAVE_IN6_PKTINFO
if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { else if (match_cmsg(cmsg, IPPROTO_IPV6, IPV6_PKTINFO, sizeof (struct in6_pktinfo))) {
struct in6_pktinfo ipi; struct in6_pktinfo ipi;
if (message->addr_type != SCK_ADDR_IP) if (message->addr_type != SCK_ADDR_IP)
@@ -833,25 +856,23 @@ process_header(struct msghdr *msg, int msg_length, int sock_fd, int flags,
message->if_index = ipi.ipi6_ifindex; message->if_index = ipi.ipi6_ifindex;
} }
#endif #endif
#ifdef SCM_TIMESTAMP #ifdef SCM_TIMESTAMP
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) { else if (match_cmsg(cmsg, SOL_SOCKET, SCM_TIMESTAMP, sizeof (struct timeval))) {
struct timeval tv; struct timeval tv;
memcpy(&tv, CMSG_DATA(cmsg), sizeof (tv)); memcpy(&tv, CMSG_DATA(cmsg), sizeof (tv));
UTI_TimevalToTimespec(&tv, &message->timestamp.kernel); UTI_TimevalToTimespec(&tv, &message->timestamp.kernel);
} }
#endif #endif
#ifdef SCM_TIMESTAMPNS #ifdef SCM_TIMESTAMPNS
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) { else if (match_cmsg(cmsg, SOL_SOCKET, SCM_TIMESTAMPNS, sizeof (message->timestamp.kernel))) {
memcpy(&message->timestamp.kernel, CMSG_DATA(cmsg), sizeof (message->timestamp.kernel)); memcpy(&message->timestamp.kernel, CMSG_DATA(cmsg), sizeof (message->timestamp.kernel));
} }
#endif #endif
#ifdef HAVE_LINUX_TIMESTAMPING #ifdef HAVE_LINUX_TIMESTAMPING
#ifdef HAVE_LINUX_TIMESTAMPING_OPT_PKTINFO #ifdef HAVE_LINUX_TIMESTAMPING_OPT_PKTINFO
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPING_PKTINFO) { else if (match_cmsg(cmsg, SOL_SOCKET, SCM_TIMESTAMPING_PKTINFO,
sizeof (struct scm_ts_pktinfo))) {
struct scm_ts_pktinfo ts_pktinfo; struct scm_ts_pktinfo ts_pktinfo;
memcpy(&ts_pktinfo, CMSG_DATA(cmsg), sizeof (ts_pktinfo)); memcpy(&ts_pktinfo, CMSG_DATA(cmsg), sizeof (ts_pktinfo));
@@ -859,17 +880,17 @@ process_header(struct msghdr *msg, int msg_length, int sock_fd, int flags,
message->timestamp.l2_length = ts_pktinfo.pkt_length; message->timestamp.l2_length = ts_pktinfo.pkt_length;
} }
#endif #endif
else if (match_cmsg(cmsg, SOL_SOCKET, SCM_TIMESTAMPING,
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPING) { sizeof (struct scm_timestamping))) {
struct scm_timestamping ts3; struct scm_timestamping ts3;
memcpy(&ts3, CMSG_DATA(cmsg), sizeof (ts3)); memcpy(&ts3, CMSG_DATA(cmsg), sizeof (ts3));
message->timestamp.kernel = ts3.ts[0]; message->timestamp.kernel = ts3.ts[0];
message->timestamp.hw = ts3.ts[2]; message->timestamp.hw = ts3.ts[2];
} }
else if ((match_cmsg(cmsg, SOL_IP, IP_RECVERR, 0) ||
if ((cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVERR) || match_cmsg(cmsg, SOL_IPV6, IPV6_RECVERR, 0)) &&
(cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_RECVERR)) { cmsg->cmsg_len >= CMSG_LEN(sizeof (struct sock_extended_err))) {
struct sock_extended_err err; struct sock_extended_err err;
memcpy(&err, CMSG_DATA(cmsg), sizeof (err)); memcpy(&err, CMSG_DATA(cmsg), sizeof (err));
@@ -881,8 +902,7 @@ process_header(struct msghdr *msg, int msg_length, int sock_fd, int flags,
} }
} }
#endif #endif
else if (match_cmsg(cmsg, SOL_SOCKET, SCM_RIGHTS, 0)) {
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
if (!(flags & SCK_FLAG_MSG_DESCRIPTOR) || cmsg->cmsg_len != CMSG_LEN(sizeof (int))) { if (!(flags & SCK_FLAG_MSG_DESCRIPTOR) || cmsg->cmsg_len != CMSG_LEN(sizeof (int))) {
int i, fd; int i, fd;
@@ -896,6 +916,10 @@ process_header(struct msghdr *msg, int msg_length, int sock_fd, int flags,
memcpy(&message->descriptor, CMSG_DATA(cmsg), sizeof (message->descriptor)); memcpy(&message->descriptor, CMSG_DATA(cmsg), sizeof (message->descriptor));
} }
} }
else {
DEBUG_LOG("Unexpected control message level=%d type=%d len=%d",
cmsg->cmsg_level, cmsg->cmsg_type, (int)cmsg->cmsg_len);
}
} }
if (!r && message->descriptor != INVALID_SOCK_FD) if (!r && message->descriptor != INVALID_SOCK_FD)

220
sources.c
View File

@@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011-2016, 2018, 2020 * Copyright (C) Miroslav Lichvar 2011-2016, 2018, 2020-2021
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of version 2 of the GNU General Public License as
@@ -54,7 +54,6 @@ static int initialised = 0;
/* ================================================== */ /* ================================================== */
/* Structure used to hold info for selecting between sources */ /* Structure used to hold info for selecting between sources */
struct SelectInfo { struct SelectInfo {
int stratum;
int select_ok; int select_ok;
double std_dev; double std_dev;
double root_distance; double root_distance;
@@ -69,6 +68,7 @@ struct SelectInfo {
typedef enum { typedef enum {
SRC_OK, /* OK so far, not a final status! */ SRC_OK, /* OK so far, not a final status! */
SRC_UNSELECTABLE, /* Has noselect option set */ SRC_UNSELECTABLE, /* Has noselect option set */
SRC_UNSYNCHRONISED, /* Provides samples but not unsynchronised */
SRC_BAD_STATS, /* Doesn't have valid stats data */ SRC_BAD_STATS, /* Doesn't have valid stats data */
SRC_BAD_DISTANCE, /* Has root distance longer than allowed maximum */ SRC_BAD_DISTANCE, /* Has root distance longer than allowed maximum */
SRC_JITTERY, /* Had std dev larger than allowed maximum */ SRC_JITTERY, /* Had std dev larger than allowed maximum */
@@ -132,7 +132,10 @@ struct SRC_Instance_Record {
struct SelectInfo sel_info; struct SelectInfo sel_info;
/* Latest leap status */ /* Current stratum */
int stratum;
/* Current leap status */
NTP_Leap leap; NTP_Leap leap;
/* Flag indicating the source has a leap second vote */ /* Flag indicating the source has a leap second vote */
@@ -175,6 +178,11 @@ static double reselect_distance;
static double stratum_weight; static double stratum_weight;
static double combine_limit; static double combine_limit;
static LOG_FileID logfileid;
/* Identifier of the dump file */
#define DUMP_IDENTIFIER "SRC0\n"
/* ================================================== */ /* ================================================== */
/* Forward prototype */ /* Forward prototype */
@@ -183,6 +191,7 @@ static void slew_sources(struct timespec *raw, struct timespec *cooked, double d
double doffset, LCL_ChangeType change_type, void *anything); double doffset, LCL_ChangeType change_type, void *anything);
static void add_dispersion(double dispersion, void *anything); static void add_dispersion(double dispersion, void *anything);
static char *source_to_string(SRC_Instance inst); static char *source_to_string(SRC_Instance inst);
static char get_status_char(SRC_Status status);
/* ================================================== */ /* ================================================== */
/* Initialisation function */ /* Initialisation function */
@@ -202,6 +211,10 @@ void SRC_Initialise(void) {
LCL_AddParameterChangeHandler(slew_sources, NULL); LCL_AddParameterChangeHandler(slew_sources, NULL);
LCL_AddDispersionNotifyHandler(add_dispersion, NULL); LCL_AddDispersionNotifyHandler(add_dispersion, NULL);
logfileid = CNF_GetLogSelection() ? LOG_FileOpen("selection",
" Date (UTC) Time IP Address S EOpts Reach Score Last sample Low end High end")
: -1;
} }
/* ================================================== */ /* ================================================== */
@@ -313,6 +326,7 @@ SRC_ResetInstance(SRC_Instance instance)
instance->distant = 0; instance->distant = 0;
instance->status = SRC_BAD_STATS; instance->status = SRC_BAD_STATS;
instance->sel_score = 1.0; instance->sel_score = 1.0;
instance->stratum = 0;
instance->leap = LEAP_Unsynchronised; instance->leap = LEAP_Unsynchronised;
instance->leap_vote = 0; instance->leap_vote = 0;
@@ -371,8 +385,10 @@ get_leap_status(void)
/* ================================================== */ /* ================================================== */
void void
SRC_SetLeapStatus(SRC_Instance inst, NTP_Leap leap) SRC_UpdateStatus(SRC_Instance inst, int stratum, NTP_Leap leap)
{ {
inst->stratum = stratum;
if (REF_IsLeapSecondClose(NULL, 0.0)) if (REF_IsLeapSecondClose(NULL, 0.0))
return; return;
@@ -398,9 +414,9 @@ SRC_AccumulateSample(SRC_Instance inst, NTP_Sample *sample)
assert(initialised); assert(initialised);
DEBUG_LOG("src=%s ts=%s offset=%e delay=%e disp=%e stratum=%d", DEBUG_LOG("src=%s ts=%s offset=%e delay=%e disp=%e",
source_to_string(inst), UTI_TimespecToString(&sample->time), -sample->offset, source_to_string(inst), UTI_TimespecToString(&sample->time), -sample->offset,
sample->root_delay, sample->root_dispersion, sample->stratum); sample->root_delay, sample->root_dispersion);
if (REF_IsLeapSecondClose(&sample->time, sample->offset)) { if (REF_IsLeapSecondClose(&sample->time, sample->offset)) {
LOG(LOGS_INFO, "Dropping sample around leap second"); LOG(LOGS_INFO, "Dropping sample around leap second");
@@ -631,13 +647,33 @@ source_to_string(SRC_Instance inst)
static void static void
mark_source(SRC_Instance inst, SRC_Status status) mark_source(SRC_Instance inst, SRC_Status status)
{ {
struct timespec now;
inst->status = status; inst->status = status;
DEBUG_LOG("%s status=%d options=%x reach=%o/%d updates=%d distant=%d leap=%d vote=%d lo=%f hi=%f", DEBUG_LOG("%s status=%c options=%x reach=%o/%d updates=%d distant=%d leap=%d vote=%d lo=%f hi=%f",
source_to_string(inst), (int)inst->status, (unsigned int)inst->sel_options, source_to_string(inst), get_status_char(inst->status),
(unsigned int)inst->reachability, inst->reachability_size, inst->updates, (unsigned int)inst->sel_options, (unsigned int)inst->reachability,
inst->reachability_size, inst->updates,
inst->distant, (int)inst->leap, inst->leap_vote, inst->distant, (int)inst->leap, inst->leap_vote,
inst->sel_info.lo_limit, inst->sel_info.hi_limit); inst->sel_info.lo_limit, inst->sel_info.hi_limit);
if (logfileid == -1)
return;
SCH_GetLastEventTime(&now, NULL, NULL);
LOG_FileWrite(logfileid,
"%s %-15s %c -%c%c%c%c %4o %5.2f %10.3e %10.3e %10.3e",
UTI_TimeToLogForm(now.tv_sec), source_to_string(inst),
get_status_char(inst->status),
inst->sel_options & SRC_SELECT_NOSELECT ? 'N' : '-',
inst->sel_options & SRC_SELECT_PREFER ? 'P' : '-',
inst->sel_options & SRC_SELECT_TRUST ? 'T' : '-',
inst->sel_options & SRC_SELECT_REQUIRE ? 'R' : '-',
(unsigned int)inst->reachability, inst->sel_score,
inst->sel_info.last_sample_ago,
inst->sel_info.lo_limit, inst->sel_info.hi_limit);
} }
/* ================================================== */ /* ================================================== */
@@ -714,8 +750,8 @@ combine_sources(int n_sel_sources, struct timespec *ref_time, double *offset,
offset_weight = 1.0 / sources[index]->sel_info.root_distance; offset_weight = 1.0 / sources[index]->sel_info.root_distance;
frequency_weight = 1.0 / SQUARE(src_frequency_sd); frequency_weight = 1.0 / SQUARE(src_frequency_sd);
DEBUG_LOG("combining index=%d oweight=%e offset=%e osd=%e fweight=%e freq=%e fsd=%e skew=%e", DEBUG_LOG("combining %s oweight=%e offset=%e osd=%e fweight=%e freq=%e fsd=%e skew=%e",
index, offset_weight, src_offset, src_offset_sd, source_to_string(sources[index]), offset_weight, src_offset, src_offset_sd,
frequency_weight, src_frequency, src_frequency_sd, src_skew); frequency_weight, src_frequency, src_frequency_sd, src_skew);
sum_offset_weight += offset_weight; sum_offset_weight += offset_weight;
@@ -807,8 +843,14 @@ SRC_SelectSource(SRC_Instance updated_inst)
continue; continue;
} }
/* Ignore sources which are not synchronised */
if (sources[i]->leap == LEAP_Unsynchronised) {
mark_source(sources[i], SRC_UNSYNCHRONISED);
continue;
}
si = &sources[i]->sel_info; si = &sources[i]->sel_info;
SST_GetSelectionData(sources[i]->stats, &now, &si->stratum, SST_GetSelectionData(sources[i]->stats, &now,
&si->lo_limit, &si->hi_limit, &si->root_distance, &si->lo_limit, &si->hi_limit, &si->root_distance,
&si->std_dev, &first_sample_ago, &si->std_dev, &first_sample_ago,
&si->last_sample_ago, &si->select_ok); &si->last_sample_ago, &si->select_ok);
@@ -890,10 +932,10 @@ SRC_SelectSource(SRC_Instance updated_inst)
source can settle down to a state where only one server is serving its source can settle down to a state where only one server is serving its
local unsychronised time and others are synchronised to it. */ local unsychronised time and others are synchronised to it. */
if (si->stratum >= orphan_stratum && sources[i]->type == SRC_NTP) { if (sources[i]->stratum >= orphan_stratum && sources[i]->type == SRC_NTP) {
mark_source(sources[i], SRC_ORPHAN); mark_source(sources[i], SRC_ORPHAN);
if (si->stratum == orphan_stratum && sources[i]->reachability && if (sources[i]->stratum == orphan_stratum && sources[i]->reachability &&
(orphan_source == INVALID_SOURCE || (orphan_source == INVALID_SOURCE ||
sources[i]->ref_id < sources[orphan_source]->ref_id)) sources[i]->ref_id < sources[orphan_source]->ref_id))
orphan_source = i; orphan_source = i;
@@ -1131,10 +1173,10 @@ SRC_SelectSource(SRC_Instance updated_inst)
/* Find minimum stratum */ /* Find minimum stratum */
index = sel_sources[0]; index = sel_sources[0];
min_stratum = sources[index]->sel_info.stratum; min_stratum = sources[index]->stratum;
for (i = 1; i < n_sel_sources; i++) { for (i = 1; i < n_sel_sources; i++) {
index = sel_sources[i]; index = sel_sources[i];
stratum = sources[index]->sel_info.stratum; stratum = sources[index]->stratum;
if (stratum < min_stratum) if (stratum < min_stratum)
min_stratum = stratum; min_stratum = stratum;
} }
@@ -1147,7 +1189,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
if (selected_source_index != INVALID_SOURCE) if (selected_source_index != INVALID_SOURCE)
sel_src_distance = sources[selected_source_index]->sel_info.root_distance + sel_src_distance = sources[selected_source_index]->sel_info.root_distance +
(sources[selected_source_index]->sel_info.stratum - min_stratum) * stratum_weight; (sources[selected_source_index]->stratum - min_stratum) * stratum_weight;
for (i = 0; i < n_sources; i++) { for (i = 0; i < n_sources; i++) {
/* Reset score for non-selectable sources */ /* Reset score for non-selectable sources */
@@ -1159,7 +1201,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
} }
distance = sources[i]->sel_info.root_distance + distance = sources[i]->sel_info.root_distance +
(sources[i]->sel_info.stratum - min_stratum) * stratum_weight; (sources[i]->stratum - min_stratum) * stratum_weight;
if (sources[i]->type == SRC_NTP) if (sources[i]->type == SRC_NTP)
distance += reselect_distance; distance += reselect_distance;
@@ -1247,7 +1289,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
combined = combine_sources(n_sel_sources, &ref_time, &src_offset, &src_offset_sd, combined = combine_sources(n_sel_sources, &ref_time, &src_offset, &src_offset_sd,
&src_frequency, &src_frequency_sd, &src_skew); &src_frequency, &src_frequency_sd, &src_skew);
REF_SetReference(sources[selected_source_index]->sel_info.stratum, REF_SetReference(sources[selected_source_index]->stratum,
leap_status, combined, leap_status, combined,
sources[selected_source_index]->ref_id, sources[selected_source_index]->ref_id,
sources[selected_source_index]->ip_addr, sources[selected_source_index]->ip_addr,
@@ -1320,24 +1362,60 @@ add_dispersion(double dispersion, void *anything)
/* ================================================== */ /* ================================================== */
static static int
FILE *open_dumpfile(SRC_Instance inst, char mode) get_dumpfile(SRC_Instance inst, char *filename, size_t len)
{ {
char filename[64], *dumpdir; /* Use the IP address, or reference ID with reference clocks */
switch (inst->type) {
case SRC_NTP:
if (!UTI_IsIPReal(inst->ip_addr) ||
snprintf(filename, len, "%s", source_to_string(inst)) >= len)
return 0;
break;
case SRC_REFCLOCK:
if (snprintf(filename, len, "refid:%08"PRIx32, inst->ref_id) >= len)
return 0;
break;
default:
assert(0);
}
return 1;
}
/* ================================================== */
static void
save_source(SRC_Instance inst)
{
char filename[64], *dumpdir, *ntp_name;
FILE *f;
dumpdir = CNF_GetDumpDir(); dumpdir = CNF_GetDumpDir();
if (!dumpdir) if (!dumpdir)
return NULL; return;
/* Include IP address in the name for NTP sources, or reference ID in hex */ if (!get_dumpfile(inst, filename, sizeof (filename)))
if (inst->type == SRC_NTP && UTI_IsIPReal(inst->ip_addr)) return;
snprintf(filename, sizeof (filename), "%s", source_to_string(inst));
else if (inst->type == SRC_REFCLOCK)
snprintf(filename, sizeof (filename), "refid:%08"PRIx32, inst->ref_id);
else
return NULL;
return UTI_OpenFile(dumpdir, filename, ".dat", mode, 0644); f = UTI_OpenFile(dumpdir, filename, ".dat", 'w', 0644);
if (!f)
return;
ntp_name = inst->type == SRC_NTP ? NSR_GetName(inst->ip_addr) : ".";
if (fprintf(f, "%s%s\n%d %o %d %d %d\n",
DUMP_IDENTIFIER, ntp_name, inst->authenticated,
(unsigned int)inst->reachability, inst->reachability_size,
inst->stratum, (int)inst->leap) < 0 ||
!SST_SaveToFile(inst->stats, f)) {
fclose(f);
if (!UTI_RemoveFile(dumpdir, filename, ".dat"))
;
return;
}
fclose(f);
} }
/* ================================================== */ /* ================================================== */
@@ -1346,16 +1424,60 @@ FILE *open_dumpfile(SRC_Instance inst, char mode)
void void
SRC_DumpSources(void) SRC_DumpSources(void)
{ {
FILE *out;
int i; int i;
for (i = 0; i < n_sources; i++) { for (i = 0; i < n_sources; i++)
out = open_dumpfile(sources[i], 'w'); save_source(sources[i]);
if (!out) }
continue;
SST_SaveToFile(sources[i]->stats, out); /* ================================================== */
fclose(out);
#define MAX_WORDS 1
static void
load_source(SRC_Instance inst)
{
char filename[64], line[256], *dumpdir, *ntp_name, *words[MAX_WORDS];
int auth, leap, reach_size, stratum;
unsigned int reach;
FILE *f;
dumpdir = CNF_GetDumpDir();
if (!dumpdir)
return;
if (!get_dumpfile(inst, filename, sizeof (filename)))
return;
f = UTI_OpenFile(dumpdir, filename, ".dat", 'r', 0);
if (!f)
return;
ntp_name = inst->type == SRC_NTP ? NSR_GetName(inst->ip_addr) : NULL;
if (!fgets(line, sizeof (line), f) || strcmp(line, DUMP_IDENTIFIER) != 0 ||
!fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 1 ||
(inst->type == SRC_NTP && (!ntp_name || strcmp(words[0], ntp_name) != 0)) ||
!fgets(line, sizeof (line), f) ||
sscanf(words[0], "%d %o %d %d %d",
&auth, &reach, &reach_size, &stratum, &leap) != 5 ||
(!auth && inst->authenticated) ||
stratum < 0 || stratum >= NTP_MAX_STRATUM ||
leap < LEAP_Normal || leap >= LEAP_Unsynchronised ||
!SST_LoadFromFile(inst->stats, f)) {
LOG(LOGS_WARN, "Could not load dump file for %s", source_to_string(inst));
fclose(f);
return;
} }
inst->reachability = reach & ((1U << SOURCE_REACH_BITS) - 1);
inst->reachability_size = CLAMP(0, reach_size, SOURCE_REACH_BITS);
inst->stratum = stratum;
inst->leap = leap;
LOG(LOGS_INFO, "Loaded dump file for %s", source_to_string(inst));
fclose(f);
} }
/* ================================================== */ /* ================================================== */
@@ -1363,21 +1485,17 @@ SRC_DumpSources(void)
void void
SRC_ReloadSources(void) SRC_ReloadSources(void)
{ {
FILE *in;
int i; int i;
for (i = 0; i < n_sources; i++) { for (i = 0; i < n_sources; i++) {
in = open_dumpfile(sources[i], 'r'); load_source(sources[i]);
if (!in)
continue; /* Allow an immediate update of the reference */
if (!SST_LoadFromFile(sources[i]->stats, in)) sources[i]->updates++;
LOG(LOGS_WARN, "Could not load dump file for %s",
source_to_string(sources[i]));
else
LOG(LOGS_INFO, "Loaded dump file for %s",
source_to_string(sources[i]));
fclose(in);
} }
/* Select sources and set the reference */
SRC_SelectSource(NULL);
} }
/* ================================================== */ /* ================================================== */
@@ -1492,6 +1610,8 @@ SRC_ReportSource(int index, RPT_SourceReport *report, struct timespec *now)
report->ip_addr.family = IPADDR_INET4; report->ip_addr.family = IPADDR_INET4;
} }
report->stratum = src->stratum;
switch (src->status) { switch (src->status) {
case SRC_FALSETICKER: case SRC_FALSETICKER:
report->state = RPT_FALSETICKER; report->state = RPT_FALSETICKER;
@@ -1556,6 +1676,8 @@ get_status_char(SRC_Status status)
switch (status) { switch (status) {
case SRC_UNSELECTABLE: case SRC_UNSELECTABLE:
return 'N'; return 'N';
case SRC_UNSYNCHRONISED:
return 's';
case SRC_BAD_STATS: case SRC_BAD_STATS:
return 'M'; return 'M';
case SRC_BAD_DISTANCE: case SRC_BAD_DISTANCE:

View File

@@ -87,8 +87,8 @@ extern void SRC_SetRefid(SRC_Instance instance, uint32_t ref_id, IPAddr *addr);
/* Function to get access to the sourcestats instance */ /* Function to get access to the sourcestats instance */
extern SST_Stats SRC_GetSourcestats(SRC_Instance instance); extern SST_Stats SRC_GetSourcestats(SRC_Instance instance);
/* Function to set the current leap status according to the source */ /* Function to update the stratum and leap status of the source */
extern void SRC_SetLeapStatus(SRC_Instance instance, NTP_Leap leap); extern void SRC_UpdateStatus(SRC_Instance instance, int stratum, NTP_Leap leap);
/* Function to accumulate a new sample from the source */ /* Function to accumulate a new sample from the source */
extern void SRC_AccumulateSample(SRC_Instance instance, NTP_Sample *sample); extern void SRC_AccumulateSample(SRC_Instance instance, NTP_Sample *sample);

View File

@@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011-2014, 2016-2018 * Copyright (C) Miroslav Lichvar 2011-2014, 2016-2018, 2021
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of version 2 of the GNU General Public License as
@@ -177,9 +177,6 @@ struct SST_Stats_Record {
/* This array contains the root dispersions of each sample at the /* This array contains the root dispersions of each sample at the
time of the measurements */ time of the measurements */
double root_dispersions[MAX_SAMPLES]; double root_dispersions[MAX_SAMPLES];
/* The stratum from the last accumulated sample */
int stratum;
}; };
/* ================================================== */ /* ================================================== */
@@ -214,8 +211,8 @@ SST_CreateInstance(uint32_t refid, IPAddr *addr, int min_samples, int max_sample
SST_Stats inst; SST_Stats inst;
inst = MallocNew(struct SST_Stats_Record); inst = MallocNew(struct SST_Stats_Record);
inst->min_samples = min_samples; inst->max_samples = max_samples > 0 ? CLAMP(1, max_samples, MAX_SAMPLES) : MAX_SAMPLES;
inst->max_samples = max_samples; inst->min_samples = CLAMP(1, min_samples, inst->max_samples);
inst->fixed_min_delay = min_delay; inst->fixed_min_delay = min_delay;
inst->fixed_asymmetry = asymmetry; inst->fixed_asymmetry = asymmetry;
@@ -321,7 +318,6 @@ SST_AccumulateSample(SST_Stats inst, NTP_Sample *sample)
inst->peer_dispersions[m] = sample->peer_dispersion; inst->peer_dispersions[m] = sample->peer_dispersion;
inst->root_delays[m] = sample->root_delay; inst->root_delays[m] = sample->root_delay;
inst->root_dispersions[m] = sample->root_dispersion; inst->root_dispersions[m] = sample->root_dispersion;
inst->stratum = sample->stratum;
if (inst->peer_delays[n] < inst->fixed_min_delay) if (inst->peer_delays[n] < inst->fixed_min_delay)
inst->peer_delays[n] = 2.0 * inst->fixed_min_delay - inst->peer_delays[n]; inst->peer_delays[n] = 2.0 * inst->fixed_min_delay - inst->peer_delays[n];
@@ -650,7 +646,6 @@ SST_GetFrequencyRange(SST_Stats inst,
void void
SST_GetSelectionData(SST_Stats inst, struct timespec *now, SST_GetSelectionData(SST_Stats inst, struct timespec *now,
int *stratum,
double *offset_lo_limit, double *offset_lo_limit,
double *offset_hi_limit, double *offset_hi_limit,
double *root_distance, double *root_distance,
@@ -670,7 +665,6 @@ SST_GetSelectionData(SST_Stats inst, struct timespec *now,
i = get_runsbuf_index(inst, inst->best_single_sample); i = get_runsbuf_index(inst, inst->best_single_sample);
j = get_buf_index(inst, inst->best_single_sample); j = get_buf_index(inst, inst->best_single_sample);
*stratum = inst->stratum;
*std_dev = inst->std_dev; *std_dev = inst->std_dev;
sample_elapsed = fabs(UTI_DiffTimespecsToDouble(now, &inst->sample_times[i])); sample_elapsed = fabs(UTI_DiffTimespecsToDouble(now, &inst->sample_times[i]));
@@ -704,7 +698,8 @@ SST_GetSelectionData(SST_Stats inst, struct timespec *now,
/* If maxsamples is too small to have a successful regression, enable the /* If maxsamples is too small to have a successful regression, enable the
selection as a special case for a fast update/print-once reference mode */ selection as a special case for a fast update/print-once reference mode */
if (!*select_ok && inst->n_samples < 3 && inst->n_samples == inst->max_samples) { if (!*select_ok && inst->n_samples < MIN_SAMPLES_FOR_REGRESS &&
inst->n_samples == inst->max_samples) {
*std_dev = CNF_GetMaxJitter(); *std_dev = CNF_GetMaxJitter();
*select_ok = 1; *select_ok = 1;
} }
@@ -785,6 +780,22 @@ SST_SlewSamples(SST_Stats inst, struct timespec *when, double dfreq, double doff
/* ================================================== */ /* ================================================== */
void
SST_CorrectOffset(SST_Stats inst, double doffset)
{
int i;
if (!inst->n_samples)
return;
for (i = -inst->runs_samples; i < inst->n_samples; i++)
inst->offsets[get_runsbuf_index(inst, i)] += doffset;
inst->estimated_offset += doffset;
}
/* ================================================== */
void void
SST_AddDispersion(SST_Stats inst, double dispersion) SST_AddDispersion(SST_Stats inst, double dispersion)
{ {
@@ -804,7 +815,7 @@ SST_PredictOffset(SST_Stats inst, struct timespec *when)
{ {
double elapsed; double elapsed;
if (inst->n_samples < 3) { if (inst->n_samples < MIN_SAMPLES_FOR_REGRESS) {
/* We don't have any useful statistics, and presumably the poll /* We don't have any useful statistics, and presumably the poll
interval is minimal. We can't do any useful prediction other interval is minimal. We can't do any useful prediction other
than use the latest sample or zero if we don't have any samples */ than use the latest sample or zero if we don't have any samples */
@@ -858,38 +869,30 @@ SST_GetDelayTestData(SST_Stats inst, struct timespec *sample_time,
/* This is used to save the register to a file, so that we can reload /* This is used to save the register to a file, so that we can reload
it after restarting the daemon */ it after restarting the daemon */
void int
SST_SaveToFile(SST_Stats inst, FILE *out) SST_SaveToFile(SST_Stats inst, FILE *out)
{ {
int m, i, j; int m, i, j;
fprintf(out, "%d\n", inst->n_samples); if (inst->n_samples < 1)
return 0;
if (fprintf(out, "%d %d\n", inst->n_samples, inst->asymmetry_run) < 0)
return 0;
for(m = 0; m < inst->n_samples; m++) { for(m = 0; m < inst->n_samples; m++) {
i = get_runsbuf_index(inst, m); i = get_runsbuf_index(inst, m);
j = get_buf_index(inst, m); j = get_buf_index(inst, m);
fprintf(out, if (fprintf(out, "%s %.6e %.6e %.6e %.6e %.6e %.6e\n",
#ifdef HAVE_LONG_TIME_T UTI_TimespecToString(&inst->sample_times[i]),
"%08"PRIx64" %08lx %.6e %.6e %.6e %.6e %.6e %.6e %.6e %d\n", inst->offsets[i], inst->orig_offsets[j],
(uint64_t)inst->sample_times[i].tv_sec, inst->peer_delays[i], inst->peer_dispersions[j],
#else inst->root_delays[j], inst->root_dispersions[j]) < 0)
"%08lx %08lx %.6e %.6e %.6e %.6e %.6e %.6e %.6e %d\n", return 0;
(unsigned long)inst->sample_times[i].tv_sec,
#endif
(unsigned long)inst->sample_times[i].tv_nsec / 1000,
inst->offsets[i],
inst->orig_offsets[j],
inst->peer_delays[i],
inst->peer_dispersions[j],
inst->root_delays[j],
inst->root_dispersions[j],
1.0, /* used to be inst->weights[i] */
inst->stratum /* used to be an array */);
} }
fprintf(out, "%d\n", inst->asymmetry_run); return 1;
} }
/* ================================================== */ /* ================================================== */
@@ -898,65 +901,47 @@ SST_SaveToFile(SST_Stats inst, FILE *out)
int int
SST_LoadFromFile(SST_Stats inst, FILE *in) SST_LoadFromFile(SST_Stats inst, FILE *in)
{ {
#ifdef HAVE_LONG_TIME_T int i, n_samples, arun;
uint64_t sec; struct timespec now;
#else double sample_time;
unsigned long sec; char line[256];
#endif
unsigned long usec; if (!fgets(line, sizeof (line), in) ||
int i; sscanf(line, "%d %d", &n_samples, &arun) != 2 ||
char line[1024]; n_samples < 1 || n_samples > MAX_SAMPLES)
double weight; return 0;
SST_ResetInstance(inst); SST_ResetInstance(inst);
if (fgets(line, sizeof(line), in) && LCL_ReadCookedTime(&now, NULL);
sscanf(line, "%d", &inst->n_samples) == 1 &&
inst->n_samples >= 0 && inst->n_samples <= MAX_SAMPLES) {
for (i=0; i<inst->n_samples; i++) { for (i = 0; i < n_samples; i++) {
if (!fgets(line, sizeof(line), in) || if (!fgets(line, sizeof (line), in) ||
(sscanf(line, sscanf(line, "%lf %lf %lf %lf %lf %lf %lf",
#ifdef HAVE_LONG_TIME_T &sample_time, &inst->offsets[i], &inst->orig_offsets[i],
"%"SCNx64"%lx%lf%lf%lf%lf%lf%lf%lf%d\n", &inst->peer_delays[i], &inst->peer_dispersions[i],
#else &inst->root_delays[i], &inst->root_dispersions[i]) != 7)
"%lx%lx%lf%lf%lf%lf%lf%lf%lf%d\n", return 0;
#endif
&(sec), &(usec),
&(inst->offsets[i]),
&(inst->orig_offsets[i]),
&(inst->peer_delays[i]),
&(inst->peer_dispersions[i]),
&(inst->root_delays[i]),
&(inst->root_dispersions[i]),
&weight, /* not used anymore */
&inst->stratum) != 10)) {
/* This is the branch taken if the read FAILED */ if (!UTI_IsTimeOffsetSane(&now, sample_time - UTI_TimespecToDouble(&now)))
return 0;
inst->n_samples = 0; /* Load abandoned if any sign of corruption */ /* Some resolution is lost in the double format, but that's ok */
return 0; UTI_DoubleToTimespec(sample_time, &inst->sample_times[i]);
} else {
/* This is the branch taken if the read is SUCCESSFUL */ /* Make sure the samples are sane and they are in order */
inst->sample_times[i].tv_sec = sec; if (!UTI_IsTimeOffsetSane(&inst->sample_times[i], -inst->offsets[i]) ||
inst->sample_times[i].tv_nsec = 1000 * usec; UTI_CompareTimespecs(&now, &inst->sample_times[i]) < 0 ||
UTI_NormaliseTimespec(&inst->sample_times[i]); !(fabs(inst->peer_delays[i]) < 1.0e6 && fabs(inst->peer_dispersions[i]) < 1.0e6 &&
} fabs(inst->root_delays[i]) < 1.0e6 && fabs(inst->root_dispersions[i]) < 1.0e6) ||
} (i > 0 && UTI_CompareTimespecs(&inst->sample_times[i],
&inst->sample_times[i - 1]) <= 0))
/* This field was not saved in older versions */ return 0;
if (!fgets(line, sizeof(line), in) || sscanf(line, "%d\n", &inst->asymmetry_run) != 1)
inst->asymmetry_run = 0;
} else {
inst->n_samples = 0; /* Load abandoned if any sign of corruption */
return 0;
} }
if (!inst->n_samples) inst->n_samples = n_samples;
return 1;
inst->last_sample = inst->n_samples - 1; inst->last_sample = inst->n_samples - 1;
inst->asymmetry_run = CLAMP(-MAX_ASYMMETRY_RUN, arun, MAX_ASYMMETRY_RUN);
find_min_delay_sample(inst); find_min_delay_sample(inst);
SST_DoNewRegression(inst); SST_DoNewRegression(inst);
@@ -978,7 +963,6 @@ SST_DoSourceReport(SST_Stats inst, RPT_SourceReport *report, struct timespec *no
report->orig_latest_meas = inst->orig_offsets[j]; report->orig_latest_meas = inst->orig_offsets[j];
report->latest_meas = inst->offsets[i]; report->latest_meas = inst->offsets[i];
report->latest_meas_err = 0.5*inst->root_delays[j] + inst->root_dispersions[j]; report->latest_meas_err = 0.5*inst->root_delays[j] + inst->root_dispersions[j];
report->stratum = inst->stratum;
/* Align the sample time to reduce the leak of the receive timestamp */ /* Align the sample time to reduce the leak of the receive timestamp */
last_sample_time = inst->sample_times[i]; last_sample_time = inst->sample_times[i];
@@ -1003,6 +987,14 @@ SST_Samples(SST_Stats inst)
/* ================================================== */ /* ================================================== */
int
SST_GetMinSamples(SST_Stats inst)
{
return inst->min_samples;
}
/* ================================================== */
void void
SST_DoSourcestatsReport(SST_Stats inst, RPT_SourcestatsReport *report, struct timespec *now) SST_DoSourcestatsReport(SST_Stats inst, RPT_SourcestatsReport *report, struct timespec *now)
{ {

View File

@@ -69,7 +69,6 @@ extern void SST_GetFrequencyRange(SST_Stats inst, double *lo, double *hi);
/* Get data needed for selection */ /* Get data needed for selection */
extern void extern void
SST_GetSelectionData(SST_Stats inst, struct timespec *now, SST_GetSelectionData(SST_Stats inst, struct timespec *now,
int *stratum,
double *offset_lo_limit, double *offset_lo_limit,
double *offset_hi_limit, double *offset_hi_limit,
double *root_distance, double *root_distance,
@@ -103,6 +102,10 @@ SST_GetTrackingData(SST_Stats inst, struct timespec *ref_time,
extern void SST_SlewSamples(SST_Stats inst, struct timespec *when, double dfreq, double doffset); extern void SST_SlewSamples(SST_Stats inst, struct timespec *when, double dfreq, double doffset);
/* This routine corrects already accumulated samples to improve the
frequency estimate when a new sample is accumulated */
extern void SST_CorrectOffset(SST_Stats inst, double doffset);
/* This routine is called when an indeterminate offset is introduced /* This routine is called when an indeterminate offset is introduced
into the local time. */ into the local time. */
extern void SST_AddDispersion(SST_Stats inst, double dispersion); extern void SST_AddDispersion(SST_Stats inst, double dispersion);
@@ -120,7 +123,7 @@ extern int SST_GetDelayTestData(SST_Stats inst, struct timespec *sample_time,
double *last_sample_ago, double *predicted_offset, double *last_sample_ago, double *predicted_offset,
double *min_delay, double *skew, double *std_dev); double *min_delay, double *skew, double *std_dev);
extern void SST_SaveToFile(SST_Stats inst, FILE *out); extern int SST_SaveToFile(SST_Stats inst, FILE *out);
extern int SST_LoadFromFile(SST_Stats inst, FILE *in); extern int SST_LoadFromFile(SST_Stats inst, FILE *in);
@@ -130,6 +133,8 @@ extern void SST_DoSourcestatsReport(SST_Stats inst, RPT_SourcestatsReport *repor
extern int SST_Samples(SST_Stats inst); extern int SST_Samples(SST_Stats inst);
extern int SST_GetMinSamples(SST_Stats inst);
extern double SST_GetJitterAsymmetry(SST_Stats inst); extern double SST_GetJitterAsymmetry(SST_Stats inst);
#endif /* GOT_SOURCESTATS_H */ #endif /* GOT_SOURCESTATS_H */

View File

@@ -54,10 +54,14 @@ typedef struct {
int sel_options; int sel_options;
int nts; int nts;
int nts_port; int nts_port;
int copy;
int ext_fields;
uint32_t authkey; uint32_t authkey;
uint32_t cert_set;
double max_delay; double max_delay;
double max_delay_ratio; double max_delay_ratio;
double max_delay_dev_ratio; double max_delay_dev_ratio;
double max_delay_quant;
double min_delay; double min_delay;
double asymmetry; double asymmetry;
double offset; double offset;
@@ -77,6 +81,7 @@ typedef struct {
#define SRC_DEFAULT_MAXSAMPLES (-1) #define SRC_DEFAULT_MAXSAMPLES (-1)
#define SRC_DEFAULT_ASYMMETRY 1.0 #define SRC_DEFAULT_ASYMMETRY 1.0
#define SRC_DEFAULT_NTSPORT 4460 #define SRC_DEFAULT_NTSPORT 4460
#define SRC_DEFAULT_CERTSET 0
#define INACTIVE_AUTHKEY 0 #define INACTIVE_AUTHKEY 0
/* Flags for source selection */ /* Flags for source selection */

View File

@@ -491,7 +491,8 @@ NNS_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *req_info,
} }
NNC_Instance NNC_Instance
NNC_CreateInstance(IPSockAddr *nts_address, const char *name, const IPSockAddr *ntp_address) NNC_CreateInstance(IPSockAddr *nts_address, const char *name, uint32_t cert_set,
uint16_t ntp_port)
{ {
return NULL; return NULL;
} }

View File

@@ -73,13 +73,28 @@ static double slew_freq;
/* Time (raw) of last update of slewing frequency and offset */ /* Time (raw) of last update of slewing frequency and offset */
static struct timespec slew_start; static struct timespec slew_start;
/* Limits for the slew timeout */ /* Limits for the slew length */
#define MIN_SLEW_TIMEOUT 1.0 #define MIN_SLEW_DURATION 1.0
#define MAX_SLEW_TIMEOUT 1.0e4 #define MAX_SLEW_DURATION 1.0e4
/* Scheduler timeout ID for ending of the currently running slew */ /* Scheduler timeout ID for ending of the currently running slew */
static SCH_TimeoutID slew_timeout_id; static SCH_TimeoutID slew_timeout_id;
/* Scheduled duration of the currently running slew */
static double slew_duration;
/* Expected delay in ending of the slew due to process scheduling and
execution time, tracked as a decaying maximum value */
static double slew_excess_duration;
/* Maximum accepted excess duration to ignore large jumps after resuming
suspended system and other reasons (which should be handled in the
scheduler), a constant to determine the minimum slew duration to avoid
oscillations due to the excess, and the decay constant */
#define MAX_SLEW_EXCESS_DURATION 100.0
#define MIN_SLEW_DURATION_EXCESS_RATIO 5.0
#define SLEW_EXCESS_DURATION_DECAY 0.9
/* Suggested offset correction rate (correction time * offset) */ /* Suggested offset correction rate (correction time * offset) */
static double correction_rate; static double correction_rate;
@@ -109,12 +124,7 @@ static void
handle_step(struct timespec *raw, struct timespec *cooked, double dfreq, handle_step(struct timespec *raw, struct timespec *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything) double doffset, LCL_ChangeType change_type, void *anything)
{ {
if (change_type == LCL_ChangeUnknownStep) { if (change_type == LCL_ChangeStep) {
/* Reset offset and slewing */
slew_start = *raw;
offset_register = 0.0;
update_slew();
} else if (change_type == LCL_ChangeStep) {
UTI_AddDoubleToTimespec(&slew_start, -doffset, &slew_start); UTI_AddDoubleToTimespec(&slew_start, -doffset, &slew_start);
} }
} }
@@ -169,8 +179,8 @@ clamp_freq(double freq)
static void static void
update_slew(void) update_slew(void)
{ {
double old_slew_freq, total_freq, corr_freq, duration, excess_duration;
struct timespec now, end_of_slew; struct timespec now, end_of_slew;
double old_slew_freq, total_freq, corr_freq, duration;
/* Remove currently running timeout */ /* Remove currently running timeout */
SCH_RemoveTimeout(slew_timeout_id); SCH_RemoveTimeout(slew_timeout_id);
@@ -183,13 +193,25 @@ update_slew(void)
stop_fastslew(&now); stop_fastslew(&now);
/* Estimate how long should the next slew take */ /* Update the maximum excess duration, decaying even when the slew did
not time out (i.e. frequency was set or offset accrued), but add a small
value to avoid denormals */
slew_excess_duration = (slew_excess_duration + 1.0e-9) * SLEW_EXCESS_DURATION_DECAY;
excess_duration = duration - slew_duration;
if (slew_excess_duration < excess_duration &&
excess_duration <= MAX_SLEW_EXCESS_DURATION)
slew_excess_duration = excess_duration;
/* Calculate the duration of the new slew, considering the current correction
rate and previous delays in stopping of the slew */
if (fabs(offset_register) < MIN_OFFSET_CORRECTION) { if (fabs(offset_register) < MIN_OFFSET_CORRECTION) {
duration = MAX_SLEW_TIMEOUT; duration = MAX_SLEW_DURATION;
} else { } else {
duration = correction_rate / fabs(offset_register); duration = correction_rate / fabs(offset_register);
if (duration < MIN_SLEW_TIMEOUT) if (duration < MIN_SLEW_DURATION)
duration = MIN_SLEW_TIMEOUT; duration = MIN_SLEW_DURATION;
if (duration < MIN_SLEW_DURATION_EXCESS_RATIO * slew_excess_duration)
duration = MIN_SLEW_DURATION_EXCESS_RATIO * slew_excess_duration;
} }
/* Get frequency offset needed to slew the offset in the duration /* Get frequency offset needed to slew the offset in the duration
@@ -232,23 +254,25 @@ update_slew(void)
maximum timeout and try again on the next update. */ maximum timeout and try again on the next update. */
if (fabs(offset_register) < MIN_OFFSET_CORRECTION || if (fabs(offset_register) < MIN_OFFSET_CORRECTION ||
offset_register * slew_freq <= 0.0) { offset_register * slew_freq <= 0.0) {
duration = MAX_SLEW_TIMEOUT; duration = MAX_SLEW_DURATION;
} else { } else {
duration = offset_register / slew_freq; duration = offset_register / slew_freq;
if (duration < MIN_SLEW_TIMEOUT) if (duration < MIN_SLEW_DURATION)
duration = MIN_SLEW_TIMEOUT; duration = MIN_SLEW_DURATION;
else if (duration > MAX_SLEW_TIMEOUT) else if (duration > MAX_SLEW_DURATION)
duration = MAX_SLEW_TIMEOUT; duration = MAX_SLEW_DURATION;
} }
/* Restart timer for the next update */ /* Restart timer for the next update */
UTI_AddDoubleToTimespec(&now, duration, &end_of_slew); UTI_AddDoubleToTimespec(&now, duration, &end_of_slew);
slew_timeout_id = SCH_AddTimeout(&end_of_slew, handle_end_of_slew, NULL); slew_timeout_id = SCH_AddTimeout(&end_of_slew, handle_end_of_slew, NULL);
slew_start = now; slew_start = now;
slew_duration = duration;
DEBUG_LOG("slew offset=%e corr_rate=%e base_freq=%f total_freq=%f slew_freq=%e duration=%f slew_error=%e", DEBUG_LOG("slew offset=%e corr_rate=%e base_freq=%f total_freq=%f slew_freq=%e"
offset_register, correction_rate, base_freq, total_freq, slew_freq, " duration=%f excess=%f slew_error=%e",
duration, slew_error); offset_register, correction_rate, base_freq, total_freq, slew_freq,
slew_duration, slew_excess_duration, slew_error);
} }
/* ================================================== */ /* ================================================== */
@@ -385,6 +409,7 @@ SYS_Generic_CompleteFreqDriver(double max_set_freq_ppm, double max_set_freq_dela
base_freq = (*drv_read_freq)(); base_freq = (*drv_read_freq)();
slew_freq = 0.0; slew_freq = 0.0;
offset_register = 0.0; offset_register = 0.0;
slew_excess_duration = 0.0;
max_corr_freq = CNF_GetMaxSlewRate() / 1.0e6; max_corr_freq = CNF_GetMaxSlewRate() / 1.0e6;

View File

@@ -97,21 +97,6 @@ static int have_setoffset;
updated in the kernel */ updated in the kernel */
static int tick_update_hz; static int tick_update_hz;
/* ================================================== */
inline static long
our_round(double x)
{
long y;
if (x > 0.0)
y = x + 0.5;
else
y = x - 0.5;
return y;
}
/* ================================================== */ /* ================================================== */
/* Positive means currently fast of true time, i.e. jump backwards */ /* Positive means currently fast of true time, i.e. jump backwards */
@@ -149,7 +134,7 @@ set_frequency(double freq_ppm)
double required_freq; double required_freq;
int required_delta_tick; int required_delta_tick;
required_delta_tick = our_round(freq_ppm / dhz); required_delta_tick = round(freq_ppm / dhz);
/* Older kernels (pre-2.6.18) don't apply the frequency offset exactly as /* Older kernels (pre-2.6.18) don't apply the frequency offset exactly as
set by adjtimex() and a scaling constant (that depends on the internal set by adjtimex() and a scaling constant (that depends on the internal
@@ -486,7 +471,7 @@ void check_seccomp_applicability(void)
void void
SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context) SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context)
{ {
const int syscalls[] = { const int allowed[] = {
/* Clock */ /* Clock */
SCMP_SYS(adjtimex), SCMP_SYS(adjtimex),
SCMP_SYS(clock_adjtime), SCMP_SYS(clock_adjtime),
@@ -503,11 +488,18 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context)
/* Process */ /* Process */
SCMP_SYS(clone), SCMP_SYS(clone),
#ifdef __NR_clone3
SCMP_SYS(clone3),
#endif
SCMP_SYS(exit), SCMP_SYS(exit),
SCMP_SYS(exit_group), SCMP_SYS(exit_group),
SCMP_SYS(getpid), SCMP_SYS(getpid),
SCMP_SYS(getrlimit), SCMP_SYS(getrlimit),
SCMP_SYS(getuid), SCMP_SYS(getuid),
SCMP_SYS(getuid32),
#ifdef __NR_rseq
SCMP_SYS(rseq),
#endif
SCMP_SYS(rt_sigaction), SCMP_SYS(rt_sigaction),
SCMP_SYS(rt_sigreturn), SCMP_SYS(rt_sigreturn),
SCMP_SYS(rt_sigprocmask), SCMP_SYS(rt_sigprocmask),
@@ -537,6 +529,7 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context)
SCMP_SYS(fchownat), SCMP_SYS(fchownat),
SCMP_SYS(fstat), SCMP_SYS(fstat),
SCMP_SYS(fstat64), SCMP_SYS(fstat64),
SCMP_SYS(fstatat64),
SCMP_SYS(getdents), SCMP_SYS(getdents),
SCMP_SYS(getdents64), SCMP_SYS(getdents64),
SCMP_SYS(lseek), SCMP_SYS(lseek),
@@ -547,11 +540,16 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context)
SCMP_SYS(readlinkat), SCMP_SYS(readlinkat),
SCMP_SYS(rename), SCMP_SYS(rename),
SCMP_SYS(renameat), SCMP_SYS(renameat),
#ifdef __NR_renameat2
SCMP_SYS(renameat2), SCMP_SYS(renameat2),
#endif
SCMP_SYS(stat), SCMP_SYS(stat),
SCMP_SYS(stat64), SCMP_SYS(stat64),
SCMP_SYS(statfs), SCMP_SYS(statfs),
SCMP_SYS(statfs64), SCMP_SYS(statfs64),
#ifdef __NR_statx
SCMP_SYS(statx),
#endif
SCMP_SYS(unlink), SCMP_SYS(unlink),
SCMP_SYS(unlinkat), SCMP_SYS(unlinkat),
@@ -588,6 +586,7 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context)
#ifdef __NR_ppoll_time64 #ifdef __NR_ppoll_time64
SCMP_SYS(ppoll_time64), SCMP_SYS(ppoll_time64),
#endif #endif
SCMP_SYS(pread64),
SCMP_SYS(pselect6), SCMP_SYS(pselect6),
#ifdef __NR_pselect6_time64 #ifdef __NR_pselect6_time64
SCMP_SYS(pselect6_time64), SCMP_SYS(pselect6_time64),
@@ -607,6 +606,22 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context)
SCMP_SYS(uname), SCMP_SYS(uname),
}; };
const int denied_any[] = {
SCMP_SYS(execve),
#ifdef __NR_execveat
SCMP_SYS(execveat),
#endif
SCMP_SYS(fork),
SCMP_SYS(ptrace),
SCMP_SYS(vfork),
};
const int denied_ntske[] = {
SCMP_SYS(ioctl),
SCMP_SYS(setsockopt),
SCMP_SYS(socket),
};
const int socket_domains[] = { const int socket_domains[] = {
AF_NETLINK, AF_UNIX, AF_INET, AF_NETLINK, AF_UNIX, AF_INET,
#ifdef FEAT_IPV6 #ifdef FEAT_IPV6
@@ -615,9 +630,12 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context)
}; };
const static int socket_options[][2] = { const static int socket_options[][2] = {
{ SOL_IP, IP_PKTINFO }, { SOL_IP, IP_FREEBIND }, { SOL_IP, IP_PKTINFO }, { SOL_IP, IP_FREEBIND }, { SOL_IP, IP_TOS },
#ifdef FEAT_IPV6 #ifdef FEAT_IPV6
{ SOL_IPV6, IPV6_V6ONLY }, { SOL_IPV6, IPV6_RECVPKTINFO }, { SOL_IPV6, IPV6_V6ONLY }, { SOL_IPV6, IPV6_RECVPKTINFO },
#endif
#ifdef SO_BINDTODEVICE
{ SOL_SOCKET, SO_BINDTODEVICE },
#endif #endif
{ SOL_SOCKET, SO_BROADCAST }, { SOL_SOCKET, SO_REUSEADDR }, { SOL_SOCKET, SO_BROADCAST }, { SOL_SOCKET, SO_REUSEADDR },
#ifdef SO_REUSEPORT #ifdef SO_REUSEPORT
@@ -656,31 +674,65 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context)
#endif #endif
}; };
unsigned int default_action, deny_action;
scmp_filter_ctx *ctx; scmp_filter_ctx *ctx;
int i; int i;
/* Sign of the level determines the deny action (kill or SIGSYS).
At level 1, selected syscalls are allowed, others are denied.
At level 2, selected syscalls are denied, others are allowed. */
deny_action = level > 0 ? SCMP_ACT_KILL : SCMP_ACT_TRAP;
if (level < 0)
level = -level;
switch (level) {
case 1:
default_action = deny_action;
break;
case 2:
default_action = SCMP_ACT_ALLOW;
break;
default:
LOG_FATAL("Unsupported filter level");
}
if (context == SYS_MAIN_PROCESS) { if (context == SYS_MAIN_PROCESS) {
/* Check if the chronyd configuration is supported */ /* Check if the chronyd configuration is supported */
check_seccomp_applicability(); check_seccomp_applicability();
/* Start the helper process, which will run without any seccomp filter. It /* At level 1, start a helper process which will not have a seccomp filter.
will be used for getaddrinfo(), for which it's difficult to maintain a It will be used for getaddrinfo(), for which it is difficult to maintain
list of required system calls (with glibc it depends on what NSS modules a list of required system calls (with glibc it depends on what NSS
are installed and enabled on the system). */ modules are installed and enabled on the system). */
PRV_StartHelper(); if (default_action != SCMP_ACT_ALLOW)
PRV_StartHelper();
} }
ctx = seccomp_init(level > 0 ? SCMP_ACT_KILL : SCMP_ACT_TRAP); ctx = seccomp_init(default_action);
if (ctx == NULL) if (ctx == NULL)
LOG_FATAL("Failed to initialize seccomp"); LOG_FATAL("Failed to initialize seccomp");
/* Add system calls that are always allowed */ if (default_action != SCMP_ACT_ALLOW) {
for (i = 0; i < (sizeof (syscalls) / sizeof (*syscalls)); i++) { for (i = 0; i < sizeof (allowed) / sizeof (*allowed); i++) {
if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, syscalls[i], 0) < 0) if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, allowed[i], 0) < 0)
goto add_failed; goto add_failed;
}
} else {
for (i = 0; i < sizeof (denied_any) / sizeof (*denied_any); i++) {
if (seccomp_rule_add(ctx, deny_action, denied_any[i], 0) < 0)
goto add_failed;
}
if (context == SYS_NTSKE_HELPER) {
for (i = 0; i < sizeof (denied_ntske) / sizeof (*denied_ntske); i++) {
if (seccomp_rule_add(ctx, deny_action, denied_ntske[i], 0) < 0)
goto add_failed;
}
}
} }
if (context == SYS_MAIN_PROCESS) { if (default_action != SCMP_ACT_ALLOW && context == SYS_MAIN_PROCESS) {
/* Allow opening sockets in selected domains */ /* Allow opening sockets in selected domains */
for (i = 0; i < sizeof (socket_domains) / sizeof (*socket_domains); i++) { for (i = 0; i < sizeof (socket_domains) / sizeof (*socket_domains); i++) {
if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 1, if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 1,
@@ -690,10 +742,9 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context)
/* Allow selected socket options */ /* Allow selected socket options */
for (i = 0; i < sizeof (socket_options) / sizeof (*socket_options); i++) { for (i = 0; i < sizeof (socket_options) / sizeof (*socket_options); i++) {
if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt), 3, if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt), 2,
SCMP_A1(SCMP_CMP_EQ, socket_options[i][0]), SCMP_A1(SCMP_CMP_EQ, socket_options[i][0]),
SCMP_A2(SCMP_CMP_EQ, socket_options[i][1]), SCMP_A2(SCMP_CMP_EQ, socket_options[i][1])))
SCMP_A4(SCMP_CMP_LE, sizeof (int))) < 0)
goto add_failed; goto add_failed;
} }
@@ -717,7 +768,8 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context)
if (seccomp_load(ctx) < 0) if (seccomp_load(ctx) < 0)
LOG_FATAL("Failed to load seccomp rules"); LOG_FATAL("Failed to load seccomp rules");
LOG(context == SYS_MAIN_PROCESS ? LOGS_INFO : LOGS_DEBUG, "Loaded seccomp filter"); LOG(context == SYS_MAIN_PROCESS ? LOGS_INFO : LOGS_DEBUG,
"Loaded seccomp filter (level %d)", level);
seccomp_release(ctx); seccomp_release(ctx);
return; return;
@@ -742,73 +794,25 @@ SYS_Linux_CheckKernelVersion(int req_major, int req_minor)
#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING) #if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
#define PHC_READINGS 10
static int static int
process_phc_readings(struct timespec ts[][3], int n, double precision, get_phc_readings(int phc_fd, int max_samples, struct timespec ts[][3])
struct timespec *phc_ts, struct timespec *sys_ts, double *err)
{ {
double min_delay = 0.0, delays[PTP_MAX_SAMPLES], phc_sum, sys_sum, sys_prec;
int i, combined;
if (n > PTP_MAX_SAMPLES)
return 0;
for (i = 0; i < n; i++) {
delays[i] = UTI_DiffTimespecsToDouble(&ts[i][2], &ts[i][0]);
if (delays[i] < 0.0) {
/* Step in the middle of a PHC reading? */
DEBUG_LOG("Bad PTP_SYS_OFFSET sample delay=%e", delays[i]);
return 0;
}
if (!i || delays[i] < min_delay)
min_delay = delays[i];
}
sys_prec = LCL_GetSysPrecisionAsQuantum();
/* Combine best readings */
for (i = combined = 0, phc_sum = sys_sum = 0.0; i < n; i++) {
if (delays[i] > min_delay + MAX(sys_prec, precision))
continue;
phc_sum += UTI_DiffTimespecsToDouble(&ts[i][1], &ts[0][1]);
sys_sum += UTI_DiffTimespecsToDouble(&ts[i][0], &ts[0][0]) + delays[i] / 2.0;
combined++;
}
assert(combined);
UTI_AddDoubleToTimespec(&ts[0][1], phc_sum / combined, phc_ts);
UTI_AddDoubleToTimespec(&ts[0][0], sys_sum / combined, sys_ts);
*err = MAX(min_delay / 2.0, precision);
return 1;
}
/* ================================================== */
static int
get_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
struct timespec *sys_ts, double *err)
{
struct timespec ts[PHC_READINGS][3];
struct ptp_sys_offset sys_off; struct ptp_sys_offset sys_off;
int i; int i;
max_samples = CLAMP(0, max_samples, PTP_MAX_SAMPLES);
/* Silence valgrind */ /* Silence valgrind */
memset(&sys_off, 0, sizeof (sys_off)); memset(&sys_off, 0, sizeof (sys_off));
sys_off.n_samples = PHC_READINGS; sys_off.n_samples = max_samples;
if (ioctl(phc_fd, PTP_SYS_OFFSET, &sys_off)) { if (ioctl(phc_fd, PTP_SYS_OFFSET, &sys_off)) {
DEBUG_LOG("ioctl(%s) failed : %s", "PTP_SYS_OFFSET", strerror(errno)); DEBUG_LOG("ioctl(%s) failed : %s", "PTP_SYS_OFFSET", strerror(errno));
return 0; return 0;
} }
for (i = 0; i < PHC_READINGS; i++) { for (i = 0; i < max_samples; i++) {
ts[i][0].tv_sec = sys_off.ts[i * 2].sec; ts[i][0].tv_sec = sys_off.ts[i * 2].sec;
ts[i][0].tv_nsec = sys_off.ts[i * 2].nsec; ts[i][0].tv_nsec = sys_off.ts[i * 2].nsec;
ts[i][1].tv_sec = sys_off.ts[i * 2 + 1].sec; ts[i][1].tv_sec = sys_off.ts[i * 2 + 1].sec;
@@ -817,31 +821,31 @@ get_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
ts[i][2].tv_nsec = sys_off.ts[i * 2 + 2].nsec; ts[i][2].tv_nsec = sys_off.ts[i * 2 + 2].nsec;
} }
return process_phc_readings(ts, PHC_READINGS, precision, phc_ts, sys_ts, err); return max_samples;
} }
/* ================================================== */ /* ================================================== */
static int static int
get_extended_phc_sample(int phc_fd, double precision, struct timespec *phc_ts, get_extended_phc_readings(int phc_fd, int max_samples, struct timespec ts[][3])
struct timespec *sys_ts, double *err)
{ {
#ifdef PTP_SYS_OFFSET_EXTENDED #ifdef PTP_SYS_OFFSET_EXTENDED
struct timespec ts[PHC_READINGS][3];
struct ptp_sys_offset_extended sys_off; struct ptp_sys_offset_extended sys_off;
int i; int i;
max_samples = CLAMP(0, max_samples, PTP_MAX_SAMPLES);
/* Silence valgrind */ /* Silence valgrind */
memset(&sys_off, 0, sizeof (sys_off)); memset(&sys_off, 0, sizeof (sys_off));
sys_off.n_samples = PHC_READINGS; sys_off.n_samples = max_samples;
if (ioctl(phc_fd, PTP_SYS_OFFSET_EXTENDED, &sys_off)) { if (ioctl(phc_fd, PTP_SYS_OFFSET_EXTENDED, &sys_off)) {
DEBUG_LOG("ioctl(%s) failed : %s", "PTP_SYS_OFFSET_EXTENDED", strerror(errno)); DEBUG_LOG("ioctl(%s) failed : %s", "PTP_SYS_OFFSET_EXTENDED", strerror(errno));
return 0; return 0;
} }
for (i = 0; i < PHC_READINGS; i++) { for (i = 0; i < max_samples; i++) {
ts[i][0].tv_sec = sys_off.ts[i][0].sec; ts[i][0].tv_sec = sys_off.ts[i][0].sec;
ts[i][0].tv_nsec = sys_off.ts[i][0].nsec; ts[i][0].tv_nsec = sys_off.ts[i][0].nsec;
ts[i][1].tv_sec = sys_off.ts[i][1].sec; ts[i][1].tv_sec = sys_off.ts[i][1].sec;
@@ -850,7 +854,7 @@ get_extended_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
ts[i][2].tv_nsec = sys_off.ts[i][2].nsec; ts[i][2].tv_nsec = sys_off.ts[i][2].nsec;
} }
return process_phc_readings(ts, PHC_READINGS, precision, phc_ts, sys_ts, err); return max_samples;
#else #else
return 0; return 0;
#endif #endif
@@ -859,12 +863,14 @@ get_extended_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
/* ================================================== */ /* ================================================== */
static int static int
get_precise_phc_sample(int phc_fd, double precision, struct timespec *phc_ts, get_precise_phc_readings(int phc_fd, int max_samples, struct timespec ts[][3])
struct timespec *sys_ts, double *err)
{ {
#ifdef PTP_SYS_OFFSET_PRECISE #ifdef PTP_SYS_OFFSET_PRECISE
struct ptp_sys_offset_precise sys_off; struct ptp_sys_offset_precise sys_off;
if (max_samples < 1)
return 0;
/* Silence valgrind */ /* Silence valgrind */
memset(&sys_off, 0, sizeof (sys_off)); memset(&sys_off, 0, sizeof (sys_off));
@@ -874,11 +880,11 @@ get_precise_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
return 0; return 0;
} }
phc_ts->tv_sec = sys_off.device.sec; ts[0][0].tv_sec = sys_off.sys_realtime.sec;
phc_ts->tv_nsec = sys_off.device.nsec; ts[0][0].tv_nsec = sys_off.sys_realtime.nsec;
sys_ts->tv_sec = sys_off.sys_realtime.sec; ts[0][1].tv_sec = sys_off.device.sec;
sys_ts->tv_nsec = sys_off.sys_realtime.nsec; ts[0][1].tv_nsec = sys_off.device.nsec;
*err = MAX(LCL_GetSysPrecisionAsQuantum(), precision); ts[0][2] = ts[0][0];
return 1; return 1;
#else #else
@@ -922,23 +928,23 @@ SYS_Linux_OpenPHC(const char *path, int phc_index)
/* ================================================== */ /* ================================================== */
int int
SYS_Linux_GetPHCSample(int fd, int nocrossts, double precision, int *reading_mode, SYS_Linux_GetPHCReadings(int fd, int nocrossts, int *reading_mode, int max_readings,
struct timespec *phc_ts, struct timespec *sys_ts, double *err) struct timespec tss[][3])
{ {
if ((*reading_mode == 2 || !*reading_mode) && !nocrossts && int r = 0;
get_precise_phc_sample(fd, precision, phc_ts, sys_ts, err)) {
if ((*reading_mode == 2 || *reading_mode == 0) && !nocrossts &&
(r = get_precise_phc_readings(fd, max_readings, tss)) > 0) {
*reading_mode = 2; *reading_mode = 2;
return 1; } else if ((*reading_mode == 3 || *reading_mode == 0) &&
} else if ((*reading_mode == 3 || !*reading_mode) && (r = get_extended_phc_readings(fd, max_readings, tss)) > 0) {
get_extended_phc_sample(fd, precision, phc_ts, sys_ts, err)) {
*reading_mode = 3; *reading_mode = 3;
return 1; } else if ((*reading_mode == 1 || *reading_mode == 0) &&
} else if ((*reading_mode == 1 || !*reading_mode) && (r = get_phc_readings(fd, max_readings, tss)) > 0) {
get_phc_sample(fd, precision, phc_ts, sys_ts, err)) {
*reading_mode = 1; *reading_mode = 1;
return 1;
} }
return 0;
return r;
} }
/* ================================================== */ /* ================================================== */
@@ -956,7 +962,7 @@ SYS_Linux_SetPHCExtTimestamping(int fd, int pin, int channel,
pin_desc.func = enable ? PTP_PF_EXTTS : PTP_PF_NONE; pin_desc.func = enable ? PTP_PF_EXTTS : PTP_PF_NONE;
pin_desc.chan = channel; pin_desc.chan = channel;
if (ioctl(fd, PTP_PIN_SETFUNC, &pin_desc)) { if (pin >= 0 && ioctl(fd, PTP_PIN_SETFUNC, &pin_desc)) {
DEBUG_LOG("ioctl(%s) failed : %s", "PTP_PIN_SETFUNC", strerror(errno)); DEBUG_LOG("ioctl(%s) failed : %s", "PTP_PIN_SETFUNC", strerror(errno));
return 0; return 0;
} }

View File

@@ -41,8 +41,8 @@ extern int SYS_Linux_CheckKernelVersion(int req_major, int req_minor);
extern int SYS_Linux_OpenPHC(const char *path, int phc_index); extern int SYS_Linux_OpenPHC(const char *path, int phc_index);
extern int SYS_Linux_GetPHCSample(int fd, int nocrossts, double precision, int *reading_mode, extern int SYS_Linux_GetPHCReadings(int fd, int nocrossts, int *reading_mode, int max_readings,
struct timespec *phc_ts, struct timespec *sys_ts, double *err); struct timespec tss[][3]);
extern int SYS_Linux_SetPHCExtTimestamping(int fd, int pin, int channel, extern int SYS_Linux_SetPHCExtTimestamping(int fd, int pin, int channel,
int rising, int falling, int enable); int rising, int falling, int enable);

View File

@@ -21,23 +21,54 @@
======================================================================= =======================================================================
Driver file for Solaris operating system Driver file for illumos operating system (previously Solaris)
*/ */
#include "config.h" #include "config.h"
#include "sysincl.h" #include "sysincl.h"
#include "logging.h"
#include "privops.h" #include "privops.h"
#include "sys_solaris.h" #include "sys_solaris.h"
#include "sys_timex.h" #include "sys_timex.h"
#include "util.h" #include "util.h"
#include <kvm.h>
#include <nlist.h>
/* ================================================== */
static void
set_dosynctodr(int on_off)
{
struct nlist nl[] = { {"dosynctodr"}, {NULL} };
kvm_t *kt;
kt = kvm_open(NULL, NULL, NULL, O_RDWR, NULL);
if (!kt)
LOG_FATAL("Could not open kvm");
if (kvm_nlist(kt, nl) < 0 || !nl[0].n_value)
LOG_FATAL("Could not get dosynctodr address");
if (kvm_kwrite(kt, nl[0].n_value, &on_off, sizeof (on_off)) < 0)
LOG_FATAL("Could not write to dosynctodr");
kvm_close(kt);
}
/* ================================================== */ /* ================================================== */
void void
SYS_Solaris_Initialise(void) SYS_Solaris_Initialise(void)
{ {
/* The kernel keeps the system clock and hardware clock synchronised to each
other. The dosynctodr variable needs to be set to zero to prevent the
the system clock from following the hardware clock when the system clock
is not adjusted by adjtime() or ntp_adjtime(modes=MOD_OFFSET). */
set_dosynctodr(0);
/* The kernel allows the frequency to be set in the full range off int32_t */ /* The kernel allows the frequency to be set in the full range off int32_t */
SYS_Timex_InitialiseWithFunctions(32500, 1.0 / 100, NULL, NULL, NULL, SYS_Timex_InitialiseWithFunctions(32500, 1.0 / 100, NULL, NULL, NULL,
0.0, 0.0, NULL, NULL); 0.0, 0.0, NULL, NULL);

View File

@@ -75,13 +75,6 @@ convert_timex_frequency(const struct timex *txc)
freq_ppm = txc->freq / FREQ_SCALE; freq_ppm = txc->freq / FREQ_SCALE;
#ifdef MACOSX
/* Temporary workaround for Apple bug treating freq as unsigned number */
if (freq_ppm > 32767) {
freq_ppm -= 65536;
}
#endif
return -freq_ppm; return -freq_ppm;
} }

View File

@@ -28,6 +28,7 @@
#ifndef GOT_SYSINCL_H #ifndef GOT_SYSINCL_H
#define GOT_SYSINCL_H #define GOT_SYSINCL_H
#include <arpa/inet.h>
#include <assert.h> #include <assert.h>
#include <ctype.h> #include <ctype.h>
#include <errno.h> #include <errno.h>
@@ -61,11 +62,6 @@
#include <sys/timex.h> #include <sys/timex.h>
#endif #endif
#ifdef FEAT_IPV6
/* For inet_ntop() */
#include <arpa/inet.h>
#endif
#ifdef HAVE_GETRANDOM #ifdef HAVE_GETRANDOM
#include <sys/random.h> #include <sys/random.h>
#endif #endif

View File

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

View File

@@ -1,7 +1,9 @@
#!/bin/bash #!/usr/bin/env bash
# Run the unit and simulation tests with different compiler sanitizers # Run the unit and simulation tests with different compiler sanitizers
# and under valgrind # and under valgrind
valgrind_opts="--leak-check=full --errors-for-leak-kinds=definite"
cd ../.. cd ../..
if [ "$(uname -sm)" != "Linux x86_64" ]; then if [ "$(uname -sm)" != "Linux x86_64" ]; then
@@ -13,16 +15,23 @@ fi
if [ "$ID" = "fedora" ]; then if [ "$ID" = "fedora" ]; then
echo Checking test dependencies: echo Checking test dependencies:
rpm -q {valgrind,gcc,clang}.x86_64 {libgcc,clang-libs}.{x86_64,i686} || exit 1 rpm -q {gcc,clang}.x86_64 {valgrind,libgcc,clang-libs}.{x86_64,i686} || exit 1
rpm -q {libseccomp,nettle,nss-softokn-freebl,libtomcrypt,gnutls}-devel.{x86_64,i686} || exit 1 rpm -q {libseccomp,nettle,nss-softokn-freebl,libtomcrypt,gnutls}-devel.{x86_64,i686} || exit 1
echo echo
fi fi
touch Makefile touch Makefile
for CC in gcc clang; do for extra_config_opts in \
export CC "--all-privops" \
"--disable-ipv6" \
"--disable-scfilter" \
"--without-gnutls" \
"--without-nettle" \
"--without-nettle --without-nss" \
"--without-nettle --without-nss --without-tomcrypt" \
"--without-nettle --without-nss --without-tomcrypt --without-gnutls"; \
do
for arch_opts in "-m32" ""; do for arch_opts in "-m32" ""; do
pushd test/simulation/clknetsim || exit 1 pushd test/simulation/clknetsim || exit 1
make clean > /dev/null 2>&1 make clean > /dev/null 2>&1
@@ -31,26 +40,21 @@ for CC in gcc clang; do
popd popd
for extra_config_opts in \ for CC in gcc clang; do
"--all-privops" \ export CC
"--disable-scfilter" \
"--without-gnutls" \
"--without-nettle" \
"--without-nettle --without-nss" \
"--without-nettle --without-nss --without-tomcrypt"; \
do
for san_options in "" "-fsanitize=address" "-fsanitize=memory"; do for san_options in "" "-fsanitize=address" "-fsanitize=memory"; do
export CFLAGS="-O2 -g -fsanitize=undefined -fsanitize=float-divide-by-zero -fno-sanitize-recover=undefined,float-divide-by-zero $san_options $arch_opts" export CFLAGS="-O2 -g -fsanitize=undefined -fsanitize=float-divide-by-zero -fno-sanitize-recover=undefined,float-divide-by-zero $san_options $arch_opts"
# clang msan doesn't work on i686 and otherwise requires patches # clang msan doesn't work on i686 and otherwise requires patches
echo $CFLAGS | grep -q 'sanitize=memory' && continue echo $CFLAGS | grep -q 'sanitize=memory' && continue
# build fails with clang ubsan on i686 (Fedora only?) [ -n "$TEST_NO_M32_CLANG" -a "$arch_opts" = "-m32" -a "$CC" = "clang" ] && continue
[ "$arch_opts" = "-m32" -a "$CC" = "clang" ] && continue
[ "$CC" = "gcc" ] && echo $CFLAGS | grep -q 'sanitize=address' && CFLAGS="$CFLAGS -static-libasan" [ -n "$TEST_GCC_STATIC_ASAN" -a "$CC" = "gcc" ] &&
echo $CFLAGS | grep -q 'sanitize=address' && CFLAGS="$CFLAGS -static-libasan"
config_opts="--with-user=chrony --enable-debug --enable-scfilter --enable-ntp-signd $extra_config_opts" config_opts="--with-user=chrony --with-ntp-era=1000000000 --enable-debug --enable-scfilter --enable-ntp-signd $extra_config_opts"
echo ----------------------------------------------------------------------------- echo -----------------------------------------------------------------------------
echo CC=\"$CC\" CFLAGS=\"$CFLAGS\" ./configure $config_opts echo CC=\"$CC\" CFLAGS=\"$CFLAGS\" ./configure $config_opts
@@ -67,20 +71,31 @@ for CC in gcc clang; do
make "$@" || exit 1 make "$@" || exit 1
[ -n "$BUILD_TEST_ONLY" ] && continue [ -n "$TEST_BUILD_ONLY" ] && continue
echo echo
pushd test/unit || exit 1 pushd test/unit || exit 1
make "$@" || exit 1
if [ "$san_options" = "" ]; then if [ "$san_options" = "" ]; then
make check TEST_WRAPPER="valgrind --error-exitcode=1" || exit 1 make check TEST_WRAPPER="valgrind $valgrind_opts --error-exitcode=1" || exit 1
else else
make check || exit 1 make check || exit 1
fi fi
popd popd
[ -n "$TEST_UNIT_ONLY" ] && continue
echo echo
pushd test/simulation || exit 1 pushd test/simulation || exit 1
CLKNETSIM_RANDOM_SEED=101 ./run -i 1 || exit 1 export CLKNETSIM_RANDOM_SEED=101
if [ "$arch_opts" = "" -a "$san_options" = "" ]; then
CLKNETSIM_CLIENT_WRAPPER="valgrind $valgrind_opts" ./run -i 1 || exit 1
elif [ "$CC" = "gcc" ] && ! echo $CFLAGS | grep -q "-static-libasan"; then
libasan=$(ldd ../../chronyd | grep -o '/.*lib.*/libasan.so.[0-9]')
CLKNETSIM_PRELOAD=$libasan ./run -i 1 || exit 1
else
./run -i 1 || exit 1
fi
popd popd
done done
done done

View File

@@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
. ./test.common . ./test.common

View File

@@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
. ./test.common . ./test.common

View File

@@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
. ./test.common . ./test.common

View File

@@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
. ./test.common . ./test.common

View File

@@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
. ./test.common . ./test.common

View File

@@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
. ./test.common . ./test.common

View File

@@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
. ./test.common . ./test.common

View File

@@ -1,40 +1,50 @@
#!/bin/bash #!/usr/bin/env bash
. ./test.common . ./test.common
test_start "NTP eras" test_start "NTP eras"
# Assume NTP_ERA_SPLIT is between years 1960 and 1990 if check_config_h 'HAVE_LONG_TIME_T 1'; then
ntp_start=$(awk "BEGIN {print $(grep NTP_ERA_SPLIT ../../config.h | tr -dc '0-9*+-')}")
else
ntp_start="-2147483648"
fi
# Set date to 500 seconds before NTP second overflows, this should # Set the starting test date to 500 seconds before the second NTP era.
# work correctly with both 32-bit and 64-bit time_t # This should work with 32-bit time_t and also with 64-bit time_t if the
# configured NTP interval covers the test interval.
export CLKNETSIM_START_DATE=$(date -d 'Feb 7 06:19:56 UTC 2036' +'%s') export CLKNETSIM_START_DATE=$(date -d 'Feb 7 06:19:56 UTC 2036' +'%s')
run_test || test_fail if awk "BEGIN {exit !($ntp_start <= $CLKNETSIM_START_DATE && \
check_chronyd_exit || test_fail $CLKNETSIM_START_DATE + $limit < $ntp_start + 2^32)}"; then
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
# The following tests need 64-bit time_t
check_config_h 'HAVE_LONG_TIME_T 1' || test_skip
for year in 1990 2090; do
export CLKNETSIM_START_DATE=$(date -d "Jan 1 00:00:00 UTC $year" +'%s')
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_packet_interval || test_fail check_packet_interval || test_fail
check_sync || test_fail check_sync || test_fail
done fi
for year in 1950 2130; do # The following tests need 64-bit time_t and ntp_start not before 1970
export CLKNETSIM_START_DATE=$(date -d "Jan 1 00:00:00 UTC $year" +'%s') check_config_h 'HAVE_LONG_TIME_T 1' || test_skip
run_test || test_fail echo "$ntp_start" | grep -q '-' && test_skip
check_chronyd_exit || test_fail
check_source_selection || test_fail for time_offset in -1e-1 1e-1; do
check_packet_interval || test_fail for start_offset in 0 "2^32 - $limit"; do
# This check is expected to fail export CLKNETSIM_START_DATE=$(awk "BEGIN {printf \"%.0f\", $ntp_start + $start_offset}")
check_sync && test_fail run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
done
for start_offset in -$limit "2^32"; do
export CLKNETSIM_START_DATE=$(awk "BEGIN {printf \"%.0f\", $ntp_start + $start_offset}")
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync && test_fail
done
done done
test_pass test_pass

View File

@@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
. ./test.common . ./test.common

View File

@@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
. ./test.common . ./test.common

View File

@@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
. ./test.common . ./test.common

View File

@@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
. ./test.common . ./test.common

Some files were not shown because too many files have changed in this diff Show More