Compare commits

...

178 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
108 changed files with 4764 additions and 1184 deletions

View File

@@ -35,7 +35,7 @@ LDFLAGS = @LDFLAGS@
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 \
stubs.o smooth.o sys.o sys_null.o tempcomp.o util.o $(EXTRA_OBJS)

47
NEWS
View File

@@ -1,3 +1,48 @@
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
==================
@@ -12,7 +57,7 @@ Enhancements
* 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
* Update seccomp filter and add less restrictive level
* Restart ongoing name resolution on online command
Bug fixes

2
README
View File

@@ -28,7 +28,7 @@ What will chrony run on?
========================
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.
How do I set it up?

13
candm.h
View File

@@ -270,6 +270,7 @@ typedef struct {
#define REQ_ADDSRC_BURST 0x100
#define REQ_ADDSRC_NTS 0x200
#define REQ_ADDSRC_COPY 0x400
#define REQ_ADDSRC_EF_EXP1 0x800
typedef struct {
uint32_t type;
@@ -295,7 +296,8 @@ typedef struct {
uint32_t flags;
int32_t filter_length;
uint32_t cert_set;
uint32_t reserved[2];
Float max_delay_quant;
uint32_t reserved[1];
int32_t EOR;
} REQ_NTP_Source;
@@ -516,7 +518,8 @@ typedef struct {
#define RPY_CLIENT_ACCESSES_BY_INDEX3 21
#define RPY_SERVER_STATS2 22
#define RPY_SELECT_DATA 23
#define N_REPLY_TYPES 24
#define RPY_SERVER_STATS3 24
#define N_REPLY_TYPES 25
/* Status codes */
#define STT_SUCCESS 0
@@ -529,8 +532,7 @@ typedef struct {
#define STT_BADSUBNET 7
#define STT_ACCESSALLOWED 8
#define STT_ACCESSDENIED 9
/* Deprecated */
#define STT_NOHOSTACCESS 10
#define STT_NOHOSTACCESS 10 /* Deprecated */
#define STT_SOURCEALREADYKNOWN 11
#define STT_TOOMANYSOURCES 12
#define STT_NORTC 13
@@ -660,6 +662,9 @@ typedef struct {
uint32_t cmd_drops;
uint32_t log_drops;
uint32_t ntp_auth_hits;
uint32_t ntp_interleaved_hits;
uint32_t ntp_timestamps;
uint32_t ntp_span_seconds;
int32_t EOR;
} RPY_ServerStats;

280
client.c
View File

@@ -4,7 +4,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* 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
* it under the terms of version 2 of the GNU General Public License as
@@ -283,6 +283,9 @@ open_io(void)
close_io();
}
/* Start from the first address if called again */
address_index = 0;
return 0;
}
@@ -779,182 +782,25 @@ process_cmd_manual(CMD_Request *msg, const char *line)
/* ================================================== */
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 n, specified_subnet_bits;
int all, subnet_bits;
IPAddr ip;
char *p;
p = line;
if (!*p) {
/* blank line - applies to all addresses */
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));
}
}
}
if (!CPS_ParseAllowDeny(line, &all, &ip, &subnet_bits)) {
LOG(LOGS_ERR, "Could not read address");
return 0;
}
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;
}
/* ================================================== */
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
process_cmd_accheck(CMD_Request *msg, char *line)
{
@@ -1099,12 +945,15 @@ process_cmd_add_source(CMD_Request *msg, char *line)
(data.params.burst ? REQ_ADDSRC_BURST : 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_NOSELECT ? REQ_ADDSRC_NOSELECT : 0) |
(data.params.sel_options & SRC_SELECT_TRUST ? REQ_ADDSRC_TRUST : 0) |
(data.params.sel_options & SRC_SELECT_REQUIRE ? REQ_ADDSRC_REQUIRE : 0));
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));
result = 1;
@@ -2552,12 +2401,12 @@ process_cmd_selectdata(char *line)
n_sources = ntohl(reply.data.n_sources.n_sources);
if (verbose) {
printf( " .-- State: N - noselect, M - missing samples, d/D - large distance,\n");
printf( " / ~ - jittery, w/W - waits for others, T - not trusted,\n");
printf( "| x - falseticker, P - not preferred, U - waits for update,\n");
printf( "| S - stale, O - orphan, + - combined, * - best.\n");
printf( "| Effective options ------. (N - noselect, P - prefer\n");
printf( "| Configured options -. \\ T - trust, R - require)\n");
printf( " . State: N - noselect, s - unsynchronised, M - missing samples,\n");
printf( " / d/D - large distance, ~ - jittery, w/W - waits for others,\n");
printf( "| S - stale, O - orphan, T - not trusted, P - not preferred,\n");
printf( "| U - waits for update,, x - falseticker, + - combined, * - best.\n");
printf( "| Effective options ---------. (N - noselect, P - prefer\n");
printf( "| Configured options ----. \\ T - trust, R - require)\n");
printf( "| Auth. enabled (Y/N) -. \\ \\ Offset interval --.\n");
printf( "| | | | |\n");
}
@@ -2616,7 +2465,7 @@ process_cmd_serverstats(char *line)
CMD_Reply reply;
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;
print_report("NTP packets received : %U\n"
@@ -2626,7 +2475,10 @@ process_cmd_serverstats(char *line)
"Client log records dropped : %U\n"
"NTS-KE connections accepted: %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_drops),
(unsigned long)ntohl(reply.data.server_stats.cmd_hits),
@@ -2635,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_drops),
(unsigned long)ntohl(reply.data.server_stats.ntp_auth_hits),
(unsigned long)ntohl(reply.data.server_stats.ntp_interleaved_hits),
(unsigned long)ntohl(reply.data.server_stats.ntp_timestamps),
(unsigned long)ntohl(reply.data.server_stats.ntp_span_seconds),
REPORT_END);
return 1;
@@ -3232,11 +3087,7 @@ process_line(char *line)
} else if (!strcmp(command, "add")) {
do_normal_submit = process_cmd_add_source(&tx_message, line);
} else if (!strcmp(command, "allow")) {
if (!strncmp(line, "all", 3)) {
do_normal_submit = process_cmd_allowall(&tx_message, CPS_SplitWord(line));
} else {
do_normal_submit = process_cmd_allow(&tx_message, line);
}
do_normal_submit = process_cmd_allowdeny(&tx_message, line, REQ_ALLOW, REQ_ALLOWALL);
} else if (!strcmp(command, "authdata")) {
do_normal_submit = 0;
ret = process_cmd_authdata(line);
@@ -3248,28 +3099,15 @@ process_line(char *line)
} else if (!strcmp(command, "cmdaccheck")) {
do_normal_submit = process_cmd_cmdaccheck(&tx_message, line);
} else if (!strcmp(command, "cmdallow")) {
if (!strncmp(line, "all", 3)) {
do_normal_submit = process_cmd_cmdallowall(&tx_message, CPS_SplitWord(line));
} else {
do_normal_submit = process_cmd_cmdallow(&tx_message, line);
}
do_normal_submit = process_cmd_allowdeny(&tx_message, line, REQ_CMDALLOW, REQ_CMDALLOWALL);
} else if (!strcmp(command, "cmddeny")) {
if (!strncmp(line, "all", 3)) {
line = CPS_SplitWord(line);
do_normal_submit = process_cmd_cmddenyall(&tx_message, line);
} else {
do_normal_submit = process_cmd_cmddeny(&tx_message, line);
}
do_normal_submit = process_cmd_allowdeny(&tx_message, line, REQ_CMDDENY, REQ_CMDDENYALL);
} else if (!strcmp(command, "cyclelogs")) {
process_cmd_cyclelogs(&tx_message, line);
} else if (!strcmp(command, "delete")) {
do_normal_submit = process_cmd_delete(&tx_message, line);
} else if (!strcmp(command, "deny")) {
if (!strncmp(line, "all", 3)) {
do_normal_submit = process_cmd_denyall(&tx_message, CPS_SplitWord(line));
} else {
do_normal_submit = process_cmd_deny(&tx_message, line);
}
do_normal_submit = process_cmd_allowdeny(&tx_message, line, REQ_DENY, REQ_DENYALL);
} else if (!strcmp(command, "dfreq")) {
do_normal_submit = process_cmd_dfreq(&tx_message, line);
} else if (!strcmp(command, "dns")) {
@@ -3402,44 +3240,46 @@ process_line(char *line)
if (do_normal_submit) {
ret = request_reply(&tx_message, &rx_message, RPY_NULL, 1);
}
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;
}
/* ================================================== */
#define MAX_LINE_LENGTH 2048
static int
process_args(int argc, char **argv, int multi)
{
int total_length, i, ret = 0;
char *line;
char line[MAX_LINE_LENGTH];
int i, l, ret = 0;
total_length = 0;
for(i=0; i<argc; i++) {
total_length += strlen(argv[i]) + 1;
}
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, " ");
}
for (i = l = 0; i < argc; i++) {
l += snprintf(line + l, sizeof (line) - l, "%s ", argv[i]);
if (l >= sizeof (line)) {
LOG(LOGS_ERR, "Command too long");
return 0;
}
if (!multi && i + 1 < argc)
continue;
ret = process_line(line);
if (!ret || quit)
break;
}
Free(line);
l = 0;
}
return ret;
}
@@ -3458,7 +3298,7 @@ static void
display_gpl(void)
{
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"
"you are welcome to redistribute it under certain conditions. See the\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) 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
* it under the terms of version 2 of the GNU General Public License as
@@ -38,6 +38,7 @@
#include "array.h"
#include "clientlog.h"
#include "conf.h"
#include "local.h"
#include "memory.h"
#include "ntp.h"
#include "reports.h"
@@ -55,8 +56,6 @@ typedef struct {
int8_t rate[MAX_SERVICES];
int8_t ntp_timeout_rate;
uint8_t drop_flags;
NTP_int64 ntp_rx_ts;
NTP_int64 ntp_tx_ts;
} Record;
/* 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 */
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 */
static uint32_t total_hits[MAX_SERVICES];
static uint32_t total_drops[MAX_SERVICES];
static uint32_t total_ntp_auth_hits;
static uint32_t total_ntp_interleaved_hits;
static uint32_t total_record_drops;
#define NSEC_PER_SEC 1000000000U
@@ -135,6 +167,8 @@ static uint32_t total_record_drops;
/* ================================================== */
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->ntp_timeout_rate = INVALID_RATE;
record->drop_flags = 0;
UTI_ZeroNtp64(&record->ntp_rx_ts);
UTI_ZeroNtp64(&record->ntp_tx_ts);
return record;
}
@@ -316,7 +348,7 @@ set_bucket_params(int interval, int burst, uint16_t *max_tokens,
void
CLG_Initialise(void)
{
int i, interval, burst, lrate;
int i, interval, burst, lrate, slots2;
for (i = 0; i < MAX_SERVICES; i++) {
max_tokens[i] = 0;
@@ -359,9 +391,13 @@ CLG_Initialise(void)
/* Calculate the maximum number of slots that can be allocated in the
configured memory limit. Take into account expanding of the hash
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);
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;
records = NULL;
@@ -370,6 +406,17 @@ CLG_Initialise(void)
UTI_GetRandomBytes(&ts_offset, sizeof (ts_offset));
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;
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;
*tx_ts = &record->ntp_tx_ts;
static NtpTimestamps *
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
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->log_drops = total_record_drops;
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_LimitServiceRate(CLG_Service service, int index);
extern void CLG_LogAuthNtpRequest(void);
extern void CLG_GetNtpTimestamps(int index, NTP_int64 **rx_ts, NTP_int64 **tx_ts);
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. */
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) 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
* it under the terms of version 2 of the GNU General Public License as
@@ -321,7 +321,8 @@ transmit_reply(int sock_fd, int request_length, SCK_Message *message)
#if !defined(HAVE_IN_PKTINFO) && defined(IP_SENDSRCADDR)
/* On FreeBSD a local IPv4 address cannot be specified on bound socket */
if (message->local_addr.ip.family == IPADDR_INET4 && (sock_fd != sock_fd4 || bound_sock_fd4))
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
@@ -756,6 +757,8 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_ratio);
params.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.asymmetry = UTI_FloatNetworkToHost(rx_message->data.ntp_source.asymmetry);
params.offset = UTI_FloatNetworkToHost(rx_message->data.ntp_source.offset);
@@ -768,6 +771,8 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
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.copy = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_COPY ? 1 : 0;
params.ext_fields =
ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_EF_EXP1 ? NTP_EF_FLAG_EXP1 : 0;
params.sel_options =
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_PREFER ? SRC_SELECT_PREFER : 0) |
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_NOSELECT ? SRC_SELECT_NOSELECT : 0) |
@@ -1164,7 +1169,7 @@ handle_server_stats(CMD_Request *rx_message, CMD_Reply *tx_message)
RPT_ServerStatsReport 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.nke_hits = htonl(report.nke_hits);
tx_message->data.server_stats.cmd_hits = htonl(report.cmd_hits);
@@ -1173,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.log_drops = htonl(report.log_drops);
tx_message->data.server_stats.ntp_auth_hits = htonl(report.ntp_auth_hits);
tx_message->data.server_stats.ntp_interleaved_hits = htonl(report.ntp_interleaved_hits);
tx_message->data.server_stats.ntp_timestamps = htonl(report.ntp_timestamps);
tx_message->data.server_stats.ntp_span_seconds = htonl(report.ntp_span_seconds);
}
/* ================================================== */

View File

@@ -3,7 +3,7 @@
**********************************************************************
* 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
* 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)
{
char *hostname, *cmd;
uint32_t ef_type;
int n;
src->port = SRC_DEFAULT_PORT;
@@ -65,11 +66,13 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
src->params.nts = 0;
src->params.nts_port = SRC_DEFAULT_NTSPORT;
src->params.copy = 0;
src->params.ext_fields = 0;
src->params.authkey = INACTIVE_AUTHKEY;
src->params.cert_set = SRC_DEFAULT_CERTSET;
src->params.max_delay = SRC_DEFAULT_MAXDELAY;
src->params.max_delay_ratio = SRC_DEFAULT_MAXDELAYRATIO;
src->params.max_delay_dev_ratio = SRC_DEFAULT_MAXDELAYDEVRATIO;
src->params.max_delay_quant = 0.0;
src->params.min_delay = 0.0;
src->params.asymmetry = SRC_DEFAULT_ASYMMETRY;
src->params.offset = 0.0;
@@ -116,6 +119,16 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
} else if (!strcasecmp(cmd, "asymmetry")) {
if (sscanf(line, "%lf%n", &src->params.asymmetry, &n) != 1)
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")) {
if (sscanf(line, "%d%n", &src->params.filter_length, &n) != 1)
return 0;
@@ -128,6 +141,9 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
} else if (!strcasecmp(cmd, "maxdelaydevratio")) {
if (sscanf(line, "%lf%n", &src->params.max_delay_dev_ratio, &n) != 1)
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")) {
if (sscanf(line, "%d%n", &src->params.maxpoll, &n) != 1)
return 0;
@@ -181,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
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 */
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 */
extern int CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance);

149
conf.c
View File

@@ -115,6 +115,7 @@ static int cmd_port = DEFAULT_CANDM_PORT;
static int raw_measurements = 0;
static int do_log_measurements = 0;
static int do_log_selection = 0;
static int do_log_statistics = 0;
static int do_log_tracking = 0;
static int do_log_rtc = 0;
@@ -273,6 +274,9 @@ static int no_system_cert = 0;
/* Array of CNF_HwTsInterface */
static ARR_Instance hwts_interfaces;
/* PTP event port (disabled by default) */
static int ptp_port = 0;
typedef struct {
NTP_Source_Type type;
int pool;
@@ -686,6 +690,8 @@ CNF_ParseLine(const char *filename, int number, char *line)
parse_source(p, command, 1);
} else if (!strcasecmp(command, "port")) {
parse_int(p, &ntp_port);
} else if (!strcasecmp(command, "ptpport")) {
parse_int(p, &ptp_port);
} else if (!strcasecmp(command, "ratelimit")) {
parse_ratelimit(p, &ntp_ratelimit_enabled, &ntp_ratelimit_interval,
&ntp_ratelimit_burst, &ntp_ratelimit_leak);
@@ -856,7 +862,7 @@ static void
parse_refclock(char *line)
{
int n, poll, dpoll, filter_length, pps_rate, min_samples, max_samples, sel_options;
int max_lock_age, pps_forced, stratum, tai;
int local, max_lock_age, pps_forced, stratum, tai;
uint32_t ref_id, lock_ref_id;
double offset, delay, precision, max_dispersion, pulse_width;
char *p, *cmd, *name, *param;
@@ -866,6 +872,7 @@ parse_refclock(char *line)
poll = 4;
dpoll = 0;
filter_length = 64;
local = 0;
pps_forced = 0;
pps_rate = 0;
min_samples = SRC_DEFAULT_MINSAMPLES;
@@ -924,6 +931,9 @@ parse_refclock(char *line)
if (sscanf(line, "%d%n", &filter_length, &n) != 1) {
break;
}
} else if (!strcasecmp(cmd, "local")) {
n = 0;
local = 1;
} else if (!strcasecmp(cmd, "rate")) {
if (sscanf(line, "%d%n", &pps_rate, &n) != 1)
break;
@@ -990,6 +1000,7 @@ parse_refclock(char *line)
refclock->driver_poll = dpoll;
refclock->poll = poll;
refclock->filter_length = filter_length;
refclock->local = local;
refclock->pps_forced = pps_forced;
refclock->pps_rate = pps_rate;
refclock->min_samples = min_samples;
@@ -1022,6 +1033,8 @@ parse_log(char *line)
raw_measurements = 1;
} else if (!strcmp(log_name, "measurements")) {
do_log_measurements = 1;
} else if (!strcmp(log_name, "selection")) {
do_log_selection = 1;
} else if (!strcmp(log_name, "statistics")) {
do_log_statistics = 1;
} else if (!strcmp(log_name, "tracking")) {
@@ -1212,100 +1225,18 @@ parse_ntstrustedcerts(char *line)
static void
parse_allow_deny(char *line, ARR_Instance restrictions, int allow)
{
char *p;
unsigned long a, b, c, d, n;
int all = 0;
AllowDeny *new_node = NULL;
IPAddr ip_addr;
int all, subnet_bits;
AllowDeny *node;
IPAddr ip;
p = line;
if (!CPS_ParseAllowDeny(line, &all, &ip, &subnet_bits))
command_parse_error();
if (!strncmp(p, "all", 3)) {
all = 1;
p = CPS_SplitWord(line);
}
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();
}
}
}
node = ARR_GetNewElement(restrictions);
node->allow = allow;
node->all = all;
node->ip = ip;
node->subnet_bits = subnet_bits;
}
/* ================================================== */
@@ -1556,6 +1487,8 @@ parse_hwtimestamp(char *line)
iface->rxfilter = CNF_HWTS_RXFILTER_NONE;
else if (!strcasecmp(filter, "ntp"))
iface->rxfilter = CNF_HWTS_RXFILTER_NTP;
else if (!strcasecmp(filter, "ptp"))
iface->rxfilter = CNF_HWTS_RXFILTER_PTP;
else if (!strcasecmp(filter, "all"))
iface->rxfilter = CNF_HWTS_RXFILTER_ALL;
else
@@ -1793,6 +1726,8 @@ reload_source_dirs(void)
if (s == NSR_UnresolvedName) {
unresolved++;
} else if (s != NSR_Success) {
LOG(LOGS_ERR, "Could not add source %s", source->params.name);
/* Mark the source as not present */
source->params.name[0] = '\0';
}
@@ -1864,7 +1799,8 @@ CNF_AddInitSources(void)
ntp_addr.port = cps_source.port;
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);
@@ -1877,11 +1813,16 @@ CNF_AddSources(void)
{
NTP_Source *source;
unsigned int i;
NSR_Status s;
for (i = 0; i < ARR_GetSize(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);
}
@@ -1991,6 +1932,14 @@ CNF_GetLogMeasurements(int *raw)
/* ================================================== */
int
CNF_GetLogSelection(void)
{
return do_log_selection;
}
/* ================================================== */
int
CNF_GetLogStatistics(void)
{
@@ -2551,6 +2500,14 @@ CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface)
/* ================================================== */
int
CNF_GetPtpPort(void)
{
return ptp_port;
}
/* ================================================== */
char *
CNF_GetNtsDumpDir(void)
{

4
conf.h
View File

@@ -58,6 +58,7 @@ extern char *CNF_GetLogDir(void);
extern char *CNF_GetDumpDir(void);
extern int CNF_GetLogBanner(void);
extern int CNF_GetLogMeasurements(int *raw);
extern int CNF_GetLogSelection(void);
extern int CNF_GetLogStatistics(void);
extern int CNF_GetLogTracking(void);
extern int CNF_GetLogRtc(void);
@@ -134,6 +135,7 @@ typedef enum {
CNF_HWTS_RXFILTER_ANY,
CNF_HWTS_RXFILTER_NONE,
CNF_HWTS_RXFILTER_NTP,
CNF_HWTS_RXFILTER_PTP,
CNF_HWTS_RXFILTER_ALL,
} CNF_HwTs_RxFilter;
@@ -151,6 +153,8 @@ typedef struct {
extern int CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface);
extern int CNF_GetPtpPort(void);
extern char *CNF_GetNtsDumpDir(void);
extern char *CNF_GetNtsNtpServer(void);
extern int CNF_GetNtsServerCertAndKeyFiles(const char ***certs, const char ***keys);

80
configure vendored
View File

@@ -5,7 +5,7 @@
#
# Copyright (C) Richard P. Curnow 1997-2003
# Copyright (C) Bryan Christianson 2016
# Copyright (C) Miroslav Lichvar 2009, 2012-2020
# Copyright (C) Miroslav Lichvar 2009, 2012-2021
# Copyright (C) Stefan R. Filipek 2019
#
# =======================================================================
@@ -245,6 +245,7 @@ try_lockmem=0
feat_asyncdns=1
feat_forcednsretry=1
try_clock_gettime=1
try_arc4random=1
try_recvmmsg=1
feat_timestamping=1
try_timestamping=0
@@ -421,6 +422,7 @@ case $OPERATINGSYSTEM in
try_setsched=1
try_lockmem=1
try_phc=1
try_arc4random=0
add_def LINUX
echo "Configuring for " $SYSTEM
;;
@@ -467,7 +469,7 @@ case $OPERATINGSYSTEM in
;;
SunOS)
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_lockmem=1
add_def SOLARIS
@@ -479,7 +481,7 @@ case $OPERATINGSYSTEM in
add_def FEAT_PRIVDROP
priv_ops="ADJUSTTIMEX SETTIME BINDSOCKET"
fi
echo "Configuring for Solaris (" $SYSTEM "SunOS version" $VERSION ")"
echo "Configuring for illumos (" $SYSTEM "SunOS version" $VERSION ")"
;;
* )
echo "error: $SYSTEM is not supported (yet?)"
@@ -671,12 +673,12 @@ fi
if [ $try_clock_gettime = "1" ]; then
if test_code 'clock_gettime()' 'time.h' '' '' \
'clock_gettime(CLOCK_REALTIME, NULL);'
'clock_gettime(CLOCK_REALTIME, (void *)1);'
then
add_def HAVE_CLOCK_GETTIME
else
if test_code 'clock_gettime() in -lrt' 'time.h' '' '-lrt' \
'clock_gettime(CLOCK_REALTIME, NULL);'
'clock_gettime(CLOCK_REALTIME, (void *)1);'
then
add_def HAVE_CLOCK_GETTIME
EXTRA_LIBS="$EXTRA_LIBS -lrt"
@@ -702,11 +704,14 @@ then
use_pthread=1
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
else
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
fi
fi
@@ -900,7 +905,7 @@ if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_nettle = "1" ];
add_def FEAT_SECHASH
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
add_def HAVE_CMAC
EXTRA_OBJECTS="$EXTRA_OBJECTS cmac_nettle.o"
@@ -925,7 +930,7 @@ fi
if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_tomcrypt = "1" ]; then
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
HASH_OBJ="hash_tomcrypt.o"
HASH_LINK="-ltomcrypt"
@@ -934,36 +939,69 @@ if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_tomcrypt = "1" ]
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_CLI_OBJECTS="$EXTRA_CLI_OBJECTS $HASH_OBJ"
LIBS="$LIBS $HASH_LINK"
if [ $feat_ntp = "1" ] && [ $feat_nts = "1" ] && [ $try_gnutls = "1" ]; then
test_cflags="`pkg_config --cflags gnutls`"
test_link="`pkg_config --libs gnutls`"
if test_code 'gnutls' 'gnutls/gnutls.h' \
"$test_cflags" "$test_link" '
return gnutls_init(NULL, 0) + GNUTLS_TLS1_3 +
gnutls_priority_init2(NULL, "", NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND) +
gnutls_prf_rfc5705(NULL, 0, "", 0, "", 16, NULL);'
if [ "$HASH_OBJ" = "hash_gnutls.o" ]; then
test_cflags=""
test_link=""
else
test_cflags="`pkg_config --cflags gnutls`"
test_link="`pkg_config --libs gnutls`"
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
if test_code 'SIV in nettle' \
'nettle/siv-cmac.h' "" "$LIBS" \
'siv_cmac_aes128_set_key(NULL, NULL);'
'siv_cmac_aes128_set_key((void *)1, (void *)2);'
then
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o"
add_def HAVE_SIV
add_def HAVE_NETTLE_SIV_CMAC
else
if test_code 'SIV in gnutls' 'gnutls/gnutls.h' \
"$test_cflags" "$test_link" '
return gnutls_aead_cipher_init(NULL, GNUTLS_CIPHER_AES_128_SIV, NULL);'
if test_code 'SIV in gnutls' 'gnutls/crypto.h' \
"$test_cflags" "$test_link $LIBS" '
return gnutls_aead_cipher_init((void *)1, GNUTLS_CIPHER_AES_128_SIV, (void *)2);'
then
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_gnutls.o"
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
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
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o"
add_def HAVE_SIV

View File

@@ -3,7 +3,7 @@
// Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Stephen Wadeley 2016
// 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
// 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
as a power of 2 in seconds. For example, *minpoll 5* would mean that the
polling interval should not drop below 32 seconds. The default is 6 (64
seconds), the minimum is -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
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
@@ -82,7 +82,7 @@ should be in a local network.
This option specifies the maximum interval between requests sent to the server
as a power of 2 in seconds. For example, *maxpoll 9* indicates that the polling
interval should stay at or below 9 (512 seconds). The default is 10 (1024
seconds), the minimum is -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).
*iburst*:::
With this option, *chronyd* will start with a burst of 4-8 requests in order to
@@ -138,18 +138,33 @@ 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
measurement to be ignored, this level can be defined with the *maxdelay*
option. For example, *maxdelay 0.3* would indicate that measurements with a
round-trip delay of 0.3 seconds or more should be ignored. The default value is
3 seconds and the maximum value is 1000 seconds.
round-trip delay greater than 0.3 seconds should be ignored. The default value
is 3 seconds and the maximum value is 1000 seconds.
*maxdelayratio* _ratio_:::
This option is similar to the *maxdelay* option above. *chronyd* keeps a record
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
specified ratio times the minimum delay, it will be rejected.
buffered. If a measurement has a round-trip delay that is greater than the
specified ratio times the minimum delay, it will be rejected. By default, this
test is disabled.
*maxdelaydevratio* _ratio_:::
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
the previous measurements that is greater than the specified ratio, it will be
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_:::
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
@@ -176,11 +191,12 @@ Set the minimum number of samples kept for this source. This overrides the
*maxsamples* _samples_:::
Set the maximum number of samples kept for this source. This overrides the
<<maxsamples,*maxsamples*>> directive.
*filter* _samples_:::
*filter* _polls_:::
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
intended to be used with very short polling intervals in local networks where
it is acceptable to generate a lot of NTP traffic.
filter will process samples collected in the specified number of polls
into a single sample. It is intended to be used with very short polling
intervals in local networks where it is acceptable to generate a lot of NTP
traffic.
*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
@@ -273,10 +289,11 @@ sources are unreachable.
*version* _version_:::
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
requests using a newer version. The default version depends on whether a key is
specified by the *key* option and which authentication hash function the key
is using. If the output size of the hash function is longer than 160 bits, the
default version is 3 for compatibility with older *chronyd* servers. Otherwise,
requests using a newer version. The default version depends on other options.
If the *extfield* or *xleave* option is used, the default version is 4. If
those options are not used and the *key* option specifies a key using a hash
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.
*copy*:::
This option specifies that the server and client are closely related, their
@@ -287,6 +304,25 @@ This is useful when multiple instances of `chronyd` are running on one computer
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_]...::
The syntax of this directive is similar to that for the <<server,*server*>>
@@ -491,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
PPS samples.
*pin*=_index_::::
This option specifies the index of the pin to which is connected the PPS
signal. The default value is 0.
This option specifies the index of the pin which should be enabled for the
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_::::
This option specifies the index of the channel for the PPS mode. The default
value is 0.
@@ -604,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
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.
*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_:::
Set the minimum number of samples kept for this source. This overrides the
<<minsamples,*minsamples*>> directive.
@@ -1079,7 +1124,11 @@ clock will be off for a longer time. On Linux with the default
*ignore*:::
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
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}::
+
When serving time to NTP clients that cannot be configured to correct their
@@ -1186,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.
[[maxchange]]*maxchange* _offset_ _start_ _ignore_::
This directive sets the maximum allowed offset corrected on a clock update. The
check is performed only after the specified number of updates to allow a large
initial adjustment of the system clock. When an offset larger than the
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
exit). In both cases a message is sent to syslog.
This directive sets the maximum offset to be accepted on a clock update. The
offset is measured relative to the current estimate of the true time, which is
different from the system time if a previous slew did not finish.
+
The check is enabled after the specified number of clock updates to allow a
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:
+
@@ -1218,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
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
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
limited by the system driver rather than this directive.
@@ -1257,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
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
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).
[[tempcomp]]
@@ -1498,9 +1547,12 @@ directive.
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
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
clients at the same time. The maximum value is 2^32-1 (4 GB) on 32-bit systems
and 2^35 (32 GB) on 64-bit systems.
limit is 524288 bytes, which enables monitoring of up to 4096 IP addresses at
the same time and holding NTP timestamps for up to 4096 clients using the
interleaved mode (depending on uniformity of their polling interval). The
number of addresses and timestamps is always a power of 2. The maximum
effective value is 2147483648 (2 GB), which corresponds to 16777216 addresses
and timestamps.
+
An example of the use of this directive is:
+
@@ -1604,7 +1656,8 @@ The port will be open only when a certificate and key is specified by the
This directive specifies a file containing a certificate in the PEM format
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.
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.
@@ -1616,7 +1669,9 @@ recommended for a near-seamless server operation.
[[ntsserverkey]]*ntsserverkey* _file_::
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
@@ -1710,20 +1765,20 @@ directive.
The *ratelimit* directive supports a number of options (which can be defined
in any order):
+
*interval*:::
*interval* _interval_:::
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
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
(responses are allowed in bursts, even if the interval between them is shorter
than the specified interval).
*burst*:::
*burst* _responses_:::
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
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
maximum value is 255.
*leak*:::
*leak* _rate_:::
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
necessary to prevent an attacker who is sending requests with a spoofed
@@ -2029,9 +2084,11 @@ from the example line above):
. Stratum of remote computer. [2]
. RFC 5905 tests 1 through 3 (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,
against defined parameters, and a test for synchronisation loop (1=pass,
0=fail) [1111]
. Results of the *maxdelay*, *maxdelayratio*, and *maxdelaydevratio* (or
*maxdelayquant*) tests, and a test for synchronisation loop (1=pass,
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]
. Remote poll [10]
. '`Score`' (an internal score within each polling level used to decide when to
@@ -2105,6 +2162,71 @@ from the example line above):
the source is more variable than the delay of packets sent from the source
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*:::
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
@@ -2359,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
timestamping, which can be verified with the *ethtool -T* command. The list of
capabilities should include _SOF_TIMESTAMPING_RAW_HARDWARE_,
_SOF_TIMESTAMPING_TX_HARDWARE_, and _SOF_TIMESTAMPING_RX_HARDWARE_. Receive
filter _HWTSTAMP_FILTER_ALL_, or _HWTSTAMP_FILTER_NTP_ALL_, is necessary for
timestamping of received packets. Timestamping of packets received from bridged
and bonded interfaces is supported on Linux 4.13 and newer. When *chronyd* is
running, no other process (e.g. a PTP daemon) should be working with the NIC
clock.
capabilities should include _hardware-raw-clock_, _hardware-transmit_, and
_hardware-receive_. The receive filter _all_, or _ntp_, is necessary for
timestamping of received NTP packets. Timestamping of packets received on
bridged and bonded interfaces is supported on Linux 4.13 and newer. If HW
timestamping does not work for received packets, *chronyd* will use kernel
receive timestamps instead. Transmit-only HW timestamping can still be useful
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
interfaces. The source of timestamps (i.e. hardware, kernel, or daemon) is
@@ -2413,15 +2542,20 @@ _all_::::
Enables timestamping of all received packets.
_ntp_::::
Enables timestamping of received NTP packets.
_ptp_::::
Enables timestamping of received PTP packets.
_none_::::
Disables timestamping of received packets.
{blank}:::
The most specific filter for timestamping NTP packets which is supported by the
NIC is selected by default. Some NICs can timestamp only PTP packets, which
limits the selection to the _none_ filter. Forcing timestamping of all packets
with the _all_ filter when the NIC supports both _all_ and _ntp_ filters can be
useful when packets are received from or on a non-standard UDP port (e.g.
specified by the *port* directive).
The most specific filter for timestamping of NTP packets supported by the NIC
is selected by default. Some NICs can timestamp PTP packets only. By default,
they will be configured with the _none_ filter and expected to provide hardware
timestamps for transmitted packets only. Timestamping of PTP packets is useful
with NTP-over-PTP enabled by the <<chrony.conf.adoc#ptpport,*ptpport*>>
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}::
+
Examples of the directive are:
@@ -2465,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
supported.
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
*chronyd* using, some of the following hash functions and ciphers may
also be available:
@@ -2496,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*).
[[lock_all]]*lock_all*::
The *lock_all* directive will lock chronyd into RAM so that it will never be
paged out. This mode is supported on Linux, FreeBSD, NetBSD, and Solaris. This
directive uses the POSIX *mlockall()* system call to prevent *chronyd* from
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.
The *lock_all* directive will lock the *chronyd* process into RAM so that it
will never be paged out. This can result in lower and more consistent latency.
The directive is supported on Linux, FreeBSD, NetBSD, and illumos.
[[pidfile]]*pidfile* _file_::
Unless *chronyd* is started with the *-Q* option, it writes its process ID
@@ -2514,8 +2645,34 @@ e.g.:
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_::
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
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
@@ -2541,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.
+
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
range of privileged system calls on behalf of the parent.
+

View File

@@ -99,12 +99,15 @@ With this option multiple commands can be specified. Each argument will be
interpreted as a whole command.
*-h* _host_::
This option allows the user to specify which host (or comma-separated list of
addresses) running the *chronyd* program is to be contacted. This allows for
remote monitoring, without having to connect over SSH to the other host first.
This option specifies the host to be contacted by *chronyc*. It can be
specified with a hostname, IP address, or path to the local Unix domain socket.
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
*chronyc* is being run.
The default value is _@CHRONYRUNDIR@/chronyd.sock,127.0.0.1,::1_, i.e. the host
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_::
This option allows the user to specify the UDP port number which the target
@@ -448,6 +451,7 @@ states are reported.
The following states indicate the source is 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
<<chrony.conf.adoc#maxdistance,*maxdistance*>> directive).
@@ -695,7 +699,8 @@ packets sent to the source is more variable than the delay of packets sent
from the source back.
*NTP tests*:::
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*:::
This shows if the response was in the interleaved mode.
*Authenticated*:::
@@ -1107,13 +1112,9 @@ The columns are as follows:
received/accepted.
[[serverstats]]*serverstats*::
The *serverstats* command displays how many valid NTP and command requests, and
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
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.
The *serverstats* command displays NTP and command server statistics.
+
An example of the output is shown below.
+
----
NTP packets received : 1598
@@ -1124,7 +1125,47 @@ Client log records dropped : 0
NTS-KE connections accepted: 3
NTS-KE connections dropped : 0
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_]::
The effect of the allow command is identical to the

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.
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,
Solaris, and macOS 10.13 or later).
illumos, and macOS 10.13 or later).
*-R*::
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@_.
+
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
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.
*-F* _level_::
This option configures a system call filter when *chronyd* is compiled with
support for the Linux secure computing (seccomp) facility. In level 1 the
process is killed when a forbidden system call is made, in level -1 the SIGSYS
signal is thrown instead and in level 0 the filter is disabled. The default
value is 0.
This option configures system call filters loaded by *chronyd* processes if it
was compiled with support for the Linux secure computing (seccomp) facility.
Three levels are defined: 0, 1, 2. The filters are disabled at level 0. At
levels 1 and 2, *chronyd* will be killed if it makes a system call which is
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
version of the system where *chrony* is installed as the filter needs to allow
also system calls made from libraries that *chronyd* is using (e.g. libc) and
different 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 normal operation.
At level 1, the filters allow only selected system calls that are normally
expected to be made by *chronyd*. Other system calls are blocked. This level is
recommended only if it is known to work on the version of the system where
*chrony* is installed. The filters need to allow also system calls made by
libraries that *chronyd* is using (e.g. libc), but different versions or
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_::
On Linux, this option will 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 to disable the thread time
constraint policy or 1 for the policy to be enabled. Other systems do not
On Linux, FreeBSD, NetBSD, and illumos this option will 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 to disable the thread
time constraint policy or 1 for the policy to be enabled. Other systems do not
support this option. The default value is 0.
*-m*::
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*::
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
//
// 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
// it under the terms of version 2 of the GNU General Public License as
@@ -345,6 +345,24 @@ server ntp.local minpoll -6 maxpoll -6 filter 15 xleave
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?
Yes. With the `-q` option `chronyd` will set the system clock once and exit.
@@ -466,9 +484,68 @@ identically configured leap-smearing servers. Note that some clients can get
leap seconds from other sources (e.g. with the `leapsectz` directive in
`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?
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
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
@@ -483,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.
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?
They were removed in version 2.2. Authentication is no longer supported in the
@@ -609,6 +712,18 @@ 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
@@ -677,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
recovery by increasing the clock error rate with the `maxclockerror` directive.
[[using-pps-refclock]]
=== Using a PPS reference clock?
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
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
common to have a larger offset. It needs to be corrected with the `offset`
option.
@@ -701,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
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
to be used for synchronisation.
@@ -757,6 +874,10 @@ in parentheses) on the `Reference ID` line.
Only by the source code. See _cmdmon.c_ (`chronyd` side) and _client.c_
(`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
=== What is the real-time clock (RTC)?
@@ -883,6 +1004,34 @@ timestamps (e.g. daemon timestamp vs kernel timestamp) for serving time and
synchronisation of its own clock, which will cause the other computer to
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
=== Does `chrony` support Windows?

View File

@@ -27,7 +27,7 @@ The following libraries with their development files, and programs, are needed
to enable optional features:
* 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`)
* libseccomp: system call filter on Linux (`SCFILTER`)
* GnuTLS and Nettle: Network Time Security (`NTS`)

View File

@@ -16,5 +16,32 @@ TimeoutStartSec=180
RemainAfterExit=yes
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]
WantedBy=multi-user.target

View File

@@ -1,8 +1,7 @@
#!/bin/sh
# This is a NetworkManager dispatcher script for chronyd to update
# its NTP sources passed from DHCP options. Note that this script is
# specific to NetworkManager-dispatcher due to use of the
# DHCP4_NTP_SERVERS environment variable.
# its NTP sources with servers from DHCP options passed by NetworkManager
# in the DHCP4_NTP_SERVERS and DHCP6_DHCP6_NTP_SERVERS environment variables.
export LC_ALL=C
@@ -10,17 +9,23 @@ interface=$1
action=$2
chronyc=/usr/bin/chronyc
default_server_options=iburst
server_options=iburst
server_dir=/var/run/chrony-dhcp
dhcp_server_file=$server_dir/$interface.sources
# DHCP4_NTP_SERVERS is passed from DHCP options by NetworkManager.
nm_dhcp_servers=$DHCP4_NTP_SERVERS
dhcp_ntp_servers="$DHCP4_NTP_SERVERS $DHCP6_DHCP6_NTP_SERVERS"
add_servers_from_dhcp() {
rm -f "$dhcp_server_file"
for server in $nm_dhcp_servers; do
echo "server $server $default_server_options" >> "$dhcp_server_file"
for server in $dhcp_ntp_servers; do
# 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
$chronyc reload sources > /dev/null 2>&1 || :
}
@@ -34,10 +39,11 @@ clear_servers_from_dhcp() {
mkdir -p $server_dir
if [ "$action" = "up" ] || [ "$action" = "dhcp4-change" ]; then
add_servers_from_dhcp
elif [ "$action" = "down" ]; then
clear_servers_from_dhcp
fi
case "$action" in
up|dhcp4-change|dhcp6-change)
add_servers_from_dhcp;;
down)
clear_servers_from_dhcp;;
esac
exit 0

View File

@@ -7,8 +7,18 @@ export LC_ALL=C
chronyc=/usr/bin/chronyc
# For NetworkManager consider only up/down events
[ $# -ge 2 ] && [ "$2" != "up" ] && [ "$2" != "down" ] && exit 0
# For NetworkManager consider only selected events
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

View File

@@ -10,9 +10,40 @@ Type=forking
PIDFile=/run/chrony/chronyd.pid
EnvironmentFile=-/etc/sysconfig/chronyd
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
ProcSubset=pid
ProtectControlGroups=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]
WantedBy=multi-user.target

1
hash.h
View File

@@ -44,6 +44,7 @@ typedef enum {
HSH_SHA3_512 = 9,
HSH_TIGER = 10,
HSH_WHIRLPOOL = 11,
HSH_MD5_NONCRYPTO = 10000, /* For NTPv4 reference ID */
} HSH_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)
{
/* only MD5 is supported */
if (algorithm != HSH_MD5)
if (algorithm != HSH_MD5 && algorithm != HSH_MD5_NONCRYPTO)
return -1;
return 0;

View File

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

View File

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

View File

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

109
hwclock.c
View File

@@ -33,6 +33,7 @@
#include "local.h"
#include "logging.h"
#include "memory.h"
#include "quantiles.h"
#include "regress.h"
#include "util.h"
@@ -43,6 +44,13 @@
/* Maximum acceptable frequency offset of the clock */
#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 {
/* HW and local reference timestamp */
struct timespec hw_ref;
@@ -64,12 +72,18 @@ struct HCL_Instance_Record {
/* Minimum interval between samples */
double min_separation;
/* Expected precision of readings */
double precision;
/* Flag indicating the offset and frequency values are valid */
int valid_coefs;
/* Estimated offset and frequency of HW clock relative to local clock */
double offset;
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_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;
@@ -110,6 +124,10 @@ HCL_CreateInstance(int min_samples, int max_samples, double min_separation)
clock->n_samples = 0;
clock->valid_coefs = 0;
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);
@@ -121,6 +139,7 @@ HCL_CreateInstance(int min_samples, int max_samples, double min_separation)
void HCL_DestroyInstance(HCL_Instance clock)
{
LCL_RemoveParameterChangeHandler(handle_slew, clock);
QNT_DestroyInstance(clock->delay_quants);
Free(clock->y_data);
Free(clock->x_data);
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
HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
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 */
extern HCL_Instance HCL_CreateInstance(int min_samples, int max_samples,
double min_separation);
double min_separation, double precision);
/* Destroy a HW clock instance */
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 */
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 */
extern void HCL_AccumulateSample(HCL_Instance clock, struct timespec *hw_ts,
struct timespec *local_ts, double err);

20
local.c
View File

@@ -563,6 +563,8 @@ void
LCL_NotifyExternalTimeStep(struct timespec *raw, struct timespec *cooked,
double offset, double dispersion)
{
LCL_CancelOffsetCorrection();
/* Dispatch to all handlers */
invoke_parameter_change_handlers(raw, cooked, 0.0, offset, LCL_ChangeUnknownStep);
@@ -628,6 +630,24 @@ LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
/* ================================================== */
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;
}
/* ================================================== */
void
lcl_InvokeDispersionNotifyHandlers(double dispersion)
{

View File

@@ -173,6 +173,11 @@ extern void LCL_NotifyLeap(int leap);
a slew, in one easy step */
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. */
extern int LCL_GetSysPrecisionAsLog(void);

11
main.c
View File

@@ -76,11 +76,18 @@ static REF_Mode ref_mode = REF_ModeNormal;
static void
do_platform_checks(void)
{
struct timespec ts;
/* Require at least 32-bit integers, two's complement representation and
the usual implementation of conversion of unsigned integers */
assert(sizeof (int) >= 4);
assert(-1 == ~0);
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);
}
/* ================================================== */
@@ -141,6 +148,8 @@ MAI_CleanupAndExit(void)
HSH_Finalise();
LOG_Finalise();
UTI_ResetGetRandomFunctions();
exit(exit_status);
}
@@ -157,6 +166,8 @@ signal_cleanup(int x)
static void
quit_timeout(void *arg)
{
LOG(LOGS_INFO, "Timeout reached");
/* Return with non-zero status if the clock is not synchronised */
exit_status = REF_GetOurStratum() >= NTP_MAX_STRATUM;
SCH_QuitProgram();

25
ntp.h
View File

@@ -113,6 +113,30 @@ typedef struct {
#define NTP_REFID_LOCAL 0x7F7F0101UL /* 127.127.1.1 */
#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 */
typedef enum {
NTP_AUTH_NONE = 0, /* No authentication */
@@ -130,6 +154,7 @@ typedef struct {
NTP_Mode mode;
int ext_fields;
int ext_field_flags;
struct {
NTP_AuthMode mode;

View File

@@ -32,7 +32,6 @@
#include "logging.h"
#include "memory.h"
#include "ntp_auth.h"
#include "ntp_ext.h"
#include "ntp_signd.h"
#include "nts_ntp.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
create_instance(NTP_AuthMode mode)
{
@@ -247,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
NAU_CheckRequestAuth(NTP_Packet *request, NTP_PacketInfo *info, uint32_t *kod)
{

View File

@@ -55,9 +55,6 @@ extern int NAU_PrepareRequestAuth(NAU_Instance instance);
extern int NAU_GenerateRequestAuth(NAU_Instance instance, NTP_Packet *request,
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
kod code is returned, a KoD response should be sent back. */
extern int NAU_CheckRequestAuth(NTP_Packet *request, NTP_PacketInfo *info, uint32_t *kod);

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2020
* Copyright (C) Miroslav Lichvar 2009-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
@@ -32,8 +32,10 @@
#include "array.h"
#include "ntp_auth.h"
#include "ntp_core.h"
#include "ntp_ext.h"
#include "ntp_io.h"
#include "memory.h"
#include "quantiles.h"
#include "sched.h"
#include "reference.h"
#include "local.h"
@@ -88,6 +90,8 @@ struct NCR_Instance_Record {
from received packets) */
int remote_stratum; /* Stratum of the server/peer (recovered from
received packets) */
double remote_root_delay; /* Root delay from last valid packet */
double remote_root_dispersion;/* Root dispersion from last valid packet */
int presend_minpoll; /* If the current polling interval is
at least this, an extra client packet
@@ -129,6 +133,12 @@ struct NCR_Instance_Record {
double offset_correction; /* Correction applied to measured offset
(e.g. for asymmetry in network delay) */
int ext_field_flags; /* Enabled extension fields */
uint32_t remote_mono_epoch; /* ID of the source's monotonic scale */
double mono_doffset; /* Accumulated offset between source's
real-time and monotonic scales */
NAU_Instance auth; /* Authentication */
/* Count of transmitted packets since last valid response */
@@ -142,6 +152,7 @@ struct NCR_Instance_Record {
int valid_timestamps;
/* Receive and transmit timestamps from the last valid response */
NTP_int64 remote_ntp_monorx;
NTP_int64 remote_ntp_rx;
NTP_int64 remote_ntp_tx;
@@ -186,8 +197,12 @@ struct NCR_Instance_Record {
SRC_Instance source;
/* Optional long-term quantile estimate of peer delay */
QNT_Instance delay_quant;
/* Optional median filter for NTP measurements */
SPF_Instance filter;
int filter_count;
int burst_good_samples_to_go;
int burst_total_samples_to_go;
@@ -255,8 +270,12 @@ static ARR_Instance broadcasts;
#define MAX_MAXDELAYRATIO 1.0e6
#define MAX_MAXDELAYDEVRATIO 1.0e6
/* Parameters for the peer delay quantile */
#define DELAY_QUANT_Q 100
#define DELAY_QUANT_REPEAT 7
/* Minimum and maximum allowed poll interval */
#define MIN_POLL -6
#define MIN_POLL -7
#define MAX_POLL 24
/* Enable sub-second polling intervals only when the peer delay is not
@@ -278,6 +297,9 @@ static ARR_Instance broadcasts;
interleaved mode to prefer a sample using previous timestamps */
#define MAX_INTERLEAVED_L2L_RATIO 0.1
/* Maximum acceptable change in server mono<->real offset */
#define MAX_MONO_DOFFSET 16.0
/* Invalid socket, different from the one in ntp_io.c */
#define INVALID_SOCK_FD -2
@@ -289,6 +311,11 @@ static int server_sock_fd6;
static ADF_AuthTable access_auth_table;
/* Current offset between monotonic and cooked time, and its epoch ID
which is reset on clock steps */
static double server_mono_offset;
static uint32_t server_mono_epoch;
/* Characters for printing synchronisation status and timestamping source */
static const char leap_chars[4] = {'N', '+', '-', '?'};
static const char tss_chars[3] = {'D', 'K', 'H'};
@@ -300,6 +327,7 @@ static void transmit_timeout(void *arg);
static double get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx);
static double get_separation(int poll);
static int parse_packet(NTP_Packet *packet, int length, NTP_PacketInfo *info);
static void process_sample(NCR_Instance inst, NTP_Sample *sample);
static void set_connectivity(NCR_Instance inst, SRC_Connectivity connectivity);
/* ================================================== */
@@ -375,6 +403,20 @@ zero_local_timestamp(NTP_Local_Timestamp *ts)
/* ================================================== */
static void
handle_slew(struct timespec *raw, struct timespec *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything)
{
if (change_type == LCL_ChangeAdjust) {
server_mono_offset += doffset;
} else {
UTI_GetRandomBytes(&server_mono_epoch, sizeof (server_mono_epoch));
server_mono_offset = 0.0;
}
}
/* ================================================== */
void
NCR_Initialise(void)
{
@@ -391,6 +433,9 @@ NCR_Initialise(void)
/* Server socket will be opened when access is allowed */
server_sock_fd4 = INVALID_SOCK_FD;
server_sock_fd6 = INVALID_SOCK_FD;
LCL_AddParameterChangeHandler(handle_slew, NULL);
handle_slew(NULL, NULL, 0.0, 0.0, LCL_ChangeUnknownStep, NULL);
}
/* ================================================== */
@@ -400,6 +445,8 @@ NCR_Finalise(void)
{
unsigned int i;
LCL_RemoveParameterChangeHandler(handle_slew, NULL);
if (server_sock_fd4 != INVALID_SOCK_FD)
NIO_CloseServerSocket(server_sock_fd4);
if (server_sock_fd6 != INVALID_SOCK_FD)
@@ -456,11 +503,16 @@ start_initial_timeout(NCR_Instance inst)
/* In case the offline period was too short, adjust the delay to keep
the interval between packets at least as long as the current polling
interval */
SCH_GetLastEventTime(&now, NULL, NULL);
last_tx = UTI_DiffTimespecsToDouble(&now, &inst->local_tx.ts);
if (last_tx < 0.0)
last_tx = 0.0;
delay = get_transmit_delay(inst, 0, 0.0) - last_tx;
if (!UTI_IsZeroTimespec(&inst->local_tx.ts)) {
SCH_GetLastEventTime(&now, NULL, NULL);
last_tx = UTI_DiffTimespecsToDouble(&now, &inst->local_tx.ts);
if (last_tx < 0.0)
last_tx = 0.0;
delay = get_transmit_delay(inst, 0, 0.0) - last_tx;
} else {
delay = 0.0;
}
if (delay < INITIAL_DELAY)
delay = INITIAL_DELAY;
@@ -564,6 +616,7 @@ NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
result->auto_offline = params->auto_offline;
result->copy = params->copy && result->mode == MODE_CLIENT;
result->poll_target = params->poll_target;
result->ext_field_flags = params->ext_fields;
if (params->nts) {
IPSockAddr nts_address;
@@ -582,7 +635,10 @@ NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
result->auth = NAU_CreateNoneInstance();
}
result->version = NAU_GetSuggestedNtpVersion(result->auth);
if (result->ext_field_flags || result->interleaved)
result->version = NTP_VERSION;
else
result->version = NAU_GetSuggestedNtpVersion(result->auth);
if (params->version)
result->version = CLAMP(NTP_MIN_COMPAT_VERSION, params->version, NTP_VERSION);
@@ -594,9 +650,16 @@ NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
params->min_samples, params->max_samples,
params->min_delay, params->asymmetry);
if (params->max_delay_quant > 0.0) {
int k = round(CLAMP(0.05, params->max_delay_quant, 0.95) * DELAY_QUANT_Q);
result->delay_quant = QNT_CreateInstance(k, k, DELAY_QUANT_Q, DELAY_QUANT_REPEAT,
LCL_GetSysPrecisionAsQuantum() / 2.0);
} else {
result->delay_quant = NULL;
}
if (params->filter_length >= 1)
result->filter = SPF_CreateInstance(params->filter_length, params->filter_length,
NTP_MAX_DISPERSION, 0.0);
result->filter = SPF_CreateInstance(1, params->filter_length, NTP_MAX_DISPERSION, 0.0);
else
result->filter = NULL;
@@ -604,7 +667,7 @@ NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
result->tx_timeout_id = 0;
result->tx_suspended = 1;
result->opmode = MD_OFFLINE;
result->local_poll = result->minpoll;
result->local_poll = MAX(result->minpoll, MIN_NONLAN_POLL);
result->poll_score = 0.0;
zero_local_timestamp(&result->local_tx);
result->burst_good_samples_to_go = 0;
@@ -630,6 +693,8 @@ NCR_DestroyInstance(NCR_Instance instance)
if (instance->mode == MODE_ACTIVE)
NIO_CloseServerSocket(instance->local_addr.sock_fd);
if (instance->delay_quant)
QNT_DestroyInstance(instance->delay_quant);
if (instance->filter)
SPF_DestroyInstance(instance->filter);
@@ -664,9 +729,14 @@ NCR_ResetInstance(NCR_Instance instance)
instance->remote_poll = 0;
instance->remote_stratum = 0;
instance->remote_root_delay = 0.0;
instance->remote_root_dispersion = 0.0;
instance->remote_mono_epoch = 0;
instance->mono_doffset = 0.0;
instance->valid_rx = 0;
instance->valid_timestamps = 0;
UTI_ZeroNtp64(&instance->remote_ntp_monorx);
UTI_ZeroNtp64(&instance->remote_ntp_rx);
UTI_ZeroNtp64(&instance->remote_ntp_tx);
UTI_ZeroNtp64(&instance->local_ntp_rx);
@@ -681,8 +751,11 @@ NCR_ResetInstance(NCR_Instance instance)
UTI_ZeroNtp64(&instance->init_remote_ntp_tx);
zero_local_timestamp(&instance->init_local_rx);
if (instance->delay_quant)
QNT_Reset(instance->delay_quant);
if (instance->filter)
SPF_DropSamples(instance->filter);
instance->filter_count = 0;
}
/* ================================================== */
@@ -732,6 +805,8 @@ NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr, int
static void
adjust_poll(NCR_Instance inst, double adj)
{
NTP_Sample last_sample;
inst->poll_score += adj;
if (inst->poll_score >= 1.0) {
@@ -757,7 +832,9 @@ adjust_poll(NCR_Instance inst, double adj)
or it is not in a local network according to the measured delay */
if (inst->local_poll < MIN_NONLAN_POLL &&
(!SRC_IsReachable(inst->source) ||
SST_MinRoundTripDelay(SRC_GetSourcestats(inst->source)) > MAX_LAN_PEER_DELAY))
(SST_MinRoundTripDelay(SRC_GetSourcestats(inst->source)) > MAX_LAN_PEER_DELAY &&
(!inst->filter || !SPF_GetLastSample(inst->filter, &last_sample) ||
last_sample.peer_delay > MAX_LAN_PEER_DELAY))))
inst->local_poll = MIN_NONLAN_POLL;
}
@@ -914,12 +991,48 @@ receive_timeout(void *arg)
/* ================================================== */
static int
add_ext_exp1(NTP_Packet *message, NTP_PacketInfo *info, struct timespec *rx,
double root_delay, double root_dispersion)
{
struct timespec mono_rx;
NTP_ExtFieldExp1 exp1;
NTP_int64 ts_fuzz;
memset(&exp1, 0, sizeof (exp1));
exp1.magic = htonl(NTP_EF_EXP1_MAGIC);
if (info->mode != MODE_CLIENT) {
exp1.root_delay = UTI_DoubleToNtp32f28(root_delay);
exp1.root_dispersion = UTI_DoubleToNtp32f28(root_dispersion);
if (rx)
UTI_AddDoubleToTimespec(rx, server_mono_offset, &mono_rx);
else
UTI_ZeroTimespec(&mono_rx);
UTI_GetNtp64Fuzz(&ts_fuzz, message->precision);
UTI_TimespecToNtp64(&mono_rx, &exp1.mono_receive_ts, &ts_fuzz);
exp1.mono_epoch = htonl(server_mono_epoch);
}
if (!NEF_AddField(message, info, NTP_EF_EXP1, &exp1, sizeof (exp1))) {
DEBUG_LOG("Could not add EF");
return 0;
}
info->ext_field_flags |= NTP_EF_FLAG_EXP1;
return 1;
}
/* ================================================== */
static int
transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
int interleaved, /* Flag enabling interleaved mode */
int my_poll, /* The log2 of the local poll interval */
int version, /* The NTP version to be set in the packet */
uint32_t kod, /* KoD code - 0 disabled */
int ext_field_flags, /* Extension fields to be included in the packet */
NAU_Instance auth, /* The authentication to be used for the packet */
NTP_int64 *remote_ntp_rx, /* The receive timestamp from received packet */
NTP_int64 *remote_ntp_tx, /* The transmit timestamp from received packet */
@@ -1054,10 +1167,18 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
UTI_ZeroNtp64(&message.receive_ts);
}
do {
if (!parse_packet(&message, NTP_HEADER_LENGTH, &info))
return 0;
if (!parse_packet(&message, NTP_HEADER_LENGTH, &info))
return 0;
if (ext_field_flags) {
if (ext_field_flags & NTP_EF_FLAG_EXP1) {
if (!add_ext_exp1(&message, &info, smooth_time ? NULL : &local_receive,
our_root_delay, our_root_dispersion))
return 0;
}
}
do {
/* Prepare random bits which will be added to the transmit timestamp */
UTI_GetNtp64Fuzz(&ts_fuzz, precision);
@@ -1072,20 +1193,6 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
UTI_TimespecToNtp64(interleaved ? &local_tx->ts : &local_transmit,
&message.transmit_ts, &ts_fuzz);
/* Generate the authentication data */
if (auth) {
if (!NAU_GenerateRequestAuth(auth, &message, &info)) {
DEBUG_LOG("Could not generate request auth");
return 0;
}
} else {
if (!NAU_GenerateResponseAuth(request, request_info, &message, &info,
where_to, from, kod)) {
DEBUG_LOG("Could not generate response auth");
return 0;
}
}
/* Do not send a packet with a non-zero transmit timestamp which is
equal to any of the following timestamps:
- receive (to allow reliable detection of the interleaved mode)
@@ -1097,6 +1204,27 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
UTI_IsEqualAnyNtp64(&message.transmit_ts, &message.receive_ts,
&message.originate_ts, local_ntp_tx));
/* Encode in server timestamps a flag indicating RX timestamp to avoid
saving all RX timestamps for detection of interleaved requests */
if (my_mode == MODE_SERVER || my_mode == MODE_PASSIVE) {
message.receive_ts.lo |= htonl(1);
message.transmit_ts.lo &= ~htonl(1);
}
/* Generate the authentication data */
if (auth) {
if (!NAU_GenerateRequestAuth(auth, &message, &info)) {
DEBUG_LOG("Could not generate request auth");
return 0;
}
} else {
if (!NAU_GenerateResponseAuth(request, request_info, &message, &info,
where_to, from, kod)) {
DEBUG_LOG("Could not generate response auth");
return 0;
}
}
if (request_info && request_info->length < info.length) {
DEBUG_LOG("Response longer than request req_len=%d res_len=%d",
request_info->length, info.length);
@@ -1228,7 +1356,7 @@ transmit_timeout(void *arg)
/* Send the request (which may also be a response in the symmetric mode) */
sent = transmit_packet(inst->mode, interleaved, inst->local_poll, inst->version, 0,
inst->auth,
inst->ext_field_flags, inst->auth,
initial ? NULL : &inst->remote_ntp_rx,
initial ? &inst->init_remote_ntp_tx : &inst->remote_ntp_tx,
initial ? &inst->init_local_rx : &inst->local_rx,
@@ -1251,6 +1379,9 @@ transmit_timeout(void *arg)
}
SRC_UpdateReachability(inst->source, 0);
/* Count missing samples for the sample filter */
process_sample(inst, NULL);
}
/* With auto_offline take the source offline if sending failed */
@@ -1285,9 +1416,26 @@ transmit_timeout(void *arg)
/* ================================================== */
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 int
parse_packet(NTP_Packet *packet, int length, NTP_PacketInfo *info)
{
int parsed, remainder, ef_length, ef_type, ef_body_length;
unsigned char *data;
void *ef_body;
if (length < NTP_HEADER_LENGTH || length % 4U != 0) {
DEBUG_LOG("NTP packet has invalid length %d", length);
return 0;
@@ -1297,6 +1445,7 @@ parse_packet(NTP_Packet *packet, int length, NTP_PacketInfo *info)
info->version = NTP_LVM_TO_VERSION(packet->lvm);
info->mode = NTP_LVM_TO_MODE(packet->lvm);
info->ext_fields = 0;
info->ext_field_flags = 0;
info->auth.mode = NTP_AUTH_NONE;
if (info->version < NTP_MIN_COMPAT_VERSION || info->version > NTP_MAX_COMPAT_VERSION) {
@@ -1304,11 +1453,95 @@ parse_packet(NTP_Packet *packet, int length, NTP_PacketInfo *info)
return 0;
}
/* Parse authentication extension fields or MAC */
if (!NAU_ParsePacket(packet, info))
return 0;
data = (void *)packet;
parsed = NTP_HEADER_LENGTH;
remainder = info->length - parsed;
return 1;
/* 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, &ef_body, &ef_body_length)) {
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;
case NTP_EF_EXP1:
if (ef_body_length == sizeof (NTP_ExtFieldExp1) &&
ntohl(((NTP_ExtFieldExp1 *)ef_body)->magic) == NTP_EF_EXP1_MAGIC)
info->ext_field_flags |= NTP_EF_FLAG_EXP1;
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;
}
/* ================================================== */
@@ -1337,6 +1570,22 @@ check_delay_ratio(NCR_Instance inst, SST_Stats stats,
/* ================================================== */
static int
check_delay_quant(NCR_Instance inst, double delay)
{
double quant;
quant = QNT_GetQuantile(inst->delay_quant, QNT_GetMinK(inst->delay_quant));
if (delay <= quant)
return 1;
DEBUG_LOG("maxdelayquant: delay=%e quant=%e", delay, quant);
return 0;
}
/* ================================================== */
static int
check_delay_dev_ratio(NCR_Instance inst, SST_Stats stats,
struct timespec *sample_time, double offset, double delay)
@@ -1422,33 +1671,29 @@ check_sync_loop(NCR_Instance inst, NTP_Packet *message, NTP_Local_Address *local
static void
process_sample(NCR_Instance inst, NTP_Sample *sample)
{
double estimated_offset, error_in_estimate, filtered_sample_ago;
double estimated_offset, error_in_estimate;
NTP_Sample filtered_sample;
int filtered_samples;
/* Accumulate the sample to the median filter if it is enabled. When the
filter produces a result, check if it is not too old, i.e. the filter did
not miss too many samples due to missing responses or failing tests. */
/* Accumulate the sample to the median filter if enabled and wait for
the configured number of samples before processing (NULL indicates
a missing sample) */
if (inst->filter) {
SPF_AccumulateSample(inst->filter, sample);
if (sample)
SPF_AccumulateSample(inst->filter, sample);
filtered_samples = SPF_GetNumberOfSamples(inst->filter);
if (++inst->filter_count < SPF_GetMaxSamples(inst->filter))
return;
if (!SPF_GetFilteredSample(inst->filter, &filtered_sample))
return;
filtered_sample_ago = UTI_DiffTimespecsToDouble(&sample->time, &filtered_sample.time);
if (filtered_sample_ago > SOURCE_REACH_BITS / 2 * filtered_samples *
UTI_Log2ToDouble(inst->local_poll)) {
DEBUG_LOG("filtered sample dropped ago=%f poll=%d", filtered_sample_ago,
inst->local_poll);
return;
}
sample = &filtered_sample;
inst->filter_count = 0;
}
if (!sample)
return;
/* Get the estimated offset predicted from previous samples. The
convention here is that positive means local clock FAST of
reference, i.e. backwards to the way that 'offset' is defined. */
@@ -1456,6 +1701,12 @@ process_sample(NCR_Instance inst, NTP_Sample *sample)
error_in_estimate = fabs(-sample->offset - estimated_offset);
if (inst->mono_doffset != 0.0 && fabs(inst->mono_doffset) <= MAX_MONO_DOFFSET) {
DEBUG_LOG("Monotonic correction offset=%.9f", inst->mono_doffset);
SST_CorrectOffset(SRC_GetSourcestats(inst->source), inst->mono_doffset);
}
inst->mono_doffset = 0.0;
SRC_AccumulateSample(inst->source, sample);
SRC_SelectSource(inst->source);
@@ -1491,20 +1742,50 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
/* Kiss-o'-Death codes */
int kod_rate;
/* Extension fields */
int parsed, ef_length, ef_type, ef_body_length;
void *ef_body;
NTP_ExtFieldExp1 *ef_exp1;
NTP_Local_Timestamp local_receive, local_transmit;
double remote_interval, local_interval, response_time;
double delay_time, precision;
double delay_time, precision, mono_doffset;
int updated_timestamps;
/* ==================== */
stats = SRC_GetSourcestats(inst->source);
ef_exp1 = NULL;
/* Find requested non-authentication extension fields */
if (inst->ext_field_flags & info->ext_field_flags) {
for (parsed = NTP_HEADER_LENGTH; parsed < info->length; parsed += ef_length) {
if (!NEF_ParseField(message, info->length, parsed,
&ef_length, &ef_type, &ef_body, &ef_body_length))
break;
switch (ef_type) {
case NTP_EF_EXP1:
if (inst->ext_field_flags & NTP_EF_FLAG_EXP1 &&
ef_body_length == sizeof (*ef_exp1) &&
ntohl(((NTP_ExtFieldExp1 *)ef_body)->magic) == NTP_EF_EXP1_MAGIC)
ef_exp1 = ef_body;
break;
}
}
}
pkt_leap = NTP_LVM_TO_LEAP(message->lvm);
pkt_version = NTP_LVM_TO_VERSION(message->lvm);
pkt_refid = ntohl(message->reference_id);
pkt_root_delay = UTI_Ntp32ToDouble(message->root_delay);
pkt_root_dispersion = UTI_Ntp32ToDouble(message->root_dispersion);
if (ef_exp1) {
pkt_root_delay = UTI_Ntp32f28ToDouble(ef_exp1->root_delay);
pkt_root_dispersion = UTI_Ntp32f28ToDouble(ef_exp1->root_dispersion);
} else {
pkt_root_delay = UTI_Ntp32ToDouble(message->root_delay);
pkt_root_dispersion = UTI_Ntp32ToDouble(message->root_dispersion);
}
/* Check if the packet is valid per RFC 5905, section 8.
The test values are 1 when passed and 0 when failed. */
@@ -1559,7 +1840,27 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
/* These are the timespec equivalents of the remote and local epochs */
struct timespec remote_receive, remote_transmit, remote_request_receive;
struct timespec local_average, remote_average, prev_remote_transmit;
double prev_remote_poll_interval;
double prev_remote_poll_interval, root_delay, root_dispersion;
/* If the remote monotonic timestamps are available and are from the same
epoch, calculate the change in the offset between the monotonic and
real-time clocks, i.e. separate the source's time corrections from
frequency corrections. The offset is accumulated between measurements.
It will correct old measurements kept in sourcestats before accumulating
the new sample. In the interleaved mode, cancel the correction out in
remote timestamps of the previous request and response, which were
captured before the source accumulated the new time corrections. */
if (ef_exp1 && inst->remote_mono_epoch == ntohl(ef_exp1->mono_epoch) &&
!UTI_IsZeroNtp64(&ef_exp1->mono_receive_ts) &&
!UTI_IsZeroNtp64(&inst->remote_ntp_monorx)) {
mono_doffset =
UTI_DiffNtp64ToDouble(&ef_exp1->mono_receive_ts, &inst->remote_ntp_monorx) -
UTI_DiffNtp64ToDouble(&message->receive_ts, &inst->remote_ntp_rx);
if (fabs(mono_doffset) > MAX_MONO_DOFFSET)
mono_doffset = 0.0;
} else {
mono_doffset = 0.0;
}
/* Select remote and local timestamps for the new sample */
if (interleaved_packet) {
@@ -1571,14 +1872,20 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
UTI_DiffTimespecsToDouble(&inst->local_tx.ts, &inst->local_rx.ts) >
UTI_DiffTimespecsToDouble(&inst->local_rx.ts, &inst->prev_local_tx.ts)) {
UTI_Ntp64ToTimespec(&inst->remote_ntp_rx, &remote_receive);
UTI_AddDoubleToTimespec(&remote_receive, -mono_doffset, &remote_receive);
remote_request_receive = remote_receive;
local_transmit = inst->prev_local_tx;
root_delay = inst->remote_root_delay;
root_dispersion = inst->remote_root_dispersion;
} else {
UTI_Ntp64ToTimespec(&message->receive_ts, &remote_receive);
UTI_Ntp64ToTimespec(&inst->remote_ntp_rx, &remote_request_receive);
local_transmit = inst->local_tx;
root_delay = MAX(pkt_root_delay, inst->remote_root_delay);
root_dispersion = MAX(pkt_root_dispersion, inst->remote_root_dispersion);
}
UTI_Ntp64ToTimespec(&message->transmit_ts, &remote_transmit);
UTI_AddDoubleToTimespec(&remote_transmit, -mono_doffset, &remote_transmit);
UTI_Ntp64ToTimespec(&inst->remote_ntp_tx, &prev_remote_transmit);
local_receive = inst->local_rx;
} else {
@@ -1588,6 +1895,8 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
remote_request_receive = remote_receive;
local_receive = *rx_ts;
local_transmit = inst->local_tx;
root_delay = pkt_root_delay;
root_dispersion = pkt_root_dispersion;
}
/* Calculate intervals between remote and local timestamps */
@@ -1624,9 +1933,11 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
/* Calculate skew */
skew = (source_freq_hi - source_freq_lo) / 2.0;
/* and then calculate peer dispersion */
/* and then calculate peer dispersion and the rest of the sample */
sample.peer_dispersion = MAX(precision, MAX(local_transmit.err, local_receive.err)) +
skew * fabs(local_interval);
sample.root_delay = root_delay + sample.peer_delay;
sample.root_dispersion = root_dispersion + sample.peer_dispersion;
/* If the source is an active peer, this is the minimum assumed interval
between previous two transmissions (if not constrained by minpoll) */
@@ -1637,12 +1948,17 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
/* Test A requires that the minimum estimate of the peer delay is not
larger than the configured maximum, in both client modes that the server
processing time is sane, and in interleaved symmetric mode that the
processing time is sane, in interleaved client/server mode that the
previous response was not in basic mode (which prevents using timestamps
that minimise delay error), and in interleaved symmetric mode that the
measured delay and intervals between remote timestamps don't indicate
a missed response */
testA = sample.peer_delay - sample.peer_dispersion <= inst->max_delay &&
precision <= inst->max_delay &&
!(inst->mode == MODE_CLIENT && response_time > MAX_SERVER_INTERVAL) &&
!(inst->mode == MODE_CLIENT && interleaved_packet &&
UTI_IsZeroTimespec(&inst->prev_local_tx.ts) &&
UTI_CompareTimespecs(&local_transmit.ts, &inst->local_tx.ts) == 0) &&
!(inst->mode == MODE_ACTIVE && interleaved_packet &&
(sample.peer_delay > 0.5 * prev_remote_poll_interval ||
UTI_CompareNtp64(&message->receive_ts, &message->transmit_ts) <= 0 ||
@@ -1655,12 +1971,18 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
administrator-defined value */
testB = check_delay_ratio(inst, stats, &sample.time, sample.peer_delay);
/* Test C requires that the ratio of the increase in delay from the minimum
/* Test C either requires that the delay is less than an estimate of an
administrator-defined quantile, or (if the quantile is not specified)
it requires that the ratio of the increase in delay from the minimum
one in the stats data register to the standard deviation of the offsets
in the register is less than an administrator-defined value or the
difference between measured offset and predicted offset is larger than
the increase in delay */
testC = check_delay_dev_ratio(inst, stats, &sample.time, sample.offset, sample.peer_delay);
if (inst->delay_quant)
testC = check_delay_quant(inst, sample.peer_delay);
else
testC = check_delay_dev_ratio(inst, stats, &sample.time, sample.offset,
sample.peer_delay);
/* Test D requires that the source is not synchronised to us and is not us
to prevent a synchronisation loop */
@@ -1668,7 +1990,9 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
} else {
remote_interval = local_interval = response_time = 0.0;
sample.offset = sample.peer_delay = sample.peer_dispersion = 0.0;
sample.root_delay = sample.root_dispersion = 0.0;
sample.time = rx_ts->ts;
mono_doffset = 0.0;
local_receive = *rx_ts;
local_transmit = inst->local_tx;
testA = testB = testC = testD = 0;
@@ -1678,9 +2002,6 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
the additional tests passed */
good_packet = testA && testB && testC && testD;
sample.root_delay = pkt_root_delay + sample.peer_delay;
sample.root_dispersion = pkt_root_dispersion + sample.peer_dispersion;
/* Update the NTP timestamps. If it's a valid packet from a synchronised
source, the timestamps may be used later when processing a packet in the
interleaved mode. Protect the timestamps against replay attacks in client
@@ -1702,6 +2023,19 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
inst->updated_init_timestamps = 0;
updated_timestamps = 2;
/* If available, update the monotonic timestamp and accumulate the offset.
This needs to be done here to not lose changes in remote_ntp_rx in
symmetric mode when there are multiple responses per request. */
if (ef_exp1 && !UTI_IsZeroNtp64(&ef_exp1->mono_receive_ts)) {
inst->remote_mono_epoch = ntohl(ef_exp1->mono_epoch);
inst->remote_ntp_monorx = ef_exp1->mono_receive_ts;
inst->mono_doffset += mono_doffset;
} else {
inst->remote_mono_epoch = 0;
UTI_ZeroNtp64(&inst->remote_ntp_monorx);
inst->mono_doffset = 0.0;
}
/* Don't use the same set of timestamps for the next sample */
if (interleaved_packet)
inst->prev_local_tx = inst->local_tx;
@@ -1738,10 +2072,11 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
(unsigned int)local_transmit.source >= sizeof (tss_chars))
assert(0);
DEBUG_LOG("NTP packet lvm=%o stratum=%d poll=%d prec=%d root_delay=%f root_disp=%f refid=%"PRIx32" [%s]",
DEBUG_LOG("NTP packet lvm=%o stratum=%d poll=%d prec=%d root_delay=%.9f root_disp=%.9f refid=%"PRIx32" [%s]",
message->lvm, message->stratum, message->poll, message->precision,
pkt_root_delay, pkt_root_dispersion, pkt_refid,
message->stratum == NTP_INVALID_STRATUM ? UTI_RefidToString(pkt_refid) : "");
message->stratum == NTP_INVALID_STRATUM || message->stratum == 1 ?
UTI_RefidToString(pkt_refid) : "");
DEBUG_LOG("reference=%s origin=%s receive=%s transmit=%s",
UTI_Ntp64ToString(&message->reference_ts),
UTI_Ntp64ToString(&message->originate_ts),
@@ -1750,8 +2085,8 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
DEBUG_LOG("offset=%.9f delay=%.9f dispersion=%f root_delay=%f root_dispersion=%f",
sample.offset, sample.peer_delay, sample.peer_dispersion,
sample.root_delay, sample.root_dispersion);
DEBUG_LOG("remote_interval=%.9f local_interval=%.9f response_time=%.9f txs=%c rxs=%c",
remote_interval, local_interval, response_time,
DEBUG_LOG("remote_interval=%.9f local_interval=%.9f response_time=%.9f mono_doffset=%.9f txs=%c rxs=%c",
remote_interval, local_interval, response_time, mono_doffset,
tss_chars[local_transmit.source], tss_chars[local_receive.source]);
DEBUG_LOG("test123=%d%d%d test567=%d%d%d testABCD=%d%d%d%d kod_rate=%d interleaved=%d"
" presend=%d valid=%d good=%d updated=%d",
@@ -1763,6 +2098,8 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
inst->remote_poll = message->poll;
inst->remote_stratum = message->stratum != NTP_INVALID_STRATUM ?
MIN(message->stratum, NTP_MAX_STRATUM) : NTP_MAX_STRATUM;
inst->remote_root_delay = pkt_root_delay;
inst->remote_root_dispersion = pkt_root_dispersion;
inst->prev_local_poll = inst->local_poll;
inst->prev_tx_count = inst->tx_count;
@@ -1778,6 +2115,9 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
}
SRC_UpdateStatus(inst->source, MAX(inst->remote_stratum, inst->min_stratum), pkt_leap);
if (inst->delay_quant)
QNT_Accumulate(inst->delay_quant, sample.peer_delay);
}
if (good_packet) {
@@ -1803,6 +2143,9 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
} else {
/* Slowly increase the polling interval if we can't get a good response */
adjust_poll(inst, testD ? 0.02 : 0.1);
/* Count missing samples for the sample filter */
process_sample(inst, NULL);
}
/* If in client mode, no more packets are expected to be coming from the
@@ -2041,8 +2384,8 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
{
NTP_PacketInfo info;
NTP_Mode my_mode;
NTP_int64 *local_ntp_rx, *local_ntp_tx;
NTP_Local_Timestamp local_tx, *tx_ts;
NTP_int64 ntp_rx, *local_ntp_rx;
int log_index, interleaved, poll, version;
uint32_t kod;
@@ -2105,28 +2448,29 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
CLG_LogAuthNtpRequest();
}
local_ntp_rx = local_ntp_tx = NULL;
local_ntp_rx = NULL;
tx_ts = NULL;
interleaved = 0;
/* Check if the client is using the interleaved mode. If it is, save the
new transmit timestamp and if the old transmit timestamp is valid, respond
in the interleaved mode. This means the third reply to a new client is
the earliest one that can be interleaved. We don't want to waste time
on clients that are not using the interleaved mode. */
if (kod == 0 && log_index >= 0) {
CLG_GetNtpTimestamps(log_index, &local_ntp_rx, &local_ntp_tx);
interleaved = !UTI_IsZeroNtp64(local_ntp_rx) &&
!UTI_CompareNtp64(&message->originate_ts, local_ntp_rx) &&
UTI_CompareNtp64(&message->receive_ts, &message->transmit_ts);
/* Handle requests formed in the interleaved mode. As an optimisation to
avoid saving all receive timestamps, require that the origin timestamp
has the lowest bit equal to 1, which indicates it was set to one of our
receive timestamps instead of transmit timestamps or zero. Respond in the
interleaved mode if the receive timestamp is found and it has a non-zero
transmit timestamp (this is verified in transmit_packet()). For a new
client starting with a zero origin timestamp, the third response is the
earliest one that can be interleaved. */
if (kod == 0 && log_index >= 0 && info.version == 4 &&
message->originate_ts.lo & htonl(1) &&
UTI_CompareNtp64(&message->receive_ts, &message->transmit_ts) != 0) {
ntp_rx = message->originate_ts;
local_ntp_rx = &ntp_rx;
UTI_ZeroTimespec(&local_tx.ts);
interleaved = CLG_GetNtpTxTimestamp(&ntp_rx, &local_tx.ts);
if (interleaved) {
UTI_Ntp64ToTimespec(local_ntp_tx, &local_tx.ts);
tx_ts = &local_tx;
} else {
UTI_ZeroNtp64(local_ntp_tx);
local_ntp_tx = NULL;
}
tx_ts = &local_tx;
if (interleaved)
CLG_DisableNtpTimestamps(&ntp_rx);
}
/* Suggest the client to increase its polling interval if it indicates
@@ -2138,14 +2482,14 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
version = info.version;
/* Send a reply */
transmit_packet(my_mode, interleaved, poll, version, kod, NULL,
&message->receive_ts, &message->transmit_ts,
rx_ts, tx_ts, local_ntp_rx, NULL, remote_addr, local_addr,
message, &info);
if (!transmit_packet(my_mode, interleaved, poll, version, kod, info.ext_field_flags, NULL,
&message->receive_ts, &message->transmit_ts,
rx_ts, tx_ts, local_ntp_rx, NULL, remote_addr, local_addr,
message, &info))
return;
/* Save the transmit timestamp */
if (tx_ts)
UTI_TimespecToNtp64(&tx_ts->ts, local_ntp_tx, NULL);
if (local_ntp_rx)
CLG_SaveNtpTimestamps(local_ntp_rx, tx_ts ? &tx_ts->ts : NULL);
}
/* ================================================== */
@@ -2207,10 +2551,9 @@ void
NCR_ProcessTxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *tx_ts, NTP_Packet *message, int length)
{
NTP_int64 *local_ntp_rx, *local_ntp_tx;
NTP_Local_Timestamp local_tx;
NTP_Local_Timestamp old_tx, new_tx;
NTP_int64 *local_ntp_rx;
NTP_PacketInfo info;
int log_index;
if (!parse_packet(message, length, &info))
return;
@@ -2218,18 +2561,22 @@ NCR_ProcessTxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
if (info.mode == MODE_BROADCAST)
return;
log_index = CLG_GetClientIndex(&remote_addr->ip_addr);
if (log_index < 0)
return;
if (SMT_IsEnabled() && info.mode == MODE_SERVER)
UTI_AddDoubleToTimespec(&tx_ts->ts, SMT_GetOffset(&tx_ts->ts), &tx_ts->ts);
CLG_GetNtpTimestamps(log_index, &local_ntp_rx, &local_ntp_tx);
local_ntp_rx = &message->receive_ts;
new_tx = *tx_ts;
UTI_Ntp64ToTimespec(local_ntp_tx, &local_tx.ts);
update_tx_timestamp(&local_tx, tx_ts, local_ntp_rx, NULL, message);
UTI_TimespecToNtp64(&local_tx.ts, local_ntp_tx, NULL);
if (!CLG_GetNtpTxTimestamp(local_ntp_rx, &old_tx.ts))
return;
/* Undo a clock adjustment between the RX and TX timestamps to minimise error
in the delay measured by the client */
CLG_UndoNtpTxTimestampSlew(local_ntp_rx, &new_tx.ts);
update_tx_timestamp(&old_tx, &new_tx, local_ntp_rx, NULL, message);
CLG_UpdateNtpTxTimestamp(local_ntp_rx, &new_tx.ts);
}
/* ================================================== */
@@ -2504,11 +2851,13 @@ NCR_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all)
if (server_sock_fd4 == INVALID_SOCK_FD &&
ADF_IsAnyAllowed(access_auth_table, IPADDR_INET4)) {
remote_addr.ip_addr.family = IPADDR_INET4;
remote_addr.port = 0;
server_sock_fd4 = NIO_OpenServerSocket(&remote_addr);
}
if (server_sock_fd6 == INVALID_SOCK_FD &&
ADF_IsAnyAllowed(access_auth_table, IPADDR_INET6)) {
remote_addr.ip_addr.family = IPADDR_INET6;
remote_addr.port = 0;
server_sock_fd6 = NIO_OpenServerSocket(&remote_addr);
}
} else {
@@ -2602,12 +2951,12 @@ broadcast_timeout(void *arg)
int poll;
destination = ARR_GetElement(broadcasts, (long)arg);
poll = log(destination->interval) / log(2.0) + 0.5;
poll = round(log(destination->interval) / log(2.0));
UTI_ZeroNtp64(&orig_ts);
zero_local_timestamp(&recv_ts);
transmit_packet(MODE_BROADCAST, 0, poll, NTP_VERSION, 0, destination->auth,
transmit_packet(MODE_BROADCAST, 0, poll, NTP_VERSION, 0, 0, destination->auth,
&orig_ts, &orig_ts, &recv_ts, NULL, NULL, NULL,
&destination->addr, &destination->local_addr, NULL, NULL);

159
ntp_io.c
View File

@@ -4,7 +4,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* 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
* it under the terms of version 2 of the GNU General Public License as
@@ -30,9 +30,11 @@
#include "sysincl.h"
#include "memory.h"
#include "ntp_io.h"
#include "ntp_core.h"
#include "ntp_sources.h"
#include "ptp.h"
#include "sched.h"
#include "socket.h"
#include "local.h"
@@ -70,6 +72,16 @@ static int permanent_server_sockets;
/* Flag indicating the server IPv4 socket is bound to an address */
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 */
static int initialised=0;
@@ -221,6 +233,17 @@ NIO_Initialise(void)
client_sock_fd4 == INVALID_SOCK_FD && client_sock_fd6 == INVALID_SOCK_FD)) {
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);
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
NIO_Linux_Finalise();
#endif
@@ -250,17 +278,21 @@ NIO_Finalise(void)
int
NIO_OpenClientSocket(NTP_Remote_Address *remote_addr)
{
if (separate_client_sockets) {
return open_separate_client_socket(remote_addr);
} else {
switch (remote_addr->ip_addr.family) {
case IPADDR_INET4:
return client_sock_fd4;
case IPADDR_INET6:
return client_sock_fd6;
default:
return INVALID_SOCK_FD;
}
switch (remote_addr->ip_addr.family) {
case IPADDR_INET4:
if (ptp_port > 0 && remote_addr->port == ptp_port)
return ptp_sock_fd4;
if (separate_client_sockets)
return open_separate_client_socket(remote_addr);
return client_sock_fd4;
case IPADDR_INET6:
if (ptp_port > 0 && remote_addr->port == ptp_port)
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) {
case IPADDR_INET4:
if (ptp_port > 0 && remote_addr->port == ptp_port)
return ptp_sock_fd4;
if (permanent_server_sockets)
return server_sock_fd4;
if (server_sock_fd4 == INVALID_SOCK_FD)
@@ -279,6 +313,8 @@ NIO_OpenServerSocket(NTP_Remote_Address *remote_addr)
server_sock_ref4++;
return server_sock_fd4;
case IPADDR_INET6:
if (ptp_port > 0 && remote_addr->port == ptp_port)
return ptp_sock_fd6;
if (permanent_server_sockets)
return server_sock_fd6;
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
NIO_CloseClientSocket(int sock_fd)
{
if (is_ptp_socket(sock_fd))
return;
if (separate_client_sockets)
close_socket(sock_fd);
}
@@ -305,7 +353,7 @@ NIO_CloseClientSocket(int sock_fd)
void
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;
if (sock_fd == server_sock_fd4) {
@@ -329,7 +377,7 @@ int
NIO_IsServerSocket(int 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
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",
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 */
if (message->length < NTP_HEADER_LENGTH || message->length > sizeof (NTP_Packet)) {
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);
}
/* ================================================== */
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 */
@@ -451,6 +575,9 @@ NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
message.data = packet;
message.length = length;
if (!wrap_message(&message, local_addr->sock_fd))
return 0;
/* Specify remote address if the socket is not connected */
if (NIO_IsServerSocket(local_addr->sock_fd) || !separate_client_sockets) {
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)
/* On FreeBSD a local IPv4 address cannot be specified on bound socket */
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;
#endif

View File

@@ -31,6 +31,7 @@
#include "ntp.h"
#include "addressing.h"
#include "socket.h"
/* Function to initialise the module. */
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 */
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 */
extern int NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
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 */
int l2_udp4_ntp_start;
int l2_udp6_ntp_start;
/* Precision of PHC readings */
double precision;
/* Compensation of errors in TX and RX timestamping */
double tx_comp;
double rx_comp;
@@ -68,12 +66,12 @@ struct Interface {
};
/* Number of PHC readings per HW clock sample */
#define PHC_READINGS 10
#define PHC_READINGS 25
/* Minimum interval between PHC readings */
#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
/* Array of Interfaces */
@@ -189,6 +187,14 @@ add_interface(CNF_HwTsInterface *conf_iface)
rx_filter = HWTSTAMP_FILTER_NTP_ALL;
break;
#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:
rx_filter = HWTSTAMP_FILTER_ALL;
break;
@@ -236,12 +242,12 @@ add_interface(CNF_HwTsInterface *conf_iface)
iface->l2_udp4_ntp_start = 42;
iface->l2_udp6_ntp_start = 62;
iface->precision = conf_iface->precision;
iface->tx_comp = conf_iface->tx_comp;
iface->rx_comp = conf_iface->rx_comp;
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",
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)
{
struct timespec sample_phc_ts, sample_sys_ts, sample_local_ts, ts;
struct timespec phc_readings[PHC_READINGS][3];
double rx_correction, ts_delay, phc_err, local_err;
int n_readings;
if (HCL_NeedsNewSample(iface->clock, &local_ts->ts)) {
if (!SYS_Linux_GetPHCSample(iface->phc_fd, iface->phc_nocrossts, iface->precision,
&iface->phc_mode, &sample_phc_ts, &sample_sys_ts,
&phc_err))
return;
n_readings = SYS_Linux_GetPHCReadings(iface->phc_fd, iface->phc_nocrossts,
&iface->phc_mode, PHC_READINGS, phc_readings);
if (n_readings > 0 &&
HCL_ProcessReadings(iface->clock, n_readings, phc_readings,
&sample_phc_ts, &sample_sys_ts, &phc_err)) {
LCL_CookTime(&sample_sys_ts, &sample_local_ts, &local_err);
HCL_AccumulateSample(iface->clock, &sample_phc_ts, &sample_local_ts,
phc_err + local_err);
LCL_CookTime(&sample_sys_ts, &sample_local_ts, &local_err);
HCL_AccumulateSample(iface->clock, &sample_phc_ts, &sample_local_ts,
phc_err + local_err);
update_interface_speed(iface);
update_interface_speed(iface);
}
}
/* 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;
}
/* ================================================== */
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
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) &&
(!is_tx || UTI_IsZeroTimespec(&message->timestamp.hw))) {
LCL_CookTime(&message->timestamp.kernel, &local_ts->ts, &local_ts->err);
local_ts->source = NTP_TS_KERNEL;
process_sw_timestamp(&message->timestamp.kernel, local_ts);
}
/* 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;
}
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;
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) 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
* it under the terms of version 2 of the GNU General Public License as
@@ -353,7 +353,6 @@ add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type,
record_lock = 1;
record = get_record(slot);
assert(!name || !UTI_IsStringIP(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);
@@ -698,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_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
SourceParameters *params, uint32_t *conf_id)
{
NSR_Status s;
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;
return add_source(remote_addr, NULL, type, params, INVALID_POOL,
get_next_conf_id(conf_id));
}
/* ================================================== */
@@ -725,11 +728,13 @@ NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
struct SourcePool *sp;
NTP_Remote_Address remote_addr;
int i, new_sources, pool_id;
uint32_t cid;
/* If the name is an IP address, add the source with the address directly */
if (UTI_StringToIP(name, &remote_addr.ip_addr)) {
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 */
@@ -770,14 +775,12 @@ NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
append_unresolved_source(us);
last_conf_id++;
if (conf_id)
*conf_id = last_conf_id;
cid = get_next_conf_id(conf_id);
for (i = 0; i < new_sources; i++) {
if (i > 0)
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;
}
@@ -1100,8 +1103,10 @@ NSR_ProcessRx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
assert(initialised);
/* Must match IP address AND port number */
if (find_slot2(remote_addr, &slot) == 2) {
/* Avoid unnecessary lookup if the packet cannot be a response from our
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);
if (!NCR_ProcessRxKnown(record->data, local_addr, rx_ts, message, length))
@@ -1137,8 +1142,10 @@ NSR_ProcessTx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
SourceRecord *record;
int slot;
/* Must match IP address AND port number */
if (find_slot2(remote_addr, &slot) == 2) {
/* Avoid unnecessary lookup if the packet cannot be a request to our
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);
NCR_ProcessTxKnown(record->data, local_addr, tx_ts, message, length);
} else {

View File

@@ -2,7 +2,7 @@
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
* it under the terms of version 2 of the GNU General Public License as
@@ -127,9 +127,10 @@ process_response(NKC_Instance inst)
{
int next_protocol = -1, aead_algorithm = -1, error = 0;
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);
inst->num_cookies = 0;
@@ -141,6 +142,13 @@ process_response(NKC_Instance inst)
if (!NKSN_GetRecord(inst->session, &critical, &type, &length, &data, sizeof (data)))
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) {
case NKE_RECORD_NEXT_PROTOCOL:
if (!critical || length != 2 || ntohs(data[0]) != NKE_NEXT_PROTOCOL_NTPV4) {

View File

@@ -669,6 +669,8 @@ run_helper(uid_t uid, gid_t gid, int scfilter_level)
CNF_Finalise();
LOG_Finalise();
UTI_ResetGetRandomFunctions();
exit(0);
}
@@ -710,6 +712,8 @@ NKS_PreInitialise(uid_t uid, gid_t gid, int scfilter_level)
is_helper = 1;
UTI_ResetGetRandomFunctions();
snprintf(prefix, sizeof (prefix), "nks#%d:", i + 1);
LOG_SetDebugPrefix(prefix);
LOG_CloseParentFd();

View File

@@ -2,7 +2,7 @@
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
* it under the terms of version 2 of the GNU General Public License as
@@ -594,13 +594,13 @@ handle_step(struct timespec *raw, struct timespec *cooked, double dfreq,
static int gnutls_initialised = 0;
static void
static int
init_gnutls(void)
{
int r;
if (gnutls_initialised)
return;
return 1;
r = gnutls_global_init();
if (r < 0)
@@ -611,8 +611,12 @@ init_gnutls(void)
r = gnutls_priority_init2(&priority_cache,
"-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.2:-VERS-DTLS-ALL",
NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND);
if (r < 0)
LOG_FATAL("Could not initialise %s : %s", "priority cache", gnutls_strerror(r));
if (r < 0) {
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 */
gnutls_global_set_time_function(get_time);
@@ -621,6 +625,8 @@ init_gnutls(void)
DEBUG_LOG("Initialised");
LCL_AddParameterChangeHandler(handle_step, NULL);
return 1;
}
/* ================================================== */
@@ -649,7 +655,8 @@ create_credentials(const char **certs, const char **keys, int n_certs_keys,
gnutls_certificate_credentials_t credentials = NULL;
int i, r;
init_gnutls();
if (!init_gnutls())
return NULL;
r = gnutls_certificate_allocate_credentials(&credentials);
if (r < 0)

View File

@@ -27,11 +27,6 @@
#ifndef 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 NTS_MIN_UNIQ_ID_LENGTH 32

View File

@@ -457,7 +457,7 @@ NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet,
for (parsed = NTP_HEADER_LENGTH; parsed < info->length; parsed += ef_length) {
if (!NEF_ParseField(packet, info->length, parsed,
&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;
switch (ef_type) {
@@ -669,6 +669,8 @@ load_cookies(NNC_Instance inst)
inst->last_nke_success = context_time + SCH_GetLastEventMonoTime();
inst->context_id = context_id;
fclose(f);
DEBUG_LOG("Loaded %d cookies for %s", i, filename);
return;

View File

@@ -242,7 +242,7 @@ NNS_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *req_info,
for (parsed = NTP_HEADER_LENGTH; parsed < req_info->length; parsed += ef_length) {
if (!NEF_ParseField(request, req_info->length, parsed,
&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;
switch (ef_type) {

View File

@@ -154,8 +154,9 @@ static const uint16_t reply_lengths[] = {
RPY_LENGTH_ENTRY(ntp_source_name), /* NTP_SOURCE_NAME */
RPY_LENGTH_ENTRY(auth_data), /* AUTH_DATA */
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(server_stats), /* SERVER_STATS3 */
};
/* ================================================== */

View File

@@ -255,7 +255,7 @@ do_bind_socket(ReqBindSocket *req, PrvResponse *res)
SCK_SockaddrToIPSockAddr(sa, sa_len, &ip_saddr);
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);
res_fatal(res, "Invalid port %d", ip_saddr.port);
return;
@@ -547,7 +547,7 @@ PRV_BindSocket(int sock, struct sockaddr *address, socklen_t address_len)
SCK_SockaddrToIPSockAddr(address, address_len, &ip_saddr);
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);
if (!have_helper())
@@ -662,6 +662,8 @@ PRV_StartHelper(void)
close(fd);
}
UTI_ResetGetRandomFunctions();
/* ignore signals, the process will exit on OP_QUIT request */
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

@@ -55,21 +55,6 @@ struct FilterSample {
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 {
RefclockDriver *driver;
void *data;
@@ -79,6 +64,7 @@ struct RCL_Instance_Record {
int driver_polled;
int poll;
int leap_status;
int local;
int pps_forced;
int pps_rate;
int pps_active;
@@ -190,6 +176,7 @@ RCL_AddRefclock(RefclockParameters *params)
inst->poll = params->poll;
inst->driver_polled = 0;
inst->leap_status = LEAP_Normal;
inst->local = params->local;
inst->pps_forced = params->pps_forced;
inst->pps_rate = params->pps_rate;
inst->pps_active = 0;
@@ -231,6 +218,13 @@ RCL_AddRefclock(RefclockParameters *params)
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) {
int max_samples;
@@ -300,7 +294,7 @@ RCL_StartRefclocks(void)
break;
}
if (lock_index == -1 || lock_index == i)
if (lock_index == -1 || (lock_index == i && !inst->local))
LOG(LOGS_WARN, "Invalid lock refid %s", UTI_RefidToString(inst->lock_ref));
}
@@ -440,20 +434,24 @@ accumulate_sample(RCL_Instance instance, struct timespec *sample_time, double of
}
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;
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);
UTI_AddDoubleToTimespec(sample_time, correction, &cooked_time);
dispersion += instance->precision;
/* 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))
return 0;
@@ -468,18 +466,24 @@ RCL_AddSample(RCL_Instance instance, struct timespec *sample_time, double offset
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)) {
DEBUG_LOG("refclock sample ignored unknown TAI offset");
return 0;
}
if (!accumulate_sample(instance, &cooked_time,
offset - correction + instance->offset, dispersion))
if (!accumulate_sample(instance, &cooked_time, offset, dispersion))
return 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 */
if (!instance->driver->poll)
@@ -558,24 +562,35 @@ RCL_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
lock_refclock = get_refclock(instance->lock_ref);
if (!SPF_GetLastSample(lock_refclock->filter, &ref_sample)) {
DEBUG_LOG("refclock pulse ignored no ref sample");
return 0;
if (instance->local) {
/* 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);
sample_diff = UTI_DiffTimespecsToDouble(cooked_time, &ref_sample.time);
if (fabs(sample_diff) >= (double)instance->max_lock_age / rate) {
DEBUG_LOG("refclock pulse ignored samplediff=%.9f",
sample_diff);
DEBUG_LOG("refclock pulse ignored samplediff=%.9f", 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;
}
/* Align the offset to the reference sample */
if ((ref_sample.offset - offset) >= 0.0)
shift = (long)((ref_sample.offset - offset) * rate + 0.5) / (double)rate;
else
shift = (long)((ref_sample.offset - offset) * rate - 0.5) / (double)rate;
shift = round((ref_sample.offset - offset) * rate) / rate;
offset += shift;
@@ -696,6 +711,52 @@ pps_stratum(RCL_Instance instance, struct timespec *ts)
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
poll_timeout(void *arg)
{
@@ -716,17 +777,28 @@ poll_timeout(void *arg)
inst->driver_polled = 0;
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_UpdateStatus(inst->source, stratum, inst->leap_status);
SRC_AccumulateSample(inst->source, &sample);
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);
} else {
SRC_UpdateReachability(inst->source, 0);

View File

@@ -37,6 +37,7 @@ typedef struct {
int driver_poll;
int poll;
int filter_length;
int local;
int pps_forced;
int pps_rate;
int min_samples;
@@ -74,7 +75,8 @@ extern void *RCL_GetDriverData(RCL_Instance instance);
extern char *RCL_GetDriverParameter(RCL_Instance instance);
extern void RCL_CheckDriverOptions(RCL_Instance instance, const char **options);
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_AddCookedPulse(RCL_Instance instance, struct timespec *cooked_time,
double second, double dispersion, double raw_correction);

View File

@@ -75,13 +75,15 @@ static int phc_initialise(RCL_Instance instance)
phc->nocrossts = RCL_GetDriverOption(instance, "nocrossts") ? 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) {
s = RCL_GetDriverOption(instance, "pin");
phc->pin = s ? atoi(s) : 0;
s = RCL_GetDriverOption(instance, "channel");
phc->channel = s ? atoi(s) : 0;
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,
rising_edge, !rising_edge, 1))
@@ -90,7 +92,6 @@ static int phc_initialise(RCL_Instance instance)
SCH_AddFileHandler(phc->fd, SCH_FILE_INPUT, read_ext_pulse, instance);
} else {
phc->pin = phc->channel = 0;
phc->clock = NULL;
}
RCL_SetDriverData(instance, phc);
@@ -106,9 +107,9 @@ static void phc_finalise(RCL_Instance instance)
if (phc->extpps) {
SCH_RemoveFileHandler(phc->fd);
SYS_Linux_SetPHCExtTimestamping(phc->fd, phc->pin, phc->channel, 0, 0, 0);
HCL_DestroyInstance(phc->clock);
}
HCL_DestroyInstance(phc->clock);
close(phc->fd);
Free(phc);
}
@@ -139,29 +140,35 @@ static void read_ext_pulse(int fd, int event, void *anything)
UTI_DiffTimespecsToDouble(&phc_ts, &local_ts));
}
#define PHC_READINGS 25
static int phc_poll(RCL_Instance instance)
{
struct timespec phc_ts, sys_ts, local_ts, readings[PHC_READINGS][3];
struct phc_instance *phc;
struct timespec phc_ts, sys_ts, local_ts;
double offset, phc_err, local_err;
double phc_err, local_err;
int n_readings;
phc = (struct phc_instance *)RCL_GetDriverData(instance);
if (!SYS_Linux_GetPHCSample(phc->fd, phc->nocrossts, RCL_GetPrecision(instance),
&phc->mode, &phc_ts, &sys_ts, &phc_err))
n_readings = SYS_Linux_GetPHCReadings(phc->fd, phc->nocrossts, &phc->mode,
PHC_READINGS, readings);
if (n_readings < 1)
return 0;
if (phc->extpps) {
LCL_CookTime(&sys_ts, &local_ts, &local_err);
HCL_AccumulateSample(phc->clock, &phc_ts, &local_ts, phc_err + local_err);
if (!HCL_ProcessReadings(phc->clock, n_readings, readings, &phc_ts, &sys_ts, &phc_err))
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 = {

View File

@@ -95,7 +95,6 @@ static int shm_poll(RCL_Instance instance)
{
struct timespec receive_ts, clock_ts;
struct shmTime t, *shm;
double offset;
shm = (struct shmTime *)RCL_GetDriverData(instance);
@@ -124,9 +123,8 @@ static int shm_poll(RCL_Instance instance)
UTI_NormaliseTimespec(&clock_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 = {

View File

@@ -60,8 +60,8 @@ struct sock_sample {
static void read_sample(int sockfd, int event, void *anything)
{
struct timespec sys_ts, ref_ts;
struct sock_sample sample;
struct timespec ts;
RCL_Instance instance;
int s;
@@ -86,13 +86,18 @@ static void read_sample(int sockfd, int event, void *anything)
return;
}
UTI_TimevalToTimespec(&sample.tv, &ts);
UTI_NormaliseTimespec(&ts);
UTI_TimevalToTimespec(&sample.tv, &sys_ts);
UTI_NormaliseTimespec(&sys_ts);
if (!UTI_IsTimeOffsetSane(&sys_ts, sample.offset))
return;
UTI_AddDoubleToTimespec(&sys_ts, sample.offset, &ref_ts);
if (sample.pulse) {
RCL_AddPulse(instance, &ts, sample.offset);
RCL_AddPulse(instance, &sys_ts, sample.offset);
} 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_interval;
static double last_ref_adjustment;
static int ref_adjustments;
/* ================================================== */
static NTP_Leap get_tz_leap(time_t when, int *tai_offset);
@@ -286,6 +289,8 @@ REF_Initialise(void)
UTI_ZeroTimespec(&our_ref_time);
last_ref_update = 0.0;
last_ref_update_interval = 0.0;
last_ref_adjustment = 0.0;
ref_adjustments = 0;
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
REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
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 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;
int manual;
@@ -1024,21 +1050,6 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
last_ref_update_interval = update_interval;
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 */
if (is_step_limit_reached(offset, uncorrected_offset)) {
/* 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 */
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);
@@ -1095,6 +1107,27 @@ REF_SetReference(int stratum, NTP_Leap leap, int combined_sources,
avg2_moving = 1;
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
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
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 */
extern void REF_UpdateLeapStatus(NTP_Leap leap);

View File

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

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 HIGHEST_MEASUREMENT_PERIOD 480
@@ -82,16 +82,12 @@ static int skip_interrupts;
#define MAX_SAMPLES 64
/* Real time clock samples. We store the seconds count as originally
measured, together with a 'trim' that compensates these values for
any steps made to the RTC to bring it back into line
occasionally. The trim is in seconds. */
measured. */
static time_t *rtc_sec = NULL;
static double *rtc_trim = NULL;
/* Reference time, against which delta times on the RTC scale are measured */
static time_t rtc_ref;
/* System clock samples associated with the above samples. */
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 */
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;
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));
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) */
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");
n_samples = 0;
}
/* Always use most recent sample as reference */
/* use sample only if n_sample is not negative*/
if(n_samples >=0)
{
rtc_ref = rtc;
rtc_sec[n_samples] = rtc;
rtc_trim[n_samples] = 0.0;
system_times[n_samples] = *sys;
++n_samples_since_regression;
}
++n_samples;
}
@@ -227,7 +217,7 @@ run_regression(int new_sample,
if (n_samples > 0) {
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) -
(1.0e-9 * system_times[i].tv_nsec) +
rtc_rel[i]);
@@ -434,6 +424,7 @@ setup_config(void)
static void
read_coefs_from_file(void)
{
double ref_time;
FILE *in;
if (!tried_to_load_coefs) {
@@ -444,11 +435,12 @@ read_coefs_from_file(void)
if (coefs_file_name &&
(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,
&file_ref_time,
&ref_time,
&file_ref_offset,
&file_rate_ppm) == 4) {
file_ref_time = ref_time;
} else {
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;
/* 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);
/* Rename the temporary file to the correct location */
@@ -525,7 +517,6 @@ RTC_Linux_Initialise(void)
UTI_FdSetCloexec(fd);
rtc_sec = MallocArray(time_t, MAX_SAMPLES);
rtc_trim = MallocArray(double, MAX_SAMPLES);
system_times = MallocArray(struct timespec, MAX_SAMPLES);
/* Setup details depending on configuration options */
@@ -578,7 +569,6 @@ RTC_Linux_Finalise(void)
LCL_RemoveParameterChangeHandler(slew_samples, NULL);
Free(rtc_sec);
Free(rtc_trim);
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);
n_samples_since_regression = 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;
n_samples = 0;
read_coefs_from_file();
@@ -1028,8 +1014,7 @@ RTC_Linux_GetReport(RPT_RTC_Report *report)
report->n_samples = n_samples;
report->n_runs = n_runs;
if (n_samples > 1) {
report->span_seconds = ((rtc_sec[n_samples-1] - rtc_sec[0]) +
(long)(rtc_trim[n_samples-1] - rtc_trim[0]));
report->span_seconds = rtc_sec[n_samples - 1] - rtc_sec[0];
} else {
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
SPF_GetAvgSampleDispersion(SPF_Instance filter)
{
@@ -170,11 +178,21 @@ SPF_GetAvgSampleDispersion(SPF_Instance filter)
/* ================================================== */
void
SPF_DropSamples(SPF_Instance filter)
static void
drop_samples(SPF_Instance filter, int keep_last)
{
filter->index = -1;
filter->used = 0;
if (!keep_last)
filter->last = -1;
}
/* ================================================== */
void
SPF_DropSamples(SPF_Instance filter)
{
drop_samples(filter, 0);
}
/* ================================================== */
@@ -399,17 +417,40 @@ SPF_GetFilteredSample(SPF_Instance filter, NTP_Sample *sample)
n = select_samples(filter);
DEBUG_LOG("selected %d from %d samples", n, filter->used);
if (n < 1)
return 0;
if (!combine_selected_samples(filter, n, sample))
return 0;
SPF_DropSamples(filter);
drop_samples(filter, 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
@@ -418,18 +459,9 @@ SPF_SlewSamples(SPF_Instance filter, struct timespec *when, double dfreq, double
int i, first, last;
double delta_time;
if (filter->last < 0)
if (!get_first_last(filter, &first, &last))
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++) {
UTI_AdjustTimespec(&filter->samples[i].time, when, &filter->samples[i].time,
&delta_time, dfreq, doffset);
@@ -439,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
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_GetLastSample(SPF_Instance filter, NTP_Sample *sample);
extern int SPF_GetNumberOfSamples(SPF_Instance filter);
extern int SPF_GetMaxSamples(SPF_Instance filter);
extern double SPF_GetAvgSampleDispersion(SPF_Instance filter);
extern void SPF_DropSamples(SPF_Instance filter);
extern int SPF_GetFilteredSample(SPF_Instance filter, NTP_Sample *sample);
extern void SPF_SlewSamples(SPF_Instance filter, struct timespec *when,
double dfreq, double doffset);
extern void SPF_CorrectOffset(SPF_Instance filter, double doffset);
extern void SPF_AddDispersion(SPF_Instance filter, double dispersion);
#endif

View File

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

View File

@@ -40,6 +40,7 @@
#include "array.h"
#include "logging.h"
#include "privops.h"
#include "ptp.h"
#include "util.h"
#define INVALID_SOCK_FD (-4)
@@ -58,10 +59,16 @@ struct Message {
union sockaddr_all name;
struct iovec iov;
/* Buffer of sufficient length for all expected messages */
union {
NTP_Packet ntp_msg;
CMD_Request cmd_request;
CMD_Reply cmd_reply;
struct {
/* Extra space for Ethernet, IPv4/IPv6, and UDP headers in
timestamped messages received from the Linux error queue */
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;
/* Aligned buffer for control messages */
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;
memset(&saddr, 0, sizeof (saddr));
if (snprintf(saddr.un.sun_path, sizeof (saddr.un.sun_path), "%s", addr) >=
sizeof (saddr.un.sun_path)) {
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;
memset(&saddr, 0, sizeof (saddr));
if (snprintf(saddr.un.sun_path, sizeof (saddr.un.sun_path), "%s", addr) >=
sizeof (saddr.un.sun_path)) {
DEBUG_LOG("Unix socket path %s too long", addr);

View File

@@ -3,7 +3,7 @@
**********************************************************************
* 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
* it under the terms of version 2 of the GNU General Public License as
@@ -68,6 +68,7 @@ struct SelectInfo {
typedef enum {
SRC_OK, /* OK so far, not a final status! */
SRC_UNSELECTABLE, /* Has noselect option set */
SRC_UNSYNCHRONISED, /* Provides samples but not unsynchronised */
SRC_BAD_STATS, /* Doesn't have valid stats data */
SRC_BAD_DISTANCE, /* Has root distance longer than allowed maximum */
SRC_JITTERY, /* Had std dev larger than allowed maximum */
@@ -177,6 +178,8 @@ static double reselect_distance;
static double stratum_weight;
static double combine_limit;
static LOG_FileID logfileid;
/* Identifier of the dump file */
#define DUMP_IDENTIFIER "SRC0\n"
@@ -188,6 +191,7 @@ static void slew_sources(struct timespec *raw, struct timespec *cooked, double d
double doffset, LCL_ChangeType change_type, void *anything);
static void add_dispersion(double dispersion, void *anything);
static char *source_to_string(SRC_Instance inst);
static char get_status_char(SRC_Status status);
/* ================================================== */
/* Initialisation function */
@@ -207,6 +211,10 @@ void SRC_Initialise(void) {
LCL_AddParameterChangeHandler(slew_sources, 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;
}
/* ================================================== */
@@ -589,7 +597,7 @@ log_selection_source(const char *format, SRC_Instance inst)
name = source_to_string(inst);
ntp_name = inst->type == SRC_NTP ? NSR_GetName(inst->ip_addr) : NULL;
if (ntp_name)
if (ntp_name && strcmp(name, ntp_name) != 0)
snprintf(buf, sizeof (buf), "%s (%s)", name, ntp_name);
else
snprintf(buf, sizeof (buf), "%s", name);
@@ -639,13 +647,33 @@ source_to_string(SRC_Instance inst)
static void
mark_source(SRC_Instance inst, SRC_Status status)
{
struct timespec now;
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",
source_to_string(inst), (int)inst->status, (unsigned int)inst->sel_options,
(unsigned int)inst->reachability, inst->reachability_size, inst->updates,
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), get_status_char(inst->status),
(unsigned int)inst->sel_options, (unsigned int)inst->reachability,
inst->reachability_size, inst->updates,
inst->distant, (int)inst->leap, inst->leap_vote,
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);
}
/* ================================================== */
@@ -722,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;
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",
index, offset_weight, src_offset, src_offset_sd,
DEBUG_LOG("combining %s oweight=%e offset=%e osd=%e fweight=%e freq=%e fsd=%e skew=%e",
source_to_string(sources[index]), offset_weight, src_offset, src_offset_sd,
frequency_weight, src_frequency, src_frequency_sd, src_skew);
sum_offset_weight += offset_weight;
@@ -815,6 +843,12 @@ SRC_SelectSource(SRC_Instance updated_inst)
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;
SST_GetSelectionData(sources[i]->stats, &now,
&si->lo_limit, &si->hi_limit, &si->root_distance,
@@ -1642,6 +1676,8 @@ get_status_char(SRC_Status status)
switch (status) {
case SRC_UNSELECTABLE:
return 'N';
case SRC_UNSYNCHRONISED:
return 's';
case SRC_BAD_STATS:
return 'M';
case SRC_BAD_DISTANCE:

View File

@@ -3,7 +3,7 @@
**********************************************************************
* 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
* it under the terms of version 2 of the GNU General Public License as
@@ -211,8 +211,8 @@ SST_CreateInstance(uint32_t refid, IPAddr *addr, int min_samples, int max_sample
SST_Stats inst;
inst = MallocNew(struct SST_Stats_Record);
inst->min_samples = min_samples;
inst->max_samples = max_samples;
inst->max_samples = max_samples > 0 ? CLAMP(1, max_samples, MAX_SAMPLES) : MAX_SAMPLES;
inst->min_samples = CLAMP(1, min_samples, inst->max_samples);
inst->fixed_min_delay = min_delay;
inst->fixed_asymmetry = asymmetry;
@@ -698,7 +698,8 @@ SST_GetSelectionData(SST_Stats inst, struct timespec *now,
/* 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 */
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();
*select_ok = 1;
}
@@ -779,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
SST_AddDispersion(SST_Stats inst, double dispersion)
{
@@ -798,7 +815,7 @@ SST_PredictOffset(SST_Stats inst, struct timespec *when)
{
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
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 */
@@ -885,6 +902,7 @@ int
SST_LoadFromFile(SST_Stats inst, FILE *in)
{
int i, n_samples, arun;
struct timespec now;
double sample_time;
char line[256];
@@ -895,6 +913,8 @@ SST_LoadFromFile(SST_Stats inst, FILE *in)
SST_ResetInstance(inst);
LCL_ReadCookedTime(&now, NULL);
for (i = 0; i < n_samples; i++) {
if (!fgets(line, sizeof (line), in) ||
sscanf(line, "%lf %lf %lf %lf %lf %lf %lf",
@@ -903,8 +923,20 @@ SST_LoadFromFile(SST_Stats inst, FILE *in)
&inst->root_delays[i], &inst->root_dispersions[i]) != 7)
return 0;
if (!UTI_IsTimeOffsetSane(&now, sample_time - UTI_TimespecToDouble(&now)))
return 0;
/* Some resolution is lost in the double format, but that's ok */
UTI_DoubleToTimespec(sample_time, &inst->sample_times[i]);
/* Make sure the samples are sane and they are in order */
if (!UTI_IsTimeOffsetSane(&inst->sample_times[i], -inst->offsets[i]) ||
UTI_CompareTimespecs(&now, &inst->sample_times[i]) < 0 ||
!(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))
return 0;
}
inst->n_samples = n_samples;
@@ -955,6 +987,14 @@ SST_Samples(SST_Stats inst)
/* ================================================== */
int
SST_GetMinSamples(SST_Stats inst)
{
return inst->min_samples;
}
/* ================================================== */
void
SST_DoSourcestatsReport(SST_Stats inst, RPT_SourcestatsReport *report, struct timespec *now)
{

View File

@@ -102,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);
/* 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
into the local time. */
extern void SST_AddDispersion(SST_Stats inst, double dispersion);
@@ -129,6 +133,8 @@ extern void SST_DoSourcestatsReport(SST_Stats inst, RPT_SourcestatsReport *repor
extern int SST_Samples(SST_Stats inst);
extern int SST_GetMinSamples(SST_Stats inst);
extern double SST_GetJitterAsymmetry(SST_Stats inst);
#endif /* GOT_SOURCESTATS_H */

View File

@@ -55,11 +55,13 @@ typedef struct {
int nts;
int nts_port;
int copy;
int ext_fields;
uint32_t authkey;
uint32_t cert_set;
double max_delay;
double max_delay_ratio;
double max_delay_dev_ratio;
double max_delay_quant;
double min_delay;
double asymmetry;
double offset;

View File

@@ -73,13 +73,28 @@ static double slew_freq;
/* Time (raw) of last update of slewing frequency and offset */
static struct timespec slew_start;
/* Limits for the slew timeout */
#define MIN_SLEW_TIMEOUT 1.0
#define MAX_SLEW_TIMEOUT 1.0e4
/* Limits for the slew length */
#define MIN_SLEW_DURATION 1.0
#define MAX_SLEW_DURATION 1.0e4
/* Scheduler timeout ID for ending of the currently running slew */
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) */
static double correction_rate;
@@ -109,12 +124,7 @@ static void
handle_step(struct timespec *raw, struct timespec *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything)
{
if (change_type == LCL_ChangeUnknownStep) {
/* Reset offset and slewing */
slew_start = *raw;
offset_register = 0.0;
update_slew();
} else if (change_type == LCL_ChangeStep) {
if (change_type == LCL_ChangeStep) {
UTI_AddDoubleToTimespec(&slew_start, -doffset, &slew_start);
}
}
@@ -169,8 +179,8 @@ clamp_freq(double freq)
static void
update_slew(void)
{
double old_slew_freq, total_freq, corr_freq, duration, excess_duration;
struct timespec now, end_of_slew;
double old_slew_freq, total_freq, corr_freq, duration;
/* Remove currently running timeout */
SCH_RemoveTimeout(slew_timeout_id);
@@ -183,13 +193,25 @@ update_slew(void)
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) {
duration = MAX_SLEW_TIMEOUT;
duration = MAX_SLEW_DURATION;
} else {
duration = correction_rate / fabs(offset_register);
if (duration < MIN_SLEW_TIMEOUT)
duration = MIN_SLEW_TIMEOUT;
if (duration < MIN_SLEW_DURATION)
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
@@ -232,23 +254,25 @@ update_slew(void)
maximum timeout and try again on the next update. */
if (fabs(offset_register) < MIN_OFFSET_CORRECTION ||
offset_register * slew_freq <= 0.0) {
duration = MAX_SLEW_TIMEOUT;
duration = MAX_SLEW_DURATION;
} else {
duration = offset_register / slew_freq;
if (duration < MIN_SLEW_TIMEOUT)
duration = MIN_SLEW_TIMEOUT;
else if (duration > MAX_SLEW_TIMEOUT)
duration = MAX_SLEW_TIMEOUT;
if (duration < MIN_SLEW_DURATION)
duration = MIN_SLEW_DURATION;
else if (duration > MAX_SLEW_DURATION)
duration = MAX_SLEW_DURATION;
}
/* Restart timer for the next update */
UTI_AddDoubleToTimespec(&now, duration, &end_of_slew);
slew_timeout_id = SCH_AddTimeout(&end_of_slew, handle_end_of_slew, NULL);
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",
offset_register, correction_rate, base_freq, total_freq, slew_freq,
duration, slew_error);
DEBUG_LOG("slew offset=%e corr_rate=%e base_freq=%f total_freq=%f slew_freq=%e"
" duration=%f excess=%f slew_error=%e",
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)();
slew_freq = 0.0;
offset_register = 0.0;
slew_excess_duration = 0.0;
max_corr_freq = CNF_GetMaxSlewRate() / 1.0e6;

View File

@@ -97,21 +97,6 @@ static int have_setoffset;
updated in the kernel */
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 */
@@ -149,7 +134,7 @@ set_frequency(double freq_ppm)
double required_freq;
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
set by adjtimex() and a scaling constant (that depends on the internal
@@ -486,7 +471,7 @@ void check_seccomp_applicability(void)
void
SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context)
{
const int syscalls[] = {
const int allowed[] = {
/* Clock */
SCMP_SYS(adjtimex),
SCMP_SYS(clock_adjtime),
@@ -503,11 +488,18 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context)
/* Process */
SCMP_SYS(clone),
#ifdef __NR_clone3
SCMP_SYS(clone3),
#endif
SCMP_SYS(exit),
SCMP_SYS(exit_group),
SCMP_SYS(getpid),
SCMP_SYS(getrlimit),
SCMP_SYS(getuid),
SCMP_SYS(getuid32),
#ifdef __NR_rseq
SCMP_SYS(rseq),
#endif
SCMP_SYS(rt_sigaction),
SCMP_SYS(rt_sigreturn),
SCMP_SYS(rt_sigprocmask),
@@ -594,6 +586,7 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context)
#ifdef __NR_ppoll_time64
SCMP_SYS(ppoll_time64),
#endif
SCMP_SYS(pread64),
SCMP_SYS(pselect6),
#ifdef __NR_pselect6_time64
SCMP_SYS(pselect6_time64),
@@ -613,6 +606,22 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context)
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[] = {
AF_NETLINK, AF_UNIX, AF_INET,
#ifdef FEAT_IPV6
@@ -624,6 +633,9 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context)
{ SOL_IP, IP_PKTINFO }, { SOL_IP, IP_FREEBIND }, { SOL_IP, IP_TOS },
#ifdef FEAT_IPV6
{ SOL_IPV6, IPV6_V6ONLY }, { SOL_IPV6, IPV6_RECVPKTINFO },
#endif
#ifdef SO_BINDTODEVICE
{ SOL_SOCKET, SO_BINDTODEVICE },
#endif
{ SOL_SOCKET, SO_BROADCAST }, { SOL_SOCKET, SO_REUSEADDR },
#ifdef SO_REUSEPORT
@@ -662,31 +674,65 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context)
#endif
};
unsigned int default_action, deny_action;
scmp_filter_ctx *ctx;
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) {
/* Check if the chronyd configuration is supported */
check_seccomp_applicability();
/* Start the helper process, which will run without any seccomp filter. It
will be used for getaddrinfo(), for which it's difficult to maintain a
list of required system calls (with glibc it depends on what NSS modules
are installed and enabled on the system). */
PRV_StartHelper();
/* At level 1, start a helper process which will not have a seccomp filter.
It will be used for getaddrinfo(), for which it is difficult to maintain
a list of required system calls (with glibc it depends on what NSS
modules are installed and enabled on the system). */
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)
LOG_FATAL("Failed to initialize seccomp");
/* Add system calls that are always allowed */
for (i = 0; i < (sizeof (syscalls) / sizeof (*syscalls)); i++) {
if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, syscalls[i], 0) < 0)
goto add_failed;
if (default_action != SCMP_ACT_ALLOW) {
for (i = 0; i < sizeof (allowed) / sizeof (*allowed); i++) {
if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, allowed[i], 0) < 0)
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 */
for (i = 0; i < sizeof (socket_domains) / sizeof (*socket_domains); i++) {
if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 1,
@@ -696,10 +742,9 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context)
/* Allow selected socket options */
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_A2(SCMP_CMP_EQ, socket_options[i][1]),
SCMP_A4(SCMP_CMP_LE, sizeof (int))) < 0)
SCMP_A2(SCMP_CMP_EQ, socket_options[i][1])))
goto add_failed;
}
@@ -723,7 +768,8 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context)
if (seccomp_load(ctx) < 0)
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);
return;
@@ -748,73 +794,25 @@ SYS_Linux_CheckKernelVersion(int req_major, int req_minor)
#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
#define PHC_READINGS 10
static int
process_phc_readings(struct timespec ts[][3], int n, double precision,
struct timespec *phc_ts, struct timespec *sys_ts, double *err)
get_phc_readings(int phc_fd, int max_samples, struct timespec ts[][3])
{
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;
int i;
max_samples = CLAMP(0, max_samples, PTP_MAX_SAMPLES);
/* Silence valgrind */
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)) {
DEBUG_LOG("ioctl(%s) failed : %s", "PTP_SYS_OFFSET", strerror(errno));
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_nsec = sys_off.ts[i * 2].nsec;
ts[i][1].tv_sec = sys_off.ts[i * 2 + 1].sec;
@@ -823,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;
}
return process_phc_readings(ts, PHC_READINGS, precision, phc_ts, sys_ts, err);
return max_samples;
}
/* ================================================== */
static int
get_extended_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
struct timespec *sys_ts, double *err)
get_extended_phc_readings(int phc_fd, int max_samples, struct timespec ts[][3])
{
#ifdef PTP_SYS_OFFSET_EXTENDED
struct timespec ts[PHC_READINGS][3];
struct ptp_sys_offset_extended sys_off;
int i;
max_samples = CLAMP(0, max_samples, PTP_MAX_SAMPLES);
/* Silence valgrind */
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)) {
DEBUG_LOG("ioctl(%s) failed : %s", "PTP_SYS_OFFSET_EXTENDED", strerror(errno));
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_nsec = sys_off.ts[i][0].nsec;
ts[i][1].tv_sec = sys_off.ts[i][1].sec;
@@ -856,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;
}
return process_phc_readings(ts, PHC_READINGS, precision, phc_ts, sys_ts, err);
return max_samples;
#else
return 0;
#endif
@@ -865,12 +863,14 @@ get_extended_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
/* ================================================== */
static int
get_precise_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
struct timespec *sys_ts, double *err)
get_precise_phc_readings(int phc_fd, int max_samples, struct timespec ts[][3])
{
#ifdef PTP_SYS_OFFSET_PRECISE
struct ptp_sys_offset_precise sys_off;
if (max_samples < 1)
return 0;
/* Silence valgrind */
memset(&sys_off, 0, sizeof (sys_off));
@@ -880,11 +880,11 @@ get_precise_phc_sample(int phc_fd, double precision, struct timespec *phc_ts,
return 0;
}
phc_ts->tv_sec = sys_off.device.sec;
phc_ts->tv_nsec = sys_off.device.nsec;
sys_ts->tv_sec = sys_off.sys_realtime.sec;
sys_ts->tv_nsec = sys_off.sys_realtime.nsec;
*err = MAX(LCL_GetSysPrecisionAsQuantum(), precision);
ts[0][0].tv_sec = sys_off.sys_realtime.sec;
ts[0][0].tv_nsec = sys_off.sys_realtime.nsec;
ts[0][1].tv_sec = sys_off.device.sec;
ts[0][1].tv_nsec = sys_off.device.nsec;
ts[0][2] = ts[0][0];
return 1;
#else
@@ -928,23 +928,23 @@ SYS_Linux_OpenPHC(const char *path, int phc_index)
/* ================================================== */
int
SYS_Linux_GetPHCSample(int fd, int nocrossts, double precision, int *reading_mode,
struct timespec *phc_ts, struct timespec *sys_ts, double *err)
SYS_Linux_GetPHCReadings(int fd, int nocrossts, int *reading_mode, int max_readings,
struct timespec tss[][3])
{
if ((*reading_mode == 2 || !*reading_mode) && !nocrossts &&
get_precise_phc_sample(fd, precision, phc_ts, sys_ts, err)) {
int r = 0;
if ((*reading_mode == 2 || *reading_mode == 0) && !nocrossts &&
(r = get_precise_phc_readings(fd, max_readings, tss)) > 0) {
*reading_mode = 2;
return 1;
} else if ((*reading_mode == 3 || !*reading_mode) &&
get_extended_phc_sample(fd, precision, phc_ts, sys_ts, err)) {
} else if ((*reading_mode == 3 || *reading_mode == 0) &&
(r = get_extended_phc_readings(fd, max_readings, tss)) > 0) {
*reading_mode = 3;
return 1;
} else if ((*reading_mode == 1 || !*reading_mode) &&
get_phc_sample(fd, precision, phc_ts, sys_ts, err)) {
} else if ((*reading_mode == 1 || *reading_mode == 0) &&
(r = get_phc_readings(fd, max_readings, tss)) > 0) {
*reading_mode = 1;
return 1;
}
return 0;
return r;
}
/* ================================================== */
@@ -962,7 +962,7 @@ SYS_Linux_SetPHCExtTimestamping(int fd, int pin, int channel,
pin_desc.func = enable ? PTP_PF_EXTTS : PTP_PF_NONE;
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));
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_GetPHCSample(int fd, int nocrossts, double precision, int *reading_mode,
struct timespec *phc_ts, struct timespec *sys_ts, double *err);
extern int SYS_Linux_GetPHCReadings(int fd, int nocrossts, int *reading_mode, int max_readings,
struct timespec tss[][3]);
extern int SYS_Linux_SetPHCExtTimestamping(int fd, int pin, int channel,
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 "sysincl.h"
#include "logging.h"
#include "privops.h"
#include "sys_solaris.h"
#include "sys_timex.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
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 */
SYS_Timex_InitialiseWithFunctions(32500, 1.0 / 100, NULL, NULL, NULL,
0.0, 0.0, NULL, NULL);

View File

@@ -8,7 +8,8 @@ for opts in \
"--host-system=FreeBSD" \
"--without-nettle" \
"--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
./configure $opts
scan-build make "$@" || exit 1

View File

@@ -2,6 +2,8 @@
# Run the unit and simulation tests with different compiler sanitizers
# and under valgrind
valgrind_opts="--leak-check=full --errors-for-leak-kinds=definite"
cd ../..
if [ "$(uname -sm)" != "Linux x86_64" ]; then
@@ -13,16 +15,23 @@ fi
if [ "$ID" = "fedora" ]; then
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
echo
fi
touch Makefile
for CC in gcc clang; do
export CC
for extra_config_opts in \
"--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
pushd test/simulation/clknetsim || exit 1
make clean > /dev/null 2>&1
@@ -31,26 +40,21 @@ for CC in gcc clang; do
popd
for extra_config_opts in \
"--all-privops" \
"--disable-scfilter" \
"--without-gnutls" \
"--without-nettle" \
"--without-nettle --without-nss" \
"--without-nettle --without-nss --without-tomcrypt"; \
do
for CC in gcc clang; do
export CC
for san_options in "" "-fsanitize=address" "-fsanitize=memory"; do
export CFLAGS="-O2 -g -fsanitize=undefined -fsanitize=float-divide-by-zero -fno-sanitize-recover=undefined,float-divide-by-zero $san_options $arch_opts"
# clang msan doesn't work on i686 and otherwise requires patches
echo $CFLAGS | grep -q 'sanitize=memory' && continue
# build fails with clang ubsan on i686 (Fedora only?)
[ "$arch_opts" = "-m32" -a "$CC" = "clang" ] && continue
[ -n "$TEST_NO_M32_CLANG" -a "$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 CC=\"$CC\" CFLAGS=\"$CFLAGS\" ./configure $config_opts
@@ -67,22 +71,28 @@ for CC in gcc clang; do
make "$@" || exit 1
[ -n "$BUILD_TEST_ONLY" ] && continue
[ -n "$TEST_BUILD_ONLY" ] && continue
echo
pushd test/unit || exit 1
make "$@" || exit 1
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
make check || exit 1
fi
popd
[ -n "$TEST_UNIT_ONLY" ] && continue
echo
pushd test/simulation || exit 1
export CLKNETSIM_RANDOM_SEED=101
if [ "$arch_opts" = "" -a "$san_options" = "" ]; then
CLKNETSIM_CLIENT_WRAPPER=valgrind ./run -i 1 || exit 1
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

View File

@@ -3,38 +3,48 @@
. ./test.common
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
# work correctly with both 32-bit and 64-bit time_t
# Set the starting test date to 500 seconds before the second NTP era.
# 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')
run_test || test_fail
check_chronyd_exit || test_fail
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')
if awk "BEGIN {exit !($ntp_start <= $CLKNETSIM_START_DATE && \
$CLKNETSIM_START_DATE + $limit < $ntp_start + 2^32)}"; then
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
done
fi
for year in 1950 2130; do
export CLKNETSIM_START_DATE=$(date -d "Jan 1 00:00:00 UTC $year" +'%s')
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
# This check is expected to fail
check_sync && test_fail
# The following tests need 64-bit time_t and ntp_start not before 1970
check_config_h 'HAVE_LONG_TIME_T 1' || test_skip
echo "$ntp_start" | grep -q '-' && test_skip
for time_offset in -1e-1 1e-1; do
for start_offset in 0 "2^32 - $limit"; 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
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
test_pass

View File

@@ -27,4 +27,30 @@ for poll in $(seq 1 14); do
check_sync || test_fail
done
min_sync_time=$default_min_sync_time
max_sync_time=$default_max_sync_time
client_max_min_out_interval=$default_client_max_min_out_interval
client_min_mean_out_interval=$default_client_min_mean_out_interval
limit=10
for poll in $(seq -7 2 -1); do
client_server_options="minpoll $poll maxpoll $poll"
base_delay=1e-4
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_file_messages " 2 1 " \
$[2**-poll * limit * 9 / 10] $[2**-poll * limit] log.packets || test_fail
base_delay=2e-2
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_file_messages " 2 1 " $[limit * 9 / 10] $limit log.packets || test_fail
done
test_pass

View File

@@ -7,6 +7,9 @@ check_config_h 'FEAT_REFCLOCK 1' || test_skip
check_config_h 'FEAT_PHC 1' || test_skip
check_config_h 'FEAT_CMDMON 1' || test_skip
export CLKNETSIM_PHC_DELAY=1e-6
export CLKNETSIM_PHC_JITTER=1e-7
servers=0
limit=1000
refclock_jitter=$jitter
@@ -15,7 +18,7 @@ max_sync_time=70
chronyc_start=70
chronyc_conf="tracking"
for refclock in "SHM 0" "PHC /dev/ptp0"; do
for refclock in "SHM 0" "PHC /dev/ptp0" "PHC /dev/ptp0:nocrossts"; do
client_conf="refclock $refclock stratum 3 delay 1e-3 refid GPS
logdir tmp
log refclocks"
@@ -32,7 +35,11 @@ Root delay : 0.001000000 seconds
Update interval : 16\.. seconds
.*$" || test_fail
check_file_messages "20.* GPS.*[0-9] N " 997 1001 refclocks.log || test_fail
if echo "$refclock" | grep -q 'PHC.*nocrossts'; then
check_file_messages "20.* GPS.*[0-9] N " 650 750 refclocks.log || test_fail
else
check_file_messages "20.* GPS.*[0-9] N " 997 1001 refclocks.log || test_fail
fi
check_file_messages "20.* GPS.*- N " 61 63 refclocks.log || test_fail
rm -f tmp/refclocks.log
done
@@ -106,4 +113,31 @@ Root delay : 0\.000000001 seconds
rm -f tmp/refclocks.log
fi
refclock_offset="(+ 0.399 (sum 1e-3))"
refclock_jitter=1e-6
servers=1
freq_offset="(* 1e-4 (sine 1000))"
base_delay="(* -1.0 (equal 0.1 (min time 5000) 5000))"
client_server_options="minpoll 4 maxpoll 4 filter 5 minsamples 64"
client_conf="
refclock PHC /dev/ptp0 local poll 2
logdir tmp
log refclocks tracking"
chronyc_conf=""
limit=10000
max_sync_time=5000
time_max_limit=1e-3
time_rms_limit=5e-4
freq_max_limit=2e-5
freq_rms_limit=5e-6
run_test || test_fail
check_chronyd_exit || test_fail
check_sync || test_fail
check_file_messages "20.* PHC0 .* [0-9] ? " 9999 10001 refclocks.log || test_fail
check_file_messages "20.* PHC0 .* - ? " 2499 2501 refclocks.log || test_fail
check_file_messages "20.* PHC0 " 0 0 tracking.log || test_fail
rm -f tmp/refclocks.log tmp/tracking.log
test_pass

View File

@@ -14,6 +14,7 @@ server 192.168.123.2"
client_conf="
refclock SHM 0 noselect
smoothtime 400 0.001 leaponly"
cmdmon_unix=0
chronyc_conf="activity
tracking
@@ -92,7 +93,7 @@ check_chronyc_output "^C0A87B01,192\.168\.123\.1,2,12623049..\..........,-?0\.00
chronyc_options=""
server_strata=0
chronyc_start=0
chronyc_start=0.5
client_server_conf=""
client_conf=""
server_conf="server 192.168.123.1"
@@ -101,16 +102,14 @@ limit=1
for chronyc_conf in \
"accheck 1.2.3.4" \
"add peer 10.0.0.0 minpoll 2 maxpoll 6" \
"add server 10.0.0.0 minpoll 6 maxpoll 10 iburst burst key 1 certset 2 maxdelay 1e-3 maxdelayratio 10.0 maxdelaydevratio 10.0 mindelay 1e-4 asymmetry 0.5 offset 1e-5 minsamples 6 maxsamples 6 filter 3 offline auto_offline prefer noselect trust require xleave polltarget 20 port 123 presend 7 minstratum 3 version 4 nts ntsport 4460 copy" \
"add server 10.0.0.0 minpoll 6 maxpoll 10 iburst burst key 1 certset 2 maxdelay 1e-3 maxdelayratio 10.0 maxdelaydevratio 10.0 maxdelayquant 0.5 mindelay 1e-4 asymmetry 0.5 offset 1e-5 minsamples 6 maxsamples 6 filter 3 offline auto_offline prefer noselect trust require xleave polltarget 20 port 123 presend 7 minstratum 3 version 4 nts ntsport 4460 copy extfield F323" \
"add server node1.net1.clk" \
"allow 1.2.3.4" \
"allow 1.2" \
"allow 3.4.5" \
"allow 6.7.8/22" \
"allow 6.7.8.9/22" \
"allow 2001:db8::/32" \
"allow 0/0" \
"allow ::/0" \
"allow" \
"allow all 10/24" \
"authdata" \
@@ -180,6 +179,257 @@ do
check_chronyc_output "501 Not authorised$" || test_fail
done
cmdmon_unix=1
chronyc_conf="
authdata
clients -k -p 2
clients -r
clients
ntpdata
selectdata
serverstats"
run_test || test_fail
check_chronyd_exit || test_fail
check_chronyc_output "^Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen
=========================================================================
node1\.net1\.clk - 0 0 0 - 0 0 0 0
Hostname NTP Drop Int IntL Last NTS-KE Drop Int Last
===============================================================================
Hostname NTP Drop Int IntL Last Cmd Drop Int Last
===============================================================================
node1\.net1\.clk 1 0 - - 0 0 0 - -
Hostname NTP Drop Int IntL Last Cmd Drop Int Last
===============================================================================
node1\.net1\.clk 0 0 - - 0 0 0 - -
Remote address : 192\.168\.123\.1 \(C0A87B01\)
Remote port : 123
Local address : 192\.168\.123\.1 \(C0A87B01\)
Leap status : Normal
Version : 4
Mode : Server
Stratum : 1
Poll interval : 6 \(64 seconds\)
Precision : -23 \(0\.000000119 seconds\)
Root delay : 0\.000000 seconds
Root dispersion : 0\.000000 seconds
Reference ID : 7F7F0101 \(\)
Reference time : Thu Dec 31 23:59:5[89] 2009
Offset : [-+]0\.000...... seconds
Peer delay : 0\.00....... seconds
Peer dispersion : 0\.00000.... seconds
Response time : 0\.000000... seconds
Jitter asymmetry: \+0\.00
NTP tests : 111 111 1110
Interleaved : No
Authenticated : No
TX timestamping : Kernel
RX timestamping : Kernel
Total TX : 1
Total RX : 1
Total valid RX : 1
S Name/IP Address Auth COpts EOpts Last Score Interval Leap
=======================================================================
M node1\.net1\.clk N ----- ----- 0 1\.0 \+0ns \+0ns N
NTP packets received : 1
NTP packets dropped : 0
Command packets received : 12
Command packets dropped : 0
Client log records dropped : 0
NTS-KE connections accepted: 0
NTS-KE connections dropped : 0
Authenticated NTP packets : 0
Interleaved NTP packets : 0
NTP timestamps held : 0
NTP timestamp span : 0$" || test_fail
chronyc_conf="
deny all
cmdallow all
allow 1.2.3.4
allow 1.2.3.0/28
deny 1.2.3.0/27
allow 1.2.4.5
deny all 1.2.4.0/27
cmddeny 5.6.7.8
cmdallow all 5.6.7.0/28
accheck 1.2.3.4
accheck 1.2.3.5
accheck 1.2.4.5
cmdaccheck 5.6.7.8"
run_test || test_fail
check_chronyd_exit || test_fail
check_chronyc_output "^200 OK
200 OK
200 OK
200 OK
200 OK
200 OK
200 OK
200 OK
200 OK
208 Access allowed
209 Access denied
209 Access denied
208 Access allowed$" || test_fail
if check_config_h 'FEAT_IPV6 1'; then
chronyc_conf="
deny all
cmdallow all
allow 2001:db8::1
allow 2001:db8::/64
deny 2001:db8::/63
allow 2001:db8:1::1
deny all 2001:db8:1::/63
cmddeny 2001:db9::1
cmdallow all 2001:db9::/64
accheck 2001:db8::1
accheck 2001:db8::2
accheck 2001:db8:1::1
cmdaccheck 2001:db9::1"
run_test || test_fail
check_chronyd_exit || test_fail
check_chronyc_output "^200 OK
200 OK
200 OK
200 OK
200 OK
200 OK
200 OK
200 OK
200 OK
208 Access allowed
209 Access denied
209 Access denied
208 Access allowed$" || test_fail
fi
chronyc_conf="
delete 192.168.123.1
add server node1.net1.clk minpoll 6 maxpoll 10 iburst
offline 192.168.123.1
burst 1/1 192.168.123.1
online 192.168.123.1
maxdelay 192.168.123.1 1e-2
maxdelaydevratio 192.168.123.1 5.0
maxdelayratio 192.168.123.1 3.0
maxpoll 192.168.123.1 5
maxupdateskew 192.168.123.1 10.0
minpoll 192.168.123.1 3
minstratum 192.168.123.1 1
polltarget 192.168.123.1 10
delete 192.168.123.1"
run_test || test_fail
check_chronyd_exit || test_fail
check_chronyc_output "^200 OK
200 OK
200 OK
200 OK
200 OK
200 OK
200 OK
200 OK
200 OK
200 OK
200 OK
200 OK
200 OK
200 OK$" || test_fail
chronyc_conf="
cyclelogs
dump
dfreq 1.0e-3
doffset -0.01
local stratum 5 distance 1.0 orphan
local off
makestep 10.0 3
makestep
manual on
settime now
manual delete 0
manual reset
manual off
onoffline
refresh
rekey
reload sources
reselect
reselectdist 1e-3
reset sources
shutdown"
run_test || test_fail
check_chronyd_exit || test_fail
check_chronyc_output "^200 OK
200 OK
200 OK
200 OK
200 OK
200 OK
200 OK
200 OK
200 OK
200 OK
Clock was .\... seconds fast. Frequency change = 0.00ppm, new frequency = 0.00ppm
200 OK
200 OK
200 OK
200 OK
200 OK
200 OK
200 OK
200 OK
200 OK
200 OK
200 OK$" || test_fail
server_conf="
server 192.168.123.1
noclientlog"
commands=(
"add server nosuchnode.net1.clk" "^Invalid host/IP address$"
"allow nosuchnode.net1.clk" "^Could not read address$"
"allow 192.168.123.0/2 4" "^Could not read address$"
"allow 192.168.123.0/2e" "^Could not read address$"
"allow 192.168.12e" "^Could not read address$"
"allow 192.168123" "^Could not read address$"
"allow 192.168.123.2/33" "^507 Bad subnet$"
"clients" "Hostname.*519 Client logging is not active in the daemon$"
"delete 192.168.123.2" "^503 No such source$"
"minpoll 192.168.123.2 5" "^503 No such source$"
"ntpdata 192.168.123.2" "^503 No such source$"
"settime now" "^505 Facility not enabled in daemon$"
"smoothing" "^505 Facility not enabled in daemon$"
"smoothtime activate" "^505 Facility not enabled in daemon$"
"smoothtime reset" "^505 Facility not enabled in daemon$"
"sourcename 192.168.123.2" "^503 No such source$"
"trimrtc" "^513 RTC driver not running$"
"writertc" "^513 RTC driver not running$"
)
for i in $(seq 0 $[${#commands[*]} / 2]); do
chronyc_conf=${commands[$[i * 2]]}
run_test || test_fail
check_chronyd_exit || test_fail
check_chronyc_output "${commands[$[i * 2 + 1]]}" || test_fail
done
cmdmon_unix=0
server_conf="server 192.168.123.1"
chronyc_conf="dns -n
dns +n
dns -4

View File

@@ -25,4 +25,18 @@ for client_server_options in "maxpoll 6 maxdelay 2e-5"; do
check_sync && test_fail
done
min_sync_time=10
client_conf="
logdir tmp
log rawmeasurements"
client_server_options="minpoll 2 maxpoll 2 maxdelayquant 0.1"
run_test || test_fail
check_chronyd_exit || test_fail
check_packet_interval || test_fail
check_sync || test_fail
check_file_messages "20.*123\.1.* 111 111 1111" 200 500 measurements.log || test_fail
check_file_messages "20.*123\.1.* 111 111 1101" 2000 2300 measurements.log || test_fail
test_pass

View File

@@ -8,6 +8,19 @@ client_conf="
logdir tmp
log rawmeasurements"
server_conf="noclientlog"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
check_file_messages "111 111 .111.* 4I [DKH] [DKH]\$" 0 0 measurements.log || test_fail
rm -f tmp/measurements.log
server_conf=""
max_sync_time=270
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
@@ -16,6 +29,7 @@ check_sync || test_fail
check_file_messages "111 111 1111.* 4B [DKH] [DKH]\$" 2 2 measurements.log || test_fail
check_file_messages "111 111 1111.* 4I [DKH] [DKH]\$" 30 200 measurements.log || test_fail
check_file_messages "111 111 0111.* 4I [DKH] [DKH]\$" 1 1 measurements.log || test_fail
rm -f tmp/measurements.log
clients=2
@@ -52,4 +66,26 @@ for rpoll in 4 5 6; do
rm -f tmp/measurements.log
done
if check_config_h 'FEAT_CMDMON 1'; then
# test client timestamp selection and server timestamp correction
base_delay="(+ 1.25e-6 (* -1 (equal 0.1 from 5)))"
jitter=1e-9
wander=1e-12
client_lpeer_options="xleave minpoll 5 maxpoll 5 noselect"
client_rpeer_options="xleave minpoll 5 maxpoll 5 noselect"
chronyc_conf="doffset -0.1"
chronyc_start=7200
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync && test_fail
check_file_messages "\.2 N 2 111 111 .... 5 5 .\... ..\....e-.. 2\....e-06" \
290 310 measurements.log || test_fail
check_file_messages "\.2 N 2 111 111 .... 5 5 .\... ..\....e-.. .\....e-0[0123]" \
0 0 measurements.log || test_fail
rm -f tmp/measurements.log
fi
test_pass

View File

@@ -4,7 +4,7 @@
test_start "filter option"
client_server_options="minpoll 4 maxpoll 4 filter 15"
client_server_options="minpoll 4 maxpoll 4 filter 15 maxdelay 3.5e-4"
min_sync_time=710
max_sync_time=720
client_max_min_out_interval=16.1
@@ -16,4 +16,28 @@ check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
base_delay="(+ 1e-4 (* -1 (equal 0.3 (uniform) 0.0)))"
client_server_options="minpoll 4 maxpoll 4 filter 3"
min_sync_time=130
run_test || test_fail
check_chronyd_exit || test_fail
check_packet_interval || test_fail
check_sync || test_fail
limit=10
client_server_options="minpoll -6 maxpoll -6 filter 1"
base_delay=1e-4
run_test || test_fail
check_chronyd_exit || test_fail
check_file_messages " 2 1 " 590 640 log.packets || test_fail
base_delay=2e-2
run_test || test_fail
check_chronyd_exit || test_fail
check_file_messages " 2 1 " 9 10 log.packets || test_fail
test_pass

View File

@@ -7,32 +7,53 @@ test_start "hwtimestamp directive"
check_config_h 'HAVE_LINUX_TIMESTAMPING 1' || test_skip
export CLKNETSIM_TIMESTAMPING=2
export CLKNETSIM_PHC_DELAY=1e-6
export CLKNETSIM_PHC_JITTER=1e-7
export CLKNETSIM_PHC_JITTER_ASYM=0.4
refclock_jitter=1e-8
refclock_offset=10.0
min_sync_time=4
max_sync_time=20
time_rms_limit=1e-7
freq_rms_limit=3e-8
jitter=1e-8
freq_offset=1e-5
limit=200
server_conf="hwtimestamp eth0"
client_server_options="minpoll 0 maxpoll 0 minsamples 32 xleave"
server_conf="
clockprecision 1e-9
hwtimestamp eth0"
client_server_options="minpoll 0 maxpoll 0 xleave"
client_chronyd_options="-d"
for client_conf in "hwtimestamp eth0" "hwtimestamp eth0
acquisitionport 123"; do
for client_conf in \
"hwtimestamp eth0 nocrossts
clockprecision 1e-9" \
"hwtimestamp eth0
clockprecision 1e-9
acquisitionport 123"; do
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
if check_config_h 'FEAT_DEBUG 1'; then
check_log_messages "Accepted reading" 0 2 || test_fail
check_log_messages "Combined .* readings" 190 220 || test_fail
check_log_messages "HW clock samples" 190 200 || test_fail
check_log_messages "HW clock reset" 0 0 || test_fail
check_log_messages "Missing TX timestamp" 1 1 || test_fail
check_log_messages "Received message.*tss=KH" 195 200 || test_fail
check_log_messages "Received error.*message.*tss=KH" 195 200 || test_fail
check_log_messages "Updated RX timestamp.*tss=1" 1 1 || test_fail
check_log_messages "Updated RX timestamp.*tss=2" 195 200 || test_fail
check_log_messages "update_tx_timestamp.*Updated" 50 140 || test_fail
check_log_messages "update_tx_timestamp.*Unacceptable" 50 140 || test_fail
if echo "$client_conf" | grep -q nocrossts; then
check_log_messages "update_tx_timestamp.*Updated" 180 200 || test_fail
check_log_messages "update_tx_timestamp.*Unacceptable" 0 10 || test_fail
else
check_log_messages "update_tx_timestamp.*Updated" 50 140 || test_fail
check_log_messages "update_tx_timestamp.*Unacceptable" 50 140 || test_fail
fi
fi
done

View File

@@ -11,7 +11,7 @@ client_server_options="maxpoll 6"
client_conf="refclock PHC /dev/ptp0 dpoll 4 poll 6 noselect
logbanner 10
logdir tmp
log tracking rawmeasurements measurements statistics rtc refclocks tempcomp
log tracking rawmeasurements measurements selection statistics rtc refclocks tempcomp
tempcomp tmp/tempcomp 64 0.0 0.0 0.0 0.0"
echo 0.0 > tmp/tempcomp
@@ -26,6 +26,8 @@ check_file_messages "=============" 31 33 \
tracking.log measurements.log tempcomp.log || test_fail
check_file_messages "20.*192\.168\.123\.1" 150 160 \
tracking.log measurements.log statistics.log || test_fail
check_file_messages "20.*PHC0 * N " 300 320 selection.log || test_fail
check_file_messages "20.*192\.168\.123\.1 *[M*]" 300 320 selection.log || test_fail
check_file_messages "20.*PHC0" 150 160 statistics.log || test_fail
check_file_messages "20.*PHC0" 750 800 refclocks.log || test_fail
check_file_messages "20.* 0\.0000" 150 160 tempcomp.log || test_fail

41
test/simulation/142-ptpport Executable file
View File

@@ -0,0 +1,41 @@
#!/usr/bin/env bash
. ./test.common
test_start "PTP port"
# Block communication between 3 and 1
base_delay="(+ 1e-4 (* -1 (equal 0.1 from 3) (equal 0.1 to 1)))"
cat > tmp/peer.keys <<-EOF
1 MD5 1234567890
EOF
clients=2
peers=2
max_sync_time=420
server_conf="
ptpport 319"
client_conf="
ptpport 319
authselectmode ignore
keyfile tmp/peer.keys"
client_server_options="minpoll 6 maxpoll 6 port 319"
client_peer_options="minpoll 6 maxpoll 6 port 319 key 1"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
check_file_messages " 2 1 .* 319 319 1 96 " 150 160 \
log.packets || test_fail
check_file_messages " 1 2 .* 319 319 1 96 " 150 160 \
log.packets || test_fail
check_file_messages " 2 3 .* 319 319 1 116 " 150 160 \
log.packets || test_fail
check_file_messages " 3 2 .* 319 319 1 116 " 150 160 \
log.packets || test_fail
test_pass

70
test/simulation/143-manual Executable file
View File

@@ -0,0 +1,70 @@
#!/usr/bin/env bash
. ./test.common
export TZ=UTC
test_start "manual input"
check_config_h 'FEAT_CMDMON 1' || test_skip
limit=$[12 * 3600]
client_server_conf=" "
client_conf="manual"
chronyc_conf="timeout 4000000
settime 1:00:00
settime 2:00:00
settime 3:00:00
settime 4:00:00
manual delete 2
settime 6:00:00
manual list
settime 8:00:00
manual reset
settime 10:00:00
manual list"
chronyc_start=1800
base_delay=1800
jitter=1e-6
time_max_limit=4e-3
freq_max_limit=4e-3
time_rms_limit=2e-3
freq_rms_limit=2e-5
min_sync_time=7204
max_sync_time=7206
run_test || test_fail
check_chronyd_exit || test_fail
check_sync || test_fail
check_chronyc_output "^200 OK
Clock was 0\.4. seconds fast\. Frequency change = 0\.00ppm, new frequency = 0\.00ppm
200 OK
Clock was 0\.3. seconds fast\. Frequency change = (99|100)\...ppm, new frequency = (99|100)\...ppm
200 OK
Clock was \-?0\.0. seconds fast\. Frequency change = \-?0\.[01].ppm, new frequency = (99|100)\...ppm
200 OK
Clock was \-?0\.0. seconds fast\. Frequency change = \-?0\.[01].ppm, new frequency = (99|100)\...ppm
200 OK
200 OK
Clock was \-?0\.0. seconds fast\. Frequency change = \-?0\.[012].ppm, new frequency = (99|100)\...ppm
210 n_samples = 4
# Date Time\(UTC\) Slewed Original Residual
=======================================================
0 2010-01-01 (00:59:59|01:00:00) [- ]0\.00 0\.46 [- ]0\.00
1 2010-01-01 (01:59:59|02:00:00) [- ]0\.00 0\.36 [- ]0\.00
2 2010-01-01 (03:59:59|04:00:00) [- ]0\.00 [- ]0\.00 [- ]0\.00
3 2010-01-01 (05:59:59|06:00:00) [- ]0\.00 [- ]0\.00 [- ]0\.00
200 OK
Clock was \-?0\.0. seconds fast\. Frequency change = \-?0\.[012].ppm, new frequency = (99|100)\...ppm
200 OK
200 OK
Clock was \-?0\.0. seconds fast\. Frequency change = \-?0\.00ppm, new frequency = (99|100)\...ppm
210 n_samples = 1
# Date Time\(UTC\) Slewed Original Residual
=======================================================
0 2010-01-01 (09:59:59|10:00:00) [- ]0\.00 [- ]0\.00 [- ]0\.00$" \
|| test_fail
test_pass

55
test/simulation/144-exp1 Executable file
View File

@@ -0,0 +1,55 @@
#!/usr/bin/env bash
. ./test.common
test_start "experimental extension field"
check_config_h 'FEAT_CMDMON 1' || test_skip
primary_time_offset=0.1
server_strata=4
min_sync_time=2000
max_sync_time=2300
chronyc_conf="doffset 0.1"
chronyc_options="-h /clknetsim/unix/1:1"
chronyc_start=2000
for options in "extfield F323" "xleave extfield F323"; do
client_server_options="minpoll 6 maxpoll 6 $options"
server_server_options="$client_server_options"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
done
server_server_options=""
server_strata=1
clients=4
peers=4
max_sync_time=2400
# chain of peers and one enabled chronyc
base_delay=$(cat <<-EOF | tr -d '\n'
(+ 1e-4 -1
(equal 0.1 from (+ to 1))
(equal 0.1 from (+ to -1))
(equal 0.1 from 6)
(equal 0.1 to 6))
EOF
)
for lpoll in 5 6 7; do
for options in "minsamples 16 extfield F323" "minsamples 16 xleave extfield F323"; do
client_lpeer_options="minpoll $lpoll maxpoll $lpoll $options"
client_rpeer_options="minpoll 6 maxpoll 6 $options"
client_server_options="$client_rpeer_options"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
done
done
test_pass

View File

@@ -27,6 +27,7 @@ fi
# Default test testings
default_limit=10000
default_primary_time_offset=0.0
default_time_offset=1e-1
default_freq_offset=1e-4
default_base_delay=1e-4
@@ -76,6 +77,7 @@ default_max_sync_time=210
default_client_min_mean_out_interval=0.0
default_client_max_min_out_interval=inf
default_cmdmon_unix=1
default_dns=0
# Initialize test settings from their defaults
@@ -434,6 +436,7 @@ run_simulation() {
test_message 2 0 "running simulation:"
start_server $nodes \
-n 2 \
-o tmp/log.offset -f tmp/log.freq -p tmp/log.packets \
-R $(awk "BEGIN {print $update_interval < 0 ? 2^-($update_interval) : 1}") \
-r $(awk "BEGIN {print $max_sync_time * 2^$update_interval}") \
@@ -449,6 +452,8 @@ run_test() {
nodes=$(get_chronyd_nodes)
[ -n "$chronyc_conf" ] && nodes=$[$nodes + $clients]
export CLKNETSIM_UNIX_SUBNET=$[$cmdmon_unix != 0 ? 2 : 0]
for i in $(seq 1 $nodes); do
echo "node${i}_shift_pll = $shift_pll"
for j in $(seq 1 $nodes); do
@@ -468,7 +473,8 @@ run_test() {
step=$server_step
start=$server_start
freq=""
[ $i -le $falsetickers ] && offset=$i.0 || offset=0.0
[ $i -le $falsetickers ] &&
offset=$i.0 || offset=$primary_time_offset
options=$server_chronyd_options
elif [ $stratum -le $server_strata ]; then
step=$server_step
@@ -503,9 +509,15 @@ run_test() {
for i in $(seq 1 $[$nodes - $node + 1]); do
test_message 2 0 "starting node $node:"
options=$([ $dns -eq 0 ] && printf "%s" "-n")
if [ $cmdmon_unix -ne 0 ]; then
options+=" -h /clknetsim/unix/$[$node - $clients]:1"
else
options+=" -h $(get_node_name $[$node - $clients])"
fi
echo "node${node}_start = $chronyc_start" >> tmp/conf
start_client $node chronyc "$chronyc_conf" "" \
"$([ $dns -eq 0 ] && printf "%s" "-n") -h $(get_node_name $[$node - $clients]) $chronyc_options" && \
start_client $node chronyc "$chronyc_conf" "" "$options $chronyc_options" && \
test_ok || test_error
[ $? -ne 0 ] && return 1

View File

@@ -1,17 +0,0 @@
#!/usr/bin/env bash
. ./test.common
check_chronyd_features SCFILTER || test_skip "SCFILTER support disabled"
test_start "system call filter"
for extra_chronyd_options in "-F -1" "-F 1"; do
start_chronyd || test_fail
wait_for_sync || test_fail
stop_chronyd || test_fail
check_chronyd_messages || test_fail
check_chronyd_files || test_fail
done
test_pass

View File

@@ -23,8 +23,11 @@ for command in \
"dfreq 1.0e-3" \
"doffset -0.1" \
"dump" \
"offline" \
"local off" \
"local" \
"online" \
"onoffline" \
"maxdelay $server 1e-1" \
"maxdelaydevratio $server 5.0" \
"maxdelayratio $server 3.0" \
@@ -32,9 +35,6 @@ for command in \
"maxupdateskew $server 10.0" \
"minpoll $server 10" \
"minstratum $server 1" \
"offline" \
"online" \
"onoffline" \
"polltarget $server 10" \
"refresh" \
"rekey" \
@@ -100,7 +100,7 @@ Total valid RX : [0-9]+$" || test_fail
run_chronyc "selectdata" || test_fail
check_chronyc_output "^S Name/IP Address Auth COpts EOpts Last Score Interval Leap
=======================================================================
M 127\.0\.0\.1 N ----- ----- 0 1\.0 \+0ns \+0ns \?$" || test_fail
s 127\.0\.0\.1 N ----- ----- 0 1\.0 \+0ns \+0ns \?$" || test_fail
run_chronyc "serverstats" || test_fail
check_chronyc_output "^NTP packets received : [0-9]+
@@ -110,7 +110,10 @@ Command packets dropped : 0
Client log records dropped : 0
NTS-KE connections accepted: 0
NTS-KE connections dropped : 0
Authenticated NTP packets : 0$" || test_fail
Authenticated NTP packets : 0
Interleaved NTP packets : 0
NTP timestamps held : 0
NTP timestamp span : 0$"|| test_fail
run_chronyc "manual on" || test_fail
check_chronyc_output "^200 OK$" || test_fail

View File

@@ -5,8 +5,6 @@
check_chronyd_features NTS || test_skip "NTS support disabled"
certtool --help &> /dev/null || test_skip "certtool missing"
check_chronyd_features PRIVDROP && user="nobody"
test_start "NTS authentication"
cat > $TEST_DIR/cert.cfg <<EOF
@@ -14,8 +12,8 @@ cn = "chrony-nts-test"
dns_name = "chrony-nts-test"
ip_address = "$server"
serial = 001
activation_date = "$(date -d '1 year ago' +'%Y-%m-%d') 00:00:00 UTC"
expiration_date = "$(date -d '1 year' +'%Y-%m-%d') 00:00:00 UTC"
activation_date = "$[$(date '+%Y') - 1]-01-01 00:00:00 UTC"
expiration_date = "$[$(date '+%Y') + 2]-01-01 00:00:00 UTC"
signing_key
encryption_key
EOF
@@ -51,8 +49,6 @@ stop_chronyd || test_fail
check_chronyd_messages || test_fail
check_chronyd_files || test_fail
rm -f $TEST_LOGDIR/measurements.log
server_options="port $ntpport nts ntsport $((ntsport + 1))"
start_chronyd || test_fail

24
test/system/099-scfilter Executable file
View File

@@ -0,0 +1,24 @@
#!/usr/bin/env bash
. ./test.common
check_chronyd_features SCFILTER || test_skip "SCFILTER support disabled"
test_start "system call filter in non-destructive tests"
for level in "-1" "1" "-2" "2"; do
test_message 1 1 "level $level:"
for test in 0[0-8][0-9]-*[^_]; do
test_message 2 0 "$test"
TEST_SCFILTER=$level "./$test" > /dev/null 2> /dev/null
result=$?
if [ $result != 0 ] && [ $result != 9 ] ; then
test_bad
test_fail
fi
test_ok
done
done
test_pass

24
test/system/199-scfilter Executable file
View File

@@ -0,0 +1,24 @@
#!/usr/bin/env bash
. ./test.common
check_chronyd_features SCFILTER || test_skip "SCFILTER support disabled"
test_start "system call filter in destructive tests"
for level in "-1" "1" "-2" "2"; do
test_message 1 1 "level $level:"
for test in 1[0-8][0-9]-*[^_]; do
test_message 2 0 "$test"
TEST_SCFILTER=$level "./$test" > /dev/null 2> /dev/null
result=$?
if [ $result != 0 ] && [ $result != 9 ] ; then
test_bad
test_fail
fi
test_ok
done
done
test_pass

View File

@@ -20,6 +20,7 @@ TEST_DIR=${TEST_DIR:-$(pwd)/tmp}
TEST_LIBDIR=${TEST_LIBDIR:-$TEST_DIR}
TEST_LOGDIR=${TEST_LOGDIR:-$TEST_DIR}
TEST_RUNDIR=${TEST_RUNDIR:-$TEST_DIR}
TEST_SCFILTER=${TEST_SCFILTER:-0}
test_start() {
check_chronyd_features NTP CMDMON || test_skip "NTP/CMDMON support disabled"
@@ -228,6 +229,8 @@ generate_chrony_conf() {
echo "log tempcomp rawmeasurements refclocks statistics tracking rtc"
echo "logbanner 0"
echo "smoothtime 100.0 0.001"
echo "leapsectz right/UTC"
echo "dscp 46"
echo "include /dev/null"
echo "keyfile $TEST_DIR/keys"
@@ -242,6 +245,7 @@ get_chronyd_options() {
echo "-l $(get_logfile)"
echo "-f $(get_conffile)"
echo "-u $user"
echo "-F $TEST_SCFILTER"
echo "$extra_chronyd_options"
}
@@ -256,6 +260,8 @@ start_chronyd() {
trap stop_chronyd EXIT
rm -f "$TEST_LOGDIR"/*.log
$CHRONYD_WRAPPER "$chronyd" $(get_chronyd_options) > "$TEST_DIR/chronyd.out" 2>&1
[ $? -eq 0 ] && [ -f "$pidfile" ] && ps -p "$(cat "$pidfile")" > /dev/null && test_ok || test_error

View File

@@ -1,6 +1,6 @@
/*
**********************************************************************
* Copyright (C) Miroslav Lichvar 2016
* Copyright (C) Miroslav Lichvar 2016, 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
@@ -25,15 +25,24 @@
#include <clientlog.c>
static uint64_t
get_random64(void)
{
return ((uint64_t)random() << 40) ^ ((uint64_t)random() << 20) ^ random();
}
void
test_unit(void)
{
int i, j, index;
uint64_t ts64, prev_first_ts64, prev_last_ts64, max_step;
uint32_t index2, prev_first, prev_size;
struct timespec ts, ts2;
int i, j, k, index, shift;
CLG_Service s;
struct timespec ts;
NTP_int64 ntp_ts;
IPAddr ip;
char conf[][100] = {
"clientloglimit 10000",
"clientloglimit 20000",
"ratelimit interval 3 burst 4 leak 3",
"cmdratelimit interval 3 burst 4 leak 3",
"ntsratelimit interval 6 burst 8 leak 3",
@@ -43,6 +52,7 @@ test_unit(void)
for (i = 0; i < sizeof conf / sizeof conf[0]; i++)
CNF_ParseLine(NULL, i + 1, conf[i]);
LCL_Initialise();
CLG_Initialise();
TEST_CHECK(ARR_GetSize(records) == 16);
@@ -67,7 +77,7 @@ test_unit(void)
}
DEBUG_LOG("records %u", ARR_GetSize(records));
TEST_CHECK(ARR_GetSize(records) == 64);
TEST_CHECK(ARR_GetSize(records) == 128);
s = CLG_NTP;
@@ -82,7 +92,195 @@ test_unit(void)
DEBUG_LOG("requests %d responses %d", i, j);
TEST_CHECK(j * 4 < i && j * 6 > i);
TEST_CHECK(!ntp_ts_map.timestamps);
UTI_ZeroNtp64(&ntp_ts);
CLG_SaveNtpTimestamps(&ntp_ts, NULL);
TEST_CHECK(ntp_ts_map.timestamps);
TEST_CHECK(ntp_ts_map.first == 0);
TEST_CHECK(ntp_ts_map.size == 0);
TEST_CHECK(ntp_ts_map.max_size == 128);
TEST_CHECK(ARR_GetSize(ntp_ts_map.timestamps) == ntp_ts_map.max_size);
TEST_CHECK(ntp_ts_map.max_size > NTPTS_INSERT_LIMIT);
for (i = 0; i < 200; i++) {
DEBUG_LOG("iteration %d", i);
max_step = (1ULL << (i % 50));
ts64 = 0ULL - 100 * max_step;
if (i > 150)
ntp_ts_map.max_size = 1U << (i % 8);
assert(ntp_ts_map.max_size <= 128);
ntp_ts_map.first = i % ntp_ts_map.max_size;
ntp_ts_map.size = 0;
ntp_ts_map.cached_rx_ts = 0ULL;
ntp_ts_map.slew_epoch = i * 400;
for (j = 0; j < 500; j++) {
do {
ts64 += get_random64() % max_step + 1;
} while (ts64 == 0ULL);
int64_to_ntp64(ts64, &ntp_ts);
if (random() % 10) {
UTI_Ntp64ToTimespec(&ntp_ts, &ts);
UTI_AddDoubleToTimespec(&ts, TST_GetRandomDouble(-1.999, 1.999), &ts);
} else {
UTI_ZeroTimespec(&ts);
}
CLG_SaveNtpTimestamps(&ntp_ts,
UTI_IsZeroTimespec(&ts) ? (random() % 2 ? &ts : NULL) : &ts);
if (j < ntp_ts_map.max_size) {
TEST_CHECK(ntp_ts_map.size == j + 1);
TEST_CHECK(ntp_ts_map.first == i % ntp_ts_map.max_size);
} else {
TEST_CHECK(ntp_ts_map.size == ntp_ts_map.max_size);
TEST_CHECK(ntp_ts_map.first == (i + j + ntp_ts_map.size + 1) % ntp_ts_map.max_size);
}
TEST_CHECK(ntp_ts_map.cached_index == ntp_ts_map.size - 1);
TEST_CHECK(get_ntp_tss(ntp_ts_map.size - 1)->slew_epoch == ntp_ts_map.slew_epoch);
TEST_CHECK(CLG_GetNtpTxTimestamp(&ntp_ts, &ts2));
TEST_CHECK(UTI_CompareTimespecs(&ts, &ts2) == 0);
for (k = random() % 4; k > 0; k--) {
index2 = random() % ntp_ts_map.size;
int64_to_ntp64(get_ntp_tss(index2)->rx_ts, &ntp_ts);
if (random() % 2)
TEST_CHECK(CLG_GetNtpTxTimestamp(&ntp_ts, &ts));
UTI_Ntp64ToTimespec(&ntp_ts, &ts);
UTI_AddDoubleToTimespec(&ts, TST_GetRandomDouble(-1.999, 1.999), &ts);
ts2 = ts;
CLG_UndoNtpTxTimestampSlew(&ntp_ts, &ts);
if ((get_ntp_tss(index2)->slew_epoch + 1) % (1U << 16) != ntp_ts_map.slew_epoch) {
TEST_CHECK(UTI_CompareTimespecs(&ts, &ts2) == 0);
} else {
TEST_CHECK(fabs(UTI_DiffTimespecsToDouble(&ts, &ts2) - ntp_ts_map.slew_offset) <
1.0e-9);
}
CLG_UpdateNtpTxTimestamp(&ntp_ts, &ts);
TEST_CHECK(CLG_GetNtpTxTimestamp(&ntp_ts, &ts2));
TEST_CHECK(UTI_CompareTimespecs(&ts, &ts2) == 0);
if (random() % 2) {
uint16_t prev_epoch = ntp_ts_map.slew_epoch;
handle_slew(NULL, NULL, 0.0, TST_GetRandomDouble(-1.0e-5, 1.0e-5),
LCL_ChangeAdjust, NULL);
TEST_CHECK((prev_epoch + 1) % (1U << 16) == ntp_ts_map.slew_epoch);
}
if (ntp_ts_map.size > 1) {
index = random() % (ntp_ts_map.size - 1);
if (get_ntp_tss(index)->rx_ts + 1 != get_ntp_tss(index + 1)->rx_ts) {
int64_to_ntp64(get_ntp_tss(index)->rx_ts + 1, &ntp_ts);
TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts));
int64_to_ntp64(get_ntp_tss(index + 1)->rx_ts - 1, &ntp_ts);
TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts));
CLG_UpdateNtpTxTimestamp(&ntp_ts, &ts);
CLG_UndoNtpTxTimestampSlew(&ntp_ts, &ts);
}
}
if (random() % 2) {
int64_to_ntp64(get_ntp_tss(0)->rx_ts - 1, &ntp_ts);
TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts));
int64_to_ntp64(get_ntp_tss(ntp_ts_map.size - 1)->rx_ts + 1, &ntp_ts);
TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts));
CLG_UpdateNtpTxTimestamp(&ntp_ts, &ts);
CLG_UndoNtpTxTimestampSlew(&ntp_ts, &ts);
}
}
}
for (j = 0; j < 500; j++) {
shift = (i % 3) * 26;
if (i % 7 == 0) {
while (ntp_ts_map.size < ntp_ts_map.max_size) {
ts64 += get_random64() >> (shift + 8);
int64_to_ntp64(ts64, &ntp_ts);
CLG_SaveNtpTimestamps(&ntp_ts, NULL);
if (ntp_ts_map.cached_index + NTPTS_INSERT_LIMIT < ntp_ts_map.size)
ts64 = get_ntp_tss(ntp_ts_map.size - 1)->rx_ts;
}
}
do {
if (ntp_ts_map.size > 1 && random() % 2) {
k = random() % (ntp_ts_map.size - 1);
ts64 = get_ntp_tss(k)->rx_ts +
(get_ntp_tss(k + 1)->rx_ts - get_ntp_tss(k)->rx_ts) / 2;
} else {
ts64 = get_random64() >> shift;
}
} while (ts64 == 0ULL);
int64_to_ntp64(ts64, &ntp_ts);
prev_first = ntp_ts_map.first;
prev_size = ntp_ts_map.size;
prev_first_ts64 = get_ntp_tss(0)->rx_ts;
prev_last_ts64 = get_ntp_tss(prev_size - 1)->rx_ts;
CLG_SaveNtpTimestamps(&ntp_ts, NULL);
TEST_CHECK(find_ntp_rx_ts(ts64, &index2));
if (ntp_ts_map.size > 1) {
TEST_CHECK(ntp_ts_map.size > 0 && ntp_ts_map.size <= ntp_ts_map.max_size);
if (get_ntp_tss(index2)->flags & NTPTS_DISABLED)
continue;
TEST_CHECK(get_ntp_tss(ntp_ts_map.size - 1)->rx_ts - ts64 <= NTPTS_FUTURE_LIMIT);
if ((int64_t)(prev_last_ts64 - ts64) <= NTPTS_FUTURE_LIMIT) {
TEST_CHECK(prev_size + 1 >= ntp_ts_map.size);
if (index2 + NTPTS_INSERT_LIMIT + 1 >= ntp_ts_map.size &&
!(index2 == 0 && NTPTS_INSERT_LIMIT < ntp_ts_map.max_size &&
((NTPTS_INSERT_LIMIT == prev_size && (int64_t)(ts64 - prev_first_ts64) > 0) ||
(NTPTS_INSERT_LIMIT + 1 == prev_size && (int64_t)(ts64 - prev_first_ts64) < 0))))
TEST_CHECK((prev_first + prev_size + 1) % ntp_ts_map.max_size ==
(ntp_ts_map.first + ntp_ts_map.size) % ntp_ts_map.max_size);
else
TEST_CHECK(prev_first + prev_size == ntp_ts_map.first + ntp_ts_map.size);
}
TEST_CHECK((int64_t)(get_ntp_tss(ntp_ts_map.size - 1)->rx_ts -
get_ntp_tss(0)->rx_ts) > 0);
for (k = 0; k + 1 < ntp_ts_map.size; k++)
TEST_CHECK((int64_t)(get_ntp_tss(k + 1)->rx_ts - get_ntp_tss(k)->rx_ts) > 0);
}
if (random() % 10 == 0) {
CLG_DisableNtpTimestamps(&ntp_ts);
TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts));
}
for (k = random() % 10; k > 0; k--) {
ts64 = get_random64() >> shift;
int64_to_ntp64(ts64, &ntp_ts);
CLG_GetNtpTxTimestamp(&ntp_ts, &ts);
}
}
if (random() % 2) {
handle_slew(NULL, NULL, 0.0, TST_GetRandomDouble(-1.0e9, 1.0e9),
LCL_ChangeUnknownStep, NULL);
TEST_CHECK(ntp_ts_map.size == 0);
TEST_CHECK(ntp_ts_map.cached_rx_ts == 0ULL);
TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts));
CLG_UpdateNtpTxTimestamp(&ntp_ts, &ts);
}
}
CLG_Finalise();
LCL_Finalise();
CNF_Finalise();
}
#else

View File

@@ -38,6 +38,7 @@ test_unit(void)
unsigned char data2[] = "12345678910";
unsigned char out[MAX_HASH_LENGTH];
struct hash_test tests[] = {
{ "MD5-NC", "\xfc\x24\x97\x1b\x52\x66\xdc\x46\xef\xe0\xe8\x08\x46\x89\xb6\x88", 16 },
{ "MD5", "\xfc\x24\x97\x1b\x52\x66\xdc\x46\xef\xe0\xe8\x08\x46\x89\xb6\x88", 16 },
{ "SHA1", "\xd8\x85\xb3\x86\xce\xea\x93\xeb\x92\xcd\x7b\x94\xb9\x8d\xc2\x8e"
"\x3e\x31\x13\xdd", 20},
@@ -77,9 +78,15 @@ test_unit(void)
for (i = 0; tests[i].name[0] != '\0'; i++) {
algorithm = UTI_HashNameToAlgorithm(tests[i].name);
TEST_CHECK(algorithm != 0);
if (strcmp(tests[i].name, "MD5-NC") == 0) {
TEST_CHECK(algorithm == 0);
algorithm = HSH_MD5_NONCRYPTO;
} else {
TEST_CHECK(algorithm != 0);
}
hash_id = HSH_GetHashId(algorithm);
if (hash_id < 0) {
TEST_CHECK(algorithm != HSH_MD5_NONCRYPTO);
TEST_CHECK(algorithm != HSH_MD5);
#ifdef FEAT_SECHASH
TEST_CHECK(algorithm != HSH_SHA1);

View File

@@ -18,21 +18,29 @@
**********************************************************************
*/
#include <hwclock.c>
#include <config.h>
#include "test.h"
#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
#include <hwclock.c>
#define MAX_READINGS 20
void
test_unit(void)
{
struct timespec start_hw_ts, start_local_ts, hw_ts, local_ts, ts;
struct timespec readings[MAX_READINGS][3];
HCL_Instance clock;
double freq, jitter, interval, dj, sum;
int i, j, k, count;
double freq, jitter, interval, dj, err, sum;
int i, j, k, l, new_sample, n_readings, count;
LCL_Initialise();
TST_RegisterDummyDrivers();
for (i = 1; i <= 8; i++) {
clock = HCL_CreateInstance(random() % (1 << i), 1 << i, 1.0);
clock = HCL_CreateInstance(random() % (1 << i), 1 << i, 1.0, 1e-9);
for (j = 0, count = 0, sum = 0.0; j < 100; j++) {
UTI_ZeroTimespec(&start_hw_ts);
@@ -48,11 +56,14 @@ test_unit(void)
clock->n_samples = 0;
clock->valid_coefs = 0;
QNT_Reset(clock->delay_quants);
new_sample = 0;
for (k = 0; k < 100; k++) {
UTI_AddDoubleToTimespec(&start_hw_ts, k * interval * freq, &hw_ts);
UTI_AddDoubleToTimespec(&start_local_ts, k * interval, &local_ts);
if (HCL_CookTime(clock, &hw_ts, &ts, NULL)) {
if (HCL_CookTime(clock, &hw_ts, &ts, NULL) && new_sample) {
dj = fabs(UTI_DiffTimespecsToDouble(&ts, &local_ts) / jitter);
DEBUG_LOG("delta/jitter %f", dj);
if (clock->n_samples >= clock->max_samples / 2)
@@ -63,10 +74,25 @@ test_unit(void)
UTI_AddDoubleToTimespec(&start_hw_ts, k * interval * freq + TST_GetRandomDouble(-jitter, jitter), &hw_ts);
if (HCL_NeedsNewSample(clock, &local_ts))
HCL_AccumulateSample(clock, &hw_ts, &local_ts, 2.0 * jitter);
if (HCL_NeedsNewSample(clock, &local_ts)) {
n_readings = random() % MAX_READINGS + 1;
for (l = 0; l < n_readings; l++) {
UTI_AddDoubleToTimespec(&local_ts, -TST_GetRandomDouble(0.0, jitter / 10.0), &readings[l][0]);
readings[l][1] = hw_ts;
UTI_AddDoubleToTimespec(&local_ts, TST_GetRandomDouble(0.0, jitter / 10.0), &readings[l][2]);
}
TEST_CHECK(clock->valid_coefs || clock->n_samples < 2);
UTI_ZeroTimespec(&hw_ts);
UTI_ZeroTimespec(&local_ts);
if (HCL_ProcessReadings(clock, n_readings, readings, &hw_ts, &local_ts, &err)) {
HCL_AccumulateSample(clock, &hw_ts, &local_ts, 2.0 * jitter);
new_sample = 1;
} else {
new_sample = 0;
}
}
TEST_CHECK(clock->valid_coefs == (clock->n_samples >= 2));
if (!clock->valid_coefs)
continue;
@@ -82,3 +108,10 @@ test_unit(void)
LCL_Finalise();
}
#else
void
test_unit(void)
{
TEST_REQUIRE(0);
}
#endif

View File

@@ -229,12 +229,8 @@ test_unit(void)
if (!inst || !can_auth_req)
add_dummy_auth(mode, key_id, &req, &req_info);
TEST_CHECK(req_info.auth.mode == mode);
memset(&req_info.auth, 0, sizeof (req_info.auth));
TEST_CHECK(NAU_ParsePacket(&req, &req_info));
TEST_CHECK(req_info.auth.mode == mode);
TEST_CHECK(req_info.auth.mac.key_id == key_id);
assert(req_info.auth.mode == mode);
assert(req_info.auth.mac.key_id == key_id);
kod = 1;
TEST_CHECK(NAU_CheckRequestAuth(&req, &req_info, &kod) == can_auth_req);
@@ -259,10 +255,8 @@ test_unit(void)
if (!can_auth_res)
add_dummy_auth(mode, key_id, &res, &res_info);
memset(&res_info.auth, 0, sizeof (res_info.auth));
TEST_CHECK(NAU_ParsePacket(&res, &res_info));
TEST_CHECK(res_info.auth.mode == mode);
TEST_CHECK(res_info.auth.mac.key_id == key_id);
assert(res_info.auth.mode == mode);
assert(res_info.auth.mac.key_id == key_id);
if (inst) {
if (mode == NTP_AUTH_SYMMETRIC) {

View File

@@ -331,12 +331,61 @@ process_replay(NCR_Instance inst, NTP_Packet *packet_queue,
advance_time(1e-6);
}
static void
add_dummy_auth(NTP_AuthMode auth_mode, uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *info)
{
unsigned char buf[64];
int len, fill;
info->auth.mode = auth_mode;
switch (auth_mode) {
case NTP_AUTH_NONE:
break;
case NTP_AUTH_SYMMETRIC:
case NTP_AUTH_MSSNTP:
case NTP_AUTH_MSSNTP_EXT:
switch (auth_mode) {
case NTP_AUTH_SYMMETRIC:
len = 16 + random() % 2 * 4;
fill = 1 + random() % 255;
break;
case NTP_AUTH_MSSNTP:
len = 16;
fill = 0;
break;
case NTP_AUTH_MSSNTP_EXT:
len = 68;
fill = 0;
break;
default:
assert(0);
}
assert(info->length + 4 + len <= sizeof (*packet));
*(uint32_t *)((unsigned char *)packet + info->length) = htonl(key_id);
info->auth.mac.key_id = key_id;
info->length += 4;
memset((unsigned char *)packet + info->length, fill, len);
info->length += len;
break;
case NTP_AUTH_NTS:
memset(buf, 0, sizeof (buf));
TEST_CHECK(NEF_AddField(packet, info, NTP_EF_NTS_AUTH_AND_EEF, buf, sizeof (buf)));
break;
default:
assert(0);
}
}
#define PACKET_QUEUE_LENGTH 10
void
test_unit(void)
{
char source_line[] = "127.0.0.1 maxdelaydevratio 1e6";
char source_line[] = "127.0.0.1 maxdelaydevratio 1e6 noselect";
char conf[][100] = {
"allow",
"port 0",
@@ -347,7 +396,8 @@ test_unit(void)
CPS_NTP_Source source;
NTP_Remote_Address remote_addr;
NCR_Instance inst1, inst2;
NTP_Packet packet_queue[PACKET_QUEUE_LENGTH];
NTP_Packet packet_queue[PACKET_QUEUE_LENGTH], packet;
NTP_PacketInfo info;
CNF_Initialise(0, 0);
for (i = 0; i < sizeof conf / sizeof conf[0]; i++)
@@ -361,6 +411,7 @@ test_unit(void)
NCR_Initialise();
REF_Initialise();
KEY_Initialise();
CLG_Initialise();
CNF_SetupAccessRestrictions();
@@ -372,6 +423,9 @@ test_unit(void)
source.params.version = random() % 4 + 1;
UTI_ZeroTimespec(&current_time);
#if HAVE_LONG_TIME_T
advance_time(NTP_ERA_SPLIT);
#endif
advance_time(TST_GetRandomDouble(1.0, 1e9));
TST_GetRandomAddress(&remote_addr.ip_addr, IPADDR_UNSPEC, -1);
@@ -442,7 +496,10 @@ test_unit(void)
send_request(inst1);
process_request(&remote_addr);
proc_response(inst1, 1, 1, 1, 0);
proc_response(inst1,
!source.params.interleaved || source.params.version != 4 ||
inst1->mode == MODE_ACTIVE || j != 2,
1, 1, 0);
advance_time(1 << inst1->local_poll);
}
@@ -504,6 +561,48 @@ test_unit(void)
NCR_DestroyInstance(inst2);
}
memset(&packet, 0, sizeof (packet));
packet.lvm = NTP_LVM(LEAP_Normal, NTP_VERSION, MODE_CLIENT);
TEST_CHECK(parse_packet(&packet, NTP_HEADER_LENGTH, &info));
TEST_CHECK(info.auth.mode == NTP_AUTH_NONE);
TEST_CHECK(parse_packet(&packet, NTP_HEADER_LENGTH, &info));
add_dummy_auth(NTP_AUTH_SYMMETRIC, 100, &packet, &info);
memset(&info.auth, 0, sizeof (info.auth));
TEST_CHECK(parse_packet(&packet, info.length, &info));
TEST_CHECK(info.auth.mode == NTP_AUTH_SYMMETRIC);
TEST_CHECK(info.auth.mac.start == NTP_HEADER_LENGTH);
TEST_CHECK(info.auth.mac.length == info.length - NTP_HEADER_LENGTH);
TEST_CHECK(info.auth.mac.key_id == 100);
TEST_CHECK(parse_packet(&packet, NTP_HEADER_LENGTH, &info));
add_dummy_auth(NTP_AUTH_NTS, 0, &packet, &info);
memset(&info.auth, 0, sizeof (info.auth));
TEST_CHECK(parse_packet(&packet, info.length, &info));
TEST_CHECK(info.auth.mode == NTP_AUTH_NTS);
packet.lvm = NTP_LVM(LEAP_Normal, 3, MODE_CLIENT);
TEST_CHECK(parse_packet(&packet, NTP_HEADER_LENGTH, &info));
add_dummy_auth(NTP_AUTH_MSSNTP, 200, &packet, &info);
memset(&info.auth, 0, sizeof (info.auth));
TEST_CHECK(parse_packet(&packet, info.length, &info));
TEST_CHECK(info.auth.mode == NTP_AUTH_MSSNTP);
TEST_CHECK(info.auth.mac.start == NTP_HEADER_LENGTH);
TEST_CHECK(info.auth.mac.length == 20);
TEST_CHECK(info.auth.mac.key_id == 200);
TEST_CHECK(parse_packet(&packet, NTP_HEADER_LENGTH, &info));
add_dummy_auth(NTP_AUTH_MSSNTP_EXT, 300, &packet, &info);
memset(&info.auth, 0, sizeof (info.auth));
TEST_CHECK(parse_packet(&packet, info.length, &info));
TEST_CHECK(info.auth.mode == NTP_AUTH_MSSNTP_EXT);
TEST_CHECK(info.auth.mac.start == NTP_HEADER_LENGTH);
TEST_CHECK(info.auth.mac.length == 72);
TEST_CHECK(info.auth.mac.key_id == 300);
CLG_Finalise();
KEY_Finalise();
REF_Finalise();
NCR_Finalise();

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