Compare commits

..

177 Commits

Author SHA1 Message Date
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
Miroslav Lichvar
7b197953e8 update copyright years 2022-08-29 15:04:33 +02:00
Miroslav Lichvar
9dcace0fc4 doc: improve description of server directive 2022-08-29 15:03:59 +02:00
Miroslav Lichvar
a07ac38331 doc: improve description of system time in tracking report 2022-08-29 12:39:22 +02:00
Miroslav Lichvar
166e43b13e cmdmon: add good responses to ntpdata report 2022-08-18 11:59:40 +02:00
Miroslav Lichvar
b84d6759f9 ntp: initialize remote address in ntpdata report
Don't wait for the first response with setting the address.
2022-08-17 16:14:38 +02:00
112 changed files with 4192 additions and 1178 deletions

View File

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

40
NEWS
View File

@@ -1,3 +1,43 @@
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 New in version 4.3
================== ==================

33
README
View File

@@ -47,32 +47,7 @@ Frequently Asked Questions (FAQ).
The documentation is also available on the chrony web pages, accessible The documentation is also available on the chrony web pages, accessible
through the URL through the URL
https://chrony.tuxfamily.org/ https://chrony-project.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.
License License
======= =======
@@ -108,6 +83,7 @@ Erik Bryer <ebryer@spots.ab.ca>
Jonathan Cameron <jic23@cam.ac.uk> Jonathan Cameron <jic23@cam.ac.uk>
Bryan Christianson <bryan@whatroute.net> Bryan Christianson <bryan@whatroute.net>
Juliusz Chroboczek <jch@pps.jussieu.fr> Juliusz Chroboczek <jch@pps.jussieu.fr>
Dan Drown <dan-ntp@drown.org>
Kamil Dudka <kdudka@redhat.com> Kamil Dudka <kdudka@redhat.com>
Christian Ehrhardt <christian.ehrhardt@canonical.com> Christian Ehrhardt <christian.ehrhardt@canonical.com>
Paul Elliott <pelliott@io.com> Paul Elliott <pelliott@io.com>
@@ -121,6 +97,7 @@ Juergen Hannken-Illjes <hannken@eis.cs.tu-bs.de>
John Hasler <john@dhh.gt.org> John Hasler <john@dhh.gt.org>
Tjalling Hattink <t.hattink@fugro.nl> Tjalling Hattink <t.hattink@fugro.nl>
Liam Hatton <me@liamhatton.com> Liam Hatton <me@liamhatton.com>
Holger Hoffstätte <holger@applied-asynchrony.com>
Jachym Holecek <jakym@volny.cz> Jachym Holecek <jakym@volny.cz>
Håkan Johansson <f96hajo@chalmers.se> Håkan Johansson <f96hajo@chalmers.se>
Jim Knoble <jmknoble@pobox.com> Jim Knoble <jmknoble@pobox.com>
@@ -136,15 +113,19 @@ Victor Moroz <vim@prv.adlum.ru>
Kalle Olavi Niemitalo <tosi@stekt.oulu.fi> Kalle Olavi Niemitalo <tosi@stekt.oulu.fi>
Frank Otto <sandwichmacher@web.de> Frank Otto <sandwichmacher@web.de>
Denny Page <dennypage@me.com> Denny Page <dennypage@me.com>
Rupesh Patel <rupatel@redhat.com>
Chris Perl <cperl@janestreet.com> Chris Perl <cperl@janestreet.com>
Gautier PHILIPPON <gautier.philippon@ensimag.grenoble-inp.fr> Gautier PHILIPPON <gautier.philippon@ensimag.grenoble-inp.fr>
Andreas Piesk <apiesk@virbus.de> Andreas Piesk <apiesk@virbus.de>
Mike Ryan <msr@hsilop.net>
Baruch Siach <baruch@tkos.co.il> Baruch Siach <baruch@tkos.co.il>
Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
Foster Snowhill <forst@forstwoof.ru> Foster Snowhill <forst@forstwoof.ru>
Andreas Steinmetz <ast@domdv.de> Andreas Steinmetz <ast@domdv.de>
NAKAMURA Takumi <takumi@ps.sakura.ne.jp> NAKAMURA Takumi <takumi@ps.sakura.ne.jp>
Timo Teras <timo.teras@iki.fi> Timo Teras <timo.teras@iki.fi>
Bill Unruh <unruh@physics.ubc.ca> Bill Unruh <unruh@physics.ubc.ca>
Luke Valenta <lvalenta@cloudflare.com>
Stephen Wadeley <swadeley@redhat.com> Stephen Wadeley <swadeley@redhat.com>
Bernhard Weiss <lisnablagh@web.de> Bernhard Weiss <lisnablagh@web.de>
Wolfgang Weisselberg <weissel@netcologne.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); 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 void
ARR_SetSize(ARR_Instance array, unsigned int size) 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 */ /* Add a new element to the end of the array */
extern void ARR_AppendElement(ARR_Instance array, void *element); 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 */ /* Set the size of the array */
extern void ARR_SetSize(ARR_Instance array, unsigned int size); extern void ARR_SetSize(ARR_Instance array, unsigned int size);

57
candm.h
View File

@@ -109,7 +109,8 @@
#define REQ_SELECT_DATA 69 #define REQ_SELECT_DATA 69
#define REQ_RELOAD_SOURCES 70 #define REQ_RELOAD_SOURCES 70
#define REQ_DOFFSET2 71 #define REQ_DOFFSET2 71
#define N_REQUEST_TYPES 72 #define REQ_MODIFY_SELECTOPTS 72
#define N_REQUEST_TYPES 73
/* Structure used to exchange timespecs independent of time_t size */ /* Structure used to exchange timespecs independent of time_t size */
typedef struct { typedef struct {
@@ -121,6 +122,12 @@ typedef struct {
/* This is used in tv_sec_high for 32-bit timestamps */ /* This is used in tv_sec_high for 32-bit timestamps */
#define TV_NOHIGHSEC 0x7fffffff #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 /* 32-bit floating-point format consisting of 7-bit signed exponent
and 25-bit signed coefficient without hidden bit. and 25-bit signed coefficient without hidden bit.
The result is calculated as: 2^(exp - 25) * coef */ The result is calculated as: 2^(exp - 25) * coef */
@@ -270,7 +277,8 @@ typedef struct {
#define REQ_ADDSRC_BURST 0x100 #define REQ_ADDSRC_BURST 0x100
#define REQ_ADDSRC_NTS 0x200 #define REQ_ADDSRC_NTS 0x200
#define REQ_ADDSRC_COPY 0x400 #define REQ_ADDSRC_COPY 0x400
#define REQ_ADDSRC_EF_EXP1 0x800 #define REQ_ADDSRC_EF_EXP_MONO_ROOT 0x800
#define REQ_ADDSRC_EF_EXP_NET_CORRECTION 0x1000
typedef struct { typedef struct {
uint32_t type; uint32_t type;
@@ -371,6 +379,15 @@ typedef struct {
int32_t EOR; int32_t EOR;
} REQ_SelectData; } 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;
/* ================================================== */ /* ================================================== */
#define PKT_TYPE_CMD_REQUEST 1 #define PKT_TYPE_CMD_REQUEST 1
@@ -477,6 +494,7 @@ typedef struct {
REQ_NTPSourceName ntp_source_name; REQ_NTPSourceName ntp_source_name;
REQ_AuthData auth_data; REQ_AuthData auth_data;
REQ_SelectData select_data; REQ_SelectData select_data;
REQ_Modify_SelectOpts modify_select_opts;
} data; /* Command specific parameters */ } data; /* Command specific parameters */
/* Padding used to prevent traffic amplification. It only defines the /* Padding used to prevent traffic amplification. It only defines the
@@ -519,7 +537,8 @@ typedef struct {
#define RPY_SERVER_STATS2 22 #define RPY_SERVER_STATS2 22
#define RPY_SELECT_DATA 23 #define RPY_SELECT_DATA 23
#define RPY_SERVER_STATS3 24 #define RPY_SERVER_STATS3 24
#define N_REPLY_TYPES 25 #define RPY_SERVER_STATS4 25
#define N_REPLY_TYPES 26
/* Status codes */ /* Status codes */
#define STT_SUCCESS 0 #define STT_SUCCESS 0
@@ -654,17 +673,24 @@ typedef struct {
} RPY_ClientAccessesByIndex; } RPY_ClientAccessesByIndex;
typedef struct { typedef struct {
uint32_t ntp_hits; Integer64 ntp_hits;
uint32_t nke_hits; Integer64 nke_hits;
uint32_t cmd_hits; Integer64 cmd_hits;
uint32_t ntp_drops; Integer64 ntp_drops;
uint32_t nke_drops; Integer64 nke_drops;
uint32_t cmd_drops; Integer64 cmd_drops;
uint32_t log_drops; Integer64 log_drops;
uint32_t ntp_auth_hits; Integer64 ntp_auth_hits;
uint32_t ntp_interleaved_hits; Integer64 ntp_interleaved_hits;
uint32_t ntp_timestamps; Integer64 ntp_timestamps;
uint32_t ntp_span_seconds; 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; int32_t EOR;
} RPY_ServerStats; } RPY_ServerStats;
@@ -734,7 +760,8 @@ typedef struct {
uint32_t total_tx_count; uint32_t total_tx_count;
uint32_t total_rx_count; uint32_t total_rx_count;
uint32_t total_valid_count; uint32_t total_valid_count;
uint32_t reserved[4]; uint32_t total_good_count;
uint32_t reserved[3];
int32_t EOR; int32_t EOR;
} RPY_NTPData; } RPY_NTPData;

259
client.c
View File

@@ -4,7 +4,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Lonnie Abelbeck 2016, 2018 * Copyright (C) Lonnie Abelbeck 2016, 2018
* Copyright (C) Miroslav Lichvar 2009-2021 * Copyright (C) Miroslav Lichvar 2009-2023
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -71,6 +71,8 @@ static int source_names = 0;
static int csv_mode = 0; static int csv_mode = 0;
static int end_dot = 0;
/* ================================================== */ /* ================================================== */
/* Log a message. This is a minimalistic replacement of the logging.c /* Log a message. This is a minimalistic replacement of the logging.c
implementation to avoid linking with it and other modules. */ implementation to avoid linking with it and other modules. */
@@ -869,6 +871,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 static int
process_cmd_add_source(CMD_Request *msg, char *line) process_cmd_add_source(CMD_Request *msg, char *line)
{ {
@@ -945,11 +958,11 @@ process_cmd_add_source(CMD_Request *msg, char *line)
(data.params.burst ? REQ_ADDSRC_BURST : 0) | (data.params.burst ? REQ_ADDSRC_BURST : 0) |
(data.params.nts ? REQ_ADDSRC_NTS : 0) | (data.params.nts ? REQ_ADDSRC_NTS : 0) |
(data.params.copy ? REQ_ADDSRC_COPY : 0) | (data.params.copy ? REQ_ADDSRC_COPY : 0) |
(data.params.ext_fields & NTP_EF_FLAG_EXP1 ? REQ_ADDSRC_EF_EXP1 : 0) | (data.params.ext_fields & NTP_EF_FLAG_EXP_MONO_ROOT ?
(data.params.sel_options & SRC_SELECT_PREFER ? REQ_ADDSRC_PREFER : 0) | REQ_ADDSRC_EF_EXP_MONO_ROOT : 0) |
(data.params.sel_options & SRC_SELECT_NOSELECT ? REQ_ADDSRC_NOSELECT : 0) | (data.params.ext_fields & NTP_EF_FLAG_EXP_NET_CORRECTION ?
(data.params.sel_options & SRC_SELECT_TRUST ? REQ_ADDSRC_TRUST : 0) | REQ_ADDSRC_EF_EXP_NET_CORRECTION : 0) |
(data.params.sel_options & SRC_SELECT_REQUIRE ? REQ_ADDSRC_REQUIRE : 0)); convert_addsrc_sel_options(data.params.sel_options));
msg->data.ntp_source.filter_length = htonl(data.params.filter_length); msg->data.ntp_source.filter_length = htonl(data.params.filter_length);
msg->data.ntp_source.cert_set = htonl(data.params.cert_set); msg->data.ntp_source.cert_set = htonl(data.params.cert_set);
msg->data.ntp_source.max_delay_quant = msg->data.ntp_source.max_delay_quant =
@@ -1013,6 +1026,7 @@ give_help(void)
"sources [-a] [-v]\0Display information about current sources\0" "sources [-a] [-v]\0Display information about current sources\0"
"sourcestats [-a] [-v]\0Display statistics about collected measurements\0" "sourcestats [-a] [-v]\0Display statistics about collected measurements\0"
"selectdata [-a] [-v]\0Display information about source selection\0" "selectdata [-a] [-v]\0Display information about source selection\0"
"selectopts <address|refid> <+|-options>\0Modify selection options\0"
"reselect\0Force reselecting synchronisation source\0" "reselect\0Force reselecting synchronisation source\0"
"reselectdist <dist>\0Modify reselection distance\0" "reselectdist <dist>\0Modify reselection distance\0"
"\0\0" "\0\0"
@@ -1127,8 +1141,8 @@ command_name_generator(const char *text, int state)
"manual", "maxdelay", "maxdelaydevratio", "maxdelayratio", "maxpoll", "manual", "maxdelay", "maxdelaydevratio", "maxdelayratio", "maxpoll",
"maxupdateskew", "minpoll", "minstratum", "ntpdata", "offline", "online", "onoffline", "maxupdateskew", "minpoll", "minstratum", "ntpdata", "offline", "online", "onoffline",
"polltarget", "quit", "refresh", "rekey", "reload", "reselect", "reselectdist", "reset", "polltarget", "quit", "refresh", "rekey", "reload", "reselect", "reselectdist", "reset",
"retries", "rtcdata", "selectdata", "serverstats", "settime", "shutdown", "smoothing", "retries", "rtcdata", "selectdata", "selectopts", "serverstats", "settime",
"smoothtime", "sourcename", "sources", "sourcestats", "shutdown", "smoothing", "smoothtime", "sourcename", "sources", "sourcestats",
"timeout", "tracking", "trimrtc", "waitsync", "writertc", "timeout", "tracking", "trimrtc", "waitsync", "writertc",
NULL NULL
}; };
@@ -1156,7 +1170,7 @@ command_name_generator(const char *text, int state)
while ((name = names[tab_complete_index][list_index++])) { while ((name = names[tab_complete_index][list_index++])) {
if (strncmp(name, text, len) == 0) { if (strncmp(name, text, len) == 0) {
return strdup(name); return Strdup(name);
} }
} }
@@ -1462,24 +1476,24 @@ request_reply(CMD_Request *request, CMD_Reply *reply, int requested_reply, int v
/* ================================================== */ /* ================================================== */
static void static void
print_seconds(unsigned long s) print_seconds(uint32_t s)
{ {
unsigned long d; uint32_t d;
if (s == (uint32_t)-1) { if (s == (uint32_t)-1) {
printf(" -"); printf(" -");
} else if (s < 1200) { } else if (s < 1200) {
printf("%4lu", s); printf("%4"PRIu32, s);
} else if (s < 36000) { } else if (s < 36000) {
printf("%3lum", s / 60); printf("%3"PRIu32"m", s / 60);
} else if (s < 345600) { } else if (s < 345600) {
printf("%3luh", s / 3600); printf("%3"PRIu32"h", s / 3600);
} else { } else {
d = s / 86400; d = s / 86400;
if (d > 999) { if (d > 999) {
printf("%3luy", d / 365); printf("%3"PRIu32"y", d / 365);
} else { } else {
printf("%3lud", d); printf("%3"PRIu32"d", d);
} }
} }
} }
@@ -1610,8 +1624,9 @@ print_report(const char *format, ...)
va_list ap; va_list ap;
int i, field, sign, width, prec, spec; int i, field, sign, width, prec, spec;
const char *string; const char *string;
unsigned long long_uinteger;
unsigned int uinteger; unsigned int uinteger;
uint64_t uinteger64;
uint32_t uinteger32;
int integer; int integer;
struct timespec *ts; struct timespec *ts;
struct tm *tm; struct tm *tm;
@@ -1709,9 +1724,9 @@ print_report(const char *format, ...)
spec == 'O' ? "seconds" : "ppm", spec == 'O' ? "seconds" : "ppm",
(dbl > 0.0) ^ (spec != 'O') ? "slow" : "fast"); (dbl > 0.0) ^ (spec != 'O') ? "slow" : "fast");
break; break;
case 'I': /* interval with unit */ case 'I': /* uint32_t interval with unit */
long_uinteger = va_arg(ap, unsigned long); uinteger32 = va_arg(ap, uint32_t);
print_seconds(long_uinteger); print_seconds(uinteger32);
break; break;
case 'L': /* leap status */ case 'L': /* leap status */
integer = va_arg(ap, int); integer = va_arg(ap, int);
@@ -1778,8 +1793,8 @@ print_report(const char *format, ...)
print_freq_ppm(dbl); print_freq_ppm(dbl);
break; break;
case 'R': /* reference ID in hexdecimal */ case 'R': /* reference ID in hexdecimal */
long_uinteger = va_arg(ap, unsigned long); uinteger32 = va_arg(ap, uint32_t);
printf("%08lX", long_uinteger); printf("%08"PRIX32, uinteger32);
break; break;
case 'S': /* offset with unit */ case 'S': /* offset with unit */
dbl = va_arg(ap, double); dbl = va_arg(ap, double);
@@ -1796,14 +1811,18 @@ print_report(const char *format, ...)
strftime(buf, sizeof (buf), "%a %b %d %T %Y", tm); strftime(buf, sizeof (buf), "%a %b %d %T %Y", tm);
printf("%s", buf); printf("%s", buf);
break; break;
case 'U': /* unsigned long in decimal */ case 'U': /* uint32_t in decimal */
long_uinteger = va_arg(ap, unsigned long); uinteger32 = va_arg(ap, uint32_t);
printf("%*lu", width, long_uinteger); printf("%*"PRIu32, width, uinteger32);
break; break;
case 'V': /* timespec as seconds since epoch */ case 'V': /* timespec as seconds since epoch */
ts = va_arg(ap, struct timespec *); ts = va_arg(ap, struct timespec *);
printf("%s", UTI_TimespecToString(ts)); printf("%s", UTI_TimespecToString(ts));
break; break;
case 'Q': /* uint64_t in decimal */
uinteger64 = va_arg(ap, uint64_t);
printf("%*"PRIu64, width, uinteger64);
break;
case 'b': /* unsigned int in binary */ case 'b': /* unsigned int in binary */
uinteger = va_arg(ap, unsigned int); uinteger = va_arg(ap, unsigned int);
for (i = prec - 1; i >= 0; i--) for (i = prec - 1; i >= 0; i--)
@@ -1963,7 +1982,7 @@ process_cmd_sources(char *line)
IPAddr ip_addr; IPAddr ip_addr;
uint32_t i, mode, n_sources; uint32_t i, mode, n_sources;
char name[256], mode_ch, state_ch; char name[256], mode_ch, state_ch;
int all, verbose; int all, verbose, ref;
parse_sources_options(line, &all, &verbose); parse_sources_options(line, &all, &verbose);
@@ -2000,9 +2019,8 @@ process_cmd_sources(char *line)
if (!all && ip_addr.family == IPADDR_ID) if (!all && ip_addr.family == IPADDR_ID)
continue; continue;
format_name(name, sizeof (name), 25, ref = mode == RPY_SD_MD_REF && ip_addr.family == IPADDR_INET4;
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);
ip_addr.addr.in4, 1, &ip_addr);
switch (mode) { switch (mode) {
case RPY_SD_MD_CLIENT: case RPY_SD_MD_CLIENT:
@@ -2051,7 +2069,7 @@ process_cmd_sources(char *line)
ntohs(reply.data.source_data.stratum), ntohs(reply.data.source_data.stratum),
(int16_t)ntohs(reply.data.source_data.poll), (int16_t)ntohs(reply.data.source_data.poll),
ntohs(reply.data.source_data.reachability), 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.latest_meas),
UTI_FloatNetworkToHost(reply.data.source_data.orig_latest_meas), UTI_FloatNetworkToHost(reply.data.source_data.orig_latest_meas),
UTI_FloatNetworkToHost(reply.data.source_data.latest_meas_err), UTI_FloatNetworkToHost(reply.data.source_data.latest_meas_err),
@@ -2112,9 +2130,9 @@ process_cmd_sourcestats(char *line)
print_report("%-25s %3U %3U %I %+P %P %+S %S\n", print_report("%-25s %3U %3U %I %+P %P %+S %S\n",
name, name,
(unsigned long)ntohl(reply.data.sourcestats.n_samples), ntohl(reply.data.sourcestats.n_samples),
(unsigned long)ntohl(reply.data.sourcestats.n_runs), ntohl(reply.data.sourcestats.n_runs),
(unsigned long)ntohl(reply.data.sourcestats.span_seconds), ntohl(reply.data.sourcestats.span_seconds),
UTI_FloatNetworkToHost(reply.data.sourcestats.resid_freq_ppm), UTI_FloatNetworkToHost(reply.data.sourcestats.resid_freq_ppm),
UTI_FloatNetworkToHost(reply.data.sourcestats.skew_ppm), UTI_FloatNetworkToHost(reply.data.sourcestats.skew_ppm),
UTI_FloatNetworkToHost(reply.data.sourcestats.est_offset), UTI_FloatNetworkToHost(reply.data.sourcestats.est_offset),
@@ -2162,7 +2180,7 @@ process_cmd_tracking(char *line)
"Root dispersion : %.9f seconds\n" "Root dispersion : %.9f seconds\n"
"Update interval : %.1f seconds\n" "Update interval : %.1f seconds\n"
"Leap status : %L\n", "Leap status : %L\n",
(unsigned long)ref_id, name, ref_id, name,
ntohs(reply.data.tracking.stratum), ntohs(reply.data.tracking.stratum),
&ref_time, &ref_time,
UTI_FloatNetworkToHost(reply.data.tracking.current_correction), UTI_FloatNetworkToHost(reply.data.tracking.current_correction),
@@ -2250,10 +2268,10 @@ process_cmd_authdata(char *line)
print_report("%-27s %4s %5U %4d %4d %I %4d %4d %4d %4d\n", print_report("%-27s %4s %5U %4d %4d %I %4d %4d %4d %4d\n",
name, mode_str, 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_type),
ntohs(reply.data.auth_data.key_length), 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.ke_attempts),
ntohs(reply.data.auth_data.nak), ntohs(reply.data.auth_data.nak),
ntohs(reply.data.auth_data.cookies), ntohs(reply.data.auth_data.cookies),
@@ -2346,18 +2364,18 @@ process_cmd_ntpdata(char *line)
"RX timestamping : %N\n" "RX timestamping : %N\n"
"Total TX : %U\n" "Total TX : %U\n"
"Total RX : %U\n" "Total RX : %U\n"
"Total valid RX : %U\n", "Total valid RX : %U\n"
UTI_IPToString(&remote_addr), (unsigned long)UTI_IPToRefid(&remote_addr), "Total good RX : %U\n",
UTI_IPToString(&remote_addr), UTI_IPToRefid(&remote_addr),
ntohs(reply.data.ntp_data.remote_port), 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.leap, reply.data.ntp_data.version,
reply.data.ntp_data.mode, reply.data.ntp_data.stratum, 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.poll, UTI_Log2ToDouble(reply.data.ntp_data.poll),
reply.data.ntp_data.precision, UTI_Log2ToDouble(reply.data.ntp_data.precision), 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_delay),
UTI_FloatNetworkToHost(reply.data.ntp_data.root_dispersion), UTI_FloatNetworkToHost(reply.data.ntp_data.root_dispersion),
(unsigned long)ntohl(reply.data.ntp_data.ref_id), ntohl(reply.data.ntp_data.ref_id), reply.data.ntp_data.stratum <= 1 ?
reply.data.ntp_data.stratum <= 1 ?
UTI_RefidToString(ntohl(reply.data.ntp_data.ref_id)) : "", UTI_RefidToString(ntohl(reply.data.ntp_data.ref_id)) : "",
&ref_time, &ref_time,
UTI_FloatNetworkToHost(reply.data.ntp_data.offset), UTI_FloatNetworkToHost(reply.data.ntp_data.offset),
@@ -2371,9 +2389,10 @@ process_cmd_ntpdata(char *line)
ntohs(reply.data.ntp_data.flags) & RPY_NTP_FLAG_INTERLEAVED, ntohs(reply.data.ntp_data.flags) & RPY_NTP_FLAG_INTERLEAVED,
ntohs(reply.data.ntp_data.flags) & RPY_NTP_FLAG_AUTHENTICATED, ntohs(reply.data.ntp_data.flags) & RPY_NTP_FLAG_AUTHENTICATED,
reply.data.ntp_data.tx_tss_char, reply.data.ntp_data.rx_tss_char, reply.data.ntp_data.tx_tss_char, reply.data.ntp_data.rx_tss_char,
(unsigned long)ntohl(reply.data.ntp_data.total_tx_count), ntohl(reply.data.ntp_data.total_tx_count),
(unsigned long)ntohl(reply.data.ntp_data.total_rx_count), ntohl(reply.data.ntp_data.total_rx_count),
(unsigned long)ntohl(reply.data.ntp_data.total_valid_count), ntohl(reply.data.ntp_data.total_valid_count),
ntohl(reply.data.ntp_data.total_good_count),
REPORT_END); REPORT_END);
} }
@@ -2445,7 +2464,7 @@ process_cmd_selectdata(char *line)
eff_options & RPY_SD_OPTION_TRUST ? 'T' : '-', eff_options & RPY_SD_OPTION_TRUST ? 'T' : '-',
eff_options & RPY_SD_OPTION_REQUIRE ? 'R' : '-', 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.score),
UTI_FloatNetworkToHost(reply.data.select_data.lo_limit), UTI_FloatNetworkToHost(reply.data.select_data.lo_limit),
UTI_FloatNetworkToHost(reply.data.select_data.hi_limit), UTI_FloatNetworkToHost(reply.data.select_data.hi_limit),
@@ -2465,31 +2484,43 @@ process_cmd_serverstats(char *line)
CMD_Reply reply; CMD_Reply reply;
request.command = htons(REQ_SERVER_STATS); request.command = htons(REQ_SERVER_STATS);
if (!request_reply(&request, &reply, RPY_SERVER_STATS3, 0)) if (!request_reply(&request, &reply, RPY_SERVER_STATS4, 0))
return 0; return 0;
print_report("NTP packets received : %U\n" print_report("NTP packets received : %Q\n"
"NTP packets dropped : %U\n" "NTP packets dropped : %Q\n"
"Command packets received : %U\n" "Command packets received : %Q\n"
"Command packets dropped : %U\n" "Command packets dropped : %Q\n"
"Client log records dropped : %U\n" "Client log records dropped : %Q\n"
"NTS-KE connections accepted: %U\n" "NTS-KE connections accepted: %Q\n"
"NTS-KE connections dropped : %U\n" "NTS-KE connections dropped : %Q\n"
"Authenticated NTP packets : %U\n" "Authenticated NTP packets : %Q\n"
"Interleaved NTP packets : %U\n" "Interleaved NTP packets : %Q\n"
"NTP timestamps held : %U\n" "NTP timestamps held : %Q\n"
"NTP timestamp span : %U\n", "NTP timestamp span : %Q\n"
(unsigned long)ntohl(reply.data.server_stats.ntp_hits), "NTP daemon RX timestamps : %Q\n"
(unsigned long)ntohl(reply.data.server_stats.ntp_drops), "NTP daemon TX timestamps : %Q\n"
(unsigned long)ntohl(reply.data.server_stats.cmd_hits), "NTP kernel RX timestamps : %Q\n"
(unsigned long)ntohl(reply.data.server_stats.cmd_drops), "NTP kernel TX timestamps : %Q\n"
(unsigned long)ntohl(reply.data.server_stats.log_drops), "NTP hardware RX timestamps : %Q\n"
(unsigned long)ntohl(reply.data.server_stats.nke_hits), "NTP hardware TX timestamps : %Q\n",
(unsigned long)ntohl(reply.data.server_stats.nke_drops), UTI_Integer64NetworkToHost(reply.data.server_stats.ntp_hits),
(unsigned long)ntohl(reply.data.server_stats.ntp_auth_hits), UTI_Integer64NetworkToHost(reply.data.server_stats.ntp_drops),
(unsigned long)ntohl(reply.data.server_stats.ntp_interleaved_hits), UTI_Integer64NetworkToHost(reply.data.server_stats.cmd_hits),
(unsigned long)ntohl(reply.data.server_stats.ntp_timestamps), UTI_Integer64NetworkToHost(reply.data.server_stats.cmd_drops),
(unsigned long)ntohl(reply.data.server_stats.ntp_span_seconds), 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); REPORT_END);
return 1; return 1;
@@ -2571,7 +2602,7 @@ process_cmd_rtcreport(char *line)
&ref_time, &ref_time,
ntohs(reply.data.rtc.n_samples), ntohs(reply.data.rtc.n_samples),
ntohs(reply.data.rtc.n_runs), 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_seconds_fast),
UTI_FloatNetworkToHost(reply.data.rtc.rtc_gain_rate_ppm), UTI_FloatNetworkToHost(reply.data.rtc.rtc_gain_rate_ppm),
REPORT_END); REPORT_END);
@@ -2646,16 +2677,15 @@ process_cmd_clients(char *line)
print_report("%-25s %6U %5U %C %C %I %6U %5U %C %I\n", print_report("%-25s %6U %5U %C %C %I %6U %5U %C %I\n",
name, name,
(unsigned long)ntohl(client->ntp_hits), ntohl(client->ntp_hits),
(unsigned long)ntohl(client->ntp_drops), ntohl(client->ntp_drops),
client->ntp_interval, client->ntp_interval,
client->ntp_timeout_interval, client->ntp_timeout_interval,
(unsigned long)ntohl(client->last_ntp_hit_ago), ntohl(client->last_ntp_hit_ago),
(unsigned long)ntohl(nke ? client->nke_hits : client->cmd_hits), ntohl(nke ? client->nke_hits : client->cmd_hits),
(unsigned long)ntohl(nke ? client->nke_drops : client->cmd_drops), ntohl(nke ? client->nke_drops : client->cmd_drops),
nke ? client->nke_interval : client->cmd_interval, nke ? client->nke_interval : client->cmd_interval,
(unsigned long)ntohl(nke ? client->last_nke_hit_ago : ntohl(nke ? client->last_nke_hit_ago : client->last_cmd_hit_ago),
client->last_cmd_hit_ago),
REPORT_END); REPORT_END);
} }
@@ -2686,7 +2716,7 @@ process_cmd_manual_list(const char *line)
return 0; return 0;
n_samples = ntohl(reply.data.manual_list.n_samples); 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"); print_header("# Date Time(UTC) Slewed Original Residual");
@@ -2806,11 +2836,11 @@ process_cmd_activity(const char *line)
"%U sources doing burst (return to online)\n" "%U sources doing burst (return to online)\n"
"%U sources doing burst (return to offline)\n" "%U sources doing burst (return to offline)\n"
"%U sources with unknown address\n", "%U sources with unknown address\n",
(unsigned long)ntohl(reply.data.activity.online), ntohl(reply.data.activity.online),
(unsigned long)ntohl(reply.data.activity.offline), ntohl(reply.data.activity.offline),
(unsigned long)ntohl(reply.data.activity.burst_online), ntohl(reply.data.activity.burst_online),
(unsigned long)ntohl(reply.data.activity.burst_offline), ntohl(reply.data.activity.burst_offline),
(unsigned long)ntohl(reply.data.activity.unresolved), ntohl(reply.data.activity.unresolved),
REPORT_END); REPORT_END);
return 1; return 1;
@@ -2889,6 +2919,55 @@ 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);
ref_id = 0;
/* Don't allow hostnames to avoid conflicts with reference IDs */
if (!UTI_StringToIdIP(src, &ip_addr) && !UTI_StringToIP(src, &ip_addr)) {
ip_addr.family = IPADDR_UNSPEC;
if (CPS_ParseRefid(src, &ref_id) == 0) {
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 static int
process_cmd_waitsync(char *line) process_cmd_waitsync(char *line)
{ {
@@ -2924,7 +3003,7 @@ process_cmd_waitsync(char *line)
skew_ppm = UTI_FloatNetworkToHost(reply.data.tracking.skew_ppm); skew_ppm = UTI_FloatNetworkToHost(reply.data.tracking.skew_ppm);
print_report("try: %d, refid: %R, correction: %.9f, skew: %.3f\n", 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 || if ((ip_addr.family != IPADDR_UNSPEC ||
(ref_id != 0 && ref_id != 0x7f7f0101L /* LOCAL refid */)) && (ref_id != 0 && ref_id != 0x7f7f0101L /* LOCAL refid */)) &&
@@ -3191,6 +3270,8 @@ process_line(char *line)
} else if (!strcmp(command, "selectdata")) { } else if (!strcmp(command, "selectdata")) {
do_normal_submit = 0; do_normal_submit = 0;
ret = process_cmd_selectdata(line); ret = process_cmd_selectdata(line);
} else if (!strcmp(command, "selectopts")) {
do_normal_submit = process_cmd_selectopts(&tx_message, line);
} else if (!strcmp(command, "serverstats")) { } else if (!strcmp(command, "serverstats")) {
do_normal_submit = 0; do_normal_submit = 0;
ret = process_cmd_serverstats(line); ret = process_cmd_serverstats(line);
@@ -3241,6 +3322,10 @@ process_line(char *line)
ret = request_reply(&tx_message, &rx_message, RPY_NULL, 1); ret = request_reply(&tx_message, &rx_message, RPY_NULL, 1);
} }
if (end_dot) {
printf(".\n");
}
fflush(stderr); fflush(stderr);
if (fflush(stdout) != 0 || ferror(stdout) != 0) { if (fflush(stdout) != 0 || ferror(stdout) != 0) {
@@ -3298,7 +3383,7 @@ static void
display_gpl(void) display_gpl(void)
{ {
printf("chrony version %s\n" printf("chrony version %s\n"
"Copyright (C) 1997-2003, 2007, 2009-2021 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" "chrony comes with ABSOLUTELY NO WARRANTY. This is free software, and\n"
"you are welcome to redistribute it under certain conditions. See the\n" "you are welcome to redistribute it under certain conditions. See the\n"
"GNU General Public License version 2 for details.\n\n", "GNU General Public License version 2 for details.\n\n",
@@ -3317,6 +3402,7 @@ print_help(const char *progname)
" -n\t\tDon't resolve hostnames\n" " -n\t\tDon't resolve hostnames\n"
" -N\t\tPrint original source names\n" " -N\t\tPrint original source names\n"
" -c\t\tEnable CSV format\n" " -c\t\tEnable CSV format\n"
" -e\t\tEnd responses with dot\n"
#if DEBUG > 0 #if DEBUG > 0
" -d\t\tEnable debug messages\n" " -d\t\tEnable debug messages\n"
#endif #endif
@@ -3361,7 +3447,7 @@ main(int argc, char **argv)
optind = 1; optind = 1;
/* Parse short command-line options */ /* 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) { switch (opt) {
case '4': case '4':
case '6': case '6':
@@ -3379,6 +3465,9 @@ main(int argc, char **argv)
log_min_severity = LOGS_DEBUG; log_min_severity = LOGS_DEBUG;
#endif #endif
break; break;
case 'e':
end_dot = 1;
break;
case 'h': case 'h':
hostnames = optarg; hostnames = optarg;
break; break;

View File

@@ -126,7 +126,8 @@ static int active;
/* RX and TX timestamp saved for clients using interleaved mode */ /* RX and TX timestamp saved for clients using interleaved mode */
typedef struct { typedef struct {
uint64_t rx_ts; uint64_t rx_ts;
uint16_t flags; uint8_t flags;
uint8_t tx_ts_source;
uint16_t slew_epoch; uint16_t slew_epoch;
int32_t tx_ts_offset; int32_t tx_ts_offset;
} NtpTimestamps; } NtpTimestamps;
@@ -155,12 +156,17 @@ static NtpTimestampMap ntp_ts_map;
/* Maximum number of timestamps moved in the array to insert a new timestamp */ /* Maximum number of timestamps moved in the array to insert a new timestamp */
#define NTPTS_INSERT_LIMIT 64 #define NTPTS_INSERT_LIMIT 64
/* Maximum expected value of the timestamp source */
#define MAX_NTP_TS NTP_TS_HARDWARE
/* Global statistics */ /* Global statistics */
static uint32_t total_hits[MAX_SERVICES]; static uint64_t total_hits[MAX_SERVICES];
static uint32_t total_drops[MAX_SERVICES]; static uint64_t total_drops[MAX_SERVICES];
static uint32_t total_ntp_auth_hits; static uint64_t total_ntp_auth_hits;
static uint32_t total_ntp_interleaved_hits; static uint64_t total_ntp_interleaved_hits;
static uint32_t total_record_drops; 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 #define NSEC_PER_SEC 1000000000U
@@ -639,9 +645,14 @@ CLG_LimitServiceRate(CLG_Service service, int index)
/* ================================================== */ /* ================================================== */
void 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 +784,8 @@ push_ntp_tss(uint32_t index)
/* ================================================== */ /* ================================================== */
static void 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; struct timespec ts;
@@ -792,12 +804,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->tx_ts_offset = (int32_t)ts.tv_nsec + (int32_t)ts.tv_sec * (int32_t)NSEC_PER_SEC;
tss->flags |= NTPTS_VALID_TX; tss->flags |= NTPTS_VALID_TX;
tss->tx_ts_source = tx_src;
} }
/* ================================================== */ /* ================================================== */
static void 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; int32_t offset = tss->tx_ts_offset;
NTP_int64 ntp_ts; NTP_int64 ntp_ts;
@@ -814,12 +827,14 @@ get_ntp_tx(NtpTimestamps *tss, struct timespec *tx_ts)
} else { } else {
UTI_ZeroTimespec(tx_ts); UTI_ZeroTimespec(tx_ts);
} }
*tx_src = tss->tx_ts_source;
} }
/* ================================================== */ /* ================================================== */
void 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; NtpTimestamps *tss;
uint32_t i, index; uint32_t i, index;
@@ -877,7 +892,7 @@ CLG_SaveNtpTimestamps(NTP_int64 *rx_ts, struct timespec *tx_ts)
tss->rx_ts = rx; tss->rx_ts = rx;
tss->flags = 0; tss->flags = 0;
tss->slew_epoch = ntp_ts_map.slew_epoch; 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, DEBUG_LOG("Saved RX+TX index=%"PRIu32" first=%"PRIu32" size=%"PRIu32,
index, ntp_ts_map.first, ntp_ts_map.size); index, ntp_ts_map.first, ntp_ts_map.size);
@@ -921,7 +936,8 @@ CLG_UndoNtpTxTimestampSlew(NTP_int64 *rx_ts, struct timespec *tx_ts)
/* ================================================== */ /* ================================================== */
void 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; uint32_t index;
@@ -931,13 +947,14 @@ CLG_UpdateNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts)
if (!find_ntp_rx_ts(ntp64_to_int64(rx_ts), &index)) if (!find_ntp_rx_ts(ntp64_to_int64(rx_ts), &index))
return; 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 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; NtpTimestamps *tss;
uint32_t index; uint32_t index;
@@ -953,7 +970,7 @@ CLG_GetNtpTxTimestamp(NTP_int64 *rx_ts, struct timespec *tx_ts)
if (tss->flags & NTPTS_DISABLED) if (tss->flags & NTPTS_DISABLED)
return 0; return 0;
get_ntp_tx(tss, tx_ts); get_ntp_tx(tss, tx_ts, tx_src);
return 1; return 1;
} }
@@ -1085,4 +1102,10 @@ CLG_GetServerStatsReport(RPT_ServerStatsReport *report)
report->ntp_span_seconds = ntp_ts_map.size > 1 ? report->ntp_span_seconds = ntp_ts_map.size > 1 ?
(get_ntp_tss(ntp_ts_map.size - 1)->rx_ts - (get_ntp_tss(ntp_ts_map.size - 1)->rx_ts -
get_ntp_tss(0)->rx_ts) >> 32 : 0; 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

@@ -42,14 +42,18 @@ extern void CLG_Finalise(void);
extern int CLG_GetClientIndex(IPAddr *client); extern int CLG_GetClientIndex(IPAddr *client);
extern int CLG_LogServiceAccess(CLG_Service service, IPAddr *client, struct timespec *now); extern int CLG_LogServiceAccess(CLG_Service service, IPAddr *client, struct timespec *now);
extern int CLG_LimitServiceRate(CLG_Service service, int index); extern int CLG_LimitServiceRate(CLG_Service service, int index);
extern void CLG_LogAuthNtpRequest(void); extern void CLG_UpdateNtpStats(int auth, NTP_Timestamp_Source rx_ts_src,
NTP_Timestamp_Source tx_ts_src);
extern int CLG_GetNtpMinPoll(void); extern int CLG_GetNtpMinPoll(void);
/* Functions to save and retrieve timestamps for server interleaved mode */ /* 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_UndoNtpTxTimestampSlew(NTP_int64 *rx_ts, struct timespec *tx_ts);
extern void CLG_UpdateNtpTxTimestamp(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); 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); extern void CLG_DisableNtpTimestamps(NTP_int64 *rx_ts);
/* And some reporting functions, for use by chronyc. */ /* And some reporting functions, for use by chronyc. */

104
cmdmon.c
View File

@@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2016, 2018-2021 * Copyright (C) Miroslav Lichvar 2009-2016, 2018-2023
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -144,6 +144,7 @@ static const char permissions[] = {
PERMIT_AUTH, /* SELECT_DATA */ PERMIT_AUTH, /* SELECT_DATA */
PERMIT_AUTH, /* RELOAD_SOURCES */ PERMIT_AUTH, /* RELOAD_SOURCES */
PERMIT_AUTH, /* DOFFSET2 */ PERMIT_AUTH, /* DOFFSET2 */
PERMIT_AUTH, /* MODIFY_SELECTOPTS */
}; };
/* ================================================== */ /* ================================================== */
@@ -703,6 +704,17 @@ 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 static void
handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message) handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
{ {
@@ -771,13 +783,11 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
params.burst = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_BURST ? 1 : 0; params.burst = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_BURST ? 1 : 0;
params.nts = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_NTS ? 1 : 0; params.nts = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_NTS ? 1 : 0;
params.copy = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_COPY ? 1 : 0; params.copy = ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_COPY ? 1 : 0;
params.ext_fields = params.ext_fields = (ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_EF_EXP_MONO_ROOT ?
ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_EF_EXP1 ? NTP_EF_FLAG_EXP1 : 0; NTP_EF_FLAG_EXP_MONO_ROOT : 0) |
params.sel_options = (ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_EF_EXP_NET_CORRECTION ?
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_PREFER ? SRC_SELECT_PREFER : 0) | NTP_EF_FLAG_EXP_NET_CORRECTION : 0);
(ntohl(rx_message->data.ntp_source.flags) & REQ_ADDSRC_NOSELECT ? SRC_SELECT_NOSELECT : 0) | params.sel_options = convert_addsrc_select_options(ntohl(rx_message->data.ntp_source.flags));
(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);
status = NSR_AddSourceByName(name, port, pool, type, &params, NULL); status = NSR_AddSourceByName(name, port, pool, type, &params, NULL);
switch (status) { switch (status) {
@@ -1169,18 +1179,36 @@ handle_server_stats(CMD_Request *rx_message, CMD_Reply *tx_message)
RPT_ServerStatsReport report; RPT_ServerStatsReport report;
CLG_GetServerStatsReport(&report); CLG_GetServerStatsReport(&report);
tx_message->reply = htons(RPY_SERVER_STATS3); tx_message->reply = htons(RPY_SERVER_STATS4);
tx_message->data.server_stats.ntp_hits = htonl(report.ntp_hits); tx_message->data.server_stats.ntp_hits = UTI_Integer64HostToNetwork(report.ntp_hits);
tx_message->data.server_stats.nke_hits = htonl(report.nke_hits); tx_message->data.server_stats.nke_hits = UTI_Integer64HostToNetwork(report.nke_hits);
tx_message->data.server_stats.cmd_hits = htonl(report.cmd_hits); tx_message->data.server_stats.cmd_hits = UTI_Integer64HostToNetwork(report.cmd_hits);
tx_message->data.server_stats.ntp_drops = htonl(report.ntp_drops); tx_message->data.server_stats.ntp_drops = UTI_Integer64HostToNetwork(report.ntp_drops);
tx_message->data.server_stats.nke_drops = htonl(report.nke_drops); tx_message->data.server_stats.nke_drops = UTI_Integer64HostToNetwork(report.nke_drops);
tx_message->data.server_stats.cmd_drops = htonl(report.cmd_drops); tx_message->data.server_stats.cmd_drops = UTI_Integer64HostToNetwork(report.cmd_drops);
tx_message->data.server_stats.log_drops = htonl(report.log_drops); tx_message->data.server_stats.log_drops = UTI_Integer64HostToNetwork(report.log_drops);
tx_message->data.server_stats.ntp_auth_hits = htonl(report.ntp_auth_hits); tx_message->data.server_stats.ntp_auth_hits =
tx_message->data.server_stats.ntp_interleaved_hits = htonl(report.ntp_interleaved_hits); UTI_Integer64HostToNetwork(report.ntp_auth_hits);
tx_message->data.server_stats.ntp_timestamps = htonl(report.ntp_timestamps); tx_message->data.server_stats.ntp_interleaved_hits =
tx_message->data.server_stats.ntp_span_seconds = htonl(report.ntp_span_seconds); 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));
} }
/* ================================================== */ /* ================================================== */
@@ -1224,6 +1252,7 @@ handle_ntp_data(CMD_Request *rx_message, CMD_Reply *tx_message)
tx_message->data.ntp_data.total_tx_count = htonl(report.total_tx_count); tx_message->data.ntp_data.total_tx_count = htonl(report.total_tx_count);
tx_message->data.ntp_data.total_rx_count = htonl(report.total_rx_count); 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_valid_count = htonl(report.total_valid_count);
tx_message->data.ntp_data.total_good_count = htonl(report.total_good_count);
memset(tx_message->data.ntp_data.reserved, 0xff, sizeof (tx_message->data.ntp_data.reserved)); memset(tx_message->data.ntp_data.reserved, 0xff, sizeof (tx_message->data.ntp_data.reserved));
} }
@@ -1327,7 +1356,7 @@ handle_auth_data(CMD_Request *rx_message, CMD_Reply *tx_message)
/* ================================================== */ /* ================================================== */
static uint16_t static uint16_t
convert_select_options(int options) convert_sd_sel_options(int options)
{ {
return (options & SRC_SELECT_PREFER ? RPY_SD_OPTION_PREFER : 0) | return (options & SRC_SELECT_PREFER ? RPY_SD_OPTION_PREFER : 0) |
(options & SRC_SELECT_NOSELECT ? RPY_SD_OPTION_NOSELECT : 0) | (options & SRC_SELECT_NOSELECT ? RPY_SD_OPTION_NOSELECT : 0) |
@@ -1354,14 +1383,32 @@ 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.state_char = report.state_char;
tx_message->data.select_data.authentication = report.authentication; tx_message->data.select_data.authentication = report.authentication;
tx_message->data.select_data.leap = report.leap; 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.conf_options = htons(convert_sd_sel_options(report.conf_options));
tx_message->data.select_data.eff_options = htons(convert_select_options(report.eff_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.last_sample_ago = htonl(report.last_sample_ago);
tx_message->data.select_data.score = UTI_FloatHostToNetwork(report.score); 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.hi_limit = UTI_FloatHostToNetwork(report.hi_limit);
tx_message->data.select_data.lo_limit = UTI_FloatHostToNetwork(report.lo_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);
}
/* ================================================== */ /* ================================================== */
/* Read a packet and process it */ /* Read a packet and process it */
@@ -1514,6 +1561,8 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
} }
if (allowed) { if (allowed) {
LOG_SetContext(LOGC_Command);
switch(rx_command) { switch(rx_command) {
case REQ_NULL: case REQ_NULL:
/* Do nothing */ /* Do nothing */
@@ -1756,11 +1805,17 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
handle_reload_sources(&rx_message, &tx_message); handle_reload_sources(&rx_message, &tx_message);
break; break;
case REQ_MODIFY_SELECTOPTS:
handle_modify_selectopts(&rx_message, &tx_message);
break;
default: default:
DEBUG_LOG("Unhandled command %d", rx_command); DEBUG_LOG("Unhandled command %d", rx_command);
tx_message.status = htons(STT_FAILED); tx_message.status = htons(STT_FAILED);
break; break;
} }
LOG_UnsetContext(LOGC_Command);
} else { } else {
tx_message.status = htons(STT_UNAUTH); tx_message.status = htons(STT_UNAUTH);
} }
@@ -1794,6 +1849,9 @@ CAM_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all)
if (status == ADF_BADSUBNET) { if (status == ADF_BADSUBNET) {
return 0; return 0;
} else if (status == ADF_SUCCESS) { } 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; return 1;
} else { } else {
return 0; return 0;

View File

@@ -44,7 +44,7 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
{ {
char *hostname, *cmd; char *hostname, *cmd;
uint32_t ef_type; uint32_t ef_type;
int n; int n, sel_option;
src->port = SRC_DEFAULT_PORT; src->port = SRC_DEFAULT_PORT;
src->params.minpoll = SRC_DEFAULT_MINPOLL; src->params.minpoll = SRC_DEFAULT_MINPOLL;
@@ -101,14 +101,6 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
src->params.iburst = 1; src->params.iburst = 1;
} else if (!strcasecmp(cmd, "offline")) { } else if (!strcasecmp(cmd, "offline")) {
src->params.connectivity = SRC_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")) { } else if (!strcasecmp(cmd, "certset")) {
if (sscanf(line, "%"SCNu32"%n", &src->params.cert_set, &n) != 1) if (sscanf(line, "%"SCNu32"%n", &src->params.cert_set, &n) != 1)
return 0; return 0;
@@ -123,8 +115,11 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
if (sscanf(line, "%"SCNx32"%n", &ef_type, &n) != 1) if (sscanf(line, "%"SCNx32"%n", &ef_type, &n) != 1)
return 0; return 0;
switch (ef_type) { switch (ef_type) {
case NTP_EF_EXP1: case NTP_EF_EXP_MONO_ROOT:
src->params.ext_fields |= NTP_EF_FLAG_EXP1; 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; break;
default: default:
return 0; return 0;
@@ -187,6 +182,8 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
return 0; return 0;
} else if (!strcasecmp(cmd, "xleave")) { } else if (!strcasecmp(cmd, "xleave")) {
src->params.interleaved = 1; src->params.interleaved = 1;
} else if ((sel_option = CPS_GetSelectOption(cmd)) != 0) {
src->params.sel_options |= sel_option;
} else { } else {
return 0; return 0;
} }
@@ -197,6 +194,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 int
CPS_ParseAllowDeny(char *line, int *all, IPAddr *ip, int *subnet_bits) CPS_ParseAllowDeny(char *line, int *all, IPAddr *ip, int *subnet_bits)
{ {
@@ -396,3 +410,19 @@ CPS_ParseKey(char *line, uint32_t *id, const char **type, char **key)
return 1; 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

@@ -39,6 +39,9 @@ typedef struct {
/* Parse a command to add an NTP server or peer */ /* Parse a command to add an NTP server or peer */
extern int CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src); extern int CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src);
/* Get an NTP/refclock select option */
extern int CPS_GetSelectOption(char *option);
/* Parse a command to allow/deny access */ /* Parse a command to allow/deny access */
extern int CPS_ParseAllowDeny(char *line, int *all, IPAddr *ip, int *subnet_bits); extern int CPS_ParseAllowDeny(char *line, int *all, IPAddr *ip, int *subnet_bits);
@@ -54,4 +57,7 @@ extern char *CPS_SplitWord(char *line);
/* Parse a key from keyfile */ /* Parse a key from keyfile */
extern int CPS_ParseKey(char *line, uint32_t *id, const char **type, char **key); 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 */ #endif /* GOT_CMDPARSE_H */

138
conf.c
View File

@@ -252,6 +252,9 @@ static char *leapsec_tz = NULL;
/* Name of the user to which will be dropped root privileges. */ /* Name of the user to which will be dropped root privileges. */
static char *user; static char *user;
/* Address refresh interval */
static int refresh = 1209600; /* 2 weeks */
/* NTS server and client configuration */ /* NTS server and client configuration */
static char *nts_dump_dir = NULL; static char *nts_dump_dir = NULL;
static char *nts_ntp_server = NULL; static char *nts_ntp_server = NULL;
@@ -274,6 +277,9 @@ static int no_system_cert = 0;
/* Array of CNF_HwTsInterface */ /* Array of CNF_HwTsInterface */
static ARR_Instance hwts_interfaces; static ARR_Instance hwts_interfaces;
/* Timeout for resuming reading from sockets waiting for HW TX timestamp */
static double hwts_timeout = 0.001;
/* PTP event port (disabled by default) */ /* PTP event port (disabled by default) */
static int ptp_port = 0; static int ptp_port = 0;
@@ -602,6 +608,8 @@ CNF_ParseLine(const char *filename, int number, char *line)
parse_string(p, &hwclock_file); parse_string(p, &hwclock_file);
} else if (!strcasecmp(command, "hwtimestamp")) { } else if (!strcasecmp(command, "hwtimestamp")) {
parse_hwtimestamp(p); parse_hwtimestamp(p);
} else if (!strcasecmp(command, "hwtstimeout")) {
parse_double(p, &hwts_timeout);
} else if (!strcasecmp(command, "include")) { } else if (!strcasecmp(command, "include")) {
parse_include(p); parse_include(p);
} else if (!strcasecmp(command, "initstepslew")) { } else if (!strcasecmp(command, "initstepslew")) {
@@ -697,6 +705,8 @@ CNF_ParseLine(const char *filename, int number, char *line)
&ntp_ratelimit_burst, &ntp_ratelimit_leak); &ntp_ratelimit_burst, &ntp_ratelimit_leak);
} else if (!strcasecmp(command, "refclock")) { } else if (!strcasecmp(command, "refclock")) {
parse_refclock(p); parse_refclock(p);
} else if (!strcasecmp(command, "refresh")) {
parse_int(p, &refresh);
} else if (!strcasecmp(command, "reselectdist")) { } else if (!strcasecmp(command, "reselectdist")) {
parse_double(p, &reselect_distance); parse_double(p, &reselect_distance);
} else if (!strcasecmp(command, "rtcautotrim")) { } else if (!strcasecmp(command, "rtcautotrim")) {
@@ -862,11 +872,10 @@ static void
parse_refclock(char *line) parse_refclock(char *line)
{ {
int n, poll, dpoll, filter_length, pps_rate, min_samples, max_samples, sel_options; int n, poll, dpoll, filter_length, pps_rate, min_samples, max_samples, sel_options;
int 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; uint32_t ref_id, lock_ref_id;
double offset, delay, precision, max_dispersion, pulse_width; double offset, delay, precision, max_dispersion, pulse_width;
char *p, *cmd, *name, *param; char *p, *cmd, *name, *param;
unsigned char ref[5];
RefclockParameters *refclock; RefclockParameters *refclock;
poll = 4; poll = 4;
@@ -912,13 +921,11 @@ parse_refclock(char *line)
line = CPS_SplitWord(line); line = CPS_SplitWord(line);
if (!strcasecmp(cmd, "refid")) { if (!strcasecmp(cmd, "refid")) {
if (sscanf(line, "%4s%n", (char *)ref, &n) != 1) if ((n = CPS_ParseRefid(line, &ref_id)) == 0)
break; break;
ref_id = (uint32_t)ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3];
} else if (!strcasecmp(cmd, "lock")) { } else if (!strcasecmp(cmd, "lock")) {
if (sscanf(line, "%4s%n", (char *)ref, &n) != 1) if ((n = CPS_ParseRefid(line, &lock_ref_id)) == 0)
break; break;
lock_ref_id = (uint32_t)ref[0] << 24 | ref[1] << 16 | ref[2] << 8 | ref[3];
} else if (!strcasecmp(cmd, "poll")) { } else if (!strcasecmp(cmd, "poll")) {
if (sscanf(line, "%d%n", &poll, &n) != 1) { if (sscanf(line, "%d%n", &poll, &n) != 1) {
break; break;
@@ -971,18 +978,9 @@ parse_refclock(char *line)
} else if (!strcasecmp(cmd, "width")) { } else if (!strcasecmp(cmd, "width")) {
if (sscanf(line, "%lf%n", &pulse_width, &n) != 1) if (sscanf(line, "%lf%n", &pulse_width, &n) != 1)
break; break;
} else if (!strcasecmp(cmd, "noselect")) { } else if ((sel_option = CPS_GetSelectOption(cmd)) != 0) {
n = 0; n = 0;
sel_options |= SRC_SELECT_NOSELECT; sel_options |= sel_option;
} 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;
} else { } else {
other_parse_error("Invalid refclock option"); other_parse_error("Invalid refclock option");
return; return;
@@ -1437,8 +1435,8 @@ static void
parse_hwtimestamp(char *line) parse_hwtimestamp(char *line)
{ {
CNF_HwTsInterface *iface; CNF_HwTsInterface *iface;
int n, maxpoll_set = 0;
char *p, filter[5]; char *p, filter[5];
int n;
if (!*line) { if (!*line) {
command_parse_error(); command_parse_error();
@@ -1468,6 +1466,10 @@ parse_hwtimestamp(char *line)
} else if (!strcasecmp(p, "minpoll")) { } else if (!strcasecmp(p, "minpoll")) {
if (sscanf(line, "%d%n", &iface->minpoll, &n) != 1) if (sscanf(line, "%d%n", &iface->minpoll, &n) != 1)
break; break;
} else if (!strcasecmp(p, "maxpoll")) {
if (sscanf(line, "%d%n", &iface->maxpoll, &n) != 1)
break;
maxpoll_set = 1;
} else if (!strcasecmp(p, "minsamples")) { } else if (!strcasecmp(p, "minsamples")) {
if (sscanf(line, "%d%n", &iface->min_samples, &n) != 1) if (sscanf(line, "%d%n", &iface->min_samples, &n) != 1)
break; break;
@@ -1503,6 +1505,9 @@ parse_hwtimestamp(char *line)
if (*p) if (*p)
command_parse_error(); command_parse_error();
if (!maxpoll_set)
iface->maxpoll = iface->minpoll + 1;
} }
/* ================================================== */ /* ================================================== */
@@ -1655,11 +1660,11 @@ compare_sources(const void *a, const void *b)
return 1; return 1;
if ((d = strcmp(sa->params.name, sb->params.name)) != 0) if ((d = strcmp(sa->params.name, sb->params.name)) != 0)
return d; return d;
if ((d = (int)(sa->type) - (int)(sb->type)) != 0) if ((d = (int)sa->type - (int)sb->type) != 0)
return d; return d;
if ((d = sa->pool - sb->pool) != 0) if ((d = (int)sa->pool - (int)sb->pool) != 0)
return d; return d;
if ((d = sa->params.port - sb->params.port) != 0) if ((d = (int)sa->params.port - (int)sb->params.port) != 0)
return d; return d;
return memcmp(&sa->params.params, &sb->params.params, sizeof (sa->params.params)); return memcmp(&sa->params.params, &sb->params.params, sizeof (sa->params.params));
} }
@@ -1674,7 +1679,7 @@ reload_source_dirs(void)
uint32_t *prev_ids, *new_ids; uint32_t *prev_ids, *new_ids;
char buf[MAX_LINE_LENGTH]; char buf[MAX_LINE_LENGTH];
NSR_Status s; NSR_Status s;
int d; int d, pass;
prev_size = ARR_GetSize(ntp_source_ids); prev_size = ARR_GetSize(ntp_source_ids);
if (prev_size > 0 && ARR_GetSize(ntp_sources) != prev_size) if (prev_size > 0 && ARR_GetSize(ntp_sources) != prev_size)
@@ -1704,41 +1709,47 @@ reload_source_dirs(void)
new_ids = ARR_GetElements(ntp_source_ids); new_ids = ARR_GetElements(ntp_source_ids);
unresolved = 0; unresolved = 0;
LOG_SetContext(LOGC_SourceFile);
qsort(new_sources, new_size, sizeof (new_sources[0]), compare_sources); qsort(new_sources, new_size, sizeof (new_sources[0]), compare_sources);
for (i = j = 0; i < prev_size || j < new_size; ) { for (pass = 0; pass < 2; pass++) {
if (i < prev_size && j < new_size) for (i = j = 0; i < prev_size || j < new_size; i += d <= 0, j += d >= 0) {
d = compare_sources(&prev_sources[i], &new_sources[j]); if (i < prev_size && j < new_size)
else d = compare_sources(&prev_sources[i], &new_sources[j]);
d = i < prev_size ? -1 : 1; else
d = i < prev_size ? -1 : 1;
if (d < 0) { /* Remove missing sources before adding others to avoid conflicts */
/* Remove the missing source */ if (pass == 0 && d < 0 && prev_sources[i].params.name[0] != '\0') {
if (prev_sources[i].params.name[0] != '\0')
NSR_RemoveSourcesById(prev_ids[i]); 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 { /* Add new sources */
/* Keep the existing source */ if (pass == 1 && d > 0) {
new_ids[j] = prev_ids[i]; source = &new_sources[j];
i++, 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 : %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++) for (i = 0; i < prev_size; i++)
Free(prev_sources[i].params.name); Free(prev_sources[i].params.name);
Free(prev_sources); Free(prev_sources);
@@ -1782,6 +1793,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 void
CNF_AddInitSources(void) CNF_AddInitSources(void)
{ {
@@ -2500,6 +2524,14 @@ CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface)
/* ================================================== */ /* ================================================== */
double
CNF_GetHwTsTimeout(void)
{
return hwts_timeout;
}
/* ================================================== */
int int
CNF_GetPtpPort(void) CNF_GetPtpPort(void)
{ {
@@ -2508,6 +2540,14 @@ CNF_GetPtpPort(void)
/* ================================================== */ /* ================================================== */
int
CNF_GetRefresh(void)
{
return refresh;
}
/* ================================================== */
char * char *
CNF_GetNtsDumpDir(void) CNF_GetNtsDumpDir(void)
{ {

6
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_CreateDirs(uid_t uid, gid_t gid);
extern void CNF_CheckReadOnlyAccess(void);
extern void CNF_AddInitSources(void); extern void CNF_AddInitSources(void);
extern void CNF_AddSources(void); extern void CNF_AddSources(void);
extern void CNF_AddBroadcasts(void); extern void CNF_AddBroadcasts(void);
@@ -142,6 +144,7 @@ typedef enum {
typedef struct { typedef struct {
char *name; char *name;
int minpoll; int minpoll;
int maxpoll;
int min_samples; int min_samples;
int max_samples; int max_samples;
int nocrossts; int nocrossts;
@@ -152,9 +155,12 @@ typedef struct {
} CNF_HwTsInterface; } CNF_HwTsInterface;
extern int CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface); extern int CNF_GetHwTsInterface(unsigned int index, CNF_HwTsInterface **iface);
extern double CNF_GetHwTsTimeout(void);
extern int CNF_GetPtpPort(void); extern int CNF_GetPtpPort(void);
extern int CNF_GetRefresh(void);
extern char *CNF_GetNtsDumpDir(void); extern char *CNF_GetNtsDumpDir(void);
extern char *CNF_GetNtsNtpServer(void); extern char *CNF_GetNtsNtpServer(void);
extern int CNF_GetNtsServerCertAndKeyFiles(const char ***certs, const char ***keys); extern int CNF_GetNtsServerCertAndKeyFiles(const char ***certs, const char ***keys);

79
configure vendored
View File

@@ -5,7 +5,7 @@
# #
# Copyright (C) Richard P. Curnow 1997-2003 # Copyright (C) Richard P. Curnow 1997-2003
# Copyright (C) Bryan Christianson 2016 # Copyright (C) Bryan Christianson 2016
# Copyright (C) Miroslav Lichvar 2009, 2012-2021 # Copyright (C) Miroslav Lichvar 2009, 2012-2022
# Copyright (C) Stefan R. Filipek 2019 # 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 --without-editline Don't use editline even if it is available
--disable-sechash Disable support for hashes other than MD5 --disable-sechash Disable support for hashes other than MD5
--without-nettle Don't use nettle even if it is available --without-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-nss Don't use NSS even if it is available
--without-tomcrypt Don't use libtomcrypt even if it is available --without-tomcrypt Don't use libtomcrypt even if it is available
--disable-nts Disable NTS support --disable-nts Disable NTS support
--without-gnutls Don't use gnutls even if it is available
--disable-cmdmon Disable command and monitoring support --disable-cmdmon Disable command and monitoring support
--disable-ntp Disable NTP support --disable-ntp Disable NTP support
--disable-refclock Disable reference clock 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 --without-seccomp Don't use seccomp even if it is available
--disable-asyncdns Disable asynchronous name resolving --disable-asyncdns Disable asynchronous name resolving
--disable-forcednsretry Don't retry on permanent DNS error --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 --without-clock-gettime Don't use clock_gettime() even if it is available
--disable-timestamping Disable support for SW/HW timestamping --disable-timestamping Disable support for SW/HW timestamping
--enable-ntp-signd Enable support for MS-SNTP authentication in Samba --enable-ntp-signd Enable support for MS-SNTP authentication in Samba
@@ -244,6 +245,7 @@ try_setsched=0
try_lockmem=0 try_lockmem=0
feat_asyncdns=1 feat_asyncdns=1
feat_forcednsretry=1 feat_forcednsretry=1
try_aes_gcm_siv=1
try_clock_gettime=1 try_clock_gettime=1
try_arc4random=1 try_arc4random=1
try_recvmmsg=1 try_recvmmsg=1
@@ -345,6 +347,9 @@ do
--disable-forcednsretry) --disable-forcednsretry)
feat_forcednsretry=0 feat_forcednsretry=0
;; ;;
--without-aes-gcm-siv)
try_aes_gcm_siv=0
;;
--without-clock-gettime) --without-clock-gettime)
try_clock_gettime=0 try_clock_gettime=0
;; ;;
@@ -565,6 +570,13 @@ if [ "x$MYCFLAGS" = "x" ]; then
fi fi
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 if [ "x$MYCC" = "xgcc" ] || [ "x$MYCC" = "xclang" ]; then
MYCFLAGS="$MYCFLAGS -Wmissing-prototypes -Wall" MYCFLAGS="$MYCFLAGS -Wmissing-prototypes -Wall"
fi fi
@@ -914,6 +926,28 @@ if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_nettle = "1" ];
fi fi
fi fi
if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_gnutls = "1" ]; then
test_cflags="`pkg_config --cflags gnutls`"
test_link="`pkg_config --libs gnutls`"
if test_code 'gnutls' 'gnutls/crypto.h' \
"$test_cflags" "$test_link" '
return gnutls_hash((void *)1, (void *)2, 1);'
then
HASH_OBJ="hash_gnutls.o"
HASH_LINK="$test_link"
MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
add_def FEAT_SECHASH
if test_code 'CMAC in gnutls' 'gnutls/crypto.h' "$test_cflags" "$test_link" \
'return gnutls_hmac_init((void *)1, GNUTLS_MAC_AES_CMAC_128, (void *)2, 0);'
then
add_def HAVE_CMAC
EXTRA_OBJECTS="$EXTRA_OBJECTS cmac_gnutls.o"
EXTRA_CLI_OBJECTS="$EXTRA_CLI_OBJECTS cmac_gnutls.o"
fi
fi
fi
if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_nss = "1" ]; then if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_nss = "1" ]; then
test_cflags="`pkg_config --cflags nss`" test_cflags="`pkg_config --cflags nss`"
test_link="`pkg_config --libs-only-L nss` -lfreebl3 -lnssutil3" 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
fi fi
if [ $feat_sechash = "1" ] && [ "x$HASH_LINK" = "x" ] && [ $try_gnutls = "1" ]; then
test_cflags="`pkg_config --cflags gnutls`"
test_link="`pkg_config --libs gnutls`"
if test_code 'gnutls' 'gnutls/crypto.h' \
"$test_cflags" "$test_link" '
return gnutls_hash((void *)1, (void *)2, 1);'
then
HASH_OBJ="hash_gnutls.o"
HASH_LINK="$test_link"
MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
add_def FEAT_SECHASH
if test_code 'CMAC in gnutls' 'gnutls/crypto.h' "$test_cflags" "$test_link" \
'return gnutls_hmac_init((void *)1, GNUTLS_MAC_AES_CMAC_128, (void *)2, 0);'
then
add_def HAVE_CMAC
EXTRA_OBJECTS="$EXTRA_OBJECTS cmac_gnutls.o"
EXTRA_CLI_OBJECTS="$EXTRA_CLI_OBJECTS cmac_gnutls.o"
fi
fi
fi
EXTRA_OBJECTS="$EXTRA_OBJECTS $HASH_OBJ" EXTRA_OBJECTS="$EXTRA_OBJECTS $HASH_OBJ"
EXTRA_CLI_OBJECTS="$EXTRA_CLI_OBJECTS $HASH_OBJ" EXTRA_CLI_OBJECTS="$EXTRA_CLI_OBJECTS $HASH_OBJ"
LIBS="$LIBS $HASH_LINK" LIBS="$LIBS $HASH_LINK"
@@ -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_priority_init2((void *)1, "", NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND) +
gnutls_prf_rfc5705((void *)1, 0, "", 0, "", 16, (void *)2);' gnutls_prf_rfc5705((void *)1, 0, "", 0, "", 16, (void *)2);'
then then
if test_code 'SIV in nettle' \ if [ $try_nettle = "1" ] && test_code 'AES-SIV-CMAC in nettle' \
'nettle/siv-cmac.h' "" "$LIBS" \ 'nettle/siv-cmac.h' "" "$LIBS" \
'siv_cmac_aes128_set_key((void *)1, (void *)2);' 'siv_cmac_aes128_set_key((void *)1, (void *)2);'
then then
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o" EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o"
add_def HAVE_SIV add_def HAVE_SIV
add_def HAVE_NETTLE_SIV_CMAC add_def HAVE_NETTLE_SIV_CMAC
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 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" ' "$test_cflags" "$test_link $LIBS" '
return gnutls_aead_cipher_init((void *)1, GNUTLS_CIPHER_AES_128_SIV, (void *)2);' return gnutls_aead_cipher_init((void *)1, GNUTLS_CIPHER_AES_128_SIV, (void *)2);'
then then
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_gnutls.o" EXTRA_OBJECTS="$EXTRA_OBJECTS siv_gnutls.o"
add_def HAVE_SIV add_def HAVE_SIV
if [ $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' \ if test_code 'gnutls_aead_cipher_set_key()' 'gnutls/crypto.h' \
"$test_cflags" "$test_link $LIBS" ' "$test_cflags" "$test_link $LIBS" '
return gnutls_aead_cipher_set_key((void *)1, (void *)2);' return gnutls_aead_cipher_set_key((void *)1, (void *)2);'
@@ -1110,6 +1136,7 @@ do
s%@CFLAGS@%${MYCFLAGS}%;\ s%@CFLAGS@%${MYCFLAGS}%;\
s%@CPPFLAGS@%${MYCPPFLAGS}%;\ s%@CPPFLAGS@%${MYCPPFLAGS}%;\
s%@LDFLAGS@%${MYLDFLAGS}%;\ s%@LDFLAGS@%${MYLDFLAGS}%;\
s%@GETDATE_CFLAGS@%${GETDATE_CFLAGS}%;\
s%@LIBS@%${LIBS}%;\ s%@LIBS@%${LIBS}%;\
s%@EXTRA_LIBS@%${EXTRA_LIBS}%;\ s%@EXTRA_LIBS@%${EXTRA_LIBS}%;\
s%@EXTRA_CLI_LIBS@%${EXTRA_CLI_LIBS}%;\ s%@EXTRA_CLI_LIBS@%${EXTRA_CLI_LIBS}%;\

View File

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

View File

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

View File

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

View File

@@ -3,7 +3,7 @@
// Copyright (C) Richard P. Curnow 1997-2003 // Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Stephen Wadeley 2016 // Copyright (C) Stephen Wadeley 2016
// Copyright (C) Bryan Christianson 2017 // Copyright (C) Bryan Christianson 2017
// Copyright (C) Miroslav Lichvar 2009-2021 // Copyright (C) Miroslav Lichvar 2009-2023
// //
// This program is free software; you can redistribute it and/or modify // This program is free software; you can redistribute it and/or modify
// it under the terms of version 2 of the GNU General Public License as // it under the terms of version 2 of the GNU General Public License as
@@ -63,10 +63,20 @@ source. The client-server relationship is strictly hierarchical: a client might
synchronise its system time to that of the server, but the server's system time synchronise its system time to that of the server, but the server's system time
will never be influenced by that of a client. will never be influenced by that of a client.
+ +
The server can be specified by its hostname or IP address. If the hostname cannot
be resolved on start, *chronyd* will try it again in increasing intervals, and
also when the <<chronyc.adoc#online,*online*>> command is issued in *chronyc*.
+
The DNS record can change over time. The used address will be replaced with a
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.
+
This directive can be used multiple times to specify multiple servers. This directive can be used multiple times to specify multiple servers.
+ +
The directive is immediately followed by either the name of the The directive supports the following options:
server, or its IP address. It supports the following options:
+ +
*minpoll* _poll_::: *minpoll* _poll_:::
This option specifies the minimum interval between requests sent to the server This option specifies the minimum interval between requests sent to the server
@@ -312,16 +322,27 @@ server implementations do not respond to requests containing an unknown
extension field (*chronyd* as a server responded to such requests since extension field (*chronyd* as a server responded to such requests since
version 2.0). 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_:::: _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 proposed for the next version of the NTP protocol (NTPv5). The field contains
root delay and dispersion in higher resolution and a monotonic receive root delay and dispersion in higher resolution and a monotonic receive
timestamp, which enables a frequency transfer between the server and client. It timestamp, which enables a frequency transfer between the server and client to
can significantly improve stability of the synchronization. Generally, it significantly improve stability of the synchronisation. This field should be
should be expected to work only between servers and clients running the same enabled only for servers known to be running *chronyd* version 4.2 or later.
version of *chronyd*. _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.
{blank}::: {blank}:::
[[pool]]*pool* _name_ [_option_]...:: [[pool]]*pool* _name_ [_option_]...::
@@ -341,12 +362,6 @@ This option sets the desired number of sources to be used from the pool.
sources responding to requests. The default value is 4 and the maximum value is sources responding to requests. The default value is 4 and the maximum value is
16. 16.
+ +
{blank}::
When an NTP source is unreachable,
marked as a falseticker, or has a distance larger than the limit set by the
<<maxdistance,*maxdistance*>> directive, *chronyd* will try to replace the
source with a newly resolved address of the name.
+
An example of the *pool* directive is An example of the *pool* directive is
+ +
---- ----
@@ -414,7 +429,7 @@ error. *chronyd* then enters its normal operating mode.
An example of the use of the directive is: An example of the use of the directive is:
+ +
---- ----
initstepslew 30 foo.example.net bar.example.net 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 where 3 NTP servers are used to make the measurement. The _30_ indicates that
@@ -467,16 +482,41 @@ instead.
Examples: Examples:
+ +
---- ----
refclock PPS /dev/pps0 lock NMEA refid GPS refclock PPS /dev/pps0 lock NMEA refid GPS1
refclock SHM 0 offset 0.5 delay 0.2 refid NMEA noselect refclock SOCK /var/run/chrony.clk.ttyS0.sock offset 0.5 delay 0.2 refid NMEA noselect
refclock PPS /dev/pps1:clear refid GPS2 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*::: *SHM*:::
NTP shared memory driver. This driver uses a shared memory segment to receive NTP shared memory driver. This driver implements the protocol of the *ntpd*
samples from another process (e.g. *gpsd*). The parameter is the number of the driver type 28. It is functionally similar to the SOCK driver, but uses a
shared memory segment, typically a small number like 0, 1, 2, or 3. The driver shared memory segment instead of a socket. The parameter is the unit number,
supports the following option: 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_:::: *perm*=_mode_::::
This option specifies the permissions of the shared memory segment created by This option specifies the permissions of the shared memory segment created by
@@ -484,6 +524,16 @@ This option specifies the permissions of the shared memory segment created by
(read-write access for owner only). (read-write access for owner only).
{blank}::: {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: Examples:
+ +
---- ----
@@ -491,23 +541,6 @@ refclock SHM 0 poll 3 refid GPS1
refclock SHM 1:perm=0644 refid GPS2 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*::: *PHC*:::
PTP hardware clock (PHC) driver. The parameter is the path to the device of 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 the PTP clock which should be used as a time source. If the clock is kept in
@@ -618,7 +651,9 @@ 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 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 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 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*:::
Prefer this source over sources without the prefer option. Prefer this source over sources without the prefer option.
*noselect*::: *noselect*:::
@@ -803,11 +838,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 This directive specifies the maximum interval between NTS-KE handshakes (in
seconds) in order to refresh the keys authenticating NTP packets. The default seconds) in order to refresh the keys authenticating NTP packets. The default
value is 2419200 (4 weeks) and the maximum value is 2^31-1 (68 years). 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_:: [[ntstrustedcerts]]*ntstrustedcerts* [_set-ID_] _file_|_directory_::
This directive specifies a file or directory containing certificates (in the This directive specifies a file or directory containing trusted certificates
PEM format) of trusted certificate authorities (CA) which can be used to (in the PEM format) which are needed to verify certificates of NTS-KE servers,
verify certificates of NTS 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 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 selects the set of certificates where certificates from the specified file
@@ -827,10 +867,10 @@ they change (e.g. after a renewal).
An example is: An example is:
+ +
---- ----
ntstrustedcerts /etc/pki/nts/foo.crt ntstrustedcerts /etc/pki/nts/ca1.example.net.crt
ntstrustedcerts 1 /etc/pki/nts/bar.crt ntstrustedcerts 1 /etc/pki/nts/ca2.example.net.crt
ntstrustedcerts 1 /etc/pki/nts/baz.crt ntstrustedcerts 1 /etc/pki/nts/ca3.example.net.crt
ntstrustedcerts 2 /etc/pki/nts/qux.crt ntstrustedcerts 2 /etc/pki/nts/ntp2.example.net.crt
---- ----
[[nosystemcert]]*nosystemcert*:: [[nosystemcert]]*nosystemcert*::
@@ -856,6 +896,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 time, assuming the first update corrects the clock and later checks can work
with correct time. 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 === Source selection
[[authselectmode]]*authselectmode* _mode_:: [[authselectmode]]*authselectmode* _mode_::
@@ -914,20 +967,20 @@ before 4.0.
As an example, the following configuration using the default *mix* mode: As an example, the following configuration using the default *mix* mode:
+ +
---- ----
server foo.example.net nts server ntp1.example.net nts
server bar.example.net nts server ntp2.example.net nts
server baz.example.net server ntp3.example.net
refclock SHM 0 refclock SOCK /var/run/chrony.ttyS0.sock
---- ----
+ +
is equivalent to the following configuration using the *ignore* mode: is equivalent to the following configuration using the *ignore* mode:
+ +
---- ----
authselectmode ignore authselectmode ignore
server foo.example.net nts require trust server ntp1.example.net nts require trust
server bar.example.net nts require trust server ntp2.example.net nts require trust
server baz.example.net server ntp3.example.net
refclock SHM 0 require trust refclock /var/run/chrony.ttyS0.sock require trust
---- ----
[[combinelimit]]*combinelimit* _limit_:: [[combinelimit]]*combinelimit* _limit_::
@@ -1296,10 +1349,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 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 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 generated following another measurement from one of the sources, a weighted
combination algorithm is used to update the master estimate. So if *chronyd* combination algorithm is used to update the existing estimate. If it has
has an existing highly-reliable master estimate and a new estimate is generated significantly smaller error bounds than the new estimate, the existing estimate
which has large error bounds, the existing master estimate will dominate in the will dominate in the new combined value.
new master estimate.
[[maxslewrate]]*maxslewrate* _rate-in-ppm_:: [[maxslewrate]]*maxslewrate* _rate-in-ppm_::
The *maxslewrate* directive sets the maximum rate at which *chronyd* is allowed The *maxslewrate* directive sets the maximum rate at which *chronyd* is allowed
@@ -1729,7 +1781,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 the <<chronyc.adoc#rekey,*rekey*>> command is issued in *chronyc*. The file can
be periodically copied from another server running *chronyd* (which does be periodically copied from another server running *chronyd* (which does
not have *ntsrotate* set to 0) in order to have one or more servers dedicated not have *ntsrotate* set to 0) in order to have one or more servers dedicated
to NTS-KE. The NTS-KE servers need to be configured with the to NTS-KE. The 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 <<ntsntpserver,*ntsntpserver*>> directive to point the clients to the right NTP
server. server.
+ +
@@ -1933,8 +1988,9 @@ all* directive.
[[cmdport]]*cmdport* _port_:: [[cmdport]]*cmdport* _port_::
The *cmdport* directive allows the port that is used for run-time monitoring 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, (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* *chronyd* will not open the port, which disables remote *chronyc* access (with
access from the Internet. (It does not disable the Unix domain command socket.) a non-default *bindcmdaddress*) and local access for unprivileged users. It
does not disable the Unix domain command socket.
+ +
An example shows the syntax: An example shows the syntax:
+ +
@@ -1943,7 +1999,7 @@ cmdport 257
---- ----
+ +
This would make *chronyd* use UDP 257 as its command port. (*chronyc* would 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_]...:: [[cmdratelimit]]*cmdratelimit* [_option_]...::
This directive enables response rate limiting for command packets. It is This directive enables response rate limiting for command packets. It is
@@ -2212,6 +2268,13 @@ Used for synchronisation of the local clock:
* _+_ - combined with the best source. * _+_ - combined with the best source.
* _*_ - selected as the best source to update the reference data (e.g. root * _*_ - selected as the best source to update the reference data (e.g. root
delay, root dispersion). 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 . 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 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 indicates that a valid reply was received for all from the last eight
@@ -2497,10 +2560,13 @@ physical clock created by writing to _/sys/class/ptp/ptpX/n_vclocks_. This
feature is available on Linux 5.14 and newer. feature is available on Linux 5.14 and newer.
+ +
If the kernel supports software timestamping, it will be enabled for all If the kernel supports software timestamping, it will be enabled for all
interfaces. The source of timestamps (i.e. hardware, kernel, or daemon) is interfaces automatically.
indicated in the _measurements.log_ file if enabled by the <<log,*log +
measurements*>> directive, and the <<chronyc.adoc#ntpdata,*ntpdata*>> report in The source of timestamps (i.e. hardware, kernel, or daemon) is indicated on the
*chronyc*. 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 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 interfaces. If the specified interface is _*_, *chronyd* will try to enable HW
@@ -2510,10 +2576,15 @@ The *hwtimestamp* directive has the following options:
+ +
*minpoll* _poll_::: *minpoll* _poll_:::
This option specifies the minimum interval between readings of the NIC clock. 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 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 clients. The default value is 0 (1 second), the minimum value is -6 (1/64th
of a second). 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_::: *minsamples* _samples_:::
This option specifies the minimum number of readings kept for tracking of the This option specifies the minimum number of readings kept for tracking of the
NIC clock. The default value is 2. NIC clock. The default value is 2.
@@ -2566,6 +2637,27 @@ hwtimestamp eth1 txcomp 300e-9 rxcomp 645e-9
hwtimestamp * 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_:: [[keyfile]]*keyfile* _file_::
This directive is used to specify the location of the file containing symmetric 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 keys which are shared between NTP servers and clients, or peers, in order to
@@ -2649,24 +2741,27 @@ pidfile /run/chronyd.pid
The *ptpport* directive enables *chronyd* to send and receive NTP messages The *ptpport* directive enables *chronyd* to send and receive NTP messages
contained in PTP event messages (NTP-over-PTP) to enable hardware timestamping contained in PTP event messages (NTP-over-PTP) to enable hardware timestamping
on NICs which cannot timestamp NTP packets, but can timestamp unicast PTP 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 packets, and also use corrections provided by PTP one-step end-to-end
value is 0 (disabled). 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 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 change in future. It should be used only in local networks.
only between servers and clients running the same version of *chronyd*.
+ +
The PTP port will be open even if *chronyd* is not configured to operate as a 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 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 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 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 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: 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 hwtimestamp * rxfilter ptp
ptpport 319 ptpport 319
---- ----
@@ -2727,13 +2822,13 @@ the following methods:
facilities. facilities.
* Use public servers from the https://www.pool.ntp.org/[pool.ntp.org] project. * 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_ Assuming that your NTP servers are called _ntp1.example.net_, _ntp2.example.net_
and _baz.example.net_, your _chrony.conf_ file could contain as a minimum: and _ntp3.example.net_, your _chrony.conf_ file could contain as a minimum:
---- ----
server foo.example.net server ntp1.example.net
server bar.example.net server ntp2.example.net
server baz.example.net server ntp3.example.net
---- ----
However, you will probably want to include some of the other directives. The However, you will probably want to include some of the other directives. The
@@ -2744,9 +2839,9 @@ synchronisation. The smallest useful configuration file would look something
like: like:
---- ----
server foo.example.net iburst server ntp1.example.net iburst
server bar.example.net iburst server ntp2.example.net iburst
server baz.example.net iburst server ntp3.example.net iburst
driftfile @CHRONYVARDIR@/drift driftfile @CHRONYVARDIR@/drift
makestep 1.0 3 makestep 1.0 3
rtcsync rtcsync
@@ -2770,9 +2865,9 @@ option will enable a secure synchronisation to the servers. The configuration
file could look like: file could look like:
---- ----
server foo.example.net iburst nts server ntp1.example.net iburst nts
server bar.example.net iburst nts server ntp2.example.net iburst nts
server baz.example.net iburst nts server ntp3.example.net iburst nts
driftfile @CHRONYVARDIR@/drift driftfile @CHRONYVARDIR@/drift
makestep 1.0 3 makestep 1.0 3
rtcsync rtcsync
@@ -2786,14 +2881,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 down. This saves the program from continuously trying to poll the servers when
they are inaccessible. they are inaccessible.
Again, assuming that your NTP servers are called _foo.example.net_, Again, assuming that your NTP servers are called _ntp1.example.net_,
_bar.example.net_ and _baz.example.net_, your _chrony.conf_ file would now _ntp2.example.net_ and _ntp3.example.net_, your _chrony.conf_ file would now
contain: contain:
---- ----
server foo.example.net offline server ntp1.example.net offline
server bar.example.net offline server ntp2.example.net offline
server baz.example.net offline server ntp3.example.net offline
driftfile @CHRONYVARDIR@/drift driftfile @CHRONYVARDIR@/drift
makestep 1.0 3 makestep 1.0 3
rtcsync rtcsync
@@ -2977,9 +3072,9 @@ configuration files are shown.
For the _chrony.conf_ file, the following can be used as an example. For the _chrony.conf_ file, the following can be used as an example.
---- ----
server foo.example.net maxdelay 0.4 offline server ntp1.example.net maxdelay 0.4 offline
server bar.example.net maxdelay 0.4 offline server ntp2.example.net maxdelay 0.4 offline
server baz.example.net maxdelay 0.4 offline server ntp3.example.net maxdelay 0.4 offline
logdir /var/log/chrony logdir /var/log/chrony
log statistics measurements tracking log statistics measurements tracking
driftfile @CHRONYVARDIR@/drift driftfile @CHRONYVARDIR@/drift
@@ -3038,10 +3133,10 @@ configuration).
The configuration file could look like: The configuration file could look like:
---- ----
server foo.example.net iburst server ntp1.example.net iburst
server bar.example.net iburst server ntp2.example.net iburst
server baz.example.net iburst server ntp3.example.net iburst
server qux.example.net iburst server ntp4.example.net iburst
makestep 1.0 3 makestep 1.0 3
rtcsync rtcsync
allow allow
@@ -3058,7 +3153,7 @@ dumpdir @CHRONYRUNDIR@
== BUGS == BUGS
For instructions on how to report bugs, please visit For instructions on how to report bugs, please visit
https://chrony.tuxfamily.org/. https://chrony-project.org/.
== AUTHORS == AUTHORS

View File

@@ -2,7 +2,7 @@
// //
// Copyright (C) Richard P. Curnow 1997-2003 // Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Stephen Wadeley 2016 // Copyright (C) Stephen Wadeley 2016
// Copyright (C) Miroslav Lichvar 2009-2017, 2019-2020 // Copyright (C) Miroslav Lichvar 2009-2017, 2019-2023
// //
// This program is free software; you can redistribute it and/or modify // This program is free software; you can redistribute it and/or modify
// it under the terms of version 2 of the GNU General Public License as // it under the terms of version 2 of the GNU General Public License as
@@ -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 seconds since the epoch, and values in seconds will not be converted to other
units. units.
*-e*::
With this option each *chronyc* response will end with a line containing a
single dot.
*-d*:: *-d*::
This option enables printing of debugging messages if *chronyc* was compiled This option enables printing of debugging messages if *chronyc* was compiled
with debugging support. 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. performance. An example of the output is shown below.
+ +
---- ----
Reference ID : CB00710F (foo.example.net) Reference ID : CB00710F (ntp1.example.net)
Stratum : 3 Stratum : 3
Ref time (UTC) : Fri Jan 27 09:49:17 2017 Ref time (UTC) : Fri Jan 27 09:49:17 2017
System time : 0.000006523 seconds slow of NTP time System time : 0.000006523 seconds slow of NTP time
@@ -174,21 +178,25 @@ with an IPv4 address.
*Stratum*::: *Stratum*:::
The stratum indicates how many hops away from a computer with an attached 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 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). stratum-2 and is synchronised from a stratum-1).
*Ref time*::: *Ref time*:::
This is the time (UTC) at which the last measurement from the reference This is the time (UTC) at which the last measurement from the reference
source was processed. source was processed.
*System time*::: *System time*:::
In normal operation, *chronyd* by default never steps the system clock, because This is the current offset between the NTP clock and system clock. The NTP
any jump in the time can have adverse consequences for certain application clock is a software (virtual) clock maintained by *chronyd*, which is
programs. Instead, any error in the system clock is corrected by slightly synchronised to the configured time sources and provides time to NTP clients.
speeding up or slowing down the system clock until the error has been removed, The system clock is synchronised to the NTP clock. To avoid steps in the
and then returning to the system clock's normal speed. A consequence of this is system time, which might have adverse consequences for certain applications,
that there will be a period when the system clock (as read by other programs) the system clock is normally corrected only by speeding up or slowing down (up
will be different from *chronyd*'s estimate of the current true time (which it to the rate configured by the <<chrony.conf.adoc#maxslewrate,*maxslewrate*>>
reports to NTP clients when it is operating as a server). The value reported directive). If the offset is too large, this correction will take a very long
on this line is the difference due to this effect. time. A step can be forced by the <<makestep,*makestep*>> command, or the
<<chrony.conf.adoc#makestep,*makestep*>> directive in the configuration file.
+
Note that all other offsets reported by *chronyc* and most offsets in the log
files are relative to the NTP clock, not the system clock.
*Last offset*::: *Last offset*:::
This is the estimated local offset on the last clock update. A positive value This is the estimated local offset on the last clock update. A positive value
indicates the local time (as previously estimated true time) was ahead of the indicates the local time (as previously estimated true time) was ahead of the
@@ -313,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 MS Name/IP address Stratum Poll Reach LastRx Last sample
=============================================================================== ===============================================================================
#* GPS0 0 4 377 11 -479ns[ -621ns] +/- 134ns #* GPS0 0 4 377 11 -479ns[ -621ns] +/- 134ns
^? foo.example.net 2 6 377 23 -923us[ -924us] +/- 43ms ^? ntp1.example.net 2 6 377 23 -923us[ -924us] +/- 43ms
^+ bar.example.net 1 6 377 21 -2629us[-2619us] +/- 86ms ^+ ntp2.example.net 1 6 377 21 -2629us[-2619us] +/- 86ms
---- ----
+ +
The columns are as follows: The columns are as follows:
@@ -371,9 +379,9 @@ offset. This can be suffixed by _ns_ (indicating nanoseconds), _us_
(indicating microseconds), _ms_ (indicating milliseconds), or _s_ (indicating (indicating microseconds), _ms_ (indicating milliseconds), or _s_ (indicating
seconds). The number to the left of the square brackets shows the original 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 measurement, adjusted to allow for any slews applied to the local clock
since. The number following the _+/-_ indicator shows the margin of error in since. Positive offsets indicate that the local clock is ahead of the source.
the measurement. Positive offsets indicate that the local clock is ahead of The number following the _+/-_ indicator shows the margin of error in the
the source. measurement (NTP root distance).
[[sourcestats]]*sourcestats* [*-a*] [*-v*]:: [[sourcestats]]*sourcestats* [*-a*] [*-v*]::
The *sourcestats* command displays information about the drift rate and offset The *sourcestats* command displays information about the drift rate and offset
@@ -392,7 +400,7 @@ An example report is:
---- ----
Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev 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: The columns are as follows:
@@ -436,9 +444,9 @@ An example of the output is shown below.
---- ----
S Name/IP Address Auth COpts EOpts Last Score Interval Leap S Name/IP Address Auth COpts EOpts Last Score Interval Leap
======================================================================= =======================================================================
D foo.example.net Y ----- --TR- 4 1.0 -61ms +62ms N D ntp1.example.net Y ----- --TR- 4 1.0 -61ms +62ms N
* bar.example.net N ----- ----- 0 1.0 -6846us +7305us N * ntp2.example.net N ----- ----- 0 1.0 -6846us +7305us N
+ baz.example.net N ----- ----- 10 1.0 -7381us +7355us N + ntp3.example.net N ----- ----- 10 1.0 -7381us +7355us N
---- ----
+ +
The columns are as follows: The columns are as follows:
@@ -496,8 +504,8 @@ This column displays the configured selection options of the source.
This column displays the current effective 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 which can be different from the configured options due to the authentication
selection mode (configured by the selection mode (configured by the
<<chrony.conf.adoc#authselmode,*authselmode*>> directive). The symbols are the <<chrony.conf.adoc#authselectmode,*authselectmode*>> directive). The symbols
same as in the *COpts* column. are the same as in the *COpts* column.
*Last*::: *Last*:::
This column displays how long ago was the last measurement of the source made This column displays how long ago was the last measurement of the source made
when the selection was performed. when the selection was performed.
@@ -518,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 that a leap second will be deleted at the end of the month.
* _?_ indicates the unknown status (i.e. no valid measurement was made). * _?_ 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*:: [[reselect]]*reselect*::
To avoid excessive switching between sources, *chronyd* can stay synchronised 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 to a source even when it is not currently the best one among the available
@@ -567,9 +592,9 @@ shown below.
---- ----
Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen
========================================================================= =========================================================================
foo.example.net NTS 1 15 256 135m 0 0 8 100 ntp1.example.net NTS 1 15 256 135m 0 0 8 100
bar.example.net SK 30 13 128 - 0 0 0 0 ntp2.example.net SK 30 13 128 - 0 0 0 0
baz.example.net - 0 0 0 - 0 0 0 0 ntp3.example.net - 0 0 0 - 0 0 0 0
---- ----
+ +
The columns are as follows: The columns are as follows:
@@ -606,6 +631,7 @@ be reported:
* 13: AES128 * 13: AES128
* 14: AES256 * 14: AES256
* 15: AEAD-AES-SIV-CMAC-256 * 15: AEAD-AES-SIV-CMAC-256
* 30: AEAD-AES-128-GCM-SIV
*KLen*::: *KLen*:::
This column shows the length of the key in bits. This column shows the length of the key in bits.
*Last*::: *Last*:::
@@ -662,6 +688,7 @@ RX timestamping : Kernel
Total TX : 24 Total TX : 24
Total RX : 24 Total RX : 24
Total valid RX : 24 Total valid RX : 24
Total good RX : 22
---- ----
+ +
The fields are explained as follows: The fields are explained as follows:
@@ -715,7 +742,10 @@ The number of packets sent to the source.
*Total RX*::: *Total RX*:::
The number of all packets received from the source. The number of all packets received from the source.
*Total valid RX*::: *Total valid RX*:::
The number of valid packets received from the source. 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.
[[add_peer]]*add peer* _name_ [_option_]...:: [[add_peer]]*add peer* _name_ [_option_]...::
The *add peer* command allows a new NTP peer to be added whilst The *add peer* command allows a new NTP peer to be added whilst
@@ -728,7 +758,7 @@ parameters and options is identical to that for the
An example of using this command is shown below. 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_]...:: [[add_pool]]*add pool* _name_ [_option_]...::
@@ -742,7 +772,7 @@ directive in the configuration file.
An example of using this command is shown below: 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_]...:: [[add_server]]*add server* _name_ [_option_]...::
@@ -756,7 +786,7 @@ directive in the configuration file.
An example of using this command is shown below: 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_:: [[delete]]*delete* _address_::
@@ -832,7 +862,7 @@ IPv6 addresses have first 48 bits equal to _2001:db8:789a_.
Example of the three-argument form of the command is: 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_:: [[maxdelay]]*maxdelay* _address_ _delay_::
@@ -898,7 +928,7 @@ uses an IP address or a hostname. These forms are illustrated below.
offline offline
offline 255.255.255.0/1.2.3.0 offline 255.255.255.0/1.2.3.0
offline 2001:db8:789a::/48 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 The second form means that the *offline* command is to be applied to any source
@@ -940,17 +970,26 @@ current set of sources. It is equivalent to the *polltarget* option in the
[[refresh]]*refresh*:: [[refresh]]*refresh*::
The *refresh* command can be used to force *chronyd* to resolve the names of 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 configured NTP sources to IP addresses again and replace any addresses missing
the machine in a different network. in the list of resolved addresses.
+ +
Sources that stop responding will be replaced with newly resolved addresses Sources that stop responding are replaced with newly resolved addresses
automatically after 8 polling intervals, but this command can still be useful automatically after 8 polling intervals. This command can be used to replace
to replace them immediately and not wait until they are marked as unreachable. 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*:: [[reload]]*reload* *sources*::
The *reload sources* command causes *chronyd* to re-read all _*.sources_ files The *reload sources* command causes *chronyd* to re-read all _*.sources_ files
from the directories specified by the from the directories specified by the
<<chrony.conf.adoc#sourcedir,*sourcedir*>> directive. <<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_:: [[sourcename]]*sourcename* _address_::
The *sourcename* command prints the original hostname or address that was The *sourcename* command prints the original hostname or address that was
@@ -1059,7 +1098,7 @@ particular host.
Examples of use, showing a named host and a numeric IP address, are as follows: 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 1.2.3.4
accheck 2001:db8::1 accheck 2001:db8::1
---- ----
@@ -1086,7 +1125,7 @@ An example of the output is:
Hostname NTP Drop Int IntL Last Cmd Drop Int Last Hostname NTP Drop Int IntL Last Cmd Drop Int Last
=============================================================================== ===============================================================================
localhost 2 0 2 - 133 15 0 -1 7 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 Each row shows the data for a single host. Only hosts that have passed the host
@@ -1128,6 +1167,12 @@ Authenticated NTP packets : 189
Interleaved NTP packets : 43 Interleaved NTP packets : 43
NTP timestamps held : 44 NTP timestamps held : 44
NTP timestamp span : 120 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: The fields have the following meaning:
@@ -1162,10 +1207,24 @@ The number of pairs of receive and transmit timestamps that the server is
currently holding in memory for clients using the interleaved mode. currently holding in memory for clients using the interleaved mode.
*NTP timestamp span*::: *NTP timestamp span*:::
The interval (in seconds) covered by the currently held NTP timestamps. The interval (in seconds) covered by the currently held NTP timestamps.
{blank}:: *NTP daemon RX timestamps*:::
+ The number of NTP responses which included a receive timestamp captured by the
Note that the numbers reported by this overflow to zero after 4294967295 daemon.
(32-bit values). *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_]:: [[allow]]*allow* [*all*] [_subnet_]::
The effect of the allow command is identical to the The effect of the allow command is identical to the
@@ -1204,8 +1263,8 @@ deny all
*local* *off*:: *local* *off*::
The *local* command allows *chronyd* to be told that it is to appear as a 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 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 source. This can be used on isolated networks, to allow a computer to be the
master time server with the other computers slaving to it.) primary time server for other computers.
+ +
The first form enables the local reference mode on the host. The syntax is 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 identical to the <<chrony.conf.adoc#local,*local*>> directive in the
@@ -1266,7 +1325,7 @@ used to check whether monitoring access is permitted from a named host.
Examples of use are as follows: Examples of use are as follows:
+ +
---- ----
cmdaccheck foo.example.net cmdaccheck ntp1.example.net
cmdaccheck 1.2.3.4 cmdaccheck 1.2.3.4
cmdaccheck 2001:db8::1 cmdaccheck 2001:db8::1
---- ----
@@ -1493,7 +1552,7 @@ The *help* command displays a summary of the commands and their arguments.
== BUGS == BUGS
For instructions on how to report bugs, please visit For instructions on how to report bugs, please visit
https://chrony.tuxfamily.org/. https://chrony-project.org/.
== AUTHORS == AUTHORS

View File

@@ -72,9 +72,9 @@ terminal.
*-L* _level_:: *-L* _level_::
This option specifies the minimum severity level of messages to be written to 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: the log file, syslog, or terminal. The following levels can be specified: -1
0 (informational), 1 (warning), 2 (non-fatal error), and 3 (fatal error). The (debug, if compiled with enabled support for debugging), 0 (informational), 1
default value is 0. (warning), 2 (non-fatal error), and 3 (fatal error). The default value is 0.
*-p*:: *-p*::
When run in this mode, *chronyd* will print the configuration and exit. It will 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*:: *-Q*::
This option is similar to the *-q* option, except it only prints the offset 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 without making any corrections of the clock and disables server ports to allow
started without root privileges. *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*:: *-r*::
This option will try to reload and then delete files containing sample 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*:: *-h*, *--help*::
With this option *chronyd* will print a help message to the terminal and exit. 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 == FILES
_@SYSCONFDIR@/chrony.conf_ _@SYSCONFDIR@/chrony.conf_
@@ -215,7 +228,7 @@ _@SYSCONFDIR@/chrony.conf_
== BUGS == BUGS
For instructions on how to report bugs, please visit For instructions on how to report bugs, please visit
https://chrony.tuxfamily.org/. https://chrony-project.org/.
== AUTHORS == AUTHORS

View File

@@ -1,7 +1,8 @@
// This file is part of chrony // This file is part of chrony
// //
// Copyright (C) Richard P. Curnow 1997-2003 // Copyright (C) Richard P. Curnow 1997-2003
// Copyright (C) Miroslav Lichvar 2014-2016, 2020-2021 // 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 // This program is free software; you can redistribute it and/or modify
// it under the terms of version 2 of the GNU General Public License as // it under the terms of version 2 of the GNU General Public License as
@@ -40,9 +41,36 @@ on an isolated network with no hardware reference clocks in sight, `chrony`
will probably work better for you. will probably work better for you.
For a more detailed comparison of features and performance, see the 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. 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 == Configuration issues
=== What is the minimum recommended configuration for an NTP client? === 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 the unauthenticated servers to override the authenticated servers in the source
selection. Since `chrony` version 4.0, the selection options are enabled in 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 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 An example of a client configuration limiting the impact of the attacks could
be be
---- ----
server foo.example.net iburst nts maxdelay 0.1 server ntp1.example.net iburst nts maxdelay 0.1
server bar.example.net iburst nts maxdelay 0.2 server ntp2.example.net iburst nts maxdelay 0.2
server baz.example.net iburst nts maxdelay 0.05 server ntp3.example.net iburst nts maxdelay 0.05
server qux.example.net iburst nts maxdelay 0.1 server ntp4.example.net iburst nts maxdelay 0.1
server quux.example.net iburst nts maxdelay 0.1 server ntp5.example.net iburst nts maxdelay 0.1
minsources 3 minsources 3
maxchange 100 0 0 maxchange 100 0 0
makestep 0.001 1 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 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 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 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 extension field containing an additional timestamp to enable frequency transfer
and significantly improve stability of synchronisation. It can be enabled by and significantly improve stability of synchronisation. It can be enabled by
the `extfield F323` option. For example: 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 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? === Does `chronyd` have an ntpdate mode?
Yes. With the `-q` option `chronyd` will set the system clock once and exit. Yes. With the `-q` option `chronyd` will set the system clock once and exit.
@@ -470,6 +510,59 @@ pidfile /var/run/chronyd-server1.pid
driftfile /var/lib/chrony/drift-server1 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? === Should be a leap smear enabled on NTP server?
With the `smoothtime` and `leapsecmode` directives it is possible to enable a 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 leap seconds from other sources (e.g. with the `leapsectz` directive in
`chrony`) and they will not work correctly with a leap smearing server. `chrony`) and they will not work correctly with a leap smearing server.
=== How should `chronyd` be configuration with `gpsd`? === How should `chronyd` be configured with `gpsd`?
A GPS or other GNSS receiver can be used as a reference clock with `gpsd`. It 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 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 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. `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` The PPS-based time source provided by `gpsd` is available as a `SHM 1`
refclock, or other even number if `gpsd` is configured with multiple receivers. 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
The PPS-based time source is specified as a `SHM 1` refclock (or other odd
number), or `SOCK /var/run/chrony.DEV.sock` where `DEV` is the name of the
serial device (e.g. ttyS0). serial device (e.g. ttyS0).
With `chronyd` and `gpsd` both supporting PPS, and `gpsd` providing two The message-based time source is available as a `SHM 0` refclock (or other even
different refclocks for PPS, there are three different recommended number) and since `gpsd` version 3.25 also as
configurations: `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 # First option
refclock SOCK /var/run/chrony.ttyS0.sock refid GPS refclock SOCK /var/run/chrony.ttyS0.sock refid GPS
# Second option # Second option
refclock SHM 1 refid GPS
# Third option
refclock PPS /dev/pps0 lock NMEA refid GPS 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`), * `SOCK` can be more accurate than `PPS` if `gpsd` corrects for the
but it requires `gpsd` to be started after `chronyd` in order to connect to
its socket
* `SOCK` and `SHM 1` can be more accurate than `PPS` if `gpsd` corrects for the
sawtooth error provided by the receiver in serial data sawtooth error provided by the receiver in serial data
* `PPS` can be used with higher PPS rates (specified by the `rate` option), * `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 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 <<using-pps-refclock,correctly>> to compensate for the message delay, while
`gpsd` can apply HW-specific information `gpsd` can apply HW-specific information
If the PPS signal is not available, or cannot be used for some reason, the only If the PPS signal is not available, or cannot be used for some reason, the only
option is the message-based timing 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 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 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 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 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`? === 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 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 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 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 requests sent to the UDP port 123 of the server or responses sent back from
if you are getting any responses from the server. 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 When `chronyd` is receiving responses from the servers, the output of the
`sources` command issued few minutes after `chronyd` start might look like `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 MS Name/IP address Stratum Poll Reach LastRx Last sample
=============================================================================== ===============================================================================
^* foo.example.net 2 6 377 34 +484us[ -157us] +/- 30ms ^* ntp1.example.net 2 6 377 34 +484us[ -157us] +/- 30ms
^- bar.example.net 2 6 377 34 +33ms[ +32ms] +/- 47ms ^- ntp2.example.net 2 6 377 34 +33ms[ +32ms] +/- 47ms
^+ baz.example.net 3 6 377 35 -1397us[-2033us] +/- 60ms ^+ ntp3.example.net 3 6 377 35 -1397us[-2033us] +/- 60ms
---- ----
=== Are NTP servers specified with the `offline` option? === Are NTP servers specified with the `offline` option?
@@ -687,9 +810,9 @@ successful:
# chronyc -N authdata # chronyc -N authdata
Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen
========================================================================= =========================================================================
foo.example.net NTS 1 15 256 33m 0 0 8 100 ntp1.example.net NTS 1 15 256 33m 0 0 8 100
bar.example.net NTS 1 15 256 33m 0 0 8 100 ntp2.example.net NTS 1 15 256 33m 0 0 8 100
baz.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 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 PPS0 0 0 0 +0.000 2000.000 +0ns 4000ms
NMEA 58 30 231 -96.494 38.406 +504ms 6080us 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 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 MemoryDenyWriteExecute=yes
PrivateDevices=yes PrivateDevices=yes
PrivateUsers=yes PrivateUsers=yes
ProcSubset=pid
ProtectClock=yes ProtectClock=yes
ProtectControlGroups=yes ProtectControlGroups=yes
ProtectHome=yes ProtectHome=yes

View File

@@ -27,9 +27,9 @@
# you can access at http://support.ntp.org/bin/view/Servers/WebHome or # you can access at http://support.ntp.org/bin/view/Servers/WebHome or
# you can use servers from the pool.ntp.org project. # you can use servers from the pool.ntp.org project.
! server foo.example.net iburst ! server ntp1.example.net iburst
! server bar.example.net iburst ! server ntp2.example.net iburst
! server baz.example.net iburst ! server ntp3.example.net iburst
! pool pool.ntp.org 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 # and edit the following lines to specify the locations of the certificate and
# key. # key.
! ntsservercert /etc/.../foo.example.net.crt ! ntsservercert /etc/.../nts-server.crt
! ntsserverkey /etc/.../foo.example.net.key ! ntsserverkey /etc/.../nts-server.key
# chronyd can save the measurement history for the servers to files when # chronyd can save the measurement history for the servers to files when
# it exits. This is useful in 2 situations: # it exits. This is useful in 2 situations:
@@ -238,7 +238,7 @@ ntsdumpdir /var/lib/chrony
# several people, you need to set up a mailing list or sendmail alias # several people, you need to set up a mailing list or sendmail alias
# for them and use the address of that.) # for them and use the address of that.)
! mailonchange wibble@foo.example.net 0.5 ! mailonchange wibble@example.net 0.5
####################################################################### #######################################################################
### COMMAND ACCESS ### COMMAND ACCESS

View File

@@ -11,3 +11,5 @@
#1 MD5 AVeryLongAndRandomPassword #1 MD5 AVeryLongAndRandomPassword
#2 MD5 HEX:12114855C7931009B4049EF3EFC48A139C3F989F #2 MD5 HEX:12114855C7931009B4049EF3EFC48A139C3F989F
#3 SHA1 HEX:B2159C05D6A219673A3B7E896B6DE07F6A440995 #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 case "$2" in
up|down|connectivity-change) up|down|connectivity-change)
;; ;;
dhcp6-change) dhcp4-change|dhcp6-change)
# No other action is reported for routable IPv6 # 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;; 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 MemoryDenyWriteExecute=yes
NoNewPrivileges=yes NoNewPrivileges=yes
PrivateTmp=yes PrivateTmp=yes
ProcSubset=pid
ProtectControlGroups=yes ProtectControlGroups=yes
ProtectHome=yes ProtectHome=yes
ProtectHostname=yes ProtectHostname=yes

View File

@@ -448,9 +448,9 @@ o_merid : /* NULL */
the same signature as the function definition does. */ the same signature as the function definition does. */
#include "getdate.h" #include "getdate.h"
extern struct tm *gmtime (); extern struct tm *gmtime (const time_t *timep);
extern struct tm *localtime (); extern struct tm *localtime (const time_t *timep);
extern time_t mktime (); extern time_t mktime (struct tm *tm);
/* Month and day table. */ /* Month and day table. */
static TABLE const MonthDayTable[] = { static TABLE const MonthDayTable[] = {
@@ -641,16 +641,13 @@ static TABLE const MilitaryTable[] = {
/* ARGSUSED */ /* ARGSUSED */
static int static int
yyerror (s) yyerror (char *s ATTRIBUTE_UNUSED)
char *s ATTRIBUTE_UNUSED;
{ {
return 0; return 0;
} }
static int static int
ToHour (Hours, Meridian) ToHour (int Hours, MERIDIAN Meridian)
int Hours;
MERIDIAN Meridian;
{ {
switch (Meridian) switch (Meridian)
{ {
@@ -677,8 +674,7 @@ ToHour (Hours, Meridian)
} }
static int static int
ToYear (Year) ToYear (int Year)
int Year;
{ {
if (Year < 0) if (Year < 0)
Year = -Year; Year = -Year;
@@ -694,8 +690,7 @@ ToYear (Year)
} }
static int static int
LookupWord (buff) LookupWord (char *buff)
char *buff;
{ {
register char *p; register char *p;
register char *q; register char *q;

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate. chronyd/chronyc - Programs for keeping computer clocks accurate.
********************************************************************** **********************************************************************
* Copyright (C) Miroslav Lichvar 2016-2018 * Copyright (C) Miroslav Lichvar 2016-2018, 2022
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as

5
keys.c
View File

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

View File

@@ -39,6 +39,9 @@
/* This is used by DEBUG_LOG macro */ /* This is used by DEBUG_LOG macro */
LOG_Severity log_min_severity = LOGS_INFO; LOG_Severity log_min_severity = LOGS_INFO;
/* Current logging contexts */
static LOG_Context log_contexts;
/* ================================================== */ /* ================================================== */
/* Flag indicating we have initialised */ /* Flag indicating we have initialised */
static int initialised = 0; static int initialised = 0;
@@ -72,6 +75,8 @@ void
LOG_Initialise(void) LOG_Initialise(void)
{ {
debug_prefix = Strdup(""); debug_prefix = Strdup("");
log_contexts = 0;
initialised = 1; initialised = 1;
LOG_OpenFileLog(NULL); LOG_OpenFileLog(NULL);
} }
@@ -140,6 +145,7 @@ void LOG_Message(LOG_Severity severity,
struct tm *tm; struct tm *tm;
assert(initialised); assert(initialised);
severity = CLAMP(LOGS_DEBUG, severity, LOGS_FATAL);
if (!system_log && file_log && severity >= log_min_severity) { if (!system_log && file_log && severity >= log_min_severity) {
/* Don't clutter up syslog with timestamps and internal debugging info */ /* 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); fprintf(file_log, "%s ", buf);
} }
#if DEBUG > 0 #if DEBUG > 0
if (log_min_severity <= LOGS_DEBUG) if (log_min_severity <= LOGS_DEBUG) {
fprintf(file_log, "%s%s:%d:(%s) ", debug_prefix, filename, line_number, function_name); /* 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 #endif
} }
@@ -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 void
LOG_SetDebugPrefix(const char *prefix) LOG_SetDebugPrefix(const char *prefix)
{ {

View File

@@ -100,6 +100,20 @@ extern void LOG_SetMinSeverity(LOG_Severity severity);
/* Get the minimum severity */ /* Get the minimum severity */
extern LOG_Severity LOG_GetMinSeverity(void); 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 */ /* Set a prefix for debug messages */
extern void LOG_SetDebugPrefix(const char *prefix); extern void LOG_SetDebugPrefix(const char *prefix);

20
main.c
View File

@@ -331,6 +331,9 @@ go_daemon(void)
char message[1024]; char message[1024];
int r; int r;
/* Don't exit before the 'parent' */
waitpid(pid, NULL, 0);
close(pipefd[1]); close(pipefd[1]);
r = read(pipefd[0], message, sizeof (message)); r = read(pipefd[0], message, sizeof (message));
if (r) { if (r) {
@@ -353,7 +356,9 @@ go_daemon(void)
if (pid < 0) { if (pid < 0) {
LOG_FATAL("fork() failed : %s", strerror(errno)); LOG_FATAL("fork() failed : %s", strerror(errno));
} else if (pid > 0) { } else if (pid > 0) {
exit(0); /* In the 'parent' */ /* In the 'parent' */
close(pipefd[1]);
exit(0);
} else { } else {
/* In the child we want to leave running as the daemon */ /* In the child we want to leave running as the daemon */
@@ -363,9 +368,9 @@ go_daemon(void)
} }
/* Don't keep stdin/out/err from before. But don't close /* 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++) { for (fd=0; fd<1024; fd++) {
if (fd != pipefd[1]) if (fd != pipefd[1] && !SCK_IsReusable(fd))
close(fd); close(fd);
} }
@@ -555,6 +560,9 @@ int main
if (user_check && getuid() != 0) if (user_check && getuid() != 0)
LOG_FATAL("Not superuser"); LOG_FATAL("Not superuser");
/* Initialise reusable file descriptors before fork */
SCK_PreInitialise();
/* Turn into a daemon */ /* Turn into a daemon */
if (!nofork) { if (!nofork) {
go_daemon(); go_daemon();
@@ -637,9 +645,13 @@ int main
} }
/* Drop root privileges if the specified user has a non-zero UID */ /* Drop root privileges if the specified user has a non-zero UID */
if (!geteuid() && (pw->pw_uid || pw->pw_gid)) if (!geteuid() && (pw->pw_uid || pw->pw_gid)) {
SYS_DropRoot(pw->pw_uid, pw->pw_gid, SYS_MAIN_PROCESS); 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()) if (!geteuid())
LOG(LOGS_WARN, "Running with root privileges"); LOG(LOGS_WARN, "Running with root privileges");

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 /* The routine MD5Init initializes the message-digest context
mdContext. All fields are set to zero. mdContext. All fields are set to zero.
*/ */
void MD5Init (mdContext) void MD5Init (MD5_CTX *mdContext)
MD5_CTX *mdContext;
{ {
mdContext->i[0] = mdContext->i[1] = (UINT4)0; 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] account for the presence of each of the characters inBuf[0..inLen-1]
in the message whose digest is being computed. in the message whose digest is being computed.
*/ */
void MD5Update (mdContext, inBuf, inLen) void MD5Update (MD5_CTX *mdContext, unsigned const char *inBuf, unsigned int inLen)
MD5_CTX *mdContext;
unsigned const char *inBuf;
unsigned int inLen;
{ {
UINT4 in[16]; UINT4 in[16];
int mdi; int mdi;
@@ -173,8 +169,7 @@ unsigned int inLen;
ends with the desired message digest in mdContext->digest[0...15]. ends with the desired message digest in mdContext->digest[0...15].
*/ */
void MD5Final (mdContext) void MD5Final (MD5_CTX *mdContext)
MD5_CTX *mdContext;
{ {
UINT4 in[16]; UINT4 in[16];
int mdi; int mdi;
@@ -214,9 +209,7 @@ MD5_CTX *mdContext;
/* Basic MD5 step. Transforms buf based on in. /* Basic MD5 step. Transforms buf based on in.
*/ */
static void Transform (buf, in) static void Transform (UINT4 *buf, UINT4 *in)
UINT4 *buf;
UINT4 *in;
{ {
UINT4 a = buf[0], b = buf[1], c = buf[2], d = buf[3]; 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; void *r;
if (size == 0) {
Free(ptr);
return NULL;
}
r = realloc(ptr, size); r = realloc(ptr, size);
if (!r && size) if (!r)
LOG_FATAL("Could not allocate memory"); LOG_FATAL("Could not allocate memory");
return r; return r;

26
ntp.h
View File

@@ -115,9 +115,11 @@ typedef struct {
/* Non-authentication extension fields and corresponding internal flags */ /* 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 */ /* Pre-NTPv5 experimental extension field */
typedef struct { typedef struct {
@@ -126,9 +128,18 @@ typedef struct {
NTP_int32 root_dispersion; NTP_int32 root_dispersion;
NTP_int64 mono_receive_ts; NTP_int64 mono_receive_ts;
uint32_t mono_epoch; 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 */ /* Authentication extension fields */
@@ -179,4 +190,11 @@ typedef struct {
double root_dispersion; double root_dispersion;
} NTP_Sample; } 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 */ #endif /* GOT_NTP_H */

View File

@@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2021 * Copyright (C) Miroslav Lichvar 2009-2023
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -64,6 +64,17 @@ typedef enum {
MD_BURST_WAS_ONLINE, /* Burst sampling, return to online afterwards */ MD_BURST_WAS_ONLINE, /* Burst sampling, return to online afterwards */
} OperatingMode; } 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 /* Structure used for holding a single peer/server's
protocol machine */ protocol machine */
@@ -204,6 +215,9 @@ struct NCR_Instance_Record {
SPF_Instance filter; SPF_Instance filter;
int filter_count; 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_good_samples_to_go;
int burst_total_samples_to_go; int burst_total_samples_to_go;
@@ -300,6 +314,9 @@ static ARR_Instance broadcasts;
/* Maximum acceptable change in server mono<->real offset */ /* Maximum acceptable change in server mono<->real offset */
#define MAX_MONO_DOFFSET 16.0 #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 */ /* Invalid socket, different from the one in ntp_io.c */
#define INVALID_SOCK_FD -2 #define INVALID_SOCK_FD -2
@@ -324,10 +341,15 @@ static const char tss_chars[3] = {'D', 'K', 'H'};
/* Forward prototypes */ /* Forward prototypes */
static void transmit_timeout(void *arg); 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 double get_separation(int poll);
static int parse_packet(NTP_Packet *packet, int length, NTP_PacketInfo *info); static int parse_packet(NTP_Packet *packet, int length, NTP_PacketInfo *info);
static void process_sample(NCR_Instance inst, NTP_Sample *sample); 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); 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, originate_ts) == 24);
assert(offsetof(NTP_Packet, receive_ts) == 32); assert(offsetof(NTP_Packet, receive_ts) == 32);
assert(offsetof(NTP_Packet, transmit_ts) == 40); 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); UTI_ZeroTimespec(&ts->ts);
ts->err = 0.0; ts->err = 0.0;
ts->source = NTP_TS_DAEMON; 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 static void
start_initial_timeout(NCR_Instance inst) start_initial_timeout(NCR_Instance inst)
{ {
double delay, last_tx; double delay;
struct timespec now;
if (!inst->tx_timeout_id) { if (!inst->tx_timeout_id) {
/* This will be the first transmission after mode change */ /* 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 the interval between packets at least as long as the current polling
interval */ interval */
if (!UTI_IsZeroTimespec(&inst->local_tx.ts)) { if (!UTI_IsZeroTimespec(&inst->local_tx.ts)) {
SCH_GetLastEventTime(&now, NULL, NULL); delay = get_transmit_delay(inst, 0);
last_tx = UTI_DiffTimespecsToDouble(&now, &inst->local_tx.ts);
if (last_tx < 0.0)
last_tx = 0.0;
delay = get_transmit_delay(inst, 0, 0.0) - last_tx;
} else { } else {
delay = 0.0; delay = 0.0;
} }
@@ -531,6 +553,11 @@ close_client_socket(NCR_Instance inst)
SCH_RemoveTimeout(inst->rx_timeout_id); SCH_RemoveTimeout(inst->rx_timeout_id);
inst->rx_timeout_id = 0; inst->rx_timeout_id = 0;
if (has_saved_response(inst)) {
SCH_RemoveTimeout(inst->saved_response->timeout_id);
inst->saved_response->timeout_id = 0;
}
} }
/* ================================================== */ /* ================================================== */
@@ -556,6 +583,16 @@ take_offline(NCR_Instance inst)
/* ================================================== */ /* ================================================== */
static void
reset_report(NCR_Instance inst)
{
memset(&inst->report, 0, sizeof (inst->report));
inst->report.remote_addr = inst->remote_addr.ip_addr;
inst->report.remote_port = inst->remote_addr.port;
}
/* ================================================== */
NCR_Instance NCR_Instance
NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type, NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
SourceParameters *params, const char *name) SourceParameters *params, const char *name)
@@ -615,7 +652,7 @@ NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
result->auto_burst = params->burst; result->auto_burst = params->burst;
result->auto_offline = params->auto_offline; result->auto_offline = params->auto_offline;
result->copy = params->copy && result->mode == MODE_CLIENT; 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; result->ext_field_flags = params->ext_fields;
if (params->nts) { if (params->nts) {
@@ -663,6 +700,8 @@ NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
else else
result->filter = NULL; result->filter = NULL;
result->saved_response = NULL;
result->rx_timeout_id = 0; result->rx_timeout_id = 0;
result->tx_timeout_id = 0; result->tx_timeout_id = 0;
result->tx_suspended = 1; result->tx_suspended = 1;
@@ -672,12 +711,13 @@ NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
zero_local_timestamp(&result->local_tx); zero_local_timestamp(&result->local_tx);
result->burst_good_samples_to_go = 0; result->burst_good_samples_to_go = 0;
result->burst_total_samples_to_go = 0; result->burst_total_samples_to_go = 0;
memset(&result->report, 0, sizeof (result->report));
NCR_ResetInstance(result); NCR_ResetInstance(result);
set_connectivity(result, params->connectivity); set_connectivity(result, params->connectivity);
reset_report(result);
return result; return result;
} }
@@ -698,6 +738,9 @@ NCR_DestroyInstance(NCR_Instance instance)
if (instance->filter) if (instance->filter)
SPF_DestroyInstance(instance->filter); SPF_DestroyInstance(instance->filter);
if (instance->saved_response)
Free(instance->saved_response);
NAU_DestroyInstance(instance->auth); NAU_DestroyInstance(instance->auth);
/* This will destroy the source instance inside the /* This will destroy the source instance inside the
@@ -763,12 +806,14 @@ NCR_ResetInstance(NCR_Instance instance)
void void
NCR_ResetPoll(NCR_Instance instance) NCR_ResetPoll(NCR_Instance instance)
{ {
instance->poll_score = 0.0;
if (instance->local_poll != instance->minpoll) { if (instance->local_poll != instance->minpoll) {
instance->local_poll = instance->minpoll; instance->local_poll = instance->minpoll;
/* The timer was set with a longer poll interval, restart it */ /* The timer was set with a longer poll interval, restart it */
if (instance->tx_timeout_id) if (instance->tx_timeout_id)
restart_timeout(instance, get_transmit_delay(instance, 0, 0.0)); restart_timeout(instance, get_transmit_delay(instance, 0));
} }
} }
@@ -777,7 +822,6 @@ NCR_ResetPoll(NCR_Instance instance)
void void
NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr, int ntp_only) NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr, int ntp_only)
{ {
memset(&inst->report, 0, sizeof (inst->report));
NCR_ResetInstance(inst); NCR_ResetInstance(inst);
if (!ntp_only) if (!ntp_only)
@@ -794,10 +838,18 @@ NCR_ChangeRemoteAddress(NCR_Instance inst, NTP_Remote_Address *remote_addr, int
inst->local_addr.sock_fd = NIO_OpenServerSocket(remote_addr); 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 */ /* Update the reference ID and reset the source/sourcestats instances */
SRC_SetRefid(inst->source, UTI_IPToRefid(&remote_addr->ip_addr), SRC_SetRefid(inst->source, UTI_IPToRefid(&remote_addr->ip_addr),
&inst->remote_addr.ip_addr); &inst->remote_addr.ip_addr);
SRC_ResetInstance(inst->source); SRC_ResetInstance(inst->source);
reset_report(inst);
} }
/* ================================================== */ /* ================================================== */
@@ -888,10 +940,19 @@ get_transmit_poll(NCR_Instance inst)
/* ================================================== */ /* ================================================== */
static double 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; 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. /* If we're in burst mode, queue for immediate dispatch.
@@ -931,12 +992,6 @@ get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx)
last_tx / delay_time > PEER_SAMPLING_ADJ - 0.5)) last_tx / delay_time > PEER_SAMPLING_ADJ - 0.5))
delay_time *= PEER_SAMPLING_ADJ; 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; break;
default: default:
assert(0); assert(0);
@@ -954,6 +1009,12 @@ get_transmit_delay(NCR_Instance inst, int on_tx, double last_tx)
break; 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; return delay_time;
} }
@@ -992,34 +1053,64 @@ receive_timeout(void *arg)
/* ================================================== */ /* ================================================== */
static int static int
add_ext_exp1(NTP_Packet *message, NTP_PacketInfo *info, struct timespec *rx, add_ef_mono_root(NTP_Packet *message, NTP_PacketInfo *info, struct timespec *rx,
double root_delay, double root_dispersion) double root_delay, double root_dispersion)
{ {
struct timespec mono_rx; struct timespec mono_rx;
NTP_ExtFieldExp1 exp1; NTP_EFExpMonoRoot ef;
NTP_int64 ts_fuzz; NTP_int64 ts_fuzz;
memset(&exp1, 0, sizeof (exp1)); memset(&ef, 0, sizeof (ef));
exp1.magic = htonl(NTP_EF_EXP1_MAGIC); ef.magic = htonl(NTP_EF_EXP_MONO_ROOT_MAGIC);
if (info->mode != MODE_CLIENT) { if (info->mode != MODE_CLIENT) {
exp1.root_delay = UTI_DoubleToNtp32f28(root_delay); ef.root_delay = UTI_DoubleToNtp32f28(root_delay);
exp1.root_dispersion = UTI_DoubleToNtp32f28(root_dispersion); ef.root_dispersion = UTI_DoubleToNtp32f28(root_dispersion);
if (rx) if (rx)
UTI_AddDoubleToTimespec(rx, server_mono_offset, &mono_rx); UTI_AddDoubleToTimespec(rx, server_mono_offset, &mono_rx);
else else
UTI_ZeroTimespec(&mono_rx); UTI_ZeroTimespec(&mono_rx);
UTI_GetNtp64Fuzz(&ts_fuzz, message->precision); UTI_GetNtp64Fuzz(&ts_fuzz, message->precision);
UTI_TimespecToNtp64(&mono_rx, &exp1.mono_receive_ts, &ts_fuzz); UTI_TimespecToNtp64(&mono_rx, &ef.mono_receive_ts, &ts_fuzz);
exp1.mono_epoch = htonl(server_mono_epoch); 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"); DEBUG_LOG("Could not add EF");
return 0; 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; return 1;
} }
@@ -1171,9 +1262,13 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
return 0; return 0;
if (ext_field_flags) { if (ext_field_flags) {
if (ext_field_flags & NTP_EF_FLAG_EXP1) { if (ext_field_flags & NTP_EF_FLAG_EXP_MONO_ROOT) {
if (!add_ext_exp1(&message, &info, smooth_time ? NULL : &local_receive, if (!add_ef_mono_root(&message, &info, smooth_time ? NULL : &local_receive,
our_root_delay, our_root_dispersion)) 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; return 0;
} }
} }
@@ -1244,6 +1339,8 @@ transmit_packet(NTP_Mode my_mode, /* The mode this machine wants to be */
local_tx->ts = local_transmit; local_tx->ts = local_transmit;
local_tx->err = local_transmit_err; local_tx->err = local_transmit_err;
local_tx->source = NTP_TS_DAEMON; local_tx->source = NTP_TS_DAEMON;
local_tx->rx_duration = 0.0;
local_tx->net_correction = 0.0;
} }
if (local_ntp_rx) if (local_ntp_rx)
@@ -1266,6 +1363,15 @@ transmit_timeout(void *arg)
inst->tx_timeout_id = 0; 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) { switch (inst->opmode) {
case MD_BURST_WAS_ONLINE: case MD_BURST_WAS_ONLINE:
/* With online burst switch to online before last packet */ /* With online burst switch to online before last packet */
@@ -1297,11 +1403,10 @@ transmit_timeout(void *arg)
/* Prepare authentication */ /* Prepare authentication */
if (!NAU_PrepareRequestAuth(inst->auth)) { 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); 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; return;
} }
@@ -1404,7 +1509,7 @@ transmit_timeout(void *arg)
} }
/* Restart timer for this message */ /* 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 /* 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 at the time when all server replies would fail the delay test, so the
@@ -1429,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 static int
parse_packet(NTP_Packet *packet, int length, NTP_PacketInfo *info) parse_packet(NTP_Packet *packet, int length, NTP_PacketInfo *info)
{ {
@@ -1515,10 +1628,15 @@ parse_packet(NTP_Packet *packet, int length, NTP_PacketInfo *info)
case NTP_EF_NTS_AUTH_AND_EEF: case NTP_EF_NTS_AUTH_AND_EEF:
info->auth.mode = NTP_AUTH_NTS; info->auth.mode = NTP_AUTH_NTS;
break; break;
case NTP_EF_EXP1: case NTP_EF_EXP_MONO_ROOT:
if (ef_body_length == sizeof (NTP_ExtFieldExp1) && if (is_exp_ef(ef_body, ef_body_length, sizeof (NTP_EFExpMonoRoot),
ntohl(((NTP_ExtFieldExp1 *)ef_body)->magic) == NTP_EF_EXP1_MAGIC) NTP_EF_EXP_MONO_ROOT_MAGIC))
info->ext_field_flags |= NTP_EF_FLAG_EXP1; 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; break;
default: default:
DEBUG_LOG("Unknown extension field type=%x", (unsigned int)ef_type); DEBUG_LOG("Unknown extension field type=%x", (unsigned int)ef_type);
@@ -1546,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 static int
check_delay_ratio(NCR_Instance inst, SST_Stats stats, check_delay_ratio(NCR_Instance inst, SST_Stats stats,
struct timespec *sample_time, double delay) struct timespec *sample_time, double delay)
@@ -1717,7 +1882,69 @@ process_sample(NCR_Instance inst, NTP_Sample *sample)
/* ================================================== */ /* ================================================== */
static int 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_Local_Timestamp *rx_ts, NTP_Packet *message, NTP_PacketInfo *info)
{ {
NTP_Sample sample; NTP_Sample sample;
@@ -1745,18 +1972,20 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
/* Extension fields */ /* Extension fields */
int parsed, ef_length, ef_type, ef_body_length; int parsed, ef_length, ef_type, ef_body_length;
void *ef_body; void *ef_body;
NTP_ExtFieldExp1 *ef_exp1; NTP_EFExpMonoRoot *ef_mono_root;
NTP_EFExpNetCorrection *ef_net_correction;
NTP_Local_Timestamp local_receive, local_transmit; NTP_Local_Timestamp local_receive, local_transmit;
double remote_interval, local_interval, response_time; double remote_interval, local_interval, response_time;
double delay_time, precision, mono_doffset; double delay_time, precision, mono_doffset, net_correction;
int updated_timestamps; int updated_timestamps;
/* ==================== */ /* ==================== */
stats = SRC_GetSourcestats(inst->source); stats = SRC_GetSourcestats(inst->source);
ef_exp1 = NULL; ef_mono_root = NULL;
ef_net_correction = NULL;
/* Find requested non-authentication extension fields */ /* Find requested non-authentication extension fields */
if (inst->ext_field_flags & info->ext_field_flags) { if (inst->ext_field_flags & info->ext_field_flags) {
@@ -1766,11 +1995,17 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
break; break;
switch (ef_type) { switch (ef_type) {
case NTP_EF_EXP1: case NTP_EF_EXP_MONO_ROOT:
if (inst->ext_field_flags & NTP_EF_FLAG_EXP1 && if (inst->ext_field_flags & NTP_EF_FLAG_EXP_MONO_ROOT &&
ef_body_length == sizeof (*ef_exp1) && is_exp_ef(ef_body, ef_body_length, sizeof (*ef_mono_root),
ntohl(((NTP_ExtFieldExp1 *)ef_body)->magic) == NTP_EF_EXP1_MAGIC) NTP_EF_EXP_MONO_ROOT_MAGIC))
ef_exp1 = ef_body; 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; break;
} }
} }
@@ -1779,9 +2014,9 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
pkt_leap = NTP_LVM_TO_LEAP(message->lvm); pkt_leap = NTP_LVM_TO_LEAP(message->lvm);
pkt_version = NTP_LVM_TO_VERSION(message->lvm); pkt_version = NTP_LVM_TO_VERSION(message->lvm);
pkt_refid = ntohl(message->reference_id); pkt_refid = ntohl(message->reference_id);
if (ef_exp1) { if (ef_mono_root) {
pkt_root_delay = UTI_Ntp32f28ToDouble(ef_exp1->root_delay); pkt_root_delay = UTI_Ntp32f28ToDouble(ef_mono_root->root_delay);
pkt_root_dispersion = UTI_Ntp32f28ToDouble(ef_exp1->root_dispersion); pkt_root_dispersion = UTI_Ntp32f28ToDouble(ef_mono_root->root_dispersion);
} else { } else {
pkt_root_delay = UTI_Ntp32ToDouble(message->root_delay); pkt_root_delay = UTI_Ntp32ToDouble(message->root_delay);
pkt_root_dispersion = UTI_Ntp32ToDouble(message->root_dispersion); pkt_root_dispersion = UTI_Ntp32ToDouble(message->root_dispersion);
@@ -1811,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 /* Test 4 would check for denied access. It would always pass as this
function is called only for known sources. */ function is called only for known sources. */
/* Test 5 checks for authentication failure */ /* Test 5 checks for authentication failure. If it is a saved message,
test5 = NAU_CheckResponseAuth(inst->auth, message, info); 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 */ /* Test 6 checks for unsynchronised server */
test6 = pkt_leap != LEAP_Unsynchronised && test6 = pkt_leap != LEAP_Unsynchronised &&
@@ -1828,6 +2065,20 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
valid_packet = test1 && test2 && test3 && test5; valid_packet = test1 && test2 && test3 && test5;
synced_packet = valid_packet && test6 && test7; 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 */ /* Check for Kiss-o'-Death codes */
kod_rate = 0; kod_rate = 0;
if (test1 && test2 && test5 && pkt_leap == LEAP_Unsynchronised && if (test1 && test2 && test5 && pkt_leap == LEAP_Unsynchronised &&
@@ -1850,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 the new sample. In the interleaved mode, cancel the correction out in
remote timestamps of the previous request and response, which were remote timestamps of the previous request and response, which were
captured before the source accumulated the new time corrections. */ captured before the source accumulated the new time corrections. */
if (ef_exp1 && inst->remote_mono_epoch == ntohl(ef_exp1->mono_epoch) && if (ef_mono_root && inst->remote_mono_epoch == ntohl(ef_mono_root->mono_epoch) &&
!UTI_IsZeroNtp64(&ef_exp1->mono_receive_ts) && !UTI_IsZeroNtp64(&ef_mono_root->mono_receive_ts) &&
!UTI_IsZeroNtp64(&inst->remote_ntp_monorx)) { !UTI_IsZeroNtp64(&inst->remote_ntp_monorx)) {
mono_doffset = 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); UTI_DiffNtp64ToDouble(&message->receive_ts, &inst->remote_ntp_rx);
if (fabs(mono_doffset) > MAX_MONO_DOFFSET) if (fabs(mono_doffset) > MAX_MONO_DOFFSET)
mono_doffset = 0.0; mono_doffset = 0.0;
@@ -1862,6 +2113,12 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
mono_doffset = 0.0; 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 */ /* Select remote and local timestamps for the new sample */
if (interleaved_packet) { if (interleaved_packet) {
/* Prefer previous local TX and remote RX timestamps if it will make /* Prefer previous local TX and remote RX timestamps if it will make
@@ -1881,6 +2138,7 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
UTI_Ntp64ToTimespec(&message->receive_ts, &remote_receive); UTI_Ntp64ToTimespec(&message->receive_ts, &remote_receive);
UTI_Ntp64ToTimespec(&inst->remote_ntp_rx, &remote_request_receive); UTI_Ntp64ToTimespec(&inst->remote_ntp_rx, &remote_request_receive);
local_transmit = inst->local_tx; local_transmit = inst->local_tx;
local_transmit.net_correction = net_correction;
root_delay = MAX(pkt_root_delay, inst->remote_root_delay); root_delay = MAX(pkt_root_delay, inst->remote_root_delay);
root_dispersion = MAX(pkt_root_dispersion, inst->remote_root_dispersion); root_dispersion = MAX(pkt_root_dispersion, inst->remote_root_dispersion);
} }
@@ -1895,6 +2153,7 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
remote_request_receive = remote_receive; remote_request_receive = remote_receive;
local_receive = *rx_ts; local_receive = *rx_ts;
local_transmit = inst->local_tx; local_transmit = inst->local_tx;
local_transmit.net_correction = net_correction;
root_delay = pkt_root_delay; root_delay = pkt_root_delay;
root_dispersion = pkt_root_dispersion; root_dispersion = pkt_root_dispersion;
} }
@@ -1938,6 +2197,9 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
skew * fabs(local_interval); skew * fabs(local_interval);
sample.root_delay = root_delay + sample.peer_delay; sample.root_delay = root_delay + sample.peer_delay;
sample.root_dispersion = root_dispersion + sample.peer_dispersion; 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 /* If the source is an active peer, this is the minimum assumed interval
between previous two transmissions (if not constrained by minpoll) */ between previous two transmissions (if not constrained by minpoll) */
@@ -1946,8 +2208,10 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
/* Additional tests required to pass before accumulating the sample */ /* Additional tests required to pass before accumulating the sample */
/* Test A requires that the minimum estimate of the peer delay is not /* Test A combines multiple tests to avoid changing the measurements log
larger than the configured maximum, in both client modes that the server 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, in both client modes that the server
processing time is sane, in interleaved client/server mode that the processing time is sane, in interleaved client/server mode that the
previous response was not in basic mode (which prevents using timestamps previous response was not in basic mode (which prevents using timestamps
that minimise delay error), and in interleaved symmetric mode that the that minimise delay error), and in interleaved symmetric mode that the
@@ -1955,6 +2219,7 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
a missed response */ a missed response */
testA = sample.peer_delay - sample.peer_dispersion <= inst->max_delay && testA = sample.peer_delay - sample.peer_dispersion <= inst->max_delay &&
precision <= inst->max_delay && precision <= inst->max_delay &&
inst->presend_done <= 0 &&
!(inst->mode == MODE_CLIENT && response_time > MAX_SERVER_INTERVAL) && !(inst->mode == MODE_CLIENT && response_time > MAX_SERVER_INTERVAL) &&
!(inst->mode == MODE_CLIENT && interleaved_packet && !(inst->mode == MODE_CLIENT && interleaved_packet &&
UTI_IsZeroTimespec(&inst->prev_local_tx.ts) && UTI_IsZeroTimespec(&inst->prev_local_tx.ts) &&
@@ -1993,6 +2258,7 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
sample.root_delay = sample.root_dispersion = 0.0; sample.root_delay = sample.root_dispersion = 0.0;
sample.time = rx_ts->ts; sample.time = rx_ts->ts;
mono_doffset = 0.0; mono_doffset = 0.0;
net_correction = 0.0;
local_receive = *rx_ts; local_receive = *rx_ts;
local_transmit = inst->local_tx; local_transmit = inst->local_tx;
testA = testB = testC = testD = 0; testA = testB = testC = testD = 0;
@@ -2026,9 +2292,9 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
/* If available, update the monotonic timestamp and accumulate the offset. /* 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 This needs to be done here to not lose changes in remote_ntp_rx in
symmetric mode when there are multiple responses per request. */ symmetric mode when there are multiple responses per request. */
if (ef_exp1 && !UTI_IsZeroNtp64(&ef_exp1->mono_receive_ts)) { if (ef_mono_root && !UTI_IsZeroNtp64(&ef_mono_root->mono_receive_ts)) {
inst->remote_mono_epoch = ntohl(ef_exp1->mono_epoch); inst->remote_mono_epoch = ntohl(ef_mono_root->mono_epoch);
inst->remote_ntp_monorx = ef_exp1->mono_receive_ts; inst->remote_ntp_monorx = ef_mono_root->mono_receive_ts;
inst->mono_doffset += mono_doffset; inst->mono_doffset += mono_doffset;
} else { } else {
inst->remote_mono_epoch = 0; inst->remote_mono_epoch = 0;
@@ -2036,8 +2302,11 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
inst->mono_doffset = 0.0; inst->mono_doffset = 0.0;
} }
/* Don't use the same set of timestamps for the next sample */ inst->local_tx.net_correction = net_correction;
if (interleaved_packet)
/* 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; inst->prev_local_tx = inst->local_tx;
else else
zero_local_timestamp(&inst->prev_local_tx); zero_local_timestamp(&inst->prev_local_tx);
@@ -2056,15 +2325,11 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
/* Accept at most one response per request. The NTP specification recommends /* Accept at most one response per request. The NTP specification recommends
resetting local_ntp_tx to make the following packets fail test2 or test3, 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 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) { if (inst->valid_rx) {
test2 = test3 = 0; test2 = test3 = 0;
valid_packet = synced_packet = good_packet = 0; valid_packet = synced_packet = good_packet = 0;
} else if (valid_packet) { } else if (valid_packet) {
if (inst->presend_done) {
testA = 0;
good_packet = 0;
}
inst->valid_rx = 1; inst->valid_rx = 1;
} }
@@ -2158,8 +2423,7 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
/* And now, requeue the timer */ /* And now, requeue the timer */
if (inst->opmode != MD_OFFLINE) { if (inst->opmode != MD_OFFLINE) {
delay_time = get_transmit_delay(inst, 0, delay_time = get_transmit_delay(inst, 0);
UTI_DiffTimespecsToDouble(&inst->local_rx.ts, &inst->local_tx.ts));
if (kod_rate) { if (kod_rate) {
LOG(LOGS_WARN, "Received KoD RATE from %s", LOG(LOGS_WARN, "Received KoD RATE from %s",
@@ -2174,14 +2438,13 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
} }
/* Get rid of old timeout and start a new one */ /* 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); restart_timeout(inst, delay_time);
} }
/* Update the NTP report */ /* Update the NTP report */
inst->report.remote_addr = inst->remote_addr.ip_addr;
inst->report.local_addr = inst->local_addr.ip_addr; inst->report.local_addr = inst->local_addr.ip_addr;
inst->report.remote_port = inst->remote_addr.port;
inst->report.leap = pkt_leap; inst->report.leap = pkt_leap;
inst->report.version = pkt_version; inst->report.version = pkt_version;
inst->report.mode = NTP_LVM_TO_MODE(message->lvm); inst->report.mode = NTP_LVM_TO_MODE(message->lvm);
@@ -2206,6 +2469,8 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
inst->report.rx_tss_char = tss_chars[local_receive.source]; inst->report.rx_tss_char = tss_chars[local_receive.source];
inst->report.total_valid_count++; inst->report.total_valid_count++;
if (good_packet)
inst->report.total_good_count++;
} }
/* Do measurement logging */ /* Do measurement logging */
@@ -2319,8 +2584,8 @@ NCR_ProcessRxKnown(NCR_Instance inst, NTP_Local_Address *local_addr,
client mode operation. client mode operation.
This copes with the case for an isolated network where one This copes with the case for an isolated network where one
machine is set by eye and is used as the master, with the machine is set by eye and is used as the primary server, with
other machines pointed at it. If the master goes down, we 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 want to be able to reset its time at startup by relying on
one of the secondaries to flywheel it. The behaviour coded here one of the secondaries to flywheel it. The behaviour coded here
is required in the secondaries to make this possible. */ is required in the secondaries to make this possible. */
@@ -2363,7 +2628,7 @@ NCR_ProcessRxKnown(NCR_Instance inst, NTP_Local_Address *local_addr,
return 0; 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) { } else if (proc_as_unknown) {
NCR_ProcessRxUnknown(&inst->remote_addr, local_addr, rx_ts, message, length); NCR_ProcessRxUnknown(&inst->remote_addr, local_addr, rx_ts, message, length);
/* It's not a reply to our request, don't return success */ /* It's not a reply to our request, don't return success */
@@ -2444,8 +2709,6 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
/* Don't respond unless a non-zero KoD was returned */ /* Don't respond unless a non-zero KoD was returned */
if (kod == 0) if (kod == 0)
return; return;
} else if (info.auth.mode != NTP_AUTH_NONE && info.auth.mode != NTP_AUTH_MSSNTP) {
CLG_LogAuthNtpRequest();
} }
local_ntp_rx = NULL; local_ntp_rx = NULL;
@@ -2465,14 +2728,18 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
UTI_CompareNtp64(&message->receive_ts, &message->transmit_ts) != 0) { UTI_CompareNtp64(&message->receive_ts, &message->transmit_ts) != 0) {
ntp_rx = message->originate_ts; ntp_rx = message->originate_ts;
local_ntp_rx = &ntp_rx; local_ntp_rx = &ntp_rx;
UTI_ZeroTimespec(&local_tx.ts); zero_local_timestamp(&local_tx);
interleaved = CLG_GetNtpTxTimestamp(&ntp_rx, &local_tx.ts); interleaved = CLG_GetNtpTxTimestamp(&ntp_rx, &local_tx.ts, &local_tx.source);
tx_ts = &local_tx; tx_ts = &local_tx;
if (interleaved) if (interleaved)
CLG_DisableNtpTimestamps(&ntp_rx); 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 /* Suggest the client to increase its polling interval if it indicates
the interval is shorter than the rate limiting interval */ the interval is shorter than the rate limiting interval */
poll = CLG_GetNtpMinPoll(); poll = CLG_GetNtpMinPoll();
@@ -2489,7 +2756,7 @@ NCR_ProcessRxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
return; return;
if (local_ntp_rx) 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);
} }
/* ================================================== */ /* ================================================== */
@@ -2543,6 +2810,11 @@ 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, update_tx_timestamp(&inst->local_tx, tx_ts, &inst->local_ntp_rx, &inst->local_ntp_tx,
message); message);
if (tx_ts->source == NTP_TS_HARDWARE) {
if (has_saved_response(inst))
process_saved_response(inst);
}
} }
/* ================================================== */ /* ================================================== */
@@ -2567,7 +2839,7 @@ NCR_ProcessTxUnknown(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_a
local_ntp_rx = &message->receive_ts; local_ntp_rx = &message->receive_ts;
new_tx = *tx_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; return;
/* Undo a clock adjustment between the RX and TX timestamps to minimise error /* Undo a clock adjustment between the RX and TX timestamps to minimise error
@@ -2576,7 +2848,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); 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);
} }
/* ================================================== */ /* ================================================== */
@@ -2599,6 +2871,10 @@ NCR_SlewTimes(NCR_Instance inst, struct timespec *when, double dfreq, double dof
if (inst->filter) if (inst->filter)
SPF_SlewSamples(inst->filter, when, dfreq, doffset); 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);
} }
/* ================================================== */ /* ================================================== */
@@ -2744,7 +3020,7 @@ NCR_ModifyMinstratum(NCR_Instance inst, int new_min_stratum)
void void
NCR_ModifyPolltarget(NCR_Instance inst, int new_poll_target) 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", LOG(LOGS_INFO, "Source %s new polltarget %d",
UTI_IPToString(&inst->remote_addr.ip_addr), new_poll_target); UTI_IPToString(&inst->remote_addr.ip_addr), new_poll_target);
} }
@@ -2844,6 +3120,10 @@ NCR_AddAccessRestriction(IPAddr *ip_addr, int subnet_bits, int allow, int all)
if (status != ADF_SUCCESS) if (status != ADF_SUCCESS)
return 0; 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 */ /* Keep server sockets open only when an address allowed */
if (allow) { if (allow) {
NTP_Remote_Address remote_addr; NTP_Remote_Address remote_addr;

View File

@@ -38,16 +38,12 @@ typedef enum {
NTP_SERVER, NTP_PEER NTP_SERVER, NTP_PEER
} NTP_Source_Type; } NTP_Source_Type;
typedef enum {
NTP_TS_DAEMON = 0,
NTP_TS_KERNEL,
NTP_TS_HARDWARE
} NTP_Timestamp_Source;
typedef struct { typedef struct {
struct timespec ts; struct timespec ts;
double err; double err;
NTP_Timestamp_Source source; NTP_Timestamp_Source source;
double rx_duration;
double net_correction;
} NTP_Local_Timestamp; } NTP_Local_Timestamp;
/* This is a private data type used for storing the instance record for /* This is a private data type used for storing the instance record for

View File

@@ -126,8 +126,14 @@ open_socket(int family, int local_port, int client_only, IPSockAddr *remote_addr
dscp = CNF_GetNtpDscp(); dscp = CNF_GetNtpDscp();
if (dscp > 0 && dscp < 64) { if (dscp > 0 && dscp < 64) {
#ifdef IP_TOS #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 #endif
} }
@@ -163,9 +169,6 @@ close_socket(int sock_fd)
if (sock_fd == INVALID_SOCK_FD) if (sock_fd == INVALID_SOCK_FD)
return; return;
#ifdef HAVE_LINUX_TIMESTAMPING
NIO_Linux_NotifySocketClosing(sock_fd);
#endif
SCH_RemoveFileHandler(sock_fd); SCH_RemoveFileHandler(sock_fd);
SCK_CloseSocket(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 int
NIO_OpenClientSocket(NTP_Remote_Address *remote_addr) 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); SCH_GetLastEventTime(&local_ts.ts, &local_ts.err, NULL);
local_ts.source = NTP_TS_DAEMON; local_ts.source = NTP_TS_DAEMON;
local_ts.rx_duration = 0.0;
local_ts.net_correction = 0.0;
sched_ts = local_ts.ts; sched_ts = local_ts.ts;
if (message->addr_type != SCK_ADDR_IP) { 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", DEBUG_LOG("Updated RX timestamp delay=%.9f tss=%u",
UTI_DiffTimespecsToDouble(&sched_ts, &local_ts.ts), local_ts.source); UTI_DiffTimespecsToDouble(&sched_ts, &local_ts.ts), local_ts.source);
if (!NIO_UnwrapMessage(message, sock_fd)) if (!NIO_UnwrapMessage(message, sock_fd, &local_ts.net_correction))
return; return;
/* Just ignore the packet if it's not of a recognized length */ /* Just ignore the packet if it's not of a recognized length */
@@ -461,11 +479,6 @@ read_from_socket(int sock_fd, int event, void *anything)
SCK_Message *messages; SCK_Message *messages;
int i, received, flags = 0; int i, received, flags = 0;
#ifdef HAVE_LINUX_TIMESTAMPING
if (NIO_Linux_ProcessEvent(sock_fd, event))
return;
#endif
if (event == SCH_FILE_EXCEPTION) { if (event == SCH_FILE_EXCEPTION) {
#ifdef HAVE_LINUX_TIMESTAMPING #ifdef HAVE_LINUX_TIMESTAMPING
flags |= SCK_FLAG_MSG_ERRQUEUE; flags |= SCK_FLAG_MSG_ERRQUEUE;
@@ -485,8 +498,9 @@ read_from_socket(int sock_fd, int event, void *anything)
/* ================================================== */ /* ================================================== */
int 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; PTP_NtpMessage *msg;
if (!is_ptp_socket(sock_fd)) if (!is_ptp_socket(sock_fd))
@@ -512,7 +526,14 @@ NIO_UnwrapMessage(SCK_Message *message, int sock_fd)
message->data = (char *)message->data + PTP_NTP_PREFIX_LENGTH; message->data = (char *)message->data + PTP_NTP_PREFIX_LENGTH;
message->length -= 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; return 1;
} }
@@ -522,6 +543,8 @@ NIO_UnwrapMessage(SCK_Message *message, int sock_fd)
static int static int
wrap_message(SCK_Message *message, int sock_fd) wrap_message(SCK_Message *message, int sock_fd)
{ {
static uint16_t sequence_id = 0;
assert(PTP_NTP_PREFIX_LENGTH == 48); assert(PTP_NTP_PREFIX_LENGTH == 48);
if (!is_ptp_socket(sock_fd)) if (!is_ptp_socket(sock_fd))
@@ -542,6 +565,7 @@ wrap_message(SCK_Message *message, int sock_fd)
ptp_message->header.length = htons(PTP_NTP_PREFIX_LENGTH + message->length); ptp_message->header.length = htons(PTP_NTP_PREFIX_LENGTH + message->length);
ptp_message->header.domain = PTP_DOMAIN_NTP; ptp_message->header.domain = PTP_DOMAIN_NTP;
ptp_message->header.flags = htons(PTP_FLAG_UNICAST); 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.type = htons(PTP_TLV_NTP);
ptp_message->tlv_header.length = htons(message->length); ptp_message->tlv_header.length = htons(message->length);
memcpy((char *)ptp_message + PTP_NTP_PREFIX_LENGTH, message->data, 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 */ /* Function to finalise the module */
extern void NIO_Finalise(void); 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 */ /* Function to obtain a socket for sending client packets */
extern int NIO_OpenClientSocket(NTP_Remote_Address *remote_addr); 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); extern int NIO_IsServerConnectable(NTP_Remote_Address *remote_addr);
/* Function to unwrap an NTP message from non-native transport (e.g. PTP) */ /* 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 */ /* Function to transmit a packet */
extern int NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, extern int NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate. chronyd/chronyc - Programs for keeping computer clocks accurate.
********************************************************************** **********************************************************************
* Copyright (C) Miroslav Lichvar 2016-2019 * Copyright (C) Miroslav Lichvar 2016-2019, 2021-2023
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -39,6 +39,7 @@
#include "hwclock.h" #include "hwclock.h"
#include "local.h" #include "local.h"
#include "logging.h" #include "logging.h"
#include "memory.h"
#include "ntp_core.h" #include "ntp_core.h"
#include "ntp_io.h" #include "ntp_io.h"
#include "ntp_io_linux.h" #include "ntp_io_linux.h"
@@ -63,13 +64,16 @@ struct Interface {
double tx_comp; double tx_comp;
double rx_comp; double rx_comp;
HCL_Instance clock; HCL_Instance clock;
int maxpoll;
SCH_TimeoutID poll_timeout_id;
}; };
/* Number of PHC readings per HW clock sample */ /* Number of PHC readings per HW clock sample */
#define PHC_READINGS 25 #define PHC_READINGS 25
/* Minimum interval between PHC readings */ /* Minimum and maximum interval between PHC readings */
#define MIN_PHC_POLL -6 #define MIN_PHC_POLL -6
#define MAX_PHC_POLL 20
/* Maximum acceptable offset between SW/HW and daemon timestamp */ /* Maximum acceptable offset between SW/HW and daemon timestamp */
#define MAX_TS_DELAY 1.0 #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 */ /* Flag indicating the socket options can't be changed in control messages */
static int permanent_ts_options; static int permanent_ts_options;
/* When sending client requests to a close and fast server, it is possible that
a response will be received before the HW transmit timestamp of the request
itself. To avoid processing of the response without the HW timestamp, we
monitor events returned by select() and suspend reading of packets from the
receive queue for up to 200 microseconds. As the requests are normally
separated by at least 200 milliseconds, it is sufficient to monitor and
suspend one socket at a time. */
static int monitored_socket;
static int suspended_socket;
static SCH_TimeoutID resume_timeout_id;
#define RESUME_TIMEOUT 200.0e-6
/* Unbound socket keeping the kernel RX timestamping permanently enabled /* Unbound socket keeping the kernel RX timestamping permanently enabled
in order to avoid a race condition between receiving a server response in order to avoid a race condition between receiving a server response
and the kernel actually starting to timestamp received packets after 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 static int
add_interface(CNF_HwTsInterface *conf_iface) 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 ethtool_ts_info ts_info;
struct hwtstamp_config ts_config; struct hwtstamp_config ts_config;
struct ifreq req; struct ifreq req;
int sock_fd, if_index, phc_fd, req_hwts_flags, rx_filter;
unsigned int i; unsigned int i;
struct Interface *iface; struct Interface *iface;
@@ -245,9 +240,15 @@ add_interface(CNF_HwTsInterface *conf_iface)
iface->tx_comp = conf_iface->tx_comp; iface->tx_comp = conf_iface->tx_comp;
iface->rx_comp = conf_iface->rx_comp; iface->rx_comp = conf_iface->rx_comp;
minpoll = CLAMP(MIN_PHC_POLL, conf_iface->minpoll, MAX_PHC_POLL);
iface->clock = HCL_CreateInstance(conf_iface->min_samples, conf_iface->max_samples, iface->clock = HCL_CreateInstance(conf_iface->min_samples, conf_iface->max_samples,
UTI_Log2ToDouble(MAX(conf_iface->minpoll, MIN_PHC_POLL)), UTI_Log2ToDouble(minpoll), conf_iface->precision);
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", LOG(LOGS_INFO, "Enabled HW timestamping %son %s",
ts_config.rx_filter == HWTSTAMP_FILTER_NONE ? "(TX only) " : "", iface->name); ts_config.rx_filter == HWTSTAMP_FILTER_NONE ? "(TX only) " : "", iface->name);
@@ -412,8 +413,6 @@ NIO_Linux_Initialise(void)
/* Kernels before 4.7 ignore timestamping flags set in control messages */ /* Kernels before 4.7 ignore timestamping flags set in control messages */
permanent_ts_options = !SYS_Linux_CheckKernelVersion(4, 7); permanent_ts_options = !SYS_Linux_CheckKernelVersion(4, 7);
monitored_socket = INVALID_SOCK_FD;
suspended_socket = INVALID_SOCK_FD;
dummy_rxts_socket = INVALID_SOCK_FD; dummy_rxts_socket = INVALID_SOCK_FD;
} }
@@ -430,6 +429,7 @@ NIO_Linux_Finalise(void)
for (i = 0; i < ARR_GetSize(interfaces); i++) { for (i = 0; i < ARR_GetSize(interfaces); i++) {
iface = ARR_GetElement(interfaces, i); iface = ARR_GetElement(interfaces, i);
SCH_RemoveTimeout(iface->poll_timeout_id);
HCL_DestroyInstance(iface->clock); HCL_DestroyInstance(iface->clock);
close(iface->phc_fd); close(iface->phc_fd);
} }
@@ -439,6 +439,14 @@ NIO_Linux_Finalise(void)
/* ================================================== */ /* ================================================== */
int
NIO_Linux_IsHwTsEnabled(void)
{
return ARR_GetSize(interfaces) > 0;
}
/* ================================================== */
int int
NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events) 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 * static struct Interface *
get_interface(int if_index) 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 static void
process_hw_timestamp(struct Interface *iface, struct timespec *hw_ts, process_hw_timestamp(struct Interface *iface, struct timespec *hw_ts,
NTP_Local_Timestamp *local_ts, int rx_ntp_length, int family, NTP_Local_Timestamp *local_ts, int rx_ntp_length, int family,
int l2_length) int l2_length)
{ {
struct timespec sample_phc_ts, sample_sys_ts, sample_local_ts, ts; double rx_correction = 0.0, ts_delay, local_err;
struct timespec phc_readings[PHC_READINGS][3]; struct timespec ts;
double rx_correction, ts_delay, phc_err, local_err;
int n_readings;
if (HCL_NeedsNewSample(iface->clock, &local_ts->ts)) { poll_phc(iface, &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);
}
}
/* We need to transpose RX timestamps as hardware timestamps are normally /* We need to transpose RX timestamps as hardware timestamps are normally
preamble timestamps and RX timestamps in NTP are supposed to be trailer 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->ts = ts;
local_ts->err = local_err; local_ts->err = local_err;
local_ts->source = NTP_TS_HARDWARE; 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; struct Interface *iface;
int is_tx, ts_if_index, l2_length; int is_tx, ts_if_index, l2_length;
double c = 0.0;
is_tx = event == SCH_FILE_EXCEPTION; is_tx = event == SCH_FILE_EXCEPTION;
iface = NULL; iface = NULL;
@@ -758,11 +745,6 @@ NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
} else { } else {
DEBUG_LOG("HW clock not found for interface %d", ts_if_index); DEBUG_LOG("HW clock not found for interface %d", ts_if_index);
} }
/* If a HW transmit timestamp was received, resume processing
of non-error messages on this socket */
if (is_tx)
resume_socket(local_addr->sock_fd);
} }
if (local_ts->source == NTP_TS_DAEMON && !UTI_IsZeroTimespec(&message->timestamp.kernel) && 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; return 1;
} }
if (!NIO_UnwrapMessage(message, local_addr->sock_fd)) if (!NIO_UnwrapMessage(message, local_addr->sock_fd, &c))
return 1; return 1;
if (message->length < NTP_HEADER_LENGTH || message->length > sizeof (NTP_Packet)) 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) if (!ts_flags)
return; 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 */ /* Check if TX timestamping is disabled on this socket */
if (permanent_ts_options || !NIO_IsServerSocket(sock_fd)) if (permanent_ts_options || !NIO_IsServerSocket(sock_fd))
return; return;
message->timestamp.tx_flags = ts_tx_flags; 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 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, extern int NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *local_ts, int event); NTP_Local_Timestamp *local_ts, int event);
extern void NIO_Linux_RequestTxTimestamp(SCK_Message *message, int sock_fd); extern void NIO_Linux_RequestTxTimestamp(SCK_Message *message, int sock_fd);
extern void NIO_Linux_NotifySocketClosing(int sock_fd);
#endif #endif

View File

@@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011-2012, 2014, 2016, 2020-2021 * Copyright (C) Miroslav Lichvar 2011-2012, 2014, 2016, 2020-2023
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -32,6 +32,7 @@
#include "sysincl.h" #include "sysincl.h"
#include "array.h" #include "array.h"
#include "conf.h"
#include "ntp_sources.h" #include "ntp_sources.h"
#include "ntp_core.h" #include "ntp_core.h"
#include "ntp_io.h" #include "ntp_io.h"
@@ -58,12 +59,15 @@ typedef struct {
NCR_Instance data; /* Data for the protocol engine for this source */ NCR_Instance data; /* Data for the protocol engine for this source */
char *name; /* Name of the source as it was specified char *name; /* Name of the source as it was specified
(may be an IP address) */ (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 pool_id; /* ID of the pool from which was this source int pool_id; /* ID of the pool from which was this source
added or INVALID_POOL */ added or INVALID_POOL */
int tentative; /* Flag indicating there was no valid response int tentative; /* Flag indicating there was no valid response
received from the source yet */ received from the source yet */
uint32_t conf_id; /* Configuration ID, which can be shared with uint32_t conf_id; /* Configuration ID, which can be shared with
different sources in case of a pool */ different sources in case of a pool */
double last_resolving; /* Time of last name resolving (monotonic) */
} SourceRecord; } SourceRecord;
/* Hash table of SourceRecord, its size is a power of two and it's never /* Hash table of SourceRecord, its size is a power of two and it's never
@@ -96,6 +100,9 @@ struct UnresolvedSource {
char *name; char *name;
/* Flag indicating addresses should be used in a random order */ /* Flag indicating addresses should be used in a random order */
int 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 */ /* Next unresolved source in the list */
struct UnresolvedSource *next; struct UnresolvedSource *next;
}; };
@@ -103,7 +110,7 @@ struct UnresolvedSource {
#define RESOLVE_INTERVAL_UNIT 7 #define RESOLVE_INTERVAL_UNIT 7
#define MIN_RESOLVE_INTERVAL 2 #define MIN_RESOLVE_INTERVAL 2
#define MAX_RESOLVE_INTERVAL 9 #define MAX_RESOLVE_INTERVAL 9
#define MIN_REPLACEMENT_INTERVAL 8 #define MAX_REPLACEMENT_INTERVAL 9
static struct UnresolvedSource *unresolved_sources = NULL; static struct UnresolvedSource *unresolved_sources = NULL;
static int resolving_interval = 0; static int resolving_interval = 0;
@@ -184,6 +191,7 @@ void
NSR_Initialise(void) NSR_Initialise(void)
{ {
n_sources = 0; n_sources = 0;
resolving_id = 0;
initialised = 1; initialised = 1;
records = ARR_CreateInstance(sizeof (SourceRecord)); records = ARR_CreateInstance(sizeof (SourceRecord));
@@ -206,6 +214,7 @@ NSR_Finalise(void)
ARR_DestroyInstance(records); ARR_DestroyInstance(records);
ARR_DestroyInstance(pools); ARR_DestroyInstance(pools);
SCH_RemoveTimeout(resolving_id);
while (unresolved_sources) while (unresolved_sources)
remove_unresolved_source(unresolved_sources); remove_unresolved_source(unresolved_sources);
@@ -317,6 +326,31 @@ 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 */ /* Procedure to add a new source */
static NSR_Status 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, NTP_Source_Type type,
@@ -356,9 +390,11 @@ 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->name = Strdup(name ? name : UTI_IPToString(&remote_addr->ip_addr));
record->data = NCR_CreateInstance(remote_addr, type, params, record->name); record->data = NCR_CreateInstance(remote_addr, type, params, record->name);
record->remote_addr = NCR_GetRemoteAddress(record->data); record->remote_addr = NCR_GetRemoteAddress(record->data);
record->resolved_addr = remote_addr->ip_addr;
record->pool_id = pool_id; record->pool_id = pool_id;
record->tentative = 1; record->tentative = 1;
record->conf_id = conf_id; record->conf_id = conf_id;
record->last_resolving = SCH_GetLastEventMonoTime();
record_lock = 0; record_lock = 0;
@@ -371,6 +407,8 @@ add_source(NTP_Remote_Address *remote_addr, char *name, NTP_Source_Type type,
if (auto_start_sources && UTI_IsIPReal(&remote_addr->ip_addr)) if (auto_start_sources && UTI_IsIPReal(&remote_addr->ip_addr))
NCR_StartInstance(record->data); NCR_StartInstance(record->data);
log_source(record, 1, 1);
/* The new instance is allowed to change its address immediately */ /* The new instance is allowed to change its address immediately */
handle_saved_address_update(); handle_saved_address_update();
@@ -405,6 +443,8 @@ change_source_address(NTP_Remote_Address *old_addr, NTP_Remote_Address *new_addr
record = get_record(slot1); record = get_record(slot1);
NCR_ChangeRemoteAddress(record->data, new_addr, !replacement); NCR_ChangeRemoteAddress(record->data, new_addr, !replacement);
if (replacement)
record->resolved_addr = new_addr->ip_addr;
if (record->remote_addr != NCR_GetRemoteAddress(record->data) || if (record->remote_addr != NCR_GetRemoteAddress(record->data) ||
UTI_CompareIPs(&record->remote_addr->ip_addr, &new_addr->ip_addr, NULL) != 0) UTI_CompareIPs(&record->remote_addr->ip_addr, &new_addr->ip_addr, NULL) != 0)
@@ -488,7 +528,21 @@ process_resolved_name(struct UnresolvedSource *us, IPAddr *ip_addrs, int n_addrs
NTP_Remote_Address old_addr, new_addr; NTP_Remote_Address old_addr, new_addr;
SourceRecord *record; SourceRecord *record;
unsigned short first = 0; 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) if (us->random_order)
UTI_GetRandomBytes(&first, sizeof (first)); UTI_GetRandomBytes(&first, sizeof (first));
@@ -746,6 +800,7 @@ NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
us = MallocNew(struct UnresolvedSource); us = MallocNew(struct UnresolvedSource);
us->name = Strdup(name); us->name = Strdup(name);
us->random_order = 0; us->random_order = 0;
us->refreshment = 0;
remote_addr.ip_addr.family = IPADDR_ID; remote_addr.ip_addr.family = IPADDR_ID;
remote_addr.ip_addr.addr.id = ++last_address_id; remote_addr.ip_addr.addr.id = ++last_address_id;
@@ -789,6 +844,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 void
NSR_SetSourceResolvingEndHandler(NSR_SourceResolvingEndHandler handler) NSR_SetSourceResolvingEndHandler(NSR_SourceResolvingEndHandler handler)
{ {
@@ -884,6 +964,7 @@ NSR_RemoveSource(IPAddr *address)
if (find_slot(address, &slot) == 0) if (find_slot(address, &slot) == 0)
return NSR_NoSuchSource; return NSR_NoSuchSource;
log_source(get_record(slot), 0, 0);
clean_source_record(get_record(slot)); clean_source_record(get_record(slot));
/* Rehash the table to make sure there are no broken probe sequences. /* Rehash the table to make sure there are no broken probe sequences.
@@ -906,6 +987,7 @@ NSR_RemoveSourcesById(uint32_t conf_id)
record = get_record(i); record = get_record(i);
if (!record->remote_addr || record->conf_id != conf_id) if (!record->remote_addr || record->conf_id != conf_id)
continue; continue;
log_source(record, 0, 1);
clean_source_record(record); clean_source_record(record);
} }
@@ -933,20 +1015,23 @@ NSR_RemoveAllSources(void)
/* ================================================== */ /* ================================================== */
static void static void
resolve_source_replacement(SourceRecord *record) resolve_source_replacement(SourceRecord *record, int refreshment)
{ {
struct UnresolvedSource *us; 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); UTI_IPToString(&record->remote_addr->ip_addr), record->name);
record->last_resolving = SCH_GetLastEventMonoTime();
us = MallocNew(struct UnresolvedSource); us = MallocNew(struct UnresolvedSource);
us->name = Strdup(record->name); us->name = Strdup(record->name);
/* If there never was a valid reply from this source (e.g. it was a bad /* Ignore the order of addresses from the resolver to not get
replacement), ignore the order of addresses from the resolver to not get stuck with a pair of unreachable or otherwise unusable servers
stuck to a pair of addresses if the order doesn't change, or a group of (e.g. falsetickers) in case the order doesn't change, or a group
IPv4/IPv6 addresses if the resolver prefers inaccessible IP family */ of servers if they are ordered by IP family */
us->random_order = record->tentative; us->random_order = 1;
us->refreshment = refreshment;
us->pool_id = INVALID_POOL; us->pool_id = INVALID_POOL;
us->address = *record->remote_addr; us->address = *record->remote_addr;
@@ -959,11 +1044,11 @@ resolve_source_replacement(SourceRecord *record)
void void
NSR_HandleBadSource(IPAddr *address) NSR_HandleBadSource(IPAddr *address)
{ {
static struct timespec last_replacement; static double next_replacement = 0.0;
struct timespec now;
SourceRecord *record; SourceRecord *record;
IPAddr ip_addr; IPAddr ip_addr;
double diff; uint32_t rnd;
double now;
int slot; int slot;
if (!find_slot(address, &slot)) if (!find_slot(address, &slot))
@@ -978,15 +1063,56 @@ NSR_HandleBadSource(IPAddr *address)
return; return;
/* Don't resolve names too frequently */ /* Don't resolve names too frequently */
SCH_GetLastEventTime(NULL, NULL, &now); now = SCH_GetLastEventMonoTime();
diff = UTI_DiffTimespecsToDouble(&now, &last_replacement); if (now < next_replacement) {
if (fabs(diff) < RESOLVE_INTERVAL_UNIT * (1 << MIN_REPLACEMENT_INTERVAL)) {
DEBUG_LOG("replacement postponed"); DEBUG_LOG("replacement postponed");
return; 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 +1128,7 @@ NSR_RefreshAddresses(void)
if (!record->remote_addr) if (!record->remote_addr)
continue; continue;
resolve_source_replacement(record); resolve_source_replacement(record, 1);
} }
} }
@@ -1128,6 +1254,8 @@ NSR_ProcessRx(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr,
remove_pool_sources(record->pool_id, 1, 0); remove_pool_sources(record->pool_id, 1, 0);
} }
} }
maybe_refresh_source();
} else { } else {
NCR_ProcessRxUnknown(remote_addr, local_addr, rx_ts, message, length); NCR_ProcessRxUnknown(remote_addr, local_addr, rx_ts, message, length);
} }

View File

@@ -60,6 +60,8 @@ extern NSR_Status NSR_AddSource(NTP_Remote_Address *remote_addr, NTP_Source_Type
extern NSR_Status NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type, extern NSR_Status NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
SourceParameters *params, uint32_t *conf_id); 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 /* Function type for handlers to be called back when an attempt
* (possibly unsuccessful) to resolve unresolved sources ends */ * (possibly unsuccessful) to resolve unresolved sources ends */
typedef void (*NSR_SourceResolvingEndHandler)(void); typedef void (*NSR_SourceResolvingEndHandler)(void);

View File

@@ -102,16 +102,22 @@ static int
prepare_request(NKC_Instance inst) prepare_request(NKC_Instance inst)
{ {
NKSN_Instance session = inst->session; NKSN_Instance session = inst->session;
uint16_t datum; uint16_t data[2];
int length;
NKSN_BeginMessage(session); NKSN_BeginMessage(session);
datum = htons(NKE_NEXT_PROTOCOL_NTPV4); data[0] = htons(NKE_NEXT_PROTOCOL_NTPV4);
if (!NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, &datum, sizeof (datum))) if (!NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, data, sizeof (data[0])))
return 0; return 0;
datum = htons(AEAD_AES_SIV_CMAC_256); length = 0;
if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, &datum, sizeof (datum))) 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; return 0;
if (!NKSN_EndMessage(session)) if (!NKSN_EndMessage(session))
@@ -159,12 +165,14 @@ process_response(NKC_Instance inst)
next_protocol = NKE_NEXT_PROTOCOL_NTPV4; next_protocol = NKE_NEXT_PROTOCOL_NTPV4;
break; break;
case NKE_RECORD_AEAD_ALGORITHM: 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"); DEBUG_LOG("Unexpected NTS-KE AEAD algorithm");
error = 1; error = 1;
break; break;
} }
aead_algorithm = AEAD_AES_SIV_CMAC_256; aead_algorithm = ntohs(data[0]);
inst->context.algorithm = aead_algorithm; inst->context.algorithm = aead_algorithm;
break; break;
case NKE_RECORD_ERROR: case NKE_RECORD_ERROR:
@@ -236,7 +244,7 @@ process_response(NKC_Instance inst)
if (error || inst->num_cookies == 0 || if (error || inst->num_cookies == 0 ||
next_protocol != NKE_NEXT_PROTOCOL_NTPV4 || next_protocol != NKE_NEXT_PROTOCOL_NTPV4 ||
aead_algorithm != AEAD_AES_SIV_CMAC_256) aead_algorithm < 0)
return 0; return 0;
return 1; return 1;
@@ -370,6 +378,13 @@ NKC_Start(NKC_Instance inst)
return 0; 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 */ /* Follow the bindacqaddress and bindacqdevice settings */
CNF_GetBindAcquisitionAddress(inst->address.ip_addr.family, &local_addr.ip_addr); CNF_GetBindAcquisitionAddress(inst->address.ip_addr.family, &local_addr.ip_addr);
local_addr.port = 0; local_addr.port = 0;

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate. chronyd/chronyc - Programs for keeping computer clocks accurate.
********************************************************************** **********************************************************************
* Copyright (C) Miroslav Lichvar 2020 * Copyright (C) Miroslav Lichvar 2020, 2022
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -47,31 +47,33 @@
#define SERVER_TIMEOUT 2.0 #define SERVER_TIMEOUT 2.0
#define SERVER_COOKIE_SIV AEAD_AES_SIV_CMAC_256 #define MAX_COOKIE_NONCE_LENGTH 16
#define SERVER_COOKIE_NONCE_LENGTH 16
#define KEY_ID_INDEX_BITS 2 #define KEY_ID_INDEX_BITS 2
#define MAX_SERVER_KEYS (1U << KEY_ID_INDEX_BITS) #define MAX_SERVER_KEYS (1U << KEY_ID_INDEX_BITS)
#define FUTURE_KEYS 1 #define FUTURE_KEYS 1
#define DUMP_FILENAME "ntskeys" #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) #define INVALID_SOCK_FD (-7)
typedef struct { typedef struct {
uint32_t key_id; uint32_t key_id;
unsigned char nonce[SERVER_COOKIE_NONCE_LENGTH];
} ServerCookieHeader; } ServerCookieHeader;
typedef struct { typedef struct {
uint32_t id; uint32_t id;
unsigned char key[SIV_MAX_KEY_LENGTH]; unsigned char key[SIV_MAX_KEY_LENGTH];
SIV_Algorithm siv_algorithm;
SIV_Instance siv; SIV_Instance siv;
int nonce_length;
} ServerKey; } ServerKey;
typedef struct { typedef struct {
uint32_t key_id; uint32_t key_id;
uint32_t siv_algorithm;
unsigned char key[SIV_MAX_KEY_LENGTH]; unsigned char key[SIV_MAX_KEY_LENGTH];
IPAddr client_addr; IPAddr client_addr;
uint16_t client_port; 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 static void
handle_helper_request(int fd, int event, void *arg) handle_helper_request(int fd, int event, void *arg)
{ {
SCK_Message *message; SCK_Message *message;
HelperRequest *req; HelperRequest *req;
IPSockAddr client_addr; IPSockAddr client_addr;
ServerKey *key;
int sock_fd; int sock_fd;
/* Receive the helper request with the NTS-KE session socket. /* 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; req = message->data;
/* Extract the current server key and client address from the request */ /* Extract the current server key and client address from the request */
server_keys[current_server_key].id = ntohl(req->key_id); key = &server_keys[current_server_key];
assert(sizeof (server_keys[current_server_key].key) == sizeof (req->key)); key->id = ntohl(req->key_id);
memcpy(server_keys[current_server_key].key, req->key, assert(sizeof (key->key) == sizeof (req->key));
sizeof (server_keys[current_server_key].key)); memcpy(key->key, req->key, sizeof (key->key));
UTI_IPNetworkToHost(&req->client_addr, &client_addr.ip_addr); UTI_IPNetworkToHost(&req->client_addr, &client_addr.ip_addr);
client_addr.port = ntohs(req->client_port); client_addr.port = ntohs(req->client_port);
if (!SIV_SetKey(server_keys[current_server_key].siv, server_keys[current_server_key].key, update_key_siv(key, ntohl(req->siv_algorithm));
SIV_GetKeyLength(SERVER_COOKIE_SIV)))
LOG_FATAL("Could not set SIV key");
if (!handle_client(sock_fd, &client_addr)) { if (!handle_client(sock_fd, &client_addr)) {
SCK_CloseSocket(sock_fd); 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 */ /* Include the current server key and client address in the request */
req.key_id = htonl(server_keys[current_server_key].id); 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)); assert(sizeof (req.key) == sizeof (server_keys[current_server_key].key));
memcpy(req.key, server_keys[current_server_key].key, sizeof (req.key)); memcpy(req.key, server_keys[current_server_key].key, sizeof (req.key));
UTI_IPHostToNetwork(&addr.ip_addr, &req.client_addr); 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++) { for (i = 0; i < MIN(length, sizeof (data)) / 2; i++) {
aead_algorithm_values++; aead_algorithm_values++;
if (ntohs(data[i]) == AEAD_AES_SIV_CMAC_256) /* Use the first supported algorithm */
aead_algorithm = AEAD_AES_SIV_CMAC_256; if (aead_algorithm < 0 && SIV_GetKeyLength(ntohs(data[i])) > 0)
aead_algorithm = ntohs(data[i]);
} }
break; break;
case NKE_RECORD_ERROR: case NKE_RECORD_ERROR:
@@ -470,28 +490,38 @@ handle_message(void *arg)
static void static void
generate_key(int index) generate_key(int index)
{ {
SIV_Algorithm algorithm;
ServerKey *key;
int key_length; int key_length;
if (index < 0 || index >= MAX_SERVER_KEYS) if (index < 0 || index >= MAX_SERVER_KEYS)
assert(0); assert(0);
key_length = SIV_GetKeyLength(SERVER_COOKIE_SIV); /* Prefer AES-128-GCM-SIV if available. Note that if older keys loaded
if (key_length > sizeof (server_keys[index].key)) 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); assert(0);
UTI_GetRandomBytesUrandom(server_keys[index].key, key_length); UTI_GetRandomBytesUrandom(key->key, key_length);
memset(key->key + key_length, 0, sizeof (key->key) - key_length);
if (!server_keys[index].siv || UTI_GetRandomBytes(&key->id, sizeof (key->id));
!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));
/* Encode the index in the lowest bits of the ID */ /* Encode the index in the lowest bits of the ID */
server_keys[index].id &= -1U << KEY_ID_INDEX_BITS; key->id &= -1U << KEY_ID_INDEX_BITS;
server_keys[index].id |= index; 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(); last_server_key_ts = SCH_GetLastEventMonoTime();
} }
@@ -519,18 +549,19 @@ save_keys(void)
if (!f) if (!f)
return; return;
key_length = SIV_GetKeyLength(SERVER_COOKIE_SIV);
last_key_age = SCH_GetLastEventMonoTime() - last_server_key_ts; 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; goto error;
for (i = 0; i < MAX_SERVER_KEYS; i++) { for (i = 0; i < MAX_SERVER_KEYS; i++) {
index = (current_server_key + i + 1 + FUTURE_KEYS) % MAX_SERVER_KEYS; 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) || if (key_length > sizeof (server_keys[index].key) ||
!UTI_BytesToHex(server_keys[index].key, key_length, buf, sizeof (buf)) || !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; goto error;
} }
@@ -545,7 +576,7 @@ save_keys(void)
return; return;
error: error:
DEBUG_LOG("Could not %s server keys", "save"); LOG(LOGS_ERR, "Could not %s %s", "save", "server NTS keys");
fclose(f); fclose(f);
if (!UTI_RemoveFile(dump_dir, DUMP_FILENAME, NULL)) if (!UTI_RemoveFile(dump_dir, DUMP_FILENAME, NULL))
@@ -554,17 +585,16 @@ error:
/* ================================================== */ /* ================================================== */
#define MAX_WORDS 2 #define MAX_WORDS 3
static int static int
load_keys(void) load_keys(void)
{ {
int i, index, key_length, algorithm = 0, old_ver;
char *dump_dir, line[1024], *words[MAX_WORDS]; char *dump_dir, line[1024], *words[MAX_WORDS];
unsigned char key[SIV_MAX_KEY_LENGTH]; ServerKey new_keys[MAX_SERVER_KEYS];
int i, index, key_length, algorithm;
double key_age; double key_age;
FILE *f; FILE *f;
uint32_t id;
dump_dir = CNF_GetNtsDumpDir(); dump_dir = CNF_GetNtsDumpDir();
if (!dump_dir) if (!dump_dir)
@@ -574,43 +604,58 @@ load_keys(void)
if (!f) if (!f)
return 0; return 0;
if (!fgets(line, sizeof (line), f) || strcmp(line, DUMP_IDENTIFIER) != 0 || if (!fgets(line, sizeof (line), f) ||
!fgets(line, sizeof (line), f) || UTI_SplitString(line, words, MAX_WORDS) != 2 || (strcmp(line, DUMP_IDENTIFIER) != 0 && strcmp(line, OLD_DUMP_IDENTIFIER) != 0))
sscanf(words[0], "%d", &algorithm) != 1 || algorithm != SERVER_COOKIE_SIV ||
sscanf(words[1], "%lf", &key_age) != 1)
goto error; goto error;
key_length = SIV_GetKeyLength(SERVER_COOKIE_SIV); old_ver = strcmp(line, DUMP_IDENTIFIER) != 0;
last_server_key_ts = SCH_GetLastEventMonoTime() - MAX(key_age, 0.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++) { for (i = 0; i < MAX_SERVER_KEYS && fgets(line, sizeof (line), f); i++) {
if (UTI_SplitString(line, words, MAX_WORDS) != 2 || if (UTI_SplitString(line, words, MAX_WORDS) != (old_ver ? 2 : 3) ||
sscanf(words[0], "%"PRIX32, &id) != 1) sscanf(words[0], "%"PRIX32, &new_keys[i].id) != 1 ||
(!old_ver && sscanf(words[2], "%d", &algorithm) != 1))
goto error; 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; goto error;
memset(new_keys[i].key + key_length, 0, sizeof (new_keys[i].key) - key_length);
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;
} }
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); fclose(f);
LOG(LOGS_INFO, "Loaded %s", "server NTS keys");
return 1; return 1;
error: error:
DEBUG_LOG("Could not %s server keys", "load"); LOG(LOGS_ERR, "Could not %s %s", "load", "server NTS keys");
fclose(f); fclose(f);
return 0; return 0;
@@ -640,6 +685,8 @@ run_helper(uid_t uid, gid_t gid, int scfilter_level)
DEBUG_LOG("Helper started"); DEBUG_LOG("Helper started");
SCK_CloseReusableSockets();
/* Suppress a log message about disabled clock control */ /* Suppress a log message about disabled clock control */
log_severity = LOG_GetMinSeverity(); log_severity = LOG_GetMinSeverity();
LOG_SetMinSeverity(LOGS_ERR); LOG_SetMinSeverity(LOGS_ERR);
@@ -759,7 +806,7 @@ NKS_Initialise(void)
/* Generate random keys, even if they will be replaced by reloaded keys, /* Generate random keys, even if they will be replaced by reloaded keys,
or unused (in the helper) */ or unused (in the helper) */
for (i = 0; i < MAX_SERVER_KEYS; i++) { 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); generate_key(i);
} }
@@ -779,6 +826,11 @@ NKS_Initialise(void)
key_delay = key_rotation_interval - (SCH_GetLastEventMonoTime() - last_server_key_ts); key_delay = key_rotation_interval - (SCH_GetLastEventMonoTime() - last_server_key_ts);
SCH_AddTimeoutByDelay(MAX(key_delay, 0.0), key_timeout, NULL); 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; initialised = 1;
@@ -852,7 +904,7 @@ NKS_ReloadKeys(void)
int int
NKS_GenerateCookie(NKE_Context *context, NKE_Cookie *cookie) 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; int plaintext_length, tag_length;
ServerCookieHeader *header; ServerCookieHeader *header;
ServerKey *key; ServerKey *key;
@@ -862,14 +914,12 @@ NKS_GenerateCookie(NKE_Context *context, NKE_Cookie *cookie)
return 0; return 0;
} }
/* The algorithm is hardcoded for now */ /* The AEAD ID is not encoded in the cookie. It is implied from the key
if (context->algorithm != AEAD_AES_SIV_CMAC_256) { length (as long as only algorithms with different key lengths are
DEBUG_LOG("Unexpected SIV algorithm"); supported). */
return 0;
}
if (context->c2s.length < 0 || context->c2s.length > NKE_MAX_KEY_LENGTH || 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"); DEBUG_LOG("Invalid key length");
return 0; return 0;
} }
@@ -879,7 +929,11 @@ NKS_GenerateCookie(NKE_Context *context, NKE_Cookie *cookie)
header = (ServerCookieHeader *)cookie->cookie; header = (ServerCookieHeader *)cookie->cookie;
header->key_id = htonl(key->id); 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; plaintext_length = context->c2s.length + context->s2c.length;
assert(plaintext_length <= sizeof (plaintext)); 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); memcpy(plaintext + context->c2s.length, context->s2c.key, context->s2c.length);
tag_length = SIV_GetTagLength(key->siv); 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)); 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, "", 0,
plaintext, plaintext_length, plaintext, plaintext_length,
ciphertext, plaintext_length + tag_length)) { ciphertext, plaintext_length + tag_length)) {
@@ -907,7 +961,7 @@ NKS_GenerateCookie(NKE_Context *context, NKE_Cookie *cookie)
int int
NKS_DecodeCookie(NKE_Cookie *cookie, NKE_Context *context) 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; int ciphertext_length, plaintext_length, tag_length;
ServerCookieHeader *header; ServerCookieHeader *header;
ServerKey *key; ServerKey *key;
@@ -924,8 +978,6 @@ NKS_DecodeCookie(NKE_Cookie *cookie, NKE_Context *context)
} }
header = (ServerCookieHeader *)cookie->cookie; header = (ServerCookieHeader *)cookie->cookie;
ciphertext = cookie->cookie + sizeof (*header);
ciphertext_length = cookie->length - sizeof (*header);
key_id = ntohl(header->key_id); key_id = ntohl(header->key_id);
key = &server_keys[key_id % MAX_SERVER_KEYS]; 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); 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"); DEBUG_LOG("Invalid cookie length");
return 0; 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; plaintext_length = ciphertext_length - tag_length;
if (plaintext_length > sizeof (plaintext) || plaintext_length % 2 != 0) { if (plaintext_length > sizeof (plaintext) || plaintext_length % 2 != 0) {
DEBUG_LOG("Invalid cookie length"); DEBUG_LOG("Invalid cookie length");
return 0; return 0;
} }
if (!SIV_Decrypt(key->siv, header->nonce, sizeof (header->nonce), if (!SIV_Decrypt(key->siv, nonce, key->nonce_length,
"", 0, "", 0,
ciphertext, ciphertext_length, ciphertext, ciphertext_length,
plaintext, plaintext_length)) { plaintext, plaintext_length)) {
@@ -954,7 +1011,19 @@ NKS_DecodeCookie(NKE_Cookie *cookie, NKE_Context *context)
return 0; 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->c2s.length = plaintext_length / 2;
context->s2c.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); assert(0);
for (i = 0; i < n_certs_keys; i++) { 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], r = gnutls_certificate_set_x509_key_file(credentials, certs[i], keys[i],
GNUTLS_X509_FMT_PEM); GNUTLS_X509_FMT_PEM);
if (r < 0) if (r < 0)

View File

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

View File

@@ -32,7 +32,7 @@
#include "siv.h" #include "siv.h"
extern int NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv, 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, const unsigned char *plaintext, int plaintext_length,
int min_ef_length); int min_ef_length);

View File

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

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate. chronyd/chronyc - Programs for keeping computer clocks accurate.
********************************************************************** **********************************************************************
* Copyright (C) Miroslav Lichvar 2020 * Copyright (C) Miroslav Lichvar 2020, 2022
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -41,13 +41,15 @@
#include "siv.h" #include "siv.h"
#include "util.h" #include "util.h"
#define SERVER_SIV AEAD_AES_SIV_CMAC_256 #define MAX_SERVER_SIVS 2
struct NtsServer { 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]; unsigned char nonce[NTS_MIN_UNPADDED_NONCE_LENGTH];
NKE_Cookie cookies[NTS_MAX_COOKIES]; NKE_Cookie cookies[NTS_MAX_COOKIES];
int num_cookies; int num_cookies;
int siv_index;
NTP_int64 req_tx; NTP_int64 req_tx;
}; };
@@ -60,6 +62,7 @@ void
NNS_Initialise(void) NNS_Initialise(void)
{ {
const char **certs, **keys; const char **certs, **keys;
int i;
/* Create an NTS-NTP server instance only if NTS-KE server is enabled */ /* Create an NTS-NTP server instance only if NTS-KE server is enabled */
if (CNF_GetNtsServerCertAndKeyFiles(&certs, &keys) <= 0) { if (CNF_GetNtsServerCertAndKeyFiles(&certs, &keys) <= 0) {
@@ -68,9 +71,17 @@ NNS_Initialise(void)
} }
server = Malloc(sizeof (struct NtsServer)); server = Malloc(sizeof (struct NtsServer));
server->siv = SIV_CreateInstance(SERVER_SIV);
if (!server->siv) server->siv_algorithms[0] = AEAD_AES_SIV_CMAC_256;
LOG_FATAL("Could not initialise SIV cipher"); 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 void
NNS_Finalise(void) NNS_Finalise(void)
{ {
int i;
if (!server) if (!server)
return; return;
SIV_DestroyInstance(server->siv); for (i = 0; i < MAX_SERVER_SIVS; i++) {
if (server->sivs[i])
SIV_DestroyInstance(server->sivs[i]);
}
Free(server); Free(server);
server = NULL; server = NULL;
} }
@@ -96,6 +112,7 @@ NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
unsigned char plaintext[NTP_MAX_EXTENSIONS_LENGTH]; unsigned char plaintext[NTP_MAX_EXTENSIONS_LENGTH];
NKE_Context context; NKE_Context context;
NKE_Cookie cookie; NKE_Cookie cookie;
SIV_Instance siv;
void *ef_body; void *ef_body;
*kod = 0; *kod = 0;
@@ -104,6 +121,7 @@ NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
return 0; return 0;
server->num_cookies = 0; server->num_cookies = 0;
server->siv_index = -1;
server->req_tx = packet->transmit_ts; server->req_tx = packet->transmit_ts;
if (info->ext_fields == 0 || info->mode != MODE_CLIENT) 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; 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"); DEBUG_LOG("Unexpected SIV");
return 0; 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"); DEBUG_LOG("Could not set C2S key");
return 0; return 0;
} }
if (!NNA_DecryptAuthEF(packet, info, server->siv, auth_start, if (!NNA_DecryptAuthEF(packet, info, siv, auth_start,
plaintext, sizeof (plaintext), &plaintext_length)) { plaintext, sizeof (plaintext), &plaintext_length)) {
*kod = NTP_KOD_NTS_NAK; *kod = NTP_KOD_NTS_NAK;
return 0; 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"); DEBUG_LOG("Could not set S2C key");
return 0; return 0;
} }
@@ -271,9 +294,12 @@ NNS_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *req_info,
server->num_cookies = 0; server->num_cookies = 0;
if (server->siv_index < 0)
return 0;
/* Generate an authenticator field which will make the length /* Generate an authenticator field which will make the length
of the response equal to the length of the request */ 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), server->nonce, sizeof (server->nonce),
plaintext, plaintext_length, plaintext, plaintext_length,
req_info->length - res_info->length)) req_info->length - res_info->length))

View File

@@ -129,6 +129,7 @@ static const struct request_length request_lengths[] = {
REQ_LENGTH_ENTRY(select_data, select_data), /* SELECT_DATA */ REQ_LENGTH_ENTRY(select_data, select_data), /* SELECT_DATA */
REQ_LENGTH_ENTRY(null, null), /* RELOAD_SOURCES */ REQ_LENGTH_ENTRY(null, null), /* RELOAD_SOURCES */
REQ_LENGTH_ENTRY(doffset, null), /* DOFFSET2 */ REQ_LENGTH_ENTRY(doffset, null), /* DOFFSET2 */
REQ_LENGTH_ENTRY(modify_select_opts, null), /* MODIFY_SELECTOPTS */
}; };
static const uint16_t reply_lengths[] = { static const uint16_t reply_lengths[] = {
@@ -156,7 +157,8 @@ static const uint16_t reply_lengths[] = {
RPY_LENGTH_ENTRY(client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX3 */ RPY_LENGTH_ENTRY(client_accesses_by_index), /* CLIENT_ACCESSES_BY_INDEX3 */
0, /* SERVER_STATS2 - not supported */ 0, /* SERVER_STATS2 - not supported */
RPY_LENGTH_ENTRY(select_data), /* SELECT_DATA */ RPY_LENGTH_ENTRY(select_data), /* SELECT_DATA */
RPY_LENGTH_ENTRY(server_stats), /* SERVER_STATS3 */ 0, /* SERVER_STATS3 - not supported */
RPY_LENGTH_ENTRY(server_stats), /* SERVER_STATS4 */
}; };
/* ================================================== */ /* ================================================== */

7
ptp.h
View File

@@ -44,7 +44,12 @@ typedef struct {
uint8_t domain; uint8_t domain;
uint8_t min_sdoid; uint8_t min_sdoid;
uint16_t flags; 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; } PTP_Header;
typedef struct { typedef struct {

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate. chronyd/chronyc - Programs for keeping computer clocks accurate.
********************************************************************** **********************************************************************
* Copyright (C) Miroslav Lichvar 2009-2011, 2013-2014, 2016-2019 * Copyright (C) Miroslav Lichvar 2009-2011, 2013-2014, 2016-2019, 2022
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate. chronyd/chronyc - Programs for keeping computer clocks accurate.
********************************************************************** **********************************************************************
* Copyright (C) Miroslav Lichvar 2013, 2017 * Copyright (C) Miroslav Lichvar 2013, 2017, 2023
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -33,6 +33,9 @@
#include "sysincl.h" #include "sysincl.h"
#include <sys/sysmacros.h>
#include "array.h"
#include "refclock.h" #include "refclock.h"
#include "hwclock.h" #include "hwclock.h"
#include "local.h" #include "local.h"
@@ -44,14 +47,19 @@
struct phc_instance { struct phc_instance {
int fd; int fd;
int dev_index;
int mode; int mode;
int nocrossts; int nocrossts;
int extpps; int extpps;
int pin; int pin;
int channel; int channel;
struct timespec last_extts;
HCL_Instance clock; 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 void read_ext_pulse(int sockfd, int event, void *anything);
static int phc_initialise(RCL_Instance instance) 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}; const char *options[] = {"nocrossts", "extpps", "pin", "channel", "clear", NULL};
struct phc_instance *phc; struct phc_instance *phc;
int phc_fd, rising_edge; int phc_fd, rising_edge;
struct stat st;
char *path, *s; char *path, *s;
RCL_CheckDriverOptions(instance, options); RCL_CheckDriverOptions(instance, options);
@@ -71,10 +80,13 @@ static int phc_initialise(RCL_Instance instance)
phc = MallocNew(struct phc_instance); phc = MallocNew(struct phc_instance);
phc->fd = phc_fd; 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->mode = 0;
phc->nocrossts = RCL_GetDriverOption(instance, "nocrossts") ? 1 : 0; phc->nocrossts = RCL_GetDriverOption(instance, "nocrossts") ? 1 : 0;
phc->extpps = RCL_GetDriverOption(instance, "extpps") ? 1 : 0; phc->extpps = RCL_GetDriverOption(instance, "extpps") ? 1 : 0;
UTI_ZeroTimespec(&phc->last_extts);
phc->clock = HCL_CreateInstance(0, 16, UTI_Log2ToDouble(RCL_GetDriverPoll(instance)), phc->clock = HCL_CreateInstance(0, 16, UTI_Log2ToDouble(RCL_GetDriverPoll(instance)),
RCL_GetPrecision(instance)); RCL_GetPrecision(instance));
@@ -90,6 +102,10 @@ static int phc_initialise(RCL_Instance instance)
LOG_FATAL("Could not enable external PHC timestamping"); LOG_FATAL("Could not enable external PHC timestamping");
SCH_AddFileHandler(phc->fd, SCH_FILE_INPUT, read_ext_pulse, instance); 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 { } else {
phc->pin = phc->channel = 0; phc->pin = phc->channel = 0;
} }
@@ -101,12 +117,22 @@ static int phc_initialise(RCL_Instance instance)
static void phc_finalise(RCL_Instance instance) static void phc_finalise(RCL_Instance instance)
{ {
struct phc_instance *phc; struct phc_instance *phc;
unsigned int i;
phc = (struct phc_instance *)RCL_GetDriverData(instance); phc = (struct phc_instance *)RCL_GetDriverData(instance);
if (phc->extpps) { if (phc->extpps) {
SCH_RemoveFileHandler(phc->fd); SCH_RemoveFileHandler(phc->fd);
SYS_Linux_SetPHCExtTimestamping(phc->fd, phc->pin, phc->channel, 0, 0, 0); SYS_Linux_SetPHCExtTimestamping(phc->fd, phc->pin, phc->channel, 0, 0, 0);
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); HCL_DestroyInstance(phc->clock);
@@ -114,30 +140,52 @@ static void phc_finalise(RCL_Instance instance)
Free(phc); 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 phc_instance *phc;
struct timespec phc_ts, local_ts; struct timespec local_ts;
double local_err; double local_err;
int channel;
instance = anything;
phc = RCL_GetDriverData(instance); phc = RCL_GetDriverData(instance);
if (!SYS_Linux_ReadPHCExtTimestamp(phc->fd, &phc_ts, &channel)) if (UTI_CompareTimespecs(&phc->last_extts, phc_ts) == 0) {
return; DEBUG_LOG("Ignoring duplicated PHC timestamp");
if (channel != phc->channel) {
DEBUG_LOG("Unexpected extts channel %d\n", channel);
return; 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; return;
RCL_AddCookedPulse(instance, &local_ts, 1.0e-9 * local_ts.tv_nsec, local_err, 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);
/* The Linux kernel (as of 6.2) has 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 #define PHC_READINGS 25

View File

@@ -58,8 +58,29 @@ struct sock_sample {
int magic; 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) static void read_sample(int sockfd, int event, void *anything)
{ {
char buf[sizeof (struct sock_sample) + 16];
struct timespec sys_ts, ref_ts; struct timespec sys_ts, ref_ts;
struct sock_sample sample; struct sock_sample sample;
RCL_Instance instance; RCL_Instance instance;
@@ -67,14 +88,33 @@ static void read_sample(int sockfd, int event, void *anything)
instance = (RCL_Instance)anything; instance = (RCL_Instance)anything;
s = recv(sockfd, &sample, sizeof (sample), 0); s = recv(sockfd, buf, sizeof (buf), 0);
if (s < 0) { if (s < 0) {
DEBUG_LOG("Could not read SOCK sample : %s", strerror(errno)); DEBUG_LOG("Could not read SOCK sample : %s", strerror(errno));
return; 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", DEBUG_LOG("Unexpected length of SOCK sample : %d != %ld",
s, (long)sizeof (sample)); s, (long)sizeof (sample));
return; return;

View File

@@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2009-2018, 2020 * Copyright (C) Miroslav Lichvar 2009-2018, 2020, 2022
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -1329,6 +1329,7 @@ void
REF_ModifyMaxupdateskew(double new_max_update_skew) REF_ModifyMaxupdateskew(double new_max_update_skew)
{ {
max_update_skew = new_max_update_skew * 1.0e-6; max_update_skew = new_max_update_skew * 1.0e-6;
LOG(LOGS_INFO, "New maxupdateskew %f ppm", new_max_update_skew);
} }
/* ================================================== */ /* ================================================== */
@@ -1338,6 +1339,7 @@ REF_ModifyMakestep(int limit, double threshold)
{ {
make_step_limit = limit; make_step_limit = limit;
make_step_threshold = threshold; make_step_threshold = threshold;
LOG(LOGS_INFO, "New makestep %f %d", threshold, limit);
} }
/* ================================================== */ /* ================================================== */
@@ -1349,6 +1351,7 @@ REF_EnableLocal(int stratum, double distance, int orphan)
local_stratum = CLAMP(1, stratum, NTP_MAX_STRATUM - 1); local_stratum = CLAMP(1, stratum, NTP_MAX_STRATUM - 1);
local_distance = distance; local_distance = distance;
local_orphan = !!orphan; local_orphan = !!orphan;
LOG(LOGS_INFO, "%s local reference mode", "Enabled");
} }
/* ================================================== */ /* ================================================== */
@@ -1357,6 +1360,7 @@ void
REF_DisableLocal(void) REF_DisableLocal(void)
{ {
enable_local_stratum = 0; enable_local_stratum = 0;
LOG(LOGS_INFO, "%s local reference mode", "Disabled");
} }
/* ================================================== */ /* ================================================== */

View File

@@ -109,17 +109,23 @@ typedef struct {
} RPT_ClientAccessByIndex_Report; } RPT_ClientAccessByIndex_Report;
typedef struct { typedef struct {
uint32_t ntp_hits; uint64_t ntp_hits;
uint32_t nke_hits; uint64_t nke_hits;
uint32_t cmd_hits; uint64_t cmd_hits;
uint32_t ntp_drops; uint64_t ntp_drops;
uint32_t nke_drops; uint64_t nke_drops;
uint32_t cmd_drops; uint64_t cmd_drops;
uint32_t log_drops; uint64_t log_drops;
uint32_t ntp_auth_hits; uint64_t ntp_auth_hits;
uint32_t ntp_interleaved_hits; uint64_t ntp_interleaved_hits;
uint32_t ntp_timestamps; uint64_t ntp_timestamps;
uint32_t ntp_span_seconds; 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; } RPT_ServerStatsReport;
typedef struct { typedef struct {
@@ -174,6 +180,7 @@ typedef struct {
uint32_t total_tx_count; uint32_t total_tx_count;
uint32_t total_rx_count; uint32_t total_rx_count;
uint32_t total_valid_count; uint32_t total_valid_count;
uint32_t total_good_count;
} RPT_NTPReport; } RPT_NTPReport;
typedef struct { typedef struct {

16
sched.c
View File

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

View File

@@ -37,6 +37,7 @@ typedef enum {
SCH_NtpClientClass, SCH_NtpClientClass,
SCH_NtpPeerClass, SCH_NtpPeerClass,
SCH_NtpBroadcastClass, SCH_NtpBroadcastClass,
SCH_PhcPollClass,
SCH_NumberOfClasses /* needs to be last */ SCH_NumberOfClasses /* needs to be last */
} SCH_TimeoutClass; } 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_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_GetTagLength(SIV_Instance instance);
extern int SIV_Encrypt(SIV_Instance instance, extern int SIV_Encrypt(SIV_Instance instance,

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate. chronyd/chronyc - Programs for keeping computer clocks accurate.
********************************************************************** **********************************************************************
* Copyright (C) Miroslav Lichvar 2020 * Copyright (C) Miroslav Lichvar 2020, 2023
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -37,6 +37,8 @@
struct SIV_Instance_Record { struct SIV_Instance_Record {
gnutls_cipher_algorithm_t algorithm; gnutls_cipher_algorithm_t algorithm;
gnutls_aead_cipher_hd_t cipher; 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) { switch (algorithm) {
case AEAD_AES_SIV_CMAC_256: case AEAD_AES_SIV_CMAC_256:
return GNUTLS_CIPHER_AES_128_SIV; 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: default:
return 0; return 0;
} }
@@ -112,6 +118,19 @@ SIV_CreateInstance(SIV_Algorithm algorithm)
instance->algorithm = calgo; instance->algorithm = calgo;
instance->cipher = NULL; 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++; instance_counter++;
return instance; return instance;
@@ -143,6 +162,8 @@ SIV_GetKeyLength(SIV_Algorithm algorithm)
return 0; return 0;
len = gnutls_cipher_get_key_size(calgo); len = gnutls_cipher_get_key_size(calgo);
if (len == 0)
return 0;
if (len < 1 || len > SIV_MAX_KEY_LENGTH) if (len < 1 || len > SIV_MAX_KEY_LENGTH)
LOG_FATAL("Invalid 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 int
SIV_GetTagLength(SIV_Instance instance) SIV_GetTagLength(SIV_Instance instance)
{ {
@@ -222,7 +259,8 @@ SIV_Encrypt(SIV_Instance instance,
if (!instance->cipher) if (!instance->cipher)
return 0; 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) plaintext_length < 0 || ciphertext_length < 0)
return 0; return 0;
@@ -253,7 +291,8 @@ SIV_Decrypt(SIV_Instance instance,
if (!instance->cipher) if (!instance->cipher)
return 0; 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) plaintext_length < 0 || ciphertext_length < 0)
return 0; return 0;

View File

@@ -2,7 +2,7 @@
chronyd/chronyc - Programs for keeping computer clocks accurate. chronyd/chronyc - Programs for keeping computer clocks accurate.
********************************************************************** **********************************************************************
* Copyright (C) Miroslav Lichvar 2019 * Copyright (C) Miroslav Lichvar 2019, 2022
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -34,12 +34,25 @@
#include "siv_nettle_int.c" #include "siv_nettle_int.c"
#endif #endif
#ifdef HAVE_NETTLE_SIV_GCM
#include <nettle/siv-gcm.h>
#endif
#include "memory.h" #include "memory.h"
#include "siv.h" #include "siv.h"
struct SIV_Instance_Record { struct SIV_Instance_Record {
struct siv_cmac_aes128_ctx siv; SIV_Algorithm algorithm;
int key_set; 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; SIV_Instance instance;
if (algorithm != AEAD_AES_SIV_CMAC_256) if (SIV_GetKeyLength(algorithm) <= 0)
return NULL; return NULL;
instance = MallocNew(struct SIV_Instance_Record); instance = MallocNew(struct SIV_Instance_Record);
instance->algorithm = algorithm;
instance->key_set = 0; 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; return instance;
} }
@@ -71,11 +102,18 @@ SIV_DestroyInstance(SIV_Instance instance)
int int
SIV_GetKeyLength(SIV_Algorithm algorithm) 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) switch (algorithm) {
return 32; case AEAD_AES_SIV_CMAC_256:
return 0; 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 int
SIV_SetKey(SIV_Instance instance, const unsigned char *key, int length) SIV_SetKey(SIV_Instance instance, const unsigned char *key, int length)
{ {
if (length != 32) if (length != SIV_GetKeyLength(instance->algorithm))
return 0; 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; 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 int
SIV_GetTagLength(SIV_Instance instance) SIV_GetTagLength(SIV_Instance instance)
{ {
assert(SIV_DIGEST_SIZE <= SIV_MAX_TAG_LENGTH); if (instance->tag_length < 1 || instance->tag_length > SIV_MAX_TAG_LENGTH)
assert(0);
return SIV_DIGEST_SIZE; return instance->tag_length;
} }
/* ================================================== */ /* ================================================== */
@@ -115,16 +180,31 @@ SIV_Encrypt(SIV_Instance instance,
if (!instance->key_set) if (!instance->key_set)
return 0; 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 < 0 || plaintext_length > ciphertext_length ||
plaintext_length + SIV_DIGEST_SIZE != ciphertext_length) plaintext_length + SIV_GetTagLength(instance) != ciphertext_length)
return 0; return 0;
assert(assoc && plaintext); assert(assoc && plaintext);
siv_cmac_aes128_encrypt_message(&instance->siv, nonce_length, nonce, switch (instance->algorithm) {
assoc_length, assoc, case AEAD_AES_SIV_CMAC_256:
ciphertext_length, ciphertext, plaintext); 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; return 1;
} }
@@ -140,17 +220,32 @@ SIV_Decrypt(SIV_Instance instance,
if (!instance->key_set) if (!instance->key_set)
return 0; 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 < 0 || plaintext_length > ciphertext_length ||
plaintext_length + SIV_DIGEST_SIZE != ciphertext_length) plaintext_length + SIV_GetTagLength(instance) != ciphertext_length)
return 0; return 0;
assert(assoc && plaintext); assert(assoc && plaintext);
if (!siv_cmac_aes128_decrypt_message(&instance->siv, nonce_length, nonce, switch (instance->algorithm) {
assoc_length, assoc, case AEAD_AES_SIV_CMAC_256:
plaintext_length, plaintext, ciphertext)) if (!siv_cmac_aes128_decrypt_message(&instance->ctx.cmac_aes128,
return 0; 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; return 1;
} }

View File

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

183
socket.c
View File

@@ -5,6 +5,7 @@
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Timo Teras 2009 * Copyright (C) Timo Teras 2009
* Copyright (C) Miroslav Lichvar 2009, 2013-2020 * Copyright (C) Miroslav Lichvar 2009, 2013-2020
* Copyright (C) Luke Valenta 2023
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -89,6 +90,9 @@ struct MessageHeader {
static int initialised; static int initialised;
static int first_reusable_fd;
static int reusable_fds;
/* Flags indicating in which IP families sockets can be requested */ /* Flags indicating in which IP families sockets can be requested */
static int ip4_enabled; static int ip4_enabled;
static int ip6_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) #if defined(SOCK_CLOEXEC) || defined(SOCK_NONBLOCK)
static int static int
check_socket_flag(int sock_flag, int fd_flag, int fs_flag) 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) set_socket_flags(int sock_fd, int flags)
{ {
/* Close the socket automatically on exec */ /* Close the socket automatically on exec */
if ( if (!SCK_IsReusable(sock_fd) &&
#ifdef SOCK_CLOEXEC #ifdef SOCK_CLOEXEC
(supported_socket_flags & SOCK_CLOEXEC) == 0 && (supported_socket_flags & SOCK_CLOEXEC) == 0 &&
#endif #endif
@@ -222,7 +279,7 @@ set_socket_flags(int sock_fd, int flags)
/* Enable non-blocking mode */ /* Enable non-blocking mode */
if ((flags & SCK_FLAG_BLOCK) == 0 && if ((flags & SCK_FLAG_BLOCK) == 0 &&
#ifdef SOCK_NONBLOCK #ifdef SOCK_NONBLOCK
(supported_socket_flags & SOCK_NONBLOCK) == 0 && (SCK_IsReusable(sock_fd) || (supported_socket_flags & SOCK_NONBLOCK) == 0) &&
#endif #endif
!set_socket_nonblock(sock_fd)) !set_socket_nonblock(sock_fd))
return 0; 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 static int
set_socket_options(int sock_fd, int flags) set_socket_options(int sock_fd, int flags)
{ {
@@ -295,8 +378,10 @@ static int
set_ip_options(int sock_fd, int family, int flags) set_ip_options(int sock_fd, int family, int flags)
{ {
#if defined(FEAT_IPV6) && defined(IPV6_V6ONLY) #if defined(FEAT_IPV6) && defined(IPV6_V6ONLY)
/* Receive only IPv6 packets on an IPv6 socket */ /* Receive only IPv6 packets on an IPv6 socket, but do not attempt
if (family == IPADDR_INET6 && !SCK_SetIntOption(sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, 1)) 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; return 0;
#endif #endif
@@ -385,6 +470,10 @@ bind_ip_address(int sock_fd, IPSockAddr *addr, int flags)
; ;
#endif #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)); saddr_len = SCK_IPSockAddrToSockaddr(addr, (struct sockaddr *)&saddr, sizeof (saddr));
if (saddr_len == 0) if (saddr_len == 0)
return 0; return 0;
@@ -457,7 +546,7 @@ open_ip_socket(IPSockAddr *remote_addr, IPSockAddr *local_addr, const char *ifac
return INVALID_SOCK_FD; 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) if (sock_fd < 0)
return INVALID_SOCK_FD; return INVALID_SOCK_FD;
@@ -482,7 +571,8 @@ open_ip_socket(IPSockAddr *remote_addr, IPSockAddr *local_addr, const char *ifac
goto error; goto error;
if (remote_addr || local_addr) 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" : "?", type == SOCK_DGRAM ? "UDP" : type == SOCK_STREAM ? "TCP" : "?",
family == IPADDR_INET4 ? "v4" : "v6", family == IPADDR_INET4 ? "v4" : "v6",
sock_fd, 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)); memcpy(&message->timestamp.kernel, CMSG_DATA(cmsg), sizeof (message->timestamp.kernel));
} }
#endif #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
#ifdef HAVE_LINUX_TIMESTAMPING_OPT_PKTINFO #ifdef HAVE_LINUX_TIMESTAMPING_OPT_PKTINFO
else if (match_cmsg(cmsg, SOL_SOCKET, SCM_TIMESTAMPING_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 void
SCK_Initialise(int family) SCK_Initialise(int family)
{ {
int fd;
ip4_enabled = family == IPADDR_INET4 || family == IPADDR_UNSPEC; ip4_enabled = family == IPADDR_INET4 || family == IPADDR_UNSPEC;
#ifdef FEAT_IPV6 #ifdef FEAT_IPV6
ip6_enabled = family == IPADDR_INET6 || family == IPADDR_UNSPEC; ip6_enabled = family == IPADDR_INET6 || family == IPADDR_UNSPEC;
@@ -1196,6 +1326,9 @@ SCK_Initialise(int family)
supported_socket_flags |= SOCK_NONBLOCK; supported_socket_flags |= SOCK_NONBLOCK;
#endif #endif
for (fd = first_reusable_fd; fd < first_reusable_fd + reusable_fds; fd++)
UTI_FdSetCloexec(fd);
initialised = 1; initialised = 1;
} }
@@ -1208,6 +1341,8 @@ SCK_Finalise(void)
ARR_DestroyInstance(recv_headers); ARR_DestroyInstance(recv_headers);
ARR_DestroyInstance(recv_messages); ARR_DestroyInstance(recv_messages);
SCK_CloseReusableSockets();
initialised = 0; 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 int
SCK_SetIntOption(int sock_fd, int level, int name, int value) SCK_SetIntOption(int sock_fd, int level, int name, int value)
{ {
@@ -1386,8 +1542,15 @@ SCK_EnableKernelRxTimestamping(int sock_fd)
return 1; return 1;
#endif #endif
#ifdef SO_TIMESTAMP #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; return 1;
}
#endif #endif
return 0; return 0;
@@ -1398,7 +1561,7 @@ SCK_EnableKernelRxTimestamping(int sock_fd)
int int
SCK_ListenOnSocket(int sock_fd, int backlog) 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)); DEBUG_LOG("listen() failed : %s", strerror(errno));
return 0; return 0;
} }
@@ -1561,6 +1724,10 @@ SCK_RemoveSocket(int sock_fd)
void void
SCK_CloseSocket(int sock_fd) SCK_CloseSocket(int sock_fd)
{ {
/* Reusable sockets are closed in finalisation */
if (SCK_IsReusable(sock_fd))
return;
close(sock_fd); close(sock_fd);
} }

View File

@@ -73,6 +73,9 @@ typedef struct {
int descriptor; int descriptor;
} SCK_Message; } SCK_Message;
/* Pre-initialisation function */
extern void SCK_PreInitialise(void);
/* Initialisation function (the specified IP family is enabled, /* Initialisation function (the specified IP family is enabled,
or all if IPADDR_UNSPEC) */ or all if IPADDR_UNSPEC) */
extern void SCK_Initialise(int family); extern void SCK_Initialise(int family);
@@ -106,6 +109,12 @@ extern int SCK_OpenUnixStreamSocket(const char *remote_addr, const char *local_a
int flags); int flags);
extern int SCK_OpenUnixSocketPair(int flags, int *other_fd); 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 */ /* 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_SetIntOption(int sock_fd, int level, int name, int value);
extern int SCK_GetIntOption(int sock_fd, int level, int name, int *value); extern int SCK_GetIntOption(int sock_fd, int level, int name, int *value);

220
sources.c
View File

@@ -3,7 +3,7 @@
********************************************************************** **********************************************************************
* Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Richard P. Curnow 1997-2003
* Copyright (C) Miroslav Lichvar 2011-2016, 2018, 2020-2021 * Copyright (C) Miroslav Lichvar 2011-2016, 2018, 2020-2023
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -112,6 +112,9 @@ struct SRC_Instance_Record {
/* Updates left before allowing combining */ /* Updates left before allowing combining */
int distant; int distant;
/* Updates with a status requiring source replacement */
int bad;
/* Flag indicating the status of the source */ /* Flag indicating the status of the source */
SRC_Status status; SRC_Status status;
@@ -140,6 +143,10 @@ struct SRC_Instance_Record {
/* Flag indicating the source has a leap second vote */ /* Flag indicating the source has a leap second vote */
int leap_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,11 @@ static int max_n_sources; /* Capacity of the table */
static int selected_source_index; /* Which source index is currently static int selected_source_index; /* Which source index is currently
selected (set to INVALID_SOURCE selected (set to INVALID_SOURCE
if no current valid reference) */ 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) */
/* Score needed to replace the currently selected source */ /* Score needed to replace the currently selected source */
#define SCORE_LIMIT 10.0 #define SCORE_LIMIT 10.0
@@ -172,12 +184,17 @@ static int selected_source_index; /* Which source index is currently
/* Number of updates needed to reset the distant status */ /* Number of updates needed to reset the distant status */
#define DISTANT_PENALTY 32 #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_distance;
static double max_jitter; static double max_jitter;
static double reselect_distance; static double reselect_distance;
static double stratum_weight; static double stratum_weight;
static double combine_limit; static double combine_limit;
static SRC_Instance last_updated_inst;
static LOG_FileID logfileid; static LOG_FileID logfileid;
/* Identifier of the dump file */ /* Identifier of the dump file */
@@ -187,6 +204,8 @@ static LOG_FileID logfileid;
/* Forward prototype */ /* Forward prototype */
static void update_sel_options(void); 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, static void slew_sources(struct timespec *raw, struct timespec *cooked, double dfreq,
double doffset, LCL_ChangeType change_type, void *anything); double doffset, LCL_ChangeType change_type, void *anything);
static void add_dispersion(double dispersion, void *anything); static void add_dispersion(double dispersion, void *anything);
@@ -212,6 +231,8 @@ void SRC_Initialise(void) {
LCL_AddParameterChangeHandler(slew_sources, NULL); LCL_AddParameterChangeHandler(slew_sources, NULL);
LCL_AddDispersionNotifyHandler(add_dispersion, NULL); LCL_AddDispersionNotifyHandler(add_dispersion, NULL);
last_updated_inst = NULL;
logfileid = CNF_GetLogSelection() ? LOG_FileOpen("selection", logfileid = CNF_GetLogSelection() ? LOG_FileOpen("selection",
" Date (UTC) Time IP Address S EOpts Reach Score Last sample Low end High end") " Date (UTC) Time IP Address S EOpts Reach Score Last sample Low end High end")
: -1; : -1;
@@ -295,7 +316,13 @@ void SRC_DestroyInstance(SRC_Instance instance)
{ {
int dead_index, i; int dead_index, i;
if (last_updated_inst == instance)
last_updated_inst = NULL;
assert(initialised); assert(initialised);
if (instance->index < 0 || instance->index >= n_sources ||
instance != sources[instance->index])
assert(0);
SST_DeleteInstance(instance->stats); SST_DeleteInstance(instance->stats);
dead_index = instance->index; dead_index = instance->index;
@@ -308,11 +335,12 @@ void SRC_DestroyInstance(SRC_Instance instance)
update_sel_options(); update_sel_options();
/* If this was the previous reference source, we have to reselect! */ if (selected_source_index > dead_index)
if (selected_source_index == dead_index)
SRC_ReselectSource();
else if (selected_source_index > dead_index)
--selected_source_index; --selected_source_index;
else if (selected_source_index == dead_index)
unselect_selected_source(LOGS_INFO, NULL, NULL);
SRC_SelectSource(NULL);
} }
/* ================================================== */ /* ================================================== */
@@ -324,15 +352,20 @@ SRC_ResetInstance(SRC_Instance instance)
instance->reachability = 0; instance->reachability = 0;
instance->reachability_size = 0; instance->reachability_size = 0;
instance->distant = 0; instance->distant = 0;
instance->bad = 0;
instance->status = SRC_BAD_STATS; instance->status = SRC_BAD_STATS;
instance->sel_score = 1.0; instance->sel_score = 1.0;
instance->stratum = 0; instance->stratum = 0;
instance->leap = LEAP_Unsynchronised; instance->leap = LEAP_Unsynchronised;
instance->leap_vote = 0; instance->leap_vote = 0;
instance->reported_falseticker = 0;
memset(&instance->sel_info, 0, sizeof (instance->sel_info)); memset(&instance->sel_info, 0, sizeof (instance->sel_info));
SST_ResetInstance(instance->stats); SST_ResetInstance(instance->stats);
if (selected_source_index == instance->index)
SRC_SelectSource(NULL);
} }
/* ================================================== */ /* ================================================== */
@@ -468,6 +501,19 @@ special_mode_end(void)
return 1; 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 void
SRC_UpdateReachability(SRC_Instance inst, int reachable) SRC_UpdateReachability(SRC_Instance inst, int reachable)
{ {
@@ -488,14 +534,9 @@ SRC_UpdateReachability(SRC_Instance inst, int reachable)
REF_SetUnsynchronised(); REF_SetUnsynchronised();
} }
/* Try to replace NTP sources that are unreachable, falsetickers, or /* Try to replace unreachable NTP sources */
have root distance or jitter larger than the allowed maximums */ if (inst->reachability == 0 && inst->reachability_size == SOURCE_REACH_BITS)
if (inst->type == SRC_NTP && handle_bad_source(inst);
((!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);
}
} }
/* ================================================== */ /* ================================================== */
@@ -555,18 +596,17 @@ update_sel_options(void)
for (i = 0; i < n_sources; i++) { for (i = 0; i < n_sources; i++) {
options = sources[i]->conf_sel_options; options = sources[i]->conf_sel_options;
if (options & SRC_SELECT_NOSELECT) if (!(options & SRC_SELECT_NOSELECT)) {
continue; switch (sources[i]->type) {
case SRC_NTP:
switch (sources[i]->type) { options |= sources[i]->authenticated ? auth_ntp_options : unauth_ntp_options;
case SRC_NTP: break;
options |= sources[i]->authenticated ? auth_ntp_options : unauth_ntp_options; case SRC_REFCLOCK:
break; options |= refclk_options;
case SRC_REFCLOCK: break;
options |= refclk_options; default:
break; assert(0);
default: }
assert(0);
} }
if (sources[i]->sel_options != options) { if (sources[i]->sel_options != options) {
@@ -580,17 +620,17 @@ update_sel_options(void)
/* ================================================== */ /* ================================================== */
static 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) if (REF_GetMode() != REF_ModeNormal)
return; return;
LOG(LOGS_INFO, format, arg); LOG(severity, format, arg);
} }
/* ================================================== */ /* ================================================== */
static void 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; char buf[320], *name, *ntp_name;
@@ -602,7 +642,7 @@ log_selection_source(const char *format, SRC_Instance inst)
else else
snprintf(buf, sizeof (buf), "%s", name); snprintf(buf, sizeof (buf), "%s", name);
log_selection_message(format, buf); log_selection_message(severity, format, buf);
} }
/* ================================================== */ /* ================================================== */
@@ -651,11 +691,23 @@ mark_source(SRC_Instance inst, SRC_Status status)
inst->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), source_to_string(inst), get_status_char(inst->status),
(unsigned int)inst->sel_options, (unsigned int)inst->reachability, (unsigned int)inst->sel_options, (unsigned int)inst->reachability,
inst->reachability_size, inst->updates, 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); inst->sel_info.lo_limit, inst->sel_info.hi_limit);
if (logfileid == -1) if (logfileid == -1)
@@ -690,6 +742,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 static int
@@ -801,15 +873,13 @@ SRC_SelectSource(SRC_Instance updated_inst)
double first_sample_ago, max_reach_sample_ago; double first_sample_ago, max_reach_sample_ago;
NTP_Leap leap_status; NTP_Leap leap_status;
if (updated_inst) if (updated_inst) {
updated_inst->updates++; updated_inst->updates++;
last_updated_inst = updated_inst;
}
if (n_sources == 0) { if (n_sources == 0) {
/* In this case, we clearly cannot synchronise to anything */ unselect_selected_source(LOGS_INFO, "Can't synchronise: no sources", NULL);
if (selected_source_index != INVALID_SOURCE) {
log_selection_message("Can't synchronise: no sources", NULL);
selected_source_index = INVALID_SOURCE;
}
return; return;
} }
@@ -994,15 +1064,13 @@ SRC_SelectSource(SRC_Instance updated_inst)
if (n_badstats_sources && n_sel_sources && selected_source_index == INVALID_SOURCE && 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) { max_sel_reach_size < SOURCE_REACH_BITS && max_sel_reach >> 1 == max_badstat_reach) {
mark_ok_sources(SRC_WAITS_STATS); mark_ok_sources(SRC_WAITS_STATS);
unselect_selected_source(LOGS_INFO, NULL, NULL);
return; return;
} }
if (n_endpoints == 0) { if (n_endpoints == 0) {
/* No sources provided valid endpoints */ /* No sources provided valid endpoints */
if (selected_source_index != INVALID_SOURCE) { unselect_selected_source(LOGS_INFO, "Can't synchronise: no selectable sources", NULL);
log_selection_message("Can't synchronise: no selectable sources", NULL);
selected_source_index = INVALID_SOURCE;
}
return; return;
} }
@@ -1080,8 +1148,13 @@ SRC_SelectSource(SRC_Instance updated_inst)
(best_trust_depth > 0 && best_trust_depth <= n_sel_trust_sources / 2)) { (best_trust_depth > 0 && best_trust_depth <= n_sel_trust_sources / 2)) {
/* Could not even get half the reachable (trusted) sources to agree */ /* 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) { if (selected_source_index != INVALID_SOURCE) {
log_selection_message("Can't synchronise: no majority", NULL);
REF_SetUnsynchronised(); REF_SetUnsynchronised();
selected_source_index = INVALID_SOURCE; selected_source_index = INVALID_SOURCE;
} }
@@ -1127,16 +1200,17 @@ SRC_SelectSource(SRC_Instance updated_inst)
sel_req_source = 0; sel_req_source = 0;
} else { } else {
mark_source(sources[i], SRC_FALSETICKER); 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 (!n_sel_sources || sel_req_source || n_sel_sources < CNF_GetMinSources()) {
if (selected_source_index != INVALID_SOURCE) { unselect_selected_source(LOGS_INFO, "Can't synchronise: %s selectable sources",
log_selection_message("Can't synchronise: %s selectable sources", !n_sel_sources ? "no" :
!n_sel_sources ? "no" : sel_req_source ? "no required source in" : "not enough");
sel_req_source ? "no required source in" : "not enough");
selected_source_index = INVALID_SOURCE;
}
mark_ok_sources(SRC_WAITS_SOURCES); mark_ok_sources(SRC_WAITS_SOURCES);
return; return;
} }
@@ -1243,19 +1317,23 @@ SRC_SelectSource(SRC_Instance updated_inst)
/* Before selecting the new synchronisation source wait until the reference /* Before selecting the new synchronisation source wait until the reference
can be updated */ can be updated */
if (sources[max_score_index]->updates == 0) { 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); mark_ok_sources(SRC_WAITS_UPDATE);
return; return;
} }
selected_source_index = max_score_index; 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 */ /* New source has been selected, reset all scores */
for (i = 0; i < n_sources; i++) { for (i = 0; i < n_sources; i++) {
sources[i]->sel_score = 1.0; sources[i]->sel_score = 1.0;
sources[i]->distant = 0; sources[i]->distant = 0;
sources[i]->reported_falseticker = 0;
} }
reported_no_majority = 0;
report_selection_loss = 0;
} }
mark_source(sources[selected_source_index], SRC_SELECTED); mark_source(sources[selected_source_index], SRC_SELECTED);
@@ -1546,6 +1624,8 @@ SRC_ResetSources(void)
for (i = 0; i < n_sources; i++) for (i = 0; i < n_sources; i++)
SRC_ResetInstance(sources[i]); SRC_ResetInstance(sources[i]);
LOG(LOGS_INFO, "Reset all sources");
} }
/* ================================================== */ /* ================================================== */
@@ -1593,6 +1673,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 int
SRC_ReportSource(int index, RPT_SourceReport *report, struct timespec *now) SRC_ReportSource(int index, RPT_SourceReport *report, struct timespec *now)
{ {

View File

@@ -131,6 +131,10 @@ extern int SRC_IsReachable(SRC_Instance inst);
extern int SRC_ReadNumberOfSources(void); extern int SRC_ReadNumberOfSources(void);
extern int SRC_ActiveSources(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_ReportSource(int index, RPT_SourceReport *report, struct timespec *now);
extern int SRC_ReportSourcestats(int index, RPT_SourcestatsReport *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); extern int SRC_GetSelectReport(int index, RPT_SelectReport *report);

View File

@@ -80,7 +80,7 @@ static LOG_FileID logfileid;
struct SST_Stats_Record { 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; uint32_t refid;
IPAddr *ip_addr; IPAddr *ip_addr;
@@ -964,9 +964,10 @@ SST_DoSourceReport(SST_Stats inst, RPT_SourceReport *report, struct timespec *no
report->latest_meas = inst->offsets[i]; report->latest_meas = inst->offsets[i];
report->latest_meas_err = 0.5*inst->root_delays[j] + inst->root_dispersions[j]; report->latest_meas_err = 0.5*inst->root_delays[j] + inst->root_dispersions[j];
/* 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 = 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); report->latest_meas_ago = UTI_DiffTimespecsToDouble(now, &last_sample_time);
} else { } else {
report->latest_meas_ago = (uint32_t)-1; report->latest_meas_ago = (uint32_t)-1;

View File

@@ -207,6 +207,12 @@ NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type,
return NSR_TooManySources; return NSR_TooManySources;
} }
const char *
NSR_StatusToString(NSR_Status status)
{
return "NTP not supported";
}
NSR_Status NSR_Status
NSR_RemoveSource(IPAddr *address) NSR_RemoveSource(IPAddr *address)
{ {

View File

@@ -35,6 +35,7 @@
#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING) #if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
#include <linux/ptp_clock.h> #include <linux/ptp_clock.h>
#include <poll.h>
#endif #endif
#ifdef FEAT_SCFILTER #ifdef FEAT_SCFILTER
@@ -497,6 +498,9 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context)
SCMP_SYS(getrlimit), SCMP_SYS(getrlimit),
SCMP_SYS(getuid), SCMP_SYS(getuid),
SCMP_SYS(getuid32), SCMP_SYS(getuid32),
#ifdef __NR_membarrier
SCMP_SYS(membarrier),
#endif
#ifdef __NR_rseq #ifdef __NR_rseq
SCMP_SYS(rseq), SCMP_SYS(rseq),
#endif #endif
@@ -599,6 +603,7 @@ SYS_Linux_EnableSystemCallFilter(int level, SYS_ProcessContext context)
SCMP_SYS(select), SCMP_SYS(select),
SCMP_SYS(set_robust_list), SCMP_SYS(set_robust_list),
SCMP_SYS(write), SCMP_SYS(write),
SCMP_SYS(writev),
/* Miscellaneous */ /* Miscellaneous */
SCMP_SYS(getrandom), 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 }, { SOL_IP, IP_PKTINFO }, { SOL_IP, IP_FREEBIND }, { SOL_IP, IP_TOS },
#ifdef FEAT_IPV6 #ifdef FEAT_IPV6
{ SOL_IPV6, IPV6_V6ONLY }, { SOL_IPV6, IPV6_RECVPKTINFO }, { SOL_IPV6, IPV6_V6ONLY }, { SOL_IPV6, IPV6_RECVPKTINFO },
#ifdef IPV6_TCLASS
{ SOL_IPV6, IPV6_TCLASS },
#endif
#endif #endif
#ifdef SO_BINDTODEVICE #ifdef SO_BINDTODEVICE
{ SOL_SOCKET, 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 int fcntls[] = { F_GETFD, F_SETFD, F_GETFL, F_SETFL };
const static unsigned long ioctls[] = { const static unsigned long ioctls[] = {
FIONREAD, TCGETS, FIONREAD, TCGETS, TIOCGWINSZ,
#if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING) #if defined(FEAT_PHC) || defined(HAVE_LINUX_TIMESTAMPING)
PTP_EXTTS_REQUEST, PTP_SYS_OFFSET, PTP_EXTTS_REQUEST, PTP_SYS_OFFSET,
#ifdef PTP_PIN_SETFUNC #ifdef PTP_PIN_SETFUNC
@@ -991,6 +999,16 @@ int
SYS_Linux_ReadPHCExtTimestamp(int fd, struct timespec *phc_ts, int *channel) SYS_Linux_ReadPHCExtTimestamp(int fd, struct timespec *phc_ts, int *channel)
{ {
struct ptp_extts_event extts_event; 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)) { if (read(fd, &extts_event, sizeof (extts_event)) != sizeof (extts_event)) {
DEBUG_LOG("Could not read PHC extts event"); DEBUG_LOG("Could not read PHC extts event");

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
. ./test.common . ./test.common
test_start "SHM refclock" test_start "reference clocks"
check_config_h 'FEAT_REFCLOCK 1' || test_skip check_config_h 'FEAT_REFCLOCK 1' || test_skip
check_config_h 'FEAT_PHC 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$" \ 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 || 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="" chronyc_options=""
server_strata=0 server_strata=0
chronyc_start=0.5 chronyc_start=0.5
@@ -102,7 +114,7 @@ limit=1
for chronyc_conf in \ for chronyc_conf in \
"accheck 1.2.3.4" \ "accheck 1.2.3.4" \
"add peer 10.0.0.0 minpoll 2 maxpoll 6" \ "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" \
"add server node1.net1.clk" \ "add server node1.net1.clk" \
"allow 1.2.3.4" \ "allow 1.2.3.4" \
"allow 1.2" \ "allow 1.2" \
@@ -164,6 +176,9 @@ for chronyc_conf in \
"reselectdist 1e-3" \ "reselectdist 1e-3" \
"reset sources" \ "reset sources" \
"selectdata" \ "selectdata" \
"selectopts 1.2.3.4 -noselect +trust +require +prefer" \
"selectopts ID#0000000001 +prefer" \
"selectopts PPS0 +prefer" \
"settime 16:30" \ "settime 16:30" \
"settime 16:30:05" \ "settime 16:30:05" \
"settime Nov 21, 2015 16:30:05" \ "settime Nov 21, 2015 16:30:05" \
@@ -231,6 +246,7 @@ RX timestamping : Kernel
Total TX : 1 Total TX : 1
Total RX : 1 Total RX : 1
Total valid RX : 1 Total valid RX : 1
Total good RX : 0
S Name/IP Address Auth COpts EOpts Last Score Interval Leap S Name/IP Address Auth COpts EOpts Last Score Interval Leap
======================================================================= =======================================================================
M node1\.net1\.clk N ----- ----- 0 1\.0 \+0ns \+0ns N M node1\.net1\.clk N ----- ----- 0 1\.0 \+0ns \+0ns N
@@ -244,7 +260,13 @@ NTS-KE connections dropped : 0
Authenticated NTP packets : 0 Authenticated NTP packets : 0
Interleaved NTP packets : 0 Interleaved NTP packets : 0
NTP timestamps held : 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=" chronyc_conf="
deny all deny all
@@ -326,6 +348,10 @@ maxupdateskew 192.168.123.1 10.0
minpoll 192.168.123.1 3 minpoll 192.168.123.1 3
minstratum 192.168.123.1 1 minstratum 192.168.123.1 1
polltarget 192.168.123.1 10 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" delete 192.168.123.1"
run_test || test_fail run_test || test_fail
@@ -344,6 +370,14 @@ check_chronyc_output "^200 OK
200 OK 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 200 OK$" || test_fail
chronyc_conf=" chronyc_conf="

View File

@@ -3,6 +3,7 @@
. ./test.common . ./test.common
test_start "presend option" test_start "presend option"
limit=9900
min_sync_time=136 min_sync_time=136
max_sync_time=260 max_sync_time=260
client_server_options="presend 6 maxdelay 16" client_server_options="presend 6 maxdelay 16"
@@ -22,4 +23,29 @@ check_source_selection || test_fail
check_packet_interval || test_fail check_packet_interval || test_fail
check_sync || 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 test_pass

View File

@@ -16,6 +16,6 @@ check_chronyd_exit || test_fail
check_source_selection || test_fail check_source_selection || test_fail
check_packet_interval || test_fail check_packet_interval || test_fail
check_sync || test_fail check_sync || test_fail
check_log_messages "clock wrong by" 4 8 || test_fail check_log_messages "clock wrong by" 3 8 || test_fail
test_pass 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 "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=1" 1 1 || test_fail
check_log_messages "Updated RX timestamp.*tss=2" 195 200 || 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 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.*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 else
check_log_messages "update_tx_timestamp.*Updated" 50 140 || test_fail check_log_messages "update_tx_timestamp.*Updated" 50 140 || test_fail
check_log_messages "update_tx_timestamp.*Unacceptable" 50 140 || test_fail check_log_messages "update_tx_timestamp.*Unacceptable" 50 140 || test_fail
@@ -57,4 +58,32 @@ for client_conf in \
fi fi
done 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 test_pass

View File

@@ -158,10 +158,10 @@ for dns in 1 0; do
check_source_selection && test_fail check_source_selection && test_fail
check_sync && 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_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.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" 6 8 || test_fail check_log_messages "Source 192.168.123.2 replaced with 192.168.123.1" 3 10 || test_fail
servers=2 servers=2
@@ -225,6 +225,8 @@ for dns in 1 0; do
check_file_messages " 3 2 .* 4460 " 0 0 log.packets || test_fail check_file_messages " 3 2 .* 4460 " 0 0 log.packets || test_fail
done done
min_sync_time=$[default_min_sync_time + 200]
max_sync_time=600
server_conf=" server_conf="
ntsserverkey tmp/server1.key ntsserverkey tmp/server1.key
ntsservercert tmp/server1.crt 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 check_file_messages " 3 2 .* 11123 " 3 3 log.packets || test_fail
dns=1 dns=1
min_sync_time=$default_min_sync_time
max_sync_time=400
server_conf=" server_conf="
ntsserverkey tmp/server1.key ntsserverkey tmp/server1.key
ntsservercert tmp/server1.crt ntsservercert tmp/server1.crt

106
test/simulation/142-ntpoverptp Executable file
View File

@@ -0,0 +1,106 @@
#!/usr/bin/env bash
. ./test.common
test_start "NTP over PTP"
# Block communication between 3 and 1
base_delay="(+ 1e-4 (* -1 (equal 0.1 from 3) (equal 0.1 to 1)))"
cat > tmp/peer.keys <<-EOF
1 MD5 1234567890
EOF
clients=2
peers=2
max_sync_time=420
server_conf="
ptpport 319"
client_conf="
ptpport 319
authselectmode ignore
keyfile tmp/peer.keys"
client_server_options="minpoll 6 maxpoll 6 port 319"
client_peer_options="minpoll 6 maxpoll 6 port 319 key 1"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
check_file_messages " 2 1 .* 319 319 1 96 " 150 160 \
log.packets || test_fail
check_file_messages " 1 2 .* 319 319 1 96 " 150 160 \
log.packets || test_fail
check_file_messages " 2 3 .* 319 319 1 116 " 150 160 \
log.packets || test_fail
check_file_messages " 3 2 .* 319 319 1 116 " 150 160 \
log.packets || test_fail
check_config_h 'HAVE_LINUX_TIMESTAMPING 1' || test_skip
export CLKNETSIM_TIMESTAMPING=2
export CLKNETSIM_LINK_SPEED=100
client_server_options+=" extfield F324 minpoll 0 maxpoll 0"
client_peer_options+=" extfield F324 minpoll 0 maxpoll 0 maxdelaydevratio 1e6"
server_conf+="
clockprecision 1e-9
hwtimestamp eth0"
client_conf+="
clockprecision 1e-9
hwtimestamp eth0"
delay_correction="(+ delay (* -8e-8 (+ length 46)))"
wander=1e-9
limit=1000
freq_offset=-1e-4
min_sync_time=5
max_sync_time=20
time_max_limit=1e-7
time_rms_limit=2e-8
freq_max_limit=1e-7
freq_rms_limit=5e-8
client_chronyd_options="-d"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
if check_config_h 'FEAT_DEBUG 1'; then
check_log_messages "apply_net_correction.*Applied" 900 2100 || test_fail
check_log_messages "apply_net_correction.*Invalid" 0 4 || test_fail
fi
client_server_options+=" xleave"
client_peer_options+=" xleave"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
if check_config_h 'FEAT_DEBUG 1'; then
check_log_messages "apply_net_correction.*Applied" 900 2100 || test_fail
check_log_messages "apply_net_correction.*Invalid" 0 4 || test_fail
freq_offset=0.0
delay_correction="(+ -1.0e-9 (* 1.0001 delay))"
run_test || test_fail
check_chronyd_exit || test_fail
check_log_messages "apply_net_correction.*Applied" 350 1400 || test_fail
check_log_messages "apply_net_correction.*Invalid" 350 1400 || test_fail
server_conf="ptpport 319"
client_conf="ptpport 319"
run_test || test_fail
check_chronyd_exit || test_fail
check_log_messages "apply_net_correction.*Applied" 0 0 || test_fail
fi
test_pass

View File

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

View File

@@ -2,7 +2,7 @@
. ./test.common . ./test.common
test_start "experimental extension field" test_start "mono+root extension field"
check_config_h 'FEAT_CMDMON 1' || test_skip check_config_h 'FEAT_CMDMON 1' || test_skip

75
test/simulation/145-rtc Executable file
View File

@@ -0,0 +1,75 @@
#!/usr/bin/env bash
. ./test.common
test_start "RTC tracking"
check_config_h 'FEAT_CMDMON 1' || test_skip
check_config_h 'FEAT_RTC 1' || test_skip
export CLKNETSIM_START_DATE=$(date -d 'Jan 1 00:00:00 UTC 2010' +'%s')
export CLKNETSIM_RTC_OFFSET=-10.0
time_offset=$(awk "BEGIN {print -($freq_offset * $limit)}")
wander=0.0
chronyc_start=9900
chronyc_conf="rtcdata"
client_chronyd_options="-x"
client_conf="
hwclockfile /dev/null
driftfile tmp/drift
rtcfile tmp/rtc
rtconutc"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_chronyc_output "^RTC ref time \(UTC\) : Fri Jan 01 02:4[34]:.. 2010
Number of samples : [0-9]+
Number of runs : [0-9]+
Sample span period : [ 0-9]+
RTC is fast by : -9\.01.... seconds
RTC gains time at : 99\.9[98]. ppm$" \
|| test_fail
export CLKNETSIM_START_DATE=$(date -d 'Jan 5 00:00:00 UTC 2010' +'%s')
export CLKNETSIM_RTC_OFFSET=$(awk "BEGIN {print -(10.0 - 4 * 86400 * $freq_offset)}")
touch -d 'Jan 1 00:00:00 UTC 2010' tmp/drift
time_offset=10
min_sync_time=2
max_sync_time=12
time_max_limit=1e-2
freq_max_limit=1e-1
time_rms_limit=1e-3
freq_rms_limit=1e-3
client_chronyd_options="-s"
client_conf+="
rtcautotrim 1"
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
check_chronyc_output "^RTC ref time \(UTC\) : Tue Jan 05 02:4[34]:.. 2010
Number of samples : [0-9]+
Number of runs : [0-9]+
Sample span period : [ 0-9]+
RTC is fast by : 0\.1..... seconds
RTC gains time at : [- ]0\.0.. ppm$" \
|| test_fail
export CLKNETSIM_START_DATE=$(date -d 'Jan 10 00:00:00 UTC 2010' +'%s')
export CLKNETSIM_RTC_OFFSET=-10.0
touch -d 'Jan 10 00:00:00 UTC 2010' tmp/drift
time_offset=-10000
min_sync_time=1
max_sync_time=1
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_sync || test_fail
test_pass

73
test/simulation/146-offline Executable file
View File

@@ -0,0 +1,73 @@
#!/usr/bin/env bash
. ./test.common
test_start "online/offline switching"
check_config_h 'FEAT_CMDMON 1' || test_skip
servers=2
limit=$[10 * 1800]
client_server_conf="
server 192.168.123.1 offline iburst
server 192.168.123.2 polltarget 64"
chronyc_conf="timeout 4000000
activity
offline
activity
onoffline 192.168.123.1
online 192.168.123.2
activity
offline
activity
"
chronyc_start=1
base_delay="(+ 1e-4 (* 1800 (equal 0.1 from 4)))"
jitter=1e-6
time_max_limit=2e-2
freq_max_limit=1e-3
time_rms_limit=2e-2
freq_rms_limit=1e-5
min_sync_time=120
max_sync_time=140
run_test || test_fail
check_chronyd_exit || test_fail
check_packet_interval || test_fail
check_sync || test_fail
check_file_messages " 3 1 .* 123 " 30 90 log.packets || test_fail
check_file_messages " 3 2 .* 123 " 130 150 log.packets || test_fail
check_chronyc_output "^200 OK
1 sources online
1 sources offline
0 sources doing burst \(return to online\)
0 sources doing burst \(return to offline\)
0 sources with unknown address
200 OK
200 OK
0 sources online
2 sources offline
0 sources doing burst \(return to online\)
0 sources doing burst \(return to offline\)
0 sources with unknown address
200 OK
200 OK
200 OK
2 sources online
0 sources offline
0 sources doing burst \(return to online\)
0 sources doing burst \(return to offline\)
0 sources with unknown address
200 OK
200 OK
0 sources online
2 sources offline
0 sources doing burst \(return to online\)
0 sources doing burst \(return to offline\)
0 sources with unknown address" \
|| test_fail
test_pass

59
test/simulation/147-refresh Executable file
View File

@@ -0,0 +1,59 @@
#!/usr/bin/env bash
. ./test.common
test_start "address refreshment"
limit=1000
servers=5
client_conf="logdir tmp
log measurements"
client_server_conf="server nodes-1-2.net1.clk maxpoll 6
pool nodes-3-4-5.net1.clk maxpoll 6 maxsources 2"
client_chronyd_options="-d"
chronyc_conf="refresh"
chronyc_start=500
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 "20.*192.168.123.1" 0 0 measurements.log || test_fail
check_file_messages "20.*192.168.123.2" 15 17 measurements.log || test_fail
check_file_messages "20.*192.168.123.[345]" 31 33 measurements.log || test_fail
rm -f tmp/measurements.log
if check_config_h 'FEAT_DEBUG 1'; then
check_log_messages "refreshing 192.168.123" 3 3 || test_fail
check_log_messages "resolved_name.*still fresh" 3 3 || test_fail
fi
limit=1100
client_server_conf="
server nodes-1-2.net1.clk maxpoll 6
pool nodes-3-4-5.net1.clk maxpoll 6 maxsources 3"
client_conf+="
refresh 128"
chronyc_conf=""
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 "20.*192.168.123.1" 0 0 measurements.log || test_fail
check_file_messages "20.*192.168.123.2" 16 18 measurements.log || test_fail
check_file_messages "20.*192.168.123.[345]" 50 55 measurements.log || test_fail
rm -f tmp/measurements.log
if check_config_h 'FEAT_DEBUG 1'; then
check_log_messages "refreshing 192.168.123" 8 8 || test_fail
check_log_messages "resolved_name.*still fresh" 8 8 || test_fail
check_log_messages "refreshing 192.168.123.2" 2 2 || test_fail
check_log_messages "refreshing 192.168.123.3" 2 2 || test_fail
check_log_messages "refreshing 192.168.123.4" 2 2 || test_fail
check_log_messages "refreshing 192.168.123.5" 2 2 || test_fail
fi
test_pass

56
test/simulation/148-replacement Executable file
View File

@@ -0,0 +1,56 @@
#!/usr/bin/env bash
. ./test.common
test_start "source replacement"
limit=5000
client_conf="logdir tmp
log measurements"
servers=6
falsetickers=2
client_server_conf="pool nodes-1-2-3-4-5-6.net1.clk maxsources 5 polltarget 1 iburst"
wander=1e-12
jitter=1e-6
min_sync_time=7
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection || test_fail
check_packet_interval || test_fail
check_sync || test_fail
check_log_messages "Detected falseticker" 2 10 || test_fail
check_log_messages "Source 192.168.123.. replaced with" 1 3 || test_fail
check_file_messages "20.*192.168.123.* 11.1 6 6 " 15 18 measurements.log || test_fail
check_file_messages "20.*00:[1-5].:.. 192.168.123.* 11.1 6 6 " 1 4 measurements.log || test_fail
rm -f tmp/measurements.log
# 1 unreplaceable falseticker against 2 replaceable unreachable servers
servers=5
falsetickers=1
limit=200000
base_delay="(+ 1e-4 (* -1 (equal 0.6 to 4.5)))"
client_conf+="
minsources 2"
client_server_conf="
server 192.168.123.1
server nodes-2-4.net1.clk
server nodes-3-5.net1.clk"
max_sync_time=150000
run_test || test_fail
check_chronyd_exit || test_fail
check_source_selection && test_fail
check_packet_interval || test_fail
check_sync || test_fail
check_log_messages "Detected falseticker" 2 10 || test_fail
check_log_messages "Source 192.168.123.. replaced with" 2 70 || test_fail
check_log_messages "2010-01-01T0[0-4]:.*Source 192.168.123.. replaced with" 2 15 || test_fail
check_log_messages "2010-01-01T0[5-9]:.*Source 192.168.123.. replaced with" 0 15 || test_fail
check_file_messages "20.*192.168.123.* 11.1 6 6 " 20 500 measurements.log || test_fail
rm -f tmp/measurements.log
test_pass

View File

@@ -31,6 +31,7 @@ default_primary_time_offset=0.0
default_time_offset=1e-1 default_time_offset=1e-1
default_freq_offset=1e-4 default_freq_offset=1e-4
default_base_delay=1e-4 default_base_delay=1e-4
default_delay_correction=""
default_jitter=1e-4 default_jitter=1e-4
default_jitter_asymmetry=0.0 default_jitter_asymmetry=0.0
default_wander=1e-9 default_wander=1e-9
@@ -78,6 +79,7 @@ default_client_min_mean_out_interval=0.0
default_client_max_min_out_interval=inf default_client_max_min_out_interval=inf
default_cmdmon_unix=1 default_cmdmon_unix=1
default_pcap_dumps=0
default_dns=0 default_dns=0
# Initialize test settings from their defaults # Initialize test settings from their defaults
@@ -459,6 +461,10 @@ run_test() {
for j in $(seq 1 $nodes); do for j in $(seq 1 $nodes); do
echo "node${i}_delay${j} = $(get_delay_expr up)" echo "node${i}_delay${j} = $(get_delay_expr up)"
echo "node${j}_delay${i} = $(get_delay_expr down)" echo "node${j}_delay${i} = $(get_delay_expr down)"
if [ -n "$delay_correction" ]; then
echo "node${i}_delay_correction${j} = $delay_correction"
echo "node${j}_delay_correction${i} = $delay_correction"
fi
done done
done > tmp/conf done > tmp/conf
@@ -469,6 +475,9 @@ run_test() {
for i in $(seq 1 $n); do for i in $(seq 1 $n); do
test_message 2 0 "starting node $node:" test_message 2 0 "starting node $node:"
[ $pcap_dumps -ne 0 ] && export CLKNETSIM_PCAP_DUMP=tmp/pcap.$node
if [ $stratum -eq 1 ]; then if [ $stratum -eq 1 ]; then
step=$server_step step=$server_step
start=$server_start start=$server_start
@@ -509,6 +518,8 @@ run_test() {
for i in $(seq 1 $[$nodes - $node + 1]); do for i in $(seq 1 $[$nodes - $node + 1]); do
test_message 2 0 "starting node $node:" test_message 2 0 "starting node $node:"
[ $pcap_dumps -ne 0 ] && export CLKNETSIM_PCAP_DUMP=tmp/pcap.$node
options=$([ $dns -eq 0 ] && printf "%s" "-n") options=$([ $dns -eq 0 ] && printf "%s" "-n")
if [ $cmdmon_unix -ne 0 ]; then if [ $cmdmon_unix -ne 0 ]; then
options+=" -h /clknetsim/unix/$[$node - $clients]:1" options+=" -h /clknetsim/unix/$[$node - $clients]:1"

View File

@@ -42,6 +42,7 @@ for command in \
"reselect" \ "reselect" \
"reselectdist 1e-3" \ "reselectdist 1e-3" \
"reset sources" \ "reset sources" \
"selectopts $server -noselect +trust +prefer +require" \
"smoothtime reset" \ "smoothtime reset" \
"smoothtime activate" \ "smoothtime activate" \
; do ; do
@@ -95,12 +96,13 @@ TX timestamping : (Daemon|Kernel)
RX timestamping : (Daemon|Kernel) RX timestamping : (Daemon|Kernel)
Total TX : [0-9]+ Total TX : [0-9]+
Total RX : [0-9]+ Total RX : [0-9]+
Total valid RX : [0-9]+$" || test_fail Total valid RX : [0-9]+
Total good RX : [0-9]+$" || test_fail
run_chronyc "selectdata" || test_fail run_chronyc "selectdata" || test_fail
check_chronyc_output "^S Name/IP Address Auth COpts EOpts Last Score Interval Leap check_chronyc_output "^S Name/IP Address Auth COpts EOpts Last Score Interval Leap
======================================================================= =======================================================================
s 127\.0\.0\.1 N ----- ----- 0 1\.0 \+0ns \+0ns \?$" || test_fail s 127\.0\.0\.1 N -PTR- -PTR- 0 1\.0 \+0ns \+0ns \?$" || test_fail
run_chronyc "serverstats" || test_fail run_chronyc "serverstats" || test_fail
check_chronyc_output "^NTP packets received : [0-9]+ check_chronyc_output "^NTP packets received : [0-9]+
@@ -113,7 +115,13 @@ NTS-KE connections dropped : 0
Authenticated NTP packets : 0 Authenticated NTP packets : 0
Interleaved NTP packets : 0 Interleaved NTP packets : 0
NTP timestamps held : 0 NTP timestamps held : 0
NTP timestamp span : 0$"|| test_fail NTP timestamp span : 0
NTP daemon RX timestamps : 0
NTP daemon TX timestamps : [0-9]+
NTP kernel RX timestamps : [0-9]+
NTP kernel TX timestamps : 0
NTP hardware RX timestamps : 0
NTP hardware TX timestamps : 0$"|| test_fail
run_chronyc "manual on" || test_fail run_chronyc "manual on" || test_fail
check_chronyc_output "^200 OK$" || test_fail check_chronyc_output "^200 OK$" || test_fail

View File

@@ -30,6 +30,8 @@ echo "server 127.123.4.4" > $TEST_DIR/conf4.d/4.conf
echo "server 127.123.5.1" > $TEST_DIR/conf5.d/1.sources echo "server 127.123.5.1" > $TEST_DIR/conf5.d/1.sources
echo "server 127.123.5.2" > $TEST_DIR/conf5.d/2.sources echo "server 127.123.5.2" > $TEST_DIR/conf5.d/2.sources
echo "server 127.123.5.3" > $TEST_DIR/conf5.d/3.sources echo "server 127.123.5.3" > $TEST_DIR/conf5.d/3.sources
echo "server 127.123.5.4" > $TEST_DIR/conf5.d/4.sources
echo "server 127.123.5.5" > $TEST_DIR/conf5.d/5.sources
start_chronyd || test_fail start_chronyd || test_fail
@@ -46,12 +48,16 @@ check_chronyc_output "^[^=]*
.. 127\.123\.1\.2 [^^]* .. 127\.123\.1\.2 [^^]*
.. 127\.123\.5\.1 [^^]* .. 127\.123\.5\.1 [^^]*
.. 127\.123\.5\.2 [^^]* .. 127\.123\.5\.2 [^^]*
.. 127\.123\.5\.3 [^^]*$" || test_fail .. 127\.123\.5\.3 [^^]*
.. 127\.123\.5\.4 [^^]*
.. 127\.123\.5\.5 [^^]*$" || test_fail
rm $TEST_DIR/conf5.d/1.sources rm $TEST_DIR/conf5.d/1.sources
echo "server 127.123.5.2 minpoll 7" > $TEST_DIR/conf5.d/2.sources echo "server 127.123.5.2 minpoll 5" > $TEST_DIR/conf5.d/2.sources
echo > $TEST_DIR/conf5.d/3.sources echo "server 127.123.5.3 minpoll 7" > $TEST_DIR/conf5.d/3.sources
echo "server 127.123.5.4" > $TEST_DIR/conf5.d/4.sources echo > $TEST_DIR/conf5.d/4.sources
echo "server 127.123.5.5" >> $TEST_DIR/conf5.d/5.sources
echo "server 127.123.5.6" > $TEST_DIR/conf5.d/6.sources
run_chronyc "reload sources" || test_fail run_chronyc "reload sources" || test_fail
@@ -66,9 +72,12 @@ check_chronyc_output "^[^=]*
.. 127\.123\.2\.3 [^^]* .. 127\.123\.2\.3 [^^]*
.. 127\.123\.4\.4 [^^]* .. 127\.123\.4\.4 [^^]*
.. 127\.123\.1\.2 *[05] 6 [^^]* .. 127\.123\.1\.2 *[05] 6 [^^]*
.. 127\.123\.5\.2 *[05] 7 [^^]* .. 127\.123\.5\.5 [^^]*
.. 127\.123\.5\.4 [^^]*$" || test_fail .. 127\.123\.5\.2 *[05] 5 [^^]*
.. 127\.123\.5\.3 *[05] 7 [^^]*
.. 127\.123\.5\.6 [^^]*$" || test_fail
stop_chronyd || test_fail stop_chronyd || test_fail
check_chronyd_message_count "Could not add source" 1 1 || test_fail
test_pass test_pass

View File

@@ -43,7 +43,7 @@ wait_for_sync || test_fail
run_chronyc "authdata" || test_fail run_chronyc "authdata" || test_fail
check_chronyc_output "^Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen check_chronyc_output "^Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen
========================================================================= =========================================================================
127\.0\.0\.1 NTS 1 15 256 [0-9] 0 0 [78] 100$" || test_fail 127\.0\.0\.1 NTS 1 (30|15) (128|256) [0-9] 0 0 [78] ( 64|100)$" || test_fail
stop_chronyd || test_fail stop_chronyd || test_fail
check_chronyd_messages || test_fail check_chronyd_messages || test_fail
@@ -57,7 +57,7 @@ wait_for_sync || test_fail
run_chronyc "authdata" || test_fail run_chronyc "authdata" || test_fail
check_chronyc_output "^Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen check_chronyc_output "^Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen
========================================================================= =========================================================================
127\.0\.0\.1 NTS 1 15 256 [0-9] 0 0 [78] 100$" || test_fail 127\.0\.0\.1 NTS 1 (30|15) (128|256) [0-9] 0 0 [78] ( 64|100)$" || test_fail
stop_chronyd || test_fail stop_chronyd || test_fail
check_chronyd_messages || test_fail check_chronyd_messages || test_fail

140
test/system/011-systemd Executable file
View File

@@ -0,0 +1,140 @@
#!/usr/bin/env bash
. ./test.common
check_chronyd_features NTS || test_skip "NTS support disabled"
certtool --help &> /dev/null || test_skip "certtool missing"
check_chronyd_features DEBUG || test_skip "DEBUG support disabled"
systemd-socket-activate -h &> /dev/null || test_skip "systemd-socket-activate missing"
has_ipv6=$(check_chronyd_features IPV6 && ping6 -c 1 ::1 > /dev/null 2>&1 && echo 1 || echo 0)
test_start "systemd socket activation"
cat > $TEST_DIR/cert.cfg <<EOF
cn = "chrony-nts-test"
dns_name = "chrony-nts-test"
ip_address = "$server"
$([ "$has_ipv6" = "1" ] && echo 'ip_address = "::1"')
serial = 001
activation_date = "$[$(date '+%Y') - 1]-01-01 00:00:00 UTC"
expiration_date = "$[$(date '+%Y') + 2]-01-01 00:00:00 UTC"
signing_key
encryption_key
EOF
certtool --generate-privkey --key-type=ed25519 --outfile $TEST_DIR/server.key \
&> $TEST_DIR/certtool.log
certtool --generate-self-signed --load-privkey $TEST_DIR/server.key \
--template $TEST_DIR/cert.cfg --outfile $TEST_DIR/server.crt &>> $TEST_DIR/certtool.log
chown $user $TEST_DIR/server.*
ntpport=$(get_free_port)
ntsport=$(get_free_port)
server_options="port $ntpport nts ntsport $ntsport"
extra_chronyd_directives="
port $ntpport
ntsport $ntsport
ntsserverkey $TEST_DIR/server.key
ntsservercert $TEST_DIR/server.crt
ntstrustedcerts $TEST_DIR/server.crt
ntsdumpdir $TEST_LIBDIR
ntsprocesses 3"
if [ "$has_ipv6" = "1" ]; then
extra_chronyd_directives="$extra_chronyd_directives
bindaddress ::1
server ::1 minpoll -6 maxpoll -6 $server_options"
fi
# enable debug logging
extra_chronyd_options="-L -1"
# Hack to trigger systemd-socket-activate to activate the service. Normally,
# chronyd.service would be configured with the WantedBy= directive so it starts
# without waiting for socket activation.
# (https://0pointer.de/blog/projects/socket-activation.html).
for i in $(seq 10); do
sleep 1
(echo "wake up" > /dev/udp/127.0.0.1/$ntpport) 2>/dev/null
(echo "wake up" > /dev/tcp/127.0.0.1/$ntsport) 2>/dev/null
done &
# Test with UDP sockets (unfortunately systemd-socket-activate doesn't support
# both datagram and stream sockets in the same invocation:
# https://github.com/systemd/systemd/issues/9983).
CHRONYD_WRAPPER="systemd-socket-activate \
--datagram \
--listen 127.0.0.1:$ntpport \
--listen 127.0.0.1:$ntsport"
if [ "$has_ipv6" = "1" ]; then
CHRONYD_WRAPPER="$CHRONYD_WRAPPER \
--listen [::1]:$ntpport \
--listen [::1]:$ntsport"
fi
start_chronyd || test_fail
wait_for_sync || test_fail
if [ "$has_ipv6" = "1" ]; then
run_chronyc "ntpdata ::1" || test_fail
check_chronyc_output "Total RX +: [1-9]" || test_fail
fi
run_chronyc "authdata" || test_fail
check_chronyc_output "^Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen
=========================================================================\
$([ "$has_ipv6" = "1" ] && printf "\n%s\n" '::1 NTS 1 (30|15) (128|256) [0-9] 0 0 [78] ( 64|100)')
127\.0\.0\.1 NTS 1 (30|15) (128|256) [0-9] 0 0 [78] ( 64|100)$" || test_fail
stop_chronyd || test_fail
# DGRAM ntpport socket should be used
check_chronyd_message_count "Reusing UDPv4 socket fd=3 local=127.0.0.1:$ntpport" 1 1 || test_fail
# DGRAM ntsport socket should be ignored
check_chronyd_message_count "Reusing TCPv4 socket fd=4 local=127.0.0.1:$ntsport" 0 0 || test_fail
if [ "$has_ipv6" = "1" ]; then
# DGRAM ntpport socket should be used
check_chronyd_message_count "Reusing UDPv6 socket fd=5 local=\[::1\]:$ntpport" 1 1 || test_fail
# DGRAM ntsport socket should be ignored
check_chronyd_message_count "Reusing TCPv6 socket fd=6 local=\[::1\]:$ntsport" 0 0 || test_fail
fi
check_chronyd_messages || test_fail
check_chronyd_files || test_fail
# Test with TCP sockets
CHRONYD_WRAPPER="systemd-socket-activate \
--listen 127.0.0.1:$ntpport \
--listen 127.0.0.1:$ntsport"
if [ "$has_ipv6" = "1" ]; then
CHRONYD_WRAPPER="$CHRONYD_WRAPPER \
--listen [::1]:$ntpport \
--listen [::1]:$ntsport"
fi
start_chronyd || test_fail
wait_for_sync || test_fail
if [ "$has_ipv6" = "1" ]; then
run_chronyc "ntpdata ::1" || test_fail
check_chronyc_output "Total RX +: [1-9]" || test_fail
fi
run_chronyc "authdata" || test_fail
check_chronyc_output "^Name/IP address Mode KeyID Type KLen Last Atmp NAK Cook CLen
=========================================================================\
$([ "$has_ipv6" = "1" ] && printf "\n%s\n" '::1 NTS 1 (30|15) (128|256) [0-9] 0 0 [78] ( 64|100)')
127\.0\.0\.1 NTS 1 (30|15) (128|256) [0-9] 0 0 [78] ( 64|100)$" || test_fail
stop_chronyd || test_fail
# STREAM ntpport should be ignored
check_chronyd_message_count "Reusing TCPv4 socket fd=3 local=127.0.0.1:$ntpport" 0 0 || test_fail
# STREAM ntsport should be used
check_chronyd_message_count "Reusing TCPv4 socket fd=4 local=127.0.0.1:$ntsport" 1 1 || test_fail
if [ "$has_ipv6" = "1" ]; then
# STREAM ntpport should be ignored
check_chronyd_message_count "Reusing TCPv6 socket fd=5 local=\[::1\]:$ntpport" 0 0 || test_fail
# STREAM ntsport should be used
check_chronyd_message_count "Reusing TCPv6 socket fd=6 local=\[::1\]:$ntsport" 1 1 || test_fail
fi
check_chronyd_messages || test_fail
check_chronyd_files || test_fail
test_pass

View File

@@ -6,7 +6,7 @@ check_chronyd_features SCFILTER || test_skip "SCFILTER support disabled"
test_start "system call filter in non-destructive tests" test_start "system call filter in non-destructive tests"
for level in "-1" "1" "-2" "2"; do for level in 1 2 -1 -2; do
test_message 1 1 "level $level:" test_message 1 1 "level $level:"
for test in 0[0-8][0-9]-*[^_]; do for test in 0[0-8][0-9]-*[^_]; do
test_message 2 0 "$test" test_message 2 0 "$test"

View File

@@ -6,7 +6,7 @@ check_chronyd_features SCFILTER || test_skip "SCFILTER support disabled"
test_start "system call filter in destructive tests" test_start "system call filter in destructive tests"
for level in "-1" "1" "-2" "2"; do for level in 1 2 -1 -2; do
test_message 1 1 "level $level:" test_message 1 1 "level $level:"
for test in 1[0-8][0-9]-*[^_]; do for test in 1[0-8][0-9]-*[^_]; do
test_message 2 0 "$test" test_message 2 0 "$test"

View File

@@ -42,6 +42,8 @@ test_start() {
su "$user" -s /bin/sh -c "touch $TEST_DIR/test" 2> /dev/null || \ su "$user" -s /bin/sh -c "touch $TEST_DIR/test" 2> /dev/null || \
test_skip "$user cannot access $TEST_DIR" test_skip "$user cannot access $TEST_DIR"
rm "$TEST_DIR/test" rm "$TEST_DIR/test"
else
chown 0:0 "$TEST_DIR" || test_skip "could not chown $TEST_DIR"
fi fi
echo "Testing $*:" echo "Testing $*:"
@@ -322,7 +324,7 @@ check_chronyd_messages() {
([ "$clock_control" -ne 0 ] || grep -q 'Disabled control of system clock' "$logfile") && \ ([ "$clock_control" -ne 0 ] || grep -q 'Disabled control of system clock' "$logfile") && \
([ "$minimal_config" -ne 0 ] || grep -q 'Frequency .* read from' "$logfile") && \ ([ "$minimal_config" -ne 0 ] || grep -q 'Frequency .* read from' "$logfile") && \
grep -q 'chronyd exiting' "$logfile" && \ grep -q 'chronyd exiting' "$logfile" && \
! grep -q 'Could not' "$logfile" && \ ! (grep -v '^.\{19\}Z D:' "$logfile" | grep -q 'Could not') && \
! grep -q 'Disabled command socket' "$logfile" && \ ! grep -q 'Disabled command socket' "$logfile" && \
test_ok || test_bad test_ok || test_bad
} }

97
test/unit/array.c Normal file
View File

@@ -0,0 +1,97 @@
/*
**********************************************************************
* Copyright (C) Miroslav Lichvar 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.
*
**********************************************************************
*/
#include <array.c>
#include <util.h>
#include "test.h"
void
test_unit(void)
{
unsigned int i, j, k, k2, l, es, s;
unsigned char *el1, el2[20];
ARR_Instance a;
for (i = 0; i < 1000; i++) {
es = random() % sizeof (el2) + 1;
a = ARR_CreateInstance(es);
TEST_CHECK(ARR_GetSize(a) == 0);
for (j = 0; j < 100; j++) {
s = ARR_GetSize(a);
switch (random() % 6) {
case 0:
el1 = ARR_GetNewElement(a);
TEST_CHECK(ARR_GetSize(a) == s + 1);
memset(el1, s % 256, es);
TEST_CHECK(ARR_GetElement(a, s) == el1);
break;
case 1:
for (k = 0; k < s; k++) {
el1 = ARR_GetElement(a, k);
for (l = 0; l < es; l++)
TEST_CHECK(el1[l] == k % 256);
}
break;
case 2:
if (s == 0)
break;
TEST_CHECK(ARR_GetElements(a) == ARR_GetElement(a, 0));
break;
case 3:
memset(el2, s % 256, es);
ARR_AppendElement(a, el2);
TEST_CHECK(ARR_GetSize(a) == s + 1);
break;
case 4:
if (s == 0)
break;
k2 = random() % s;
ARR_RemoveElement(a, k2);
TEST_CHECK(ARR_GetSize(a) == s - 1);
for (k = k2; k < s - 1; k++) {
el1 = ARR_GetElement(a, k);
for (l = 0; l < es; l++) {
TEST_CHECK(el1[l] == (k + 1) % 256);
el1[l] = k % 256;
}
}
break;
case 5:
k2 = random() % 1000;
ARR_SetSize(a, k2);
TEST_CHECK(ARR_GetSize(a) == k2);
for (k = s; k < k2; k++) {
el1 = ARR_GetElement(a, k);
for (l = 0; l < es; l++)
el1[l] = k % 256;
}
break;
default:
assert(0);
}
}
ARR_DestroyInstance(a);
}
}

View File

@@ -36,6 +36,7 @@ test_unit(void)
{ {
uint64_t ts64, prev_first_ts64, prev_last_ts64, max_step; uint64_t ts64, prev_first_ts64, prev_last_ts64, max_step;
uint32_t index2, prev_first, prev_size; uint32_t index2, prev_first, prev_size;
NTP_Timestamp_Source ts_src, ts_src2;
struct timespec ts, ts2; struct timespec ts, ts2;
int i, j, k, index, shift; int i, j, k, index, shift;
CLG_Service s; CLG_Service s;
@@ -95,7 +96,7 @@ test_unit(void)
TEST_CHECK(!ntp_ts_map.timestamps); TEST_CHECK(!ntp_ts_map.timestamps);
UTI_ZeroNtp64(&ntp_ts); UTI_ZeroNtp64(&ntp_ts);
CLG_SaveNtpTimestamps(&ntp_ts, NULL); CLG_SaveNtpTimestamps(&ntp_ts, NULL, 0);
TEST_CHECK(ntp_ts_map.timestamps); TEST_CHECK(ntp_ts_map.timestamps);
TEST_CHECK(ntp_ts_map.first == 0); TEST_CHECK(ntp_ts_map.first == 0);
TEST_CHECK(ntp_ts_map.size == 0); TEST_CHECK(ntp_ts_map.size == 0);
@@ -132,8 +133,10 @@ test_unit(void)
UTI_ZeroTimespec(&ts); UTI_ZeroTimespec(&ts);
} }
ts_src = random() % (MAX_NTP_TS + 1);
CLG_SaveNtpTimestamps(&ntp_ts, CLG_SaveNtpTimestamps(&ntp_ts,
UTI_IsZeroTimespec(&ts) ? (random() % 2 ? &ts : NULL) : &ts); UTI_IsZeroTimespec(&ts) ? (random() % 2 ? &ts : NULL) : &ts,
ts_src);
if (j < ntp_ts_map.max_size) { if (j < ntp_ts_map.max_size) {
TEST_CHECK(ntp_ts_map.size == j + 1); TEST_CHECK(ntp_ts_map.size == j + 1);
@@ -144,14 +147,15 @@ test_unit(void)
} }
TEST_CHECK(ntp_ts_map.cached_index == ntp_ts_map.size - 1); TEST_CHECK(ntp_ts_map.cached_index == ntp_ts_map.size - 1);
TEST_CHECK(get_ntp_tss(ntp_ts_map.size - 1)->slew_epoch == ntp_ts_map.slew_epoch); TEST_CHECK(get_ntp_tss(ntp_ts_map.size - 1)->slew_epoch == ntp_ts_map.slew_epoch);
TEST_CHECK(CLG_GetNtpTxTimestamp(&ntp_ts, &ts2)); TEST_CHECK(CLG_GetNtpTxTimestamp(&ntp_ts, &ts2, &ts_src2));
TEST_CHECK(UTI_CompareTimespecs(&ts, &ts2) == 0); TEST_CHECK(UTI_CompareTimespecs(&ts, &ts2) == 0);
TEST_CHECK(UTI_IsZeroTimespec(&ts) || ts_src == ts_src2);
for (k = random() % 4; k > 0; k--) { for (k = random() % 4; k > 0; k--) {
index2 = random() % ntp_ts_map.size; index2 = random() % ntp_ts_map.size;
int64_to_ntp64(get_ntp_tss(index2)->rx_ts, &ntp_ts); int64_to_ntp64(get_ntp_tss(index2)->rx_ts, &ntp_ts);
if (random() % 2) if (random() % 2)
TEST_CHECK(CLG_GetNtpTxTimestamp(&ntp_ts, &ts)); TEST_CHECK(CLG_GetNtpTxTimestamp(&ntp_ts, &ts, &ts_src2));
UTI_Ntp64ToTimespec(&ntp_ts, &ts); UTI_Ntp64ToTimespec(&ntp_ts, &ts);
UTI_AddDoubleToTimespec(&ts, TST_GetRandomDouble(-1.999, 1.999), &ts); UTI_AddDoubleToTimespec(&ts, TST_GetRandomDouble(-1.999, 1.999), &ts);
@@ -165,10 +169,12 @@ test_unit(void)
1.0e-9); 1.0e-9);
} }
CLG_UpdateNtpTxTimestamp(&ntp_ts, &ts); ts_src = random() % (MAX_NTP_TS + 1);
CLG_UpdateNtpTxTimestamp(&ntp_ts, &ts, ts_src);
TEST_CHECK(CLG_GetNtpTxTimestamp(&ntp_ts, &ts2)); TEST_CHECK(CLG_GetNtpTxTimestamp(&ntp_ts, &ts2, &ts_src2));
TEST_CHECK(UTI_CompareTimespecs(&ts, &ts2) == 0); TEST_CHECK(UTI_CompareTimespecs(&ts, &ts2) == 0);
TEST_CHECK(ts_src == ts_src2);
if (random() % 2) { if (random() % 2) {
uint16_t prev_epoch = ntp_ts_map.slew_epoch; uint16_t prev_epoch = ntp_ts_map.slew_epoch;
@@ -181,20 +187,20 @@ test_unit(void)
index = random() % (ntp_ts_map.size - 1); index = random() % (ntp_ts_map.size - 1);
if (get_ntp_tss(index)->rx_ts + 1 != get_ntp_tss(index + 1)->rx_ts) { if (get_ntp_tss(index)->rx_ts + 1 != get_ntp_tss(index + 1)->rx_ts) {
int64_to_ntp64(get_ntp_tss(index)->rx_ts + 1, &ntp_ts); int64_to_ntp64(get_ntp_tss(index)->rx_ts + 1, &ntp_ts);
TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts)); TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts, &ts_src2));
int64_to_ntp64(get_ntp_tss(index + 1)->rx_ts - 1, &ntp_ts); int64_to_ntp64(get_ntp_tss(index + 1)->rx_ts - 1, &ntp_ts);
TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts)); TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts, &ts_src2));
CLG_UpdateNtpTxTimestamp(&ntp_ts, &ts); CLG_UpdateNtpTxTimestamp(&ntp_ts, &ts, ts_src);
CLG_UndoNtpTxTimestampSlew(&ntp_ts, &ts); CLG_UndoNtpTxTimestampSlew(&ntp_ts, &ts);
} }
} }
if (random() % 2) { if (random() % 2) {
int64_to_ntp64(get_ntp_tss(0)->rx_ts - 1, &ntp_ts); int64_to_ntp64(get_ntp_tss(0)->rx_ts - 1, &ntp_ts);
TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts)); TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts, &ts_src2));
int64_to_ntp64(get_ntp_tss(ntp_ts_map.size - 1)->rx_ts + 1, &ntp_ts); int64_to_ntp64(get_ntp_tss(ntp_ts_map.size - 1)->rx_ts + 1, &ntp_ts);
TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts)); TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts, &ts_src2));
CLG_UpdateNtpTxTimestamp(&ntp_ts, &ts); CLG_UpdateNtpTxTimestamp(&ntp_ts, &ts, ts_src);
CLG_UndoNtpTxTimestampSlew(&ntp_ts, &ts); CLG_UndoNtpTxTimestampSlew(&ntp_ts, &ts);
} }
} }
@@ -207,7 +213,7 @@ test_unit(void)
while (ntp_ts_map.size < ntp_ts_map.max_size) { while (ntp_ts_map.size < ntp_ts_map.max_size) {
ts64 += get_random64() >> (shift + 8); ts64 += get_random64() >> (shift + 8);
int64_to_ntp64(ts64, &ntp_ts); int64_to_ntp64(ts64, &ntp_ts);
CLG_SaveNtpTimestamps(&ntp_ts, NULL); CLG_SaveNtpTimestamps(&ntp_ts, NULL, 0);
if (ntp_ts_map.cached_index + NTPTS_INSERT_LIMIT < ntp_ts_map.size) if (ntp_ts_map.cached_index + NTPTS_INSERT_LIMIT < ntp_ts_map.size)
ts64 = get_ntp_tss(ntp_ts_map.size - 1)->rx_ts; ts64 = get_ntp_tss(ntp_ts_map.size - 1)->rx_ts;
} }
@@ -228,7 +234,7 @@ test_unit(void)
prev_size = ntp_ts_map.size; prev_size = ntp_ts_map.size;
prev_first_ts64 = get_ntp_tss(0)->rx_ts; prev_first_ts64 = get_ntp_tss(0)->rx_ts;
prev_last_ts64 = get_ntp_tss(prev_size - 1)->rx_ts; prev_last_ts64 = get_ntp_tss(prev_size - 1)->rx_ts;
CLG_SaveNtpTimestamps(&ntp_ts, NULL); CLG_SaveNtpTimestamps(&ntp_ts, NULL, 0);
TEST_CHECK(find_ntp_rx_ts(ts64, &index2)); TEST_CHECK(find_ntp_rx_ts(ts64, &index2));
@@ -259,13 +265,13 @@ test_unit(void)
if (random() % 10 == 0) { if (random() % 10 == 0) {
CLG_DisableNtpTimestamps(&ntp_ts); CLG_DisableNtpTimestamps(&ntp_ts);
TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts)); TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts, &ts_src2));
} }
for (k = random() % 10; k > 0; k--) { for (k = random() % 10; k > 0; k--) {
ts64 = get_random64() >> shift; ts64 = get_random64() >> shift;
int64_to_ntp64(ts64, &ntp_ts); int64_to_ntp64(ts64, &ntp_ts);
CLG_GetNtpTxTimestamp(&ntp_ts, &ts); CLG_GetNtpTxTimestamp(&ntp_ts, &ts, &ts_src2);
} }
} }
@@ -274,8 +280,8 @@ test_unit(void)
LCL_ChangeUnknownStep, NULL); LCL_ChangeUnknownStep, NULL);
TEST_CHECK(ntp_ts_map.size == 0); TEST_CHECK(ntp_ts_map.size == 0);
TEST_CHECK(ntp_ts_map.cached_rx_ts == 0ULL); TEST_CHECK(ntp_ts_map.cached_rx_ts == 0ULL);
TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts)); TEST_CHECK(!CLG_GetNtpTxTimestamp(&ntp_ts, &ts, &ts_src2));
CLG_UpdateNtpTxTimestamp(&ntp_ts, &ts); CLG_UpdateNtpTxTimestamp(&ntp_ts, &ts, ts_src);
} }
} }

View File

@@ -1,6 +1,6 @@
/* /*
********************************************************************** **********************************************************************
* Copyright (C) Miroslav Lichvar 2016-2018 * Copyright (C) Miroslav Lichvar 2016-2018, 2022
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as

View File

@@ -1,6 +1,6 @@
/* /*
********************************************************************** **********************************************************************
* Copyright (C) Miroslav Lichvar 2017-2018 * Copyright (C) Miroslav Lichvar 2017-2018, 2023
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
@@ -35,6 +35,7 @@ static struct timespec current_time;
static NTP_Packet req_buffer, res_buffer; static NTP_Packet req_buffer, res_buffer;
static int req_length, res_length; static int req_length, res_length;
#define NIO_IsHwTsEnabled() 1
#define NIO_OpenServerSocket(addr) ((addr)->ip_addr.family != IPADDR_UNSPEC ? 100 : 0) #define NIO_OpenServerSocket(addr) ((addr)->ip_addr.family != IPADDR_UNSPEC ? 100 : 0)
#define NIO_CloseServerSocket(fd) assert(fd == 100) #define NIO_CloseServerSocket(fd) assert(fd == 100)
#define NIO_OpenClientSocket(addr) ((addr)->ip_addr.family != IPADDR_UNSPEC ? 101 : 0) #define NIO_OpenClientSocket(addr) ((addr)->ip_addr.family != IPADDR_UNSPEC ? 101 : 0)
@@ -80,7 +81,7 @@ get_random_key_id(void)
} }
static void static void
send_request(NCR_Instance inst) send_request(NCR_Instance inst, int late_hwts)
{ {
NTP_Local_Address local_addr; NTP_Local_Address local_addr;
NTP_Local_Timestamp local_ts; NTP_Local_Timestamp local_ts;
@@ -98,12 +99,18 @@ send_request(NCR_Instance inst)
local_addr.ip_addr.family = IPADDR_UNSPEC; local_addr.ip_addr.family = IPADDR_UNSPEC;
local_addr.if_index = INVALID_IF_INDEX; local_addr.if_index = INVALID_IF_INDEX;
local_addr.sock_fd = 101; local_addr.sock_fd = 101;
zero_local_timestamp(&local_ts);
local_ts.ts = current_time; local_ts.ts = current_time;
local_ts.err = 0.0;
local_ts.source = NTP_TS_KERNEL; local_ts.source = NTP_TS_KERNEL;
NCR_ProcessTxKnown(inst, &local_addr, &local_ts, &req_buffer, req_length); NCR_ProcessTxKnown(inst, &local_addr, &local_ts, &req_buffer, req_length);
} }
if (late_hwts) {
inst->report.total_good_count++;
} else {
inst->report.total_good_count = 0;
}
} }
static void static void
@@ -115,8 +122,8 @@ process_request(NTP_Remote_Address *remote_addr)
local_addr.ip_addr.family = IPADDR_UNSPEC; local_addr.ip_addr.family = IPADDR_UNSPEC;
local_addr.if_index = INVALID_IF_INDEX; local_addr.if_index = INVALID_IF_INDEX;
local_addr.sock_fd = 100; local_addr.sock_fd = 100;
zero_local_timestamp(&local_ts);
local_ts.ts = current_time; local_ts.ts = current_time;
local_ts.err = 0.0;
local_ts.source = NTP_TS_KERNEL; local_ts.source = NTP_TS_KERNEL;
res_length = 0; res_length = 0;
@@ -267,7 +274,8 @@ send_response(int interleaved, int authenticated, int allow_update, int valid_ts
} }
static void static void
proc_response(NCR_Instance inst, int good, int valid, int updated_sync, int updated_init) proc_response(NCR_Instance inst, int good, int valid, int updated_sync,
int updated_init, int save)
{ {
NTP_Local_Address local_addr; NTP_Local_Address local_addr;
NTP_Local_Timestamp local_ts; NTP_Local_Timestamp local_ts;
@@ -281,8 +289,8 @@ proc_response(NCR_Instance inst, int good, int valid, int updated_sync, int upda
local_addr.ip_addr.family = IPADDR_UNSPEC; local_addr.ip_addr.family = IPADDR_UNSPEC;
local_addr.if_index = INVALID_IF_INDEX; local_addr.if_index = INVALID_IF_INDEX;
local_addr.sock_fd = NTP_LVM_TO_MODE(res->lvm) != MODE_SERVER ? 100 : 101; local_addr.sock_fd = NTP_LVM_TO_MODE(res->lvm) != MODE_SERVER ? 100 : 101;
zero_local_timestamp(&local_ts);
local_ts.ts = current_time; local_ts.ts = current_time;
local_ts.err = 0.0;
local_ts.source = NTP_TS_KERNEL; local_ts.source = NTP_TS_KERNEL;
prev_rx_count = inst->report.total_rx_count; prev_rx_count = inst->report.total_rx_count;
@@ -292,6 +300,19 @@ proc_response(NCR_Instance inst, int good, int valid, int updated_sync, int upda
ret = NCR_ProcessRxKnown(inst, &local_addr, &local_ts, res, res_length); ret = NCR_ProcessRxKnown(inst, &local_addr, &local_ts, res, res_length);
if (save) {
TEST_CHECK(ret);
TEST_CHECK(inst->saved_response);
TEST_CHECK(inst->saved_response->timeout_id != 0);
TEST_CHECK(has_saved_response(inst));
if (random() % 2)
saved_response_timeout(inst);
else
transmit_timeout(inst);
TEST_CHECK(inst->saved_response->timeout_id == 0);
TEST_CHECK(!has_saved_response(inst));
}
if (good > 0) if (good > 0)
TEST_CHECK(ret); TEST_CHECK(ret);
else if (!good) else if (!good)
@@ -327,7 +348,7 @@ process_replay(NCR_Instance inst, NTP_Packet *packet_queue,
do { do {
res_buffer = packet_queue[random() % queue_length]; res_buffer = packet_queue[random() % queue_length];
} while (!UTI_CompareNtp64(&res_buffer.transmit_ts, &inst->remote_ntp_tx)); } while (!UTI_CompareNtp64(&res_buffer.transmit_ts, &inst->remote_ntp_tx));
proc_response(inst, 0, 0, 0, updated_init); proc_response(inst, 0, 0, 0, updated_init, 0);
advance_time(1e-6); advance_time(1e-6);
} }
@@ -392,7 +413,7 @@ test_unit(void)
"local", "local",
"keyfile ntp_core.keys" "keyfile ntp_core.keys"
}; };
int i, j, k, interleaved, authenticated, valid, updated, has_updated; int i, j, k, interleaved, authenticated, valid, updated, has_updated, late_hwts;
CPS_NTP_Source source; CPS_NTP_Source source;
NTP_Remote_Address remote_addr; NTP_Remote_Address remote_addr;
NCR_Instance inst1, inst2; NCR_Instance inst1, inst2;
@@ -439,6 +460,8 @@ test_unit(void)
for (j = 0; j < 50; j++) { for (j = 0; j < 50; j++) {
DEBUG_LOG("client/peer test iteration %d/%d", i, j); DEBUG_LOG("client/peer test iteration %d/%d", i, j);
late_hwts = random() % 2;
authenticated = random() % 2;
interleaved = random() % 2 && (inst1->mode != MODE_CLIENT || interleaved = random() % 2 && (inst1->mode != MODE_CLIENT ||
inst1->tx_count < MAX_CLIENT_INTERLEAVED_TX); inst1->tx_count < MAX_CLIENT_INTERLEAVED_TX);
authenticated = random() % 2; authenticated = random() % 2;
@@ -454,35 +477,35 @@ test_unit(void)
(int)source.params.authkey, source.params.version, (int)source.params.authkey, source.params.version,
interleaved, authenticated, valid, updated, has_updated); interleaved, authenticated, valid, updated, has_updated);
send_request(inst1); send_request(inst1, late_hwts);
send_response(interleaved, authenticated, 1, 0, 1); send_response(interleaved, authenticated, 1, 0, 1);
DEBUG_LOG("response 1"); DEBUG_LOG("response 1");
proc_response(inst1, 0, 0, 0, updated); proc_response(inst1, 0, 0, 0, updated, 0);
if (source.params.authkey) { if (source.params.authkey) {
send_response(interleaved, authenticated, 1, 1, 0); send_response(interleaved, authenticated, 1, 1, 0);
DEBUG_LOG("response 2"); DEBUG_LOG("response 2");
proc_response(inst1, 0, 0, 0, 0); proc_response(inst1, 0, 0, 0, 0, 0);
} }
send_response(interleaved, authenticated, 1, 1, 1); send_response(interleaved, authenticated, 1, 1, 1);
DEBUG_LOG("response 3"); DEBUG_LOG("response 3");
proc_response(inst1, -1, valid, valid, updated); proc_response(inst1, -1, valid, valid, updated, valid && late_hwts);
DEBUG_LOG("response 4"); DEBUG_LOG("response 4");
proc_response(inst1, 0, 0, 0, 0); proc_response(inst1, 0, 0, 0, 0, 0);
advance_time(-1.0); advance_time(-1.0);
send_response(interleaved, authenticated, 1, 1, 1); send_response(interleaved, authenticated, 1, 1, 1);
DEBUG_LOG("response 5"); DEBUG_LOG("response 5");
proc_response(inst1, 0, 0, 0, updated && valid); proc_response(inst1, 0, 0, 0, updated && valid, 0);
advance_time(1.0); advance_time(1.0);
send_response(interleaved, authenticated, 1, 1, 1); send_response(interleaved, authenticated, 1, 1, 1);
DEBUG_LOG("response 6"); DEBUG_LOG("response 6");
proc_response(inst1, 0, 0, valid && updated, updated); proc_response(inst1, 0, 0, valid && updated, updated, 0);
} }
NCR_DestroyInstance(inst1); NCR_DestroyInstance(inst1);
@@ -494,12 +517,12 @@ test_unit(void)
for (j = 0; j < 20; j++) { for (j = 0; j < 20; j++) {
DEBUG_LOG("server test iteration %d/%d", i, j); DEBUG_LOG("server test iteration %d/%d", i, j);
send_request(inst1); send_request(inst1, 0);
process_request(&remote_addr); process_request(&remote_addr);
proc_response(inst1, proc_response(inst1,
!source.params.interleaved || source.params.version != 4 || !source.params.interleaved || source.params.version != 4 ||
inst1->mode == MODE_ACTIVE || j != 2, inst1->mode == MODE_ACTIVE || j != 2,
1, 1, 0); 1, 1, 0, 0);
advance_time(1 << inst1->local_poll); advance_time(1 << inst1->local_poll);
} }
@@ -515,7 +538,9 @@ test_unit(void)
for (j = 0; j < 20; j++) { for (j = 0; j < 20; j++) {
DEBUG_LOG("peer replay test iteration %d/%d", i, j); DEBUG_LOG("peer replay test iteration %d/%d", i, j);
send_request(inst1); late_hwts = random() % 2;
send_request(inst1, late_hwts);
res_buffer = req_buffer; res_buffer = req_buffer;
assert(!res_length || res_length == req_length); assert(!res_length || res_length == req_length);
res_length = req_length; res_length = req_length;
@@ -523,7 +548,7 @@ test_unit(void)
TEST_CHECK(inst1->valid_timestamps == (j > 0)); TEST_CHECK(inst1->valid_timestamps == (j > 0));
DEBUG_LOG("response 1->2"); DEBUG_LOG("response 1->2");
proc_response(inst2, j > source.params.interleaved, j > 0, j > 0, 1); proc_response(inst2, j > source.params.interleaved, j > 0, j > 0, 1, 0);
packet_queue[(j * 2) % PACKET_QUEUE_LENGTH] = res_buffer; packet_queue[(j * 2) % PACKET_QUEUE_LENGTH] = res_buffer;
@@ -536,14 +561,14 @@ test_unit(void)
advance_time(1 << (source.params.minpoll - 1)); advance_time(1 << (source.params.minpoll - 1));
send_request(inst2); send_request(inst2, 0);
res_buffer = req_buffer; res_buffer = req_buffer;
assert(res_length == req_length); assert(res_length == req_length);
TEST_CHECK(inst2->valid_timestamps == (j > 0)); TEST_CHECK(inst2->valid_timestamps == (j > 0));
DEBUG_LOG("response 2->1"); DEBUG_LOG("response 2->1");
proc_response(inst1, 1, 1, 1, 1); proc_response(inst1, 1, 1, 1, 1, late_hwts);
packet_queue[(j * 2 + 1) % PACKET_QUEUE_LENGTH] = res_buffer; packet_queue[(j * 2 + 1) % PACKET_QUEUE_LENGTH] = res_buffer;

View File

@@ -28,6 +28,7 @@
#include <nameserv_async.h> #include <nameserv_async.h>
#include <ntp_core.h> #include <ntp_core.h>
#include <ntp_io.h> #include <ntp_io.h>
#include <sched.h>
static char *requested_name = NULL; static char *requested_name = NULL;
static DNS_NameResolveHandler resolve_handler = NULL; static DNS_NameResolveHandler resolve_handler = NULL;
@@ -41,9 +42,11 @@ static void *resolve_handler_arg = NULL;
change_remote_address(inst, remote_addr, ntp_only) change_remote_address(inst, remote_addr, ntp_only)
#define NCR_ProcessRxKnown(remote_addr, local_addr, ts, msg, len) (random() % 2) #define NCR_ProcessRxKnown(remote_addr, local_addr, ts, msg, len) (random() % 2)
#define NIO_IsServerConnectable(addr) (random() % 2) #define NIO_IsServerConnectable(addr) (random() % 2)
#define SCH_GetLastEventMonoTime() get_mono_time()
static void change_remote_address(NCR_Instance inst, NTP_Remote_Address *remote_addr, static void change_remote_address(NCR_Instance inst, NTP_Remote_Address *remote_addr,
int ntp_only); int ntp_only);
static double get_mono_time(void);
#include <ntp_sources.c> #include <ntp_sources.c>
@@ -85,6 +88,8 @@ update_random_address(NTP_Remote_Address *addr, int rand_bits)
TEST_CHECK(status == NSR_Success || status == NSR_AlreadyInUse); TEST_CHECK(status == NSR_Success || status == NSR_AlreadyInUse);
} }
TEST_CHECK(strlen(NSR_StatusToString(status)) > 0);
return status == NSR_Success; return status == NSR_Success;
} }
@@ -96,17 +101,26 @@ change_remote_address(NCR_Instance inst, NTP_Remote_Address *remote_addr, int nt
TEST_CHECK(record_lock); TEST_CHECK(record_lock);
if (update && update_pos == 0) if (update && update_pos == 0)
r = update_random_address(remote_addr, 4); r = update_random_address(random() % 2 ? remote_addr : NCR_GetRemoteAddress(inst), 4);
NCR_ChangeRemoteAddress(inst, remote_addr, ntp_only); NCR_ChangeRemoteAddress(inst, remote_addr, ntp_only);
if (update && update_pos == 1) if (update && update_pos == 1)
r = update_random_address(remote_addr, 4); r = update_random_address(random() % 2 ? remote_addr : NCR_GetRemoteAddress(inst), 4);
if (r) if (r)
TEST_CHECK(UTI_IsIPReal(&saved_address_update.old_address.ip_addr)); TEST_CHECK(UTI_IsIPReal(&saved_address_update.old_address.ip_addr));
} }
static double get_mono_time(void) {
static double t = 0.0;
if (random() % 2)
t += TST_GetRandomDouble(0.0, 100.0);
return t;
}
void void
test_unit(void) test_unit(void)
{ {

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