Compare commits

...

229 Commits

Author SHA1 Message Date
Miroslav Lichvar
43189651b0 doc: update NEWS 2024-07-30 14:05:42 +02:00
Miroslav Lichvar
f518b8d00f doc: update README 2024-07-30 12:11:09 +02:00
Miroslav Lichvar
42b3c40c32 doc: fix typo in kod option description 2024-07-30 12:11:09 +02:00
Miroslav Lichvar
66512ebcb3 ntp: make sure new configuration IDs are unused
The configuration IDs assigned to individual sources (used when they
don't have a resolved IP address) and pools of sources are 32-bit. The
ID could overflow if some sources were very frequently removed and added
again. Two unrelated sources could end up with the same ID, causing some
operations to unexpectedly impact only one or both sources.

Make sure the ID is currently unused before assigning it to a new source.
2024-07-30 12:11:09 +02:00
Miroslav Lichvar
3940d2aae3 leapdb: add explicit cast to int64_t
Add an explicit cast to int64_t to not rely on LEAP_SEC_LIST_OFFSET
not fitting in 32-bit time_t.
2024-07-30 12:09:53 +02:00
Miroslav Lichvar
34be117c9c main: check for killed foreground process
On start, if the foreground process waiting for the daemon process to
close the pipe (after finishing the RTC initialization, initstepslew,
etc) is killed, terminate the daemon too assuming that whatever killed
the foreground process it wanted all chronyd processes to stop.

In the daemon, before closing the pipe file descriptor, send an empty
message to check if the pipe isn't already closed on the other end.
2024-07-04 16:50:22 +02:00
Miroslav Lichvar
7915f52495 logging: add function to send message to foreground process 2024-07-04 16:50:22 +02:00
Miroslav Lichvar
05bd4898a9 test: fix 142-ntpoverptp 2024-06-20 15:10:42 +02:00
Miroslav Lichvar
4da088ec2f ntp: make NTP-over-PTP domain configurable
Add ptpdomain directive to set the domain number of transmitted and
accepted NTP-over-PTP messages. It might need to be changed in networks
using a PTP profile with the same domain number. The default domain
number of 123 follows the current NTP-over-PTP specification.
2024-06-20 15:00:17 +02:00
Miroslav Lichvar
c46e0549ab ntp: update NTP-over-PTP support
Following the latest version of the draft, accept NTP messages in both
PTPv2 and PTPv2.1 messages, accept sync messages in addition to delay
request messages, and check the minorSdoId field in PTPv2.1 messages.

Transmitted messages are still PTPv2 delay requests.

Don't switch to the organization-specific TLV yet. Wait for the NTP TLV
subtype and Network Correction extension field to be assigned by IANA to
avoid an additional break in compatibility.
2024-06-20 14:35:28 +02:00
Miroslav Lichvar
8f5b308414 test: make 124-tai more reliable
Reported-by: Reinhard Max <max@suse.de>
2024-06-04 16:25:55 +02:00
Miroslav Lichvar
084fe6b0cc doc: clarify prefer source option 2024-06-04 16:25:55 +02:00
Miroslav Lichvar
ebfc676d74 ntp: limit offset correction to supported NTP interval
When an NTP source is specified with the offset option, the corrected
offset may get outside of the supported NTP interval (by default -50..86
years around the build date). If the source passed the source selection,
the offset would be rejected only later in the adjustment of the local
clock.

Check the offset validity as part of the NTP test A to make the source
unselectable and make it visible in the measurements log and ntpdata
report.
2024-05-02 14:43:51 +02:00
Miroslav Lichvar
adaca0ff19 reference: switch is_leap_close() from time_t to double
Avoid undefined behavior in the timestamp conversion from double to
time_t in REF_IsLeapSecondClose() with NTP sources configured with a
large offset correction.
2024-05-02 14:43:46 +02:00
Miroslav Lichvar
84d6c7a527 sources: allow logging one selection failure on start
Allow one message about failed selection (e.g. no selectable sources)
to be logged before first successful selection when a source has
full-size reachability register (8 polls with a received or missed
response).

This should make it more obvious that chronyd has a wrong configuration
or there is a firewall/networking issue.
2024-05-02 12:51:38 +02:00
Miroslav Lichvar
c43efccf02 sources: update source selection with unreachable sources
When updating the reachability register of a source with zero, call the
source selection even if the source is not the currently selected as the
best source. But do that only if all reachability bits are zero, i.e.
there was no synchronized response for last 8 polls.

This will enable the source selection to log a message when only
unreachable sources are updating reachability and it decreases the
number of unnecessary source selections.
2024-04-30 15:52:18 +02:00
Miroslav Lichvar
1affd03cca sources: reorder unsynchronised source status
In the source selection, check for the unsynchronized leap status after
getting sourcestats data. The unsynchronized source status is supposed
to indicate an unsynchronized source that is providing samples, not a
source which doesn't have any samples.

Also, fix the comment describing the status.

Fixes: 4c29f8888c ("sources: handle unsynchronized sources in selection")
2024-04-30 15:52:18 +02:00
Miroslav Lichvar
276591172e ntp: improve copying of server status
When a server specified with the copy option responds with an
unsynchronized status (e.g. due to selection failure), reset the
source instance to immediately switch the local reference status
instead of waiting for the source to become unreachable after 8 polls.
2024-04-29 11:21:45 +02:00
Rob Gill
989ef702aa doc: fix typo in README
Typo correction only - no change to content

Signed-off-by: Rob Gill <rrobgill@protonmail.com>
2024-04-16 07:50:11 +02:00
Rob Gill
1920b1efde doc: fix typo in chronyc docs
Typo fix only - no change to content

Signed-off-by: Rob Gill <rrobgill@protonmail.com>
2024-04-16 07:49:59 +02:00
Miroslav Lichvar
bb5db828c6 ntp: log failed connection to Samba signd socket
Log an error message (in addition to the socket-specific debug message)
when the connection to signd socket fails, but only once before a
successful signd exchange to avoid flooding the system log.
2024-04-15 16:35:33 +02:00
Miroslav Lichvar
dcc94a4c10 doc: add contributing.adoc 2024-04-11 16:52:06 +02:00
Miroslav Lichvar
2ed72c49c9 test: add --enable-debug option to 002-scanbuild 2024-04-11 12:53:01 +02:00
Miroslav Lichvar
342b588e3b avoid some static analysis errors
Modify the code to avoid some false positives reported by the clang and
gcc static analyzers.
2024-04-11 10:31:02 +02:00
Miroslav Lichvar
a914140bd4 sys_linux: disable other external timestamping channels
Use new ioctls added in Linux 6.7 to disable receiving events from other
channels when enabling external timestamping on a PHC. This should save
some CPU time when other applications or chronyd instances are using
other channels of the same PHC.
2024-04-10 15:33:04 +02:00
Miroslav Lichvar
28e4eec1c4 refclock: update comment in PHC driver
Since Linux 6.7 external timestamping events are no longer shared among
all descriptors of a PHC. Each descriptor gets its own copy of each
timestamp.
2024-04-10 12:13:29 +02:00
Miroslav Lichvar
5235c51801 cmdmon: add reserved fields to local command
Add two reserved fields initialized to zero to the new REQ_LOCAL3
command to allow adding more options (e.g. delay in activation) without
changing the command number again.
2024-04-04 16:24:43 +02:00
Miroslav Lichvar
26ea4e35e7 test: add tests of local directive options 2024-04-04 16:24:02 +02:00
Andy Fiddaman
9397ae2b0a reference: add "local activate" option
This option sets an activating root distance for the local reference. The
local reference will not be used until the root distance drops below the
configured value for the first time. This can be used to prevent the local
reference from being activated on a server which has never been synchronised
with an upstream server. The default value of 0.0 causes no activating
distance to be used, such that the local reference is always eligible for
activation.
2024-04-04 15:17:05 +02:00
Miroslav Lichvar
b8ead3485b leapdb: fix leapsec list processing with 32-bit time_t
A 32-bit time_t value overflows when converted to the Y1900 epoch used
in the leapsec list. Use a 64-bit variable in get_list_leap() to fix the
comparisons on systems using 32-bit time_t.

Fixes: 53823b9f1c ("leapdb: support leap-seconds.list as second source")
2024-04-03 11:01:44 +02:00
Miroslav Lichvar
24d28cd679 ntp: add server support for KoD RATE
Add "kod" option to the ratelimit directive to respond with the KoD
RATE code to randomly selected requests exceeding the configured limit.
This complements the client support of KoD RATE. It's disabled by
default.

There can be only one KoD code in one response. If both NTS NAK and RATE
codes are triggered, drop the response. The KoD RATE code can be set in
an NTS-authenticated response.
2024-04-02 15:39:12 +02:00
Miroslav Lichvar
aac898343e clientlog: add support for KoD rate limiting
Add a third return value to CLG_LimitServiceRate() to indicate the
server should send a response requesting the client to reduce its
polling rate. It randomly selects from a fraction (configurable to 1/2,
1/4, 1/8, 1/16, or disabled) of responses which would be dropped
(after selecting responses for the leak option).
2024-04-02 15:23:26 +02:00
Miroslav Lichvar
c8c7f518b1 clientlog: return enum from CLG_LimitServiceRate()
Change CLG_LimitServiceRate() to return an enum in preparation for
adding KoD RATE support.
2024-04-02 11:55:02 +02:00
Miroslav Lichvar
ce956c99a8 nts: check for NTS NAK specifically when responding
Ignore other KoD codes than NTS NAK when deciding if the server response
should not be authenticated.
2024-04-02 11:33:04 +02:00
Miroslav Lichvar
863866354d ntp: avoid unnecessary restart of resolving round on refresh
Don't call NSR_ResolveSources() when a resolving round is already
started. This cuts the number of calls of the system resolver made due
to the refresh command to half.
2024-03-14 16:39:41 +01:00
Miroslav Lichvar
6e5513c80b ntp: don't keep refresh requests in list of unresolved sources
The refresh command adds requests to reresolve addresses of all sources.
If some sources didn't have an IP address resolved yet, the
corresponding requests were not removed after failed resolving. Repeated
refresh commands increased the number of requests and number of calls of
the system resolver, which might not be caching DNS responses.

Remove all refresh requests from the list after resolving attempt to fix
that.

Reported-by: t.barnewski@avm.de
Fixes: d7e3ad17ff ("ntp: create sources for unresolved addresses")
2024-03-14 16:39:41 +01:00
Miroslav Lichvar
6d0143e963 ntp: add more debug messages for resolving 2024-03-14 16:39:32 +01:00
Miroslav Lichvar
f49be7f063 conf: don't load sourcedir during initstepslew and RTC init
If the reload sources command was received in the chronyd start-up
sequence with initstepslew and/or RTC init (-s option), the sources
loaded from sourcedirs caused a crash due to failed assertion after
adding sources specified in the config.

Ignore the reload sources command until chronyd enters the normal
operation mode.

Fixes: 519796de37 ("conf: add sourcedirs directive")
2024-03-12 14:57:30 +01:00
Miroslav Lichvar
7fe98a83b8 test: replace another C99-style declaration in for loop 2024-03-11 12:00:12 +01:00
Miroslav Lichvar
ad37c409c9 cmdmon: add offset command
Add a new command to modify the offset option of NTP sources and
reference clocks.
2024-03-07 16:20:27 +01:00
Miroslav Lichvar
719c6f6a8a ntp+refclock: add functions to modify offset option 2024-03-07 16:19:04 +01:00
Miroslav Lichvar
b0750136b5 rtc+getdate: initialize tm_wday for mktime()
Even though mktime() is documented as ignoring the tm_wday field, the
coverity static analyzer complains about passing an uninitialized value.
Set the field to zero to make it happy.
2024-03-04 11:38:16 +01:00
Miroslav Lichvar
ad79aec946 test: avoid C99-style declaration in for loop
This fixes compilation without the -std=c99 option with an older gcc.
2024-03-04 11:38:11 +01:00
Miroslav Lichvar
008dc16727 examples: switch chrony.conf examples to leapseclist 2024-02-08 16:21:42 +01:00
Miroslav Lichvar
6cf9fe2f16 test: improve 113-leapsecond and 124-tai tests
Use leapseclist instead of leapsectz and test also negative leap
seconds. Add a test for leapsectz when the date command indicates
right/UTC is available on the system and mktime() works as expected.
Check TAI offset in the server's log.
2024-02-08 15:54:24 +01:00
Patrick Oppenlander
637b77d1bd test: add leapdb unit test 2024-02-08 15:54:24 +01:00
Patrick Oppenlander
53823b9f1c leapdb: support leap-seconds.list as second source
The existing implementation of getting leap second information from a
timezone in get_tz_leap() relies on non-portable C library behaviour.

Specifically, mktime is not required to return '60' in the tm_sec field
when a leap second is inserted leading to "Timezone right/UTC failed
leap second check, ignoring" errors on musl based systems.

This patch adds support for getting leap second information from the
leap-seconds.list file included with tzdata and adds a new configuration
directive leapseclist to switch on the feature.
2024-02-08 15:54:24 +01:00
Patrick Oppenlander
83f90279b0 leapdb: move source check into separate function
The sanity checks are valid for all possible sources of leap second
information, so move them into a separate function check_leap_source().
2024-02-08 15:54:21 +01:00
Patrick Oppenlander
02ae9a8607 leapdb: make twice per day check logic common
We want to do the twice per day check regardless of the data source.
Move the check up one level from get_tz_leap() into LDB_GetLeap().
2024-02-08 12:54:37 +01:00
Patrick Oppenlander
017d6f8f56 reference: move leap second source into leapdb
Separate out source of leap second data into a new module in preparation
for supporting more sources such as leap-seconds.list.
2024-02-08 12:54:37 +01:00
Miroslav Lichvar
eb26d13140 cmdmon: add timestamp counters to ntpdata report 2024-02-07 10:23:40 +01:00
Miroslav Lichvar
8d19f49341 ntp: add per-source counters of kernel and hardware timestamps 2024-02-07 10:23:40 +01:00
Miroslav Lichvar
637fa29e1e cmdmon: add ipv4/ipv6 options to add source command 2024-02-07 10:23:40 +01:00
Miroslav Lichvar
2d349595ee cmdmon: simplify flag checking in handle_add_source() 2024-02-07 10:23:40 +01:00
Miroslav Lichvar
5cb584d6c1 conf: add ipv4 and ipv6 options to server/pool/peer directive
Accept "ipv4" and "ipv6" options in the server/pool/peer directive to
use only IPv4 or IPv6 addresses respectively.

The configuration is different from the "server [-4|-6] hostname" syntax
supported by ntpd to avoid breaking existing scripts which expect the
hostname to always be the first argument of the directives.
2024-02-07 10:23:40 +01:00
Miroslav Lichvar
d7c2b1d2f3 ntp: support per-source IP family restriction
Add a new parameter to the NSR_AddSourceByName() function to allow
individual sources to be limited to IPv4 or IPv6 addresses. This doesn't
change the options passed to the resolver. It's just an additional
filter in the processing of resolved addresses following the -4/-6
command-line option of chronyd.
2024-02-07 10:23:36 +01:00
Miroslav Lichvar
e11b518a1f ntp: fix authenticated requests in serverstats
Fix the CLG_UpdateNtpStats() call to count requests passing the
authentication check instead of requests triggering a KoD response
(i.e. NTS NAK).
2024-01-08 11:46:32 +01:00
Miroslav Lichvar
120dfb8b36 update copyright years 2023-12-05 14:22:10 +01:00
Miroslav Lichvar
598b893e1d doc: update FAQ on improving accuracy 2023-12-05 14:22:10 +01:00
Miroslav Lichvar
89aa8fa342 doc: mention dependency of net corrections on HW timestamping 2023-12-05 14:22:08 +01:00
Miroslav Lichvar
42fdad5dcc doc: improve description of reload sources command 2023-12-04 16:50:51 +01:00
Miroslav Lichvar
3ee7b3e786 sources: rework logging of selection loss
The commit 5dd288dc0c ("sources: reselect earlier when removing
selected source") didn't cover all paths that can lead to a missing log
message when all sources are removed.

Add a flag to track the loss of selection and postpone the log message
in transient states where no message is logged to avoid spamming in
normal operation. Call SRC_SelectSource() after removing the source
to get a log message if there are no (selectable) sources left.

Reported-by: Thomas Lange <thomas@corelatus.se>
2023-11-28 12:21:23 +01:00
Miroslav Lichvar
426fe2ee58 doc: update NEWS 2023-11-22 13:53:23 +01:00
Miroslav Lichvar
3f66202d79 doc: update README 2023-11-22 11:56:16 +01:00
Miroslav Lichvar
ed6b0b55c7 doc: replace foobar naming in examples 2023-11-22 11:56:15 +01:00
Miroslav Lichvar
5e5adbea0c doc: update description of NTP over PTP in FAQ 2023-11-22 11:56:15 +01:00
Miroslav Lichvar
82959431df doc: mention version supporting socket activation in FAQ 2023-11-22 11:56:15 +01:00
Miroslav Lichvar
b92b2da24a doc: improve ntstrustedcerts description 2023-11-22 11:55:27 +01:00
Miroslav Lichvar
68a3d52086 doc: improve cmdport description 2023-11-22 09:48:36 +01:00
Miroslav Lichvar
1a15be1e9e sources: drop unreachable log message
With forced reselection during source removal selected_source_index
can only be INVALID_SOURCE if there are no sources. The "Can't
synchronise: no sources" message couldn't be logged even before that as
SRC_ReselectSource() resets the index before calling SRC_SelectSource().

Replace the message with an assertion.
2023-11-21 12:38:41 +01:00
Miroslav Lichvar
5dd288dc0c sources: reselect earlier when removing selected source
When a selected source is being removed, reset the instance and rerun
the selection while the source is still marked as selected. This forces
a "Can't synchronise" message to be logged when all sources are removed.

Reported-by: Thomas Lange <thomas@corelatus.se>
2023-11-21 12:38:41 +01:00
Miroslav Lichvar
cbee464c75 sources: reselect after resetting selected source
Avoid showing in the sources report a selected source which has no
samples (e.g. after replacement).
2023-11-21 12:38:41 +01:00
Miroslav Lichvar
4e36295889 ntp: allow reusing timestamps of unused samples
When switching from basic mode to interleaved mode following a response
which wasn't accumulated due to failed test A, B, C, or D, allow
timestamps of the failed sample to be reused in interleaved mode, i.e.
replacing the server's less accurate transmit timestamp with a more
accurate timestamp server can turn a failed sample into acceptable one.

Move the presend check into test A to simplify the code.
2023-11-21 12:38:41 +01:00
Miroslav Lichvar
2d2642bb82 ntp: fix presend in interleaved mode
The presend option in interleaved mode uses two presend requests instead
of one to get an interleaved response from servers like chrony which
delay the first interleaved response due to an optimization saving
timestamps only for clients actually using the interleaved mode.

After commit 0ae6f2485b ("ntp: don't use first response in interleaved
mode") the first interleaved response following the two presend
responses in basic mode is dropped as the preferred set of timestamps
minimizing error in delay was already used by the second sample in
basic mode. There are only three responses in the burst and no sample is
accumulated.

Increasing the number of presend requests to three to get a fourth
sample would be wasteful. Instead, allow reusing timestamps of the
second presend sample in basic mode, which is never accumulated.

Reported-by: Aaron Thompson
Fixes: 0ae6f2485b ("ntp: don't use first response in interleaved mode")
2023-11-21 12:38:41 +01:00
Miroslav Lichvar
9c6eaccc32 nts: close reusable sockets in helper process
Close all reusable sockets when the NTS-KE server helper is forked. It
is not supposed to have access to any of the server sockets, just the
socket for getting requests from the main process and the syslog socket.
2023-11-21 12:38:41 +01:00
Miroslav Lichvar
0aa4d5ac14 socket: provide function for closing reusable sockets 2023-11-21 12:38:39 +01:00
Miroslav Lichvar
ee9d721b7b socket: set close-on-exec on all reusable sockets
Set the CLOEXEC flag on all reusable sockets in the initialization to
avoid leaking them to sendmail (mailonchange directive) in case the
chrony configuration doesn't use all sockets provided by systemd.
2023-11-20 13:33:45 +01:00
Luke Valenta
b6eec0068a doc: add FAQ section on minimizing service downtime 2023-11-13 17:05:46 +01:00
Luke Valenta
e6a0476eb7 socket: add support for systemd sockets
Before opening new IPv4/IPv6 server sockets, chronyd will check for
matching reusable sockets passed from the service manager (for example,
passed via systemd socket activation:
https://www.freedesktop.org/software/systemd/man/latest/sd_listen_fds.html)
and use those instead.

Aside from IPV6_V6ONLY (which cannot be set on already-bound sockets),
the daemon sets the same socket options on reusable sockets as it would
on sockets it opens itself.

Unit tests test the correct parsing of the LISTEN_FDS environment
variable.

Add 011-systemd system test to test socket activation for DGRAM and
STREAM sockets (both IPv4 and IPv6).  The tests use the
systemd-socket-activate test tool, which has some limitations requiring
workarounds discussed in inline comments.
2023-11-13 17:05:26 +01:00
Luke Valenta
c063b9e78a logging: move severity_chars to fix compiler warning 2023-11-08 15:09:03 +01:00
Luke Valenta
f6f1863fe2 logging: add log severity to file log prefix when debug is enabled 2023-11-07 15:43:57 +01:00
Miroslav Lichvar
51a621bc2b ntp: initialize network correction of transmitted packets
Initialize the unused value of network correction parsed from
own transmitted packets to avoid a use-of-uninitialized-value error
in NIO_UnwrapMessage() reported by clang.

Fixes: 6372a9f93f ("ntp: save PTP correction from NTP-over-PTP messages")
2023-11-02 12:53:00 +01:00
Luke Valenta
1488b31a38 doc: document '-L -1' option for debug logging output 2023-10-24 11:37:56 +02:00
Miroslav Lichvar
70cdd8b1ef ntp: add client support for network correction
If the network correction is known for both the request and response,
and their sum is not larger that the measured peer delay, allowing the
transparent clocks to be running up to 100 ppm faster than the client's
clock, apply the corrections to the NTP offset and peer delay. Don't
correct the root delay to not change the estimated maximum error.
2023-09-26 15:14:13 +02:00
Miroslav Lichvar
8eef631009 ntp: add server support for network correction
Provide the network correction (PTP correction + RX duration) of the
request in the new extension field if included in the request and
NTP-over-PTP is enabled.
2023-09-26 15:14:13 +02:00
Miroslav Lichvar
d9ae724c60 ntp: add extension field to provide network correction
To be able to verify PTP corrections, the client will need to know both
the correction of the request received by the server and the correction
of the response. Add a new experimental NTP extension field that the
clients will use to request the correction and servers return the
value.
2023-09-26 15:14:13 +02:00
Miroslav Lichvar
6372a9f93f ntp: save PTP correction from NTP-over-PTP messages
When the RX duration is known (HW timestamping), save the PTP correction
from received PTP messages in the local RX timestamp.
2023-09-26 15:14:13 +02:00
Miroslav Lichvar
b0267475e3 ntp: extend local timestamp for PTP correction
Add two new fields to the NTP_Local_Timestamp structure:
- receive duration as the time it takes to receive the ethernet frame,
  currently known only with HW timestamping
- network correction as a generalized PTP correction

The PTP correction is provided by transparent clocks in the correction
field of PTP messages to remove the receive, processing and queueing
delays of network switches and routers. Only one-step end-to-end unicast
transparent clocks are useful for NTP-over-PTP. Two-step transparent
clocks use follow-up messages and peer-to-peer transparent clocks don't
handle delay requests.

The RX duration will be included in the network correction to compensate
for asymmetric link speeds of the server and client as the NTP RX
timestamp corresponds to the end of the reception (in order to
compensate for the asymmetry in the normal case when no corrections
are applied).
2023-09-26 15:10:19 +02:00
Miroslav Lichvar
07134f2625 ntp: add function for detection of experimental fields 2023-09-26 15:03:33 +02:00
Miroslav Lichvar
85db8e3a9c ntp: assert size of exp_mono_root field 2023-09-26 15:02:06 +02:00
Miroslav Lichvar
05f4f79cbf ntp: rename exp1 extension field
Rename the exp1 extension field to exp_mono_root (monotonic timestamp +
root delay/dispersion) to better distinguish it from future experimental
extension fields.
2023-09-26 15:01:24 +02:00
Miroslav Lichvar
bf616eafa1 util: add conversion between intervals and NTP 64-bit format
This will be needed to save PTP correction in NTP timestamp format.
2023-09-26 15:00:06 +02:00
Miroslav Lichvar
e08a0ee668 doc: don't require same version for experimental features 2023-09-26 14:58:42 +02:00
Miroslav Lichvar
f2d7baa94f configure: prefer gnutls over nss and tomcrypt for hashing
Reorder the tests in the configure script to prefer gnutls over nss and
tomcrypt as its support includes AES-CMAC.
2023-09-12 10:36:23 +02:00
Miroslav Lichvar
558931524d configure: don't try AES-SIV-CMAC in nettle when disabled
Avoid confusing message when --without-nettle is specified.
2023-09-12 10:31:36 +02:00
Miroslav Lichvar
a74b63277a siv: add support for AES-GCM-SIV in gnutls
Add support for AES-128-GCM-SIV in the current development code of
gnutls. There doesn't seem to be an API to get the cipher's minimum and
maximum nonce length and it doesn't check for invalid lengths. Hardcode
and check the limits in chrony for now.
2023-09-12 10:31:36 +02:00
Miroslav Lichvar
aa8196328c conf: improve log message for failed additions in sources reload
Describe the error status in the log message when adding a source from
sourcedir failed.
2023-09-12 08:11:25 +02:00
Miroslav Lichvar
37deee7140 conf: cast subtraction operands in source comparison
Cast the values to int to not break the sorting in case they are changed
to unsigned types.
2023-09-12 08:03:23 +02:00
Miroslav Lichvar
7ff74d9efe conf: fix reloading modified sources specified by IP address
When reloading a modified source from sourcedir which is ordered before
the original source (e.g. maxpoll was decreased), the new source is
added before the original one is removed. If the source is specified by
IP address, the addition fails due to the conflict with the original
source. Sources specified by hostname don't conflict. They are resolved
later (repeatedly if the resolver provides only conflicting addresses).

Split the processing of sorted source lists into two phases, so all
modified sources are removed before they are added again to avoid the
conflict.

Reported-by: Thomas Lange <thomas@corelatus.se>
2023-09-12 08:02:36 +02:00
Miroslav Lichvar
43320a1d6b doc: update NEWS and README 2023-08-09 15:20:55 +02:00
Josef 'Jeff' Sipek
8caaa0b056 socket: enable nanosecond resolution RX timestamp on FreeBSD
FreeBSD allows switching the receive timestamp format to struct timespec by
setting the SO_TS_CLOCK socket option to SO_TS_REALTIME after enabling
SO_TIMESTAMP.  If successful, the kernel then starts adding SCM_REALTIME
control messages instead of SCM_TIMESTAMP.
2023-08-09 15:19:46 +02:00
Miroslav Lichvar
e48a34392c test: make 139-nts more reliable 2023-08-08 17:11:46 +02:00
Miroslav Lichvar
8bc8bf9cc4 test: make 114-presend more reliable
Avoid frequently ending in the middle of a client/server exchange with
long delays. This changed after commit 4a11399c2e ("ntp: rework
calculation of transmit timeout").
2023-08-08 16:24:10 +02:00
Miroslav Lichvar
3dc9f1ff92 ntp: don't require previous HW TX timestamp to wait for another
Clients sockets are closed immediately after receiving valid response.
Don't wait for the first early HW TX timestamp to enable waiting for
late timestamps. It may take a long time or never come if the HW/driver
is consistently slow. It's a chicken and egg problem.

Instead, simply check if HW timestamping is enabled on at least one
interface. Responses from NTP sources on other interfaces will always be
saved (for 1 millisecond by default).
2023-08-08 16:06:58 +02:00
Miroslav Lichvar
7bc7d00297 ntp: fix adding noselect to selection options
If noselect is present in the configured options, don't assume it
cannot change and the effective options are equal. This fixes chronyc
selectopts +noselect command.

Fixes: 3877734814 ("sources: add function to modify selection options")
2023-08-07 14:58:48 +02:00
Bryan Christianson
b5cf861cd7 contrib: replace tuxfamily with chrony-project
The chrony project has moved from tuxfamily.org to chrony-project.org.
Reflect these changes in the macOS startup files and documentation.
2023-08-01 12:11:27 +02:00
Miroslav Lichvar
25cc84d5e2 doc: update links to chrony website 2023-07-27 13:05:23 +02:00
Miroslav Lichvar
f74e4cf1fe doc: don't mention mailing lists in README
Current information about mailing lists is available on the project's
website.
2023-07-26 16:33:49 +02:00
Miroslav Lichvar
5f66722b66 update copyright years 2023-07-20 12:57:33 +02:00
Miroslav Lichvar
b31461af7a doc: add more questions to FAQ 2023-07-20 12:55:31 +02:00
Miroslav Lichvar
ae177f2742 doc: fix typo in FAQ 2023-07-20 12:47:11 +02:00
Miroslav Lichvar
1a736078df doc: refer to root distance in chronyc sources report 2023-07-20 12:46:49 +02:00
Miroslav Lichvar
9b46ea7255 test: make 132-logchange more reliable 2023-07-18 15:16:03 +02:00
Miroslav Lichvar
ff4e932f17 test: make 148-replacement more reliable 2023-07-18 15:15:45 +02:00
Miroslav Lichvar
68c35a0072 test: improve ntp_sources unit test 2023-07-18 14:53:54 +02:00
Miroslav Lichvar
b6c634298d ntp: handle negotiated NTS-KE server in refreshment
When refreshing a source, compare the newly resolved addresses with the
originally resolved address instead of the current address to avoid
unnecessary replacements when the address is changed due to the NTS-KE
server negotiation.
2023-07-18 14:53:54 +02:00
Miroslav Lichvar
010df12459 nts: fix log severity for loaded server keys 2023-07-18 14:53:54 +02:00
Miroslav Lichvar
22ef2fbb0e makefile: compile getdate.o with -fwrapv option
The getdate code (copied from gnulib before it was switched to GPLv3)
has multiple issues with signed integer overflows. Use the -fwrapv
compiler option for this object to at least make the operations defined.
2023-07-18 14:52:55 +02:00
Miroslav Lichvar
7a03206222 doc: update NEWS 2023-06-21 11:28:54 +02:00
Miroslav Lichvar
b86c50bb9f ntp: refresh IP addresses periodically
Refresh NTP sources specified by hostname periodically (every 2 weeks
by default) to avoid long-running instances using a server which is no
longer intended for service, even if it is still responding correctly
and would not be replaced as unreachable, and help redistributing load
in large pools like pool.ntp.org. Only one source is refreshed at a time
to not interrupt clock updates if there are multiple selectable servers.

The refresh directive configures the interval. A value of 0 disables
the periodic refreshment.

Suggested-by: Ask Bjørn Hansen <ask@develooper.com>
2023-06-21 11:28:42 +02:00
Miroslav Lichvar
36f9b24dfe doc: remove out-of-date statement in server description
chronyc refresh no longer forces replacement of sources.

Fixes: b2dac47c82 ("ntp: avoid unneccessary replacements on refresh command")
2023-06-20 15:28:07 +02:00
Miroslav Lichvar
e0b75b87bf ntp: remove resolving timeout in finalization
Don't assume NSR_Finalise() can be called only on exit when the
scheduler is finalized.
2023-06-20 13:03:53 +02:00
Miroslav Lichvar
6661a61486 sched: reset timer queue in finalization
Don't leave dangling pointers to timer queue entries when they are
freed in the scheduler finalization in case some code tried to remove
a timer later.

Fixes: 6ea1082a72 ("sched: free timer blocks on exit")
2023-06-19 16:15:07 +02:00
Miroslav Lichvar
bc76291750 examples: don't set ProcSubset=pid in systemd unit files
This option seems to break detection of the FIPS mode, which is needed
by gnutls.
2023-06-15 15:23:40 +02:00
Miroslav Lichvar
2aefadd129 sources: delay source replacement
Wait for four consecutive source selections giving a bad status
(falseticker, bad distance or jittery) before triggering the source
replacement. This should reduce the rate of unnecessary replacements
and shorten the time needed to find a solution when unreplaceable
falsetickers are preventing other sources from forming a majority due
to switching back and forth to unreachable servers.
2023-06-15 14:23:47 +02:00
Miroslav Lichvar
123cb497b9 sources: replace reachable sources in selection
Instead of waiting for the next update of reachability, trigger
replacement of falsetickers, jittery and distant sources as soon as
the selection status is updated in their SRC_SelectSource() call.
2023-06-15 14:23:47 +02:00
Miroslav Lichvar
0c38e4a6ca ntp: reset poll score
When the polling interval is reset (e.g. after replacement), don't
forget to reset also the score impacting the next poll adjustment.
2023-06-15 14:23:47 +02:00
Miroslav Lichvar
0db30fd0b1 main: wait for parent process to terminate
When starting the daemon, wait in the grandparent process for the parent
process to terminate before exiting to avoid systemd logging a warning
"Supervising process $PID which is not our child". Waiting for the pipe
to be closed by the kernel when the parent process exits is not
sufficient.

Reported-by: Jan Pazdziora <jpazdziora@redhat.com>
2023-06-12 16:40:53 +02:00
Miroslav Lichvar
b90d2c084f ntp: randomize replacement interval
Replacement attempts are globally rate limited to one per 7*2^8 seconds
to limit the rate of DNS requests for public servers like pool.ntp.org.
If multiple sources are repeatedly attempting replacement (at their
polling intervals), one source can be getting all attempts for periods
of time.

Use a randomly generated interval to randomize the order of source
replacements without changing the average rate.
2023-06-08 16:14:48 +02:00
Miroslav Lichvar
ab8da7ecb9 ntp: use monotonic time for replacement interval
Avoid errors in the measured interval due to clock steps.
2023-06-08 16:10:26 +02:00
Miroslav Lichvar
05809e937c ntp: add debug message for bad sources 2023-06-08 16:10:26 +02:00
Miroslav Lichvar
8265fe2e30 client: check for allocation errors in tab completition 2023-06-08 16:10:26 +02:00
Miroslav Lichvar
c11a052955 client: avoid passing uninitialized address to format_name()
The clang memory sanitizer seems to trigger on an uninitialized value
passed to format_name() when the source is a refclock, even though the
value is not used for anything. Pass 0 in this case to avoid the error.
2023-06-08 15:56:19 +02:00
Miroslav Lichvar
109970f687 memory: use free() instead of realloc() for size 0
valgrind 3.21.0 reports realloc() of 0 bytes as an error due to having
different behavior on different systems. The only place where this can
happen in chrony is the array, which doesn't care what value realloc()
returns.

Modify the realloc wrapper to call free() in this case to make valgrind
happy.
2023-06-08 14:31:52 +02:00
Miroslav Lichvar
ca10b9e072 sys_linux: allow writev and TIOCGWINSZ in seccomp filter
Allow more syscalls for musl.

Reported-by: jvoisin <julien.voisin@dustri.org>
2023-06-01 14:52:56 +02:00
Miroslav Lichvar
19da1d95a8 test: set root ownership of tmp directory in system tests
Allow the tests to be started under a non-zero GID.
2023-06-01 14:52:56 +02:00
Miroslav Lichvar
61da7d0913 test: modify order of scfilter levels in system tests
Start with positive levels to get the offending system calls in the
system or audit log.
2023-06-01 14:52:56 +02:00
Miroslav Lichvar
105f1f90c1 test: fix 010-nts test for AES-GCM-SIV support 2023-06-01 14:52:51 +02:00
Miroslav Lichvar
c9d791e02d nts: don't load zero-length keys with unsupported algorithm
Don't load keys and cookies from the client's dump file if it has an
unsupported algorithm and unparseable keys (matching the algorithm's
expected length of zero). They would fail all SIV operations and trigger
new NTS-KE session.
2023-05-29 16:08:13 +02:00
Miroslav Lichvar
de678ff780 doc: clarify limitation of refresh command 2023-05-25 10:28:56 +02:00
Miroslav Lichvar
e16bcca617 sys_linux: allow membarrier in seccomp filter
This system call is used by musl.

Reported-by: jvoisin <julien.voisin@dustri.org>
2023-05-25 10:28:56 +02:00
Miroslav Lichvar
b57d7040b3 configure: add option to disable AES-GCM-SIV support 2023-05-25 10:28:56 +02:00
Miroslav Lichvar
c80858f738 nts: remove superfluous semicolon 2023-05-25 10:28:56 +02:00
Miroslav Lichvar
81bf7cdcdc nts: initialize unused part of server key
Initialize the unused part of shorter server NTS keys (AES-128-GCM-SIV)
loaded from ntsdumpdir to avoid sending uninitialized data in requests
to the NTS-KE helper process.

Do that also for newly generated keys in case the memory will be
allocated dynamically.

Fixes: b1230efac3 ("nts: add support for encrypting cookies with AES-128-GCM-SIV")
2023-05-25 10:28:50 +02:00
Miroslav Lichvar
b8b3830dc4 ntp: randomize address selection on all source replacements
If the resolver orders addresses by IP family, there is more than one
address in the preferred IP family, and they are all reachable, but
not selectable (e.g. falsetickers in a small pool which cannot remove
them from DNS), chronyd is unable to switch to addresses in the other IP
family as it follows the resolver's order.

Enable randomization of the address selection for all source
replacements and not just replacement of (unreachable) tentative
sources. If the system doesn't have connectivity in the other family,
the addresses will be skipped and no change in behavior should be
observed.
2023-05-23 09:33:48 +02:00
Miroslav Lichvar
d4738e1259 ntp: set minimum polltarget
The polltarget value is used in a floating-point division in the
calculation of the poll adjustment. Set 1 as the minimum accepted
polltarget value to avoid working with infinite values.
2023-05-18 10:46:46 +02:00
Miroslav Lichvar
5ba42cee45 ntp: reset polling interval when replacing sources
Set the polling interval to minpoll when changing address of a source,
but only if it is reachable to avoid increasing load on server or
network in case that is the reason for the source being unreachable.

This shortens the time needed to replace a falseticker or
unsynchronized source with a selectable source.
2023-05-18 10:46:42 +02:00
Miroslav Lichvar
b2dac47c82 ntp: avoid unneccessary replacements on refresh command
When the refresh command is issued, instead of trying to replace all
NTP sources as if they were unreachable or falsetickers, keep using the
current address if it is still returned by the resolver for the name.
This avoids unnecessary loss of measurements and switching to
potentially unreachable addresses.
2023-05-15 17:23:48 +02:00
Miroslav Lichvar
6a6161dc0f doc: update NEWS 2023-05-10 14:28:42 +02:00
Miroslav Lichvar
a4eb5be8ea doc: update list of contributors in README 2023-05-10 10:46:57 +02:00
Rupesh Patel
3050e29b1d examples: improve chrony.nm-dispatcher.onoffline script 2023-05-10 10:46:57 +02:00
Miroslav Lichvar
fb1af6e55b test: add 146-offline test 2023-04-18 11:39:27 +02:00
Miroslav Lichvar
47a13ae88c md5: fix old-style function definitions
This fixes -Wdeprecated-non-prototype clang warnings.

Reported-by: Bryan Christianson <bryan@whatroute.net>
2023-04-17 11:40:18 +02:00
Miroslav Lichvar
a8496658a0 test: free memory in unit tests on exit 2023-04-13 16:22:50 +02:00
Miroslav Lichvar
6ea1082a72 sched: free timer blocks on exit
Save pointers to allocated timer blocks and free them on exit. This
fixes the long-standing annoying "possibly lost" leak reported by
valgrind.
2023-04-13 16:22:50 +02:00
Miroslav Lichvar
4f674539fd test: add 145-rtc test 2023-04-13 16:22:50 +02:00
Miroslav Lichvar
68d3fb4af8 doc: improve description of chronyd -Q option 2023-04-13 16:22:47 +02:00
Miroslav Lichvar
3c24f2c2ed test: add option to dump traffic to pcaps
This will be useful for debugging.
2023-04-03 14:36:30 +02:00
Miroslav Lichvar
0189dac7d8 ntp: save response when waiting for HW TX timestamp
Rework handling of late HW TX timestamps. Instead of suspending reading
from client-only sockets that have HW TX timestamping enabled, save the
whole response if it is valid and a HW TX timestamp was received for the
source before. When the timestamp is received, or the configurable
timeout is reached, process the saved response again, but skip the
authentication test as the NTS code allows only one response per
request. Only one valid response per source can be saved. If a second
valid response is received while waiting for the timestamp, process both
responses immediately in the order they were received.

The main advantage of this approach is that it works on all sockets, i.e.
even in the symmetric mode and with NTP-over-PTP, and the kernel does
not need to buffer invalid responses.
2023-03-30 15:44:19 +02:00
Miroslav Lichvar
4a11399c2e ntp: rework calculation of transmit timeout
Previously, in the calculation of the next transmission time
corresponding to the current polling interval, the reference point was
the current time in the client mode (i.e. the time when the response is
processed) and the last transmission time in the symmetric mode.

Rework the code to use the last transmission in both modes and make it
independent from the time when the response is processed to avoid extra
delays due to waiting for HW TX timestamps.
2023-03-30 15:28:08 +02:00
Miroslav Lichvar
cf98551ea1 client: avoid casting to long
Use the PRI*32 specifiers in printf formats to avoid casting received
values to unsigned long.
2023-03-30 15:17:52 +02:00
Miroslav Lichvar
5508b01bd8 cmdmon: switch serverstats to 64-bit integers
Update the serverstats response to use the new 64-bit integers.

Don't define a new value for the response as it already had an
incompatible change since the latest release (new fields added for
timestamp counters).
2023-03-30 15:17:52 +02:00
Miroslav Lichvar
907accec87 clientlog: extend serverstats counters to 64 bits
On a busy server the 32-bit counters included in the serverstats report
may overflow every few hours or days. Extend them to 64 bits.
2023-03-30 15:17:52 +02:00
Miroslav Lichvar
a511029cc2 cmdmon: define 64-bit integer
Add a structure for 64-bit integers without requiring 64-bit alignment
to be usable in CMD_Reply without struct packing.

Add utility functions for conversion to/from network order. Avoid using
be64toh() and htobe64() as they don't seem to be available on all
supported systems.
2023-03-30 15:17:50 +02:00
Miroslav Lichvar
0845df7684 cmdmon: add timestamp counters to serverstats report
Add the new RX/TX daemon/kernel/hardware timestamp counters to the
serverstats report.
2023-03-22 10:47:22 +01:00
Miroslav Lichvar
2f961ab36a clientlog: count RX and TX timestamps for each source
Count served timestamps in all combinations of RX/TX and
daemon/kernel/hardware. Repurpose CLG_LogAuthNtpRequest() to update all
NTP-specific stats in one call per accepted request and response.
2023-03-22 09:42:35 +01:00
Miroslav Lichvar
a0cf7f7f12 clientlog: save source of transmit timestamps
Add the timestamp source to the data kept for clients using interleaved
mode to allow extending server statistics.
2023-03-22 09:42:06 +01:00
Miroslav Lichvar
a5f1a113f0 ntp: remove unnecessary check for NULL local timestamp
After 5f4cbaab7e ("ntp: optimize detection of clients using
interleaved mode") the local TX timestamp is saved for all requests
indicating interleaved mode even when no previous RX timestamp is found.
2023-03-22 09:13:53 +01:00
Miroslav Lichvar
5160f14fdc ntp: add maximum PHC poll interval
Specify maxpoll for HW timestamping (default minpoll + 1) to track the
PHC well even when there is little NTP traffic on the interface. After
each PHC reading schedule a timeout according to the maxpoll. Polling
between minpoll and maxpoll is still triggered by HW timestamps.

Wait for the first HW timestamp before adding the timeout to avoid
polling PHCs on interfaces that are enabled in the configuration but
not used for NTP. Add a new scheduling class to separate polling of
different PHCs to avoid too long intervals between processing I/O
events.
2023-03-22 09:13:53 +01:00
Miroslav Lichvar
b0a2ad2535 doc: add missing word in serverstats description 2023-03-22 09:13:53 +01:00
Miroslav Lichvar
ecdde75f8f doc: replace offensive words 2023-03-22 09:13:47 +01:00
Miroslav Lichvar
2d80be9541 doc: mention NTP port number in FAQ 2023-03-22 08:58:20 +01:00
Miroslav Lichvar
ab776ed9d8 ntp: make socket resume timeout configurable
In some cases even the new timeout of 1 millisecond is not sufficient to
get all HW TX timestamps. Add a new directive to allow users to
specify longer timeouts.
2023-03-09 16:13:27 +01:00
Miroslav Lichvar
ccebec3eb6 ntp: increase socket resume timeout to 1 millisecond
This seems to work significantly better on some hardware and is still
shorter than burst interval at the minimum configurable poll.
2023-03-09 16:05:38 +01:00
Miroslav Lichvar
3ea3e0efd7 ntp: add support for multiple suspended sockets
With some hardware it takes milliseconds to get the HW TX timestamp.

Rework the code to handle multiple suspended client-only sockets at the
same time in order to allow longer timeouts, which may overlap for
different sources. Instead of waiting for the first read event simply
suspend the socket and create timeout when the HW TX timestamp is
requested.
2023-03-09 15:49:43 +01:00
Dan Drown
c3e4e3e47a ntp: increment sequence id in PTP messages 2023-03-07 17:00:08 +01:00
Miroslav Lichvar
e949e1d991 test: update description of 106-refclock 2023-03-02 15:34:03 +01:00
Miroslav Lichvar
c8649ccb7e refclock_phc: support multiple extpps refclocks on one PHC
The Linux kernel (as of 6.2) has a shared queue of external timestamps
for all descriptors of the same PHC. If multiple refclocks using the
same PHC and the same or different channels were specified, some
refclocks didn't receive any or most of their timestamps, depending on
the rate and timing of the events (with the previous commit avoiding
blocking reads).

Track extpps-enabled refclocks in an array. Add PHC index to the PHC
instance. When a timestamp is read from the descriptor, provide it to
all refclocks that have the same PHC index and a channel matching the
event.

Make sure the timestamp is different from the previous one in case the
kernel will be improved to duplicate the timestamps for different
descriptors.

Reported-by: Matt Corallo <ntp-lists@mattcorallo.com>
2023-03-02 15:33:59 +01:00
Miroslav Lichvar
39ff7ceeca sys_linux: avoid blocking in reading of external PHC timestamp
The kernel has a common queue for all readers of a PHC device. With
multiple PHC refclocks using the same device some reads blocked. PHC
devices don't seem to support non-blocking reads. Use poll() to check if
a timestamp is available before reading from the descriptor.
2023-03-02 15:13:18 +01:00
Miroslav Lichvar
06945d927b test: add array unit test 2023-03-02 10:41:28 +01:00
Miroslav Lichvar
caf82b1a45 array: add function for removing elements 2023-03-02 10:41:28 +01:00
Miroslav Lichvar
f99b2f633b ntp: count missing samples when waiting for NTS-KE
Count missing samples for the median filter when
NAU_PrepareRequestAuth() is failing.

Fixes: 4234732b08 ("ntp: rework filter option to count missing samples")
2023-02-28 10:02:40 +01:00
Miroslav Lichvar
6270a3eb7c ntp: don't adjust poll interval when waiting for NTS-KE
Don't adjust the NTP polling interval and decrement the burst count when
NAU_PrepareRequestAuth() fails (e.g. no NTS-KE response received yet,
network being down, or the server refusing connections), same as if an
NTP request could not be sent. Rely on the rate limiting implemented in
the NTS code.
2023-02-27 15:36:30 +01:00
Miroslav Lichvar
1daa40a2f7 nts: use shorter NTS-KE retry interval when network is down
When chronyd configured with an NTS source not specified as offline and
resolvable without network was started before the network was up, it was
using an unnecessarily long NTS-KE retry interval, same as if the server
was refusing the connections.

When the network is down, the connect() call made from NKC_Start() on
the non-blocking TCP socket should fail with a different error than
EINPROGRESS and cause NKC_Start() to return with failure. Add a constant
2-second retry interval (matching default iburst) for this case.
2023-02-27 11:43:22 +01:00
Miroslav Lichvar
a1406eded3 nts: destroy NTS-KE client right after failed start
When NKC_Start() fails (e.g. due to unreachable network), don't wait for
the next poll to destroy the client and another poll to create and start
it again.
2023-02-23 15:01:45 +01:00
Miroslav Lichvar
1eb8994c00 client: add -e option to indicate end of response
In a non-tty session with chronyc it is not possible to detect the
end of the response without relying on timeouts, or separate responses
to a repeated command if using the -c option.

Add -e option to end each response with a line containing a single dot.
2023-02-02 17:06:46 +01:00
Miroslav Lichvar
221e5fb501 doc: improve description of refclock filter option 2023-02-02 15:05:05 +01:00
Miroslav Lichvar
ecfbde9872 doc: describe minimum useful ntsrefresh 2023-02-02 14:47:31 +01:00
Miroslav Lichvar
dec07aa844 sourcestats: don't fudge refclock LastRx in sources report
The sample time used in calculation of the last_meas_ago (LastRx) value
in the sources report is aligned to the second to minimize the leak
of the NTP receive timestamp, which could be useful in some attacks.

There is no need to do that with reference clocks, which are often used
with very short polling intervals and an extra second in the LastRx
value can be misinterpreted as a missed sample.
2023-02-02 11:26:04 +01:00
Miroslav Lichvar
5b3d4dfe76 sources: warn about detected falsetickers
Log a warning message for each detected falseticker, but only once
between changes in the selection of the best source. Don't print all
sources when no majority is reached as that case has its own warning
message.
2023-01-26 17:03:35 +01:00
Miroslav Lichvar
dc0f0cd134 sources: enable no majority message before first selection
Add a separate flag to allow the "no majority" message to be logged even
before the first successful selection.
2023-01-26 16:15:47 +01:00
Miroslav Lichvar
bd37efa52e sources: increase log level of no majority message
When the selection fails due to no majority, log the message as a
warning to get the admin's attention.
2023-01-26 16:05:57 +01:00
Miroslav Lichvar
c71185a0e5 doc: add missing description of selection log field 2023-01-26 12:04:28 +01:00
Miroslav Lichvar
f149b7b758 examples: add AES keys to chrony.keys.example 2023-01-25 15:59:45 +01:00
Miroslav Lichvar
883b0dde94 conf: warn if not having read-only access to keys
After dropping root privileges, log a warning message if chronyd
doesn't have read access or has (unnecessary) write access to the
files containing symmetric and server NTS keys.
2023-01-25 14:44:59 +01:00
Miroslav Lichvar
9cba9c8585 keys+nts: warn if loading world-readable/writable key
Log a warning message if the file specified by the keyfile or
ntsserverkey directive is world-readable or writable, which is likely
an insecure misconfiguration. There is no check of directories
containing the file.
2023-01-19 16:39:40 +01:00
Miroslav Lichvar
88e711ad9a refclock: fix preprocessor conditional
Split the new SOCK conditional using __GLIBC_PREREQ macro (which has
arguments) to fix compilation when it is not defined.

Fix also debug message using sizeof(time_t) in case it's enabled on
64-bit systems.

Reported-by: Bryan Christianson <bryan@whatroute.net>
Fixes: badaa83c31 ("refclock: convert mismatched timeval in SOCK messages")
2023-01-19 11:51:41 +01:00
Miroslav Lichvar
badaa83c31 refclock: convert mismatched timeval in SOCK messages
On 32-bit glibc-based (>=2.34) systems, allow the SOCK client to send
messages with timevals using the other time_t size than chrony. If the
length of the received message corresponds to the other size, convert
the timeval and move the rest of the message before its processing.

This is needed for compatibility with the current development version of
gpsd, which forces 64-bit time_t on these systems, while chrony needs to
be compiled with the same time_t as gnutls.
2023-01-18 16:39:25 +01:00
Miroslav Lichvar
bbeec7361c doc: deprecate SHM refclocks in favor of SOCK
The NTP SHM refclock protocol has the following properties:

- the memory segments have a predictable key (first segment 0x4e545030)
- it's expected to work in any order of starting chronyd and the program
  providing samples to chronyd, i.e. both the consumer and producer need
  to be able to create the segment
- the producer and consumer generally don't know under which user is
  the other side running (e.g. gpsd can create the segment as root and
  also as nobody after it drops root privileges)
- there is no authentication of data provided via SHM
- there is no way to restart the protocol

This makes it difficult for chronyd to ensure it is receiving
measurements from the process that the admin expects it to and not some
other process that managed to create the segment before it was started.
It's up to the admin to configure the system so that chronyd or the
producer is started before untrusted applications or users can create
the segment, or at least verify at some point later that the segment was
created with the expected owner and permissions.

There doesn't seem to be a backward-compatible fix of the protocol. Even
if one side could detect the segment had a wrong owner or permissions,
it wouldn't be able to tell the other side to reattach after recreating
the segment with the expected owner and permissions, if it still had the
permissions to do that.

The protocol would need to specify which side is responsible for
creating the segment and the start order would need to strictly follow
that.

As gpsd (likely the most common refclock source for chronyd) now
supports in the latest version SOCK even for message-based timing,
update the man page and FAQ to deprecate SHM in favor of SOCK.
2023-01-12 16:23:15 +01:00
Miroslav Lichvar
6fba5a4a7f examples: add chronyd-restricted.service
This is a more restricted version of the chronyd service intended for
minimal NTP/NTS client configurations. The daemon is started without
root privileges and is allowed to write only to its own runtime, state,
and log directories. It cannot bind to privileged ports in order to
operate as an NTP server, or provide monitoring access over IPv4/IPv6.
It cannot use reference clocks, HW timestamping, RTC tracking, and other
features.
2023-01-11 15:53:24 +01:00
Miroslav Lichvar
26889a8cb7 cmdmon+client: add selectopts command
This command uses the new source function to modify configured selection
options of an NTP source or reference clock.
2022-12-14 17:04:49 +01:00
Miroslav Lichvar
cd278d1826 cmdmon+client: split out conversion of selection options
This will be shared with new command modifying the selection options.
2022-12-14 17:04:49 +01:00
Miroslav Lichvar
3877734814 sources: add function to modify selection options
Add a function to add new selection options or remove existing options
specified in the configuration for both NTP sources and reference
clocks.

Provide a pair of IP address and reference ID to identify the source
depending on the type. Find the source directly in the array of sources
instead of going through the NSR hashtable for NTP sources to not
complicate it unnecessarily.
2022-12-14 17:04:49 +01:00
Miroslav Lichvar
19f2ab9e09 sources: add assertion for instance index 2022-12-14 17:04:46 +01:00
Miroslav Lichvar
3260dc82fe cmdparse: add functions for parsing refclock refid and select options
This will be used in new chronyc command working on refclocks.
2022-12-14 17:04:23 +01:00
Miroslav Lichvar
1a98c5ffa9 ntp: update comment about minimum request spacing 2022-12-05 16:44:38 +01:00
Miroslav Lichvar
8247b8525f log more changes made by chronyc commands
Log important changes from chronyc for auditing purposes.

Add log messages for:
- loaded symmetric keys and server NTS keys (logged also on start)
- modified maxupdateskew and makestep
- enabled/disabled local reference mode (logged also on start)
- reset time smoothing (logged also on clock steps)
- reset sources
2022-12-01 16:58:23 +01:00
Mike Ryan
8901293be8 ntp: set DSCP for IPv6
Chrony's dscp setting currently applies to IPv4 only. This patch sets
the necessary option for IPv6 as well.
2022-11-16 17:15:11 +01:00
Miroslav Lichvar
e789b0817f ntp+cmdmon: log allow/deny commands
Log added NTP and command access restrictions, using INFO severity if
from a chronyc command, DEBUG otherwise (i.e. from the config).
2022-11-16 17:15:07 +01:00
Miroslav Lichvar
d0fd04c0a2 util: add function for printing access subnets 2022-11-16 17:00:45 +01:00
Miroslav Lichvar
7122321249 ntp: log added and removed sources
Log a message when a single NTP source or pool of sources is added or
removed. Use the INFO severity if it's a result of a chronyc command or
(re)load of sourcefiles (which are assumed to change over time), and
DEBUG for other contexts, e.g. sources loaded from the config, sources
removed when pruning pools after reaching maxsources, and other parts of
normal operation.
2022-11-16 17:00:39 +01:00
Miroslav Lichvar
b328c8c348 logging: support context-specific severity
Allow messages to have severity set to INFO or DEBUG depending on the
context in which they are made to allow logging important changes made
from chronyc or sourcefile, but not spam the system log if those changes
are normally expected (e.g. specified in the config).
2022-11-16 16:57:49 +01:00
Holger Hoffstätte
7b97668319 getdate: fix various warnings which will be errors with clang-16
These were found by Gentoo's QA while rebuilding the world with
clang-16: https://bugs.gentoo.org/880519

Signed-off-by: Holger Hoffstätte <holger@applied-asynchrony.com>
2022-11-09 09:17:14 +01:00
Miroslav Lichvar
6f5df7e4a4 nts: warn if server started without ntsdumpdir
If an NTS server is configured without ntsdumpdir, keys will not be
saved and reloaded after restart, which will cause existing cookies
to be invalidated and can cause a short-term denial of service if
the server has so many clients that it cannot handle them all
making an NTS-KE session within one polling interval.

Log a warning message if a server key+certificate is specified without
ntsdumpdir.
2022-10-24 16:39:29 +02:00
Miroslav Lichvar
5a39074e01 nts: fix number of extension fields after failed encryption
If the authenticator SIV encryption fails (e.g. due to wrong nonce
length), decrement the number of extension fields to keep the packet
info consistent.
2022-10-19 15:50:39 +02:00
Miroslav Lichvar
c8e57f4350 nts: change ntskeys format to support different algorithms
Specify the AEAD ID for each key saved in the ntskeys file instead of
one ID for all keys. Keep support for loading files in the old format.

This will allow servers to save their keys after upgrading to a new
version with AES-128-GCM-SIV support before the loaded AES-SIV-CMAC-256
keys are rotated out.

If an unsupported key is found, don't load any keys. Also, change the
severity of the error message from debug to error.
2022-10-19 15:50:39 +02:00
Miroslav Lichvar
b1230efac3 nts: add support for encrypting cookies with AES-128-GCM-SIV
If AES-128-GCM-SIV is available on the server, use it for encryption of
cookies. This makes them shorter by 4 bytes due to shorter nonce and it
might also improve the server performance.

After server upgrade and restart with ntsdumpdir, the switch will happen
on the second rotation of the server key. Clients should accept shorter
cookies without restarting NTS-KE. The first response will have extra
padding in the authenticator field to make the length symmetric.
2022-10-19 15:50:39 +02:00
Miroslav Lichvar
4e1ce88981 nts: make server key access more readable
Get a pointer to the server key instead of repeated indexing.
2022-10-19 15:50:39 +02:00
Miroslav Lichvar
790a336eb2 nts: add server support for authentication with AES-128-GCM-SIV
Keep a server SIV instance for each available algorithm.

Select AES-128-GCM-SIV if requested by NTS-KE client as the first
supported algorithm.

Instead of encoding the AEAD ID in the cookie, select the algorithm
according to the length of decrypted keys. (This can work as a long as
all supported algorithms use keys with different lengths.)
2022-10-19 15:50:39 +02:00
Miroslav Lichvar
cc706b50b9 nts: add client support for authentication with AES-128-GCM-SIV
If AES-128-GCM-SIV is available on the client, add it to the requested
algorithms in NTS-KE as the first (preferred) entry.

If supported on the server, it will make the cookies shorter, which
will get the length of NTP messages containing only one cookie below
200 octets. This should make NTS more reliable in networks where longer
NTP packets are filtered as a mitigation against amplification attacks
exploiting the ntpd mode 6/7 protocol.
2022-10-19 15:50:39 +02:00
Miroslav Lichvar
73042494bd nts: add support for NTP authenticator field using AES-GCM-SIV
Add support for SIV algorithms which have maximum nonce length shorter
than 16 bytes.
2022-10-19 15:50:39 +02:00
Miroslav Lichvar
ec89739d50 nts: make sure encrypted S2C and C2S keys have equal length
Don't allow a cookie to contain keys with different lengths to not break
the assumption made in decoding, if there will ever be a case where this
could be requested.
2022-10-19 15:50:39 +02:00
Miroslav Lichvar
4baf999cc3 nts: don't connect to server if missing AES-SIV-CMAC-256
Avoid wasting server resources if the client doesn't support
AES-SIV-CMAC-256 (the only algorithm required on servers).
2022-10-19 15:50:39 +02:00
Miroslav Lichvar
9afd19c29b nts: use signed lengths in NNA_DecryptAuthEF()
Make the types consistent with the rest of the file.
2022-10-19 15:50:39 +02:00
Miroslav Lichvar
5dd173c050 siv: add functions to return min and max nonce length
While AES-SIV-CMAC allows nonces of any length, AES-GCM-SIV requires
exactly 12 bytes, which is less than the unpadded minimum length of 16
used in the NTS authenticator field. These functions will be needed to
support both ciphers in the NTS code.
2022-10-19 15:50:39 +02:00
Miroslav Lichvar
5caf0ad187 siv: add support for AES-128-GCM-SIV in Nettle
This is a newer nonce misuse-resistant cipher specified in RFC 8452,
which is now supported in the development code of the Nettle library.

The advantages over AES-SIV-CMAC-256 are shorter keys and better
performance.
2022-10-19 15:50:31 +02:00
Miroslav Lichvar
17d2291a84 doc: improve ntsrotate description 2022-10-19 15:33:04 +02:00
Miroslav Lichvar
a6179261a7 doc: fix wrong name of authselectmode directive 2022-10-17 15:24:24 +02:00
Miroslav Lichvar
098e0c43fc test: add float-cast-overflow to 003-sanitizers test 2022-09-20 10:56:28 +02:00
133 changed files with 5560 additions and 1475 deletions

View File

@@ -33,9 +33,11 @@ CFLAGS = @CFLAGS@
CPPFLAGS = @CPPFLAGS@
LDFLAGS = @LDFLAGS@
GETDATE_CFLAGS = @GETDATE_CFLAGS@
EXTRA_OBJS = @EXTRA_OBJS@
OBJS = array.o cmdparse.o conf.o local.o logging.o main.o memory.o quantiles.o \
OBJS = array.o cmdparse.o conf.o leapdb.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)
@@ -61,6 +63,8 @@ chronyd : $(OBJS)
chronyc : $(CLI_OBJS)
$(CC) $(CFLAGS) -o chronyc $(CLI_OBJS) $(LDFLAGS) $(LIBS) $(EXTRA_CLI_LIBS)
getdate.o: CFLAGS += $(GETDATE_CFLAGS)
distclean : clean
$(MAKE) -C doc distclean
$(MAKE) -C test/unit distclean

60
NEWS
View File

@@ -1,3 +1,63 @@
New in version 4.6
==================
Enhancements
------------
* Add activate option to local directive to set activation threshold
* Add ipv4 and ipv6 options to server/pool/peer directive
* Add kod option to ratelimit directive for server KoD RATE support
* Add leapseclist directive to read NIST/IERS leap-seconds.list file
* Add ptpdomain directive to set PTP domain for NTP over PTP
* Improve copy server option to accept unsynchronised status instantly
* Log one selection failure on start
* Add offset command to modify source offset correction
* Add timestamp sources to ntpdata report
Bug fixes
---------
* Fix crash on sources reload during initstepslew or RTC initialisation
* Fix source refreshment to not repeat failed name resolving attempts
New in version 4.5
==================
Enhancements
------------
* Add support for AES-GCM-SIV in GnuTLS
* Add support for corrections from PTP transparent clocks
* Add support for systemd socket activation
Bug fixes
---------
* Fix presend in interleaved mode
* Fix reloading of modified sources from sourcedir
New in version 4.4
==================
Enhancements
------------
* Add support for AES-GCM-SIV with Nettle >= 3.9 to shorten NTS
cookies to avoid some length-specific blocking of NTP on Internet
* Add support for multiple refclocks using extpps option on one PHC
* Add maxpoll option to hwtimestamp directive to improve PHC tracking
with low packet rates
* Add hwtstimeout directive to configure timeout for late timestamps
* Handle late hardware transmit timestamps of NTP requests on all sockets
* Handle mismatched 32/64-bit time_t in SOCK refclock samples
* Improve source replacement
* Log important changes made by command requests (chronyc)
* Refresh address of NTP sources periodically
* Request nanosecond kernel RX timestamping on FreeBSD
* Set DSCP for IPv6 packets
* Shorten NTS-KE retry interval when network is down
* Update seccomp filter for musl
* Warn if loading keys from file with unexpected permissions
* Warn if source selection fails or falseticker is detected
* Add selectopts command to modify source-specific selection options
* Add timestamp sources to serverstats report and make its fields 64-bit
* Add -e option to chronyc to indicate end of response
New in version 4.3
==================

38
README
View File

@@ -12,7 +12,7 @@ a time service to other computers in the network.
It is designed to perform well in a wide range of conditions, including
intermittent network connections, heavily congested networks, changing
temperatures (ordinary computer clocks are sensitive to temperature),
and systems that do not run continuosly, or run on a virtual machine.
and systems that do not run continuously, or run on a virtual machine.
Typical accuracy between two machines synchronised over the Internet is
within a few milliseconds; on a LAN, accuracy is typically in tens of
@@ -47,32 +47,7 @@ Frequently Asked Questions (FAQ).
The documentation is also available on the chrony web pages, accessible
through the URL
https://chrony.tuxfamily.org/
Where are new versions announced?
=================================
There is a low volume mailing list where new versions and other
important news relating to chrony are announced. You can join this list
by sending mail with the subject "subscribe" to
chrony-announce-request@chrony.tuxfamily.org
How can I get support for chrony?
=================================
There are two other mailing lists relating to chrony. chrony-users is a
discussion list for users, e.g. for questions about chrony configuration
and bug reports. chrony-dev is a more technical list for developers,
e.g. for submitting patches and discussing how new features should be
implemented. To subscribe to either of these lists, send a message with
the subject "subscribe" to
chrony-users-request@chrony.tuxfamily.org
or
chrony-dev-request@chrony.tuxfamily.org
as applicable.
https://chrony-project.org/
License
=======
@@ -108,12 +83,15 @@ Erik Bryer <ebryer@spots.ab.ca>
Jonathan Cameron <jic23@cam.ac.uk>
Bryan Christianson <bryan@whatroute.net>
Juliusz Chroboczek <jch@pps.jussieu.fr>
Dan Drown <dan-ntp@drown.org>
Kamil Dudka <kdudka@redhat.com>
Christian Ehrhardt <christian.ehrhardt@canonical.com>
Paul Elliott <pelliott@io.com>
Robert Fairley <rfairley@redhat.com>
Stefan R. Filipek <srfilipek@gmail.com>
Andy Fiddaman <illumos@fiddaman.net>
Mike Fleetwood <mike@rockover.demon.co.uk>
Rob Gill <rrobgill@protonmail.com>
Alexander Gretencord <arutha@gmx.de>
Andrew Griffiths <agriffit@redhat.com>
Walter Haidinger <walter.haidinger@gmx.at>
@@ -121,6 +99,7 @@ Juergen Hannken-Illjes <hannken@eis.cs.tu-bs.de>
John Hasler <john@dhh.gt.org>
Tjalling Hattink <t.hattink@fugro.nl>
Liam Hatton <me@liamhatton.com>
Holger Hoffstätte <holger@applied-asynchrony.com>
Jachym Holecek <jakym@volny.cz>
Håkan Johansson <f96hajo@chalmers.se>
Jim Knoble <jmknoble@pobox.com>
@@ -134,17 +113,22 @@ Paul Menzel <paulepanter@users.sourceforge.net>
Vladimir Michl <vladimir.michl@seznam.cz>
Victor Moroz <vim@prv.adlum.ru>
Kalle Olavi Niemitalo <tosi@stekt.oulu.fi>
Patrick Oppenlander <patrick.oppenlander@gmail.com>
Frank Otto <sandwichmacher@web.de>
Denny Page <dennypage@me.com>
Rupesh Patel <rupatel@redhat.com>
Chris Perl <cperl@janestreet.com>
Gautier PHILIPPON <gautier.philippon@ensimag.grenoble-inp.fr>
Andreas Piesk <apiesk@virbus.de>
Mike Ryan <msr@hsilop.net>
Baruch Siach <baruch@tkos.co.il>
Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
Foster Snowhill <forst@forstwoof.ru>
Andreas Steinmetz <ast@domdv.de>
NAKAMURA Takumi <takumi@ps.sakura.ne.jp>
Timo Teras <timo.teras@iki.fi>
Bill Unruh <unruh@physics.ubc.ca>
Luke Valenta <lvalenta@cloudflare.com>
Stephen Wadeley <swadeley@redhat.com>
Bernhard Weiss <lisnablagh@web.de>
Wolfgang Weisselberg <weissel@netcologne.de>

15
array.c
View File

@@ -116,6 +116,21 @@ ARR_AppendElement(ARR_Instance array, void *element)
memcpy(e, element, array->elem_size);
}
void
ARR_RemoveElement(ARR_Instance array, unsigned int index)
{
void *e, *l;
e = ARR_GetElement(array, index);
l = ARR_GetElement(array, array->used - 1);
if (e < l)
memmove(e, (char *)e + array->elem_size, (char *)l - (char *)e);
array->used--;
realloc_array(array, array->used);
}
void
ARR_SetSize(ARR_Instance array, unsigned int size)
{

View File

@@ -47,6 +47,9 @@ extern void *ARR_GetElements(ARR_Instance array);
/* Add a new element to the end of the array */
extern void ARR_AppendElement(ARR_Instance array, void *element);
/* Remove element with given index */
extern void ARR_RemoveElement(ARR_Instance array, unsigned int index);
/* Set the size of the array */
extern void ARR_SetSize(ARR_Instance array, unsigned int size);

75
candm.h
View File

@@ -109,7 +109,10 @@
#define REQ_SELECT_DATA 69
#define REQ_RELOAD_SOURCES 70
#define REQ_DOFFSET2 71
#define N_REQUEST_TYPES 72
#define REQ_MODIFY_SELECTOPTS 72
#define REQ_MODIFY_OFFSET 73
#define REQ_LOCAL3 74
#define N_REQUEST_TYPES 75
/* Structure used to exchange timespecs independent of time_t size */
typedef struct {
@@ -121,6 +124,12 @@ typedef struct {
/* This is used in tv_sec_high for 32-bit timestamps */
#define TV_NOHIGHSEC 0x7fffffff
/* Structure for 64-bit integers (not requiring 64-bit alignment) */
typedef struct {
uint32_t high;
uint32_t low;
} Integer64;
/* 32-bit floating-point format consisting of 7-bit signed exponent
and 25-bit signed coefficient without hidden bit.
The result is calculated as: 2^(exp - 25) * coef */
@@ -229,6 +238,8 @@ typedef struct {
int32_t stratum;
Float distance;
int32_t orphan;
Float activate;
uint32_t reserved[2];
int32_t EOR;
} REQ_Local;
@@ -270,7 +281,10 @@ typedef struct {
#define REQ_ADDSRC_BURST 0x100
#define REQ_ADDSRC_NTS 0x200
#define REQ_ADDSRC_COPY 0x400
#define REQ_ADDSRC_EF_EXP1 0x800
#define REQ_ADDSRC_EF_EXP_MONO_ROOT 0x800
#define REQ_ADDSRC_EF_EXP_NET_CORRECTION 0x1000
#define REQ_ADDSRC_IPV4 0x2000
#define REQ_ADDSRC_IPV6 0x4000
typedef struct {
uint32_t type;
@@ -371,6 +385,22 @@ typedef struct {
int32_t EOR;
} REQ_SelectData;
/* Mask and options reuse the REQ_ADDSRC flags */
typedef struct {
IPAddr address;
uint32_t ref_id;
uint32_t mask;
uint32_t options;
int32_t EOR;
} REQ_Modify_SelectOpts;
typedef struct {
IPAddr address;
uint32_t ref_id;
Float new_offset;
int32_t EOR;
} REQ_Modify_Offset;
/* ================================================== */
#define PKT_TYPE_CMD_REQUEST 1
@@ -477,6 +507,8 @@ typedef struct {
REQ_NTPSourceName ntp_source_name;
REQ_AuthData auth_data;
REQ_SelectData select_data;
REQ_Modify_SelectOpts modify_select_opts;
REQ_Modify_Offset modify_offset;
} data; /* Command specific parameters */
/* Padding used to prevent traffic amplification. It only defines the
@@ -519,7 +551,9 @@ typedef struct {
#define RPY_SERVER_STATS2 22
#define RPY_SELECT_DATA 23
#define RPY_SERVER_STATS3 24
#define N_REPLY_TYPES 25
#define RPY_SERVER_STATS4 25
#define RPY_NTP_DATA2 26
#define N_REPLY_TYPES 27
/* Status codes */
#define STT_SUCCESS 0
@@ -654,17 +688,24 @@ typedef struct {
} RPY_ClientAccessesByIndex;
typedef struct {
uint32_t ntp_hits;
uint32_t nke_hits;
uint32_t cmd_hits;
uint32_t ntp_drops;
uint32_t nke_drops;
uint32_t cmd_drops;
uint32_t log_drops;
uint32_t ntp_auth_hits;
uint32_t ntp_interleaved_hits;
uint32_t ntp_timestamps;
uint32_t ntp_span_seconds;
Integer64 ntp_hits;
Integer64 nke_hits;
Integer64 cmd_hits;
Integer64 ntp_drops;
Integer64 nke_drops;
Integer64 cmd_drops;
Integer64 log_drops;
Integer64 ntp_auth_hits;
Integer64 ntp_interleaved_hits;
Integer64 ntp_timestamps;
Integer64 ntp_span_seconds;
Integer64 ntp_daemon_rx_timestamps;
Integer64 ntp_daemon_tx_timestamps;
Integer64 ntp_kernel_rx_timestamps;
Integer64 ntp_kernel_tx_timestamps;
Integer64 ntp_hw_rx_timestamps;
Integer64 ntp_hw_tx_timestamps;
Integer64 reserved[4];
int32_t EOR;
} RPY_ServerStats;
@@ -735,7 +776,11 @@ typedef struct {
uint32_t total_rx_count;
uint32_t total_valid_count;
uint32_t total_good_count;
uint32_t reserved[3];
uint32_t total_kernel_tx_ts;
uint32_t total_kernel_rx_ts;
uint32_t total_hw_tx_ts;
uint32_t total_hw_rx_ts;
uint32_t reserved[4];
int32_t EOR;
} RPY_NTPData;

326
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-2021
* Copyright (C) Miroslav Lichvar 2009-2023
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -71,6 +71,8 @@ static int source_names = 0;
static int csv_mode = 0;
static int end_dot = 0;
/* ================================================== */
/* Log a message. This is a minimalistic replacement of the logging.c
implementation to avoid linking with it and other modules. */
@@ -342,6 +344,24 @@ parse_source_address(char *word, IPAddr *address)
/* ================================================== */
static int
parse_source_address_or_refid(char *s, IPAddr *address, uint32_t *ref_id)
{
address->family = IPADDR_UNSPEC;
*ref_id = 0;
/* Don't allow hostnames to avoid conflicts with reference IDs */
if (UTI_StringToIdIP(s, address) || UTI_StringToIP(s, address))
return 1;
if (CPS_ParseRefid(s, ref_id) > 0)
return 1;
return 0;
}
/* ================================================== */
static int
read_mask_address(char *line, IPAddr *mask, IPAddr *address)
{
@@ -735,22 +755,24 @@ static int
process_cmd_local(CMD_Request *msg, char *line)
{
int on_off, stratum = 0, orphan = 0;
double distance = 0.0;
double distance = 0.0, activate = 0.0;
if (!strcmp(line, "off")) {
on_off = 0;
} else if (CPS_ParseLocal(line, &stratum, &orphan, &distance)) {
} else if (CPS_ParseLocal(line, &stratum, &orphan, &distance, &activate)) {
on_off = 1;
} else {
LOG(LOGS_ERR, "Invalid syntax for local command");
return 0;
}
msg->command = htons(REQ_LOCAL2);
msg->command = htons(REQ_LOCAL3);
msg->data.local.on_off = htonl(on_off);
msg->data.local.stratum = htonl(stratum);
msg->data.local.distance = UTI_FloatHostToNetwork(distance);
msg->data.local.orphan = htonl(orphan);
msg->data.local.activate = UTI_FloatHostToNetwork(activate);
memset(msg->data.local.reserved, 0, sizeof (msg->data.local.reserved));
return 1;
}
@@ -869,6 +891,17 @@ process_cmd_doffset(CMD_Request *msg, char *line)
/* ================================================== */
static int
convert_addsrc_sel_options(int options)
{
return (options & SRC_SELECT_PREFER ? REQ_ADDSRC_PREFER : 0) |
(options & SRC_SELECT_NOSELECT ? REQ_ADDSRC_NOSELECT : 0) |
(options & SRC_SELECT_TRUST ? REQ_ADDSRC_TRUST : 0) |
(options & SRC_SELECT_REQUIRE ? REQ_ADDSRC_REQUIRE : 0);
}
/* ================================================== */
static int
process_cmd_add_source(CMD_Request *msg, char *line)
{
@@ -945,11 +978,13 @@ 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));
(data.params.ext_fields & NTP_EF_FLAG_EXP_MONO_ROOT ?
REQ_ADDSRC_EF_EXP_MONO_ROOT : 0) |
(data.params.ext_fields & NTP_EF_FLAG_EXP_NET_CORRECTION ?
REQ_ADDSRC_EF_EXP_NET_CORRECTION : 0) |
(data.family == IPADDR_INET4 ? REQ_ADDSRC_IPV4 : 0) |
(data.family == IPADDR_INET6 ? REQ_ADDSRC_IPV6 : 0) |
convert_addsrc_sel_options(data.params.sel_options));
msg->data.ntp_source.filter_length = htonl(data.params.filter_length);
msg->data.ntp_source.cert_set = htonl(data.params.cert_set);
msg->data.ntp_source.max_delay_quant =
@@ -1013,8 +1048,10 @@ give_help(void)
"sources [-a] [-v]\0Display information about current sources\0"
"sourcestats [-a] [-v]\0Display statistics about collected measurements\0"
"selectdata [-a] [-v]\0Display information about source selection\0"
"selectopts <address|refid> <+|-options>\0Modify selection options\0"
"reselect\0Force reselecting synchronisation source\0"
"reselectdist <dist>\0Modify reselection distance\0"
"offset <address|refid> <offset>\0Modify offset correction\0"
"\0\0"
"NTP sources:\0\0"
"activity\0Check how many NTP sources are online/offline\0"
@@ -1125,10 +1162,11 @@ command_name_generator(const char *text, int state)
"clients", "cmdaccheck", "cmdallow", "cmddeny", "cyclelogs", "delete",
"deny", "dns", "dump", "exit", "help", "keygen", "local", "makestep",
"manual", "maxdelay", "maxdelaydevratio", "maxdelayratio", "maxpoll",
"maxupdateskew", "minpoll", "minstratum", "ntpdata", "offline", "online", "onoffline",
"maxupdateskew", "minpoll", "minstratum", "ntpdata",
"offline", "offset", "online", "onoffline",
"polltarget", "quit", "refresh", "rekey", "reload", "reselect", "reselectdist", "reset",
"retries", "rtcdata", "selectdata", "serverstats", "settime", "shutdown", "smoothing",
"smoothtime", "sourcename", "sources", "sourcestats",
"retries", "rtcdata", "selectdata", "selectopts", "serverstats", "settime",
"shutdown", "smoothing", "smoothtime", "sourcename", "sources", "sourcestats",
"timeout", "tracking", "trimrtc", "waitsync", "writertc",
NULL
};
@@ -1156,7 +1194,7 @@ command_name_generator(const char *text, int state)
while ((name = names[tab_complete_index][list_index++])) {
if (strncmp(name, text, len) == 0) {
return strdup(name);
return Strdup(name);
}
}
@@ -1462,24 +1500,24 @@ request_reply(CMD_Request *request, CMD_Reply *reply, int requested_reply, int v
/* ================================================== */
static void
print_seconds(unsigned long s)
print_seconds(uint32_t s)
{
unsigned long d;
uint32_t d;
if (s == (uint32_t)-1) {
printf(" -");
} else if (s < 1200) {
printf("%4lu", s);
printf("%4"PRIu32, s);
} else if (s < 36000) {
printf("%3lum", s / 60);
printf("%3"PRIu32"m", s / 60);
} else if (s < 345600) {
printf("%3luh", s / 3600);
printf("%3"PRIu32"h", s / 3600);
} else {
d = s / 86400;
if (d > 999) {
printf("%3luy", d / 365);
printf("%3"PRIu32"y", d / 365);
} else {
printf("%3lud", d);
printf("%3"PRIu32"d", d);
}
}
}
@@ -1610,8 +1648,9 @@ print_report(const char *format, ...)
va_list ap;
int i, field, sign, width, prec, spec;
const char *string;
unsigned long long_uinteger;
unsigned int uinteger;
uint64_t uinteger64;
uint32_t uinteger32;
int integer;
struct timespec *ts;
struct tm *tm;
@@ -1709,9 +1748,9 @@ print_report(const char *format, ...)
spec == 'O' ? "seconds" : "ppm",
(dbl > 0.0) ^ (spec != 'O') ? "slow" : "fast");
break;
case 'I': /* interval with unit */
long_uinteger = va_arg(ap, unsigned long);
print_seconds(long_uinteger);
case 'I': /* uint32_t interval with unit */
uinteger32 = va_arg(ap, uint32_t);
print_seconds(uinteger32);
break;
case 'L': /* leap status */
integer = va_arg(ap, int);
@@ -1778,8 +1817,8 @@ print_report(const char *format, ...)
print_freq_ppm(dbl);
break;
case 'R': /* reference ID in hexdecimal */
long_uinteger = va_arg(ap, unsigned long);
printf("%08lX", long_uinteger);
uinteger32 = va_arg(ap, uint32_t);
printf("%08"PRIX32, uinteger32);
break;
case 'S': /* offset with unit */
dbl = va_arg(ap, double);
@@ -1796,14 +1835,18 @@ print_report(const char *format, ...)
strftime(buf, sizeof (buf), "%a %b %d %T %Y", tm);
printf("%s", buf);
break;
case 'U': /* unsigned long in decimal */
long_uinteger = va_arg(ap, unsigned long);
printf("%*lu", width, long_uinteger);
case 'U': /* uint32_t in decimal */
uinteger32 = va_arg(ap, uint32_t);
printf("%*"PRIu32, width, uinteger32);
break;
case 'V': /* timespec as seconds since epoch */
ts = va_arg(ap, struct timespec *);
printf("%s", UTI_TimespecToString(ts));
break;
case 'Q': /* uint64_t in decimal */
uinteger64 = va_arg(ap, uint64_t);
printf("%*"PRIu64, width, uinteger64);
break;
case 'b': /* unsigned int in binary */
uinteger = va_arg(ap, unsigned int);
for (i = prec - 1; i >= 0; i--)
@@ -1963,7 +2006,7 @@ process_cmd_sources(char *line)
IPAddr ip_addr;
uint32_t i, mode, n_sources;
char name[256], mode_ch, state_ch;
int all, verbose;
int all, verbose, ref;
parse_sources_options(line, &all, &verbose);
@@ -2000,9 +2043,8 @@ process_cmd_sources(char *line)
if (!all && ip_addr.family == IPADDR_ID)
continue;
format_name(name, sizeof (name), 25,
mode == RPY_SD_MD_REF && ip_addr.family == IPADDR_INET4,
ip_addr.addr.in4, 1, &ip_addr);
ref = mode == RPY_SD_MD_REF && ip_addr.family == IPADDR_INET4;
format_name(name, sizeof (name), 25, ref, ref ? ip_addr.addr.in4 : 0, 1, &ip_addr);
switch (mode) {
case RPY_SD_MD_CLIENT:
@@ -2051,7 +2093,7 @@ process_cmd_sources(char *line)
ntohs(reply.data.source_data.stratum),
(int16_t)ntohs(reply.data.source_data.poll),
ntohs(reply.data.source_data.reachability),
(unsigned long)ntohl(reply.data.source_data.since_sample),
ntohl(reply.data.source_data.since_sample),
UTI_FloatNetworkToHost(reply.data.source_data.latest_meas),
UTI_FloatNetworkToHost(reply.data.source_data.orig_latest_meas),
UTI_FloatNetworkToHost(reply.data.source_data.latest_meas_err),
@@ -2112,9 +2154,9 @@ process_cmd_sourcestats(char *line)
print_report("%-25s %3U %3U %I %+P %P %+S %S\n",
name,
(unsigned long)ntohl(reply.data.sourcestats.n_samples),
(unsigned long)ntohl(reply.data.sourcestats.n_runs),
(unsigned long)ntohl(reply.data.sourcestats.span_seconds),
ntohl(reply.data.sourcestats.n_samples),
ntohl(reply.data.sourcestats.n_runs),
ntohl(reply.data.sourcestats.span_seconds),
UTI_FloatNetworkToHost(reply.data.sourcestats.resid_freq_ppm),
UTI_FloatNetworkToHost(reply.data.sourcestats.skew_ppm),
UTI_FloatNetworkToHost(reply.data.sourcestats.est_offset),
@@ -2162,7 +2204,7 @@ process_cmd_tracking(char *line)
"Root dispersion : %.9f seconds\n"
"Update interval : %.1f seconds\n"
"Leap status : %L\n",
(unsigned long)ref_id, name,
ref_id, name,
ntohs(reply.data.tracking.stratum),
&ref_time,
UTI_FloatNetworkToHost(reply.data.tracking.current_correction),
@@ -2250,10 +2292,10 @@ process_cmd_authdata(char *line)
print_report("%-27s %4s %5U %4d %4d %I %4d %4d %4d %4d\n",
name, mode_str,
(unsigned long)ntohl(reply.data.auth_data.key_id),
ntohl(reply.data.auth_data.key_id),
ntohs(reply.data.auth_data.key_type),
ntohs(reply.data.auth_data.key_length),
(unsigned long)ntohl(reply.data.auth_data.last_ke_ago),
ntohl(reply.data.auth_data.last_ke_ago),
ntohs(reply.data.auth_data.ke_attempts),
ntohs(reply.data.auth_data.nak),
ntohs(reply.data.auth_data.cookies),
@@ -2311,7 +2353,7 @@ process_cmd_ntpdata(char *line)
request.command = htons(REQ_NTP_DATA);
UTI_IPHostToNetwork(&remote_addr, &request.data.ntp_data.ip_addr);
if (!request_reply(&request, &reply, RPY_NTP_DATA, 0))
if (!request_reply(&request, &reply, RPY_NTP_DATA2, 0))
return 0;
UTI_IPNetworkToHost(&reply.data.ntp_data.remote_addr, &remote_addr);
@@ -2347,18 +2389,21 @@ process_cmd_ntpdata(char *line)
"Total TX : %U\n"
"Total RX : %U\n"
"Total valid RX : %U\n"
"Total good RX : %U\n",
UTI_IPToString(&remote_addr), (unsigned long)UTI_IPToRefid(&remote_addr),
"Total good RX : %U\n"
"Total kernel TX : %U\n"
"Total kernel RX : %U\n"
"Total HW TX : %U\n"
"Total HW RX : %U\n",
UTI_IPToString(&remote_addr), UTI_IPToRefid(&remote_addr),
ntohs(reply.data.ntp_data.remote_port),
UTI_IPToString(&local_addr), (unsigned long)UTI_IPToRefid(&local_addr),
UTI_IPToString(&local_addr), UTI_IPToRefid(&local_addr),
reply.data.ntp_data.leap, reply.data.ntp_data.version,
reply.data.ntp_data.mode, reply.data.ntp_data.stratum,
reply.data.ntp_data.poll, UTI_Log2ToDouble(reply.data.ntp_data.poll),
reply.data.ntp_data.precision, UTI_Log2ToDouble(reply.data.ntp_data.precision),
UTI_FloatNetworkToHost(reply.data.ntp_data.root_delay),
UTI_FloatNetworkToHost(reply.data.ntp_data.root_dispersion),
(unsigned long)ntohl(reply.data.ntp_data.ref_id),
reply.data.ntp_data.stratum <= 1 ?
ntohl(reply.data.ntp_data.ref_id), reply.data.ntp_data.stratum <= 1 ?
UTI_RefidToString(ntohl(reply.data.ntp_data.ref_id)) : "",
&ref_time,
UTI_FloatNetworkToHost(reply.data.ntp_data.offset),
@@ -2372,10 +2417,14 @@ process_cmd_ntpdata(char *line)
ntohs(reply.data.ntp_data.flags) & RPY_NTP_FLAG_INTERLEAVED,
ntohs(reply.data.ntp_data.flags) & RPY_NTP_FLAG_AUTHENTICATED,
reply.data.ntp_data.tx_tss_char, reply.data.ntp_data.rx_tss_char,
(unsigned long)ntohl(reply.data.ntp_data.total_tx_count),
(unsigned long)ntohl(reply.data.ntp_data.total_rx_count),
(unsigned long)ntohl(reply.data.ntp_data.total_valid_count),
(unsigned long)ntohl(reply.data.ntp_data.total_good_count),
ntohl(reply.data.ntp_data.total_tx_count),
ntohl(reply.data.ntp_data.total_rx_count),
ntohl(reply.data.ntp_data.total_valid_count),
ntohl(reply.data.ntp_data.total_good_count),
ntohl(reply.data.ntp_data.total_kernel_tx_ts),
ntohl(reply.data.ntp_data.total_kernel_rx_ts),
ntohl(reply.data.ntp_data.total_hw_tx_ts),
ntohl(reply.data.ntp_data.total_hw_rx_ts),
REPORT_END);
}
@@ -2447,7 +2496,7 @@ process_cmd_selectdata(char *line)
eff_options & RPY_SD_OPTION_TRUST ? 'T' : '-',
eff_options & RPY_SD_OPTION_REQUIRE ? 'R' : '-',
'-',
(unsigned long)ntohl(reply.data.select_data.last_sample_ago),
ntohl(reply.data.select_data.last_sample_ago),
UTI_FloatNetworkToHost(reply.data.select_data.score),
UTI_FloatNetworkToHost(reply.data.select_data.lo_limit),
UTI_FloatNetworkToHost(reply.data.select_data.hi_limit),
@@ -2467,31 +2516,43 @@ process_cmd_serverstats(char *line)
CMD_Reply reply;
request.command = htons(REQ_SERVER_STATS);
if (!request_reply(&request, &reply, RPY_SERVER_STATS3, 0))
if (!request_reply(&request, &reply, RPY_SERVER_STATS4, 0))
return 0;
print_report("NTP packets received : %U\n"
"NTP packets dropped : %U\n"
"Command packets received : %U\n"
"Command packets dropped : %U\n"
"Client log records dropped : %U\n"
"NTS-KE connections accepted: %U\n"
"NTS-KE connections dropped : %U\n"
"Authenticated NTP packets : %U\n"
"Interleaved NTP packets : %U\n"
"NTP timestamps held : %U\n"
"NTP timestamp span : %U\n",
(unsigned long)ntohl(reply.data.server_stats.ntp_hits),
(unsigned long)ntohl(reply.data.server_stats.ntp_drops),
(unsigned long)ntohl(reply.data.server_stats.cmd_hits),
(unsigned long)ntohl(reply.data.server_stats.cmd_drops),
(unsigned long)ntohl(reply.data.server_stats.log_drops),
(unsigned long)ntohl(reply.data.server_stats.nke_hits),
(unsigned long)ntohl(reply.data.server_stats.nke_drops),
(unsigned long)ntohl(reply.data.server_stats.ntp_auth_hits),
(unsigned long)ntohl(reply.data.server_stats.ntp_interleaved_hits),
(unsigned long)ntohl(reply.data.server_stats.ntp_timestamps),
(unsigned long)ntohl(reply.data.server_stats.ntp_span_seconds),
print_report("NTP packets received : %Q\n"
"NTP packets dropped : %Q\n"
"Command packets received : %Q\n"
"Command packets dropped : %Q\n"
"Client log records dropped : %Q\n"
"NTS-KE connections accepted: %Q\n"
"NTS-KE connections dropped : %Q\n"
"Authenticated NTP packets : %Q\n"
"Interleaved NTP packets : %Q\n"
"NTP timestamps held : %Q\n"
"NTP timestamp span : %Q\n"
"NTP daemon RX timestamps : %Q\n"
"NTP daemon TX timestamps : %Q\n"
"NTP kernel RX timestamps : %Q\n"
"NTP kernel TX timestamps : %Q\n"
"NTP hardware RX timestamps : %Q\n"
"NTP hardware TX timestamps : %Q\n",
UTI_Integer64NetworkToHost(reply.data.server_stats.ntp_hits),
UTI_Integer64NetworkToHost(reply.data.server_stats.ntp_drops),
UTI_Integer64NetworkToHost(reply.data.server_stats.cmd_hits),
UTI_Integer64NetworkToHost(reply.data.server_stats.cmd_drops),
UTI_Integer64NetworkToHost(reply.data.server_stats.log_drops),
UTI_Integer64NetworkToHost(reply.data.server_stats.nke_hits),
UTI_Integer64NetworkToHost(reply.data.server_stats.nke_drops),
UTI_Integer64NetworkToHost(reply.data.server_stats.ntp_auth_hits),
UTI_Integer64NetworkToHost(reply.data.server_stats.ntp_interleaved_hits),
UTI_Integer64NetworkToHost(reply.data.server_stats.ntp_timestamps),
UTI_Integer64NetworkToHost(reply.data.server_stats.ntp_span_seconds),
UTI_Integer64NetworkToHost(reply.data.server_stats.ntp_daemon_rx_timestamps),
UTI_Integer64NetworkToHost(reply.data.server_stats.ntp_daemon_tx_timestamps),
UTI_Integer64NetworkToHost(reply.data.server_stats.ntp_kernel_rx_timestamps),
UTI_Integer64NetworkToHost(reply.data.server_stats.ntp_kernel_tx_timestamps),
UTI_Integer64NetworkToHost(reply.data.server_stats.ntp_hw_rx_timestamps),
UTI_Integer64NetworkToHost(reply.data.server_stats.ntp_hw_tx_timestamps),
REPORT_END);
return 1;
@@ -2573,7 +2634,7 @@ process_cmd_rtcreport(char *line)
&ref_time,
ntohs(reply.data.rtc.n_samples),
ntohs(reply.data.rtc.n_runs),
(unsigned long)ntohl(reply.data.rtc.span_seconds),
ntohl(reply.data.rtc.span_seconds),
UTI_FloatNetworkToHost(reply.data.rtc.rtc_seconds_fast),
UTI_FloatNetworkToHost(reply.data.rtc.rtc_gain_rate_ppm),
REPORT_END);
@@ -2648,16 +2709,15 @@ process_cmd_clients(char *line)
print_report("%-25s %6U %5U %C %C %I %6U %5U %C %I\n",
name,
(unsigned long)ntohl(client->ntp_hits),
(unsigned long)ntohl(client->ntp_drops),
ntohl(client->ntp_hits),
ntohl(client->ntp_drops),
client->ntp_interval,
client->ntp_timeout_interval,
(unsigned long)ntohl(client->last_ntp_hit_ago),
(unsigned long)ntohl(nke ? client->nke_hits : client->cmd_hits),
(unsigned long)ntohl(nke ? client->nke_drops : client->cmd_drops),
ntohl(client->last_ntp_hit_ago),
ntohl(nke ? client->nke_hits : client->cmd_hits),
ntohl(nke ? client->nke_drops : client->cmd_drops),
nke ? client->nke_interval : client->cmd_interval,
(unsigned long)ntohl(nke ? client->last_nke_hit_ago :
client->last_cmd_hit_ago),
ntohl(nke ? client->last_nke_hit_ago : client->last_cmd_hit_ago),
REPORT_END);
}
@@ -2688,7 +2748,7 @@ process_cmd_manual_list(const char *line)
return 0;
n_samples = ntohl(reply.data.manual_list.n_samples);
print_info_field("210 n_samples = %lu\n", (unsigned long)n_samples);
print_info_field("210 n_samples = %"PRIu32"\n", n_samples);
print_header("# Date Time(UTC) Slewed Original Residual");
@@ -2808,11 +2868,11 @@ process_cmd_activity(const char *line)
"%U sources doing burst (return to online)\n"
"%U sources doing burst (return to offline)\n"
"%U sources with unknown address\n",
(unsigned long)ntohl(reply.data.activity.online),
(unsigned long)ntohl(reply.data.activity.offline),
(unsigned long)ntohl(reply.data.activity.burst_online),
(unsigned long)ntohl(reply.data.activity.burst_offline),
(unsigned long)ntohl(reply.data.activity.unresolved),
ntohl(reply.data.activity.online),
ntohl(reply.data.activity.offline),
ntohl(reply.data.activity.burst_online),
ntohl(reply.data.activity.burst_offline),
ntohl(reply.data.activity.unresolved),
REPORT_END);
return 1;
@@ -2820,6 +2880,34 @@ process_cmd_activity(const char *line)
/* ================================================== */
static int
process_cmd_offset(CMD_Request *msg, char *line)
{
uint32_t ref_id;
IPAddr ip_addr;
double offset;
char *src;
src = line;
line = CPS_SplitWord(line);
if (!parse_source_address_or_refid(src, &ip_addr, &ref_id) ||
sscanf(line, "%lf", &offset) != 1) {
LOG(LOGS_ERR, "Invalid syntax for offset command");
return 0;
}
UTI_IPHostToNetwork(&ip_addr, &msg->data.modify_offset.address);
msg->data.modify_offset.ref_id = htonl(ref_id);
msg->data.modify_offset.new_offset = UTI_FloatHostToNetwork(offset);
msg->command = htons(REQ_MODIFY_OFFSET);
return 1;
}
/* ================================================== */
static int
process_cmd_reselectdist(CMD_Request *msg, char *line)
{
@@ -2891,6 +2979,50 @@ process_cmd_reset(CMD_Request *msg, char *line)
/* ================================================== */
static int
process_cmd_selectopts(CMD_Request *msg, char *line)
{
int mask, options, option;
uint32_t ref_id;
IPAddr ip_addr;
char *src, *opt;
src = line;
line = CPS_SplitWord(line);
if (!parse_source_address_or_refid(src, &ip_addr, &ref_id)) {
LOG(LOGS_ERR, "Invalid syntax for selectopts command");
return 0;
}
mask = options = 0;
while (*line != '\0') {
opt = line;
line = CPS_SplitWord(line);
if ((opt[0] != '+' && opt[0] != '-') || (option = CPS_GetSelectOption(opt + 1)) == 0) {
LOG(LOGS_ERR, "Invalid syntax for selectopts command");
return 0;
}
mask |= option;
if (opt[0] == '+')
options |= option;
}
UTI_IPHostToNetwork(&ip_addr, &msg->data.modify_select_opts.address);
msg->data.modify_select_opts.ref_id = htonl(ref_id);
msg->data.modify_select_opts.mask = htonl(mask);
msg->data.modify_select_opts.options = htonl(convert_addsrc_sel_options(options));
msg->command = htons(REQ_MODIFY_SELECTOPTS);
return 1;
}
/* ================================================== */
static int
process_cmd_waitsync(char *line)
{
@@ -2926,7 +3058,7 @@ process_cmd_waitsync(char *line)
skew_ppm = UTI_FloatNetworkToHost(reply.data.tracking.skew_ppm);
print_report("try: %d, refid: %R, correction: %.9f, skew: %.3f\n",
i, (unsigned long)ref_id, correction, skew_ppm, REPORT_END);
i, ref_id, correction, skew_ppm, REPORT_END);
if ((ip_addr.family != IPADDR_UNSPEC ||
(ref_id != 0 && ref_id != 0x7f7f0101L /* LOCAL refid */)) &&
@@ -3162,6 +3294,8 @@ process_line(char *line)
ret = process_cmd_ntpdata(line);
} else if (!strcmp(command, "offline")) {
do_normal_submit = process_cmd_offline(&tx_message, line);
} else if (!strcmp(command, "offset")) {
do_normal_submit = process_cmd_offset(&tx_message, line);
} else if (!strcmp(command, "online")) {
do_normal_submit = process_cmd_online(&tx_message, line);
} else if (!strcmp(command, "onoffline")) {
@@ -3193,6 +3327,8 @@ process_line(char *line)
} else if (!strcmp(command, "selectdata")) {
do_normal_submit = 0;
ret = process_cmd_selectdata(line);
} else if (!strcmp(command, "selectopts")) {
do_normal_submit = process_cmd_selectopts(&tx_message, line);
} else if (!strcmp(command, "serverstats")) {
do_normal_submit = 0;
ret = process_cmd_serverstats(line);
@@ -3243,6 +3379,10 @@ process_line(char *line)
ret = request_reply(&tx_message, &rx_message, RPY_NULL, 1);
}
if (end_dot) {
printf(".\n");
}
fflush(stderr);
if (fflush(stdout) != 0 || ferror(stdout) != 0) {
@@ -3300,7 +3440,7 @@ static void
display_gpl(void)
{
printf("chrony version %s\n"
"Copyright (C) 1997-2003, 2007, 2009-2022 Richard P. Curnow and others\n"
"Copyright (C) 1997-2003, 2007, 2009-2023 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",
@@ -3319,6 +3459,7 @@ print_help(const char *progname)
" -n\t\tDon't resolve hostnames\n"
" -N\t\tPrint original source names\n"
" -c\t\tEnable CSV format\n"
" -e\t\tEnd responses with dot\n"
#if DEBUG > 0
" -d\t\tEnable debug messages\n"
#endif
@@ -3363,7 +3504,7 @@ main(int argc, char **argv)
optind = 1;
/* Parse short command-line options */
while ((opt = getopt(argc, argv, "+46acdf:h:mnNp:v")) != -1) {
while ((opt = getopt(argc, argv, "+46acdef:h:mnNp:v")) != -1) {
switch (opt) {
case '4':
case '6':
@@ -3381,6 +3522,9 @@ main(int argc, char **argv)
log_min_severity = LOGS_DEBUG;
#endif
break;
case 'e':
end_dot = 1;
break;
case 'h':
hostnames = optarg;
break;

View File

@@ -117,6 +117,14 @@ static int token_shift[MAX_SERVICES];
static int leak_rate[MAX_SERVICES];
/* Rates at which responses requesting clients to reduce their rate
(e.g. NTP KoD RATE) are randomly allowed (in log2, but 0 means disabled) */
#define MIN_KOD_RATE 0
#define MAX_KOD_RATE 4
static int kod_rate[MAX_SERVICES];
/* Limit intervals in log2 */
static int limit_interval[MAX_SERVICES];
@@ -126,7 +134,8 @@ static int active;
/* RX and TX timestamp saved for clients using interleaved mode */
typedef struct {
uint64_t rx_ts;
uint16_t flags;
uint8_t flags;
uint8_t tx_ts_source;
uint16_t slew_epoch;
int32_t tx_ts_offset;
} NtpTimestamps;
@@ -155,12 +164,17 @@ static NtpTimestampMap ntp_ts_map;
/* Maximum number of timestamps moved in the array to insert a new timestamp */
#define NTPTS_INSERT_LIMIT 64
/* Maximum expected value of the timestamp source */
#define MAX_NTP_TS NTP_TS_HARDWARE
/* Global statistics */
static uint32_t total_hits[MAX_SERVICES];
static uint32_t total_drops[MAX_SERVICES];
static uint32_t total_ntp_auth_hits;
static uint32_t total_ntp_interleaved_hits;
static uint32_t total_record_drops;
static uint64_t total_hits[MAX_SERVICES];
static uint64_t total_drops[MAX_SERVICES];
static uint64_t total_ntp_auth_hits;
static uint64_t total_ntp_interleaved_hits;
static uint64_t total_record_drops;
static uint64_t total_ntp_rx_timestamps[MAX_NTP_TS + 1];
static uint64_t total_ntp_tx_timestamps[MAX_NTP_TS + 1];
#define NSEC_PER_SEC 1000000000U
@@ -348,18 +362,19 @@ set_bucket_params(int interval, int burst, uint16_t *max_tokens,
void
CLG_Initialise(void)
{
int i, interval, burst, lrate, slots2;
int i, interval, burst, lrate, krate, slots2;
for (i = 0; i < MAX_SERVICES; i++) {
max_tokens[i] = 0;
tokens_per_hit[i] = 0;
token_shift[i] = 0;
leak_rate[i] = 0;
kod_rate[i] = 0;
limit_interval[i] = MIN_LIMIT_INTERVAL;
switch (i) {
case CLG_NTP:
if (!CNF_GetNTPRateLimit(&interval, &burst, &lrate))
if (!CNF_GetNTPRateLimit(&interval, &burst, &lrate, &krate))
continue;
break;
case CLG_NTSKE:
@@ -376,6 +391,7 @@ CLG_Initialise(void)
set_bucket_params(interval, burst, &max_tokens[i], &tokens_per_hit[i], &token_shift[i]);
leak_rate[i] = CLAMP(MIN_LEAK_RATE, lrate, MAX_LEAK_RATE);
kod_rate[i] = CLAMP(MIN_KOD_RATE, krate, MAX_KOD_RATE);
limit_interval[i] = CLAMP(MIN_LIMIT_INTERVAL, interval, MAX_LIMIT_INTERVAL);
}
@@ -573,28 +589,28 @@ CLG_LogServiceAccess(CLG_Service service, IPAddr *client, struct timespec *now)
/* ================================================== */
static int
limit_response_random(int leak_rate)
limit_response_random(int rate)
{
static uint32_t rnd;
static int bits_left = 0;
int r;
if (bits_left < leak_rate) {
if (bits_left < rate) {
UTI_GetRandomBytes(&rnd, sizeof (rnd));
bits_left = 8 * sizeof (rnd);
}
/* Return zero on average once per 2^leak_rate */
r = rnd % (1U << leak_rate) ? 1 : 0;
rnd >>= leak_rate;
bits_left -= leak_rate;
/* Return zero on average once per 2^rate */
r = rnd % (1U << rate) ? 1 : 0;
rnd >>= rate;
bits_left -= rate;
return r;
}
/* ================================================== */
int
CLG_Limit
CLG_LimitServiceRate(CLG_Service service, int index)
{
Record *record;
@@ -603,14 +619,14 @@ CLG_LimitServiceRate(CLG_Service service, int index)
check_service_number(service);
if (tokens_per_hit[service] == 0)
return 0;
return CLG_PASS;
record = ARR_GetElement(records, index);
record->drop_flags &= ~(1U << service);
if (record->tokens[service] >= tokens_per_hit[service]) {
record->tokens[service] -= tokens_per_hit[service];
return 0;
return CLG_PASS;
}
drop = limit_response_random(leak_rate[service]);
@@ -626,22 +642,31 @@ CLG_LimitServiceRate(CLG_Service service, int index)
if (!drop) {
record->tokens[service] = 0;
return 0;
return CLG_PASS;
}
if (kod_rate[service] > 0 && !limit_response_random(kod_rate[service])) {
return CLG_KOD;
}
record->drop_flags |= 1U << service;
record->drops[service]++;
total_drops[service]++;
return 1;
return CLG_DROP;
}
/* ================================================== */
void
CLG_LogAuthNtpRequest(void)
CLG_UpdateNtpStats(int auth, NTP_Timestamp_Source rx_ts_src, NTP_Timestamp_Source tx_ts_src)
{
total_ntp_auth_hits++;
if (auth)
total_ntp_auth_hits++;
if (rx_ts_src >= 0 && rx_ts_src <= MAX_NTP_TS)
total_ntp_rx_timestamps[rx_ts_src]++;
if (tx_ts_src >= 0 && tx_ts_src <= MAX_NTP_TS)
total_ntp_tx_timestamps[tx_ts_src]++;
}
/* ================================================== */
@@ -773,7 +798,8 @@ push_ntp_tss(uint32_t index)
/* ================================================== */
static void
set_ntp_tx_offset(NtpTimestamps *tss, NTP_int64 *rx_ts, struct timespec *tx_ts)
set_ntp_tx(NtpTimestamps *tss, NTP_int64 *rx_ts, struct timespec *tx_ts,
NTP_Timestamp_Source tx_src)
{
struct timespec ts;
@@ -792,12 +818,13 @@ set_ntp_tx_offset(NtpTimestamps *tss, NTP_int64 *rx_ts, struct timespec *tx_ts)
tss->tx_ts_offset = (int32_t)ts.tv_nsec + (int32_t)ts.tv_sec * (int32_t)NSEC_PER_SEC;
tss->flags |= NTPTS_VALID_TX;
tss->tx_ts_source = tx_src;
}
/* ================================================== */
static void
get_ntp_tx(NtpTimestamps *tss, struct timespec *tx_ts)
get_ntp_tx(NtpTimestamps *tss, struct timespec *tx_ts, NTP_Timestamp_Source *tx_src)
{
int32_t offset = tss->tx_ts_offset;
NTP_int64 ntp_ts;
@@ -814,12 +841,14 @@ get_ntp_tx(NtpTimestamps *tss, struct timespec *tx_ts)
} else {
UTI_ZeroTimespec(tx_ts);
}
*tx_src = tss->tx_ts_source;
}
/* ================================================== */
void
CLG_SaveNtpTimestamps(NTP_int64 *rx_ts, struct timespec *tx_ts)
CLG_SaveNtpTimestamps(NTP_int64 *rx_ts, struct timespec *tx_ts, NTP_Timestamp_Source tx_src)
{
NtpTimestamps *tss;
uint32_t i, index;
@@ -877,7 +906,7 @@ CLG_SaveNtpTimestamps(NTP_int64 *rx_ts, struct timespec *tx_ts)
tss->rx_ts = rx;
tss->flags = 0;
tss->slew_epoch = ntp_ts_map.slew_epoch;
set_ntp_tx_offset(tss, rx_ts, tx_ts);
set_ntp_tx(tss, rx_ts, tx_ts, tx_src);
DEBUG_LOG("Saved RX+TX index=%"PRIu32" first=%"PRIu32" size=%"PRIu32,
index, ntp_ts_map.first, ntp_ts_map.size);
@@ -921,7 +950,8 @@ CLG_UndoNtpTxTimestampSlew(NTP_int64 *rx_ts, struct timespec *tx_ts)
/* ================================================== */
void
CLG_UpdateNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts)
CLG_UpdateNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts,
NTP_Timestamp_Source tx_src)
{
uint32_t index;
@@ -931,13 +961,14 @@ CLG_UpdateNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts)
if (!find_ntp_rx_ts(ntp64_to_int64(rx_ts), &index))
return;
set_ntp_tx_offset(get_ntp_tss(index), rx_ts, tx_ts);
set_ntp_tx(get_ntp_tss(index), rx_ts, tx_ts, tx_src);
}
/* ================================================== */
int
CLG_GetNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts)
CLG_GetNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts,
NTP_Timestamp_Source *tx_src)
{
NtpTimestamps *tss;
uint32_t index;
@@ -953,7 +984,7 @@ CLG_GetNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts)
if (tss->flags & NTPTS_DISABLED)
return 0;
get_ntp_tx(tss, tx_ts);
get_ntp_tx(tss, tx_ts, tx_src);
return 1;
}
@@ -1085,4 +1116,10 @@ CLG_GetServerStatsReport(RPT_ServerStatsReport *report)
report->ntp_span_seconds = ntp_ts_map.size > 1 ?
(get_ntp_tss(ntp_ts_map.size - 1)->rx_ts -
get_ntp_tss(0)->rx_ts) >> 32 : 0;
report->ntp_daemon_rx_timestamps = total_ntp_rx_timestamps[NTP_TS_DAEMON];
report->ntp_daemon_tx_timestamps = total_ntp_tx_timestamps[NTP_TS_DAEMON];
report->ntp_kernel_rx_timestamps = total_ntp_rx_timestamps[NTP_TS_KERNEL];
report->ntp_kernel_tx_timestamps = total_ntp_tx_timestamps[NTP_TS_KERNEL];
report->ntp_hw_rx_timestamps = total_ntp_rx_timestamps[NTP_TS_HARDWARE];
report->ntp_hw_tx_timestamps = total_ntp_tx_timestamps[NTP_TS_HARDWARE];
}

View File

@@ -37,19 +37,29 @@ typedef enum {
CLG_CMDMON,
} CLG_Service;
typedef enum {
CLG_PASS = 0,
CLG_DROP,
CLG_KOD,
} CLG_Limit;
extern void CLG_Initialise(void);
extern void CLG_Finalise(void);
extern int CLG_GetClientIndex(IPAddr *client);
extern int CLG_LogServiceAccess(CLG_Service service, IPAddr *client, struct timespec *now);
extern int CLG_LimitServiceRate(CLG_Service service, int index);
extern void CLG_LogAuthNtpRequest(void);
extern CLG_Limit CLG_LimitServiceRate(CLG_Service service, int index);
extern void CLG_UpdateNtpStats(int auth, NTP_Timestamp_Source rx_ts_src,
NTP_Timestamp_Source tx_ts_src);
extern int CLG_GetNtpMinPoll(void);
/* Functions to save and retrieve timestamps for server interleaved mode */
extern void CLG_SaveNtpTimestamps(NTP_int64 *rx_ts, struct timespec *tx_ts);
extern void CLG_SaveNtpTimestamps(NTP_int64 *rx_ts, struct timespec *tx_ts,
NTP_Timestamp_Source tx_src);
extern void CLG_UndoNtpTxTimestampSlew(NTP_int64 *rx_ts, struct timespec *tx_ts);
extern void CLG_UpdateNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts);
extern int CLG_GetNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts);
extern void CLG_UpdateNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts,
NTP_Timestamp_Source tx_src);
extern int CLG_GetNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts,
NTP_Timestamp_Source *tx_src);
extern void CLG_DisableNtpTimestamps(NTP_int64 *rx_ts);
/* And some reporting functions, for use by chronyc. */

172
cmdmon.c
View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2016, 2018-2021
* Copyright (C) Miroslav Lichvar 2009-2016, 2018-2023
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -144,6 +144,9 @@ static const char permissions[] = {
PERMIT_AUTH, /* SELECT_DATA */
PERMIT_AUTH, /* RELOAD_SOURCES */
PERMIT_AUTH, /* DOFFSET2 */
PERMIT_AUTH, /* MODIFY_SELECTOPTS */
PERMIT_AUTH, /* MODIFY_OFFSET */
PERMIT_AUTH, /* LOCAL3 */
};
/* ================================================== */
@@ -529,7 +532,8 @@ handle_local(CMD_Request *rx_message, CMD_Reply *tx_message)
if (ntohl(rx_message->data.local.on_off)) {
REF_EnableLocal(ntohl(rx_message->data.local.stratum),
UTI_FloatNetworkToHost(rx_message->data.local.distance),
ntohl(rx_message->data.local.orphan));
ntohl(rx_message->data.local.orphan),
UTI_FloatNetworkToHost(rx_message->data.local.activate));
} else {
REF_DisableLocal();
}
@@ -703,14 +707,26 @@ handle_cmdaccheck(CMD_Request *rx_message, CMD_Reply *tx_message)
/* ================================================== */
static int
convert_addsrc_select_options(int flags)
{
return (flags & REQ_ADDSRC_PREFER ? SRC_SELECT_PREFER : 0) |
(flags & REQ_ADDSRC_NOSELECT ? SRC_SELECT_NOSELECT : 0) |
(flags & REQ_ADDSRC_TRUST ? SRC_SELECT_TRUST : 0) |
(flags & REQ_ADDSRC_REQUIRE ? SRC_SELECT_REQUIRE : 0);
}
/* ================================================== */
static void
handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
{
NTP_Source_Type type;
SourceParameters params;
int family, pool, port;
NSR_Status status;
uint32_t flags;
char *name;
int pool, port;
switch (ntohl(rx_message->data.ntp_source.type)) {
case REQ_ADDSRC_SERVER:
@@ -738,6 +754,10 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
return;
}
flags = ntohl(rx_message->data.ntp_source.flags);
family = flags & REQ_ADDSRC_IPV4 ? IPADDR_INET4 :
flags & REQ_ADDSRC_IPV6 ? IPADDR_INET6 : IPADDR_UNSPEC;
port = ntohl(rx_message->data.ntp_source.port);
params.minpoll = ntohl(rx_message->data.ntp_source.minpoll);
params.maxpoll = ntohl(rx_message->data.ntp_source.maxpoll);
@@ -763,23 +783,19 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
params.asymmetry = UTI_FloatNetworkToHost(rx_message->data.ntp_source.asymmetry);
params.offset = UTI_FloatNetworkToHost(rx_message->data.ntp_source.offset);
params.connectivity = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_ONLINE ?
SRC_ONLINE : SRC_OFFLINE;
params.auto_offline = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_AUTOOFFLINE ? 1 : 0;
params.iburst = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_IBURST ? 1 : 0;
params.interleaved = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_INTERLEAVED ? 1 : 0;
params.burst = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_BURST ? 1 : 0;
params.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) |
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_TRUST ? SRC_SELECT_TRUST : 0) |
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_REQUIRE ? SRC_SELECT_REQUIRE : 0);
params.connectivity = flags & REQ_ADDSRC_ONLINE ? SRC_ONLINE : SRC_OFFLINE;
params.auto_offline = !!(flags & REQ_ADDSRC_AUTOOFFLINE);
params.iburst = !!(flags & REQ_ADDSRC_IBURST);
params.interleaved = !!(flags & REQ_ADDSRC_INTERLEAVED);
params.burst = !!(flags & REQ_ADDSRC_BURST);
params.nts = !!(flags & REQ_ADDSRC_NTS);
params.copy = !!(flags & REQ_ADDSRC_COPY);
params.ext_fields = (flags & REQ_ADDSRC_EF_EXP_MONO_ROOT ? NTP_EF_FLAG_EXP_MONO_ROOT : 0) |
(flags & REQ_ADDSRC_EF_EXP_NET_CORRECTION ?
NTP_EF_FLAG_EXP_NET_CORRECTION : 0);
params.sel_options = convert_addsrc_select_options(ntohl(rx_message->data.ntp_source.flags));
status = NSR_AddSourceByName(name, port, pool, type, &params, NULL);
status = NSR_AddSourceByName(name, family, port, pool, type, &params, NULL);
switch (status) {
case NSR_Success:
break;
@@ -797,6 +813,8 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
tx_message->status = htons(STT_INVALIDNAME);
break;
case NSR_InvalidAF:
tx_message->status = htons(STT_INVALIDAF);
break;
case NSR_NoSuchSource:
assert(0);
break;
@@ -1169,18 +1187,36 @@ handle_server_stats(CMD_Request *rx_message, CMD_Reply *tx_message)
RPT_ServerStatsReport report;
CLG_GetServerStatsReport(&report);
tx_message->reply = htons(RPY_SERVER_STATS3);
tx_message->data.server_stats.ntp_hits = htonl(report.ntp_hits);
tx_message->data.server_stats.nke_hits = htonl(report.nke_hits);
tx_message->data.server_stats.cmd_hits = htonl(report.cmd_hits);
tx_message->data.server_stats.ntp_drops = htonl(report.ntp_drops);
tx_message->data.server_stats.nke_drops = htonl(report.nke_drops);
tx_message->data.server_stats.cmd_drops = htonl(report.cmd_drops);
tx_message->data.server_stats.log_drops = htonl(report.log_drops);
tx_message->data.server_stats.ntp_auth_hits = htonl(report.ntp_auth_hits);
tx_message->data.server_stats.ntp_interleaved_hits = htonl(report.ntp_interleaved_hits);
tx_message->data.server_stats.ntp_timestamps = htonl(report.ntp_timestamps);
tx_message->data.server_stats.ntp_span_seconds = htonl(report.ntp_span_seconds);
tx_message->reply = htons(RPY_SERVER_STATS4);
tx_message->data.server_stats.ntp_hits = UTI_Integer64HostToNetwork(report.ntp_hits);
tx_message->data.server_stats.nke_hits = UTI_Integer64HostToNetwork(report.nke_hits);
tx_message->data.server_stats.cmd_hits = UTI_Integer64HostToNetwork(report.cmd_hits);
tx_message->data.server_stats.ntp_drops = UTI_Integer64HostToNetwork(report.ntp_drops);
tx_message->data.server_stats.nke_drops = UTI_Integer64HostToNetwork(report.nke_drops);
tx_message->data.server_stats.cmd_drops = UTI_Integer64HostToNetwork(report.cmd_drops);
tx_message->data.server_stats.log_drops = UTI_Integer64HostToNetwork(report.log_drops);
tx_message->data.server_stats.ntp_auth_hits =
UTI_Integer64HostToNetwork(report.ntp_auth_hits);
tx_message->data.server_stats.ntp_interleaved_hits =
UTI_Integer64HostToNetwork(report.ntp_interleaved_hits);
tx_message->data.server_stats.ntp_timestamps =
UTI_Integer64HostToNetwork(report.ntp_timestamps);
tx_message->data.server_stats.ntp_span_seconds =
UTI_Integer64HostToNetwork(report.ntp_span_seconds);
tx_message->data.server_stats.ntp_daemon_rx_timestamps =
UTI_Integer64HostToNetwork(report.ntp_daemon_rx_timestamps);
tx_message->data.server_stats.ntp_daemon_tx_timestamps =
UTI_Integer64HostToNetwork(report.ntp_daemon_tx_timestamps);
tx_message->data.server_stats.ntp_kernel_rx_timestamps =
UTI_Integer64HostToNetwork(report.ntp_kernel_rx_timestamps);
tx_message->data.server_stats.ntp_kernel_tx_timestamps =
UTI_Integer64HostToNetwork(report.ntp_kernel_tx_timestamps);
tx_message->data.server_stats.ntp_hw_rx_timestamps =
UTI_Integer64HostToNetwork(report.ntp_hw_rx_timestamps);
tx_message->data.server_stats.ntp_hw_tx_timestamps =
UTI_Integer64HostToNetwork(report.ntp_hw_tx_timestamps);
memset(tx_message->data.server_stats.reserved, 0xff,
sizeof (tx_message->data.server_stats.reserved));
}
/* ================================================== */
@@ -1197,7 +1233,7 @@ handle_ntp_data(CMD_Request *rx_message, CMD_Reply *tx_message)
return;
}
tx_message->reply = htons(RPY_NTP_DATA);
tx_message->reply = htons(RPY_NTP_DATA2);
UTI_IPHostToNetwork(&report.remote_addr, &tx_message->data.ntp_data.remote_addr);
UTI_IPHostToNetwork(&report.local_addr, &tx_message->data.ntp_data.local_addr);
tx_message->data.ntp_data.remote_port = htons(report.remote_port);
@@ -1225,6 +1261,10 @@ handle_ntp_data(CMD_Request *rx_message, CMD_Reply *tx_message)
tx_message->data.ntp_data.total_rx_count = htonl(report.total_rx_count);
tx_message->data.ntp_data.total_valid_count = htonl(report.total_valid_count);
tx_message->data.ntp_data.total_good_count = htonl(report.total_good_count);
tx_message->data.ntp_data.total_kernel_tx_ts = htonl(report.total_kernel_tx_ts);
tx_message->data.ntp_data.total_kernel_rx_ts = htonl(report.total_kernel_rx_ts);
tx_message->data.ntp_data.total_hw_tx_ts = htonl(report.total_hw_tx_ts);
tx_message->data.ntp_data.total_hw_rx_ts = htonl(report.total_hw_rx_ts);
memset(tx_message->data.ntp_data.reserved, 0xff, sizeof (tx_message->data.ntp_data.reserved));
}
@@ -1328,7 +1368,7 @@ handle_auth_data(CMD_Request *rx_message, CMD_Reply *tx_message)
/* ================================================== */
static uint16_t
convert_select_options(int options)
convert_sd_sel_options(int options)
{
return (options & SRC_SELECT_PREFER ? RPY_SD_OPTION_PREFER : 0) |
(options & SRC_SELECT_NOSELECT ? RPY_SD_OPTION_NOSELECT : 0) |
@@ -1355,14 +1395,50 @@ handle_select_data(CMD_Request *rx_message, CMD_Reply *tx_message)
tx_message->data.select_data.state_char = report.state_char;
tx_message->data.select_data.authentication = report.authentication;
tx_message->data.select_data.leap = report.leap;
tx_message->data.select_data.conf_options = htons(convert_select_options(report.conf_options));
tx_message->data.select_data.eff_options = htons(convert_select_options(report.eff_options));
tx_message->data.select_data.conf_options = htons(convert_sd_sel_options(report.conf_options));
tx_message->data.select_data.eff_options = htons(convert_sd_sel_options(report.eff_options));
tx_message->data.select_data.last_sample_ago = htonl(report.last_sample_ago);
tx_message->data.select_data.score = UTI_FloatHostToNetwork(report.score);
tx_message->data.select_data.hi_limit = UTI_FloatHostToNetwork(report.hi_limit);
tx_message->data.select_data.lo_limit = UTI_FloatHostToNetwork(report.lo_limit);
}
/* ================================================== */
static void
handle_modify_selectopts(CMD_Request *rx_message, CMD_Reply *tx_message)
{
int mask, options;
uint32_t ref_id;
IPAddr ip_addr;
UTI_IPNetworkToHost(&rx_message->data.modify_select_opts.address, &ip_addr);
ref_id = ntohl(rx_message->data.modify_select_opts.ref_id);
mask = ntohl(rx_message->data.modify_select_opts.mask);
options = convert_addsrc_select_options(ntohl(rx_message->data.modify_select_opts.options));
if (!SRC_ModifySelectOptions(&ip_addr, ref_id, options, mask))
tx_message->status = htons(STT_NOSUCHSOURCE);
}
/* ================================================== */
static void
handle_modify_offset(CMD_Request *rx_message, CMD_Reply *tx_message)
{
uint32_t ref_id;
IPAddr ip_addr;
double offset;
UTI_IPNetworkToHost(&rx_message->data.modify_offset.address, &ip_addr);
ref_id = ntohl(rx_message->data.modify_offset.ref_id);
offset = UTI_FloatNetworkToHost(rx_message->data.modify_offset.new_offset);
if ((ip_addr.family != IPADDR_UNSPEC && !NSR_ModifyOffset(&ip_addr, offset)) ||
(ip_addr.family == IPADDR_UNSPEC && !RCL_ModifyOffset(ref_id, offset)))
tx_message->status = htons(STT_NOSUCHSOURCE);
}
/* ================================================== */
/* Read a packet and process it */
@@ -1437,9 +1513,10 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
/* Don't reply to all requests from hosts other than localhost if the rate
is excessive */
if (!localhost && log_index >= 0 && CLG_LimitServiceRate(CLG_CMDMON, log_index)) {
DEBUG_LOG("Command packet discarded to limit response rate");
return;
if (!localhost && log_index >= 0 &&
CLG_LimitServiceRate(CLG_CMDMON, log_index) != CLG_PASS) {
DEBUG_LOG("Command packet discarded to limit response rate");
return;
}
expected_length = PKL_CommandLength(&rx_message);
@@ -1515,6 +1592,8 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
}
if (allowed) {
LOG_SetContext(LOGC_Command);
switch(rx_command) {
case REQ_NULL:
/* Do nothing */
@@ -1572,8 +1651,8 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
case REQ_SETTIME:
handle_settime(&rx_message, &tx_message);
break;
case REQ_LOCAL2:
case REQ_LOCAL3:
handle_local(&rx_message, &tx_message);
break;
@@ -1757,11 +1836,21 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
handle_reload_sources(&rx_message, &tx_message);
break;
case REQ_MODIFY_SELECTOPTS:
handle_modify_selectopts(&rx_message, &tx_message);
break;
case REQ_MODIFY_OFFSET:
handle_modify_offset(&rx_message, &tx_message);
break;
default:
DEBUG_LOG("Unhandled command %d", rx_command);
tx_message.status = htons(STT_FAILED);
break;
}
LOG_UnsetContext(LOGC_Command);
} else {
tx_message.status = htons(STT_UNAUTH);
}
@@ -1795,6 +1884,9 @@ CAM_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all)
if (status == ADF_BADSUBNET) {
return 0;
} else if (status == ADF_SUCCESS) {
LOG(LOG_GetContextSeverity(LOGC_Command), "%s%s %s access from %s",
allow ? "Allowed" : "Denied", all ? " all" : "", "command",
UTI_IPSubnetToString(ip_addr, subnet_bits));
return 1;
} else {
return 0;

View File

@@ -44,8 +44,9 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
{
char *hostname, *cmd;
uint32_t ef_type;
int n;
int n, sel_option;
src->family = IPADDR_UNSPEC;
src->port = SRC_DEFAULT_PORT;
src->params.minpoll = SRC_DEFAULT_MINPOLL;
src->params.maxpoll = SRC_DEFAULT_MAXPOLL;
@@ -101,14 +102,6 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
src->params.iburst = 1;
} else if (!strcasecmp(cmd, "offline")) {
src->params.connectivity = SRC_OFFLINE;
} else if (!strcasecmp(cmd, "noselect")) {
src->params.sel_options |= SRC_SELECT_NOSELECT;
} else if (!strcasecmp(cmd, "prefer")) {
src->params.sel_options |= SRC_SELECT_PREFER;
} else if (!strcasecmp(cmd, "require")) {
src->params.sel_options |= SRC_SELECT_REQUIRE;
} else if (!strcasecmp(cmd, "trust")) {
src->params.sel_options |= SRC_SELECT_TRUST;
} else if (!strcasecmp(cmd, "certset")) {
if (sscanf(line, "%"SCNu32"%n", &src->params.cert_set, &n) != 1)
return 0;
@@ -123,8 +116,11 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
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;
case NTP_EF_EXP_MONO_ROOT:
src->params.ext_fields |= NTP_EF_FLAG_EXP_MONO_ROOT;
break;
case NTP_EF_EXP_NET_CORRECTION:
src->params.ext_fields |= NTP_EF_FLAG_EXP_NET_CORRECTION;
break;
default:
return 0;
@@ -132,6 +128,10 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
} else if (!strcasecmp(cmd, "filter")) {
if (sscanf(line, "%d%n", &src->params.filter_length, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "ipv4")) {
src->family = IPADDR_INET4;
} else if (!strcasecmp(cmd, "ipv6")) {
src->family = IPADDR_INET6;
} else if (!strcasecmp(cmd, "maxdelay")) {
if (sscanf(line, "%lf%n", &src->params.max_delay, &n) != 1)
return 0;
@@ -187,6 +187,8 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
return 0;
} else if (!strcasecmp(cmd, "xleave")) {
src->params.interleaved = 1;
} else if ((sel_option = CPS_GetSelectOption(cmd)) != 0) {
src->params.sel_options |= sel_option;
} else {
return 0;
}
@@ -197,6 +199,23 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
/* ================================================== */
int
CPS_GetSelectOption(char *option)
{
if (!strcasecmp(option, "noselect")) {
return SRC_SELECT_NOSELECT;
} else if (!strcasecmp(option, "prefer")) {
return SRC_SELECT_PREFER;
} else if (!strcasecmp(option, "require")) {
return SRC_SELECT_REQUIRE;
} else if (!strcasecmp(option, "trust")) {
return SRC_SELECT_TRUST;
}
return 0;
}
/* ================================================== */
int
CPS_ParseAllowDeny(char *line, int *all, IPAddr *ip, int *subnet_bits)
{
@@ -277,13 +296,14 @@ CPS_ParseAllowDeny(char *line, int *all, IPAddr *ip, int *subnet_bits)
/* ================================================== */
int
CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance)
CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance, double *activate)
{
int n;
char *cmd;
*stratum = 10;
*distance = 1.0;
*activate = 0.0;
*orphan = 0;
while (*line) {
@@ -300,6 +320,9 @@ CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance)
} else if (!strcasecmp(cmd, "distance")) {
if (sscanf(line, "%lf%n", distance, &n) != 1)
return 0;
} else if (!strcasecmp(cmd, "activate")) {
if (sscanf(line, "%lf%n", activate, &n) != 1)
return 0;
} else {
return 0;
}
@@ -396,3 +419,19 @@ CPS_ParseKey(char *line, uint32_t *id, const char **type, char **key)
return 1;
}
/* ================================================== */
int
CPS_ParseRefid(char *line, uint32_t *ref_id)
{
int i;
for (i = *ref_id = 0; line[i] && !isspace((unsigned char)line[i]); i++) {
if (i >= 4)
return 0;
*ref_id |= (uint32_t)line[i] << (24 - i * 8);
}
return i;
}

View File

@@ -32,6 +32,7 @@
typedef struct {
char *name;
int family;
int port;
SourceParameters params;
} CPS_NTP_Source;
@@ -39,11 +40,14 @@ typedef struct {
/* Parse a command to add an NTP server or peer */
extern int CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src);
/* Get an NTP/refclock select option */
extern int CPS_GetSelectOption(char *option);
/* Parse a command to allow/deny access */
extern int CPS_ParseAllowDeny(char *line, int *all, IPAddr *ip, int *subnet_bits);
/* Parse a command to enable local reference */
extern int CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance);
extern int CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance, double *activate);
/* Remove extra white-space and comments */
extern void CPS_NormalizeLine(char *line);
@@ -54,4 +58,7 @@ extern char *CPS_SplitWord(char *line);
/* Parse a key from keyfile */
extern int CPS_ParseKey(char *line, uint32_t *id, const char **type, char **key);
/* Parse a refclock reference ID (returns number of characters) */
extern int CPS_ParseRefid(char *line, uint32_t *ref_id);
#endif /* GOT_CMDPARSE_H */

204
conf.c
View File

@@ -79,7 +79,7 @@ static void parse_maxchange(char *);
static void parse_ntsserver(char *, ARR_Instance files);
static void parse_ntstrustedcerts(char *);
static void parse_ratelimit(char *line, int *enabled, int *interval,
int *burst, int *leak);
int *burst, int *leak, int *kod);
static void parse_refclock(char *);
static void parse_smoothtime(char *);
static void parse_source(char *line, char *type, int fatal);
@@ -129,6 +129,7 @@ static int enable_local=0;
static int local_stratum;
static int local_orphan;
static double local_distance;
static double local_activate;
/* Threshold (in seconds) - if absolute value of initial error is less
than this, slew instead of stepping */
@@ -220,6 +221,7 @@ static int ntp_ratelimit_enabled = 0;
static int ntp_ratelimit_interval = 3;
static int ntp_ratelimit_burst = 8;
static int ntp_ratelimit_leak = 2;
static int ntp_ratelimit_kod = 0;
static int nts_ratelimit_enabled = 0;
static int nts_ratelimit_interval = 6;
static int nts_ratelimit_burst = 8;
@@ -249,9 +251,15 @@ static REF_LeapMode leapsec_mode = REF_LeapModeSystem;
/* Name of a system timezone containing leap seconds occuring at midnight */
static char *leapsec_tz = NULL;
/* File name of leap seconds list, usually /usr/share/zoneinfo/leap-seconds.list */
static char *leapsec_list = NULL;
/* Name of the user to which will be dropped root privileges. */
static char *user;
/* Address refresh interval */
static int refresh = 1209600; /* 2 weeks */
/* NTS server and client configuration */
static char *nts_dump_dir = NULL;
static char *nts_ntp_server = NULL;
@@ -274,8 +282,13 @@ static int no_system_cert = 0;
/* Array of CNF_HwTsInterface */
static ARR_Instance hwts_interfaces;
/* Timeout for resuming reading from sockets waiting for HW TX timestamp */
static double hwts_timeout = 0.001;
/* PTP event port (disabled by default) */
static int ptp_port = 0;
/* PTP domain number of NTP-over-PTP messages */
static int ptp_domain = 123;
typedef struct {
NTP_Source_Type type;
@@ -289,6 +302,8 @@ static ARR_Instance ntp_sources;
static ARR_Instance ntp_source_dirs;
/* Array of uint32_t corresponding to ntp_sources (for sourcedirs reload) */
static ARR_Instance ntp_source_ids;
/* Flag indicating ntp_sources and ntp_source_ids are used for sourcedirs */
static int conf_ntp_sources_added = 0;
/* Array of RefclockParameters */
static ARR_Instance refclock_sources;
@@ -465,6 +480,7 @@ CNF_Finalise(void)
Free(hwclock_file);
Free(keys_file);
Free(leapsec_tz);
Free(leapsec_list);
Free(logdir);
Free(bind_ntp_iface);
Free(bind_acq_iface);
@@ -579,7 +595,7 @@ CNF_ParseLine(const char *filename, int number, char *line)
parse_int(p, &cmd_port);
} else if (!strcasecmp(command, "cmdratelimit")) {
parse_ratelimit(p, &cmd_ratelimit_enabled, &cmd_ratelimit_interval,
&cmd_ratelimit_burst, &cmd_ratelimit_leak);
&cmd_ratelimit_burst, &cmd_ratelimit_leak, NULL);
} else if (!strcasecmp(command, "combinelimit")) {
parse_double(p, &combine_limit);
} else if (!strcasecmp(command, "confdir")) {
@@ -602,6 +618,8 @@ CNF_ParseLine(const char *filename, int number, char *line)
parse_string(p, &hwclock_file);
} else if (!strcasecmp(command, "hwtimestamp")) {
parse_hwtimestamp(p);
} else if (!strcasecmp(command, "hwtstimeout")) {
parse_double(p, &hwts_timeout);
} else if (!strcasecmp(command, "include")) {
parse_include(p);
} else if (!strcasecmp(command, "initstepslew")) {
@@ -612,6 +630,8 @@ CNF_ParseLine(const char *filename, int number, char *line)
parse_leapsecmode(p);
} else if (!strcasecmp(command, "leapsectz")) {
parse_string(p, &leapsec_tz);
} else if (!strcasecmp(command, "leapseclist")) {
parse_string(p, &leapsec_list);
} else if (!strcasecmp(command, "local")) {
parse_local(p);
} else if (!strcasecmp(command, "lock_all")) {
@@ -662,7 +682,7 @@ CNF_ParseLine(const char *filename, int number, char *line)
parse_string(p, &ntp_signd_socket);
} else if (!strcasecmp(command, "ntsratelimit")) {
parse_ratelimit(p, &nts_ratelimit_enabled, &nts_ratelimit_interval,
&nts_ratelimit_burst, &nts_ratelimit_leak);
&nts_ratelimit_burst, &nts_ratelimit_leak, NULL);
} else if (!strcasecmp(command, "ntscachedir") ||
!strcasecmp(command, "ntsdumpdir")) {
parse_string(p, &nts_dump_dir);
@@ -690,13 +710,17 @@ 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, "ptpdomain")) {
parse_int(p, &ptp_domain);
} 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);
&ntp_ratelimit_burst, &ntp_ratelimit_leak, &ntp_ratelimit_kod);
} else if (!strcasecmp(command, "refclock")) {
parse_refclock(p);
} else if (!strcasecmp(command, "refresh")) {
parse_int(p, &refresh);
} else if (!strcasecmp(command, "reselectdist")) {
parse_double(p, &reselect_distance);
} else if (!strcasecmp(command, "rtcautotrim")) {
@@ -830,7 +854,7 @@ parse_sourcedir(char *line)
/* ================================================== */
static void
parse_ratelimit(char *line, int *enabled, int *interval, int *burst, int *leak)
parse_ratelimit(char *line, int *enabled, int *interval, int *burst, int *leak, int *kod)
{
int n, val;
char *opt;
@@ -851,6 +875,8 @@ parse_ratelimit(char *line, int *enabled, int *interval, int *burst, int *leak)
*burst = val;
else if (!strcasecmp(opt, "leak"))
*leak = val;
else if (!strcasecmp(opt, "kod") && kod)
*kod = val;
else
command_parse_error();
}
@@ -862,11 +888,10 @@ static void
parse_refclock(char *line)
{
int n, poll, dpoll, filter_length, pps_rate, min_samples, max_samples, sel_options;
int local, max_lock_age, pps_forced, stratum, tai;
int local, max_lock_age, pps_forced, sel_option, stratum, tai;
uint32_t ref_id, lock_ref_id;
double offset, delay, precision, max_dispersion, pulse_width;
char *p, *cmd, *name, *param;
unsigned char ref[5];
RefclockParameters *refclock;
poll = 4;
@@ -912,13 +937,11 @@ parse_refclock(char *line)
line = CPS_SplitWord(line);
if (!strcasecmp(cmd, "refid")) {
if (sscanf(line, "%4s%n", (char *)ref, &n) != 1)
if ((n = CPS_ParseRefid(line, &ref_id)) == 0)
break;
ref_id = (uint32_t)ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3];
} else if (!strcasecmp(cmd, "lock")) {
if (sscanf(line, "%4s%n", (char *)ref, &n) != 1)
if ((n = CPS_ParseRefid(line, &lock_ref_id)) == 0)
break;
lock_ref_id = (uint32_t)ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3];
} else if (!strcasecmp(cmd, "poll")) {
if (sscanf(line, "%d%n", &poll, &n) != 1) {
break;
@@ -971,18 +994,9 @@ parse_refclock(char *line)
} else if (!strcasecmp(cmd, "width")) {
if (sscanf(line, "%lf%n", &pulse_width, &n) != 1)
break;
} else if (!strcasecmp(cmd, "noselect")) {
} else if ((sel_option = CPS_GetSelectOption(cmd)) != 0) {
n = 0;
sel_options |= SRC_SELECT_NOSELECT;
} else if (!strcasecmp(cmd, "prefer")) {
n = 0;
sel_options |= SRC_SELECT_PREFER;
} else if (!strcasecmp(cmd, "trust")) {
n = 0;
sel_options |= SRC_SELECT_TRUST;
} else if (!strcasecmp(cmd, "require")) {
n = 0;
sel_options |= SRC_SELECT_REQUIRE;
sel_options |= sel_option;
} else {
other_parse_error("Invalid refclock option");
return;
@@ -1060,7 +1074,7 @@ parse_log(char *line)
static void
parse_local(char *line)
{
if (!CPS_ParseLocal(line, &local_stratum, &local_orphan, &local_distance))
if (!CPS_ParseLocal(line, &local_stratum, &local_orphan, &local_distance, &local_activate))
command_parse_error();
enable_local = 1;
}
@@ -1437,8 +1451,8 @@ static void
parse_hwtimestamp(char *line)
{
CNF_HwTsInterface *iface;
int n, maxpoll_set = 0;
char *p, filter[5];
int n;
if (!*line) {
command_parse_error();
@@ -1468,6 +1482,10 @@ parse_hwtimestamp(char *line)
} else if (!strcasecmp(p, "minpoll")) {
if (sscanf(line, "%d%n", &iface->minpoll, &n) != 1)
break;
} else if (!strcasecmp(p, "maxpoll")) {
if (sscanf(line, "%d%n", &iface->maxpoll, &n) != 1)
break;
maxpoll_set = 1;
} else if (!strcasecmp(p, "minsamples")) {
if (sscanf(line, "%d%n", &iface->min_samples, &n) != 1)
break;
@@ -1503,6 +1521,9 @@ parse_hwtimestamp(char *line)
if (*p)
command_parse_error();
if (!maxpoll_set)
iface->maxpoll = iface->minpoll + 1;
}
/* ================================================== */
@@ -1655,11 +1676,13 @@ compare_sources(const void *a, const void *b)
return 1;
if ((d = strcmp(sa->params.name, sb->params.name)) != 0)
return d;
if ((d = (int)(sa->type) - (int)(sb->type)) != 0)
if ((d = (int)sa->type - (int)sb->type) != 0)
return d;
if ((d = sa->pool - sb->pool) != 0)
if ((d = (int)sa->pool - (int)sb->pool) != 0)
return d;
if ((d = sa->params.port - sb->params.port) != 0)
if ((d = (int)sa->params.family - (int)sb->params.family) != 0)
return d;
if ((d = (int)sa->params.port - (int)sb->params.port) != 0)
return d;
return memcmp(&sa->params.params, &sb->params.params, sizeof (sa->params.params));
}
@@ -1674,10 +1697,14 @@ reload_source_dirs(void)
uint32_t *prev_ids, *new_ids;
char buf[MAX_LINE_LENGTH];
NSR_Status s;
int d;
int d, pass;
/* Ignore reload command before adding configured sources */
if (!conf_ntp_sources_added)
return;
prev_size = ARR_GetSize(ntp_source_ids);
if (prev_size > 0 && ARR_GetSize(ntp_sources) != prev_size)
if (ARR_GetSize(ntp_sources) != prev_size)
assert(0);
/* Save the current sources and their configuration IDs */
@@ -1704,41 +1731,48 @@ reload_source_dirs(void)
new_ids = ARR_GetElements(ntp_source_ids);
unresolved = 0;
LOG_SetContext(LOGC_SourceFile);
qsort(new_sources, new_size, sizeof (new_sources[0]), compare_sources);
for (i = j = 0; i < prev_size || j < new_size; ) {
if (i < prev_size && j < new_size)
d = compare_sources(&prev_sources[i], &new_sources[j]);
else
d = i < prev_size ? -1 : 1;
for (pass = 0; pass < 2; pass++) {
for (i = j = 0; i < prev_size || j < new_size; i += d <= 0, j += d >= 0) {
if (i < prev_size && j < new_size)
d = compare_sources(&prev_sources[i], &new_sources[j]);
else
d = i < prev_size ? -1 : 1;
if (d < 0) {
/* Remove the missing source */
if (prev_sources[i].params.name[0] != '\0')
/* Remove missing sources before adding others to avoid conflicts */
if (pass == 0 && d < 0 && prev_sources[i].params.name[0] != '\0') {
NSR_RemoveSourcesById(prev_ids[i]);
i++;
} else if (d > 0) {
/* Add a newly configured source */
source = &new_sources[j];
s = NSR_AddSourceByName(source->params.name, source->params.port, source->pool,
source->type, &source->params.params, &new_ids[j]);
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';
}
j++;
} else {
/* Keep the existing source */
new_ids[j] = prev_ids[i];
i++, j++;
/* Add new sources */
if (pass == 1 && d > 0) {
source = &new_sources[j];
s = NSR_AddSourceByName(source->params.name, source->params.family, source->params.port,
source->pool, source->type, &source->params.params,
&new_ids[j]);
if (s == NSR_UnresolvedName) {
unresolved++;
} else if (s != NSR_Success) {
LOG(LOGS_ERR, "Could not add source %s : %s",
source->params.name, NSR_StatusToString(s));
/* Mark the source as not present */
source->params.name[0] = '\0';
}
}
/* Keep unchanged sources */
if (pass == 1 && d == 0)
new_ids[j] = prev_ids[i];
}
}
LOG_UnsetContext(LOGC_SourceFile);
for (i = 0; i < prev_size; i++)
Free(prev_sources[i].params.name);
Free(prev_sources);
@@ -1782,6 +1816,19 @@ CNF_CreateDirs(uid_t uid, gid_t gid)
/* ================================================== */
void
CNF_CheckReadOnlyAccess(void)
{
unsigned int i;
if (keys_file)
UTI_CheckReadOnlyAccess(keys_file);
for (i = 0; i < ARR_GetSize(nts_server_key_files); i++)
UTI_CheckReadOnlyAccess(*(char **)ARR_GetElement(nts_server_key_files, i));
}
/* ================================================== */
void
CNF_AddInitSources(void)
{
@@ -1818,15 +1865,18 @@ CNF_AddSources(void)
for (i = 0; i < ARR_GetSize(ntp_sources); i++) {
source = (NTP_Source *)ARR_GetElement(ntp_sources, i);
s = NSR_AddSourceByName(source->params.name, source->params.port, source->pool,
source->type, &source->params.params, NULL);
s = NSR_AddSourceByName(source->params.name, source->params.family, 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);
}
/* The arrays will be used for sourcedir (re)loading */
ARR_SetSize(ntp_sources, 0);
ARR_SetSize(ntp_source_ids, 0);
conf_ntp_sources_added = 1;
reload_source_dirs();
}
@@ -2124,12 +2174,13 @@ CNF_GetCommandPort(void) {
/* ================================================== */
int
CNF_AllowLocalReference(int *stratum, int *orphan, double *distance)
CNF_AllowLocalReference(int *stratum, int *orphan, double *distance, double *activate)
{
if (enable_local) {
*stratum = local_stratum;
*orphan = local_orphan;
*distance = local_distance;
*activate = local_activate;
return 1;
} else {
return 0;
@@ -2362,6 +2413,14 @@ CNF_GetLeapSecTimezone(void)
/* ================================================== */
char *
CNF_GetLeapSecList(void)
{
return leapsec_list;
}
/* ================================================== */
int
CNF_GetSchedPriority(void)
{
@@ -2378,11 +2437,12 @@ CNF_GetLockMemory(void)
/* ================================================== */
int CNF_GetNTPRateLimit(int *interval, int *burst, int *leak)
int CNF_GetNTPRateLimit(int *interval, int *burst, int *leak, int *kod)
{
*interval = ntp_ratelimit_interval;
*burst = ntp_ratelimit_burst;
*leak = ntp_ratelimit_leak;
*kod = ntp_ratelimit_kod;
return ntp_ratelimit_enabled;
}
@@ -2500,6 +2560,14 @@ CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface)
/* ================================================== */
double
CNF_GetHwTsTimeout(void)
{
return hwts_timeout;
}
/* ================================================== */
int
CNF_GetPtpPort(void)
{
@@ -2508,6 +2576,22 @@ CNF_GetPtpPort(void)
/* ================================================== */
int
CNF_GetPtpDomain(void)
{
return ptp_domain;
}
/* ================================================== */
int
CNF_GetRefresh(void)
{
return refresh;
}
/* ================================================== */
char *
CNF_GetNtsDumpDir(void)
{

12
conf.h
View File

@@ -44,6 +44,8 @@ extern void CNF_ParseLine(const char *filename, int number, char *line);
extern void CNF_CreateDirs(uid_t uid, gid_t gid);
extern void CNF_CheckReadOnlyAccess(void);
extern void CNF_AddInitSources(void);
extern void CNF_AddSources(void);
extern void CNF_AddBroadcasts(void);
@@ -89,6 +91,7 @@ extern char *CNF_GetNtpSigndSocket(void);
extern char *CNF_GetPidFile(void);
extern REF_LeapMode CNF_GetLeapSecMode(void);
extern char *CNF_GetLeapSecTimezone(void);
extern char *CNF_GetLeapSecList(void);
/* Value returned in ppm, as read from file */
extern double CNF_GetMaxUpdateSkew(void);
@@ -105,14 +108,14 @@ extern double CNF_GetReselectDistance(void);
extern double CNF_GetStratumWeight(void);
extern double CNF_GetCombineLimit(void);
extern int CNF_AllowLocalReference(int *stratum, int *orphan, double *distance);
extern int CNF_AllowLocalReference(int *stratum, int *orphan, double *distance, double *activate);
extern void CNF_SetupAccessRestrictions(void);
extern int CNF_GetSchedPriority(void);
extern int CNF_GetLockMemory(void);
extern int CNF_GetNTPRateLimit(int *interval, int *burst, int *leak);
extern int CNF_GetNTPRateLimit(int *interval, int *burst, int *leak, int *kod);
extern int CNF_GetNtsRateLimit(int *interval, int *burst, int *leak);
extern int CNF_GetCommandRateLimit(int *interval, int *burst, int *leak);
extern void CNF_GetSmooth(double *max_freq, double *max_wander, int *leap_only);
@@ -142,6 +145,7 @@ typedef enum {
typedef struct {
char *name;
int minpoll;
int maxpoll;
int min_samples;
int max_samples;
int nocrossts;
@@ -152,8 +156,12 @@ typedef struct {
} CNF_HwTsInterface;
extern int CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface);
extern double CNF_GetHwTsTimeout(void);
extern int CNF_GetPtpPort(void);
extern int CNF_GetPtpDomain(void);
extern int CNF_GetRefresh(void);
extern char *CNF_GetNtsDumpDir(void);
extern char *CNF_GetNtsNtpServer(void);

79
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-2021
# Copyright (C) Miroslav Lichvar 2009, 2012-2022
# Copyright (C) Stefan R. Filipek 2019
#
# =======================================================================
@@ -111,10 +111,10 @@ For better control, use the options below.
--without-editline Don't use editline even if it is available
--disable-sechash Disable support for hashes other than MD5
--without-nettle Don't use nettle even if it is available
--without-gnutls Don't use gnutls even if it is available
--without-nss Don't use NSS even if it is available
--without-tomcrypt Don't use libtomcrypt even if it is available
--disable-nts Disable NTS support
--without-gnutls Don't use gnutls even if it is available
--disable-cmdmon Disable command and monitoring support
--disable-ntp Disable NTP support
--disable-refclock Disable reference clock support
@@ -128,6 +128,7 @@ For better control, use the options below.
--without-seccomp Don't use seccomp even if it is available
--disable-asyncdns Disable asynchronous name resolving
--disable-forcednsretry Don't retry on permanent DNS error
--without-aes-gcm-siv Don't use AES-GCM-SIV for NTS even if it is available
--without-clock-gettime Don't use clock_gettime() even if it is available
--disable-timestamping Disable support for SW/HW timestamping
--enable-ntp-signd Enable support for MS-SNTP authentication in Samba
@@ -244,6 +245,7 @@ try_setsched=0
try_lockmem=0
feat_asyncdns=1
feat_forcednsretry=1
try_aes_gcm_siv=1
try_clock_gettime=1
try_arc4random=1
try_recvmmsg=1
@@ -345,6 +347,9 @@ do
--disable-forcednsretry)
feat_forcednsretry=0
;;
--without-aes-gcm-siv)
try_aes_gcm_siv=0
;;
--without-clock-gettime)
try_clock_gettime=0
;;
@@ -565,6 +570,13 @@ if [ "x$MYCFLAGS" = "x" ]; then
fi
fi
TESTCFLAGS="-fwrapv"
if test_code '-fwrapv' '' "$TESTCFLAGS" '' ''; then
GETDATE_CFLAGS="-fwrapv"
else
GETDATE_CFLAGS=""
fi
if [ "x$MYCC" = "xgcc" ] || [ "x$MYCC" = "xclang" ]; then
MYCFLAGS="$MYCFLAGS -Wmissing-prototypes -Wall"
fi
@@ -914,6 +926,28 @@ if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_nettle = "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
if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_nss = "1" ]; then
test_cflags="`pkg_config --cflags nss`"
test_link="`pkg_config --libs-only-L nss` -lfreebl3 -lnssutil3"
@@ -939,28 +973,6 @@ 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"
@@ -979,20 +991,34 @@ if [ $feat_ntp = "1" ] && [ $feat_nts = "1" ] && [ $try_gnutls = "1" ]; then
gnutls_priority_init2((void *)1, "", NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND) +
gnutls_prf_rfc5705((void *)1, 0, "", 0, "", 16, (void *)2);'
then
if test_code 'SIV in nettle' \
if [ $try_nettle = "1" ] && test_code 'AES-SIV-CMAC in nettle' \
'nettle/siv-cmac.h' "" "$LIBS" \
'siv_cmac_aes128_set_key((void *)1, (void *)2);'
then
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o"
add_def HAVE_SIV
add_def HAVE_NETTLE_SIV_CMAC
if [ $try_aes_gcm_siv = "1" ] && test_code 'AES-GCM-SIV in nettle' \
'nettle/siv-gcm.h' "" "$LIBS" \
'siv_gcm_aes128_encrypt_message((void *)1, 0, NULL, 0, (void *)2, 16, (void *)3,
(void *)4);'
then
add_def HAVE_NETTLE_SIV_GCM
fi
else
if test_code 'SIV in gnutls' 'gnutls/crypto.h' \
if test_code 'AES-SIV-CMAC in gnutls' 'gnutls/crypto.h' \
"$test_cflags" "$test_link $LIBS" '
return gnutls_aead_cipher_init((void *)1, GNUTLS_CIPHER_AES_128_SIV, (void *)2);'
then
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_gnutls.o"
add_def HAVE_SIV
if [ $try_aes_gcm_siv = "1" ] && test_code 'AES-GCM-SIV in gnutls' \
'gnutls/crypto.h' "$test_cflags" "$test_link $LIBS" '
return gnutls_aead_cipher_init((void *)1, GNUTLS_CIPHER_AES_128_SIV_GCM,
(void *)2);'
then
add_def HAVE_GNUTLS_SIV_GCM
fi
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);'
@@ -1110,6 +1136,7 @@ do
s%@CFLAGS@%${MYCFLAGS}%;\
s%@CPPFLAGS@%${MYCPPFLAGS}%;\
s%@LDFLAGS@%${MYLDFLAGS}%;\
s%@GETDATE_CFLAGS@%${GETDATE_CFLAGS}%;\
s%@LIBS@%${LIBS}%;\
s%@EXTRA_LIBS@%${EXTRA_LIBS}%;\
s%@EXTRA_CLI_LIBS@%${EXTRA_CLI_LIBS}%;\

View File

@@ -60,8 +60,8 @@ Support files
Dates and sizes may differ
-rw-r--r-- 1 yourname staff 2084 4 Aug 22:54 README.txt
-rwxr-xr-x 1 yourname staff 676 4 Aug 21:18 chronylogrotate.sh
-rw-r--r-- 1 yourname staff 543 18 Jul 20:10 org.tuxfamily.chronyc.plist
-rw-r--r-- 1 yourname staff 511 19 Jun 18:30 org.tuxfamily.chronyd.plist
-rw-r--r-- 1 yourname staff 543 18 Jul 20:10 org.chrony-project.chronyc.plist
-rw-r--r-- 1 yourname staff 511 19 Jun 18:30 org.chrony-project.chronyd.plist
If you have used chrony support directories other than those suggested, you
will need to edit each file and make the appropriate changes.
@@ -83,21 +83,21 @@ sudo chmod +x /usr/local/bin/chronylogrotate.sh
sudo chown root:wheel /usr/local/bin/chronylogrotate.sh
2. org.tuxfamily.chronyc.plist
2. org.chrony-project.chronyc.plist
This file is the launchd plist that runs logrotation each day. You may
wish to edit this file to change the time of day at which the rotation
will run, currently 04:05 am
sudo cp org.tuxfamily.chronyc.plist /Library/LaunchDaemons
sudo chown root:wheel /Library/LaunchDaemons/org.tuxfamily.chronyc.plist
sudo chmod 0644 /Library/LaunchDaemons/org.tuxfamily.chronyc.plist
sudo launchctl load -w /Library/LaunchDaemons/org.tuxfamily.chronyc.plist
sudo cp org.chrony-project.chronyc.plist /Library/LaunchDaemons
sudo chown root:wheel /Library/LaunchDaemons/org.chrony-project.chronyc.plist
sudo chmod 0644 /Library/LaunchDaemons/org.chrony-project.chronyc.plist
sudo launchctl load -w /Library/LaunchDaemons/org.chrony-project.chronyc.plist
3. org.tuxfamily.chronyd.plist
3. org.chrony-project.chronyd.plist
This file is the launchd plist that runs chronyd when the Macintosh starts.
sudo cp org.tuxfamily.chronyd.plist /Library/LaunchDaemons
sudo chown root:wheel /Library/LaunchDaemons/org.tuxfamily.chronyd.plist
sudo chmod 0644 /Library/LaunchDaemons/org.tuxfamily.chronyd.plist
sudo launchctl load -w /Library/LaunchDaemons/org.tuxfamily.chronyd.plist
sudo cp org.chrony-project.chronyd.plist /Library/LaunchDaemons
sudo chown root:wheel /Library/LaunchDaemons/org.chrony-project.chronyd.plist
sudo chmod 0644 /Library/LaunchDaemons/org.chrony-project.chronyd.plist
sudo launchctl load -w /Library/LaunchDaemons/org.chrony-project.chronyd.plist

View File

@@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>Label</key>
<string>org.tuxfamily.logrotate</string>
<string>org.chrony-project.logrotate</string>
<key>KeepAlive</key>
<false/>
<key>ProgramArguments</key>

View File

@@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>Label</key>
<string>org.tuxfamily.chronyd</string>
<string>org.chrony-project.chronyd</string>
<key>Program</key>
<string>/usr/local/sbin/chronyd</string>
<key>ProgramArguments</key>

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-2022
// Copyright (C) Miroslav Lichvar 2009-2023
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of version 2 of the GNU General Public License as
@@ -72,9 +72,7 @@ newly resolved address when the server becomes unreachable (i.e. no valid
response to last 8 requests), unsynchronised, a falseticker (i.e. does not
agree with a majority of other sources), or the root distance is too large (the
limit can be configured by the <<maxdistance,*maxdistance*>> directive). The
automatic replacement happens at most once per 30 minutes. It can also be
triggered manually for all sources by the <<chronyc.adoc#refresh,*refresh*>>
command in *chronyc*.
automatic replacement happens at most once per 30 minutes.
+
This directive can be used multiple times to specify multiple servers.
+
@@ -222,7 +220,7 @@ when disconnecting the network link. (It will still be necessary to use the
<<chronyc.adoc#online,*online*>> command when the link has been established, to
enable measurements to start.)
*prefer*:::
Prefer this source over sources without the *prefer* option.
Prefer this source over other selectable sources without the *prefer* option.
*noselect*:::
Never select this source. This is particularly useful for monitoring.
*trust*:::
@@ -324,16 +322,33 @@ 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:
This option can be used multiple times to enable multiple extension fields.
+
The following extension fields are supported:
+
_F323_::::
This is an experimental extension field for some improvements that were
An experimental extension field to enable several 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*.
timestamp, which enables a frequency transfer between the server and client to
significantly improve stability of the synchronisation. This field should be
enabled only for servers known to be running *chronyd* version 4.2 or later.
_F324_::::
An experimental extension field to enable the use of the Precision Time
Protocol (PTP) correction field in NTP-over-PTP messages updated by one-step
end-to-end transparent clocks in network switches and routers to significantly
improve accuracy and stability of the synchronisation. NTP-over-PTP can be
enabled by the <<ptpport,*ptpport*>> directive and setting the *port* option to
the PTP port. The corrections are applied only to NTP measurements with HW
timestamps (enabled by the <<hwtimestamp,*hwtimestamp*>> directive). This
field should be enabled only for servers known to be running *chronyd* version
4.5 or later.
*ipv4*:::
*ipv6*:::
These options force *chronyd* to use only IPv4 or IPv6 addresses respectively
for this source. They do not override the *-4* or *-6* option on the *chronyd*
command line.
{blank}:::
[[pool]]*pool* _name_ [_option_]...::
@@ -420,7 +435,7 @@ error. *chronyd* then enters its normal operating mode.
An example of the use of the directive is:
+
----
initstepslew 30 foo.example.net bar.example.net baz.example.net
initstepslew 30 ntp1.example.net ntp2.example.net ntp3.example.net
----
+
where 3 NTP servers are used to make the measurement. The _30_ indicates that
@@ -473,16 +488,41 @@ instead.
Examples:
+
----
refclock PPS /dev/pps0 lock NMEA refid GPS
refclock SHM 0 offset 0.5 delay 0.2 refid NMEA noselect
refclock PPS /dev/pps0 lock NMEA refid GPS1
refclock SOCK /var/run/chrony.clk.ttyS0.sock offset 0.5 delay 0.2 refid NMEA noselect
refclock PPS /dev/pps1:clear refid GPS2
----
+
*SOCK*:::
Unix domain socket driver. This driver uses a datagram socket to receive
samples from another application running on the system. The parameter is the
path to the socket, which *chronyd* will create on start. The format of the
messages is described in the _refclock_sock.c_ file in the chrony source code.
+
An application which supports the SOCK protocol is the *gpsd* daemon. It can
provide accurate measurements using the receiver's PPS signal, and since
version 3.25 also (much less accurate) measurements based on the timing of
serial data (e.g. NMEA), which can be useful when the receiver does not provide
a PPS signal, or it cannot be connected to the computer. The paths where *gpsd*
expects the sockets to be created by *chronyd* are described in the *gpsd(8)*
man page. Note that *gpsd* needs to be started after *chronyd* in order to
connect to the socket.
+
Examples:
+
----
refclock SOCK /var/run/chrony.ttyS0.sock refid GPS1 poll 2 filter 4
refclock SOCK /var/run/chrony.clk.ttyUSB0.sock refid GPS2 offset 0.2 delay 0.1
----
+
*SHM*:::
NTP shared memory driver. This driver uses a shared memory segment to receive
samples from another process (e.g. *gpsd*). The parameter is the number of the
shared memory segment, typically a small number like 0, 1, 2, or 3. The driver
supports the following option:
NTP shared memory driver. This driver implements the protocol of the *ntpd*
driver type 28. It is functionally similar to the SOCK driver, but uses a
shared memory segment instead of a socket. The parameter is the unit number,
typically a small number like 0, 1, 2, or 3, from which is derived the key of
the memory segment as 0x4e545030 + unit.
+
The driver supports the following option:
+
*perm*=_mode_::::
This option specifies the permissions of the shared memory segment created by
@@ -490,6 +530,16 @@ This option specifies the permissions of the shared memory segment created by
(read-write access for owner only).
{blank}:::
+
Unlike with the SOCK driver, there is no prescribed order of starting *chronyd*
and the program providing measurements. Both are expected to create the memory
segment if it does not exist. *chronyd* will attach to an existing segment even
if it has a different owner than root or different permissions than the
permissions specified by the *perm* option. The segment needs to be created
before untrusted applications or users can execute code to prevent an attacker
from feeding *chronyd* with false measurements. The owner and permissions of
the segment can be verified with the *ipcs -m* command. For this reason, the
SHM driver is deprecated in favor of SOCK.
+
Examples:
+
----
@@ -497,23 +547,6 @@ refclock SHM 0 poll 3 refid GPS1
refclock SHM 1:perm=0644 refid GPS2
----
+
*SOCK*:::
Unix domain socket driver. It is similar to the SHM driver, but samples are
received from a Unix domain socket instead of shared memory and the messages
have a different format. The parameter is the path to the socket, which
*chronyd* creates on start. An advantage over the SHM driver is that SOCK does
not require polling and it can receive PPS samples with incomplete time. The
format of the messages is described in the _refclock_sock.c_ file in the chrony
source code.
+
An application which supports the SOCK protocol is the *gpsd* daemon. The path
where *gpsd* expects the socket to be created is described in the *gpsd(8)* man
page. For example:
+
----
refclock SOCK /var/run/chrony.ttyS0.sock
----
+
*PHC*:::
PTP hardware clock (PHC) driver. The parameter is the path to the device of
the PTP clock which should be used as a time source. If the clock is kept in
@@ -624,9 +657,11 @@ noise in the measurements. With each poll about 40 percent of the stored
samples are discarded and one final sample is calculated as an average of the
remaining samples. If the length is 4 or more, at least 4 samples have to be
collected between polls. For lengths below 4, the filter has to be full. The
default is 64.
default is 64. With drivers that perform their own polling (PPS, PHC, SHM), the
maximum value is adjusted to the number of driver polls per source poll, i.e.
2^(_poll_ - _dpoll_).
*prefer*:::
Prefer this source over sources without the prefer option.
Prefer this source over other selectable sources without the *prefer* option.
*noselect*:::
Never select this source. This is useful for monitoring or with sources which
are not very accurate, but are locked with a PPS refclock.
@@ -645,9 +680,10 @@ trusted and required source.
*tai*:::
This option indicates that the reference clock keeps time in TAI instead of UTC
and that *chronyd* should correct its offset by the current TAI-UTC offset. The
<<leapsectz,*leapsectz*>> directive must be used with this option and the
database must be kept up to date in order for this correction to work as
expected. This option does not make sense with PPS refclocks.
<<leapsectz,*leapsectz*>> or <<leapseclist,*leapseclist*>> 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
@@ -809,11 +845,16 @@ This directory is used also by the <<ntsdumpdir2,NTS server>> to save keys.
This directive specifies the maximum interval between NTS-KE handshakes (in
seconds) in order to refresh the keys authenticating NTP packets. The default
value is 2419200 (4 weeks) and the maximum value is 2^31-1 (68 years).
+
The interval must be longer than polling intervals of all configured NTP
sources using NTS, otherwise the source with a longer polling interval will
refresh the keys on each poll and no NTP packets will be exchanged.
[[ntstrustedcerts]]*ntstrustedcerts* [_set-ID_] _file_|_directory_::
This directive specifies a file or directory containing certificates (in the
PEM format) of trusted certificate authorities (CA) which can be used to
verify certificates of NTS servers.
This directive specifies a file or directory containing trusted certificates
(in the PEM format) which are needed to verify certificates of NTS-KE servers,
e.g. certificates of trusted certificate authorities (CA) or self-signed
certificates of the servers.
+
The optional _set-ID_ argument is a number in the range 0 through 2^32-1, which
selects the set of certificates where certificates from the specified file
@@ -833,10 +874,10 @@ they change (e.g. after a renewal).
An example is:
+
----
ntstrustedcerts /etc/pki/nts/foo.crt
ntstrustedcerts 1 /etc/pki/nts/bar.crt
ntstrustedcerts 1 /etc/pki/nts/baz.crt
ntstrustedcerts 2 /etc/pki/nts/qux.crt
ntstrustedcerts /etc/pki/nts/ca1.example.net.crt
ntstrustedcerts 1 /etc/pki/nts/ca2.example.net.crt
ntstrustedcerts 1 /etc/pki/nts/ca3.example.net.crt
ntstrustedcerts 2 /etc/pki/nts/ntp2.example.net.crt
----
[[nosystemcert]]*nosystemcert*::
@@ -862,6 +903,19 @@ This would disable the time checks until the clock is updated for the first
time, assuming the first update corrects the clock and later checks can work
with correct time.
[[refresh]]*refresh* _interval_::
This directive specifies the interval (in seconds) between refreshing IP
addresses of NTP sources specified by hostname. If the hostname no longer
resolves to the currently used address, it will be replaced with one of the new
addresses to avoid using a server which is no longer intended for service, even
if it is still responding correctly and would not be replaced as unreachable.
Only one source is refreshed at a time. The default value is 1209600 (2 weeks)
and the maximum value is 2^31-1 (68 years). A value of 0 disables the periodic
refreshment.
+
The <<chronyc.adoc#refresh,*refresh*>> command can be used to refresh all
sources immediately.
=== Source selection
[[authselectmode]]*authselectmode* _mode_::
@@ -920,20 +974,20 @@ before 4.0.
As an example, the following configuration using the default *mix* mode:
+
----
server foo.example.net nts
server bar.example.net nts
server baz.example.net
refclock SHM 0
server ntp1.example.net nts
server ntp2.example.net nts
server ntp3.example.net
refclock SOCK /var/run/chrony.ttyS0.sock
----
+
is equivalent to the following configuration using the *ignore* mode:
+
----
authselectmode ignore
server foo.example.net nts require trust
server bar.example.net nts require trust
server baz.example.net
refclock SHM 0 require trust
server ntp1.example.net nts require trust
server ntp2.example.net nts require trust
server ntp3.example.net
refclock /var/run/chrony.ttyS0.sock require trust
----
[[combinelimit]]*combinelimit* _limit_::
@@ -1216,6 +1270,19 @@ $ TZ=right/UTC date -d 'Dec 31 2008 23:59:60'
Wed Dec 31 23:59:60 UTC 2008
----
[[leapseclist]]*leapseclist* _file_::
This directive specifies the path to a file containing a list of leap seconds
and TAI-UTC offsets in NIST/IERS format. It is recommended to use
the file _leap-seconds.list_ usually included with the system timezone
database. The behaviour of this directive is otherwise equivalent to
<<leapsectz,*leapsectz*>>.
+
An example of this directive is:
+
----
leapseclist /usr/share/zoneinfo/leap-seconds.list
----
[[makestep]]*makestep* _threshold_ _limit_::
Normally *chronyd* will cause the system to gradually correct any time offset,
by slowing down or speeding up the clock as required. In certain situations,
@@ -1302,10 +1369,9 @@ It should be noted that this is not the only means of protection against using
unreliable estimates. At all times, *chronyd* keeps track of both the estimated
gain or loss rate, and the error bound on the estimate. When a new estimate is
generated following another measurement from one of the sources, a weighted
combination algorithm is used to update the master estimate. So if *chronyd*
has an existing highly-reliable master estimate and a new estimate is generated
which has large error bounds, the existing master estimate will dominate in the
new master estimate.
combination algorithm is used to update the existing estimate. If it has
significantly smaller error bounds than the new estimate, the existing estimate
will dominate in the new combined value.
[[maxslewrate]]*maxslewrate* _rate-in-ppm_::
The *maxslewrate* directive sets the maximum rate at which *chronyd* is allowed
@@ -1609,6 +1675,14 @@ The current root distance can be calculated from root delay and root dispersion
----
distance = delay / 2 + dispersion
----
*activate* _distance_:::
This option sets an activating root distance for the local reference. The
local reference will not be used until the root distance drops below the
configured value for the first time. This can be used to prevent the local
reference from being activated on a server which has never been synchronised
with an upstream server. The default value of 0.0 causes no activating
distance to be used, such that the local reference is always eligible for
activation.
*orphan*:::
This option enables a special '`orphan`' mode, where sources with stratum equal
to the local _stratum_ are assumed to not serve real time. They are ignored
@@ -1631,7 +1705,7 @@ The *orphan* mode is compatible with the *ntpd*'s orphan mode (enabled by the
An example of the directive is:
+
----
local stratum 10 orphan distance 0.1
local stratum 10 orphan distance 0.1 activate 0.5
----
[[ntpsigndsocket]]*ntpsigndsocket* _directory_::
@@ -1735,7 +1809,10 @@ save the keys to the _ntskeys_ file and will reload the keys from the file when
the <<chronyc.adoc#rekey,*rekey*>> command is issued in *chronyc*. The file can
be periodically copied from another server running *chronyd* (which does
not have *ntsrotate* set to 0) in order to have one or more servers dedicated
to NTS-KE. The NTS-KE servers need to be configured with the
to NTS-KE. The file includes the subsequent key to which the NTS-KE server will
switch on the next rotation, i.e. the process copying and reloading the file
does not need to be timed precisely (it can be delayed by up to one rotation
interval). The NTS-KE servers need to be configured with the
<<ntsntpserver,*ntsntpserver*>> directive to point the clients to the right NTP
server.
+
@@ -1792,6 +1869,14 @@ source address from completely blocking responses to that address. The leak
rate is defined as a power of 1/2 and it is 2 by default, i.e. on average at
least every fourth request has a response. The minimum value is 1 and the
maximum value is 4.
*kod* _rate_:::
This option sets the rate at which Kiss-o'-Death (KoD) RATE responses are
randomly sent when the limits specified by the *interval* and *burst* options
are exceeded. It is an additional stream of responses to the *leak* option. A
KoD RATE response is a request for the client to reduce its polling rate. Few
implementations actually support it. The rate is defined as a power of 1/2. The
default value is 0, which means disabled. The minimum value is 0 and the
maximum value is 4.
{blank}::
+
An example use of the directive is:
@@ -1807,7 +1892,7 @@ packets, by up to 75% (with default *leak* of 2).
[[ntsratelimit]]*ntsratelimit* [_option_]...::
This directive enables rate limiting of NTS-KE requests. It is similar to the
<<ratelimit,*ratelimit*>> directive, except the default interval is 6
(1 connection per 64 seconds).
(1 connection per 64 seconds) and the *kod* option is not supported.
+
An example of the use of the directive is:
+
@@ -1939,8 +2024,9 @@ all* directive.
[[cmdport]]*cmdport* _port_::
The *cmdport* directive allows the port that is used for run-time monitoring
(via the *chronyc* program) to be altered from its default (323). If set to 0,
*chronyd* will not open the port, this is useful to disable *chronyc*
access from the Internet. (It does not disable the Unix domain command socket.)
*chronyd* will not open the port, which disables remote *chronyc* access (with
a non-default *bindcmdaddress*) and local access for unprivileged users. It
does not disable the Unix domain command socket.
+
An example shows the syntax:
+
@@ -1949,13 +2035,13 @@ cmdport 257
----
+
This would make *chronyd* use UDP 257 as its command port. (*chronyc* would
need to be run with the *-p 257* switch to inter-operate correctly.)
need to be run with the *-p 257* option to inter-operate correctly.)
[[cmdratelimit]]*cmdratelimit* [_option_]...::
This directive enables response rate limiting for command packets. It is
similar to the <<ratelimit,*ratelimit*>> directive, except responses to
localhost are never limited and the default interval is -4 (16 packets per
second).
localhost are never limited, the default interval is -4 (16 packets per
second), and the *kod* option is not supported.
+
An example of the use of the directive is:
+
@@ -2093,8 +2179,8 @@ from the example line above):
. 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]
response time, validity of the measured offset, 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
@@ -2218,6 +2304,13 @@ Used for synchronisation of the local clock:
* _+_ - combined with the best source.
* _*_ - selected as the best source to update the reference data (e.g. root
delay, root dispersion).
. Current effective selection options of the source. which can be different
from the configured options due to the authentication selection mode
(configured by the <<authselectmode,*authselectmode*>> directive). [-----]
* _N_ indicates the *noselect* option.
* _P_ indicates the *prefer* option.
* _T_ indicates the *trust* option.
* _R_ indicates the *require* option.
. Reachability register printed as an octal number. The register has 8 bits and
is updated on every received or missed packet from the source. A value of 377
indicates that a valid reply was received for all from the last eight
@@ -2503,10 +2596,13 @@ physical clock created by writing to _/sys/class/ptp/ptpX/n_vclocks_. This
feature is available on Linux 5.14 and newer.
+
If the kernel supports software timestamping, it will be enabled for all
interfaces. The source of timestamps (i.e. hardware, kernel, or daemon) is
indicated in the _measurements.log_ file if enabled by the <<log,*log
measurements*>> directive, and the <<chronyc.adoc#ntpdata,*ntpdata*>> report in
*chronyc*.
interfaces automatically.
+
The source of timestamps (i.e. hardware, kernel, or daemon) is indicated on the
client side in the _measurements.log_ file (if enabled by the <<log,*log*>>
directive) and the <<chronyc.adoc#ntpdata,*ntpdata*>> report. On the server
side, the number of served timestamps from each source is provided in the
<<chronyc.adoc#serverstats,*serverstats*>> report.
+
This directive can be used multiple times to enable HW timestamping on multiple
interfaces. If the specified interface is _*_, *chronyd* will try to enable HW
@@ -2516,10 +2612,15 @@ The *hwtimestamp* directive has the following options:
+
*minpoll* _poll_:::
This option specifies the minimum interval between readings of the NIC clock.
It's defined as a power of two. It should correspond to the minimum polling
It's defined as a power of 2. It should correspond to the minimum polling
interval of all NTP sources and the minimum expected polling interval of NTP
clients. The default value is 0 (1 second) and the minimum value is -6 (1/64th
of a second).
clients. The default value is 0 (1 second), the minimum value is -6 (1/64th
of a second), and the maximum value is 20 (about 12 days).
*maxpoll* _poll_:::
This option specifies the maximum interval between readings of the NIC clock,
as a power of 2. The default value is *minpoll* + 1, i.e. 1 (2 seconds) with
the default *minpoll* of 0. The minimum and maximum values are the same as with
the *minpoll* option.
*minsamples* _samples_:::
This option specifies the minimum number of readings kept for tracking of the
NIC clock. The default value is 2.
@@ -2572,6 +2673,27 @@ hwtimestamp eth1 txcomp 300e-9 rxcomp 645e-9
hwtimestamp *
----
[[hwtstimeout]]*hwtstimeout* _timeout_::
If hardware timestamping is used with a close NTP server, or the NIC or its
driver is slow in providing the transmit timestamp of NTP requests, a response
from the server can be received before the transmit timestamp of the request.
To avoid calculating the offset with a less accurate transmit timestamp,
*chronyd* can save the response for later processing and wait for the hardware
transmit timestamp. There is no guarantee that the timestamp will be provided
(NICs typically have a limited rate of transmit timestamping). This directive
configures how long should *chronyd* wait for the timestamp after receiving a
valid response from the server. If a second valid response is received from the
server while waiting for the timestamp, they will be both processed
immediately.
+
The default value is 0.001 seconds, which should be sufficient with most
hardware. If you frequently see kernel transmit timestamps in the
_measurements.log_ file or <<chronyc.adoc#ntpdata,*ntpdata*>> report, and it is
not a server handling a high rate of requests in the interleaved mode on the
same interface (which would compete with timestamping of the server's own
requests), increasing the timeout to 0.01 or possibly even longer might help.
Note that the maximum timeout is limited by the NTP polling interval.
[[keyfile]]*keyfile* _file_::
This directive is used to specify the location of the file containing symmetric
keys which are shared between NTP servers and clients, or peers, in order to
@@ -2655,28 +2777,36 @@ pidfile /run/chronyd.pid
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).
packets, and also use corrections provided by PTP one-step end-to-end
transparent clocks in network switches and routers. The port recognized by the
NICs and PTP transparent clocks 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*.
change in future. It should be used only in local networks.
+
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_.
*rxfilter* option of the *hwtimestamp* directive needs to be set to _ptp_. The
extension field _F324_ needs to be enabled to use the corrections provided by
the PTP transparent clocks.
+
An example of client configuration is:
+
----
server foo.example.net minpoll 0 maxpoll 0 xleave port 319
server ntp1.example.net minpoll 0 maxpoll 0 xleave port 319 extfield F324
hwtimestamp * rxfilter ptp
ptpport 319
----
[[ptpdomain]]*ptpdomain* _domain_::
The *ptpdomain* directive sets the PTP domain number of transmitted and
accepted NTP-over-PTP messages. Messages from other domains are ignored.
The default is 123, the minimum is 0, and the maximum is 255.
[[sched_priority]]*sched_priority* _priority_::
On Linux, FreeBSD, NetBSD, and illumos, the *sched_priority* directive will
select the SCHED_FIFO real-time scheduler at the specified priority (which must
@@ -2733,13 +2863,13 @@ the following methods:
facilities.
* Use public servers from the https://www.pool.ntp.org/[pool.ntp.org] project.
Assuming that your NTP servers are called _foo.example.net_, _bar.example.net_
and _baz.example.net_, your _chrony.conf_ file could contain as a minimum:
Assuming that your NTP servers are called _ntp1.example.net_, _ntp2.example.net_
and _ntp3.example.net_, your _chrony.conf_ file could contain as a minimum:
----
server foo.example.net
server bar.example.net
server baz.example.net
server ntp1.example.net
server ntp2.example.net
server ntp3.example.net
----
However, you will probably want to include some of the other directives. The
@@ -2750,9 +2880,9 @@ synchronisation. The smallest useful configuration file would look something
like:
----
server foo.example.net iburst
server bar.example.net iburst
server baz.example.net iburst
server ntp1.example.net iburst
server ntp2.example.net iburst
server ntp3.example.net iburst
driftfile @CHRONYVARDIR@/drift
makestep 1.0 3
rtcsync
@@ -2776,9 +2906,9 @@ option will enable a secure synchronisation to the servers. The configuration
file could look like:
----
server foo.example.net iburst nts
server bar.example.net iburst nts
server baz.example.net iburst nts
server ntp1.example.net iburst nts
server ntp2.example.net iburst nts
server ntp3.example.net iburst nts
driftfile @CHRONYVARDIR@/drift
makestep 1.0 3
rtcsync
@@ -2792,14 +2922,14 @@ additional configuration to tell *chronyd* when the connection goes up and
down. This saves the program from continuously trying to poll the servers when
they are inaccessible.
Again, assuming that your NTP servers are called _foo.example.net_,
_bar.example.net_ and _baz.example.net_, your _chrony.conf_ file would now
Again, assuming that your NTP servers are called _ntp1.example.net_,
_ntp2.example.net_ and _ntp3.example.net_, your _chrony.conf_ file would now
contain:
----
server foo.example.net offline
server bar.example.net offline
server baz.example.net offline
server ntp1.example.net offline
server ntp2.example.net offline
server ntp3.example.net offline
driftfile @CHRONYVARDIR@/drift
makestep 1.0 3
rtcsync
@@ -2983,9 +3113,9 @@ configuration files are shown.
For the _chrony.conf_ file, the following can be used as an example.
----
server foo.example.net maxdelay 0.4 offline
server bar.example.net maxdelay 0.4 offline
server baz.example.net maxdelay 0.4 offline
server ntp1.example.net maxdelay 0.4 offline
server ntp2.example.net maxdelay 0.4 offline
server ntp3.example.net maxdelay 0.4 offline
logdir /var/log/chrony
log statistics measurements tracking
driftfile @CHRONYVARDIR@/drift
@@ -3044,10 +3174,10 @@ configuration).
The configuration file could look like:
----
server foo.example.net iburst
server bar.example.net iburst
server baz.example.net iburst
server qux.example.net iburst
server ntp1.example.net iburst
server ntp2.example.net iburst
server ntp3.example.net iburst
server ntp4.example.net iburst
makestep 1.0 3
rtcsync
allow
@@ -3064,7 +3194,7 @@ dumpdir @CHRONYRUNDIR@
== BUGS
For instructions on how to report bugs, please visit
https://chrony.tuxfamily.org/.
https://chrony-project.org/.
== AUTHORS

View File

@@ -2,7 +2,7 @@
//
// Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Stephen Wadeley 2016
// Copyright (C) Miroslav Lichvar 2009-2017, 2019-2022
// Copyright (C) Miroslav Lichvar 2009-2017, 2019-2023
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of version 2 of the GNU General Public License as
@@ -89,6 +89,10 @@ format. Reverse DNS lookups will be disabled, time will be printed as number of
seconds since the epoch, and values in seconds will not be converted to other
units.
*-e*::
With this option each *chronyc* response will end with a line containing a
single dot.
*-d*::
This option enables printing of debugging messages if *chronyc* was compiled
with debugging support.
@@ -140,7 +144,7 @@ The *tracking* command displays parameters about the system's clock
performance. An example of the output is shown below.
+
----
Reference ID : CB00710F (foo.example.net)
Reference ID : CB00710F (ntp1.example.net)
Stratum : 3
Ref time (UTC) : Fri Jan 27 09:49:17 2017
System time : 0.000006523 seconds slow of NTP time
@@ -174,7 +178,7 @@ with an IPv4 address.
*Stratum*:::
The stratum indicates how many hops away from a computer with an attached
reference clock we are. Such a computer is a stratum-1 computer, so the
computer in the example is two hops away (i.e. _foo.example.net_ is a
computer in the example is two hops away (i.e. _ntp1.example.net_ is a
stratum-2 and is synchronised from a stratum-1).
*Ref time*:::
This is the time (UTC) at which the last measurement from the reference
@@ -317,8 +321,8 @@ extra caption lines are shown as a reminder of the meanings of the columns.
MS Name/IP address Stratum Poll Reach LastRx Last sample
===============================================================================
#* GPS0 0 4 377 11 -479ns[ -621ns] +/- 134ns
^? foo.example.net 2 6 377 23 -923us[ -924us] +/- 43ms
^+ bar.example.net 1 6 377 21 -2629us[-2619us] +/- 86ms
^? ntp1.example.net 2 6 377 23 -923us[ -924us] +/- 43ms
^+ ntp2.example.net 1 6 377 21 -2629us[-2619us] +/- 86ms
----
+
The columns are as follows:
@@ -375,9 +379,9 @@ offset. This can be suffixed by _ns_ (indicating nanoseconds), _us_
(indicating microseconds), _ms_ (indicating milliseconds), or _s_ (indicating
seconds). The number to the left of the square brackets shows the original
measurement, adjusted to allow for any slews applied to the local clock
since. The number following the _+/-_ indicator shows the margin of error in
the measurement. Positive offsets indicate that the local clock is ahead of
the source.
since. Positive offsets indicate that the local clock is ahead of the source.
The number following the _+/-_ indicator shows the margin of error in the
measurement (NTP root distance).
[[sourcestats]]*sourcestats* [*-a*] [*-v*]::
The *sourcestats* command displays information about the drift rate and offset
@@ -396,7 +400,7 @@ An example report is:
----
Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev
===============================================================================
foo.example.net 11 5 46m -0.001 0.045 1us 25us
ntp1.example.net 11 5 46m -0.001 0.045 1us 25us
----
+
The columns are as follows:
@@ -440,9 +444,9 @@ An example of the output is shown below.
----
S Name/IP Address Auth COpts EOpts Last Score Interval Leap
=======================================================================
D foo.example.net Y ----- --TR- 4 1.0 -61ms +62ms N
* bar.example.net N ----- ----- 0 1.0 -6846us +7305us N
+ baz.example.net N ----- ----- 10 1.0 -7381us +7355us N
D ntp1.example.net Y ----- --TR- 4 1.0 -61ms +62ms N
* ntp2.example.net N ----- ----- 0 1.0 -6846us +7305us N
+ ntp3.example.net N ----- ----- 10 1.0 -7381us +7355us N
----
+
The columns are as follows:
@@ -455,8 +459,8 @@ 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.
* _s_ - is not synchronised.
* _d_ - has a root distance larger than the maximum distance (configured by the
<<chrony.conf.adoc#maxdistance,*maxdistance*>> directive).
* _~_ - has a jitter larger than the maximum jitter (configured by the
@@ -488,7 +492,7 @@ local clock:
This column shows the name or IP address of the source if it is an NTP server,
or the reference ID if it is a reference clock.
*Auth*:::
This column indicites whether an authentication mechanism is enabled for the
This column indicates whether an authentication mechanism is enabled for the
source. _Y_ means yes and _N_ means no.
*COpts*:::
This column displays the configured selection options of the source.
@@ -500,8 +504,8 @@ This column displays the configured selection options of the source.
This column displays the current effective selection options of the source,
which can be different from the configured options due to the authentication
selection mode (configured by the
<<chrony.conf.adoc#authselmode,*authselmode*>> directive). The symbols are the
same as in the *COpts* column.
<<chrony.conf.adoc#authselectmode,*authselectmode*>> directive). The symbols
are the same as in the *COpts* column.
*Last*:::
This column displays how long ago was the last measurement of the source made
when the selection was performed.
@@ -522,6 +526,23 @@ This column displays the current leap status of the source.
* _-_ indicates that a leap second will be deleted at the end of the month.
* _?_ indicates the unknown status (i.e. no valid measurement was made).
[[selectopts]]*selectopts* _address|refid_ [_+|-option_]...::
The *selectopts* command modifies the configured selection options of an NTP
source specified by IP address (or the _ID#XXXXXXXXXX_ identifier used for
unknown addresses), or a reference clock specified by reference ID as a string.
+
The selection options can be added with the *+* symbol or removed with the *-*
symbol. The *selectdata* command can be used to verify the configuration. The
modified options will be applied in the next source selection, e.g. when a new
measurement is made, or the *reselect* command is executed.
+
An example of using this command is shown below.
+
----
selectopts 1.2.3.4 -noselect +prefer
selectopts GPS +trust
----
[[reselect]]*reselect*::
To avoid excessive switching between sources, *chronyd* can stay synchronised
to a source even when it is not currently the best one among the available
@@ -535,6 +556,13 @@ The *reselectdist* command sets the reselection distance. It is equivalent to
the <<chrony.conf.adoc#reselectdist,*reselectdist*>> directive in the
configuration file.
[[offset]]*offset* _address|refid_ _offset_::
The *offset* command modifies the offset correction of an NTP source specified
by IP address (or the _ID#XXXXXXXXXX_ identifier used for unknown addresses),
or a reference clock specified by reference ID as a string. It is equivalent to
the *offset* option in the <<chrony.conf.adoc#server,*server*>> or
<<chrony.conf.adoc#refclock,*refclock*>> directive respectively.
=== NTP sources
[[activity]]*activity*::
@@ -571,9 +599,9 @@ shown below.
----
Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen
=========================================================================
foo.example.net NTS 1 15 256 135m 0 0 8 100
bar.example.net SK 30 13 128 - 0 0 0 0
baz.example.net - 0 0 0 - 0 0 0 0
ntp1.example.net NTS 1 15 256 135m 0 0 8 100
ntp2.example.net SK 30 13 128 - 0 0 0 0
ntp3.example.net - 0 0 0 - 0 0 0 0
----
+
The columns are as follows:
@@ -610,6 +638,7 @@ be reported:
* 13: AES128
* 14: AES256
* 15: AEAD-AES-SIV-CMAC-256
* 30: AEAD-AES-128-GCM-SIV
*KLen*:::
This column shows the length of the key in bits.
*Last*:::
@@ -667,6 +696,10 @@ Total TX : 24
Total RX : 24
Total valid RX : 24
Total good RX : 22
Total kernel TX : 24
Total kernel RX : 24
Total HW TX : 0
Total HW RX : 0
----
+
The fields are explained as follows:
@@ -724,6 +757,18 @@ The number of packets which passed the first two groups of NTP tests.
*Total good RX*:::
The number of packets which passed all three groups of NTP tests, i.e. the NTP
measurement was accepted.
*Total kernel TX*:::
The number of packets sent to the source for which a timestamp was captured by
the kernel.
*Total kernel RX*:::
The number of packets received from the source for which a timestamp was
captured by the kernel.
*Total HW TX*:::
The number of packets sent to the source for which a timestamp was captured by
the NIC.
*Total HW RX*:::
The number of packets received from the source for which a timestamp was
captured by the NIC.
[[add_peer]]*add peer* _name_ [_option_]...::
The *add peer* command allows a new NTP peer to be added whilst
@@ -736,7 +781,7 @@ parameters and options is identical to that for the
An example of using this command is shown below.
+
----
add peer foo.example.net minpoll 6 maxpoll 10 key 25
add peer ntp1.example.net minpoll 6 maxpoll 10 key 25
----
[[add_pool]]*add pool* _name_ [_option_]...::
@@ -750,7 +795,7 @@ directive in the configuration file.
An example of using this command is shown below:
+
----
add pool foo.example.net maxsources 3 iburst
add pool ntp1.example.net maxsources 3 iburst
----
[[add_server]]*add server* _name_ [_option_]...::
@@ -764,7 +809,7 @@ directive in the configuration file.
An example of using this command is shown below:
+
----
add server foo.example.net minpoll 6 maxpoll 10 key 25
add server ntp1.example.net minpoll 6 maxpoll 10 key 25
----
[[delete]]*delete* _address_::
@@ -840,7 +885,7 @@ IPv6 addresses have first 48 bits equal to _2001:db8:789a_.
Example of the three-argument form of the command is:
+
----
burst 2/10 foo.example.net
burst 2/10 ntp1.example.net
----
[[maxdelay]]*maxdelay* _address_ _delay_::
@@ -906,7 +951,7 @@ uses an IP address or a hostname. These forms are illustrated below.
offline
offline 255.255.255.0/1.2.3.0
offline 2001:db8:789a::/48
offline foo.example.net
offline ntp1.example.net
----
+
The second form means that the *offline* command is to be applied to any source
@@ -948,17 +993,26 @@ current set of sources. It is equivalent to the *polltarget* option in the
[[refresh]]*refresh*::
The *refresh* command can be used to force *chronyd* to resolve the names of
configured sources to IP addresses again, e.g. after suspending and resuming
the machine in a different network.
configured NTP sources to IP addresses again and replace any addresses missing
in the list of resolved addresses.
+
Sources that stop responding will be replaced with newly resolved addresses
automatically after 8 polling intervals, but this command can still be useful
to replace them immediately and not wait until they are marked as unreachable.
Sources that stop responding are replaced with newly resolved addresses
automatically after 8 polling intervals. This command can be used to replace
them immediately, e.g. after suspending and resuming the machine in a different
network.
+
Note that with pools which have more than 16 addresses, or not all IPv4 or IPv6
addresses are included in a single DNS response (e.g. pool.ntp.org), this
command might replace the addresses even if they are still in the pool.
[[reload]]*reload* *sources*::
The *reload sources* command causes *chronyd* to re-read all _*.sources_ files
from the directories specified by the
<<chrony.conf.adoc#sourcedir,*sourcedir*>> directive.
+
Note that modified sources (e.g. specified with a new option) are not modified
in memory. They are removed and added again, which causes them to lose old
measurements and reset the selection state.
[[sourcename]]*sourcename* _address_::
The *sourcename* command prints the original hostname or address that was
@@ -1067,7 +1121,7 @@ particular host.
Examples of use, showing a named host and a numeric IP address, are as follows:
+
----
accheck foo.example.net
accheck ntp1.example.net
accheck 1.2.3.4
accheck 2001:db8::1
----
@@ -1094,7 +1148,7 @@ An example of the output is:
Hostname NTP Drop Int IntL Last Cmd Drop Int Last
===============================================================================
localhost 2 0 2 - 133 15 0 -1 7
foo.example.net 12 0 6 - 23 0 0 - -
ntp1.example.net 12 0 6 - 23 0 0 - -
----
+
Each row shows the data for a single host. Only hosts that have passed the host
@@ -1136,6 +1190,12 @@ Authenticated NTP packets : 189
Interleaved NTP packets : 43
NTP timestamps held : 44
NTP timestamp span : 120
NTP daemon RX timestamps : 0
NTP daemon TX timestamps : 1537
NTP kernel RX timestamps : 1590
NTP kernel TX timestamps : 43
NTP hardware RX timestamps : 0
NTP hardware TX timestamps : 0
----
+
The fields have the following meaning:
@@ -1170,10 +1230,24 @@ The number of pairs of receive and transmit timestamps that the server is
currently holding in memory for clients using the interleaved mode.
*NTP timestamp span*:::
The interval (in seconds) covered by the currently held NTP timestamps.
{blank}::
+
Note that the numbers reported by this overflow to zero after 4294967295
(32-bit values).
*NTP daemon RX timestamps*:::
The number of NTP responses which included a receive timestamp captured by the
daemon.
*NTP daemon TX timestamps*:::
The number of NTP responses which included a transmit timestamp captured by the
daemon.
*NTP kernel RX timestamps*:::
The number of NTP responses which included a receive timestamp captured by the
kernel.
*NTP kernel TX timestamps*:::
The number of NTP responses (in the interleaved mode) which included a transmit
timestamp captured by the kernel.
*NTP hardware RX timestamps*:::
The number of NTP responses which included a receive timestamp captured by the
NIC.
*NTP hardware TX timestamps*:::
The number of NTP responses (in the interleaved mode) which included a transmit
timestamp captured by the NIC.
[[allow]]*allow* [*all*] [_subnet_]::
The effect of the allow command is identical to the
@@ -1212,8 +1286,8 @@ deny all
*local* *off*::
The *local* command allows *chronyd* to be told that it is to appear as a
reference source, even if it is not itself properly synchronised to an external
source. (This can be used on isolated networks, to allow one computer to be a
master time server with the other computers slaving to it.)
source. This can be used on isolated networks, to allow a computer to be the
primary time server for other computers.
+
The first form enables the local reference mode on the host. The syntax is
identical to the <<chrony.conf.adoc#local,*local*>> directive in the
@@ -1274,7 +1348,7 @@ used to check whether monitoring access is permitted from a named host.
Examples of use are as follows:
+
----
cmdaccheck foo.example.net
cmdaccheck ntp1.example.net
cmdaccheck 1.2.3.4
cmdaccheck 2001:db8::1
----
@@ -1501,7 +1575,7 @@ The *help* command displays a summary of the commands and their arguments.
== BUGS
For instructions on how to report bugs, please visit
https://chrony.tuxfamily.org/.
https://chrony-project.org/.
== AUTHORS

View File

@@ -72,9 +72,9 @@ terminal.
*-L* _level_::
This option specifies the minimum severity level of messages to be written to
the log file, syslog, or terminal. The following levels can be specified:
0 (informational), 1 (warning), 2 (non-fatal error), and 3 (fatal error). The
default value is 0.
the log file, syslog, or terminal. The following levels can be specified: -1
(debug, if compiled with enabled support for debugging), 0 (informational), 1
(warning), 2 (non-fatal error), and 3 (fatal error). The default value is 0.
*-p*::
When run in this mode, *chronyd* will print the configuration and exit. It will
@@ -88,8 +88,10 @@ will not detach from the terminal.
*-Q*::
This option is similar to the *-q* option, except it only prints the offset
without making any corrections of the clock and it allows *chronyd* to be
started without root privileges.
without making any corrections of the clock and disables server ports to allow
*chronyd* to be started without root privileges, assuming the configuration
does not have any directives which would require them (e.g. *refclock*,
*hwtimestamp*, *rtcfile*, etc).
*-r*::
This option will try to reload and then delete files containing sample
@@ -204,6 +206,17 @@ With this option *chronyd* will print version number to the terminal and exit.
*-h*, *--help*::
With this option *chronyd* will print a help message to the terminal and exit.
== ENVIRONMENT VARIABLES
*LISTEN_FDS*::
On Linux systems, the systemd service manager may pass file descriptors for
pre-initialised sockets to *chronyd*. The service manager allocates and binds
the file descriptors, and passes a copy to each spawned instance of the
service. This allows for zero-downtime service restarts as the sockets buffer
client requests until the service is able to handle them. The service manager
sets the LISTEN_FDS environment variable to the number of passed file
descriptors.
== FILES
_@SYSCONFDIR@/chrony.conf_
@@ -215,7 +228,7 @@ _@SYSCONFDIR@/chrony.conf_
== BUGS
For instructions on how to report bugs, please visit
https://chrony.tuxfamily.org/.
https://chrony-project.org/.
== AUTHORS

74
doc/contributing.adoc Normal file
View File

@@ -0,0 +1,74 @@
// This file is part of chrony
//
// Copyright (C) Miroslav Lichvar 2024
//
// 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.
= Contributing
== Patches
The source code of `chrony` is maintained in a git repository at
https://gitlab.com/chrony/chrony. Patches can be submitted to the `chrony-dev`
mailing list, or as a merge request on gitlab. Before spending a lot of time
implementing a new major feature, it is recommended to ask on the mailing list
for comments about its design and whether such feature fits the goals of the
project.
Each commit should be a self-contained logical change, which does not break
the build or tests. New functionality and fixed bugs should be covered by a new
test or an extended existing test in the test suite. The test can be included
in the same commit or added as a separate commit. The same rule applies to
documentation. All command-line options, configuration directives, and
`chronyc` commands should be documented.
The most important tests can be executed by running `make check` or `make
quickcheck`. The unit and system tests run on all supported systems. The system
tests require root privileges. The simulation tests run only on Linux and
require https://gitlab.com/chrony/clknetsim[clknetsim] to be compiled in the
directory containing the tests, but they are executed with a merge request on
gitlab.
The commit message should explain any non-trivial changes, e.g. what problem is
the commit solving and how. The commit subject (first line of the message)
should be written in an imperative form, prefixed with the component name if it
is not a more general change, starting in lower case, and no period at the end.
See the git log for examples.
Simpler code is better. Less code is better. Security is a top priority.
Assertions should catch only bugs in the `chrony` code. Unexpected values in
external input (e.g. anything received from network) must be handled correctly
without crashing and memory corruption. Fuzzing support is available at
https://gitlab.com/chrony/chrony-fuzz. The fuzzing coverage is checked by the
project maintainer before each release.
The code should mostly be self-documenting. Comments should explain the
less obvious things.
== Coding style
The code uses two spaces for indentation. No tabs. The line length should
normally not exceed 95 characters. Too much indentation indicates the code will
not be very readable.
Function names are in an imperative form. Names of static functions use
lowercase characters and underscores. Public functions, structures, typedefs
are in CamelCase with a prefix specific to the module (e.g. LCL - local, NCR
- NTP core, NKS - NTS-KE server, SST - sourcestats).
Function names are not followed by space, but keywords of the language (e.g.
`if`, `for`, `while`, `sizeof`) are followed by space.
Have a look at the existing code to get a better idea what is expected.

View File

@@ -1,7 +1,8 @@
// This file is part of chrony
//
// Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Miroslav Lichvar 2014-2016, 2020-2022
// Copyright (C) Luke Valenta 2023
// Copyright (C) Miroslav Lichvar 2014-2016, 2020-2023
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of version 2 of the GNU General Public License as
@@ -40,9 +41,36 @@ on an isolated network with no hardware reference clocks in sight, `chrony`
will probably work better for you.
For a more detailed comparison of features and performance, see the
https://chrony.tuxfamily.org/comparison.html[comparison page] on the `chrony`
https://chrony-project.org/comparison.html[comparison page] on the `chrony`
website.
=== Should I prefer `chrony` over `timesyncd` if I do not need to run a server?
Generally, yes.
`systemd-timesyncd` is a very simple NTP client included in the `systemd`
suite. It lacks almost all features of `chrony` and other advanced client
implementations listed on the
https://chrony-project.org/comparison.html[comparison page]. One of its main
limitations is that it cannot poll multiple servers at the same time and detect
servers having incorrect time (falsetickers in the NTP terminology). It should
be used only with trusted reliable servers, ideally in local network.
Using `timesyncd` with `pool.ntp.org` is problematic. The pool is very
robust as a whole, but the individual servers run by volunteers cannot be
relied on. Occasionally, servers drift away or make a step to distant past or
future due to misconfiguration, problematic implementation, and other bugs
(e.g. in firmware of a GPS receiver). The pool monitoring system detects such
servers and quickly removes them from the pool DNS, but clients like
`timesyncd` cannot recover from that. They follow the server as long as it
claims to be synchronised. They need to be restarted in order to get a new
address from the pool DNS.
Note that the complexity of NTP and clock synchronisation is on the client
side. The amount of code in `chrony` specific to NTP server is very small and
it is disabled by default. If it was removed, it would not significantly reduce
the amount of memory or storage needed.
== Configuration issues
=== What is the minimum recommended configuration for an NTP client?
@@ -232,17 +260,17 @@ authenticated servers should be configured as trusted and required to not allow
the unauthenticated servers to override the authenticated servers in the source
selection. Since `chrony` version 4.0, the selection options are enabled in
such a case automatically. This behaviour can be disabled or modified by the
`authselmode` directive.
`authselectmode` directive.
An example of a client configuration limiting the impact of the attacks could
be
----
server foo.example.net iburst nts maxdelay 0.1
server bar.example.net iburst nts maxdelay 0.2
server baz.example.net iburst nts maxdelay 0.05
server qux.example.net iburst nts maxdelay 0.1
server quux.example.net iburst nts maxdelay 0.1
server ntp1.example.net iburst nts maxdelay 0.1
server ntp2.example.net iburst nts maxdelay 0.2
server ntp3.example.net iburst nts maxdelay 0.05
server ntp4.example.net iburst nts maxdelay 0.1
server ntp5.example.net iburst nts maxdelay 0.1
minsources 3
maxchange 100 0 0
makestep 0.001 1
@@ -291,7 +319,7 @@ An example of the directive for an NTP server on the Internet that you are
allowed to poll frequently could be
----
server foo.example.net minpoll 4 maxpoll 6 polltarget 16
server ntp.example.net minpoll 4 maxpoll 6 polltarget 16
----
An example using shorter polling intervals with a server located in the same
@@ -354,7 +382,7 @@ 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
Since 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:
@@ -363,6 +391,18 @@ the `extfield F323` option. For example:
server ntp.local minpoll 0 maxpoll 0 xleave extfield F323
----
Since version 4.5, `chronyd` can apply corrections from PTP one-step end-to-end
transparent clocks (e.g. network switches) to significantly improve accuracy of
synchronisation in local networks. It requires the PTP transport to be enabled
by the `ptpport` directive, HW timestamping, and the `extfield F324` option.
For example:
----
server ntp.local minpoll -4 maxpoll -4 xleave extfield F323 extfield F324 port 319
ptpport 319
hwtimestamp eth0 minpoll -4
----
=== Does `chronyd` have an ntpdate mode?
Yes. With the `-q` option `chronyd` will set the system clock once and exit.
@@ -470,6 +510,59 @@ pidfile /var/run/chronyd-server1.pid
driftfile /var/lib/chrony/drift-server1
----
=== How can `chronyd` be configured to minimise downtime during restarts?
The `dumpdir` directive in _chrony.conf_ provides `chronyd` a location to save
a measurement history of the sources it uses when the service exits. The `-r`
option then enables `chronyd` to load state from the dump files, reducing the
synchronisation time after a restart.
Similarly, the `ntsdumpdir` directive provides a location for `chronyd` to save
NTS cookies received from the server to avoid making a NTS-KE request when
`chronyd` is started. When operating as an NTS server, `chronyd` also saves
cookies keys to this directory to allow clients to continue to use the old keys
after a server restart for a more seamless experience.
On Linux systems,
https://www.freedesktop.org/software/systemd/man/latest/sd_listen_fds.html[systemd
socket activation] provides a mechanism to reuse server sockets across
`chronyd` restarts, so that client requests will be buffered until the service
is again able to handle the requests. This allows for zero-downtime service
restarts, simplified dependency logic at boot, and on-demand service spawning
(for instance, for separated server `chronyd` instances run with the `-x`
flag).
Socket activation is supported since `chrony` version 4.5.
The service manager (systemd) creates sockets and
passes file descriptors to them to the process via the `LISTEN_FDS` environment
variable. Before opening new sockets, `chronyd` first checks for and attempts
to reuse matching sockets passed from the service manager. For instance, if an
IPv4 datagram socket bound on `bindaddress` and `port` is available, it will be
used by the NTP server to accept incoming IPv4 requests.
An example systemd socket unit is below, where `chronyd` is configured with
`bindaddress 0.0.0.0`, `bindaddress ::`, `port 123`, and `ntsport 4460`.
----
[Unit]
Description=chronyd server sockets
[Socket]
Service=chronyd.service
# IPv4 NTP server
ListenDatagram=0.0.0.0:123
# IPv6 NTP server
ListenDatagram=[::]:123
# IPv4 NTS-KE server
ListenStream=0.0.0.0:4460
# IPv6 NTS-KE server
ListenStream=[::]:4460
BindIPv6Only=ipv6-only
[Install]
WantedBy=sockets.target
----
=== Should be a leap smear enabled on NTP server?
With the `smoothtime` and `leapsecmode` directives it is possible to enable a
@@ -484,7 +577,7 @@ 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`?
=== How should `chronyd` be configured 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
@@ -499,45 +592,53 @@ it is connected to a GPIO pin, or another serial port, the PPS device needs to
be specified on the command line as an additional data source. On Linux, the
`ldattach` utility can be used to create a PPS device for a serial device.
The message-based time source provided by `gpsd` is specified as a `SHM 0`
refclock, or other even number if `gpsd` is configured with multiple receivers.
The PPS-based time source is specified as a `SHM 1` refclock (or other odd
number), or `SOCK /var/run/chrony.DEV.sock` where `DEV` is the name of the
The PPS-based time source provided by `gpsd` is available as a `SHM 1`
refclock, or other odd number if `gpsd` is configured with multiple receivers,
and also as `SOCK /var/run/chrony.DEV.sock` where `DEV` is the name of the
serial device (e.g. ttyS0).
With `chronyd` and `gpsd` both supporting PPS, and `gpsd` providing two
different refclocks for PPS, there are three different recommended
configurations:
The message-based time source is available as a `SHM 0` refclock (or other even
number) and since `gpsd` version 3.25 also as
`SOCK /var/run/chrony.clk.DEV.sock` where `DEV` is the name of the serial
device.
The SOCK refclocks should be preferred over SHM for better security
(the shared memory segment needs to be created by `chronyd` or `gpsd` with an
expected owner and permissions before an untrusted application or user has a
chance to create its own in order to feed `chronyd` with false measurements).
`gpsd` needs to be started after `chronyd` in order to connect to the socket.
With `chronyd` and `gpsd` both supporting PPS, there are two different
recommended configurations:
----
# First option
refclock SOCK /var/run/chrony.ttyS0.sock refid GPS
# Second option
refclock SHM 1 refid GPS
# Third option
refclock PPS /dev/pps0 lock NMEA refid GPS
refclock SHM 0 offset 0.5 delay 0.1 refid NMEA noselect
refclock SOCK /var/run/chrony.clk.ttyS0.sock offset 0.5 delay 0.1 refid NMEA noselect
----
Each option has some advantages:
They both have some advantages:
* `SOCK` does not use polling (i.e. it can get samples earlier than `SHM`),
but it requires `gpsd` to be started after `chronyd` in order to connect to
its socket
* `SOCK` and `SHM 1` can be more accurate than `PPS` if `gpsd` corrects for the
* `SOCK` can be more accurate than `PPS` if `gpsd` corrects for the
sawtooth error provided by the receiver in serial data
* `PPS` can be used with higher PPS rates (specified by the `rate` option),
but it requires a second refclock or another time source to pair pulses
with seconds, and the `SHM 0` offset needs to be specified
with seconds, and the `SOCK` offset needs to be specified
<<using-pps-refclock,correctly>> to compensate for the message delay, while
`gpsd` can apply HW-specific information
If the PPS signal is not available, or cannot be used for some reason, the only
option is the message-based timing
----
refclock SOCK /var/run/chrony.clk.ttyS0.sock offset 0.5 delay 0.1 refid GPS
----
or the SHM equivalent if using `gpsd` version before 3.25
----
refclock SHM 0 offset 0.5 delay 0.1 refid GPS
----
@@ -563,7 +664,28 @@ 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.
`ptpport` directive. Since version 4.5, `chrony` can also apply corrections
provided by PTP one-step end-to-end transparent clocks to reach the accuracy of
ordinary PTP clocks. The application of PTP corrections can be enabled by the
`extfield F324` option.
=== How can I avoid using wrong PHC refclock?
If your system has multiple PHC devices, normally named by `udev` as
_/dev/ptp0_, _/dev/ptp1_, and so on, their order can change randomly across
reboots depending on the order of initialisation of their drivers. If a PHC
refclock is specified by this name, `chronyd` could be using a wrong refclock
after reboot. To prevent that, you can configure `udev` to create a stable
symlink for `chronyd` with a rule like this (e.g. written to
_/etc/udev/rules.d/80-phc.rules_):
----
KERNEL=="ptp[0-9]*", DEVPATH=="/devices/pci0000:00/0000:00:01.2/0000:02:00.0/ptp/*", SYMLINK+="ptp-i350-1"
----
You can get the full _DEVPATH_ of an existing PHC device with the `udevadm
info` command. You will need to execute the `udevadm trigger` command, or
reboot the system, for these changes to take effect.
=== Why are client log records dropped before reaching `clientloglimit`?
@@ -605,8 +727,9 @@ following questions.
Check the `Reach` value printed by the ``chronyc``'s `sources` command. If it
is zero, it means `chronyd` did not get any valid responses from the NTP server
you are trying to use. If there is a firewall between you and the server, the
packets might be blocked. Try using a tool like `wireshark` or `tcpdump` to see
if you are getting any responses from the server.
requests sent to the UDP port 123 of the server or responses sent back from
the port might be blocked. Try using a tool like `wireshark` or `tcpdump` to
see if you are getting any responses from the server.
When `chronyd` is receiving responses from the servers, the output of the
`sources` command issued few minutes after `chronyd` start might look like
@@ -615,9 +738,9 @@ this:
----
MS Name/IP address Stratum Poll Reach LastRx Last sample
===============================================================================
^* foo.example.net 2 6 377 34 +484us[ -157us] +/- 30ms
^- bar.example.net 2 6 377 34 +33ms[ +32ms] +/- 47ms
^+ baz.example.net 3 6 377 35 -1397us[-2033us] +/- 60ms
^* ntp1.example.net 2 6 377 34 +484us[ -157us] +/- 30ms
^- ntp2.example.net 2 6 377 34 +33ms[ +32ms] +/- 47ms
^+ ntp3.example.net 3 6 377 35 -1397us[-2033us] +/- 60ms
----
=== Are NTP servers specified with the `offline` option?
@@ -687,9 +810,9 @@ successful:
# chronyc -N authdata
Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen
=========================================================================
foo.example.net NTS 1 15 256 33m 0 0 8 100
bar.example.net NTS 1 15 256 33m 0 0 8 100
baz.example.net NTS 1 15 256 33m 0 0 8 100
ntp1.example.net NTS 1 15 256 33m 0 0 8 100
ntp2.example.net NTS 1 15 256 33m 0 0 8 100
ntp3.example.net NTS 1 15 256 33m 0 0 8 100
----
The KeyID, Type, and KLen columns should have non-zero values. If they are
@@ -813,7 +936,7 @@ Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev
==============================================================================
PPS0 0 0 0 +0.000 2000.000 +0ns 4000ms
NMEA 58 30 231 -96.494 38.406 +504ms 6080us
foo.example.net 7 3 200 -2.991 16.141 -107us 492us
ntp1.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

View File

@@ -25,7 +25,6 @@ LockPersonality=yes
MemoryDenyWriteExecute=yes
PrivateDevices=yes
PrivateUsers=yes
ProcSubset=pid
ProtectClock=yes
ProtectControlGroups=yes
ProtectHome=yes

View File

@@ -37,8 +37,8 @@ ntsdumpdir /var/lib/chrony
# Insert/delete leap seconds by slewing instead of stepping.
#leapsecmode slew
# Get TAI-UTC offset and leap seconds from the system tz database.
#leapsectz right/UTC
# Set the TAI-UTC offset of the system clock.
#leapseclist /usr/share/zoneinfo/leap-seconds.list
# Specify directory for log files.
logdir /var/log/chrony

View File

@@ -27,9 +27,9 @@
# you can access at http://support.ntp.org/bin/view/Servers/WebHome or
# you can use servers from the pool.ntp.org project.
! server foo.example.net iburst
! server bar.example.net iburst
! server baz.example.net iburst
! server ntp1.example.net iburst
! server ntp2.example.net iburst
! server ntp3.example.net iburst
! pool pool.ntp.org iburst
@@ -99,8 +99,8 @@ ntsdumpdir /var/lib/chrony
# and edit the following lines to specify the locations of the certificate and
# key.
! ntsservercert /etc/.../foo.example.net.crt
! ntsserverkey /etc/.../foo.example.net.key
! ntsservercert /etc/.../nts-server.crt
! ntsserverkey /etc/.../nts-server.key
# chronyd can save the measurement history for the servers to files when
# it exits. This is useful in 2 situations:
@@ -126,11 +126,11 @@ ntsdumpdir /var/lib/chrony
! pidfile /var/run/chrony/chronyd.pid
# If the system timezone database is kept up to date and includes the
# right/UTC timezone, chronyd can use it to determine the current
# TAI-UTC offset and when will the next leap second occur.
# The system timezone database usually comes with a list of leap seconds and
# corresponding TAI-UTC offsets. chronyd can use it to set the offset of the
# system TAI clock and have an additional source of leap seconds.
! leapsectz right/UTC
! leapseclist /usr/share/zoneinfo/leap-seconds.list
#######################################################################
### INITIAL CLOCK CORRECTION
@@ -238,7 +238,7 @@ ntsdumpdir /var/lib/chrony
# several people, you need to set up a mailing list or sendmail alias
# for them and use the address of that.)
! mailonchange wibble@foo.example.net 0.5
! mailonchange wibble@example.net 0.5
#######################################################################
### COMMAND ACCESS

View File

@@ -11,3 +11,5 @@
#1 MD5 AVeryLongAndRandomPassword
#2 MD5 HEX:12114855C7931009B4049EF3EFC48A139C3F989F
#3 SHA1 HEX:B2159C05D6A219673A3B7E896B6DE07F6A440995
#4 AES128 HEX:2DA837C4B6573748CA692B8C828E4891
#5 AES256 HEX:2666B8099BFF2D5BA20876121788ED24D2BE59111B8FFB562F0F56AE6EC7246E

View File

@@ -12,8 +12,10 @@ if [ $# -ge 2 ]; then
case "$2" in
up|down|connectivity-change)
;;
dhcp6-change)
# No other action is reported for routable IPv6
dhcp4-change|dhcp6-change)
# Actions "up" and "connectivity-change" in some cases do not
# guarantee that the interface has a route (e.g. a bond).
# dhcp(x)-change handles at least cases that use DHCP.
;;
*)
exit 0;;

View File

@@ -0,0 +1,58 @@
# This is a more restricted version of the chronyd service intended for
# minimal NTP/NTS client configurations. The daemon is started without root
# privileges and is allowed to write only to its own runtime, state, and log
# directories. It cannot bind to privileged ports in order to operate as an
# NTP server, or provide monitoring access over IPv4/IPv6. It cannot use
# reference clocks, HW timestamping, RTC tracking, and other features.
[Unit]
Description=NTP client (restricted)
Documentation=man:chronyd(8) man:chrony.conf(5)
After=chronyd.service ntpdate.service sntp.service ntpd.service
Conflicts=chronyd.service ntpd.service systemd-timesyncd.service
ConditionCapability=CAP_SYS_TIME
[Service]
Type=forking
PIDFile=/run/chrony/chronyd.pid
EnvironmentFile=-/etc/sysconfig/chronyd
ExecStart=/usr/sbin/chronyd -U $OPTIONS
User=chrony
LogsDirectory=chrony
LogsDirectoryMode=0750
RuntimeDirectory=chrony
RuntimeDirectoryMode=0750
RuntimeDirectoryPreserve=restart
StateDirectory=chrony
StateDirectoryMode=0750
AmbientCapabilities=CAP_SYS_TIME
CapabilityBoundingSet=CAP_SYS_TIME
DevicePolicy=closed
LockPersonality=yes
MemoryDenyWriteExecute=yes
NoNewPrivileges=yes
PrivateDevices=yes
PrivateTmp=yes
# This breaks adjtimex()
#PrivateUsers=yes
ProtectControlGroups=yes
ProtectHome=yes
ProtectHostname=yes
ProtectKernelLogs=yes
ProtectKernelModules=yes
ProtectKernelTunables=yes
ProtectProc=invisible
ProtectSystem=strict
RemoveIPC=yes
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
RestrictNamespaces=yes
RestrictRealtime=yes
RestrictSUIDSGID=yes
SystemCallArchitectures=native
SystemCallFilter=~@cpu-emulation @debug @module @mount @obsolete @raw-io
SystemCallFilter=~@reboot @resources @swap
UMask=0077
[Install]
WantedBy=multi-user.target

View File

@@ -24,7 +24,6 @@ LockPersonality=yes
MemoryDenyWriteExecute=yes
NoNewPrivileges=yes
PrivateTmp=yes
ProcSubset=pid
ProtectControlGroups=yes
ProtectHome=yes
ProtectHostname=yes

View File

@@ -448,9 +448,9 @@ o_merid : /* NULL */
the same signature as the function definition does. */
#include "getdate.h"
extern struct tm *gmtime ();
extern struct tm *localtime ();
extern time_t mktime ();
extern struct tm *gmtime (const time_t *timep);
extern struct tm *localtime (const time_t *timep);
extern time_t mktime (struct tm *tm);
/* Month and day table. */
static TABLE const MonthDayTable[] = {
@@ -641,16 +641,13 @@ static TABLE const MilitaryTable[] = {
/* ARGSUSED */
static int
yyerror (s)
char *s ATTRIBUTE_UNUSED;
yyerror (char *s ATTRIBUTE_UNUSED)
{
return 0;
}
static int
ToHour (Hours, Meridian)
int Hours;
MERIDIAN Meridian;
ToHour (int Hours, MERIDIAN Meridian)
{
switch (Meridian)
{
@@ -677,8 +674,7 @@ ToHour (Hours, Meridian)
}
static int
ToYear (Year)
int Year;
ToYear (int Year)
{
if (Year < 0)
Year = -Year;
@@ -694,8 +690,7 @@ ToYear (Year)
}
static int
LookupWord (buff)
char *buff;
LookupWord (char *buff)
{
register char *p;
register char *q;
@@ -948,6 +943,7 @@ get_date (const char *p, const time_t *now)
tm.tm_hour += yyRelHour;
tm.tm_min += yyRelMinutes;
tm.tm_sec += yyRelSeconds;
tm.tm_wday = 0;
/* Let mktime deduce tm_isdst if we have an absolute timestamp,
or if the relative timestamp mentions days, months, or years. */

5
keys.c
View File

@@ -182,6 +182,9 @@ KEY_Reload(void)
if (!key_file)
return;
if (!UTI_CheckFilePermissions(key_file, 0771))
;
in = UTI_OpenFile(NULL, key_file, NULL, 'r', 0);
if (!in) {
LOG(LOGS_WARN, "Could not open keyfile %s", key_file);
@@ -255,6 +258,8 @@ KEY_Reload(void)
more careful! */
qsort(ARR_GetElements(keys), ARR_GetSize(keys), sizeof (Key), compare_keys_by_id);
LOG(LOGS_INFO, "Loaded %u symmetric keys", ARR_GetSize(keys));
/* Check for duplicates */
for (i = 1; i < ARR_GetSize(keys); i++) {
if (get_key(i - 1)->id == get_key(i)->id)

272
leapdb.c Normal file
View File

@@ -0,0 +1,272 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2009-2018, 2020, 2022
* Copyright (C) Patrick Oppenlander 2023, 2024
*
* 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 module provides leap second information. */
#include "config.h"
#include "sysincl.h"
#include "conf.h"
#include "leapdb.h"
#include "logging.h"
#include "util.h"
/* ================================================== */
/* Source of leap second data */
enum {
SRC_NONE,
SRC_TIMEZONE,
SRC_LIST,
} leap_src;
/* Offset between leap-seconds.list timestamp epoch and Unix epoch.
leap-seconds.list epoch is 1 Jan 1900, 00:00:00 */
#define LEAP_SEC_LIST_OFFSET 2208988800
/* ================================================== */
static NTP_Leap
get_tz_leap(time_t when, int *tai_offset)
{
struct tm stm, *tm;
time_t t;
char *tz_env, tz_orig[128];
NTP_Leap tz_leap = LEAP_Normal;
tm = gmtime(&when);
if (!tm)
return tz_leap;
stm = *tm;
/* Temporarily switch to the timezone containing leap seconds */
tz_env = getenv("TZ");
if (tz_env) {
if (strlen(tz_env) >= sizeof (tz_orig))
return tz_leap;
strcpy(tz_orig, tz_env);
}
setenv("TZ", CNF_GetLeapSecTimezone(), 1);
tzset();
/* Get the TAI-UTC offset, which started at the epoch at 10 seconds */
t = mktime(&stm);
if (t != -1)
*tai_offset = t - when + 10;
/* Set the time to 23:59:60 and see how it overflows in mktime() */
stm.tm_sec = 60;
stm.tm_min = 59;
stm.tm_hour = 23;
t = mktime(&stm);
if (tz_env)
setenv("TZ", tz_orig, 1);
else
unsetenv("TZ");
tzset();
if (t == -1)
return tz_leap;
if (stm.tm_sec == 60)
tz_leap = LEAP_InsertSecond;
else if (stm.tm_sec == 1)
tz_leap = LEAP_DeleteSecond;
return tz_leap;
}
/* ================================================== */
static NTP_Leap
get_list_leap(time_t when, int *tai_offset)
{
FILE *f;
char line[1024];
NTP_Leap ret_leap = LEAP_Normal;
int ret_tai_offset = 0, prev_lsl_tai_offset = 10;
int64_t when1900, lsl_updated = 0, lsl_expiry = 0;
const char *leap_sec_list = CNF_GetLeapSecList();
if (!(f = UTI_OpenFile(NULL, leap_sec_list, NULL, 'r', 0))) {
LOG(LOGS_ERR, "Failed to open leap seconds list %s", leap_sec_list);
goto out;
}
/* Leap second happens at midnight */
when = (when / (24 * 3600) + 1) * (24 * 3600);
/* leap-seconds.list timestamps are relative to 1 Jan 1900, 00:00:00 */
when1900 = (int64_t)when + LEAP_SEC_LIST_OFFSET;
while (fgets(line, sizeof line, f) > 0) {
int64_t lsl_when;
int lsl_tai_offset;
char *p;
/* Ignore blank lines */
for (p = line; *p && isspace(*p); ++p)
;
if (!*p)
continue;
if (*line == '#') {
/* Update time line starts with #$ */
if (line[1] == '$' && sscanf(line + 2, "%"SCNd64, &lsl_updated) != 1)
goto error;
/* Expiration time line starts with #@ */
if (line[1] == '@' && sscanf(line + 2, "%"SCNd64, &lsl_expiry) != 1)
goto error;
/* Comment or a special comment we don't care about */
continue;
}
/* Leap entry */
if (sscanf(line, "%"SCNd64" %d", &lsl_when, &lsl_tai_offset) != 2)
goto error;
if (when1900 == lsl_when) {
if (lsl_tai_offset > prev_lsl_tai_offset)
ret_leap = LEAP_InsertSecond;
else if (lsl_tai_offset < prev_lsl_tai_offset)
ret_leap = LEAP_DeleteSecond;
/* When is rounded to the end of the day, so offset hasn't changed yet! */
ret_tai_offset = prev_lsl_tai_offset;
} else if (when1900 > lsl_when) {
ret_tai_offset = lsl_tai_offset;
}
prev_lsl_tai_offset = lsl_tai_offset;
}
/* Make sure the file looks sensible */
if (!feof(f) || !lsl_updated || !lsl_expiry)
goto error;
if (when1900 >= lsl_expiry)
LOG(LOGS_WARN, "Leap second list %s needs update", leap_sec_list);
goto out;
error:
if (f)
fclose(f);
LOG(LOGS_ERR, "Failed to parse leap seconds list %s", leap_sec_list);
return LEAP_Normal;
out:
if (f)
fclose(f);
*tai_offset = ret_tai_offset;
return ret_leap;
}
/* ================================================== */
static int
check_leap_source(NTP_Leap (*src)(time_t when, int *tai_offset))
{
int tai_offset = 0;
/* Check that the leap second source has good data for Jun 30 2012 and Dec 31 2012 */
if (src(1341014400, &tai_offset) == LEAP_InsertSecond && tai_offset == 34 &&
src(1356912000, &tai_offset) == LEAP_Normal && tai_offset == 35)
return 1;
return 0;
}
/* ================================================== */
void
LDB_Initialise(void)
{
const char *leap_tzname, *leap_sec_list;
leap_tzname = CNF_GetLeapSecTimezone();
if (leap_tzname && !check_leap_source(get_tz_leap)) {
LOG(LOGS_WARN, "Timezone %s failed leap second check, ignoring", leap_tzname);
leap_tzname = NULL;
}
leap_sec_list = CNF_GetLeapSecList();
if (leap_sec_list && !check_leap_source(get_list_leap)) {
LOG(LOGS_WARN, "Leap second list %s failed check, ignoring", leap_sec_list);
leap_sec_list = NULL;
}
if (leap_sec_list) {
LOG(LOGS_INFO, "Using leap second list %s", leap_sec_list);
leap_src = SRC_LIST;
} else if (leap_tzname) {
LOG(LOGS_INFO, "Using %s timezone to obtain leap second data", leap_tzname);
leap_src = SRC_TIMEZONE;
}
}
/* ================================================== */
NTP_Leap
LDB_GetLeap(time_t when, int *tai_offset)
{
static time_t last_ldb_leap_check;
static NTP_Leap ldb_leap;
static int ldb_tai_offset;
/* Do this check at most twice a day */
when = when / (12 * 3600) * (12 * 3600);
if (last_ldb_leap_check == when)
goto out;
last_ldb_leap_check = when;
ldb_leap = LEAP_Normal;
ldb_tai_offset = 0;
switch (leap_src) {
case SRC_NONE:
break;
case SRC_TIMEZONE:
ldb_leap = get_tz_leap(when, &ldb_tai_offset);
break;
case SRC_LIST:
ldb_leap = get_list_leap(when, &ldb_tai_offset);
break;
}
out:
*tai_offset = ldb_tai_offset;
return ldb_leap;
}
/* ================================================== */
void
LDB_Finalise(void)
{
/* Nothing to do */
}

37
leapdb.h Normal file
View File

@@ -0,0 +1,37 @@
/*
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Patrick Oppenlander 2023
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
**********************************************************************
=======================================================================
This module provides leap second information.
*/
#ifndef GOT_LEAPDB_H
#define GOT_LEAPDB_H
#include "ntp.h"
extern void LDB_Initialise(void);
extern NTP_Leap LDB_GetLeap(time_t when, int *tai_offset);
extern void LDB_Finalise(void);
#endif /* GOT_LEAPDB_H */

View File

@@ -39,6 +39,9 @@
/* This is used by DEBUG_LOG macro */
LOG_Severity log_min_severity = LOGS_INFO;
/* Current logging contexts */
static LOG_Context log_contexts;
/* ================================================== */
/* Flag indicating we have initialised */
static int initialised = 0;
@@ -72,6 +75,8 @@ void
LOG_Initialise(void)
{
debug_prefix = Strdup("");
log_contexts = 0;
initialised = 1;
LOG_OpenFileLog(NULL);
}
@@ -140,6 +145,7 @@ void LOG_Message(LOG_Severity severity,
struct tm *tm;
assert(initialised);
severity = CLAMP(LOGS_DEBUG, severity, LOGS_FATAL);
if (!system_log && file_log && severity >= log_min_severity) {
/* Don't clutter up syslog with timestamps and internal debugging info */
@@ -150,8 +156,13 @@ void LOG_Message(LOG_Severity severity,
fprintf(file_log, "%s ", buf);
}
#if DEBUG > 0
if (log_min_severity <= LOGS_DEBUG)
fprintf(file_log, "%s%s:%d:(%s) ", debug_prefix, filename, line_number, function_name);
if (log_min_severity <= LOGS_DEBUG) {
/* Log severity to character mapping (debug, info, warn, err, fatal) */
const char severity_chars[LOGS_FATAL - LOGS_DEBUG + 1] = {'D', 'I', 'W', 'E', 'F'};
fprintf(file_log, "%c:%s%s:%d:(%s) ", severity_chars[severity - LOGS_DEBUG],
debug_prefix, filename, line_number, function_name);
}
#endif
}
@@ -174,7 +185,7 @@ void LOG_Message(LOG_Severity severity,
/* Send the message also to the foreground process if it is
still running, or stderr if it is still open */
if (parent_fd > 0) {
if (write(parent_fd, buf, strlen(buf) + 1) < 0)
if (!LOG_NotifyParent(buf))
; /* Not much we can do here */
} else if (system_log && parent_fd == 0) {
system_log = 0;
@@ -237,6 +248,30 @@ LOG_GetMinSeverity(void)
/* ================================================== */
void
LOG_SetContext(LOG_Context context)
{
log_contexts |= context;
}
/* ================================================== */
void
LOG_UnsetContext(LOG_Context context)
{
log_contexts &= ~context;
}
/* ================================================== */
LOG_Severity
LOG_GetContextSeverity(LOG_Context contexts)
{
return log_contexts & contexts ? LOGS_INFO : LOGS_DEBUG;
}
/* ================================================== */
void
LOG_SetDebugPrefix(const char *prefix)
{
@@ -256,6 +291,17 @@ LOG_SetParentFd(int fd)
/* ================================================== */
int
LOG_NotifyParent(const char *message)
{
if (parent_fd <= 0)
return 1;
return write(parent_fd, message, strlen(message) + 1) > 0;
}
/* ================================================== */
void
LOG_CloseParentFd()
{

View File

@@ -100,6 +100,20 @@ extern void LOG_SetMinSeverity(LOG_Severity severity);
/* Get the minimum severity */
extern LOG_Severity LOG_GetMinSeverity(void);
/* Flags for info messages that should be logged only in specific contexts */
typedef enum {
LOGC_Command = 1,
LOGC_SourceFile = 2,
} LOG_Context;
/* Modify current contexts */
extern void LOG_SetContext(LOG_Context context);
extern void LOG_UnsetContext(LOG_Context context);
/* Get severity depending on the current active contexts: INFO if they contain
at least one of the specified contexts, DEBUG otherwise */
extern LOG_Severity LOG_GetContextSeverity(LOG_Context contexts);
/* Set a prefix for debug messages */
extern void LOG_SetDebugPrefix(const char *prefix);
@@ -112,7 +126,10 @@ extern void LOG_OpenSystemLog(void);
/* Stop using stderr and send fatal message to the foreground process */
extern void LOG_SetParentFd(int fd);
/* Close the pipe to the foreground process so it can exit */
/* Send a message to the foreground process */
extern int LOG_NotifyParent(const char *message);
/* Close the pipe to the foreground process */
extern void LOG_CloseParentFd(void);
/* File logging functions */

32
main.c
View File

@@ -32,6 +32,7 @@
#include "main.h"
#include "sched.h"
#include "leapdb.h"
#include "local.h"
#include "sys.h"
#include "ntp_io.h"
@@ -134,6 +135,7 @@ MAI_CleanupAndExit(void)
RCL_Finalise();
SRC_Finalise();
REF_Finalise();
LDB_Finalise();
RTC_Finalise();
SYS_Finalise();
@@ -213,7 +215,10 @@ post_init_ntp_hook(void *anything)
REF_SetMode(ref_mode);
}
/* Close the pipe to the foreground process so it can exit */
/* Send an empty message to the foreground process so it can exit.
If that fails, indicating the process was killed, exit too. */
if (!LOG_NotifyParent(""))
SCH_QuitProgram();
LOG_CloseParentFd();
CNF_AddSources();
@@ -331,10 +336,13 @@ go_daemon(void)
char message[1024];
int r;
/* Don't exit before the 'parent' */
waitpid(pid, NULL, 0);
close(pipefd[1]);
r = read(pipefd[0], message, sizeof (message));
if (r) {
if (r > 0) {
if (r != 1 || message[0] != '\0') {
if (r > 1) {
/* Print the error message from the child */
message[sizeof (message) - 1] = '\0';
fprintf(stderr, "%s\n", message);
@@ -353,7 +361,9 @@ go_daemon(void)
if (pid < 0) {
LOG_FATAL("fork() failed : %s", strerror(errno));
} else if (pid > 0) {
exit(0); /* In the 'parent' */
/* In the 'parent' */
close(pipefd[1]);
exit(0);
} else {
/* In the child we want to leave running as the daemon */
@@ -363,9 +373,9 @@ go_daemon(void)
}
/* Don't keep stdin/out/err from before. But don't close
the parent pipe yet. */
the parent pipe yet, or reusable file descriptors. */
for (fd=0; fd<1024; fd++) {
if (fd != pipefd[1])
if (fd != pipefd[1] && !SCK_IsReusable(fd))
close(fd);
}
@@ -555,6 +565,9 @@ int main
if (user_check && getuid() != 0)
LOG_FATAL("Not superuser");
/* Initialise reusable file descriptors before fork */
SCK_PreInitialise();
/* Turn into a daemon */
if (!nofork) {
go_daemon();
@@ -637,12 +650,17 @@ int main
}
/* Drop root privileges if the specified user has a non-zero UID */
if (!geteuid() && (pw->pw_uid || pw->pw_gid))
if (!geteuid() && (pw->pw_uid || pw->pw_gid)) {
SYS_DropRoot(pw->pw_uid, pw->pw_gid, SYS_MAIN_PROCESS);
/* Warn if missing read access or having write access to keys */
CNF_CheckReadOnlyAccess();
}
if (!geteuid())
LOG(LOGS_WARN, "Running with root privileges");
LDB_Initialise();
REF_Initialise();
SST_Initialise();
NSR_Initialise();

15
md5.c
View File

@@ -117,8 +117,7 @@ inline UINT4 ROTATE_LEFT(UINT4 x, int n)
/* The routine MD5Init initializes the message-digest context
mdContext. All fields are set to zero.
*/
void MD5Init (mdContext)
MD5_CTX *mdContext;
void MD5Init (MD5_CTX *mdContext)
{
mdContext->i[0] = mdContext->i[1] = (UINT4)0;
@@ -134,10 +133,7 @@ MD5_CTX *mdContext;
account for the presence of each of the characters inBuf[0..inLen-1]
in the message whose digest is being computed.
*/
void MD5Update (mdContext, inBuf, inLen)
MD5_CTX *mdContext;
unsigned const char *inBuf;
unsigned int inLen;
void MD5Update (MD5_CTX *mdContext, unsigned const char *inBuf, unsigned int inLen)
{
UINT4 in[16];
int mdi;
@@ -173,8 +169,7 @@ unsigned int inLen;
ends with the desired message digest in mdContext->digest[0...15].
*/
void MD5Final (mdContext)
MD5_CTX *mdContext;
void MD5Final (MD5_CTX *mdContext)
{
UINT4 in[16];
int mdi;
@@ -214,9 +209,7 @@ MD5_CTX *mdContext;
/* Basic MD5 step. Transforms buf based on in.
*/
static void Transform (buf, in)
UINT4 *buf;
UINT4 *in;
static void Transform (UINT4 *buf, UINT4 *in)
{
UINT4 a = buf[0], b = buf[1], c = buf[2], d = buf[3];

View File

@@ -47,8 +47,13 @@ Realloc(void *ptr, size_t size)
{
void *r;
if (size == 0) {
Free(ptr);
return NULL;
}
r = realloc(ptr, size);
if (!r && size)
if (!r)
LOG_FATAL("Could not allocate memory");
return r;

26
ntp.h
View File

@@ -115,9 +115,11 @@ typedef struct {
/* Non-authentication extension fields and corresponding internal flags */
#define NTP_EF_EXP1 0xF323
#define NTP_EF_EXP_MONO_ROOT 0xF323
#define NTP_EF_EXP_NET_CORRECTION 0xF324
#define NTP_EF_FLAG_EXP1 0x1
#define NTP_EF_FLAG_EXP_MONO_ROOT 0x1
#define NTP_EF_FLAG_EXP_NET_CORRECTION 0x2
/* Pre-NTPv5 experimental extension field */
typedef struct {
@@ -126,9 +128,18 @@ typedef struct {
NTP_int32 root_dispersion;
NTP_int64 mono_receive_ts;
uint32_t mono_epoch;
} NTP_ExtFieldExp1;
} NTP_EFExpMonoRoot;
#define NTP_EF_EXP1_MAGIC 0xF5BEDD9AU
#define NTP_EF_EXP_MONO_ROOT_MAGIC 0xF5BEDD9AU
/* Experimental extension field to provide PTP corrections */
typedef struct {
uint32_t magic;
NTP_int64 correction;
uint32_t reserved[3];
} NTP_EFExpNetCorrection;
#define NTP_EF_EXP_NET_CORRECTION_MAGIC 0x07AC2CEBU
/* Authentication extension fields */
@@ -179,4 +190,11 @@ typedef struct {
double root_dispersion;
} NTP_Sample;
/* Possible sources of timestamps */
typedef enum {
NTP_TS_DAEMON = 0,
NTP_TS_KERNEL,
NTP_TS_HARDWARE
} NTP_Timestamp_Source;
#endif /* GOT_NTP_H */

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2022
* Copyright (C) Miroslav Lichvar 2009-2023
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -64,6 +64,17 @@ typedef enum {
MD_BURST_WAS_ONLINE, /* Burst sampling, return to online afterwards */
} OperatingMode;
/* Structure holding a response and other data waiting to be processed when
a late HW transmit timestamp of the request is available, or a timeout is
reached */
struct SavedResponse {
NTP_Local_Address local_addr;
NTP_Local_Timestamp rx_ts;
NTP_Packet message;
NTP_PacketInfo info;
SCH_TimeoutID timeout_id;
};
/* ================================================== */
/* Structure used for holding a single peer/server's
protocol machine */
@@ -204,10 +215,13 @@ struct NCR_Instance_Record {
SPF_Instance filter;
int filter_count;
/* Response waiting for a HW transmit timestamp of the request */
struct SavedResponse *saved_response;
int burst_good_samples_to_go;
int burst_total_samples_to_go;
/* Report from last valid response */
/* Report from last valid response and packet/timestamp statistics */
RPT_NTPReport report;
};
@@ -300,6 +314,9 @@ static ARR_Instance broadcasts;
/* Maximum acceptable change in server mono<->real offset */
#define MAX_MONO_DOFFSET 16.0
/* Maximum assumed frequency error in network corrections */
#define MAX_NET_CORRECTION_FREQ 100.0e-6
/* Invalid socket, different from the one in ntp_io.c */
#define INVALID_SOCK_FD -2
@@ -324,10 +341,15 @@ static const char tss_chars[3] = {'D', 'K', 'H'};
/* Forward prototypes */
static void transmit_timeout(void *arg);
static double get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx);
static double get_transmit_delay(NCR_Instance inst, int on_tx);
static double get_separation(int poll);
static int parse_packet(NTP_Packet *packet, int length, NTP_PacketInfo *info);
static void process_sample(NCR_Instance inst, NTP_Sample *sample);
static int has_saved_response(NCR_Instance inst);
static void process_saved_response(NCR_Instance inst);
static int process_response(NCR_Instance inst, int saved, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *rx_ts, NTP_Packet *message,
NTP_PacketInfo *info);
static void set_connectivity(NCR_Instance inst, SRC_Connectivity connectivity);
/* ================================================== */
@@ -354,6 +376,9 @@ do_size_checks(void)
assert(offsetof(NTP_Packet, originate_ts) == 24);
assert(offsetof(NTP_Packet, receive_ts) == 32);
assert(offsetof(NTP_Packet, transmit_ts) == 40);
assert(sizeof (NTP_EFExpMonoRoot) == 24);
assert(sizeof (NTP_EFExpNetCorrection) == 24);
}
/* ================================================== */
@@ -399,6 +424,8 @@ zero_local_timestamp(NTP_Local_Timestamp *ts)
UTI_ZeroTimespec(&ts->ts);
ts->err = 0.0;
ts->source = NTP_TS_DAEMON;
ts->rx_duration = 0.0;
ts->net_correction = 0.0;
}
/* ================================================== */
@@ -490,8 +517,7 @@ restart_timeout(NCR_Instance inst, double delay)
static void
start_initial_timeout(NCR_Instance inst)
{
double delay, last_tx;
struct timespec now;
double delay;
if (!inst->tx_timeout_id) {
/* This will be the first transmission after mode change */
@@ -504,11 +530,7 @@ start_initial_timeout(NCR_Instance inst)
the interval between packets at least as long as the current polling
interval */
if (!UTI_IsZeroTimespec(&inst->local_tx.ts)) {
SCH_GetLastEventTime(&now, NULL, NULL);
last_tx = UTI_DiffTimespecsToDouble(&now, &inst->local_tx.ts);
if (last_tx < 0.0)
last_tx = 0.0;
delay = get_transmit_delay(inst, 0, 0.0) - last_tx;
delay = get_transmit_delay(inst, 0);
} else {
delay = 0.0;
}
@@ -531,6 +553,11 @@ close_client_socket(NCR_Instance inst)
SCH_RemoveTimeout(inst->rx_timeout_id);
inst->rx_timeout_id = 0;
if (has_saved_response(inst)) {
SCH_RemoveTimeout(inst->saved_response->timeout_id);
inst->saved_response->timeout_id = 0;
}
}
/* ================================================== */
@@ -625,7 +652,7 @@ NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
result->auto_burst = params->burst;
result->auto_offline = params->auto_offline;
result->copy = params->copy && result->mode == MODE_CLIENT;
result->poll_target = params->poll_target;
result->poll_target = MAX(1, params->poll_target);
result->ext_field_flags = params->ext_fields;
if (params->nts) {
@@ -673,6 +700,8 @@ NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
else
result->filter = NULL;
result->saved_response = NULL;
result->rx_timeout_id = 0;
result->tx_timeout_id = 0;
result->tx_suspended = 1;
@@ -709,6 +738,9 @@ NCR_DestroyInstance(NCR_Instance instance)
if (instance->filter)
SPF_DestroyInstance(instance->filter);
if (instance->saved_response)
Free(instance->saved_response);
NAU_DestroyInstance(instance->auth);
/* This will destroy the source instance inside the
@@ -774,12 +806,14 @@ NCR_ResetInstance(NCR_Instance instance)
void
NCR_ResetPoll(NCR_Instance instance)
{
instance->poll_score = 0.0;
if (instance->local_poll != instance->minpoll) {
instance->local_poll = instance->minpoll;
/* The timer was set with a longer poll interval, restart it */
if (instance->tx_timeout_id)
restart_timeout(instance, get_transmit_delay(instance, 0, 0.0));
restart_timeout(instance, get_transmit_delay(instance, 0));
}
}
@@ -804,6 +838,12 @@ NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr, int
inst->local_addr.sock_fd = NIO_OpenServerSocket(remote_addr);
}
/* Reset the polling interval only if the source wasn't unreachable to
avoid increasing server/network load in case that is what caused
the source to be unreachable */
if (SRC_IsReachable(inst->source))
NCR_ResetPoll(inst);
/* Update the reference ID and reset the source/sourcestats instances */
SRC_SetRefid(inst->source, UTI_IPToRefid(&remote_addr->ip_addr),
&inst->remote_addr.ip_addr);
@@ -900,10 +940,19 @@ get_transmit_poll(NCR_Instance inst)
/* ================================================== */
static double
get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx)
get_transmit_delay(NCR_Instance inst, int on_tx)
{
int poll_to_use, stratum_diff;
double delay_time;
double delay_time, last_tx;
struct timespec now;
/* Calculate the interval since last transmission if known */
if (!on_tx && !UTI_IsZeroTimespec(&inst->local_tx.ts)) {
SCH_GetLastEventTime(&now, NULL, NULL);
last_tx = UTI_DiffTimespecsToDouble(&now, &inst->local_tx.ts);
} else {
last_tx = 0;
}
/* If we're in burst mode, queue for immediate dispatch.
@@ -943,12 +992,6 @@ get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx)
last_tx / delay_time > PEER_SAMPLING_ADJ - 0.5))
delay_time *= PEER_SAMPLING_ADJ;
/* Substract the already spend time */
if (last_tx > 0.0)
delay_time -= last_tx;
if (delay_time < 0.0)
delay_time = 0.0;
break;
default:
assert(0);
@@ -966,6 +1009,12 @@ get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx)
break;
}
/* Subtract elapsed time */
if (last_tx > 0.0)
delay_time -= last_tx;
if (delay_time < 0.0)
delay_time = 0.0;
return delay_time;
}
@@ -1004,34 +1053,64 @@ receive_timeout(void *arg)
/* ================================================== */
static int
add_ext_exp1(NTP_Packet *message, NTP_PacketInfo *info, struct timespec *rx,
double root_delay, double root_dispersion)
add_ef_mono_root(NTP_Packet *message, NTP_PacketInfo *info, struct timespec *rx,
double root_delay, double root_dispersion)
{
struct timespec mono_rx;
NTP_ExtFieldExp1 exp1;
NTP_EFExpMonoRoot ef;
NTP_int64 ts_fuzz;
memset(&exp1, 0, sizeof (exp1));
exp1.magic = htonl(NTP_EF_EXP1_MAGIC);
memset(&ef, 0, sizeof (ef));
ef.magic = htonl(NTP_EF_EXP_MONO_ROOT_MAGIC);
if (info->mode != MODE_CLIENT) {
exp1.root_delay = UTI_DoubleToNtp32f28(root_delay);
exp1.root_dispersion = UTI_DoubleToNtp32f28(root_dispersion);
ef.root_delay = UTI_DoubleToNtp32f28(root_delay);
ef.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);
UTI_TimespecToNtp64(&mono_rx, &ef.mono_receive_ts, &ts_fuzz);
ef.mono_epoch = htonl(server_mono_epoch);
}
if (!NEF_AddField(message, info, NTP_EF_EXP1, &exp1, sizeof (exp1))) {
if (!NEF_AddField(message, info, NTP_EF_EXP_MONO_ROOT, &ef, sizeof (ef))) {
DEBUG_LOG("Could not add EF");
return 0;
}
info->ext_field_flags |= NTP_EF_FLAG_EXP1;
info->ext_field_flags |= NTP_EF_FLAG_EXP_MONO_ROOT;
return 1;
}
/* ================================================== */
static int
add_ef_net_correction(NTP_Packet *message, NTP_PacketInfo *info,
NTP_Local_Timestamp *local_rx)
{
NTP_EFExpNetCorrection ef;
if (CNF_GetPtpPort() == 0) {
DEBUG_LOG("ptpport disabled");
return 1;
}
memset(&ef, 0, sizeof (ef));
ef.magic = htonl(NTP_EF_EXP_NET_CORRECTION_MAGIC);
if (info->mode != MODE_CLIENT && local_rx->net_correction > local_rx->rx_duration) {
UTI_DoubleToNtp64(local_rx->net_correction, &ef.correction);
}
if (!NEF_AddField(message, info, NTP_EF_EXP_NET_CORRECTION, &ef, sizeof (ef))) {
DEBUG_LOG("Could not add EF");
return 0;
}
info->ext_field_flags |= NTP_EF_FLAG_EXP_NET_CORRECTION;
return 1;
}
@@ -1183,9 +1262,13 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
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))
if (ext_field_flags & NTP_EF_FLAG_EXP_MONO_ROOT) {
if (!add_ef_mono_root(&message, &info, smooth_time ? NULL : &local_receive,
our_root_delay, our_root_dispersion))
return 0;
}
if (ext_field_flags & NTP_EF_FLAG_EXP_NET_CORRECTION) {
if (!add_ef_net_correction(&message, &info, local_rx))
return 0;
}
}
@@ -1256,6 +1339,8 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
local_tx->ts = local_transmit;
local_tx->err = local_transmit_err;
local_tx->source = NTP_TS_DAEMON;
local_tx->rx_duration = 0.0;
local_tx->net_correction = 0.0;
}
if (local_ntp_rx)
@@ -1278,6 +1363,15 @@ transmit_timeout(void *arg)
inst->tx_timeout_id = 0;
if (has_saved_response(inst)) {
process_saved_response(inst);
/* Wait for the new transmission timeout (if the response was still
valid and it did not cause switch to offline) */
if (inst->tx_timeout_id != 0)
return;
}
switch (inst->opmode) {
case MD_BURST_WAS_ONLINE:
/* With online burst switch to online before last packet */
@@ -1309,11 +1403,10 @@ transmit_timeout(void *arg)
/* Prepare authentication */
if (!NAU_PrepareRequestAuth(inst->auth)) {
if (inst->burst_total_samples_to_go > 0)
inst->burst_total_samples_to_go--;
adjust_poll(inst, 0.25);
SRC_UpdateReachability(inst->source, 0);
restart_timeout(inst, get_transmit_delay(inst, 1, 0.0));
restart_timeout(inst, get_transmit_delay(inst, 1));
/* Count missing samples for the sample filter */
process_sample(inst, NULL);
return;
}
@@ -1416,7 +1509,7 @@ transmit_timeout(void *arg)
}
/* Restart timer for this message */
restart_timeout(inst, get_transmit_delay(inst, 1, 0.0));
restart_timeout(inst, get_transmit_delay(inst, 1));
/* If a client packet was just sent, schedule a timeout to close the socket
at the time when all server replies would fail the delay test, so the
@@ -1441,6 +1534,14 @@ is_zero_data(unsigned char *data, int length)
/* ================================================== */
static int
is_exp_ef(void *body, int body_length, int expected_body_length, uint32_t magic)
{
return body_length == expected_body_length && *(uint32_t *)body == htonl(magic);
}
/* ================================================== */
static int
parse_packet(NTP_Packet *packet, int length, NTP_PacketInfo *info)
{
@@ -1527,10 +1628,15 @@ parse_packet(NTP_Packet *packet, int length, NTP_PacketInfo *info)
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;
case NTP_EF_EXP_MONO_ROOT:
if (is_exp_ef(ef_body, ef_body_length, sizeof (NTP_EFExpMonoRoot),
NTP_EF_EXP_MONO_ROOT_MAGIC))
info->ext_field_flags |= NTP_EF_FLAG_EXP_MONO_ROOT;
break;
case NTP_EF_EXP_NET_CORRECTION:
if (is_exp_ef(ef_body, ef_body_length, sizeof (NTP_EFExpNetCorrection),
NTP_EF_EXP_NET_CORRECTION_MAGIC))
info->ext_field_flags |= NTP_EF_FLAG_EXP_NET_CORRECTION;
break;
default:
DEBUG_LOG("Unknown extension field type=%x", (unsigned int)ef_type);
@@ -1558,6 +1664,53 @@ parse_packet(NTP_Packet *packet, int length, NTP_PacketInfo *info)
/* ================================================== */
static void
apply_net_correction(NTP_Sample *sample, NTP_Local_Timestamp *rx, NTP_Local_Timestamp *tx,
double precision)
{
double rx_correction, tx_correction, low_delay_correction;
/* Require some correction from transparent clocks to be present
in both directions (not just the local RX timestamp correction) */
if (rx->net_correction <= rx->rx_duration || tx->net_correction <= 0.0)
return;
/* With perfect corrections from PTP transparent clocks and short cables
the peer delay would be close to zero, or even negative if the server or
transparent clocks were running faster than client, which would invert the
sample weighting. Adjust the correction to get a delay corresponding to
a direct connection to the server. For simplicity, assume the TX and RX
link speeds are equal. If not, the reported delay will be wrong, but it
will not cause an error in the offset. */
rx_correction = rx->net_correction - rx->rx_duration;
tx_correction = tx->net_correction - rx->rx_duration;
/* Use a slightly smaller value in the correction of delay to not overcorrect
if the transparent clocks run up to 100 ppm fast and keep a part of the
uncorrected delay for the sample weighting */
low_delay_correction = (rx_correction + tx_correction) *
(1.0 - MAX_NET_CORRECTION_FREQ);
/* Make sure the correction is sane. The values are not authenticated! */
if (low_delay_correction < 0.0 || low_delay_correction > sample->peer_delay) {
DEBUG_LOG("Invalid correction %.9f peer_delay=%.9f",
low_delay_correction, sample->peer_delay);
return;
}
/* Correct the offset and peer delay, but not the root delay to not
change the estimated maximum error */
sample->offset += (rx_correction - tx_correction) / 2.0;
sample->peer_delay -= low_delay_correction;
if (sample->peer_delay < precision)
sample->peer_delay = precision;
DEBUG_LOG("Applied correction rx=%.9f tx=%.9f dur=%.9f",
rx->net_correction, tx->net_correction, rx->rx_duration);
}
/* ================================================== */
static int
check_delay_ratio(NCR_Instance inst, SST_Stats stats,
struct timespec *sample_time, double delay)
@@ -1729,7 +1882,69 @@ process_sample(NCR_Instance inst, NTP_Sample *sample)
/* ================================================== */
static int
process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
has_saved_response(NCR_Instance inst)
{
return inst->saved_response && inst->saved_response->timeout_id > 0;
}
/* ================================================== */
static void
process_saved_response(NCR_Instance inst)
{
SCH_RemoveTimeout(inst->saved_response->timeout_id);
inst->saved_response->timeout_id = 0;
DEBUG_LOG("Processing saved response from %s", UTI_IPToString(&inst->remote_addr.ip_addr));
process_response(inst, 1, &inst->saved_response->local_addr, &inst->saved_response->rx_ts,
&inst->saved_response->message, &inst->saved_response->info);
}
/* ================================================== */
static void
saved_response_timeout(void *arg)
{
NCR_Instance inst = arg;
inst->saved_response->timeout_id = 0;
process_saved_response(inst);
}
/* ================================================== */
static int
save_response(NCR_Instance inst, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *rx_ts, NTP_Packet *message, NTP_PacketInfo *info)
{
double timeout = CNF_GetHwTsTimeout();
if (timeout <= 0.0)
return 0;
/* If another message is already saved, process both immediately */
if (has_saved_response(inst)) {
process_saved_response(inst);
return 0;
}
if (!inst->saved_response)
inst->saved_response = MallocNew(struct SavedResponse);
inst->saved_response->local_addr = *local_addr;
inst->saved_response->rx_ts = *rx_ts;
inst->saved_response->message = *message;
inst->saved_response->info = *info;
inst->saved_response->timeout_id = SCH_AddTimeoutByDelay(timeout, saved_response_timeout,
inst);
DEBUG_LOG("Saved valid response for later processing");
return 1;
}
/* ================================================== */
static int
process_response(NCR_Instance inst, int saved, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *rx_ts, NTP_Packet *message, NTP_PacketInfo *info)
{
NTP_Sample sample;
@@ -1757,18 +1972,20 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
/* Extension fields */
int parsed, ef_length, ef_type, ef_body_length;
void *ef_body;
NTP_ExtFieldExp1 *ef_exp1;
NTP_EFExpMonoRoot *ef_mono_root;
NTP_EFExpNetCorrection *ef_net_correction;
NTP_Local_Timestamp local_receive, local_transmit;
double remote_interval, local_interval, response_time;
double delay_time, precision, mono_doffset;
double delay_time, precision, mono_doffset, net_correction;
int updated_timestamps;
/* ==================== */
stats = SRC_GetSourcestats(inst->source);
ef_exp1 = NULL;
ef_mono_root = NULL;
ef_net_correction = NULL;
/* Find requested non-authentication extension fields */
if (inst->ext_field_flags & info->ext_field_flags) {
@@ -1778,11 +1995,17 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
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;
case NTP_EF_EXP_MONO_ROOT:
if (inst->ext_field_flags & NTP_EF_FLAG_EXP_MONO_ROOT &&
is_exp_ef(ef_body, ef_body_length, sizeof (*ef_mono_root),
NTP_EF_EXP_MONO_ROOT_MAGIC))
ef_mono_root = ef_body;
break;
case NTP_EF_EXP_NET_CORRECTION:
if (inst->ext_field_flags & NTP_EF_FLAG_EXP_NET_CORRECTION &&
is_exp_ef(ef_body, ef_body_length, sizeof (*ef_net_correction),
NTP_EF_EXP_NET_CORRECTION_MAGIC))
ef_net_correction = ef_body;
break;
}
}
@@ -1791,9 +2014,9 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
pkt_leap = NTP_LVM_TO_LEAP(message->lvm);
pkt_version = NTP_LVM_TO_VERSION(message->lvm);
pkt_refid = ntohl(message->reference_id);
if (ef_exp1) {
pkt_root_delay = UTI_Ntp32f28ToDouble(ef_exp1->root_delay);
pkt_root_dispersion = UTI_Ntp32f28ToDouble(ef_exp1->root_dispersion);
if (ef_mono_root) {
pkt_root_delay = UTI_Ntp32f28ToDouble(ef_mono_root->root_delay);
pkt_root_dispersion = UTI_Ntp32f28ToDouble(ef_mono_root->root_dispersion);
} else {
pkt_root_delay = UTI_Ntp32ToDouble(message->root_delay);
pkt_root_dispersion = UTI_Ntp32ToDouble(message->root_dispersion);
@@ -1823,8 +2046,10 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
/* Test 4 would check for denied access. It would always pass as this
function is called only for known sources. */
/* Test 5 checks for authentication failure */
test5 = NAU_CheckResponseAuth(inst->auth, message, info);
/* Test 5 checks for authentication failure. If it is a saved message,
which had to pass all these tests before, avoid authenticating it for
the second time (that is not allowed in the NTS code). */
test5 = saved || NAU_CheckResponseAuth(inst->auth, message, info);
/* Test 6 checks for unsynchronised server */
test6 = pkt_leap != LEAP_Unsynchronised &&
@@ -1840,6 +2065,20 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
valid_packet = test1 && test2 && test3 && test5;
synced_packet = valid_packet && test6 && test7;
/* If the server is very close and/or the NIC hardware/driver is slow, it
is possible that a response from the server is received before the HW
transmit timestamp of the request. To avoid getting a less accurate
offset or failing one of the later tests, save the response and wait for
the transmit timestamp or timeout. Allow this only for the first valid
response to the request, when at least one good response has already been
accepted to avoid incorrectly confirming a tentative source. */
if (valid_packet && synced_packet && !saved && !inst->valid_rx &&
NIO_IsHwTsEnabled() && inst->local_tx.source != NTP_TS_HARDWARE &&
inst->report.total_good_count > 0) {
if (save_response(inst, local_addr, rx_ts, message, info))
return 1;
}
/* Check for Kiss-o'-Death codes */
kod_rate = 0;
if (test1 && test2 && test5 && pkt_leap == LEAP_Unsynchronised &&
@@ -1862,11 +2101,11 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
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) &&
if (ef_mono_root && inst->remote_mono_epoch == ntohl(ef_mono_root->mono_epoch) &&
!UTI_IsZeroNtp64(&ef_mono_root->mono_receive_ts) &&
!UTI_IsZeroNtp64(&inst->remote_ntp_monorx)) {
mono_doffset =
UTI_DiffNtp64ToDouble(&ef_exp1->mono_receive_ts, &inst->remote_ntp_monorx) -
UTI_DiffNtp64ToDouble(&ef_mono_root->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;
@@ -1874,6 +2113,12 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
mono_doffset = 0.0;
}
if (ef_net_correction) {
net_correction = UTI_Ntp64ToDouble(&ef_net_correction->correction);
} else {
net_correction = 0.0;
}
/* Select remote and local timestamps for the new sample */
if (interleaved_packet) {
/* Prefer previous local TX and remote RX timestamps if it will make
@@ -1893,6 +2138,7 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
UTI_Ntp64ToTimespec(&message->receive_ts, &remote_receive);
UTI_Ntp64ToTimespec(&inst->remote_ntp_rx, &remote_request_receive);
local_transmit = inst->local_tx;
local_transmit.net_correction = net_correction;
root_delay = MAX(pkt_root_delay, inst->remote_root_delay);
root_dispersion = MAX(pkt_root_dispersion, inst->remote_root_dispersion);
}
@@ -1907,6 +2153,7 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
remote_request_receive = remote_receive;
local_receive = *rx_ts;
local_transmit = inst->local_tx;
local_transmit.net_correction = net_correction;
root_delay = pkt_root_delay;
root_dispersion = pkt_root_dispersion;
}
@@ -1950,6 +2197,9 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
skew * fabs(local_interval);
sample.root_delay = root_delay + sample.peer_delay;
sample.root_dispersion = root_dispersion + sample.peer_dispersion;
/* Apply corrections from PTP transparent clocks if available and sane */
apply_net_correction(&sample, &local_receive, &local_transmit, precision);
/* If the source is an active peer, this is the minimum assumed interval
between previous two transmissions (if not constrained by minpoll) */
@@ -1958,8 +2208,11 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
/* Additional tests required to pass before accumulating the sample */
/* 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
/* Test A combines multiple tests to avoid changing the measurements log
format and ntpdata report. It requires that the minimum estimate of the
peer delay is not larger than the configured maximum, it is not a
response in the 'warm up' exchange, the configured offset correction is
within the supported NTP interval, both client modes that the server
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
@@ -1967,6 +2220,8 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
a missed response */
testA = sample.peer_delay - sample.peer_dispersion <= inst->max_delay &&
precision <= inst->max_delay &&
inst->presend_done <= 0 &&
UTI_IsTimeOffsetSane(&sample.time, sample.offset) &&
!(inst->mode == MODE_CLIENT && response_time > MAX_SERVER_INTERVAL) &&
!(inst->mode == MODE_CLIENT && interleaved_packet &&
UTI_IsZeroTimespec(&inst->prev_local_tx.ts) &&
@@ -2005,6 +2260,7 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
sample.root_delay = sample.root_dispersion = 0.0;
sample.time = rx_ts->ts;
mono_doffset = 0.0;
net_correction = 0.0;
local_receive = *rx_ts;
local_transmit = inst->local_tx;
testA = testB = testC = testD = 0;
@@ -2038,9 +2294,9 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
/* 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;
if (ef_mono_root && !UTI_IsZeroNtp64(&ef_mono_root->mono_receive_ts)) {
inst->remote_mono_epoch = ntohl(ef_mono_root->mono_epoch);
inst->remote_ntp_monorx = ef_mono_root->mono_receive_ts;
inst->mono_doffset += mono_doffset;
} else {
inst->remote_mono_epoch = 0;
@@ -2048,8 +2304,11 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
inst->mono_doffset = 0.0;
}
/* Don't use the same set of timestamps for the next sample */
if (interleaved_packet)
inst->local_tx.net_correction = net_correction;
/* Avoid reusing timestamps of an accumulated sample when switching
from basic mode to interleaved mode */
if (interleaved_packet || !good_packet)
inst->prev_local_tx = inst->local_tx;
else
zero_local_timestamp(&inst->prev_local_tx);
@@ -2068,15 +2327,11 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
/* Accept at most one response per request. The NTP specification recommends
resetting local_ntp_tx to make the following packets fail test2 or test3,
but that would not allow the code above to make multiple updates of the
timestamps in symmetric mode. Also, ignore presend responses. */
timestamps in symmetric mode. */
if (inst->valid_rx) {
test2 = test3 = 0;
valid_packet = synced_packet = good_packet = 0;
} else if (valid_packet) {
if (inst->presend_done) {
testA = 0;
good_packet = 0;
}
inst->valid_rx = 1;
}
@@ -2119,13 +2374,17 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
SRC_UpdateReachability(inst->source, synced_packet);
if (synced_packet) {
if (inst->copy && inst->remote_stratum > 0) {
/* Assume the reference ID and stratum of the server */
if (inst->copy) {
/* Assume the reference ID and stratum of the server */
if (synced_packet && inst->remote_stratum > 0) {
inst->remote_stratum--;
SRC_SetRefid(inst->source, ntohl(message->reference_id), &inst->remote_addr.ip_addr);
} else {
SRC_ResetInstance(inst->source);
}
}
if (synced_packet) {
SRC_UpdateStatus(inst->source, MAX(inst->remote_stratum, inst->min_stratum), pkt_leap);
if (inst->delay_quant)
@@ -2170,8 +2429,7 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
/* And now, requeue the timer */
if (inst->opmode != MD_OFFLINE) {
delay_time = get_transmit_delay(inst, 0,
UTI_DiffTimespecsToDouble(&inst->local_rx.ts, &inst->local_tx.ts));
delay_time = get_transmit_delay(inst, 0);
if (kod_rate) {
LOG(LOGS_WARN, "Received KoD RATE from %s",
@@ -2186,7 +2444,8 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
}
/* Get rid of old timeout and start a new one */
assert(inst->tx_timeout_id);
if (!saved)
assert(inst->tx_timeout_id);
restart_timeout(inst, delay_time);
}
@@ -2277,6 +2536,10 @@ NCR_ProcessRxKnown(NCR_Instance inst, NTP_Local_Address *local_addr,
NTP_PacketInfo info;
inst->report.total_rx_count++;
if (rx_ts->source == NTP_TS_KERNEL)
inst->report.total_kernel_rx_ts++;
else if (rx_ts->source == NTP_TS_HARDWARE)
inst->report.total_hw_rx_ts++;
if (!parse_packet(message, length, &info))
return 0;
@@ -2331,8 +2594,8 @@ NCR_ProcessRxKnown(NCR_Instance inst, NTP_Local_Address *local_addr,
client mode operation.
This copes with the case for an isolated network where one
machine is set by eye and is used as the master, with the
other machines pointed at it. If the master goes down, we
machine is set by eye and is used as the primary server, with
the other machines pointed at it. If the server goes down, we
want to be able to reset its time at startup by relying on
one of the secondaries to flywheel it. The behaviour coded here
is required in the secondaries to make this possible. */
@@ -2375,7 +2638,7 @@ NCR_ProcessRxKnown(NCR_Instance inst, NTP_Local_Address *local_addr,
return 0;
}
return process_response(inst, local_addr, rx_ts, message, &info);
return process_response(inst, 0, local_addr, rx_ts, message, &info);
} else if (proc_as_unknown) {
NCR_ProcessRxUnknown(&inst->remote_addr, local_addr, rx_ts, message, length);
/* It's not a reply to our request, don't return success */
@@ -2399,6 +2662,7 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
NTP_Local_Timestamp local_tx, *tx_ts;
NTP_int64 ntp_rx, *local_ntp_rx;
int log_index, interleaved, poll, version;
CLG_Limit limit;
uint32_t kod;
/* Ignore the packet if it wasn't received by server socket */
@@ -2444,7 +2708,8 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
log_index = CLG_LogServiceAccess(CLG_NTP, &remote_addr->ip_addr, &rx_ts->ts);
/* Don't reply to all requests if the rate is excessive */
if (log_index >= 0 && CLG_LimitServiceRate(CLG_NTP, log_index)) {
limit = log_index >= 0 ? CLG_LimitServiceRate(CLG_NTP, log_index) : CLG_PASS;
if (limit == CLG_DROP) {
DEBUG_LOG("NTP packet discarded to limit response rate");
return;
}
@@ -2456,8 +2721,13 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
/* Don't respond unless a non-zero KoD was returned */
if (kod == 0)
return;
} else if (info.auth.mode != NTP_AUTH_NONE && info.auth.mode != NTP_AUTH_MSSNTP) {
CLG_LogAuthNtpRequest();
}
if (limit == CLG_KOD) {
/* Don't respond if there is a conflict with the NTS NAK */
if (kod != 0)
return;
kod = KOD_RATE;
}
local_ntp_rx = NULL;
@@ -2477,14 +2747,18 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
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);
zero_local_timestamp(&local_tx);
interleaved = CLG_GetNtpTxTimestamp(&ntp_rx, &local_tx.ts, &local_tx.source);
tx_ts = &local_tx;
if (interleaved)
CLG_DisableNtpTimestamps(&ntp_rx);
}
CLG_UpdateNtpStats(kod == 0 && info.auth.mode != NTP_AUTH_NONE &&
info.auth.mode != NTP_AUTH_MSSNTP,
rx_ts->source, interleaved ? tx_ts->source : NTP_TS_DAEMON);
/* Suggest the client to increase its polling interval if it indicates
the interval is shorter than the rate limiting interval */
poll = CLG_GetNtpMinPoll();
@@ -2501,7 +2775,7 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
return;
if (local_ntp_rx)
CLG_SaveNtpTimestamps(local_ntp_rx, tx_ts ? &tx_ts->ts : NULL);
CLG_SaveNtpTimestamps(local_ntp_rx, &tx_ts->ts, tx_ts->source);
}
/* ================================================== */
@@ -2555,6 +2829,14 @@ NCR_ProcessTxKnown(NCR_Instance inst, NTP_Local_Address *local_addr,
update_tx_timestamp(&inst->local_tx, tx_ts, &inst->local_ntp_rx, &inst->local_ntp_tx,
message);
if (tx_ts->source == NTP_TS_HARDWARE) {
inst->report.total_hw_tx_ts++;
if (has_saved_response(inst))
process_saved_response(inst);
} else if (tx_ts->source == NTP_TS_KERNEL) {
inst->report.total_kernel_tx_ts++;
}
}
/* ================================================== */
@@ -2579,7 +2861,7 @@ NCR_ProcessTxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
local_ntp_rx = &message->receive_ts;
new_tx = *tx_ts;
if (!CLG_GetNtpTxTimestamp(local_ntp_rx, &old_tx.ts))
if (!CLG_GetNtpTxTimestamp(local_ntp_rx, &old_tx.ts, &old_tx.source))
return;
/* Undo a clock adjustment between the RX and TX timestamps to minimise error
@@ -2588,7 +2870,7 @@ NCR_ProcessTxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
update_tx_timestamp(&old_tx, &new_tx, local_ntp_rx, NULL, message);
CLG_UpdateNtpTxTimestamp(local_ntp_rx, &new_tx.ts);
CLG_UpdateNtpTxTimestamp(local_ntp_rx, &new_tx.ts, new_tx.source);
}
/* ================================================== */
@@ -2611,6 +2893,10 @@ NCR_SlewTimes(NCR_Instance inst, struct timespec *when, double dfreq, double dof
if (inst->filter)
SPF_SlewSamples(inst->filter, when, dfreq, doffset);
if (has_saved_response(inst))
UTI_AdjustTimespec(&inst->saved_response->rx_ts.ts, when, &inst->saved_response->rx_ts.ts,
&delta, dfreq, doffset);
}
/* ================================================== */
@@ -2753,10 +3039,20 @@ NCR_ModifyMinstratum(NCR_Instance inst, int new_min_stratum)
/* ================================================== */
void
NCR_ModifyOffset(NCR_Instance inst, double new_offset)
{
inst->offset_correction = new_offset;
LOG(LOGS_INFO, "Source %s new offset %f",
UTI_IPToString(&inst->remote_addr.ip_addr), new_offset);
}
/* ================================================== */
void
NCR_ModifyPolltarget(NCR_Instance inst, int new_poll_target)
{
inst->poll_target = new_poll_target;
inst->poll_target = MAX(1, new_poll_target);
LOG(LOGS_INFO, "Source %s new polltarget %d",
UTI_IPToString(&inst->remote_addr.ip_addr), new_poll_target);
}
@@ -2856,6 +3152,10 @@ NCR_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all)
if (status != ADF_SUCCESS)
return 0;
LOG(LOG_GetContextSeverity(LOGC_Command), "%s%s %s access from %s",
allow ? "Allowed" : "Denied", all ? " all" : "", "NTP",
UTI_IPSubnetToString(ip_addr, subnet_bits));
/* Keep server sockets open only when an address allowed */
if (allow) {
NTP_Remote_Address remote_addr;

View File

@@ -38,16 +38,12 @@ typedef enum {
NTP_SERVER, NTP_PEER
} NTP_Source_Type;
typedef enum {
NTP_TS_DAEMON = 0,
NTP_TS_KERNEL,
NTP_TS_HARDWARE
} NTP_Timestamp_Source;
typedef struct {
struct timespec ts;
double err;
NTP_Timestamp_Source source;
double rx_duration;
double net_correction;
} NTP_Local_Timestamp;
/* This is a private data type used for storing the instance record for
@@ -117,6 +113,8 @@ extern void NCR_ModifyMaxdelaydevratio(NCR_Instance inst, double new_max_delay_d
extern void NCR_ModifyMinstratum(NCR_Instance inst, int new_min_stratum);
extern void NCR_ModifyOffset(NCR_Instance inst, double new_offset);
extern void NCR_ModifyPolltarget(NCR_Instance inst, int new_poll_target);
extern void NCR_InitiateSampleBurst(NCR_Instance inst, int n_good_samples, int n_total_samples);

View File

@@ -126,8 +126,14 @@ open_socket(int family, int local_port, int client_only, IPSockAddr *remote_addr
dscp = CNF_GetNtpDscp();
if (dscp > 0 && dscp < 64) {
#ifdef IP_TOS
if (!SCK_SetIntOption(sock_fd, IPPROTO_IP, IP_TOS, dscp << 2))
;
if (family == IPADDR_INET4)
if (!SCK_SetIntOption(sock_fd, IPPROTO_IP, IP_TOS, dscp << 2))
;
#endif
#if defined(FEAT_IPV6) && defined(IPV6_TCLASS)
if (family == IPADDR_INET6)
if (!SCK_SetIntOption(sock_fd, IPPROTO_IPV6, IPV6_TCLASS, dscp << 2))
;
#endif
}
@@ -163,9 +169,6 @@ close_socket(int sock_fd)
if (sock_fd == INVALID_SOCK_FD)
return;
#ifdef HAVE_LINUX_TIMESTAMPING
NIO_Linux_NotifySocketClosing(sock_fd);
#endif
SCH_RemoveFileHandler(sock_fd);
SCK_CloseSocket(sock_fd);
}
@@ -275,6 +278,18 @@ NIO_Finalise(void)
/* ================================================== */
int
NIO_IsHwTsEnabled(void)
{
#ifdef HAVE_LINUX_TIMESTAMPING
return NIO_Linux_IsHwTsEnabled();
#else
return 0;
#endif
}
/* ================================================== */
int
NIO_OpenClientSocket(NTP_Remote_Address *remote_addr)
{
@@ -416,6 +431,9 @@ process_message(SCK_Message *message, int sock_fd, int event)
SCH_GetLastEventTime(&local_ts.ts, &local_ts.err, NULL);
local_ts.source = NTP_TS_DAEMON;
local_ts.rx_duration = 0.0;
local_ts.net_correction = 0.0;
sched_ts = local_ts.ts;
if (message->addr_type != SCK_ADDR_IP) {
@@ -441,7 +459,7 @@ 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))
if (!NIO_UnwrapMessage(message, sock_fd, &local_ts.net_correction))
return;
/* Just ignore the packet if it's not of a recognized length */
@@ -461,11 +479,6 @@ read_from_socket(int sock_fd, int event, void *anything)
SCK_Message *messages;
int i, received, flags = 0;
#ifdef HAVE_LINUX_TIMESTAMPING
if (NIO_Linux_ProcessEvent(sock_fd, event))
return;
#endif
if (event == SCH_FILE_EXCEPTION) {
#ifdef HAVE_LINUX_TIMESTAMPING
flags |= SCK_FLAG_MSG_ERRQUEUE;
@@ -485,8 +498,9 @@ read_from_socket(int sock_fd, int event, void *anything)
/* ================================================== */
int
NIO_UnwrapMessage(SCK_Message *message, int sock_fd)
NIO_UnwrapMessage(SCK_Message *message, int sock_fd, double *net_correction)
{
double ptp_correction;
PTP_NtpMessage *msg;
if (!is_ptp_socket(sock_fd))
@@ -499,9 +513,11 @@ NIO_UnwrapMessage(SCK_Message *message, int sock_fd)
msg = message->data;
if (msg->header.type != PTP_TYPE_DELAY_REQ || msg->header.version != PTP_VERSION ||
if ((msg->header.type != PTP_TYPE_DELAY_REQ && msg->header.type != PTP_TYPE_SYNC) ||
(msg->header.version != PTP_VERSION_2 &&
(msg->header.version != PTP_VERSION_2_1 || msg->header.min_sdoid != 0)) ||
ntohs(msg->header.length) != message->length ||
msg->header.domain != PTP_DOMAIN_NTP ||
msg->header.domain != CNF_GetPtpDomain() ||
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) {
@@ -512,7 +528,14 @@ NIO_UnwrapMessage(SCK_Message *message, int sock_fd)
message->data = (char *)message->data + PTP_NTP_PREFIX_LENGTH;
message->length -= PTP_NTP_PREFIX_LENGTH;
DEBUG_LOG("Unwrapped PTP->NTP len=%d", message->length);
ptp_correction = UTI_Integer64NetworkToHost(*(Integer64 *)msg->header.correction) /
((1 << 16) * 1.0e9);
/* Use the correction only if the RX duration is known (i.e. HW timestamp) */
if (*net_correction > 0.0)
*net_correction += ptp_correction;
DEBUG_LOG("Unwrapped PTP->NTP len=%d corr=%.9f", message->length, ptp_correction);
return 1;
}
@@ -522,6 +545,8 @@ NIO_UnwrapMessage(SCK_Message *message, int sock_fd)
static int
wrap_message(SCK_Message *message, int sock_fd)
{
static uint16_t sequence_id = 0;
assert(PTP_NTP_PREFIX_LENGTH == 48);
if (!is_ptp_socket(sock_fd))
@@ -538,10 +563,11 @@ wrap_message(SCK_Message *message, int sock_fd)
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.version = PTP_VERSION_2;
ptp_message->header.length = htons(PTP_NTP_PREFIX_LENGTH + message->length);
ptp_message->header.domain = PTP_DOMAIN_NTP;
ptp_message->header.domain = CNF_GetPtpDomain();
ptp_message->header.flags = htons(PTP_FLAG_UNICAST);
ptp_message->header.sequence_id = htons(sequence_id++);
ptp_message->tlv_header.type = htons(PTP_TLV_NTP);
ptp_message->tlv_header.length = htons(message->length);
memcpy((char *)ptp_message + PTP_NTP_PREFIX_LENGTH, message->data, message->length);

View File

@@ -39,6 +39,9 @@ extern void NIO_Initialise(void);
/* Function to finalise the module */
extern void NIO_Finalise(void);
/* Function to check if HW timestamping is enabled on any interface */
extern int NIO_IsHwTsEnabled(void);
/* Function to obtain a socket for sending client packets */
extern int NIO_OpenClientSocket(NTP_Remote_Address *remote_addr);
@@ -61,7 +64,7 @@ extern int NIO_IsServerSocketOpen(void);
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);
extern int NIO_UnwrapMessage(SCK_Message *message, int sock_fd, double *net_correction);
/* Function to transmit a packet */
extern int NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2016-2019, 2021-2022
* Copyright (C) Miroslav Lichvar 2016-2019, 2021-2023
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -39,6 +39,7 @@
#include "hwclock.h"
#include "local.h"
#include "logging.h"
#include "memory.h"
#include "ntp_core.h"
#include "ntp_io.h"
#include "ntp_io_linux.h"
@@ -63,13 +64,16 @@ struct Interface {
double tx_comp;
double rx_comp;
HCL_Instance clock;
int maxpoll;
SCH_TimeoutID poll_timeout_id;
};
/* Number of PHC readings per HW clock sample */
#define PHC_READINGS 25
/* Minimum interval between PHC readings */
/* Minimum and maximum interval between PHC readings */
#define MIN_PHC_POLL -6
#define MAX_PHC_POLL 20
/* Maximum acceptable offset between SW/HW and daemon timestamp */
#define MAX_TS_DELAY 1.0
@@ -84,19 +88,6 @@ static int ts_tx_flags;
/* Flag indicating the socket options can't be changed in control messages */
static int permanent_ts_options;
/* When sending client requests to a close and fast server, it is possible that
a response will be received before the HW transmit timestamp of the request
itself. To avoid processing of the response without the HW timestamp, we
monitor events returned by select() and suspend reading of packets from the
receive queue for up to 200 microseconds. As the requests are normally
separated by at least 200 milliseconds, it is sufficient to monitor and
suspend one socket at a time. */
static int monitored_socket;
static int suspended_socket;
static SCH_TimeoutID resume_timeout_id;
#define RESUME_TIMEOUT 200.0e-6
/* Unbound socket keeping the kernel RX timestamping permanently enabled
in order to avoid a race condition between receiving a server response
and the kernel actually starting to timestamp received packets after
@@ -107,13 +98,17 @@ static int dummy_rxts_socket;
/* ================================================== */
static void poll_phc(struct Interface *iface, struct timespec *now);
/* ================================================== */
static int
add_interface(CNF_HwTsInterface *conf_iface)
{
int sock_fd, if_index, minpoll, phc_fd, req_hwts_flags, rx_filter;
struct ethtool_ts_info ts_info;
struct hwtstamp_config ts_config;
struct ifreq req;
int sock_fd, if_index, phc_fd, req_hwts_flags, rx_filter;
unsigned int i;
struct Interface *iface;
@@ -245,9 +240,15 @@ add_interface(CNF_HwTsInterface *conf_iface)
iface->tx_comp = conf_iface->tx_comp;
iface->rx_comp = conf_iface->rx_comp;
minpoll = CLAMP(MIN_PHC_POLL, conf_iface->minpoll, MAX_PHC_POLL);
iface->clock = HCL_CreateInstance(conf_iface->min_samples, conf_iface->max_samples,
UTI_Log2ToDouble(MAX(conf_iface->minpoll, MIN_PHC_POLL)),
conf_iface->precision);
UTI_Log2ToDouble(minpoll), conf_iface->precision);
iface->maxpoll = CLAMP(minpoll, conf_iface->maxpoll, MAX_PHC_POLL);
/* Do not schedule the first poll timeout here! The argument (interface) can
move until all interfaces are added. Wait for the first HW timestamp. */
iface->poll_timeout_id = 0;
LOG(LOGS_INFO, "Enabled HW timestamping %son %s",
ts_config.rx_filter == HWTSTAMP_FILTER_NONE ? "(TX only) " : "", iface->name);
@@ -412,8 +413,6 @@ NIO_Linux_Initialise(void)
/* Kernels before 4.7 ignore timestamping flags set in control messages */
permanent_ts_options = !SYS_Linux_CheckKernelVersion(4, 7);
monitored_socket = INVALID_SOCK_FD;
suspended_socket = INVALID_SOCK_FD;
dummy_rxts_socket = INVALID_SOCK_FD;
}
@@ -430,6 +429,7 @@ NIO_Linux_Finalise(void)
for (i = 0; i < ARR_GetSize(interfaces); i++) {
iface = ARR_GetElement(interfaces, i);
SCH_RemoveTimeout(iface->poll_timeout_id);
HCL_DestroyInstance(iface->clock);
close(iface->phc_fd);
}
@@ -439,6 +439,14 @@ NIO_Linux_Finalise(void)
/* ================================================== */
int
NIO_Linux_IsHwTsEnabled(void)
{
return ARR_GetSize(interfaces) > 0;
}
/* ================================================== */
int
NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events)
{
@@ -472,73 +480,6 @@ NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events)
/* ================================================== */
static void
resume_socket(int sock_fd)
{
if (monitored_socket == sock_fd)
monitored_socket = INVALID_SOCK_FD;
if (sock_fd == INVALID_SOCK_FD || sock_fd != suspended_socket)
return;
suspended_socket = INVALID_SOCK_FD;
SCH_SetFileHandlerEvent(sock_fd, SCH_FILE_INPUT, 1);
DEBUG_LOG("Resumed RX processing %s timeout fd=%d",
resume_timeout_id ? "before" : "on", sock_fd);
if (resume_timeout_id) {
SCH_RemoveTimeout(resume_timeout_id);
resume_timeout_id = 0;
}
}
/* ================================================== */
static void
resume_timeout(void *arg)
{
resume_timeout_id = 0;
resume_socket(suspended_socket);
}
/* ================================================== */
static void
suspend_socket(int sock_fd)
{
resume_socket(suspended_socket);
suspended_socket = sock_fd;
SCH_SetFileHandlerEvent(suspended_socket, SCH_FILE_INPUT, 0);
resume_timeout_id = SCH_AddTimeoutByDelay(RESUME_TIMEOUT, resume_timeout, NULL);
DEBUG_LOG("Suspended RX processing fd=%d", sock_fd);
}
/* ================================================== */
int
NIO_Linux_ProcessEvent(int sock_fd, int event)
{
if (sock_fd != monitored_socket)
return 0;
if (event == SCH_FILE_INPUT) {
suspend_socket(monitored_socket);
monitored_socket = INVALID_SOCK_FD;
/* Don't process the message yet */
return 1;
}
return 0;
}
/* ================================================== */
static struct Interface *
get_interface(int if_index)
{
@@ -558,29 +499,70 @@ get_interface(int if_index)
/* ================================================== */
static void
poll_timeout(void *arg)
{
struct Interface *iface = arg;
struct timespec now;
iface->poll_timeout_id = 0;
SCH_GetLastEventTime(&now, NULL, NULL);
poll_phc(iface, &now);
}
/* ================================================== */
static void
poll_phc(struct Interface *iface, struct timespec *now)
{
struct timespec sample_phc_ts, sample_sys_ts, sample_local_ts;
struct timespec phc_readings[PHC_READINGS][3];
double phc_err, local_err, interval;
int n_readings;
if (!HCL_NeedsNewSample(iface->clock, now))
return;
DEBUG_LOG("Polling PHC on %s%s",
iface->name, iface->poll_timeout_id != 0 ? " before timeout" : "");
n_readings = SYS_Linux_GetPHCReadings(iface->phc_fd, iface->phc_nocrossts,
&iface->phc_mode, PHC_READINGS, phc_readings);
/* Add timeout for the next poll in case no HW timestamp will be captured
between the minpoll and maxpoll. Separate reading of different PHCs to
avoid long intervals between handling I/O events. */
SCH_RemoveTimeout(iface->poll_timeout_id);
interval = UTI_Log2ToDouble(iface->maxpoll);
iface->poll_timeout_id = SCH_AddTimeoutInClass(interval, interval /
ARR_GetSize(interfaces) / 4, 0.1,
SCH_PhcPollClass, poll_timeout, iface);
if (n_readings <= 0)
return;
if (!HCL_ProcessReadings(iface->clock, n_readings, phc_readings,
&sample_phc_ts, &sample_sys_ts, &phc_err))
return;
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);
}
/* ================================================== */
static void
process_hw_timestamp(struct Interface *iface, struct timespec *hw_ts,
NTP_Local_Timestamp *local_ts, int rx_ntp_length, int family,
int l2_length)
{
struct timespec sample_phc_ts, sample_sys_ts, sample_local_ts, ts;
struct timespec phc_readings[PHC_READINGS][3];
double rx_correction, ts_delay, phc_err, local_err;
int n_readings;
double rx_correction = 0.0, ts_delay, local_err;
struct timespec ts;
if (HCL_NeedsNewSample(iface->clock, &local_ts->ts)) {
n_readings = SYS_Linux_GetPHCReadings(iface->phc_fd, iface->phc_nocrossts,
&iface->phc_mode, PHC_READINGS, phc_readings);
if (n_readings > 0 &&
HCL_ProcessReadings(iface->clock, n_readings, phc_readings,
&sample_phc_ts, &sample_sys_ts, &phc_err)) {
LCL_CookTime(&sample_sys_ts, &sample_local_ts, &local_err);
HCL_AccumulateSample(iface->clock, &sample_phc_ts, &sample_local_ts,
phc_err + local_err);
update_interface_speed(iface);
}
}
poll_phc(iface, &local_ts->ts);
/* We need to transpose RX timestamps as hardware timestamps are normally
preamble timestamps and RX timestamps in NTP are supposed to be trailer
@@ -618,6 +600,10 @@ process_hw_timestamp(struct Interface *iface, struct timespec *hw_ts,
local_ts->ts = ts;
local_ts->err = local_err;
local_ts->source = NTP_TS_HARDWARE;
local_ts->rx_duration = rx_correction;
/* Network correction needs to include the RX duration to avoid
asymmetric correction with asymmetric link speeds */
local_ts->net_correction = rx_correction;
}
/* ================================================== */
@@ -741,6 +727,7 @@ NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
{
struct Interface *iface;
int is_tx, ts_if_index, l2_length;
double c = 0.0;
is_tx = event == SCH_FILE_EXCEPTION;
iface = NULL;
@@ -758,11 +745,6 @@ NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
} else {
DEBUG_LOG("HW clock not found for interface %d", ts_if_index);
}
/* If a HW transmit timestamp was received, resume processing
of non-error messages on this socket */
if (is_tx)
resume_socket(local_addr->sock_fd);
}
if (local_ts->source == NTP_TS_DAEMON && !UTI_IsZeroTimespec(&message->timestamp.kernel) &&
@@ -806,7 +788,7 @@ NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
return 1;
}
if (!NIO_UnwrapMessage(message, local_addr->sock_fd))
if (!NIO_UnwrapMessage(message, local_addr->sock_fd, &c))
return 1;
if (message->length < NTP_HEADER_LENGTH || message->length > sizeof (NTP_Packet))
@@ -825,23 +807,9 @@ NIO_Linux_RequestTxTimestamp(SCK_Message *message, int sock_fd)
if (!ts_flags)
return;
/* If a HW transmit timestamp is requested on a client socket, monitor
events on the socket in order to avoid processing of a fast response
without the HW timestamp of the request */
if (ts_tx_flags & SOF_TIMESTAMPING_TX_HARDWARE && !NIO_IsServerSocket(sock_fd))
monitored_socket = sock_fd;
/* Check if TX timestamping is disabled on this socket */
if (permanent_ts_options || !NIO_IsServerSocket(sock_fd))
return;
message->timestamp.tx_flags = ts_tx_flags;
}
/* ================================================== */
void
NIO_Linux_NotifySocketClosing(int sock_fd)
{
resume_socket(sock_fd);
}

View File

@@ -33,15 +33,13 @@ extern void NIO_Linux_Initialise(void);
extern void NIO_Linux_Finalise(void);
extern int NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events);
extern int NIO_Linux_IsHwTsEnabled(void);
extern int NIO_Linux_ProcessEvent(int sock_fd, int event);
extern int NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events);
extern int NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *local_ts, int event);
extern void NIO_Linux_RequestTxTimestamp(SCK_Message *message, int sock_fd);
extern void NIO_Linux_NotifySocketClosing(int sock_fd);
#endif

View File

@@ -99,6 +99,9 @@ static int sock_fd;
/* Flag indicating if the MS-SNTP authentication is enabled */
static int enabled;
/* Flag limiting logging of connection error messages */
static int logged_connection_error;
/* ================================================== */
static void read_write_socket(int sock_fd, int event, void *anything);
@@ -134,6 +137,14 @@ open_socket(void)
sock_fd = SCK_OpenUnixStreamSocket(path, NULL, 0);
if (sock_fd < 0) {
sock_fd = INVALID_SOCK_FD;
/* Log an error only once before a successful exchange to avoid
flooding the system log */
if (!logged_connection_error) {
LOG(LOGS_ERR, "Could not connect to signd socket %s : %s", path, strerror(errno));
logged_connection_error = 1;
}
return 0;
}
@@ -160,6 +171,8 @@ process_response(SignInstance *inst)
return;
}
logged_connection_error = 0;
/* Check if the file descriptor is still valid */
if (!NIO_IsServerSocket(inst->local_addr.sock_fd)) {
DEBUG_LOG("Invalid NTP socket");

View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011-2012, 2014, 2016, 2020-2021
* Copyright (C) Miroslav Lichvar 2011-2012, 2014, 2016, 2020-2023
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -32,6 +32,7 @@
#include "sysincl.h"
#include "array.h"
#include "conf.h"
#include "ntp_sources.h"
#include "ntp_core.h"
#include "ntp_io.h"
@@ -58,12 +59,17 @@ typedef struct {
NCR_Instance data; /* Data for the protocol engine for this source */
char *name; /* Name of the source as it was specified
(may be an IP address) */
IPAddr resolved_addr; /* Address resolved from the name, which can be
different from remote_addr (e.g. NTS-KE) */
int family; /* IP family of acceptable resolved addresses
(IPADDR_UNSPEC if any) */
int pool_id; /* ID of the pool from which was this source
added or INVALID_POOL */
int tentative; /* Flag indicating there was no valid response
received from the source yet */
uint32_t conf_id; /* Configuration ID, which can be shared with
different sources in case of a pool */
double last_resolving; /* Time of last name resolving (monotonic) */
} SourceRecord;
/* Hash table of SourceRecord, its size is a power of two and it's never
@@ -94,8 +100,13 @@ struct UnresolvedSource {
int pool_id;
/* Name to be resolved */
char *name;
/* Address family to filter resolved addresses */
int family;
/* Flag indicating addresses should be used in a random order */
int random_order;
/* Flag indicating current address should be replaced only if it is
no longer returned by the resolver */
int refreshment;
/* Next unresolved source in the list */
struct UnresolvedSource *next;
};
@@ -103,7 +114,7 @@ struct UnresolvedSource {
#define RESOLVE_INTERVAL_UNIT 7
#define MIN_RESOLVE_INTERVAL 2
#define MAX_RESOLVE_INTERVAL 9
#define MIN_REPLACEMENT_INTERVAL 8
#define MAX_REPLACEMENT_INTERVAL 9
static struct UnresolvedSource *unresolved_sources = NULL;
static int resolving_interval = 0;
@@ -184,6 +195,7 @@ void
NSR_Initialise(void)
{
n_sources = 0;
resolving_id = 0;
initialised = 1;
records = ARR_CreateInstance(sizeof (SourceRecord));
@@ -206,6 +218,7 @@ NSR_Finalise(void)
ARR_DestroyInstance(records);
ARR_DestroyInstance(pools);
SCH_RemoveTimeout(resolving_id);
while (unresolved_sources)
remove_unresolved_source(unresolved_sources);
@@ -317,9 +330,34 @@ rehash_records(void)
/* ================================================== */
static void
log_source(SourceRecord *record, int addition, int once_per_pool)
{
int pool, log_addr;
char *ip_str;
if (once_per_pool && record->pool_id != INVALID_POOL) {
if (get_pool(record->pool_id)->sources > 1)
return;
pool = 1;
log_addr = 0;
} else {
ip_str = UTI_IPToString(&record->remote_addr->ip_addr);
pool = 0;
log_addr = strcmp(record->name, ip_str) != 0;
}
LOG(LOG_GetContextSeverity(LOGC_Command | LOGC_SourceFile), "%s %s %s%s%s%s",
addition ? "Added" : "Removed", pool ? "pool" : "source",
log_addr ? ip_str : record->name,
log_addr ? " (" : "", log_addr ? record->name : "", log_addr ? ")" : "");
}
/* ================================================== */
/* Procedure to add a new source */
static NSR_Status
add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type,
add_source(NTP_Remote_Address *remote_addr, char *name, int family, NTP_Source_Type type,
SourceParameters *params, int pool_id, uint32_t conf_id)
{
SourceRecord *record;
@@ -356,9 +394,12 @@ add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type,
record->name = Strdup(name ? name : UTI_IPToString(&remote_addr->ip_addr));
record->data = NCR_CreateInstance(remote_addr, type, params, record->name);
record->remote_addr = NCR_GetRemoteAddress(record->data);
record->resolved_addr = remote_addr->ip_addr;
record->family = family;
record->pool_id = pool_id;
record->tentative = 1;
record->conf_id = conf_id;
record->last_resolving = SCH_GetLastEventMonoTime();
record_lock = 0;
@@ -371,6 +412,8 @@ add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type,
if (auto_start_sources && UTI_IsIPReal(&remote_addr->ip_addr))
NCR_StartInstance(record->data);
log_source(record, 1, 1);
/* The new instance is allowed to change its address immediately */
handle_saved_address_update();
@@ -405,6 +448,8 @@ change_source_address(NTP_Remote_Address *old_addr, NTP_Remote_Address *new_addr
record = get_record(slot1);
NCR_ChangeRemoteAddress(record->data, new_addr, !replacement);
if (replacement)
record->resolved_addr = new_addr->ip_addr;
if (record->remote_addr != NCR_GetRemoteAddress(record->data) ||
UTI_CompareIPs(&record->remote_addr->ip_addr, &new_addr->ip_addr, NULL) != 0)
@@ -488,7 +533,21 @@ process_resolved_name(struct UnresolvedSource *us, IPAddr *ip_addrs, int n_addrs
NTP_Remote_Address old_addr, new_addr;
SourceRecord *record;
unsigned short first = 0;
int i, j;
int i, j, slot;
/* Keep using the current address if it is being refreshed and it is
still included in the resolved addresses */
if (us->refreshment) {
assert(us->pool_id == INVALID_POOL);
for (i = 0; i < n_addrs; i++) {
if (find_slot2(&us->address, &slot) == 2 &&
UTI_CompareIPs(&get_record(slot)->resolved_addr, &ip_addrs[i], NULL) == 0) {
DEBUG_LOG("%s still fresh", UTI_IPToString(&us->address.ip_addr));
return;
}
}
}
if (us->random_order)
UTI_GetRandomBytes(&first, sizeof (first));
@@ -498,6 +557,10 @@ process_resolved_name(struct UnresolvedSource *us, IPAddr *ip_addrs, int n_addrs
DEBUG_LOG("(%d) %s", i + 1, UTI_IPToString(&new_addr.ip_addr));
/* Skip addresses not from the requested family */
if (us->family != IPADDR_UNSPEC && us->family != new_addr.ip_addr.family)
continue;
if (us->pool_id != INVALID_POOL) {
/* In the pool resolving mode, try to replace a source from
the pool which does not have a real address yet */
@@ -575,13 +638,16 @@ name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *any
next = us->next;
/* Don't repeat the resolving if it (permanently) failed, it was a
replacement of a real address, or all addresses are already resolved */
if (status == DNS_Failure || UTI_IsIPReal(&us->address.ip_addr) || is_resolved(us))
replacement of a real address, a refreshment, or all addresses are
already resolved */
if (status == DNS_Failure || UTI_IsIPReal(&us->address.ip_addr) ||
us->refreshment || is_resolved(us))
remove_unresolved_source(us);
/* If a restart was requested and this was the last source in the list,
start with the first source again (if there still is one) */
if (!next && resolving_restart) {
DEBUG_LOG("Restarting");
next = unresolved_sources;
resolving_restart = 0;
}
@@ -646,11 +712,15 @@ static void
append_unresolved_source(struct UnresolvedSource *us)
{
struct UnresolvedSource **i;
int n;
for (i = &unresolved_sources; *i; i = &(*i)->next)
for (i = &unresolved_sources, n = 0; *i; i = &(*i)->next, n++)
;
*i = us;
us->next = NULL;
DEBUG_LOG("Added unresolved source #%d pool_id=%d random=%d refresh=%d",
n + 1, us->pool_id, us->random_order, us->refreshment);
}
/* ================================================== */
@@ -700,8 +770,19 @@ static int get_unused_pool_id(void)
static uint32_t
get_next_conf_id(uint32_t *conf_id)
{
SourceRecord *record;
unsigned int i;
again:
last_conf_id++;
/* Make sure the ID is not already used (after 32-bit wraparound) */
for (i = 0; i < ARR_GetSize(records); i++) {
record = get_record(i);
if (record->remote_addr && record->conf_id == last_conf_id)
goto again;
}
if (conf_id)
*conf_id = last_conf_id;
@@ -714,14 +795,14 @@ NSR_Status
NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
SourceParameters *params, uint32_t *conf_id)
{
return add_source(remote_addr, NULL, type, params, INVALID_POOL,
return add_source(remote_addr, NULL, IPADDR_UNSPEC, type, params, INVALID_POOL,
get_next_conf_id(conf_id));
}
/* ================================================== */
NSR_Status
NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
NSR_AddSourceByName(char *name, int family, int port, int pool, NTP_Source_Type type,
SourceParameters *params, uint32_t *conf_id)
{
struct UnresolvedSource *us;
@@ -733,7 +814,9 @@ NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
/* 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 add_source(&remote_addr, name, type, params, INVALID_POOL,
if (family != IPADDR_UNSPEC && family != remote_addr.ip_addr.family)
return NSR_InvalidAF;
return add_source(&remote_addr, name, IPADDR_UNSPEC, type, params, INVALID_POOL,
get_next_conf_id(conf_id));
}
@@ -745,7 +828,9 @@ NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
us = MallocNew(struct UnresolvedSource);
us->name = Strdup(name);
us->family = family;
us->random_order = 0;
us->refreshment = 0;
remote_addr.ip_addr.family = IPADDR_ID;
remote_addr.ip_addr.addr.id = ++last_address_id;
@@ -780,7 +865,7 @@ NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
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, cid) != NSR_Success)
if (add_source(&remote_addr, name, family, type, params, us->pool_id, cid) != NSR_Success)
return NSR_TooManySources;
}
@@ -789,6 +874,31 @@ NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
/* ================================================== */
const char *
NSR_StatusToString(NSR_Status status)
{
switch (status) {
case NSR_Success:
return "Success";
case NSR_NoSuchSource:
return "No such source";
case NSR_AlreadyInUse:
return "Already in use";
case NSR_TooManySources:
return "Too many sources";
case NSR_InvalidAF:
return "Invalid address";
case NSR_InvalidName:
return "Invalid name";
case NSR_UnresolvedName:
return "Unresolved name";
default:
return "?";
}
}
/* ================================================== */
void
NSR_SetSourceResolvingEndHandler(NSR_SourceResolvingEndHandler handler)
{
@@ -884,6 +994,7 @@ NSR_RemoveSource(IPAddr *address)
if (find_slot(address, &slot) == 0)
return NSR_NoSuchSource;
log_source(get_record(slot), 0, 0);
clean_source_record(get_record(slot));
/* Rehash the table to make sure there are no broken probe sequences.
@@ -906,6 +1017,7 @@ NSR_RemoveSourcesById(uint32_t conf_id)
record = get_record(i);
if (!record->remote_addr || record->conf_id != conf_id)
continue;
log_source(record, 0, 1);
clean_source_record(record);
}
@@ -933,25 +1045,32 @@ NSR_RemoveAllSources(void)
/* ================================================== */
static void
resolve_source_replacement(SourceRecord *record)
resolve_source_replacement(SourceRecord *record, int refreshment)
{
struct UnresolvedSource *us;
DEBUG_LOG("trying to replace %s (%s)",
DEBUG_LOG("%s %s (%s)", refreshment ? "refreshing" : "trying to replace",
UTI_IPToString(&record->remote_addr->ip_addr), record->name);
record->last_resolving = SCH_GetLastEventMonoTime();
us = MallocNew(struct UnresolvedSource);
us->name = Strdup(record->name);
/* If there never was a valid reply from this source (e.g. it was a bad
replacement), ignore the order of addresses from the resolver to not get
stuck to a pair of addresses if the order doesn't change, or a group of
IPv4/IPv6 addresses if the resolver prefers inaccessible IP family */
us->random_order = record->tentative;
us->family = record->family;
/* Ignore the order of addresses from the resolver to not get
stuck with a pair of unreachable or otherwise unusable servers
(e.g. falsetickers) in case the order doesn't change, or a group
of servers if they are ordered by IP family */
us->random_order = 1;
us->refreshment = refreshment;
us->pool_id = INVALID_POOL;
us->address = *record->remote_addr;
append_unresolved_source(us);
NSR_ResolveSources();
/* Don't restart resolving round if already running */
if (!resolving_source)
NSR_ResolveSources();
}
/* ================================================== */
@@ -959,11 +1078,11 @@ resolve_source_replacement(SourceRecord *record)
void
NSR_HandleBadSource(IPAddr *address)
{
static struct timespec last_replacement;
struct timespec now;
static double next_replacement = 0.0;
SourceRecord *record;
IPAddr ip_addr;
double diff;
uint32_t rnd;
double now;
int slot;
if (!find_slot(address, &slot))
@@ -978,15 +1097,56 @@ NSR_HandleBadSource(IPAddr *address)
return;
/* Don't resolve names too frequently */
SCH_GetLastEventTime(NULL, NULL, &now);
diff = UTI_DiffTimespecsToDouble(&now, &last_replacement);
if (fabs(diff) < RESOLVE_INTERVAL_UNIT * (1 << MIN_REPLACEMENT_INTERVAL)) {
now = SCH_GetLastEventMonoTime();
if (now < next_replacement) {
DEBUG_LOG("replacement postponed");
return;
}
last_replacement = now;
resolve_source_replacement(record);
UTI_GetRandomBytes(&rnd, sizeof (rnd));
next_replacement = now + ((double)rnd / (uint32_t)-1) *
(RESOLVE_INTERVAL_UNIT * (1 << MAX_REPLACEMENT_INTERVAL));
resolve_source_replacement(record, 0);
}
/* ================================================== */
static void
maybe_refresh_source(void)
{
static double last_refreshment = 0.0;
SourceRecord *record, *oldest_record;
int i, min_interval;
double now;
min_interval = CNF_GetRefresh();
now = SCH_GetLastEventMonoTime();
if (min_interval <= 0 || now < last_refreshment + min_interval)
return;
last_refreshment = now;
for (i = 0, oldest_record = NULL; i < ARR_GetSize(records); i++) {
record = get_record(i);
if (!record->remote_addr || UTI_IsStringIP(record->name))
continue;
if (!oldest_record || oldest_record->last_resolving > record->last_resolving)
oldest_record = record;
}
if (!oldest_record)
return;
/* Check if the name wasn't already resolved in the last interval */
if (now < oldest_record->last_resolving + min_interval) {
last_refreshment = oldest_record->last_resolving;
return;
}
resolve_source_replacement(oldest_record, 1);
}
/* ================================================== */
@@ -1002,7 +1162,7 @@ NSR_RefreshAddresses(void)
if (!record->remote_addr)
continue;
resolve_source_replacement(record);
resolve_source_replacement(record, 1);
}
}
@@ -1128,6 +1288,8 @@ NSR_ProcessRx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
remove_pool_sources(record->pool_id, 1, 0);
}
}
maybe_refresh_source();
} else {
NCR_ProcessRxUnknown(remote_addr, local_addr, rx_ts, message, length);
}
@@ -1303,6 +1465,20 @@ NSR_ModifyMinstratum(IPAddr *address, int new_min_stratum)
/* ================================================== */
int
NSR_ModifyOffset(IPAddr *address, double new_offset)
{
int slot;
if (!find_slot(address, &slot))
return 0;
NCR_ModifyOffset(get_record(slot)->data, new_offset);
return 1;
}
/* ================================================== */
int
NSR_ModifyPolltarget(IPAddr *address, int new_poll_target)
{

View File

@@ -55,11 +55,16 @@ extern NSR_Status NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type
/* Procedure to add a new server, peer source, or pool of servers specified by
name instead of address. The name is resolved in exponentially increasing
intervals until it succeeds or fails with a non-temporary error. If the
name is an address, it is equivalent to NSR_AddSource(). */
extern NSR_Status NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
intervals until it succeeds or fails with a non-temporary error. The
specified family filters resolved addresses. If the name is an address
and its family does not conflict with the specified family, it is equivalent
to NSR_AddSource(). */
extern NSR_Status NSR_AddSourceByName(char *name, int family, int port, int pool,
NTP_Source_Type type,
SourceParameters *params, uint32_t *conf_id);
extern const char *NSR_StatusToString(NSR_Status status);
/* Function type for handlers to be called back when an attempt
* (possibly unsuccessful) to resolve unresolved sources ends */
typedef void (*NSR_SourceResolvingEndHandler)(void);
@@ -135,6 +140,8 @@ extern int NSR_ModifyMaxdelaydevratio(IPAddr *address, double new_max_delay_rati
extern int NSR_ModifyMinstratum(IPAddr *address, int new_min_stratum);
extern int NSR_ModifyOffset(IPAddr *address, double new_offset);
extern int NSR_ModifyPolltarget(IPAddr *address, int new_poll_target);
extern int NSR_InitiateSampleBurst(int n_good_samples, int n_total_samples, IPAddr *mask, IPAddr *address);

View File

@@ -102,16 +102,22 @@ static int
prepare_request(NKC_Instance inst)
{
NKSN_Instance session = inst->session;
uint16_t datum;
uint16_t data[2];
int length;
NKSN_BeginMessage(session);
datum = htons(NKE_NEXT_PROTOCOL_NTPV4);
if (!NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, &datum, sizeof (datum)))
data[0] = htons(NKE_NEXT_PROTOCOL_NTPV4);
if (!NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, data, sizeof (data[0])))
return 0;
datum = htons(AEAD_AES_SIV_CMAC_256);
if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, &datum, sizeof (datum)))
length = 0;
if (SIV_GetKeyLength(AEAD_AES_128_GCM_SIV) > 0)
data[length++] = htons(AEAD_AES_128_GCM_SIV);
if (SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256) > 0)
data[length++] = htons(AEAD_AES_SIV_CMAC_256);
if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, data,
length * sizeof (data[0])))
return 0;
if (!NKSN_EndMessage(session))
@@ -159,12 +165,14 @@ process_response(NKC_Instance inst)
next_protocol = NKE_NEXT_PROTOCOL_NTPV4;
break;
case NKE_RECORD_AEAD_ALGORITHM:
if (length != 2 || ntohs(data[0]) != AEAD_AES_SIV_CMAC_256) {
if (length != 2 || (ntohs(data[0]) != AEAD_AES_SIV_CMAC_256 &&
ntohs(data[0]) != AEAD_AES_128_GCM_SIV) ||
SIV_GetKeyLength(ntohs(data[0])) <= 0) {
DEBUG_LOG("Unexpected NTS-KE AEAD algorithm");
error = 1;
break;
}
aead_algorithm = AEAD_AES_SIV_CMAC_256;
aead_algorithm = ntohs(data[0]);
inst->context.algorithm = aead_algorithm;
break;
case NKE_RECORD_ERROR:
@@ -236,7 +244,7 @@ process_response(NKC_Instance inst)
if (error || inst->num_cookies == 0 ||
next_protocol != NKE_NEXT_PROTOCOL_NTPV4 ||
aead_algorithm != AEAD_AES_SIV_CMAC_256)
aead_algorithm < 0)
return 0;
return 1;
@@ -370,6 +378,13 @@ NKC_Start(NKC_Instance inst)
return 0;
}
/* Don't try to connect if missing the algorithm which all servers
are required to support */
if (SIV_GetKeyLength(AEAD_AES_SIV_CMAC_256) <= 0) {
LOG(LOGS_ERR, "Missing AES-SIV-CMAC-256");
return 0;
}
/* Follow the bindacqaddress and bindacqdevice settings */
CNF_GetBindAcquisitionAddress(inst->address.ip_addr.family, &local_addr.ip_addr);
local_addr.port = 0;

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2020
* Copyright (C) Miroslav Lichvar 2020, 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
@@ -47,31 +47,33 @@
#define SERVER_TIMEOUT 2.0
#define SERVER_COOKIE_SIV AEAD_AES_SIV_CMAC_256
#define SERVER_COOKIE_NONCE_LENGTH 16
#define MAX_COOKIE_NONCE_LENGTH 16
#define KEY_ID_INDEX_BITS 2
#define MAX_SERVER_KEYS (1U << KEY_ID_INDEX_BITS)
#define FUTURE_KEYS 1
#define DUMP_FILENAME "ntskeys"
#define DUMP_IDENTIFIER "NKS0\n"
#define DUMP_IDENTIFIER "NKS1\n"
#define OLD_DUMP_IDENTIFIER "NKS0\n"
#define INVALID_SOCK_FD (-7)
typedef struct {
uint32_t key_id;
unsigned char nonce[SERVER_COOKIE_NONCE_LENGTH];
} ServerCookieHeader;
typedef struct {
uint32_t id;
unsigned char key[SIV_MAX_KEY_LENGTH];
SIV_Algorithm siv_algorithm;
SIV_Instance siv;
int nonce_length;
} ServerKey;
typedef struct {
uint32_t key_id;
uint32_t siv_algorithm;
unsigned char key[SIV_MAX_KEY_LENGTH];
IPAddr client_addr;
uint16_t client_port;
@@ -148,12 +150,30 @@ handle_client(int sock_fd, IPSockAddr *addr)
/* ================================================== */
static void
update_key_siv(ServerKey *key, SIV_Algorithm algorithm)
{
if (!key->siv || key->siv_algorithm != algorithm) {
if (key->siv)
SIV_DestroyInstance(key->siv);
key->siv_algorithm = algorithm;
key->siv = SIV_CreateInstance(algorithm);
key->nonce_length = MIN(SIV_GetMaxNonceLength(key->siv), MAX_COOKIE_NONCE_LENGTH);
}
if (!key->siv || !SIV_SetKey(key->siv, key->key, SIV_GetKeyLength(key->siv_algorithm)))
LOG_FATAL("Could not set SIV key");
}
/* ================================================== */
static void
handle_helper_request(int fd, int event, void *arg)
{
SCK_Message *message;
HelperRequest *req;
IPSockAddr client_addr;
ServerKey *key;
int sock_fd;
/* Receive the helper request with the NTS-KE session socket.
@@ -181,16 +201,14 @@ handle_helper_request(int fd, int event, void *arg)
req = message->data;
/* Extract the current server key and client address from the request */
server_keys[current_server_key].id = ntohl(req->key_id);
assert(sizeof (server_keys[current_server_key].key) == sizeof (req->key));
memcpy(server_keys[current_server_key].key, req->key,
sizeof (server_keys[current_server_key].key));
key = &server_keys[current_server_key];
key->id = ntohl(req->key_id);
assert(sizeof (key->key) == sizeof (req->key));
memcpy(key->key, req->key, sizeof (key->key));
UTI_IPNetworkToHost(&req->client_addr, &client_addr.ip_addr);
client_addr.port = ntohs(req->client_port);
if (!SIV_SetKey(server_keys[current_server_key].siv, server_keys[current_server_key].key,
SIV_GetKeyLength(SERVER_COOKIE_SIV)))
LOG_FATAL("Could not set SIV key");
update_key_siv(key, ntohl(req->siv_algorithm));
if (!handle_client(sock_fd, &client_addr)) {
SCK_CloseSocket(sock_fd);
@@ -224,7 +242,7 @@ accept_connection(int listening_fd, int event, void *arg)
SCH_GetLastEventTime(&now, NULL, NULL);
log_index = CLG_LogServiceAccess(CLG_NTSKE, &addr.ip_addr, &now);
if (log_index >= 0 && CLG_LimitServiceRate(CLG_NTSKE, log_index)) {
if (log_index >= 0 && CLG_LimitServiceRate(CLG_NTSKE, log_index) != CLG_PASS) {
DEBUG_LOG("Rejected connection from %s (%s)",
UTI_IPSockAddrToString(&addr), "rate limit");
SCK_CloseSocket(sock_fd);
@@ -240,6 +258,7 @@ accept_connection(int listening_fd, int event, void *arg)
/* Include the current server key and client address in the request */
req.key_id = htonl(server_keys[current_server_key].id);
req.siv_algorithm = htonl(server_keys[current_server_key].siv_algorithm);
assert(sizeof (req.key) == sizeof (server_keys[current_server_key].key));
memcpy(req.key, server_keys[current_server_key].key, sizeof (req.key));
UTI_IPHostToNetwork(&addr.ip_addr, &req.client_addr);
@@ -427,8 +446,9 @@ process_request(NKSN_Instance session)
for (i = 0; i < MIN(length, sizeof (data)) / 2; i++) {
aead_algorithm_values++;
if (ntohs(data[i]) == AEAD_AES_SIV_CMAC_256)
aead_algorithm = AEAD_AES_SIV_CMAC_256;
/* Use the first supported algorithm */
if (aead_algorithm < 0 && SIV_GetKeyLength(ntohs(data[i])) > 0)
aead_algorithm = ntohs(data[i]);
}
break;
case NKE_RECORD_ERROR:
@@ -470,28 +490,38 @@ handle_message(void *arg)
static void
generate_key(int index)
{
SIV_Algorithm algorithm;
ServerKey *key;
int key_length;
if (index < 0 || index >= MAX_SERVER_KEYS)
assert(0);
key_length = SIV_GetKeyLength(SERVER_COOKIE_SIV);
if (key_length > sizeof (server_keys[index].key))
/* Prefer AES-128-GCM-SIV if available. Note that if older keys loaded
from ntsdumpdir use a different algorithm, responding to NTP requests
with cookies encrypted with those keys will not work if the new algorithm
produces longer cookies (i.e. response would be longer than request).
Switching from AES-SIV-CMAC-256 to AES-128-GCM-SIV is ok. */
algorithm = SIV_GetKeyLength(AEAD_AES_128_GCM_SIV) > 0 ?
AEAD_AES_128_GCM_SIV : AEAD_AES_SIV_CMAC_256;
key = &server_keys[index];
key_length = SIV_GetKeyLength(algorithm);
if (key_length > sizeof (key->key))
assert(0);
UTI_GetRandomBytesUrandom(server_keys[index].key, key_length);
if (!server_keys[index].siv ||
!SIV_SetKey(server_keys[index].siv, server_keys[index].key, key_length))
LOG_FATAL("Could not set SIV key");
UTI_GetRandomBytes(&server_keys[index].id, sizeof (server_keys[index].id));
UTI_GetRandomBytesUrandom(key->key, key_length);
memset(key->key + key_length, 0, sizeof (key->key) - key_length);
UTI_GetRandomBytes(&key->id, sizeof (key->id));
/* Encode the index in the lowest bits of the ID */
server_keys[index].id &= -1U << KEY_ID_INDEX_BITS;
server_keys[index].id |= index;
key->id &= -1U << KEY_ID_INDEX_BITS;
key->id |= index;
DEBUG_LOG("Generated server key %"PRIX32, server_keys[index].id);
update_key_siv(key, algorithm);
DEBUG_LOG("Generated key %08"PRIX32" (%d)", key->id, (int)key->siv_algorithm);
last_server_key_ts = SCH_GetLastEventMonoTime();
}
@@ -519,18 +549,19 @@ save_keys(void)
if (!f)
return;
key_length = SIV_GetKeyLength(SERVER_COOKIE_SIV);
last_key_age = SCH_GetLastEventMonoTime() - last_server_key_ts;
if (fprintf(f, "%s%d %.1f\n", DUMP_IDENTIFIER, SERVER_COOKIE_SIV, last_key_age) < 0)
if (fprintf(f, "%s%.1f\n", DUMP_IDENTIFIER, last_key_age) < 0)
goto error;
for (i = 0; i < MAX_SERVER_KEYS; i++) {
index = (current_server_key + i + 1 + FUTURE_KEYS) % MAX_SERVER_KEYS;
key_length = SIV_GetKeyLength(server_keys[index].siv_algorithm);
if (key_length > sizeof (server_keys[index].key) ||
!UTI_BytesToHex(server_keys[index].key, key_length, buf, sizeof (buf)) ||
fprintf(f, "%08"PRIX32" %s\n", server_keys[index].id, buf) < 0)
fprintf(f, "%08"PRIX32" %s %d\n", server_keys[index].id, buf,
(int)server_keys[index].siv_algorithm) < 0)
goto error;
}
@@ -545,7 +576,7 @@ save_keys(void)
return;
error:
DEBUG_LOG("Could not %s server keys", "save");
LOG(LOGS_ERR, "Could not %s %s", "save", "server NTS keys");
fclose(f);
if (!UTI_RemoveFile(dump_dir, DUMP_FILENAME, NULL))
@@ -554,17 +585,16 @@ error:
/* ================================================== */
#define MAX_WORDS 2
#define MAX_WORDS 3
static int
load_keys(void)
{
int i, index, key_length, algorithm = 0, old_ver;
char *dump_dir, line[1024], *words[MAX_WORDS];
unsigned char key[SIV_MAX_KEY_LENGTH];
int i, index, key_length, algorithm;
ServerKey new_keys[MAX_SERVER_KEYS];
double key_age;
FILE *f;
uint32_t id;
dump_dir = CNF_GetNtsDumpDir();
if (!dump_dir)
@@ -574,43 +604,58 @@ load_keys(void)
if (!f)
return 0;
if (!fgets(line, sizeof (line), f) || strcmp(line, DUMP_IDENTIFIER) != 0 ||
!fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 2 ||
sscanf(words[0], "%d", &algorithm) != 1 || algorithm != SERVER_COOKIE_SIV ||
sscanf(words[1], "%lf", &key_age) != 1)
if (!fgets(line, sizeof (line), f) ||
(strcmp(line, DUMP_IDENTIFIER) != 0 && strcmp(line, OLD_DUMP_IDENTIFIER) != 0))
goto error;
key_length = SIV_GetKeyLength(SERVER_COOKIE_SIV);
last_server_key_ts = SCH_GetLastEventMonoTime() - MAX(key_age, 0.0);
old_ver = strcmp(line, DUMP_IDENTIFIER) != 0;
if (!fgets(line, sizeof (line), f) ||
UTI_SplitString(line, words, MAX_WORDS) != (old_ver ? 2 : 1) ||
(old_ver && sscanf(words[0], "%d", &algorithm) != 1) ||
sscanf(words[old_ver ? 1 : 0], "%lf", &key_age) != 1)
goto error;
for (i = 0; i < MAX_SERVER_KEYS && fgets(line, sizeof (line), f); i++) {
if (UTI_SplitString(line, words, MAX_WORDS) != 2 ||
sscanf(words[0], "%"PRIX32, &id) != 1)
if (UTI_SplitString(line, words, MAX_WORDS) != (old_ver ? 2 : 3) ||
sscanf(words[0], "%"PRIX32, &new_keys[i].id) != 1 ||
(!old_ver && sscanf(words[2], "%d", &algorithm) != 1))
goto error;
if (UTI_HexToBytes(words[1], key, sizeof (key)) != key_length)
new_keys[i].siv_algorithm = algorithm;
key_length = SIV_GetKeyLength(algorithm);
if ((i > 0 && (new_keys[i].id - new_keys[i - 1].id) % MAX_SERVER_KEYS != 1) ||
key_length <= 0 ||
UTI_HexToBytes(words[1], new_keys[i].key, sizeof (new_keys[i].key)) != key_length)
goto error;
index = id % MAX_SERVER_KEYS;
server_keys[index].id = id;
assert(sizeof (server_keys[index].key) == sizeof (key));
memcpy(server_keys[index].key, key, key_length);
if (!SIV_SetKey(server_keys[index].siv, server_keys[index].key, key_length))
LOG_FATAL("Could not set SIV key");
DEBUG_LOG("Loaded key %"PRIX32, id);
current_server_key = (index + MAX_SERVER_KEYS - FUTURE_KEYS) % MAX_SERVER_KEYS;
memset(new_keys[i].key + key_length, 0, sizeof (new_keys[i].key) - key_length);
}
if (i < MAX_SERVER_KEYS)
goto error;
for (i = 0; i < MAX_SERVER_KEYS; i++) {
index = new_keys[i].id % MAX_SERVER_KEYS;
server_keys[index].id = new_keys[i].id;
memcpy(server_keys[index].key, new_keys[i].key, sizeof (server_keys[index].key));
update_key_siv(&server_keys[index], new_keys[i].siv_algorithm);
DEBUG_LOG("Loaded key %08"PRIX32" (%d)",
server_keys[index].id, (int)server_keys[index].siv_algorithm);
}
current_server_key = (index + MAX_SERVER_KEYS - FUTURE_KEYS) % MAX_SERVER_KEYS;
last_server_key_ts = SCH_GetLastEventMonoTime() - MAX(key_age, 0.0);
fclose(f);
LOG(LOGS_INFO, "Loaded %s", "server NTS keys");
return 1;
error:
DEBUG_LOG("Could not %s server keys", "load");
LOG(LOGS_ERR, "Could not %s %s", "load", "server NTS keys");
fclose(f);
return 0;
@@ -640,6 +685,8 @@ run_helper(uid_t uid, gid_t gid, int scfilter_level)
DEBUG_LOG("Helper started");
SCK_CloseReusableSockets();
/* Suppress a log message about disabled clock control */
log_severity = LOG_GetMinSeverity();
LOG_SetMinSeverity(LOGS_ERR);
@@ -759,7 +806,7 @@ NKS_Initialise(void)
/* Generate random keys, even if they will be replaced by reloaded keys,
or unused (in the helper) */
for (i = 0; i < MAX_SERVER_KEYS; i++) {
server_keys[i].siv = SIV_CreateInstance(SERVER_COOKIE_SIV);
server_keys[i].siv = NULL;
generate_key(i);
}
@@ -779,6 +826,11 @@ NKS_Initialise(void)
key_delay = key_rotation_interval - (SCH_GetLastEventMonoTime() - last_server_key_ts);
SCH_AddTimeoutByDelay(MAX(key_delay, 0.0), key_timeout, NULL);
}
/* Warn if keys are not saved, which can cause a flood of requests
after server restart */
if (!CNF_GetNtsDumpDir())
LOG(LOGS_WARN, "No ntsdumpdir to save server keys");
}
initialised = 1;
@@ -852,7 +904,7 @@ NKS_ReloadKeys(void)
int
NKS_GenerateCookie(NKE_Context *context, NKE_Cookie *cookie)
{
unsigned char plaintext[2 * NKE_MAX_KEY_LENGTH], *ciphertext;
unsigned char *nonce, plaintext[2 * NKE_MAX_KEY_LENGTH], *ciphertext;
int plaintext_length, tag_length;
ServerCookieHeader *header;
ServerKey *key;
@@ -862,14 +914,12 @@ NKS_GenerateCookie(NKE_Context *context, NKE_Cookie *cookie)
return 0;
}
/* The algorithm is hardcoded for now */
if (context->algorithm != AEAD_AES_SIV_CMAC_256) {
DEBUG_LOG("Unexpected SIV algorithm");
return 0;
}
/* The AEAD ID is not encoded in the cookie. It is implied from the key
length (as long as only algorithms with different key lengths are
supported). */
if (context->c2s.length < 0 || context->c2s.length > NKE_MAX_KEY_LENGTH ||
context->s2c.length < 0 || context->s2c.length > NKE_MAX_KEY_LENGTH) {
context->s2c.length != context->c2s.length) {
DEBUG_LOG("Invalid key length");
return 0;
}
@@ -879,7 +929,11 @@ NKS_GenerateCookie(NKE_Context *context, NKE_Cookie *cookie)
header = (ServerCookieHeader *)cookie->cookie;
header->key_id = htonl(key->id);
UTI_GetRandomBytes(header->nonce, sizeof (header->nonce));
nonce = cookie->cookie + sizeof (*header);
if (key->nonce_length > sizeof (cookie->cookie) - sizeof (*header))
assert(0);
UTI_GetRandomBytes(nonce, key->nonce_length);
plaintext_length = context->c2s.length + context->s2c.length;
assert(plaintext_length <= sizeof (plaintext));
@@ -887,11 +941,11 @@ NKS_GenerateCookie(NKE_Context *context, NKE_Cookie *cookie)
memcpy(plaintext + context->c2s.length, context->s2c.key, context->s2c.length);
tag_length = SIV_GetTagLength(key->siv);
cookie->length = sizeof (*header) + plaintext_length + tag_length;
cookie->length = sizeof (*header) + key->nonce_length + plaintext_length + tag_length;
assert(cookie->length <= sizeof (cookie->cookie));
ciphertext = cookie->cookie + sizeof (*header);
ciphertext = cookie->cookie + sizeof (*header) + key->nonce_length;
if (!SIV_Encrypt(key->siv, header->nonce, sizeof (header->nonce),
if (!SIV_Encrypt(key->siv, nonce, key->nonce_length,
"", 0,
plaintext, plaintext_length,
ciphertext, plaintext_length + tag_length)) {
@@ -907,7 +961,7 @@ NKS_GenerateCookie(NKE_Context *context, NKE_Cookie *cookie)
int
NKS_DecodeCookie(NKE_Cookie *cookie, NKE_Context *context)
{
unsigned char plaintext[2 * NKE_MAX_KEY_LENGTH], *ciphertext;
unsigned char *nonce, plaintext[2 * NKE_MAX_KEY_LENGTH], *ciphertext;
int ciphertext_length, plaintext_length, tag_length;
ServerCookieHeader *header;
ServerKey *key;
@@ -924,8 +978,6 @@ NKS_DecodeCookie(NKE_Cookie *cookie, NKE_Context *context)
}
header = (ServerCookieHeader *)cookie->cookie;
ciphertext = cookie->cookie + sizeof (*header);
ciphertext_length = cookie->length - sizeof (*header);
key_id = ntohl(header->key_id);
key = &server_keys[key_id % MAX_SERVER_KEYS];
@@ -935,18 +987,23 @@ NKS_DecodeCookie(NKE_Cookie *cookie, NKE_Context *context)
}
tag_length = SIV_GetTagLength(key->siv);
if (tag_length >= ciphertext_length) {
if (cookie->length <= (int)sizeof (*header) + key->nonce_length + tag_length) {
DEBUG_LOG("Invalid cookie length");
return 0;
}
nonce = cookie->cookie + sizeof (*header);
ciphertext = cookie->cookie + sizeof (*header) + key->nonce_length;
ciphertext_length = cookie->length - sizeof (*header) - key->nonce_length;
plaintext_length = ciphertext_length - tag_length;
if (plaintext_length > sizeof (plaintext) || plaintext_length % 2 != 0) {
DEBUG_LOG("Invalid cookie length");
return 0;
}
if (!SIV_Decrypt(key->siv, header->nonce, sizeof (header->nonce),
if (!SIV_Decrypt(key->siv, nonce, key->nonce_length,
"", 0,
ciphertext, ciphertext_length,
plaintext, plaintext_length)) {
@@ -954,7 +1011,19 @@ NKS_DecodeCookie(NKE_Cookie *cookie, NKE_Context *context)
return 0;
}
context->algorithm = AEAD_AES_SIV_CMAC_256;
/* Select a supported algorithm corresponding to the key length, avoiding
potentially slow SIV_GetKeyLength() */
switch (plaintext_length / 2) {
case 16:
context->algorithm = AEAD_AES_128_GCM_SIV;
break;
case 32:
context->algorithm = AEAD_AES_SIV_CMAC_256;
break;
default:
DEBUG_LOG("Unknown key length");
return 0;
}
context->c2s.length = plaintext_length / 2;
context->s2c.length = plaintext_length / 2;

View File

@@ -667,6 +667,8 @@ create_credentials(const char **certs, const char **keys, int n_certs_keys,
assert(0);
for (i = 0; i < n_certs_keys; i++) {
if (!UTI_CheckFilePermissions(keys[i], 0771))
;
r = gnutls_certificate_set_x509_key_file(credentials, certs[i], keys[i],
GNUTLS_X509_FMT_PEM);
if (r < 0)

View File

@@ -61,23 +61,25 @@ get_padded_length(int length)
int
NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
const unsigned char *nonce, int nonce_length,
const unsigned char *nonce, int max_nonce_length,
const unsigned char *plaintext, int plaintext_length,
int min_ef_length)
{
int auth_length, ciphertext_length, assoc_length;
int auth_length, ciphertext_length, assoc_length, nonce_length, max_siv_nonce_length;
int nonce_padding, ciphertext_padding, additional_padding;
unsigned char *ciphertext, *body;
struct AuthHeader *header;
assert(sizeof (*header) == 4);
if (nonce_length <= 0 || plaintext_length < 0) {
if (max_nonce_length <= 0 || plaintext_length < 0) {
DEBUG_LOG("Invalid nonce/plaintext length");
return 0;
}
assoc_length = info->length;
max_siv_nonce_length = SIV_GetMaxNonceLength(siv);
nonce_length = MIN(max_nonce_length, max_siv_nonce_length);
ciphertext_length = SIV_GetTagLength(siv) + plaintext_length;
nonce_padding = get_padding_length(nonce_length);
ciphertext_padding = get_padding_length(ciphertext_length);
@@ -86,8 +88,8 @@ NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
auth_length = sizeof (*header) + nonce_length + nonce_padding +
ciphertext_length + ciphertext_padding;
additional_padding = MAX(min_ef_length - auth_length - 4, 0);
additional_padding = MAX(NTS_MIN_UNPADDED_NONCE_LENGTH - nonce_length - nonce_padding,
additional_padding);
additional_padding = MAX(MIN(NTS_MIN_UNPADDED_NONCE_LENGTH, max_siv_nonce_length) -
nonce_length - nonce_padding, additional_padding);
auth_length += additional_padding;
if (!NEF_AddBlankField(packet, info, NTP_EF_NTS_AUTH_AND_EEF, auth_length,
@@ -113,6 +115,7 @@ NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
plaintext, plaintext_length, ciphertext, ciphertext_length)) {
DEBUG_LOG("SIV encrypt failed");
info->length = assoc_length;
info->ext_fields--;
return 0;
}
@@ -127,7 +130,7 @@ int
NNA_DecryptAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv, int ef_start,
unsigned char *plaintext, int buffer_length, int *plaintext_length)
{
unsigned int siv_tag_length, nonce_length, ciphertext_length;
int siv_tag_length, max_siv_nonce_length, nonce_length, ciphertext_length;
unsigned char *nonce, *ciphertext;
int ef_type, ef_body_length;
void *ef_body;
@@ -155,6 +158,7 @@ NNA_DecryptAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv, in
nonce = (unsigned char *)(header + 1);
ciphertext = nonce + get_padded_length(nonce_length);
max_siv_nonce_length = SIV_GetMaxNonceLength(siv);
siv_tag_length = SIV_GetTagLength(siv);
if (nonce_length < 1 ||
@@ -164,8 +168,8 @@ NNA_DecryptAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv, in
return 0;
}
if (ef_body_length < sizeof (*header) +
NTS_MIN_UNPADDED_NONCE_LENGTH + get_padded_length(ciphertext_length)) {
if (sizeof (*header) + MIN(NTS_MIN_UNPADDED_NONCE_LENGTH, max_siv_nonce_length) +
get_padded_length(ciphertext_length) > ef_body_length) {
DEBUG_LOG("Missing padding");
return 0;
}

View File

@@ -32,7 +32,7 @@
#include "siv.h"
extern int NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
const unsigned char *nonce, int nonce_length,
const unsigned char *nonce, int max_nonce_length,
const unsigned char *plaintext, int plaintext_length,
int min_ef_length);

View File

@@ -46,6 +46,9 @@
/* Maximum length of all cookies to avoid IP fragmentation */
#define MAX_TOTAL_COOKIE_LENGTH (8 * 108)
/* Retry interval for NTS-KE start (which doesn't generate network traffic) */
#define RETRY_INTERVAL_KE_START 2.0
/* Magic string of files containing keys and cookies */
#define DUMP_IDENTIFIER "NNC0\n"
@@ -203,10 +206,15 @@ set_ntp_address(NNC_Instance inst, NTP_Remote_Address *negotiated_address)
/* ================================================== */
static void
update_next_nke_attempt(NNC_Instance inst, double now)
update_next_nke_attempt(NNC_Instance inst, int failed_start, double now)
{
int factor, interval;
if (failed_start) {
inst->next_nke_attempt = now + RETRY_INTERVAL_KE_START;
return;
}
if (!inst->nke)
return;
@@ -221,8 +229,8 @@ static int
get_cookies(NNC_Instance inst)
{
NTP_Remote_Address ntp_address;
int got_data, failed_start = 0;
double now;
int got_data;
assert(inst->num_cookies == 0);
@@ -239,13 +247,12 @@ get_cookies(NNC_Instance inst)
inst->nke = NKC_CreateInstance(&inst->nts_address, inst->name, inst->cert_set);
inst->nke_attempts++;
update_next_nke_attempt(inst, now);
if (!NKC_Start(inst->nke))
return 0;
failed_start = 1;
}
update_next_nke_attempt(inst, now);
update_next_nke_attempt(inst, failed_start, now);
/* Wait until the session stops */
if (NKC_IsActive(inst->nke))
@@ -643,6 +650,7 @@ load_cookies(NNC_Instance inst)
sizeof (inst->context.c2s.key));
if (inst->context.s2c.length != SIV_GetKeyLength(algorithm) ||
inst->context.s2c.length <= 0 ||
inst->context.c2s.length != inst->context.s2c.length)
goto error;

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2020
* Copyright (C) Miroslav Lichvar 2020, 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
@@ -41,13 +41,15 @@
#include "siv.h"
#include "util.h"
#define SERVER_SIV AEAD_AES_SIV_CMAC_256
#define MAX_SERVER_SIVS 2
struct NtsServer {
SIV_Instance siv;
SIV_Instance sivs[MAX_SERVER_SIVS];
SIV_Algorithm siv_algorithms[MAX_SERVER_SIVS];
unsigned char nonce[NTS_MIN_UNPADDED_NONCE_LENGTH];
NKE_Cookie cookies[NTS_MAX_COOKIES];
int num_cookies;
int siv_index;
NTP_int64 req_tx;
};
@@ -60,6 +62,7 @@ void
NNS_Initialise(void)
{
const char **certs, **keys;
int i;
/* Create an NTS-NTP server instance only if NTS-KE server is enabled */
if (CNF_GetNtsServerCertAndKeyFiles(&certs, &keys) <= 0) {
@@ -68,9 +71,17 @@ NNS_Initialise(void)
}
server = Malloc(sizeof (struct NtsServer));
server->siv = SIV_CreateInstance(SERVER_SIV);
if (!server->siv)
LOG_FATAL("Could not initialise SIV cipher");
server->siv_algorithms[0] = AEAD_AES_SIV_CMAC_256;
server->siv_algorithms[1] = AEAD_AES_128_GCM_SIV;
assert(MAX_SERVER_SIVS == 2);
for (i = 0; i < 2; i++)
server->sivs[i] = SIV_CreateInstance(server->siv_algorithms[i]);
/* AES-SIV-CMAC-256 is required on servers */
if (!server->sivs[0])
LOG_FATAL("Missing AES-SIV-CMAC-256");
}
/* ================================================== */
@@ -78,10 +89,15 @@ NNS_Initialise(void)
void
NNS_Finalise(void)
{
int i;
if (!server)
return;
SIV_DestroyInstance(server->siv);
for (i = 0; i < MAX_SERVER_SIVS; i++) {
if (server->sivs[i])
SIV_DestroyInstance(server->sivs[i]);
}
Free(server);
server = NULL;
}
@@ -96,6 +112,7 @@ NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
unsigned char plaintext[NTP_MAX_EXTENSIONS_LENGTH];
NKE_Context context;
NKE_Cookie cookie;
SIV_Instance siv;
void *ef_body;
*kod = 0;
@@ -104,6 +121,7 @@ NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
return 0;
server->num_cookies = 0;
server->siv_index = -1;
server->req_tx = packet->transmit_ts;
if (info->ext_fields == 0 || info->mode != MODE_CLIENT)
@@ -163,17 +181,22 @@ NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
return 0;
}
if (context.algorithm != SERVER_SIV) {
/* Find the SIV instance needed for authentication */
for (i = 0; i < MAX_SERVER_SIVS && context.algorithm != server->siv_algorithms[i]; i++)
;
if (i == MAX_SERVER_SIVS || !server->sivs[i]) {
DEBUG_LOG("Unexpected SIV");
return 0;
}
server->siv_index = i;
siv = server->sivs[i];
if (!SIV_SetKey(server->siv, context.c2s.key, context.c2s.length)) {
if (!SIV_SetKey(siv, context.c2s.key, context.c2s.length)) {
DEBUG_LOG("Could not set C2S key");
return 0;
}
if (!NNA_DecryptAuthEF(packet, info, server->siv, auth_start,
if (!NNA_DecryptAuthEF(packet, info, siv, auth_start,
plaintext, sizeof (plaintext), &plaintext_length)) {
*kod = NTP_KOD_NTS_NAK;
return 0;
@@ -199,7 +222,7 @@ NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
}
}
if (!SIV_SetKey(server->siv, context.s2c.key, context.s2c.length)) {
if (!SIV_SetKey(siv, context.s2c.key, context.s2c.length)) {
DEBUG_LOG("Could not set S2C key");
return 0;
}
@@ -256,7 +279,7 @@ NNS_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *req_info,
}
/* NTS NAK response does not have any other fields */
if (kod)
if (kod == NTP_KOD_NTS_NAK)
return 1;
for (i = 0, plaintext_length = 0; i < server->num_cookies; i++) {
@@ -271,9 +294,12 @@ NNS_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *req_info,
server->num_cookies = 0;
if (server->siv_index < 0)
return 0;
/* Generate an authenticator field which will make the length
of the response equal to the length of the request */
if (!NNA_GenerateAuthEF(response, res_info, server->siv,
if (!NNA_GenerateAuthEF(response, res_info, server->sivs[server->siv_index],
server->nonce, sizeof (server->nonce),
plaintext, plaintext_length,
req_info->length - res_info->length))

View File

@@ -111,7 +111,7 @@ static const struct request_length request_lengths[] = {
REQ_LENGTH_ENTRY(null, null), /* REFRESH */
REQ_LENGTH_ENTRY(null, server_stats), /* SERVER_STATS */
{ 0, 0 }, /* CLIENT_ACCESSES_BY_INDEX2 - not supported */
REQ_LENGTH_ENTRY(local, null), /* LOCAL2 */
{ 0, 0 }, /* LOCAL2 - not supported */
REQ_LENGTH_ENTRY(ntp_data, ntp_data), /* NTP_DATA */
{ 0, 0 }, /* ADD_SERVER2 */
{ 0, 0 }, /* ADD_PEER2 */
@@ -129,6 +129,9 @@ static const struct request_length request_lengths[] = {
REQ_LENGTH_ENTRY(select_data, select_data), /* SELECT_DATA */
REQ_LENGTH_ENTRY(null, null), /* RELOAD_SOURCES */
REQ_LENGTH_ENTRY(doffset, null), /* DOFFSET2 */
REQ_LENGTH_ENTRY(modify_select_opts, null), /* MODIFY_SELECTOPTS */
REQ_LENGTH_ENTRY(modify_offset, null), /* MODIFY_OFFSET */
REQ_LENGTH_ENTRY(local, null), /* LOCAL3 */
};
static const uint16_t reply_lengths[] = {
@@ -148,7 +151,7 @@ static const uint16_t reply_lengths[] = {
RPY_LENGTH_ENTRY(smoothing), /* SMOOTHING */
0, /* SERVER_STATS - not supported */
0, /* CLIENT_ACCESSES_BY_INDEX2 - not supported */
RPY_LENGTH_ENTRY(ntp_data), /* NTP_DATA */
0, /* NTP_DATA - not supported */
RPY_LENGTH_ENTRY(manual_timestamp), /* MANUAL_TIMESTAMP2 */
RPY_LENGTH_ENTRY(manual_list), /* MANUAL_LIST2 */
RPY_LENGTH_ENTRY(ntp_source_name), /* NTP_SOURCE_NAME */
@@ -156,7 +159,9 @@ static const uint16_t reply_lengths[] = {
RPY_LENGTH_ENTRY(client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX3 */
0, /* SERVER_STATS2 - not supported */
RPY_LENGTH_ENTRY(select_data), /* SELECT_DATA */
RPY_LENGTH_ENTRY(server_stats), /* SERVER_STATS3 */
0, /* SERVER_STATS3 - not supported */
RPY_LENGTH_ENTRY(server_stats), /* SERVER_STATS4 */
RPY_LENGTH_ENTRY(ntp_data), /* NTP_DATA2 */
};
/* ================================================== */

12
ptp.h
View File

@@ -31,9 +31,10 @@
#include "ntp.h"
#define PTP_VERSION 2
#define PTP_VERSION_2 2
#define PTP_VERSION_2_1 (2 | 1 << 4)
#define PTP_TYPE_SYNC 0
#define PTP_TYPE_DELAY_REQ 1
#define PTP_DOMAIN_NTP 123
#define PTP_FLAG_UNICAST (1 << (2 + 8))
#define PTP_TLV_NTP 0x2023
@@ -44,7 +45,12 @@ typedef struct {
uint8_t domain;
uint8_t min_sdoid;
uint16_t flags;
uint8_t rest[26];
uint8_t correction[8];
uint8_t msg_specific[4];
uint8_t port_id[10];
uint16_t sequence_id;
uint8_t control;
int8_t interval;
} PTP_Header;
typedef struct {

View File

@@ -166,8 +166,8 @@ RCL_AddRefclock(RefclockParameters *params)
if (!inst->driver->init && !inst->driver->poll)
LOG_FATAL("refclock driver %s is not compiled in", params->driver_name);
if (params->tai && !CNF_GetLeapSecTimezone())
LOG_FATAL("refclock tai option requires leapsectz");
if (params->tai && !CNF_GetLeapSecList() && !CNF_GetLeapSecTimezone())
LOG_FATAL("refclock tai option requires leapseclist or leapsectz");
inst->data = NULL;
inst->driver_parameter = Strdup(params->driver_parameter);
@@ -321,6 +321,22 @@ RCL_ReportSource(RPT_SourceReport *report, struct timespec *now)
}
}
int
RCL_ModifyOffset(uint32_t ref_id, double offset)
{
unsigned int i;
for (i = 0; i < ARR_GetSize(refclocks); i++) {
RCL_Instance inst = get_refclock(i);
if (inst->ref_id == ref_id) {
inst->offset = offset;
LOG(LOGS_INFO, "Source %s new offset %f", UTI_RefidToString(ref_id), offset);
return 1;
}
}
return 0;
}
void
RCL_SetDriverData(RCL_Instance instance, void *data)
{

View File

@@ -68,6 +68,7 @@ extern void RCL_Finalise(void);
extern int RCL_AddRefclock(RefclockParameters *params);
extern void RCL_StartRefclocks(void);
extern void RCL_ReportSource(RPT_SourceReport *report, struct timespec *now);
extern int RCL_ModifyOffset(uint32_t ref_id, double offset);
/* functions used by drivers */
extern void RCL_SetDriverData(RCL_Instance instance, void *data);

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2013, 2017
* Copyright (C) Miroslav Lichvar 2013, 2017, 2023
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -33,6 +33,9 @@
#include "sysincl.h"
#include <sys/sysmacros.h>
#include "array.h"
#include "refclock.h"
#include "hwclock.h"
#include "local.h"
@@ -44,14 +47,19 @@
struct phc_instance {
int fd;
int dev_index;
int mode;
int nocrossts;
int extpps;
int pin;
int channel;
struct timespec last_extts;
HCL_Instance clock;
};
/* Array of RCL_Instance with enabled extpps */
static ARR_Instance extts_phcs = NULL;
static void read_ext_pulse(int sockfd, int event, void *anything);
static int phc_initialise(RCL_Instance instance)
@@ -59,6 +67,7 @@ static int phc_initialise(RCL_Instance instance)
const char *options[] = {"nocrossts", "extpps", "pin", "channel", "clear", NULL};
struct phc_instance *phc;
int phc_fd, rising_edge;
struct stat st;
char *path, *s;
RCL_CheckDriverOptions(instance, options);
@@ -71,10 +80,13 @@ static int phc_initialise(RCL_Instance instance)
phc = MallocNew(struct phc_instance);
phc->fd = phc_fd;
if (fstat(phc_fd, &st) < 0 || !S_ISCHR(st.st_mode))
LOG_FATAL("Could not get PHC index");
phc->dev_index = minor(st.st_rdev);
phc->mode = 0;
phc->nocrossts = RCL_GetDriverOption(instance, "nocrossts") ? 1 : 0;
phc->extpps = RCL_GetDriverOption(instance, "extpps") ? 1 : 0;
UTI_ZeroTimespec(&phc->last_extts);
phc->clock = HCL_CreateInstance(0, 16, UTI_Log2ToDouble(RCL_GetDriverPoll(instance)),
RCL_GetPrecision(instance));
@@ -90,6 +102,10 @@ static int phc_initialise(RCL_Instance instance)
LOG_FATAL("Could not enable external PHC timestamping");
SCH_AddFileHandler(phc->fd, SCH_FILE_INPUT, read_ext_pulse, instance);
if (!extts_phcs)
extts_phcs = ARR_CreateInstance(sizeof (RCL_Instance));
ARR_AppendElement(extts_phcs, &instance);
} else {
phc->pin = phc->channel = 0;
}
@@ -101,12 +117,22 @@ static int phc_initialise(RCL_Instance instance)
static void phc_finalise(RCL_Instance instance)
{
struct phc_instance *phc;
unsigned int i;
phc = (struct phc_instance *)RCL_GetDriverData(instance);
if (phc->extpps) {
SCH_RemoveFileHandler(phc->fd);
SYS_Linux_SetPHCExtTimestamping(phc->fd, phc->pin, phc->channel, 0, 0, 0);
for (i = 0; i < ARR_GetSize(extts_phcs); i++) {
if ((*(RCL_Instance *)ARR_GetElement(extts_phcs, i)) == instance)
ARR_RemoveElement(extts_phcs, i--);
}
if (ARR_GetSize(extts_phcs) == 0) {
ARR_DestroyInstance(extts_phcs);
extts_phcs = NULL;
}
}
HCL_DestroyInstance(phc->clock);
@@ -114,30 +140,52 @@ static void phc_finalise(RCL_Instance instance)
Free(phc);
}
static void read_ext_pulse(int fd, int event, void *anything)
static void process_ext_pulse(RCL_Instance instance, struct timespec *phc_ts)
{
RCL_Instance instance;
struct phc_instance *phc;
struct timespec phc_ts, local_ts;
struct timespec local_ts;
double local_err;
int channel;
instance = anything;
phc = RCL_GetDriverData(instance);
if (!SYS_Linux_ReadPHCExtTimestamp(phc->fd, &phc_ts, &channel))
return;
if (channel != phc->channel) {
DEBUG_LOG("Unexpected extts channel %d\n", channel);
if (UTI_CompareTimespecs(&phc->last_extts, phc_ts) == 0) {
DEBUG_LOG("Ignoring duplicated PHC timestamp");
return;
}
phc->last_extts = *phc_ts;
if (!HCL_CookTime(phc->clock, &phc_ts, &local_ts, &local_err))
if (!HCL_CookTime(phc->clock, phc_ts, &local_ts, &local_err))
return;
RCL_AddCookedPulse(instance, &local_ts, 1.0e-9 * local_ts.tv_nsec, local_err,
UTI_DiffTimespecsToDouble(&phc_ts, &local_ts));
UTI_DiffTimespecsToDouble(phc_ts, &local_ts));
}
static void read_ext_pulse(int fd, int event, void *anything)
{
RCL_Instance instance;
struct phc_instance *phc1, *phc2;
struct timespec phc_ts;
unsigned int i;
int channel;
if (!SYS_Linux_ReadPHCExtTimestamp(fd, &phc_ts, &channel))
return;
instance = anything;
phc1 = RCL_GetDriverData(instance);
/* Linux versions before 6.7 had one shared queue of timestamps for all
descriptors of the same PHC. Search for all refclocks that expect
the timestamp. */
for (i = 0; i < ARR_GetSize(extts_phcs); i++) {
instance = *(RCL_Instance *)ARR_GetElement(extts_phcs, i);
phc2 = RCL_GetDriverData(instance);
if (!phc2->extpps || phc2->dev_index != phc1->dev_index || phc2->channel != channel)
continue;
process_ext_pulse(instance, &phc_ts);
}
}
#define PHC_READINGS 25

View File

@@ -58,8 +58,29 @@ struct sock_sample {
int magic;
};
/* On 32-bit glibc-based systems enable conversion between timevals using
32-bit and 64-bit time_t to support SOCK clients compiled with different
time_t size than chrony */
#ifdef __GLIBC_PREREQ
#if __GLIBC_PREREQ(2, 34) && __TIMESIZE == 32
#define CONVERT_TIMEVAL 1
#if defined(_TIME_BITS) && _TIME_BITS == 64
typedef int32_t alt_time_t;
typedef int32_t alt_suseconds_t;
#else
typedef int64_t alt_time_t;
typedef int64_t alt_suseconds_t;
#endif
struct alt_timeval {
alt_time_t tv_sec;
alt_suseconds_t tv_usec;
};
#endif
#endif
static void read_sample(int sockfd, int event, void *anything)
{
char buf[sizeof (struct sock_sample) + 16];
struct timespec sys_ts, ref_ts;
struct sock_sample sample;
RCL_Instance instance;
@@ -67,14 +88,33 @@ static void read_sample(int sockfd, int event, void *anything)
instance = (RCL_Instance)anything;
s = recv(sockfd, &sample, sizeof (sample), 0);
s = recv(sockfd, buf, sizeof (buf), 0);
if (s < 0) {
DEBUG_LOG("Could not read SOCK sample : %s", strerror(errno));
return;
}
if (s != sizeof (sample)) {
if (s == sizeof (sample)) {
memcpy(&sample, buf, sizeof (sample));
#ifdef CONVERT_TIMEVAL
} else if (s == sizeof (sample) - sizeof (struct timeval) + sizeof (struct alt_timeval)) {
struct alt_timeval atv;
memcpy(&atv, buf, sizeof (atv));
#ifndef HAVE_LONG_TIME_T
if (atv.tv_sec > INT32_MAX || atv.tv_sec < INT32_MIN ||
atv.tv_usec > INT32_MAX || atv.tv_usec < INT32_MIN) {
DEBUG_LOG("Could not convert 64-bit timeval");
return;
}
#endif
sample.tv.tv_sec = atv.tv_sec;
sample.tv.tv_usec = atv.tv_usec;
DEBUG_LOG("Converted %d-bit timeval", 8 * (int)sizeof (alt_time_t));
memcpy((char *)&sample + sizeof (struct timeval), buf + sizeof (struct alt_timeval),
sizeof (sample) - sizeof (struct timeval));
#endif
} else {
DEBUG_LOG("Unexpected length of SOCK sample : %d != %ld",
s, (long)sizeof (sample));
return;

View File

@@ -33,6 +33,7 @@
#include "reference.h"
#include "util.h"
#include "conf.h"
#include "leapdb.h"
#include "logging.h"
#include "local.h"
#include "sched.h"
@@ -53,6 +54,8 @@ static int enable_local_stratum;
static int local_stratum;
static int local_orphan;
static double local_distance;
static int local_activate_ok;
static double local_activate;
static struct timespec local_ref_time;
static NTP_Leap our_leap_status;
static int our_leap_sec;
@@ -122,9 +125,6 @@ static int leap_in_progress;
/* Timer for the leap second handler */
static SCH_TimeoutID leap_timeout_id;
/* Name of a system timezone containing leap seconds occuring at midnight */
static char *leap_tzname;
/* ================================================== */
static LOG_FileID logfileid;
@@ -155,7 +155,6 @@ static int ref_adjustments;
/* ================================================== */
static NTP_Leap get_tz_leap(time_t when, int *tai_offset);
static void update_leap_status(NTP_Leap leap, time_t now, int reset);
/* ================================================== */
@@ -195,7 +194,6 @@ REF_Initialise(void)
FILE *in;
double file_freq_ppm, file_skew_ppm;
double our_frequency_ppm;
int tai_offset;
mode = REF_ModeNormal;
are_we_synchronised = 0;
@@ -211,6 +209,7 @@ REF_Initialise(void)
our_frequency_sd = 0.0;
our_offset_sd = 0.0;
drift_file_age = 0.0;
local_activate_ok = 0;
/* Now see if we can get the drift file opened */
drift_file = CNF_GetDriftFile();
@@ -249,7 +248,8 @@ REF_Initialise(void)
correction_time_ratio = CNF_GetCorrectionTimeRatio();
enable_local_stratum = CNF_AllowLocalReference(&local_stratum, &local_orphan, &local_distance);
enable_local_stratum = CNF_AllowLocalReference(&local_stratum, &local_orphan,
&local_distance, &local_activate);
UTI_ZeroTimespec(&local_ref_time);
leap_when = 0;
@@ -260,18 +260,6 @@ REF_Initialise(void)
if (leap_mode == REF_LeapModeSystem && !LCL_CanSystemLeap())
leap_mode = REF_LeapModeStep;
leap_tzname = CNF_GetLeapSecTimezone();
if (leap_tzname) {
/* Check that the timezone has good data for Jun 30 2012 and Dec 31 2012 */
if (get_tz_leap(1341014400, &tai_offset) == LEAP_InsertSecond && tai_offset == 34 &&
get_tz_leap(1356912000, &tai_offset) == LEAP_Normal && tai_offset == 35) {
LOG(LOGS_INFO, "Using %s timezone to obtain leap second data", leap_tzname);
} else {
LOG(LOGS_WARN, "Timezone %s failed leap second check, ignoring", leap_tzname);
leap_tzname = NULL;
}
}
CNF_GetMakeStep(&make_step_limit, &make_step_threshold);
CNF_GetMaxChange(&max_offset_delay, &max_offset_ignore, &max_offset);
CNF_GetMailOnChange(&do_mail_change, &mail_change_threshold, &mail_change_user);
@@ -593,77 +581,6 @@ is_leap_second_day(time_t when)
/* ================================================== */
static NTP_Leap
get_tz_leap(time_t when, int *tai_offset)
{
static time_t last_tz_leap_check;
static NTP_Leap tz_leap;
static int tz_tai_offset;
struct tm stm, *tm;
time_t t;
char *tz_env, tz_orig[128];
*tai_offset = tz_tai_offset;
/* Do this check at most twice a day */
when = when / (12 * 3600) * (12 * 3600);
if (last_tz_leap_check == when)
return tz_leap;
last_tz_leap_check = when;
tz_leap = LEAP_Normal;
tz_tai_offset = 0;
tm = gmtime(&when);
if (!tm)
return tz_leap;
stm = *tm;
/* Temporarily switch to the timezone containing leap seconds */
tz_env = getenv("TZ");
if (tz_env) {
if (strlen(tz_env) >= sizeof (tz_orig))
return tz_leap;
strcpy(tz_orig, tz_env);
}
setenv("TZ", leap_tzname, 1);
tzset();
/* Get the TAI-UTC offset, which started at the epoch at 10 seconds */
t = mktime(&stm);
if (t != -1)
tz_tai_offset = t - when + 10;
/* Set the time to 23:59:60 and see how it overflows in mktime() */
stm.tm_sec = 60;
stm.tm_min = 59;
stm.tm_hour = 23;
t = mktime(&stm);
if (tz_env)
setenv("TZ", tz_orig, 1);
else
unsetenv("TZ");
tzset();
if (t == -1)
return tz_leap;
if (stm.tm_sec == 60)
tz_leap = LEAP_InsertSecond;
else if (stm.tm_sec == 1)
tz_leap = LEAP_DeleteSecond;
*tai_offset = tz_tai_offset;
return tz_leap;
}
/* ================================================== */
static void
leap_end_timeout(void *arg)
{
@@ -751,16 +668,16 @@ set_leap_timeout(time_t now)
static void
update_leap_status(NTP_Leap leap, time_t now, int reset)
{
NTP_Leap tz_leap;
NTP_Leap ldb_leap;
int leap_sec, tai_offset;
leap_sec = 0;
tai_offset = 0;
if (leap_tzname && now) {
tz_leap = get_tz_leap(now, &tai_offset);
if (now) {
ldb_leap = LDB_GetLeap(now, &tai_offset);
if (leap == LEAP_Normal)
leap = tz_leap;
leap = ldb_leap;
}
if (leap == LEAP_InsertSecond || leap == LEAP_DeleteSecond) {
@@ -1219,7 +1136,7 @@ REF_GetReferenceParams
double *root_dispersion
)
{
double dispersion, delta;
double dispersion, delta, distance;
assert(initialised);
@@ -1229,11 +1146,16 @@ REF_GetReferenceParams
dispersion = 0.0;
}
distance = our_root_delay / 2 + dispersion;
if (local_activate == 0.0 || (are_we_synchronised && distance < local_activate))
local_activate_ok = 1;
/* Local reference is active when enabled and the clock is not synchronised
or the root distance exceeds the threshold */
if (are_we_synchronised &&
!(enable_local_stratum && our_root_delay / 2 + dispersion > local_distance)) {
!(enable_local_stratum && local_activate_ok && distance > local_distance)) {
*is_synchronised = 1;
@@ -1245,7 +1167,7 @@ REF_GetReferenceParams
*root_delay = our_root_delay;
*root_dispersion = dispersion;
} else if (enable_local_stratum) {
} else if (enable_local_stratum && local_activate_ok) {
*is_synchronised = 0;
@@ -1329,6 +1251,7 @@ void
REF_ModifyMaxupdateskew(double new_max_update_skew)
{
max_update_skew = new_max_update_skew * 1.0e-6;
LOG(LOGS_INFO, "New maxupdateskew %f ppm", new_max_update_skew);
}
/* ================================================== */
@@ -1338,17 +1261,20 @@ REF_ModifyMakestep(int limit, double threshold)
{
make_step_limit = limit;
make_step_threshold = threshold;
LOG(LOGS_INFO, "New makestep %f %d", threshold, limit);
}
/* ================================================== */
void
REF_EnableLocal(int stratum, double distance, int orphan)
REF_EnableLocal(int stratum, double distance, int orphan, double activate)
{
enable_local_stratum = 1;
local_stratum = CLAMP(1, stratum, NTP_MAX_STRATUM - 1);
local_distance = distance;
local_orphan = !!orphan;
local_activate = activate;
LOG(LOGS_INFO, "%s local reference mode", "Enabled");
}
/* ================================================== */
@@ -1357,6 +1283,7 @@ void
REF_DisableLocal(void)
{
enable_local_stratum = 0;
LOG(LOGS_INFO, "%s local reference mode", "Disabled");
}
/* ================================================== */
@@ -1364,7 +1291,7 @@ REF_DisableLocal(void)
#define LEAP_SECOND_CLOSE 5
static int
is_leap_close(time_t t)
is_leap_close(double t)
{
return leap_when != 0 &&
t >= leap_when - LEAP_SECOND_CLOSE && t < leap_when + LEAP_SECOND_CLOSE;
@@ -1394,7 +1321,7 @@ REF_GetTaiOffset(struct timespec *ts)
{
int tai_offset;
get_tz_leap(ts->tv_sec, &tai_offset);
LDB_GetLeap(ts->tv_sec, &tai_offset);
return tai_offset;
}

View File

@@ -185,7 +185,7 @@ extern void REF_ModifyMaxupdateskew(double new_max_update_skew);
/* Modify makestep settings */
extern void REF_ModifyMakestep(int limit, double threshold);
extern void REF_EnableLocal(int stratum, double distance, int orphan);
extern void REF_EnableLocal(int stratum, double distance, int orphan, double activate);
extern void REF_DisableLocal(void);
/* Check if either of the current raw and cooked time, and optionally a

View File

@@ -377,7 +377,7 @@ find_ordered_entry_with_flags(double *x, int n, int index, char *flags)
r = v;
do {
while (l < v && x[l] < piv) l++;
while (x[r] > piv) r--;
while (r > 0 && x[r] > piv) r--;
if (r <= l) break;
EXCH(x[l], x[r]);
l++;

View File

@@ -109,17 +109,23 @@ typedef struct {
} RPT_ClientAccessByIndex_Report;
typedef struct {
uint32_t ntp_hits;
uint32_t nke_hits;
uint32_t cmd_hits;
uint32_t ntp_drops;
uint32_t nke_drops;
uint32_t cmd_drops;
uint32_t log_drops;
uint32_t ntp_auth_hits;
uint32_t ntp_interleaved_hits;
uint32_t ntp_timestamps;
uint32_t ntp_span_seconds;
uint64_t ntp_hits;
uint64_t nke_hits;
uint64_t cmd_hits;
uint64_t ntp_drops;
uint64_t nke_drops;
uint64_t cmd_drops;
uint64_t log_drops;
uint64_t ntp_auth_hits;
uint64_t ntp_interleaved_hits;
uint64_t ntp_timestamps;
uint64_t ntp_span_seconds;
uint64_t ntp_daemon_rx_timestamps;
uint64_t ntp_daemon_tx_timestamps;
uint64_t ntp_kernel_rx_timestamps;
uint64_t ntp_kernel_tx_timestamps;
uint64_t ntp_hw_rx_timestamps;
uint64_t ntp_hw_tx_timestamps;
} RPT_ServerStatsReport;
typedef struct {
@@ -175,6 +181,10 @@ typedef struct {
uint32_t total_rx_count;
uint32_t total_valid_count;
uint32_t total_good_count;
uint32_t total_kernel_tx_ts;
uint32_t total_kernel_rx_ts;
uint32_t total_hw_tx_ts;
uint32_t total_hw_rx_ts;
} RPT_NTPReport;
typedef struct {

View File

@@ -802,6 +802,7 @@ read_from_device(int fd_, int event, void *any)
rtc_tm.tm_mday = rtc_raw.tm_mday;
rtc_tm.tm_mon = rtc_raw.tm_mon;
rtc_tm.tm_year = rtc_raw.tm_year;
rtc_tm.tm_wday = 0;
rtc_t = t_from_rtc(&rtc_tm);

16
sched.c
View File

@@ -104,7 +104,10 @@ static unsigned long n_timer_queue_entries;
static SCH_TimeoutID next_tqe_id;
/* Pointer to head of free list */
static TimerQueueEntry *tqe_free_list = NULL;
static TimerQueueEntry *tqe_free_list;
/* Array of all allocated tqe blocks to be freed in finalisation */
static ARR_Instance tqe_blocks;
/* Timestamp when was last timeout dispatched for each class */
static struct timespec last_class_dispatch[SCH_NumberOfClasses];
@@ -133,6 +136,8 @@ SCH_Initialise(void)
n_timer_queue_entries = 0;
next_tqe_id = 0;
tqe_free_list = NULL;
tqe_blocks = ARR_CreateInstance(sizeof (TimerQueueEntry *));
timer_queue.next = &timer_queue;
timer_queue.prev = &timer_queue;
@@ -154,8 +159,16 @@ SCH_Initialise(void)
void
SCH_Finalise(void) {
unsigned int i;
ARR_DestroyInstance(file_handlers);
timer_queue.next = &timer_queue;
timer_queue.prev = &timer_queue;
for (i = 0; i < ARR_GetSize(tqe_blocks); i++)
Free(*(TimerQueueEntry **)ARR_GetElement(tqe_blocks, i));
ARR_DestroyInstance(tqe_blocks);
LCL_RemoveParameterChangeHandler(handle_slew, NULL);
initialised = 0;
@@ -281,6 +294,7 @@ allocate_tqe(void)
}
new_block[0].next = NULL;
tqe_free_list = &(new_block[TQE_ALLOC_QUANTUM - 1]);
ARR_AppendElement(tqe_blocks, &new_block);
}
result = tqe_free_list;

View File

@@ -37,6 +37,7 @@ typedef enum {
SCH_NtpClientClass,
SCH_NtpPeerClass,
SCH_NtpBroadcastClass,
SCH_PhcPollClass,
SCH_NumberOfClasses /* needs to be last */
} SCH_TimeoutClass;

4
siv.h
View File

@@ -53,6 +53,10 @@ extern int SIV_GetKeyLength(SIV_Algorithm algorithm);
extern int SIV_SetKey(SIV_Instance instance, const unsigned char *key, int length);
extern int SIV_GetMinNonceLength(SIV_Instance instance);
extern int SIV_GetMaxNonceLength(SIV_Instance instance);
extern int SIV_GetTagLength(SIV_Instance instance);
extern int SIV_Encrypt(SIV_Instance instance,

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2020
* Copyright (C) Miroslav Lichvar 2020, 2023
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -37,6 +37,8 @@
struct SIV_Instance_Record {
gnutls_cipher_algorithm_t algorithm;
gnutls_aead_cipher_hd_t cipher;
int min_nonce_length;
int max_nonce_length;
};
/* ================================================== */
@@ -81,6 +83,10 @@ get_cipher_algorithm(SIV_Algorithm algorithm)
switch (algorithm) {
case AEAD_AES_SIV_CMAC_256:
return GNUTLS_CIPHER_AES_128_SIV;
#if HAVE_GNUTLS_SIV_GCM
case AEAD_AES_128_GCM_SIV:
return GNUTLS_CIPHER_AES_128_SIV_GCM;
#endif
default:
return 0;
}
@@ -112,6 +118,19 @@ SIV_CreateInstance(SIV_Algorithm algorithm)
instance->algorithm = calgo;
instance->cipher = NULL;
switch (algorithm) {
case AEAD_AES_SIV_CMAC_256:
instance->min_nonce_length = 1;
instance->max_nonce_length = INT_MAX;
break;
case AEAD_AES_128_GCM_SIV:
instance->min_nonce_length = 12;
instance->max_nonce_length = 12;
break;
default:
assert(0);
}
instance_counter++;
return instance;
@@ -143,6 +162,8 @@ SIV_GetKeyLength(SIV_Algorithm algorithm)
return 0;
len = gnutls_cipher_get_key_size(calgo);
if (len == 0)
return 0;
if (len < 1 || len > SIV_MAX_KEY_LENGTH)
LOG_FATAL("Invalid key length");
@@ -195,6 +216,22 @@ SIV_SetKey(SIV_Instance instance, const unsigned char *key, int length)
/* ================================================== */
int
SIV_GetMinNonceLength(SIV_Instance instance)
{
return instance->min_nonce_length;
}
/* ================================================== */
int
SIV_GetMaxNonceLength(SIV_Instance instance)
{
return instance->max_nonce_length;
}
/* ================================================== */
int
SIV_GetTagLength(SIV_Instance instance)
{
@@ -222,7 +259,8 @@ SIV_Encrypt(SIV_Instance instance,
if (!instance->cipher)
return 0;
if (nonce_length < 1 || assoc_length < 0 ||
if (nonce_length < instance->min_nonce_length ||
nonce_length > instance->max_nonce_length || assoc_length < 0 ||
plaintext_length < 0 || ciphertext_length < 0)
return 0;
@@ -253,7 +291,8 @@ SIV_Decrypt(SIV_Instance instance,
if (!instance->cipher)
return 0;
if (nonce_length < 1 || assoc_length < 0 ||
if (nonce_length < instance->min_nonce_length ||
nonce_length > instance->max_nonce_length || assoc_length < 0 ||
plaintext_length < 0 || ciphertext_length < 0)
return 0;

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate.
**********************************************************************
* Copyright (C) Miroslav Lichvar 2019
* Copyright (C) Miroslav Lichvar 2019, 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
@@ -34,12 +34,25 @@
#include "siv_nettle_int.c"
#endif
#ifdef HAVE_NETTLE_SIV_GCM
#include <nettle/siv-gcm.h>
#endif
#include "memory.h"
#include "siv.h"
struct SIV_Instance_Record {
struct siv_cmac_aes128_ctx siv;
SIV_Algorithm algorithm;
int key_set;
int min_nonce_length;
int max_nonce_length;
int tag_length;
union {
struct siv_cmac_aes128_ctx cmac_aes128;
#ifdef HAVE_NETTLE_SIV_GCM
struct aes128_ctx aes128;
#endif
} ctx;
};
/* ================================================== */
@@ -49,12 +62,30 @@ SIV_CreateInstance(SIV_Algorithm algorithm)
{
SIV_Instance instance;
if (algorithm != AEAD_AES_SIV_CMAC_256)
if (SIV_GetKeyLength(algorithm) <= 0)
return NULL;
instance = MallocNew(struct SIV_Instance_Record);
instance->algorithm = algorithm;
instance->key_set = 0;
switch (algorithm) {
case AEAD_AES_SIV_CMAC_256:
instance->min_nonce_length = SIV_MIN_NONCE_SIZE;
instance->max_nonce_length = INT_MAX;
instance->tag_length = SIV_DIGEST_SIZE;
break;
#ifdef HAVE_NETTLE_SIV_GCM
case AEAD_AES_128_GCM_SIV:
instance->min_nonce_length = SIV_GCM_NONCE_SIZE;
instance->max_nonce_length = SIV_GCM_NONCE_SIZE;
instance->tag_length = SIV_GCM_DIGEST_SIZE;
break;
#endif
default:
assert(0);
}
return instance;
}
@@ -71,11 +102,18 @@ SIV_DestroyInstance(SIV_Instance instance)
int
SIV_GetKeyLength(SIV_Algorithm algorithm)
{
assert(32 <= SIV_MAX_KEY_LENGTH);
assert(2 * AES128_KEY_SIZE <= SIV_MAX_KEY_LENGTH);
if (algorithm == AEAD_AES_SIV_CMAC_256)
return 32;
return 0;
switch (algorithm) {
case AEAD_AES_SIV_CMAC_256:
return 2 * AES128_KEY_SIZE;
#ifdef HAVE_NETTLE_SIV_GCM
case AEAD_AES_128_GCM_SIV:
return AES128_KEY_SIZE;
#endif
default:
return 0;
}
}
/* ================================================== */
@@ -83,10 +121,21 @@ SIV_GetKeyLength(SIV_Algorithm algorithm)
int
SIV_SetKey(SIV_Instance instance, const unsigned char *key, int length)
{
if (length != 32)
if (length != SIV_GetKeyLength(instance->algorithm))
return 0;
siv_cmac_aes128_set_key(&instance->siv, key);
switch (instance->algorithm) {
case AEAD_AES_SIV_CMAC_256:
siv_cmac_aes128_set_key(&instance->ctx.cmac_aes128, key);
break;
#ifdef HAVE_NETTLE_SIV_GCM
case AEAD_AES_128_GCM_SIV:
aes128_set_encrypt_key(&instance->ctx.aes128, key);
break;
#endif
default:
assert(0);
}
instance->key_set = 1;
@@ -95,12 +144,28 @@ SIV_SetKey(SIV_Instance instance, const unsigned char *key, int length)
/* ================================================== */
int
SIV_GetMinNonceLength(SIV_Instance instance)
{
return instance->min_nonce_length;
}
/* ================================================== */
int
SIV_GetMaxNonceLength(SIV_Instance instance)
{
return instance->max_nonce_length;
}
/* ================================================== */
int
SIV_GetTagLength(SIV_Instance instance)
{
assert(SIV_DIGEST_SIZE <= SIV_MAX_TAG_LENGTH);
return SIV_DIGEST_SIZE;
if (instance->tag_length < 1 || instance->tag_length > SIV_MAX_TAG_LENGTH)
assert(0);
return instance->tag_length;
}
/* ================================================== */
@@ -115,16 +180,31 @@ SIV_Encrypt(SIV_Instance instance,
if (!instance->key_set)
return 0;
if (nonce_length < SIV_MIN_NONCE_SIZE || assoc_length < 0 ||
if (nonce_length < instance->min_nonce_length ||
nonce_length > instance->max_nonce_length || assoc_length < 0 ||
plaintext_length < 0 || plaintext_length > ciphertext_length ||
plaintext_length + SIV_DIGEST_SIZE != ciphertext_length)
plaintext_length + SIV_GetTagLength(instance) != ciphertext_length)
return 0;
assert(assoc && plaintext);
siv_cmac_aes128_encrypt_message(&instance->siv, nonce_length, nonce,
assoc_length, assoc,
ciphertext_length, ciphertext, plaintext);
switch (instance->algorithm) {
case AEAD_AES_SIV_CMAC_256:
siv_cmac_aes128_encrypt_message(&instance->ctx.cmac_aes128,
nonce_length, nonce, assoc_length, assoc,
ciphertext_length, ciphertext, plaintext);
break;
#ifdef HAVE_NETTLE_SIV_GCM
case AEAD_AES_128_GCM_SIV:
siv_gcm_aes128_encrypt_message(&instance->ctx.aes128,
nonce_length, nonce, assoc_length, assoc,
ciphertext_length, ciphertext, plaintext);
break;
#endif
default:
assert(0);
}
return 1;
}
@@ -140,17 +220,32 @@ SIV_Decrypt(SIV_Instance instance,
if (!instance->key_set)
return 0;
if (nonce_length < SIV_MIN_NONCE_SIZE || assoc_length < 0 ||
if (nonce_length < instance->min_nonce_length ||
nonce_length > instance->max_nonce_length || assoc_length < 0 ||
plaintext_length < 0 || plaintext_length > ciphertext_length ||
plaintext_length + SIV_DIGEST_SIZE != ciphertext_length)
plaintext_length + SIV_GetTagLength(instance) != ciphertext_length)
return 0;
assert(assoc && plaintext);
if (!siv_cmac_aes128_decrypt_message(&instance->siv, nonce_length, nonce,
assoc_length, assoc,
plaintext_length, plaintext, ciphertext))
return 0;
switch (instance->algorithm) {
case AEAD_AES_SIV_CMAC_256:
if (!siv_cmac_aes128_decrypt_message(&instance->ctx.cmac_aes128,
nonce_length, nonce, assoc_length, assoc,
plaintext_length, plaintext, ciphertext))
return 0;
break;
#ifdef HAVE_NETTLE_SIV_GCM
case AEAD_AES_128_GCM_SIV:
if (!siv_gcm_aes128_decrypt_message(&instance->ctx.aes128,
nonce_length, nonce, assoc_length, assoc,
plaintext_length, plaintext, ciphertext))
return 0;
break;
#endif
default:
assert(0);
}
return 1;
}

View File

@@ -302,7 +302,7 @@ SMT_Activate(struct timespec *now)
if (!enabled || !locked)
return;
LOG(LOGS_INFO, "Time smoothing activated%s", leap_only_mode ?
LOG(LOGS_INFO, "Activated %s%s", "time smoothing", leap_only_mode ?
" (leap seconds only)" : "");
locked = 0;
last_update = *now;
@@ -322,6 +322,8 @@ SMT_Reset(struct timespec *now)
for (i = 0; i < NUM_STAGES; i++)
stages[i].wander = stages[i].length = 0.0;
LOG(LOGS_INFO, "Reset %s", "time smoothing");
}
void

183
socket.c
View File

@@ -5,6 +5,7 @@
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Timo Teras 2009
* Copyright (C) Miroslav Lichvar 2009, 2013-2020
* Copyright (C) Luke Valenta 2023
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -89,6 +90,9 @@ struct MessageHeader {
static int initialised;
static int first_reusable_fd;
static int reusable_fds;
/* Flags indicating in which IP families sockets can be requested */
static int ip4_enabled;
static int ip6_enabled;
@@ -155,6 +159,59 @@ domain_to_string(int domain)
/* ================================================== */
static int
get_reusable_socket(int type, IPSockAddr *spec)
{
#ifdef LINUX
union sockaddr_all sa;
IPSockAddr ip_sa;
int sock_fd, opt;
socklen_t l;
/* Abort early if not an IPv4/IPv6 server socket */
if (!spec || spec->ip_addr.family == IPADDR_UNSPEC || spec->port == 0)
return INVALID_SOCK_FD;
/* Loop over available reusable sockets */
for (sock_fd = first_reusable_fd; sock_fd < first_reusable_fd + reusable_fds; sock_fd++) {
/* Check that types match */
l = sizeof (opt);
if (getsockopt(sock_fd, SOL_SOCKET, SO_TYPE, &opt, &l) < 0 ||
l != sizeof (opt) || opt != type)
continue;
/* Get sockaddr for reusable socket */
l = sizeof (sa);
if (getsockname(sock_fd, &sa.sa, &l) < 0 || l < sizeof (sa_family_t))
continue;
SCK_SockaddrToIPSockAddr(&sa.sa, l, &ip_sa);
/* Check that reusable socket matches specification */
if (ip_sa.port != spec->port || UTI_CompareIPs(&ip_sa.ip_addr, &spec->ip_addr, NULL) != 0)
continue;
/* Check that STREAM socket is listening */
l = sizeof (opt);
if (type == SOCK_STREAM && (getsockopt(sock_fd, SOL_SOCKET, SO_ACCEPTCONN, &opt, &l) < 0 ||
l != sizeof (opt) || opt == 0))
continue;
#if defined(FEAT_IPV6) && defined(IPV6_V6ONLY)
if (spec->ip_addr.family == IPADDR_INET6 &&
(!SCK_GetIntOption(sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt) || opt != 1))
LOG(LOGS_WARN, "Reusable IPv6 socket missing IPV6_V6ONLY option");
#endif
return sock_fd;
}
#endif
return INVALID_SOCK_FD;
}
/* ================================================== */
#if defined(SOCK_CLOEXEC) || defined(SOCK_NONBLOCK)
static int
check_socket_flag(int sock_flag, int fd_flag, int fs_flag)
@@ -212,7 +269,7 @@ static int
set_socket_flags(int sock_fd, int flags)
{
/* Close the socket automatically on exec */
if (
if (!SCK_IsReusable(sock_fd) &&
#ifdef SOCK_CLOEXEC
(supported_socket_flags & SOCK_CLOEXEC) == 0 &&
#endif
@@ -222,7 +279,7 @@ set_socket_flags(int sock_fd, int flags)
/* Enable non-blocking mode */
if ((flags & SCK_FLAG_BLOCK) == 0 &&
#ifdef SOCK_NONBLOCK
(supported_socket_flags & SOCK_NONBLOCK) == 0 &&
(SCK_IsReusable(sock_fd) || (supported_socket_flags & SOCK_NONBLOCK) == 0) &&
#endif
!set_socket_nonblock(sock_fd))
return 0;
@@ -279,6 +336,32 @@ open_socket_pair(int domain, int type, int flags, int *other_fd)
/* ================================================== */
static int
get_ip_socket(int domain, int type, int flags, IPSockAddr *ip_sa)
{
int sock_fd;
/* Check if there is a matching reusable socket */
sock_fd = get_reusable_socket(type, ip_sa);
if (sock_fd < 0) {
sock_fd = open_socket(domain, type, flags);
/* Unexpected, but make sure the new socket is not in the reusable range */
if (SCK_IsReusable(sock_fd))
LOG_FATAL("Could not open %s socket : file descriptor in reusable range",
domain_to_string(domain));
} else {
/* Set socket flags on reusable socket */
if (!set_socket_flags(sock_fd, flags))
return INVALID_SOCK_FD;
}
return sock_fd;
}
/* ================================================== */
static int
set_socket_options(int sock_fd, int flags)
{
@@ -295,8 +378,10 @@ static int
set_ip_options(int sock_fd, int family, int flags)
{
#if defined(FEAT_IPV6) && defined(IPV6_V6ONLY)
/* Receive only IPv6 packets on an IPv6 socket */
if (family == IPADDR_INET6 && !SCK_SetIntOption(sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, 1))
/* Receive only IPv6 packets on an IPv6 socket, but do not attempt
to set this option on pre-initialised reuseable sockets */
if (family == IPADDR_INET6 && !SCK_IsReusable(sock_fd) &&
!SCK_SetIntOption(sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, 1))
return 0;
#endif
@@ -385,6 +470,10 @@ bind_ip_address(int sock_fd, IPSockAddr *addr, int flags)
;
#endif
/* Do not attempt to bind pre-initialised reusable socket */
if (SCK_IsReusable(sock_fd))
return 1;
saddr_len = SCK_IPSockAddrToSockaddr(addr, (struct sockaddr *)&saddr, sizeof (saddr));
if (saddr_len == 0)
return 0;
@@ -457,7 +546,7 @@ open_ip_socket(IPSockAddr *remote_addr, IPSockAddr *local_addr, const char *ifac
return INVALID_SOCK_FD;
}
sock_fd = open_socket(domain, type, flags);
sock_fd = get_ip_socket(domain, type, flags, local_addr);
if (sock_fd < 0)
return INVALID_SOCK_FD;
@@ -482,7 +571,8 @@ open_ip_socket(IPSockAddr *remote_addr, IPSockAddr *local_addr, const char *ifac
goto error;
if (remote_addr || local_addr)
DEBUG_LOG("Opened %s%s socket fd=%d%s%s%s%s",
DEBUG_LOG("%s %s%s socket fd=%d%s%s%s%s",
SCK_IsReusable(sock_fd) ? "Reusing" : "Opened",
type == SOCK_DGRAM ? "UDP" : type == SOCK_STREAM ? "TCP" : "?",
family == IPADDR_INET4 ? "v4" : "v6",
sock_fd,
@@ -869,6 +959,11 @@ process_header(struct msghdr *msg, int msg_length, int sock_fd, int flags,
memcpy(&message->timestamp.kernel, CMSG_DATA(cmsg), sizeof (message->timestamp.kernel));
}
#endif
#ifdef SCM_REALTIME
else if (match_cmsg(cmsg, SOL_SOCKET, SCM_REALTIME, sizeof (message->timestamp.kernel))) {
memcpy(&message->timestamp.kernel, CMSG_DATA(cmsg), sizeof (message->timestamp.kernel));
}
#endif
#ifdef HAVE_LINUX_TIMESTAMPING
#ifdef HAVE_LINUX_TIMESTAMPING_OPT_PKTINFO
else if (match_cmsg(cmsg, SOL_SOCKET, SCM_TIMESTAMPING_PKTINFO,
@@ -1165,9 +1260,44 @@ send_message(int sock_fd, SCK_Message *message, int flags)
/* ================================================== */
void
SCK_PreInitialise(void)
{
#ifdef LINUX
char *s, *ptr;
/* On Linux systems, the systemd service manager may pass file descriptors
for pre-initialised sockets to the chronyd daemon. The service manager
allocates and binds the file descriptors, and passes a copy to each
spawned instance of the service. This allows for zero-downtime service
restarts as the sockets buffer client requests until the service is able
to handle them. The service manager sets the LISTEN_FDS environment
variable to the number of passed file descriptors, and the integer file
descriptors start at 3 (see SD_LISTEN_FDS_START in
https://www.freedesktop.org/software/systemd/man/latest/sd_listen_fds.html). */
first_reusable_fd = 3;
reusable_fds = 0;
s = getenv("LISTEN_FDS");
if (s) {
errno = 0;
reusable_fds = strtol(s, &ptr, 10);
if (errno != 0 || *ptr != '\0' || reusable_fds < 0)
reusable_fds = 0;
}
#else
first_reusable_fd = 0;
reusable_fds = 0;
#endif
}
/* ================================================== */
void
SCK_Initialise(int family)
{
int fd;
ip4_enabled = family == IPADDR_INET4 || family == IPADDR_UNSPEC;
#ifdef FEAT_IPV6
ip6_enabled = family == IPADDR_INET6 || family == IPADDR_UNSPEC;
@@ -1196,6 +1326,9 @@ SCK_Initialise(int family)
supported_socket_flags |= SOCK_NONBLOCK;
#endif
for (fd = first_reusable_fd; fd < first_reusable_fd + reusable_fds; fd++)
UTI_FdSetCloexec(fd);
initialised = 1;
}
@@ -1208,6 +1341,8 @@ SCK_Finalise(void)
ARR_DestroyInstance(recv_headers);
ARR_DestroyInstance(recv_messages);
SCK_CloseReusableSockets();
initialised = 0;
}
@@ -1348,6 +1483,27 @@ SCK_OpenUnixSocketPair(int flags, int *other_fd)
/* ================================================== */
int
SCK_IsReusable(int fd)
{
return fd >= first_reusable_fd && fd < first_reusable_fd + reusable_fds;
}
/* ================================================== */
void
SCK_CloseReusableSockets(void)
{
int fd;
for (fd = first_reusable_fd; fd < first_reusable_fd + reusable_fds; fd++)
close(fd);
reusable_fds = 0;
first_reusable_fd = 0;
}
/* ================================================== */
int
SCK_SetIntOption(int sock_fd, int level, int name, int value)
{
@@ -1386,8 +1542,15 @@ SCK_EnableKernelRxTimestamping(int sock_fd)
return 1;
#endif
#ifdef SO_TIMESTAMP
if (SCK_SetIntOption(sock_fd, SOL_SOCKET, SO_TIMESTAMP, 1))
if (SCK_SetIntOption(sock_fd, SOL_SOCKET, SO_TIMESTAMP, 1)) {
#if defined(SO_TS_CLOCK) && defined(SO_TS_REALTIME)
/* We don't care about the return value - we'll get either a
SCM_REALTIME (if we succeded) or a SCM_TIMESTAMP (if we failed) */
if (!SCK_SetIntOption(sock_fd, SOL_SOCKET, SO_TS_CLOCK, SO_TS_REALTIME))
;
#endif
return 1;
}
#endif
return 0;
@@ -1398,7 +1561,7 @@ SCK_EnableKernelRxTimestamping(int sock_fd)
int
SCK_ListenOnSocket(int sock_fd, int backlog)
{
if (listen(sock_fd, backlog) < 0) {
if (!SCK_IsReusable(sock_fd) && listen(sock_fd, backlog) < 0) {
DEBUG_LOG("listen() failed : %s", strerror(errno));
return 0;
}
@@ -1561,6 +1724,10 @@ SCK_RemoveSocket(int sock_fd)
void
SCK_CloseSocket(int sock_fd)
{
/* Reusable sockets are closed in finalisation */
if (SCK_IsReusable(sock_fd))
return;
close(sock_fd);
}

View File

@@ -73,6 +73,9 @@ typedef struct {
int descriptor;
} SCK_Message;
/* Pre-initialisation function */
extern void SCK_PreInitialise(void);
/* Initialisation function (the specified IP family is enabled,
or all if IPADDR_UNSPEC) */
extern void SCK_Initialise(int family);
@@ -106,6 +109,12 @@ extern int SCK_OpenUnixStreamSocket(const char *remote_addr, const char *local_a
int flags);
extern int SCK_OpenUnixSocketPair(int flags, int *other_fd);
/* Check if a file descriptor was passed from the service manager */
extern int SCK_IsReusable(int sock_fd);
/* Close all reusable sockets before finalisation (e.g. in a helper process) */
extern void SCK_CloseReusableSockets(void);
/* Set and get a socket option of int size */
extern int SCK_SetIntOption(int sock_fd, int level, int name, int value);
extern int SCK_GetIntOption(int sock_fd, int level, int name, int *value);

260
sources.c
View File

@@ -3,7 +3,7 @@
**********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011-2016, 2018, 2020-2021
* Copyright (C) Miroslav Lichvar 2011-2016, 2018, 2020-2023
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
@@ -68,8 +68,8 @@ 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_UNSYNCHRONISED, /* Provides samples, but not synchronised */
SRC_BAD_DISTANCE, /* Has root distance longer than allowed maximum */
SRC_JITTERY, /* Had std dev larger than allowed maximum */
SRC_WAITS_STATS, /* Others have bad stats, selection postponed */
@@ -112,6 +112,9 @@ struct SRC_Instance_Record {
/* Updates left before allowing combining */
int distant;
/* Updates with a status requiring source replacement */
int bad;
/* Flag indicating the status of the source */
SRC_Status status;
@@ -140,6 +143,10 @@ struct SRC_Instance_Record {
/* Flag indicating the source has a leap second vote */
int leap_vote;
/* Flag indicating the source was already reported as
a falseticker since the last selection change */
int reported_falseticker;
};
/* ================================================== */
@@ -165,6 +172,13 @@ static int max_n_sources; /* Capacity of the table */
static int selected_source_index; /* Which source index is currently
selected (set to INVALID_SOURCE
if no current valid reference) */
static int reported_no_majority; /* Flag to avoid repeated log message
about no majority */
static int report_selection_loss; /* Flag to force logging a message if
selection is lost in a transient state
(SRC_WAITS_STATS, SRC_WAITS_UPDATE) */
static int forced_first_report; /* Flag to allow one failed selection to be
logged before a successful selection */
/* Score needed to replace the currently selected source */
#define SCORE_LIMIT 10.0
@@ -172,12 +186,17 @@ static int selected_source_index; /* Which source index is currently
/* Number of updates needed to reset the distant status */
#define DISTANT_PENALTY 32
/* Number of updates needed to trigger handling of bad sources */
#define BAD_HANDLE_THRESHOLD 4
static double max_distance;
static double max_jitter;
static double reselect_distance;
static double stratum_weight;
static double combine_limit;
static SRC_Instance last_updated_inst;
static LOG_FileID logfileid;
/* Identifier of the dump file */
@@ -187,6 +206,8 @@ static LOG_FileID logfileid;
/* Forward prototype */
static void update_sel_options(void);
static void unselect_selected_source(LOG_Severity severity, const char *format,
const char *arg);
static void slew_sources(struct timespec *raw, struct timespec *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything);
static void add_dispersion(double dispersion, void *anything);
@@ -212,6 +233,8 @@ void SRC_Initialise(void) {
LCL_AddParameterChangeHandler(slew_sources, NULL);
LCL_AddDispersionNotifyHandler(add_dispersion, NULL);
last_updated_inst = NULL;
logfileid = CNF_GetLogSelection() ? LOG_FileOpen("selection",
" Date (UTC) Time IP Address S EOpts Reach Score Last sample Low end High end")
: -1;
@@ -295,7 +318,13 @@ void SRC_DestroyInstance(SRC_Instance instance)
{
int dead_index, i;
if (last_updated_inst == instance)
last_updated_inst = NULL;
assert(initialised);
if (instance->index < 0 || instance->index >= n_sources ||
instance != sources[instance->index])
assert(0);
SST_DeleteInstance(instance->stats);
dead_index = instance->index;
@@ -308,11 +337,12 @@ void SRC_DestroyInstance(SRC_Instance instance)
update_sel_options();
/* If this was the previous reference source, we have to reselect! */
if (selected_source_index == dead_index)
SRC_ReselectSource();
else if (selected_source_index > dead_index)
if (selected_source_index > dead_index)
--selected_source_index;
else if (selected_source_index == dead_index)
unselect_selected_source(LOGS_INFO, NULL, NULL);
SRC_SelectSource(NULL);
}
/* ================================================== */
@@ -324,15 +354,20 @@ SRC_ResetInstance(SRC_Instance instance)
instance->reachability = 0;
instance->reachability_size = 0;
instance->distant = 0;
instance->bad = 0;
instance->status = SRC_BAD_STATS;
instance->sel_score = 1.0;
instance->stratum = 0;
instance->leap = LEAP_Unsynchronised;
instance->leap_vote = 0;
instance->reported_falseticker = 0;
memset(&instance->sel_info, 0, sizeof (instance->sel_info));
SST_ResetInstance(instance->stats);
if (selected_source_index == instance->index)
SRC_SelectSource(NULL);
}
/* ================================================== */
@@ -468,6 +503,19 @@ special_mode_end(void)
return 1;
}
/* ================================================== */
static void
handle_bad_source(SRC_Instance inst)
{
if (inst->type == SRC_NTP) {
DEBUG_LOG("Bad source status=%c", get_status_char(inst->status));
NSR_HandleBadSource(inst->ip_addr);
}
}
/* ================================================== */
void
SRC_UpdateReachability(SRC_Instance inst, int reachable)
{
@@ -478,8 +526,8 @@ SRC_UpdateReachability(SRC_Instance inst, int reachable)
if (inst->reachability_size < SOURCE_REACH_BITS)
inst->reachability_size++;
if (!reachable && inst->index == selected_source_index) {
/* Try to select a better source */
/* Source selection can change with unreachable sources */
if (inst->reachability == 0) {
SRC_SelectSource(NULL);
}
@@ -488,14 +536,9 @@ SRC_UpdateReachability(SRC_Instance inst, int reachable)
REF_SetUnsynchronised();
}
/* Try to replace NTP sources that are unreachable, falsetickers, or
have root distance or jitter larger than the allowed maximums */
if (inst->type == SRC_NTP &&
((!inst->reachability && inst->reachability_size == SOURCE_REACH_BITS) ||
inst->status == SRC_BAD_DISTANCE || inst->status == SRC_JITTERY ||
inst->status == SRC_FALSETICKER)) {
NSR_HandleBadSource(inst->ip_addr);
}
/* Try to replace unreachable NTP sources */
if (inst->reachability == 0 && inst->reachability_size == SOURCE_REACH_BITS)
handle_bad_source(inst);
}
/* ================================================== */
@@ -555,18 +598,17 @@ update_sel_options(void)
for (i = 0; i < n_sources; i++) {
options = sources[i]->conf_sel_options;
if (options & SRC_SELECT_NOSELECT)
continue;
switch (sources[i]->type) {
case SRC_NTP:
options |= sources[i]->authenticated ? auth_ntp_options : unauth_ntp_options;
break;
case SRC_REFCLOCK:
options |= refclk_options;
break;
default:
assert(0);
if (!(options & SRC_SELECT_NOSELECT)) {
switch (sources[i]->type) {
case SRC_NTP:
options |= sources[i]->authenticated ? auth_ntp_options : unauth_ntp_options;
break;
case SRC_REFCLOCK:
options |= refclk_options;
break;
default:
assert(0);
}
}
if (sources[i]->sel_options != options) {
@@ -580,17 +622,17 @@ update_sel_options(void)
/* ================================================== */
static void
log_selection_message(const char *format, const char *arg)
log_selection_message(LOG_Severity severity, const char *format, const char *arg)
{
if (REF_GetMode() != REF_ModeNormal)
return;
LOG(LOGS_INFO, format, arg);
LOG(severity, format, arg);
}
/* ================================================== */
static void
log_selection_source(const char *format, SRC_Instance inst)
log_selection_source(LOG_Severity severity, const char *format, SRC_Instance inst)
{
char buf[320], *name, *ntp_name;
@@ -602,7 +644,7 @@ log_selection_source(const char *format, SRC_Instance inst)
else
snprintf(buf, sizeof (buf), "%s", name);
log_selection_message(format, buf);
log_selection_message(severity, format, buf);
}
/* ================================================== */
@@ -651,11 +693,23 @@ mark_source(SRC_Instance inst, SRC_Status status)
inst->status = status;
DEBUG_LOG("%s status=%c options=%x reach=%o/%d updates=%d distant=%d leap=%d vote=%d lo=%f hi=%f",
/* Try to replace NTP sources that are falsetickers, or have a root
distance or jitter larger than the allowed maximums */
if (inst == last_updated_inst) {
if (inst->bad < INT_MAX &&
(status == SRC_FALSETICKER || status == SRC_BAD_DISTANCE || status == SRC_JITTERY))
inst->bad++;
else
inst->bad = 0;
if (inst->bad >= BAD_HANDLE_THRESHOLD)
handle_bad_source(inst);
}
DEBUG_LOG("%s status=%c options=%x reach=%o/%d updates=%d distant=%d bad=%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->distant, inst->bad, (int)inst->leap, inst->leap_vote,
inst->sel_info.lo_limit, inst->sel_info.hi_limit);
if (logfileid == -1)
@@ -690,6 +744,26 @@ mark_ok_sources(SRC_Status status)
}
}
/* ================================================== */
/* Reset the index of selected source and report the selection loss. If no
message is provided, assume it is a transient state and wait for another
call providing a message or selection of another source, which resets the
report_selection_loss flag. */
static void
unselect_selected_source(LOG_Severity severity, const char *format, const char *arg)
{
if (selected_source_index != INVALID_SOURCE) {
selected_source_index = INVALID_SOURCE;
report_selection_loss = 1;
}
if (report_selection_loss && format) {
log_selection_message(severity, format, arg);
report_selection_loss = 0;
}
}
/* ================================================== */
static int
@@ -790,7 +864,8 @@ SRC_SelectSource(SRC_Instance updated_inst)
struct SelectInfo *si;
struct timespec now, ref_time;
int i, j, j1, j2, index, sel_prefer, n_endpoints, n_sel_sources, sel_req_source;
int n_badstats_sources, max_sel_reach, max_sel_reach_size, max_badstat_reach;
int max_badstat_reach, max_badstat_reach_size, n_badstats_sources;
int max_sel_reach, max_sel_reach_size;
int depth, best_depth, trust_depth, best_trust_depth, n_sel_trust_sources;
int combined, stratum, min_stratum, max_score_index;
int orphan_stratum, orphan_source;
@@ -801,15 +876,13 @@ SRC_SelectSource(SRC_Instance updated_inst)
double first_sample_ago, max_reach_sample_ago;
NTP_Leap leap_status;
if (updated_inst)
if (updated_inst) {
updated_inst->updates++;
last_updated_inst = updated_inst;
}
if (n_sources == 0) {
/* In this case, we clearly cannot synchronise to anything */
if (selected_source_index != INVALID_SOURCE) {
log_selection_message("Can't synchronise: no sources", NULL);
selected_source_index = INVALID_SOURCE;
}
unselect_selected_source(LOGS_INFO, "Can't synchronise: no sources", NULL);
return;
}
@@ -823,7 +896,7 @@ SRC_SelectSource(SRC_Instance updated_inst)
n_badstats_sources = 0;
sel_req_source = 0;
max_sel_reach = max_badstat_reach = 0;
max_sel_reach_size = 0;
max_sel_reach_size = max_badstat_reach_size = 0;
max_reach_sample_ago = 0.0;
for (i = 0; i < n_sources; i++) {
@@ -843,12 +916,6 @@ 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,
@@ -860,6 +927,14 @@ SRC_SelectSource(SRC_Instance updated_inst)
mark_source(sources[i], SRC_BAD_STATS);
if (max_badstat_reach < sources[i]->reachability)
max_badstat_reach = sources[i]->reachability;
if (max_badstat_reach_size < sources[i]->reachability_size)
max_badstat_reach_size = sources[i]->reachability_size;
continue;
}
/* Ignore sources which are not synchronised */
if (sources[i]->leap == LEAP_Unsynchronised) {
mark_source(sources[i], SRC_UNSYNCHRONISED);
continue;
}
@@ -994,15 +1069,21 @@ SRC_SelectSource(SRC_Instance updated_inst)
if (n_badstats_sources && n_sel_sources && selected_source_index == INVALID_SOURCE &&
max_sel_reach_size < SOURCE_REACH_BITS && max_sel_reach >> 1 == max_badstat_reach) {
mark_ok_sources(SRC_WAITS_STATS);
unselect_selected_source(LOGS_INFO, NULL, NULL);
return;
}
/* Wait for a source to have full reachability register to allow one
failed selection to be logged before first successful selection */
if (!forced_first_report &&
MAX(max_sel_reach_size, max_badstat_reach_size) == SOURCE_REACH_BITS) {
report_selection_loss = 1;
forced_first_report = 1;
}
if (n_endpoints == 0) {
/* No sources provided valid endpoints */
if (selected_source_index != INVALID_SOURCE) {
log_selection_message("Can't synchronise: no selectable sources", NULL);
selected_source_index = INVALID_SOURCE;
}
unselect_selected_source(LOGS_INFO, "Can't synchronise: no selectable sources", NULL);
return;
}
@@ -1080,8 +1161,13 @@ SRC_SelectSource(SRC_Instance updated_inst)
(best_trust_depth > 0 && best_trust_depth <= n_sel_trust_sources / 2)) {
/* Could not even get half the reachable (trusted) sources to agree */
if (!reported_no_majority) {
log_selection_message(LOGS_WARN, "Can't synchronise: no majority", NULL);
reported_no_majority = 1;
report_selection_loss = 0;
}
if (selected_source_index != INVALID_SOURCE) {
log_selection_message("Can't synchronise: no majority", NULL);
REF_SetUnsynchronised();
selected_source_index = INVALID_SOURCE;
}
@@ -1127,16 +1213,17 @@ SRC_SelectSource(SRC_Instance updated_inst)
sel_req_source = 0;
} else {
mark_source(sources[i], SRC_FALSETICKER);
if (!sources[i]->reported_falseticker) {
log_selection_source(LOGS_WARN, "Detected falseticker %s", sources[i]);
sources[i]->reported_falseticker = 1;
}
}
}
if (!n_sel_sources || sel_req_source || n_sel_sources < CNF_GetMinSources()) {
if (selected_source_index != INVALID_SOURCE) {
log_selection_message("Can't synchronise: %s selectable sources",
!n_sel_sources ? "no" :
sel_req_source ? "no required source in" : "not enough");
selected_source_index = INVALID_SOURCE;
}
unselect_selected_source(LOGS_INFO, "Can't synchronise: %s selectable sources",
!n_sel_sources ? "no" :
sel_req_source ? "no required source in" : "not enough");
mark_ok_sources(SRC_WAITS_SOURCES);
return;
}
@@ -1243,19 +1330,24 @@ SRC_SelectSource(SRC_Instance updated_inst)
/* Before selecting the new synchronisation source wait until the reference
can be updated */
if (sources[max_score_index]->updates == 0) {
selected_source_index = INVALID_SOURCE;
unselect_selected_source(LOGS_INFO, NULL, NULL);
mark_ok_sources(SRC_WAITS_UPDATE);
return;
}
selected_source_index = max_score_index;
log_selection_source("Selected source %s", sources[selected_source_index]);
log_selection_source(LOGS_INFO, "Selected source %s", sources[selected_source_index]);
/* New source has been selected, reset all scores */
for (i = 0; i < n_sources; i++) {
sources[i]->sel_score = 1.0;
sources[i]->distant = 0;
sources[i]->reported_falseticker = 0;
}
reported_no_majority = 0;
report_selection_loss = 0;
forced_first_report = 1;
}
mark_source(sources[selected_source_index], SRC_SELECTED);
@@ -1546,6 +1638,8 @@ SRC_ResetSources(void)
for (i = 0; i < n_sources; i++)
SRC_ResetInstance(sources[i]);
LOG(LOGS_INFO, "Reset all sources");
}
/* ================================================== */
@@ -1593,6 +1687,46 @@ SRC_ActiveSources(void)
/* ================================================== */
static SRC_Instance
find_source(IPAddr *ip, uint32_t ref_id)
{
int i;
for (i = 0; i < n_sources; i++) {
if ((ip->family != IPADDR_UNSPEC && sources[i]->type == SRC_NTP &&
UTI_CompareIPs(ip, sources[i]->ip_addr, NULL) == 0) ||
(ip->family == IPADDR_UNSPEC && sources[i]->type == SRC_REFCLOCK &&
ref_id == sources[i]->ref_id))
return sources[i];
}
return NULL;
}
/* ================================================== */
int
SRC_ModifySelectOptions(IPAddr *ip, uint32_t ref_id, int options, int mask)
{
SRC_Instance inst;
inst = find_source(ip, ref_id);
if (!inst)
return 0;
if ((inst->conf_sel_options & mask) == options)
return 1;
inst->conf_sel_options = (inst->conf_sel_options & ~mask) | options;
LOG(LOGS_INFO, "Source %s selection options modified", source_to_string(inst));
update_sel_options();
return 1;
}
/* ================================================== */
int
SRC_ReportSource(int index, RPT_SourceReport *report, struct timespec *now)
{
@@ -1676,10 +1810,10 @@ 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_UNSYNCHRONISED:
return 's';
case SRC_BAD_DISTANCE:
return 'd';
case SRC_JITTERY:

View File

@@ -131,6 +131,10 @@ extern int SRC_IsReachable(SRC_Instance inst);
extern int SRC_ReadNumberOfSources(void);
extern int SRC_ActiveSources(void);
/* Modify selection options of an NTP source specified by address, or
refclock specified by its reference ID */
extern int SRC_ModifySelectOptions(IPAddr *ip, uint32_t ref_id, int options, int mask);
extern int SRC_ReportSource(int index, RPT_SourceReport *report, struct timespec *now);
extern int SRC_ReportSourcestats(int index, RPT_SourcestatsReport *report, struct timespec *now);
extern int SRC_GetSelectReport(int index, RPT_SelectReport *report);

View File

@@ -80,7 +80,7 @@ static LOG_FileID logfileid;
struct SST_Stats_Record {
/* Reference ID and IP address of source, used for logging to statistics log */
/* Reference ID and IP address (NULL if not an NTP source) */
uint32_t refid;
IPAddr *ip_addr;
@@ -549,9 +549,9 @@ SST_DoNewRegression(SST_Stats inst)
sd_weight += (peer_distances[i] - min_distance) / sd;
weights[i] = SQUARE(sd_weight);
}
}
correct_asymmetry(inst, times_back, offsets);
correct_asymmetry(inst, times_back, offsets);
}
inst->regression_ok = RGR_FindBestRegression(times_back + inst->runs_samples,
offsets + inst->runs_samples, weights,
@@ -964,9 +964,10 @@ SST_DoSourceReport(SST_Stats inst, RPT_SourceReport *report, struct timespec *no
report->latest_meas = inst->offsets[i];
report->latest_meas_err = 0.5*inst->root_delays[j] + inst->root_dispersions[j];
/* Align the sample time to reduce the leak of the receive timestamp */
/* Align the sample time to reduce the leak of the NTP receive timestamp */
last_sample_time = inst->sample_times[i];
last_sample_time.tv_nsec = 0;
if (inst->ip_addr)
last_sample_time.tv_nsec = 0;
report->latest_meas_ago = UTI_DiffTimespecsToDouble(now, &last_sample_time);
} else {
report->latest_meas_ago = (uint32_t)-1;

20
stubs.c
View File

@@ -201,12 +201,18 @@ NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
}
NSR_Status
NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
NSR_AddSourceByName(char *name, int family, int port, int pool, NTP_Source_Type type,
SourceParameters *params, uint32_t *conf_id)
{
return NSR_TooManySources;
}
const char *
NSR_StatusToString(NSR_Status status)
{
return "NTP not supported";
}
NSR_Status
NSR_RemoveSource(IPAddr *address)
{
@@ -314,6 +320,12 @@ NSR_ModifyMinstratum(IPAddr *address, int new_min_stratum)
return 0;
}
int
NSR_ModifyOffset(IPAddr *address, double new_offset)
{
return 0;
}
int
NSR_ModifyPolltarget(IPAddr *address, int new_poll_target)
{
@@ -413,6 +425,12 @@ RCL_ReportSource(RPT_SourceReport *report, struct timespec *now)
memset(report, 0, sizeof (*report));
}
int
RCL_ModifyOffset(uint32_t ref_id, double offset)
{
return 0;
}
#endif /* !FEAT_REFCLOCK */
#ifndef FEAT_SIGND

View File

@@ -35,6 +35,7 @@
#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
#include <linux/ptp_clock.h>
#include <poll.h>
#endif
#ifdef FEAT_SCFILTER
@@ -497,6 +498,9 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context)
SCMP_SYS(getrlimit),
SCMP_SYS(getuid),
SCMP_SYS(getuid32),
#ifdef __NR_membarrier
SCMP_SYS(membarrier),
#endif
#ifdef __NR_rseq
SCMP_SYS(rseq),
#endif
@@ -599,6 +603,7 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context)
SCMP_SYS(select),
SCMP_SYS(set_robust_list),
SCMP_SYS(write),
SCMP_SYS(writev),
/* Miscellaneous */
SCMP_SYS(getrandom),
@@ -633,6 +638,9 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context)
{ SOL_IP, IP_PKTINFO }, { SOL_IP, IP_FREEBIND }, { SOL_IP, IP_TOS },
#ifdef FEAT_IPV6
{ SOL_IPV6, IPV6_V6ONLY }, { SOL_IPV6, IPV6_RECVPKTINFO },
#ifdef IPV6_TCLASS
{ SOL_IPV6, IPV6_TCLASS },
#endif
#endif
#ifdef SO_BINDTODEVICE
{ SOL_SOCKET, SO_BINDTODEVICE },
@@ -650,7 +658,7 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context)
const static int fcntls[] = { F_GETFD, F_SETFD, F_GETFL, F_SETFL };
const static unsigned long ioctls[] = {
FIONREAD, TCGETS,
FIONREAD, TCGETS, TIOCGWINSZ,
#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
PTP_EXTTS_REQUEST, PTP_SYS_OFFSET,
#ifdef PTP_PIN_SETFUNC
@@ -982,6 +990,14 @@ SYS_Linux_SetPHCExtTimestamping(int fd, int pin, int channel,
return 0;
}
#if defined(PTP_MASK_CLEAR_ALL) && defined(PTP_MASK_EN_SINGLE)
/* Disable events from other channels on this descriptor */
if (ioctl(fd, PTP_MASK_CLEAR_ALL))
DEBUG_LOG("ioctl(%s) failed : %s", "PTP_MASK_CLEAR_ALL", strerror(errno));
else if (ioctl(fd, PTP_MASK_EN_SINGLE, &channel))
DEBUG_LOG("ioctl(%s) failed : %s", "PTP_MASK_EN_SINGLE", strerror(errno));
#endif
return 1;
}
@@ -991,6 +1007,16 @@ int
SYS_Linux_ReadPHCExtTimestamp(int fd, struct timespec *phc_ts, int *channel)
{
struct ptp_extts_event extts_event;
struct pollfd pfd;
/* Make sure the read will not block in case we have multiple
descriptors of the same PHC (O_NONBLOCK does not work) */
pfd.fd = fd;
pfd.events = POLLIN;
if (poll(&pfd, 1, 0) != 1 || pfd.revents != POLLIN) {
DEBUG_LOG("Missing PHC extts event");
return 0;
}
if (read(fd, &extts_event, sizeof (extts_event)) != sizeof (extts_event)) {
DEBUG_LOG("Could not read PHC extts event");

View File

@@ -66,10 +66,9 @@ get_tempcomp(double temp)
return k0 + (temp - T0) * k1 + (temp - T0) * (temp - T0) * k2;
/* Otherwise interpolate/extrapolate between two nearest points */
for (i = 1; i < ARR_GetSize(points); i++) {
p2 = (struct Point *)ARR_GetElement(points, i);
if (p2->temp >= temp)
for (i = 1; ; i++) {
p2 = ARR_GetElement(points, i);
if (p2->temp >= temp || i + 1 >= ARR_GetSize(points))
break;
}
p1 = p2 - 1;

View File

@@ -3,13 +3,14 @@
cd ../..
for opts in \
"--enable-debug" \
"--host-system=Linux" \
"--host-system=NetBSD" \
"--host-system=FreeBSD" \
"--without-nettle" \
"--without-nettle --without-nss" \
"--without-nettle --without-nss --without-tomcrypt" \
"--without-nettle --without-nss --without-tomcrypt --without-gnutls"
"--without-nettle --without-gnutls" \
"--without-nettle --without-gnutls --without-nss" \
"--without-nettle --without-gnutls --without-nss --without-tomcrypt"
do
./configure $opts
scan-build make "$@" || exit 1

View File

@@ -25,12 +25,13 @@ touch Makefile
for extra_config_opts in \
"--all-privops" \
"--disable-ipv6" \
"--disable-nts" \
"--disable-scfilter" \
"--without-gnutls" \
"--without-aes-gcm-siv" \
"--without-nettle" \
"--without-nettle --without-nss" \
"--without-nettle --without-nss --without-tomcrypt" \
"--without-nettle --without-nss --without-tomcrypt --without-gnutls"; \
"--without-nettle --without-gnutls" \
"--without-nettle --without-gnutls --without-nss" \
"--without-nettle --without-gnutls --without-nss --without-tomcrypt"; \
do
for arch_opts in "-m32" ""; do
pushd test/simulation/clknetsim || exit 1
@@ -44,7 +45,7 @@ do
export CC
for san_options in "" "-fsanitize=address" "-fsanitize=memory"; do
export CFLAGS="-O2 -g -fsanitize=undefined -fsanitize=float-divide-by-zero -fno-sanitize-recover=undefined,float-divide-by-zero $san_options $arch_opts"
export CFLAGS="-O2 -g -fsanitize=undefined -fsanitize=float-divide-by-zero -fsanitize=float-cast-overflow -fno-sanitize-recover=all $san_options $arch_opts"
# clang msan doesn't work on i686 and otherwise requires patches
echo $CFLAGS | grep -q 'sanitize=memory' && continue

View File

@@ -41,7 +41,6 @@ for time_offset in -1e-1 1e-1; 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

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env bash
. ./test.common
test_start "SHM refclock"
test_start "reference clocks"
check_config_h 'FEAT_REFCLOCK 1' || test_skip
check_config_h 'FEAT_PHC 1' || test_skip

View File

@@ -91,6 +91,18 @@ check_chronyd_exit || test_fail
check_chronyc_output "^C0A87B01,192\.168\.123\.1,2,12623049..\..........,-?0\.0000.....,-?0\.000......,0\.000......,(99|100)\....,-?[0-9]\....,[0-9]\....,0\.000......,0\.000......,[0-9]+\..,Normal$" \
|| test_fail
chronyc_options="-c -e"
chronyc_conf="sources"
run_test || test_fail
check_chronyd_exit || test_fail
check_chronyc_output "^#,.,SHM0.*
\^,.,192\.168\.123\.1.*
\^,.,192\.168\.123\.2.*
\.$" \
|| test_fail
chronyc_options=""
server_strata=0
chronyc_start=0.5
@@ -102,7 +114,7 @@ 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 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 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 extfield F324 ipv6 ipv4" \
"add server node1.net1.clk" \
"allow 1.2.3.4" \
"allow 1.2" \
@@ -133,7 +145,7 @@ for chronyc_conf in \
"dfreq 1.0e-3" \
"doffset -1.0" \
"dump" \
"local stratum 5 distance 1.0 orphan" \
"local stratum 5 distance 1.0 activate 0.5 orphan" \
"local off" \
"makestep 10.0 3" \
"makestep" \
@@ -153,6 +165,7 @@ for chronyc_conf in \
"offline" \
"offline 255.255.255.0/1.2.3.0" \
"offline 1.2.3.0/24" \
"offset 1.2.3.4 1.0" \
"online" \
"online 1.2.3.0/24" \
"onoffline" \
@@ -164,6 +177,9 @@ for chronyc_conf in \
"reselectdist 1e-3" \
"reset sources" \
"selectdata" \
"selectopts 1.2.3.4 -noselect +trust +require +prefer" \
"selectopts ID#0000000001 +prefer" \
"selectopts PPS0 +prefer" \
"settime 16:30" \
"settime 16:30:05" \
"settime Nov 21, 2015 16:30:05" \
@@ -232,6 +248,10 @@ Total TX : 1
Total RX : 1
Total valid RX : 1
Total good RX : 0
Total kernel TX : [01]
Total kernel RX : 1
Total HW TX : 0
Total HW RX : 0
S Name/IP Address Auth COpts EOpts Last Score Interval Leap
=======================================================================
M node1\.net1\.clk N ----- ----- 0 1\.0 \+0ns \+0ns N
@@ -245,7 +265,13 @@ NTS-KE connections dropped : 0
Authenticated NTP packets : 0
Interleaved NTP packets : 0
NTP timestamps held : 0
NTP timestamp span : 0$" || test_fail
NTP timestamp span : 0
NTP daemon RX timestamps : 0
NTP daemon TX timestamps : 1
NTP kernel RX timestamps : 1
NTP kernel TX timestamps : 0
NTP hardware RX timestamps : 0
NTP hardware TX timestamps : 0$" || test_fail
chronyc_conf="
deny all
@@ -326,7 +352,12 @@ 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
offset 192.168.123.1 -1.0
polltarget 192.168.123.1 10
selectopts 192.168.123.1 +trust +prefer -require
selectdata
selectopts 192.168.123.1 +noselect -prefer -trust +require
selectdata
delete 192.168.123.1"
run_test || test_fail
@@ -345,6 +376,15 @@ check_chronyc_output "^200 OK
200 OK
200 OK
200 OK
200 OK
200 OK
S Name/IP Address Auth COpts EOpts Last Score Interval Leap
=======================================================================
M node1\.net1\.clk N \-PT\-\- \-PT\-\- 0 1\.0 \+0ns \+0ns \?
200 OK
S Name/IP Address Auth COpts EOpts Last Score Interval Leap
=======================================================================
M node1\.net1\.clk N N\-\-R\- N\-\-R\- 0 1\.0 \+0ns \+0ns \?
200 OK$" || test_fail
chronyc_conf="
@@ -400,7 +440,12 @@ server_conf="
server 192.168.123.1
noclientlog"
commands=(
check_config_h 'FEAT_IPV6 1' && commands=(
"add server ::1 ipv4" "^515 Invalid address family$"
) || commands=()
commands+=(
"add server 192.168.123.1 ipv6" "^515 Invalid address family$"
"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$"

View File

@@ -8,54 +8,86 @@ check_config_h 'FEAT_REFCLOCK 1' || test_skip
export CLKNETSIM_START_DATE=$(TZ=UTC date -d 'Dec 30 2008 0:00:00' +'%s')
leap=$[2 * 24 * 3600]
limit=$[4 * 24 * 3600]
client_start=$[2 * 3600]
server_conf="refclock SHM 0 dpoll 10 poll 10
leapsectz right/UTC"
refclock_jitter=1e-9
refclock_offset="(* -1.0 (equal 0.1 (max (sum 1.0) $leap) $leap))"
for leapmode in system step slew; do
client_conf="leapsecmode $leapmode"
if [ $leapmode = slew ]; then
max_sync_time=$[$leap + 12]
else
max_sync_time=$[$leap]
fi
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
done
client_server_options="trust"
client_conf="refclock SHM 0 dpoll 10 poll 10 delay 1e-3"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
client_server_options=""
client_conf="leapsecmode system"
min_sync_time=230000
max_sync_time=240000
for smoothmode in "" "leaponly"; do
for dir in "+1" "-1"; do
leap=$[2 * 24 * 3600 + 1 + $dir]
server_conf="refclock SHM 0 dpoll 10 poll 10
leapsectz right/UTC
leapsecmode slew
smoothtime 400 0.001 $smoothmode"
leapseclist tmp/leap.list"
refclock_offset="(* $dir (equal 0.1 (max (sum 1.0) $leap) $leap))"
cat > tmp/leap.list <<-EOF
#$ 3676924800
#@ 3928521600
3345062400 33 # 1 Jan 2006
3439756800 $[33 - $dir] # 1 Jan 2009 $(
[ "$dir" = "+1" ] && echo -e "\n3471292800 33\n3502828800 34")
3550089600 35 # 1 Jul 2012
EOF
for leapmode in system step slew; do
client_conf="leapsecmode $leapmode"
if [ $leapmode = slew ]; then
max_sync_time=$[2 * 24 * 3600 + 13]
else
max_sync_time=$[2 * 24 * 3600 + 1]
fi
min_sync_time=$[$max_sync_time - 2]
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
check_file_messages "System clock TAI offset set to" 1 1 log.1 || test_fail
check_file_messages "System clock TAI offset set to 33" 1 1 log.1 || test_fail
done
client_server_options="trust"
client_conf="refclock SHM 0 dpoll 10 poll 10 delay 1e-3"
min_sync_time=$[$leap - 2]
max_sync_time=$[$leap]
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
client_server_options=""
client_conf="leapsecmode system"
min_sync_time=230000
max_sync_time=240000
for smoothmode in "" "leaponly"; do
server_conf="refclock SHM 0 dpoll 10 poll 10
leapseclist tmp/leap.list
leapsecmode slew
smoothtime 400 0.001 $smoothmode"
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
if TZ=right/UTC date -d 'Dec 31 2008 23:59:60' 2> /dev/null | grep :60; then
server_conf="refclock SHM 0 dpoll 10 poll 10
leapsectz right/UTC"
refclock_offset="(* -1 (equal 0.1 (max (sum 1.0) $leap) $leap))"
client_conf="leapsecmode system"
min_sync_time=$[$leap - 2]
max_sync_time=$[$leap]
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
fi
test_pass

View File

@@ -3,6 +3,7 @@
. ./test.common
test_start "presend option"
limit=9900
min_sync_time=136
max_sync_time=260
client_server_options="presend 6 maxdelay 16"
@@ -22,4 +23,29 @@ check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
limit=10
base_delay=$default_base_delay
client_conf="logdir tmp
log measurements"
client_server_options="presend 5"
run_test || test_fail
check_chronyd_exit || test_fail
check_packet_interval || test_fail
check_file_messages "20.*123\.1.* 111 111 0111" 1 1 measurements.log || test_fail
check_file_messages "20.*123\.1.* 111 111 1111" 1 1 measurements.log || test_fail
rm -f tmp/measurements.log
client_server_options="presend 5 xleave"
run_test || test_fail
check_chronyd_exit || test_fail
check_packet_interval || test_fail
check_file_messages "20.*123\.1.* 111 111 0111" 2 2 measurements.log || test_fail
check_file_messages "20.*123\.1.* 111 111 1111" 1 1 measurements.log || test_fail
rm -f tmp/measurements.log
test_pass

View File

@@ -14,7 +14,6 @@ client_server_options="maxpoll 6 maxdelay 3e-5 maxdelayratio 2.0 maxdelaydevrati
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail

View File

@@ -14,7 +14,7 @@ max_sync_time=800
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_source_selection && test_fail
check_sync || test_fail
limit=10000

90
test/simulation/121-local Executable file
View File

@@ -0,0 +1,90 @@
#!/usr/bin/env bash
. ./test.common
test_start "local options"
check_config_h 'FEAT_CMDMON 1' || test_skip
server_strata=3
server_conf="local stratum 5 orphan
server 192.168.123.1
server 192.168.123.2
server 192.168.123.3"
max_sync_time=900
client_start=140
chronyc_start=700
chronyc_conf="tracking"
time_rms_limit=5e-4
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
check_chronyc_output "^.*Stratum *: 7.*$" || test_fail
limit=4000
wander=0.0
jitter=0.0
server_strata=1
server_conf=""
client_server_options="minpoll 6 maxpoll 6 minsamples 64"
chronyc_start=1
chronyc_conf="timeout 1000000
tracking
tracking
tracking
tracking"
base_delay=$(cat <<-EOF | tr -d '\n'
(+ 1e-4
(* 990
(equal 0.1 from 3))
(* -1
(equal 0.1 from 1)
(equal 0.1 (max (% time 2000) 1000) 1000)))
EOF
)
client_conf="local
maxclockerror 1000"
run_test || test_fail
check_chronyd_exit || test_fail
check_chronyc_output "^.*7F7F0101.*C0A87B01.*7F7F0101.*C0A87B01.*$" || test_fail
client_conf="local distance 0.5
maxclockerror 1000"
run_test || test_fail
check_chronyd_exit || test_fail
check_chronyc_output "^.*7F7F0101.*C0A87B01.*7F7F0101.*C0A87B01.*$" || test_fail
client_conf="local distance 2.0
maxclockerror 1000"
run_test || test_fail
check_chronyd_exit || test_fail
check_chronyc_output "^.*7F7F0101.*C0A87B01.*C0A87B01.*C0A87B01.*$" || test_fail
client_conf="local activate 1e-4
maxclockerror 1000"
run_test || test_fail
check_chronyd_exit || test_fail
check_chronyc_output "^.* 00000000 .*C0A87B01.*C0A87B01.*C0A87B01.*$" || test_fail
client_conf="local activate 1e-1
maxclockerror 1000"
run_test || test_fail
check_chronyd_exit || test_fail
check_chronyc_output "^.* 00000000 .*C0A87B01.*7F7F0101.*C0A87B01.*$" || test_fail
client_conf="local activate 1e-1 distance 2.0
maxclockerror 1000"
run_test || test_fail
check_chronyd_exit || test_fail
check_chronyc_output "^.* 00000000 .*C0A87B01.*C0A87B01.*C0A87B01.*$" || test_fail
test_pass

View File

@@ -1,26 +0,0 @@
#!/usr/bin/env bash
. ./test.common
test_start "orphan option"
check_config_h 'FEAT_CMDMON 1' || test_skip
server_strata=3
server_conf="local stratum 5 orphan
server 192.168.123.1
server 192.168.123.2
server 192.168.123.3"
max_sync_time=900
client_start=140
chronyc_start=700
chronyc_conf="tracking"
time_rms_limit=5e-4
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
check_chronyc_output "^.*Stratum *: 7.*$" || test_fail
test_pass

View File

@@ -53,7 +53,6 @@ for rpoll in 4 5 6; do
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
if [ $rpoll -le 5 ]; then

View File

@@ -18,9 +18,17 @@ servers=0
refclock_offset="(+ -34 (equal 0.1 (max (sum 1.0) $leap) $leap))"
client_conf="
refclock SHM 0 dpoll 0 poll 0 tai
leapsectz right/UTC
leapseclist tmp/leap.list
leapsecmode ignore
maxchange 1e-3 1 0"
maxchange 1e-3 10 0"
cat > tmp/leap.list <<-EOF
#$ 3676924800
#@ 3928521600
3345062400 33 # 1 Jan 2006
3439756800 34 # 1 Jan 2009
3550089600 35 # 1 Jul 2012
EOF
run_test || test_fail
check_chronyd_exit || test_fail
@@ -33,9 +41,9 @@ time_offset=-1000
refclock_offset="(+ -34)"
client_conf="
refclock SHM 0 dpoll 0 poll 0 tai
leapsectz right/UTC
leapseclist tmp/leap.list
makestep 1 1
maxchange 1e-3 1 0"
maxchange 1e-3 10 0"
run_test || test_fail
check_chronyd_exit || test_fail

View File

@@ -22,7 +22,7 @@ client_min_mean_out_interval=150.0
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_source_selection && test_fail
check_packet_interval || test_fail
check_sync || test_fail

View File

@@ -12,7 +12,7 @@ client_min_mean_out_interval=15.9
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_source_selection && test_fail
check_packet_interval || test_fail
check_sync || test_fail

View File

@@ -16,6 +16,6 @@ check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
check_log_messages "clock wrong by" 4 8 || test_fail
check_log_messages "clock wrong by" 3 8 || test_fail
test_pass

View File

@@ -47,9 +47,10 @@ for client_conf in \
check_log_messages "Received error.*message.*tss=KH" 195 200 || test_fail
check_log_messages "Updated RX timestamp.*tss=1" 1 1 || test_fail
check_log_messages "Updated RX timestamp.*tss=2" 195 200 || test_fail
check_log_messages "Polling PHC" 195 220 || test_fail
if echo "$client_conf" | grep -q nocrossts; then
check_log_messages "update_tx_timestamp.*Updated" 180 200 || test_fail
check_log_messages "update_tx_timestamp.*Unacceptable" 0 10 || test_fail
check_log_messages "update_tx_timestamp.*Unacceptable" 0 13 || test_fail
else
check_log_messages "update_tx_timestamp.*Updated" 50 140 || test_fail
check_log_messages "update_tx_timestamp.*Unacceptable" 50 140 || test_fail
@@ -57,4 +58,32 @@ for client_conf in \
fi
done
server_conf+="
server 192.168.123.2 minpoll 1 maxpoll 1 noselect"
for maxpoll in -1 0 1; do
client_conf="hwtimestamp eth0 minpoll -1 maxpoll $maxpoll nocrossts"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
if check_config_h 'FEAT_DEBUG 1'; then
case $maxpoll in
-1)
check_log_messages "Polling PHC on eth0$" 360 380 || test_fail
check_log_messages "Polling PHC.*before" 3 25 || test_fail
;;
0)
check_log_messages "Polling PHC on eth0$" 8 45 || test_fail
check_log_messages "Polling PHC.*before" 150 190 || test_fail
;;
1)
check_log_messages "Polling PHC on eth0$" 1 1 || test_fail
check_log_messages "Polling PHC.*before" 194 199 || test_fail
;;
esac
fi
done
test_pass

View File

@@ -15,4 +15,15 @@ check_sync || test_fail
check_file_messages " 2 1 " 1200 1300 log.packets || test_fail
check_file_messages " 1 2 " 180 220 log.packets || test_fail
server_conf="ratelimit interval 6 burst 2 leak 4 kod 2"
run_test || test_fail
check_chronyd_exit || test_fail
check_packet_interval || test_fail
check_sync || test_fail
check_file_messages " 2 1 " 700 850 log.packets || test_fail
check_file_messages " 1 2 " 350 450 log.packets || test_fail
check_log_messages "Received KoD RATE.*\.123.1" 100 140 || test_fail
test_pass

View File

@@ -158,10 +158,10 @@ for dns in 1 0; do
check_source_selection && test_fail
check_sync && test_fail
check_file_messages " 2 1 .* 4460 " 50 100 log.packets || test_fail
check_file_messages " 2 1 .* 4460 " 45 100 log.packets || test_fail
check_file_messages " 2 2 .* 4460 " 0 0 log.packets || test_fail
check_log_messages "Source 192.168.123.1 changed to 192.168.123.2" 6 8 || test_fail
check_log_messages "Source 192.168.123.2 replaced with 192.168.123.1" 6 8 || test_fail
check_log_messages "Source 192.168.123.1 changed to 192.168.123.2" 4 10 || test_fail
check_log_messages "Source 192.168.123.2 replaced with 192.168.123.1" 3 10 || test_fail
servers=2
@@ -225,6 +225,8 @@ for dns in 1 0; do
check_file_messages " 3 2 .* 4460 " 0 0 log.packets || test_fail
done
min_sync_time=$[default_min_sync_time + 200]
max_sync_time=600
server_conf="
ntsserverkey tmp/server1.key
ntsservercert tmp/server1.crt
@@ -248,6 +250,8 @@ check_file_messages " 3 2 .* 123 " 0 0 log.packets || test_fail
check_file_messages " 3 2 .* 11123 " 3 3 log.packets || test_fail
dns=1
min_sync_time=$default_min_sync_time
max_sync_time=400
server_conf="
ntsserverkey tmp/server1.key
ntsservercert tmp/server1.crt

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